优化采购需求

This commit is contained in:
zzh 2026-03-13 10:33:38 +08:00
parent 50f8a07b1b
commit 23cf2ed453
4 changed files with 444 additions and 137 deletions

View File

@ -861,11 +861,64 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
.toast.show{opacity:1} .toast.show{opacity:1}
.overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center} .overlay{position:fixed;inset:0;background:rgba(0,0,0,.4);display:flex;align-items:center;justify-content:center}
.overlay.hidden{display:none} .overlay.hidden{display:none}
.loader{display:flex;gap:8px}
.loader .dot{width:10px;height:10px;border-radius:999px;background:var(--primary);animation:bounce .9s ease infinite} /* 芯片电路板加载动画 */
.loader .dot:nth-child(2){animation-delay:.15s} .main-container {
.loader .dot:nth-child(3){animation-delay:.3s} display: flex;
@keyframes bounce{0%,100%{transform:translateY(0);opacity:.7}50%{transform:translateY(-8px);opacity:1}} justify-content: center;
align-items: center;
height: 100%;
width: 100%;
}
.loader {
width: 100%;
max-width: 400px;
height: 100%;
max-height: 250px;
}
.trace-bg {
stroke: #333;
stroke-width: 1.8;
fill: none;
}
.trace-flow {
stroke-width: 1.8;
fill: none;
stroke-dasharray: 40 400;
stroke-dashoffset: 438;
filter: drop-shadow(0 0 6px currentColor);
animation: flow 3s cubic-bezier(0.5, 0, 0.9, 1) infinite;
}
.yellow {
stroke: #ffea00;
color: #ffea00;
}
.blue {
stroke: #00ccff;
color: #00ccff;
}
.green {
stroke: #00ff15;
color: #00ff15;
}
.purple {
stroke: #9900ff;
color: #9900ff;
}
.red {
stroke: #ff3300;
color: #ff3300;
}
@keyframes flow {
to {
stroke-dashoffset: 0;
}
}
.three-body { .three-body {
--uib-size: 35px; --uib-size: 35px;
@ -970,64 +1023,6 @@ input[type="date"]::-webkit-calendar-picker-indicator:hover{
} }
} }
.spinner {
width: 44px;
height: 44px;
position: relative;
perspective: 800px;
animation: spinner-y0fdc1 2s infinite ease;
transform-style: preserve-3d;
}
.spinner > div {
background-color: rgba(0,77,255,0.2);
height: 100%;
position: absolute;
width: 100%;
border: 2px solid #004dff;
}
.spinner div:nth-of-type(1) {
transform: translateZ(-22px) rotateY(180deg);
}
.spinner div:nth-of-type(2) {
transform: rotateY(-270deg) translateX(50%);
transform-origin: top right;
}
.spinner div:nth-of-type(3) {
transform: rotateY(270deg) translateX(-50%);
transform-origin: center left;
}
.spinner div:nth-of-type(4) {
transform: rotateX(90deg) translateY(-50%);
transform-origin: top center;
}
.spinner div:nth-of-type(5) {
transform: rotateX(-90deg) translateY(50%);
transform-origin: bottom center;
}
.spinner div:nth-of-type(6) {
transform: translateZ(22px);
}
@keyframes spinner-y0fdc1 {
0% {
transform: rotate(45deg) rotateX(-25deg) rotateY(25deg);
}
50% {
transform: rotate(45deg) rotateX(-385deg) rotateY(25deg);
}
100% {
transform: rotate(45deg) rotateX(-385deg) rotateY(385deg);
}
}
.fade-enter{opacity:0;transform:translateY(8px)} .fade-enter{opacity:0;transform:translateY(8px)}
.fade-enter-active{transition:opacity .25s ease-out,transform .25s ease-out;opacity:1;transform:translateY(0)} .fade-enter-active{transition:opacity .25s ease-out,transform .25s ease-out;opacity:1;transform:translateY(0)}
.error{color:#ffb4b4} .error{color:#ffb4b4}

View File

@ -279,13 +279,159 @@
</div> </div>
<div id="toast" class="toast"></div> <div id="toast" class="toast"></div>
<div id="overlay" class="overlay hidden"> <div id="overlay" class="overlay hidden">
<div class="spinner"> <div class="main-container">
<div></div> <div class="loader">
<div></div> <svg viewBox="0 0 800 500" xmlns="http://www.w3.org/2000/svg">
<div></div> <defs>
<div></div> <linearGradient id="chipGradient" x1="0" y1="0" x2="0" y2="1">
<div></div> <stop offset="0%" stop-color="#2d2d2d"></stop>
<div></div> <stop offset="100%" stop-color="#0f0f0f"></stop>
</linearGradient>
<linearGradient id="textGradient" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stop-color="#eeeeee"></stop>
<stop offset="100%" stop-color="#888888"></stop>
</linearGradient>
<linearGradient id="pinGradient" x1="1" y1="0" x2="0" y2="0">
<stop offset="0%" stop-color="#bbbbbb"></stop>
<stop offset="50%" stop-color="#888888"></stop>
<stop offset="100%" stop-color="#555555"></stop>
</linearGradient>
</defs>
<g id="traces">
<path d="M100 100 H200 V210 H326" class="trace-bg"></path>
<path d="M100 100 H200 V210 H326" class="trace-flow purple"></path>
<path d="M80 180 H180 V230 H326" class="trace-bg"></path>
<path d="M80 180 H180 V230 H326" class="trace-flow blue"></path>
<path d="M60 260 H150 V250 H326" class="trace-bg"></path>
<path d="M60 260 H150 V250 H326" class="trace-flow yellow"></path>
<path d="M100 350 H200 V270 H326" class="trace-bg"></path>
<path d="M100 350 H200 V270 H326" class="trace-flow green"></path>
<path d="M700 90 H560 V210 H474" class="trace-bg"></path>
<path d="M700 90 H560 V210 H474" class="trace-flow blue"></path>
<path d="M740 160 H580 V230 H474" class="trace-bg"></path>
<path d="M740 160 H580 V230 H474" class="trace-flow green"></path>
<path d="M720 250 H590 V250 H474" class="trace-bg"></path>
<path d="M720 250 H590 V250 H474" class="trace-flow red"></path>
<path d="M680 340 H570 V270 H474" class="trace-bg"></path>
<path d="M680 340 H570 V270 H474" class="trace-flow yellow"></path>
</g>
<rect
x="330"
y="190"
width="140"
height="100"
rx="20"
ry="20"
fill="url(#chipGradient)"
stroke="#222"
stroke-width="3"
filter="drop-shadow(0 0 6px rgba(0,0,0,0.8))"
></rect>
<g>
<rect
x="322"
y="205"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="322"
y="225"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="322"
y="245"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="322"
y="265"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
</g>
<g>
<rect
x="470"
y="205"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="470"
y="225"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="470"
y="245"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
<rect
x="470"
y="265"
width="8"
height="10"
fill="url(#pinGradient)"
rx="2"
></rect>
</g>
<text
x="400"
y="240"
font-family="Arial, sans-serif"
font-size="22"
fill="url(#textGradient)"
text-anchor="middle"
alignment-baseline="middle"
>
Loading
</text>
<circle cx="100" cy="100" r="5" fill="black"></circle>
<circle cx="80" cy="180" r="5" fill="black"></circle>
<circle cx="60" cy="260" r="5" fill="black"></circle>
<circle cx="100" cy="350" r="5" fill="black"></circle>
<circle cx="700" cy="90" r="5" fill="black"></circle>
<circle cx="740" cy="160" r="5" fill="black"></circle>
<circle cx="720" cy="250" r="5" fill="black"></circle>
<circle cx="680" cy="340" r="5" fill="black"></circle>
</svg>
</div>
</div> </div>
</div> </div>

View File

@ -127,15 +127,67 @@
color: var(--text); color: var(--text);
} }
#purchase-demand-page .product-tabs-wrapper {
display: flex;
gap: 12px;
flex-wrap: wrap;
margin-bottom: 20px;
}
#purchase-demand-page .product-tab { #purchase-demand-page .product-tab {
border-bottom: 2px solid transparent; min-width: 100px;
border-radius: 4px 4px 0 0; height: 51px;
border-radius: 15px;
cursor: pointer;
transition: 0.3s ease;
background: linear-gradient(
to bottom right,
#2e8eff 0%,
rgba(46, 142, 255, 0) 30%
);
background-color: rgba(46, 142, 255, 0.2);
display: flex;
align-items: center;
justify-content: center;
border: none;
padding: 2px;
}
#purchase-demand-page .product-tab:hover,
#purchase-demand-page .product-tab:focus {
background-color: rgba(46, 142, 255, 0.7);
box-shadow: 0 0 10px rgba(46, 142, 255, 0.5);
outline: none;
} }
#purchase-demand-page .product-tab.active { #purchase-demand-page .product-tab.active {
background: var(--primary); background-color: rgba(46, 142, 255, 0.9);
color: white; box-shadow: 0 0 15px rgba(46, 142, 255, 0.7);
border-bottom-color: var(--primary); }
#purchase-demand-page .product-tab-inner {
width: 100%;
height: 47px;
border-radius: 13px;
background-color: var(--bg);
display: flex;
align-items: center;
justify-content: center;
padding: 0 20px;
color: #66b3ff;
font-weight: 600;
font-size: 14px;
text-shadow: 0 0 2px rgba(102, 179, 255, 0.5);
}
[data-theme="light"] #purchase-demand-page .product-tab-inner {
color: #0066cc;
text-shadow: none;
}
#purchase-demand-page .product-tab.active .product-tab-inner {
color: #003d7a;
font-weight: 700;
} }
</style> </style>
@ -153,16 +205,13 @@
</div> </div>
<div class="content-area"> <div class="content-area">
<!-- 计算说明提示 -->
<div style="margin: 0 20px 16px 20px; padding: 8px 16px; background: linear-gradient(135deg, #e3f2fd 0%, #f3e5f5 100%); border-left: 4px solid #2196f3; border-radius: 4px; font-size: 13px; color: #424242;">
<strong>采购需求计算公式</strong>
客户订单数量 × BOM单机用量 - 期初库存 = 净需求 按最小包装向上取整 = 实际采购数量
</div>
<!-- 产品选择标签页 --> <!-- 产品选择标签页 -->
<div style="padding: 0 20px;"> <div style="padding: 0 20px;">
<div style="display: flex; gap: 10px; margin-bottom: 16px; border-bottom: 2px solid var(--border); padding-bottom: 10px;"> <div class="product-tabs-wrapper" id="product-tabs-container">
<button class="product-tab btn btn-secondary" data-product="" onclick="PurchaseDemand.switchProduct('')">全部</button> <button class="product-tab active" data-product="" onclick="PurchaseDemand.switchProduct('')">
<div class="product-tab-inner">全部</div>
</button>
</div> </div>
</div> </div>
@ -466,6 +515,25 @@
<option value="cancelled">已取消</option> <option value="cancelled">已取消</option>
</select> </select>
</div> </div>
<div class="field">
<label>应用范围</label>
<div style="display: flex; gap: 20px; margin-top: 8px;">
<label for="batch-scope-selected" style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" id="batch-scope-selected" name="batch-scope" value="selected" checked style="margin-right: 8px;">
仅当前选中的记录
</label>
<label for="batch-scope-all" style="display: flex; align-items: center; cursor: pointer;">
<input type="radio" id="batch-scope-all" name="batch-scope" value="all" style="margin-right: 8px;">
指定产品的所有记录
</label>
</div>
<div id="batch-product-select-container" style="margin-top: 12px; display: none;">
<label style="font-size: 14px; margin-bottom: 4px; display: block;">选择产品</label>
<select id="batch-product-select" class="input" style="width: 100%;">
<option value="">请选择产品</option>
</select>
</div>
</div>
<div class="field"> <div class="field">
<label>备注可选</label> <label>备注可选</label>
<textarea id="batch-remark" class="input" rows="3" placeholder="可以为所有选中的项目添加统一的备注"></textarea> <textarea id="batch-remark" class="input" rows="3" placeholder="可以为所有选中的项目添加统一的备注"></textarea>
@ -537,14 +605,20 @@
console.log('检查数据是否有product_code字段:', demandList.some(item => 'product_code' in item)); // 检查字段是否存在 console.log('检查数据是否有product_code字段:', demandList.some(item => 'product_code' in item)); // 检查字段是否存在
// 更新标签页 // 更新标签页
const tabsContainer = document.querySelector('.product-tab').parentElement; const tabsContainer = document.getElementById('product-tabs-container');
if (!tabsContainer) return; if (!tabsContainer) return;
let html = '<button class="product-tab btn btn-secondary" data-product="" onclick="PurchaseDemand.switchProduct(\'\')">全部</button>'; const productArray = Array.from(products).sort();
Array.from(products).sort().forEach(product => { let html = `<button class="product-tab ${!currentProduct ? 'active' : ''}" data-product="" onclick="PurchaseDemand.switchProduct('')">
<div class="product-tab-inner">全部</div>
</button>`;
productArray.forEach(product => {
const isActive = product === currentProduct ? 'active' : ''; const isActive = product === currentProduct ? 'active' : '';
html += `<button class="product-tab btn btn-secondary ${isActive}" data-product="${product}" onclick="PurchaseDemand.switchProduct('${product}')">${product}</button>`; html += `<button class="product-tab ${isActive}" data-product="${product}" onclick="PurchaseDemand.switchProduct('${product}')">
<div class="product-tab-inner">${product}</div>
</button>`;
}); });
tabsContainer.innerHTML = html; tabsContainer.innerHTML = html;
@ -1144,22 +1218,58 @@
const checkboxes = document.querySelectorAll('.row-checkbox:checked'); const checkboxes = document.querySelectorAll('.row-checkbox:checked');
const selectedCount = checkboxes.length; const selectedCount = checkboxes.length;
if (selectedCount === 0) {
alert('请先选择要编辑的记录');
return;
}
// 更新选中数量显示 // 更新选中数量显示
document.getElementById('selected-count').textContent = selectedCount; document.getElementById('selected-count').textContent = selectedCount;
// 加载产品列表
loadProductOptions();
// 重置表单 // 重置表单
document.getElementById('batch-status').value = ''; document.getElementById('batch-status').value = '';
document.getElementById('batch-remark').value = ''; document.getElementById('batch-remark').value = '';
document.querySelector('input[name="batch-scope"][value="selected"]').checked = true;
document.getElementById('batch-product-select-container').style.display = 'none';
// 添加事件监听
document.getElementById('batch-scope-all').onchange = function() {
document.getElementById('batch-product-select-container').style.display =
this.checked ? 'block' : 'none';
};
document.getElementById('batch-scope-selected').onchange = function() {
document.getElementById('batch-product-select-container').style.display =
this.checked ? 'none' : 'block';
};
// 显示弹窗 // 显示弹窗
document.getElementById('batch-edit-status-modal').style.display = 'flex'; document.getElementById('batch-edit-status-modal').style.display = 'flex';
} }
function loadProductOptions() {
// 获取所有产品
const products = new Set();
demandList.forEach(item => {
if (item.product_code) {
products.add(item.product_code);
}
});
const productSelect = document.getElementById('batch-product-select');
productSelect.innerHTML = '<option value="">请选择产品</option>';
// 添加产品选项
Array.from(products).sort().forEach(product => {
const option = document.createElement('option');
option.value = product;
option.textContent = product;
productSelect.appendChild(option);
});
// 如果当前有选中的产品,设为默认值
if (currentProduct) {
productSelect.value = currentProduct;
}
}
function closeBatchEditStatusModal() { function closeBatchEditStatusModal() {
document.getElementById('batch-edit-status-modal').style.display = 'none'; document.getElementById('batch-edit-status-modal').style.display = 'none';
} }
@ -1167,26 +1277,49 @@
async function saveBatchEditStatus() { async function saveBatchEditStatus() {
const status = document.getElementById('batch-status').value; const status = document.getElementById('batch-status').value;
const remark = document.getElementById('batch-remark').value.trim(); const remark = document.getElementById('batch-remark').value.trim();
const scope = document.querySelector('input[name="batch-scope"]:checked').value;
if (!status) { if (!status) {
alert('请选择状态'); alert('请选择状态');
return; return;
} }
// 获取选中的ID let requestData = {
const checkboxes = document.querySelectorAll('.row-checkbox:checked'); status: status,
const ids = Array.from(checkboxes).map(cb => parseInt(cb.dataset.id)); remark: remark
};
if (!confirm(`确定要将选中的 ${ids.length} 条记录状态更新为"${getStatusText(status)}"吗?`)) { let confirmMessage = '';
if (scope === 'all') {
// 更新指定产品的所有记录
const selectedProduct = document.getElementById('batch-product-select').value;
if (!selectedProduct) {
alert('请选择要更新的产品');
return;
}
requestData.product_code = selectedProduct;
confirmMessage = `确定要将"${selectedProduct}"的所有记录状态更新为"${getStatusText(status)}"吗?`;
} else {
// 仅更新选中的记录
const checkboxes = document.querySelectorAll('.row-checkbox:checked');
const ids = Array.from(checkboxes).map(cb => parseInt(cb.dataset.id));
if (ids.length === 0) {
alert('请选择要更新的记录');
return;
}
requestData.ids = ids;
confirmMessage = `确定要将选中的 ${ids.length} 条记录状态更新为"${getStatusText(status)}"吗?`;
}
if (!confirm(confirmMessage)) {
return; return;
} }
try { try {
const res = await API.post('/api/purchase-demand/batch-update-status', { const res = await API.post('/api/purchase-demand/batch-update-status', requestData);
ids: ids,
status: status,
remark: remark
});
if (res.ok) { if (res.ok) {
alert(`成功更新 ${res.count} 条记录`); alert(`成功更新 ${res.count} 条记录`);

View File

@ -6507,16 +6507,17 @@ def delete_purchase_demand(demand_id):
@app.post('/api/purchase-demand/batch-update-status') @app.post('/api/purchase-demand/batch-update-status')
@require_login @require_login
@require_any_role('superadmin') @require_any_role('superadmin', 'admin')
def batch_update_purchase_demand_status(): def batch_update_purchase_demand_status():
"""批量更新采购需求状态""" """批量更新采购需求状态"""
data = request.get_json() or {} data = request.get_json() or {}
ids = data.get('ids', []) ids = data.get('ids', [])
status = data.get('status', '').strip() status = data.get('status')
remark = data.get('remark', '').strip() remark = data.get('remark', '')
product_code = data.get('product_code', '').strip()
if not ids: if not ids and not product_code:
return jsonify({'error': '请选择要更新的记录'}), 400 return jsonify({'error': '请选择要更新的记录或指定产品'}), 400
if not status: if not status:
return jsonify({'error': '请选择状态'}), 400 return jsonify({'error': '请选择状态'}), 400
@ -6528,9 +6529,15 @@ def batch_update_purchase_demand_status():
c = conn.cursor() c = conn.cursor()
now = get_beijing_time() now = get_beijing_time()
# 验证ID是否存在并获取原始状态 # 根据条件查询记录
placeholders = ','.join(['?' for _ in ids]) if product_code:
c.execute(f'SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand FROM purchase_demand WHERE id IN ({placeholders})', ids) # 按产品代码查询
c.execute('SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE product_code=?', (product_code,))
else:
# 按ID列表查询
placeholders = ','.join(['?' for _ in ids])
c.execute(f'SELECT id, status, material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE id IN ({placeholders})', ids)
existing_records = c.fetchall() existing_records = c.fetchall()
if not existing_records: if not existing_records:
@ -6547,11 +6554,18 @@ def batch_update_purchase_demand_status():
material_name = record['material_name'] material_name = record['material_name']
actual_purchase_qty = record['actual_purchase_qty'] actual_purchase_qty = record['actual_purchase_qty']
net_demand = record['net_demand'] net_demand = record['net_demand']
total_demand = record['total_demand']
initial_stock = record['initial_stock']
# 计算之前增加的库存量:实际采购 - 净需求 # 根据实际采购数量计算之前的变化量
added_stock = actual_purchase_qty - net_demand if actual_purchase_qty > 0:
# 如果实际采购不等于零,之前增加的库存量 = 实际采购 - 净需求
added_stock = actual_purchase_qty - net_demand
else:
# 如果实际采购等于零,之前减少的库存量 = 期初库存 - 总需求
added_stock = -(initial_stock - total_demand)
# 从期初库存表中减去之前增加的库存(恢复原值) # 从期初库存表中减去之前的变化量(恢复原值)
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,)) c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
stock_record = c.fetchone() stock_record = c.fetchone()
@ -6576,24 +6590,30 @@ def batch_update_purchase_demand_status():
params.extend(existing_ids) params.extend(existing_ids)
c.execute(f'UPDATE purchase_demand SET {", ".join(updates)} WHERE id IN ({placeholders})', params) c.execute(f'UPDATE purchase_demand SET {", ".join(updates)} WHERE id IN ({placeholders})', params)
# 如果状态更新为已下单,更新期初库存 # 如果状态更新为已下单,更新期初库存(包括已经是已下单状态的情况)
if status == 'ordered': if status == 'ordered':
for record in existing_records: for record in existing_records:
# 只处理之前不是"已下单"状态的记录 # 处理所有记录(包括已经是"已下单"状态的记录)
if record['status'] != 'ordered': material_code = record['material_code']
material_code = record['material_code'] material_name = record['material_name']
material_name = record['material_name'] actual_purchase_qty = record['actual_purchase_qty']
actual_purchase_qty = record['actual_purchase_qty'] net_demand = record['net_demand']
net_demand = record['net_demand'] total_demand = record['total_demand']
initial_stock = record['initial_stock']
# 计算新的期初库存:实际采购 - 净需求 # 根据实际采购数量计算新的期初库存
if actual_purchase_qty > 0:
# 如果实际采购不等于零,期初库存 = 实际采购 - 净需求
new_initial_stock = actual_purchase_qty - net_demand new_initial_stock = actual_purchase_qty - net_demand
else:
# 如果实际采购等于零,期初库存 = 期初库存 - 总需求
new_initial_stock = initial_stock - total_demand
# 只更新期初库存表 # 只更新期初库存表
c.execute('''INSERT OR REPLACE INTO initial_stock c.execute('''INSERT OR REPLACE INTO initial_stock
(material_code, material_name, stock_qty, updated_at) (material_code, material_name, stock_qty, updated_at)
VALUES (?, ?, ?, ?)''', VALUES (?, ?, ?, ?)''',
(material_code, material_name, new_initial_stock, now)) (material_code, material_name, new_initial_stock, now))
count = c.rowcount count = c.rowcount
conn.commit() conn.commit()
@ -6881,11 +6901,18 @@ def update_purchase_demand(id):
material_name = existing['material_name'] material_name = existing['material_name']
actual_purchase_qty = existing['actual_purchase_qty'] actual_purchase_qty = existing['actual_purchase_qty']
net_demand = existing['net_demand'] net_demand = existing['net_demand']
total_demand = existing['total_demand']
initial_stock = existing['initial_stock']
# 计算之前增加的库存量:实际采购 - 净需求 # 根据实际采购数量计算之前的变化量
added_stock = actual_purchase_qty - net_demand if actual_purchase_qty > 0:
# 如果实际采购不等于零,之前增加的库存量 = 实际采购 - 净需求
added_stock = actual_purchase_qty - net_demand
else:
# 如果实际采购等于零,之前减少的库存量 = 期初库存 - 总需求
added_stock = -(initial_stock - total_demand)
# 从期初库存表中减去之前增加的库存(恢复原值) # 从期初库存表中减去之前的变化量(恢复原值)
c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,)) c.execute('SELECT stock_qty FROM initial_stock WHERE material_code=?', (material_code,))
stock_record = c.fetchone() stock_record = c.fetchone()
@ -6903,16 +6930,22 @@ def update_purchase_demand(id):
update_values.append(id) update_values.append(id)
c.execute(f'UPDATE purchase_demand SET {", ".join(update_fields)} WHERE id=?', update_values) c.execute(f'UPDATE purchase_demand SET {", ".join(update_fields)} WHERE id=?', update_values)
# 如果状态更新为已下单,更新期初库存 # 如果状态更新为已下单,更新期初库存(包括已经是已下单状态的情况)
if 'status' in data and new_status == 'ordered' and old_status != 'ordered': if 'status' in data and new_status == 'ordered':
# 获取更新后的记录信息 # 获取更新后的记录信息
c.execute('SELECT material_code, material_name, actual_purchase_qty, net_demand FROM purchase_demand WHERE id=?', (id,)) c.execute('SELECT material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock FROM purchase_demand WHERE id=?', (id,))
record = c.fetchone() record = c.fetchone()
if record: if record:
material_code, material_name, actual_purchase_qty, net_demand = record material_code, material_name, actual_purchase_qty, net_demand, total_demand, initial_stock = record
# 计算新的期初库存:实际采购 - 净需求
new_initial_stock = actual_purchase_qty - net_demand # 根据实际采购数量计算新的期初库存
if actual_purchase_qty > 0:
# 如果实际采购不等于零,期初库存 = 实际采购 - 净需求
new_initial_stock = actual_purchase_qty - net_demand
else:
# 如果实际采购等于零,期初库存 = 期初库存 - 总需求
new_initial_stock = initial_stock - total_demand
# 只更新期初库存表 # 只更新期初库存表
c.execute('''INSERT OR REPLACE INTO initial_stock c.execute('''INSERT OR REPLACE INTO initial_stock