已发货数量匹配逻辑修改

This commit is contained in:
zzh 2026-03-26 11:17:19 +08:00
parent 49d5dd38a1
commit 5e220be272
2 changed files with 190 additions and 7 deletions

View File

@ -88,12 +88,16 @@
text-align: center;
color: var(--text-2);
}
#customer-order-page .filtered-row {
display: none !important;
}
</style>
<div id="customer-order-page">
<div class="page-header">
<h1 style="margin: 0; font-size: 24px;">客户订单管理</h1>
<div class="page-actions" style="margin-top: 16px; display: flex; gap: 12px;">
<div class="page-actions" style="margin-top: 16px; display: flex; gap: 12px; align-items: center; flex-wrap: wrap;">
<button id="add-order-btn" class="btn btn-primary">新增订单</button>
<button id="refresh-undelivered-btn" class="btn btn-secondary" title="刷新未交订单数据">
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="vertical-align: middle; margin-right: 4px;">
@ -101,6 +105,17 @@
</svg>
刷新未交订单
</button>
<div style="display: flex; align-items: center; gap: 8px; margin-left: auto;">
<label style="font-size: 14px; color: var(--text-2);">物料筛选</label>
<input
type="text"
id="material-filter"
placeholder="输入物料名称筛选..."
class="input"
style="width: 200px; padding: 6px 12px; font-size: 14px;"
/>
<button id="clear-filter-btn" class="btn btn-secondary" style="padding: 6px 12px;" title="清除筛选"></button>
</div>
</div>
</div>
@ -202,6 +217,25 @@
});
}
// 物料筛选功能
const filterInput = document.getElementById('material-filter');
const clearFilterBtn = document.getElementById('clear-filter-btn');
if (filterInput) {
filterInput.addEventListener('input', (e) => {
filterOrders(e.target.value);
});
}
if (clearFilterBtn) {
clearFilterBtn.addEventListener('click', () => {
if (filterInput) {
filterInput.value = '';
filterOrders('');
}
});
}
loadOrders();
}, 100);
@ -230,16 +264,24 @@
tbody.innerHTML = data.list.map(order => {
const undeliveredQty = order.undelivered_qty || 0;
const shippedQty = order.shipped_qty || 0;
const isManual = order.manual_undelivered_qty !== null && order.manual_undelivered_qty !== undefined;
const undeliveredStyle = undeliveredQty > 0 ? 'color: #f59e0b; font-weight: 700;' : 'color: #10b981;';
const manualIcon = isManual ? '<span title="手动设置" style="cursor: help; margin-left: 4px;">✏️</span>' : '';
return `
<tr>
<tr data-material="${(order.material || '').replace(/"/g, '&quot;')}" data-order-id="${order.id}">
<td>${order.order_date || '—'}</td>
<td>${order.order_no || '—'}</td>
<td>${order.customer_name || '—'}</td>
<td style="white-space: pre-line; word-break: break-word;">${order.material || '—'}</td>
<td>${order.quantity || 0}</td>
<td style="color: #3b82f6;">${shippedQty}</td>
<td style="${undeliveredStyle}">${undeliveredQty}</td>
<td style="${undeliveredStyle}"
onclick="window.CustomerOrder.editUndelivered(${order.id}, ${undeliveredQty}, ${order.auto_undelivered_qty || 0})"
title="点击编辑未交订单数量(自动计算: ${order.auto_undelivered_qty || 0}"
style="cursor: pointer; ${undeliveredStyle}">
${undeliveredQty}${manualIcon}
</td>
<td>${order.unit_price || 0}</td>
<td>
<button class="btn-text" onclick="window.CustomerOrder.editOrder(${order.id})">编辑</button>
@ -247,6 +289,12 @@
</td>
</tr>
`}).join('');
// 应用当前筛选(如果有)
const filterInput = document.getElementById('material-filter');
if (filterInput && filterInput.value) {
filterOrders(filterInput.value);
}
console.log('订单列表加载完成');
} catch (err) {
console.error('加载订单失败:', err);
@ -258,6 +306,47 @@
}
}
function filterOrders(keyword) {
const tbody = document.getElementById('order-list');
if (!tbody) return;
const rows = tbody.querySelectorAll('tr');
const filterText = keyword.toLowerCase().trim();
let visibleCount = 0;
rows.forEach(row => {
// 跳过空数据行
if (row.querySelector('.text-center')) {
return;
}
const material = (row.getAttribute('data-material') || '').toLowerCase();
if (!filterText || material.includes(filterText)) {
row.classList.remove('filtered-row');
visibleCount++;
} else {
row.classList.add('filtered-row');
}
});
// 如果没有匹配结果,显示提示
if (visibleCount === 0 && filterText) {
const existingMsg = tbody.querySelector('.no-filter-results');
if (!existingMsg) {
const msgRow = document.createElement('tr');
msgRow.className = 'no-filter-results';
msgRow.innerHTML = '<td colspan="9" class="text-center" style="color: var(--text-2);">没有匹配的物料</td>';
tbody.appendChild(msgRow);
}
} else {
const msgRow = tbody.querySelector('.no-filter-results');
if (msgRow) {
msgRow.remove();
}
}
}
let materialRowIndex = 0;
let productList = []; // 缓存BOM产品列表
@ -572,6 +661,41 @@
}
}
async function editUndelivered(orderId, currentQty, autoQty) {
const newQty = prompt(
`编辑未交订单数量\n\n当前值: ${currentQty}\n自动计算值: ${autoQty}\n\n请输入新的未交订单数量(留空恢复自动计算):`,
currentQty
);
// 用户取消
if (newQty === null) {
return;
}
// 用户输入空值,恢复自动计算
const finalQty = newQty.trim() === '' ? null : newQty.trim();
try {
const res = await fetch(`/api/customer-orders/${orderId}/undelivered`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ undelivered_qty: finalQty })
});
const data = await res.json();
if (res.ok && data.ok) {
API.toast(finalQty === null ? '已恢复自动计算' : '未交订单数量已更新', 'success');
await loadOrders();
} else {
API.toast(data.error || '更新失败', 'error');
}
} catch (err) {
console.error('更新未交订单数量失败:', err);
API.toast('更新失败', 'error');
}
}
async function editOrder(id) {
try {
const res = await fetch('/api/customer-orders');
@ -597,6 +721,7 @@
saveOrder,
editOrder,
deleteOrder,
editUndelivered,
addMaterialRow,
removeMaterialRow
};

View File

@ -261,6 +261,11 @@ def init_db():
c.execute('ALTER TABLE work_orders ADD COLUMN product_model TEXT')
except Exception:
pass # 列已存在
# 为客户订单表添加手动调整的未交订单数量字段
try:
c.execute('ALTER TABLE customer_orders ADD COLUMN manual_undelivered_qty INTEGER')
except Exception:
pass # 列已存在
# BOM物料清单表 - 定义产品的物料组成
c.execute('''CREATE TABLE IF NOT EXISTS bom(
@ -5053,7 +5058,7 @@ def get_customer_orders():
conn = get_db()
c = conn.cursor()
c.execute('''SELECT id, order_date, order_no, customer_name, material, quantity, unit_price,
created_by, created_at, updated_at
manual_undelivered_qty, created_by, created_at, updated_at
FROM customer_orders
ORDER BY order_date DESC, id DESC''')
rows = c.fetchall()
@ -5072,7 +5077,7 @@ def get_customer_orders():
order_spec_model = material_lines[1].strip() if len(material_lines) > 1 else ''
# 查询对账单中已发货数量
# 匹配策略:根据合同编号,判断物料名称是否互相包含
# 匹配策略:根据合同编号,用物料第一行名字判断是否互相包含
shipped_qty = 0
try:
if order_material_name:
@ -5086,7 +5091,8 @@ def get_customer_orders():
rec_material = (rec_row['material_name'] or '').strip()
rec_qty = rec_row['quantity'] or 0
# 判断物料名称是否互相包含(忽略大小写)
# 只用物料第一行名字判断是否互相包含(忽略大小写)
# 客户订单的物料第一行 vs 对账单的物料名称
if rec_material and (
order_material_name.lower() in rec_material.lower() or
rec_material.lower() in order_material_name.lower()
@ -5098,7 +5104,14 @@ def get_customer_orders():
shipped_qty = 0
# 计算未交数量
undelivered_qty = max(0, order_qty - shipped_qty)
auto_undelivered_qty = max(0, order_qty - shipped_qty)
# 优先使用手动设置的未交订单数量,如果没有则使用自动计算的
try:
manual_qty = row['manual_undelivered_qty']
except (KeyError, IndexError):
manual_qty = None
undelivered_qty = manual_qty if manual_qty is not None else auto_undelivered_qty
orders.append({
'id': row['id'],
@ -5110,6 +5123,8 @@ def get_customer_orders():
'unit_price': row['unit_price'],
'shipped_qty': shipped_qty,
'undelivered_qty': undelivered_qty,
'manual_undelivered_qty': manual_qty,
'auto_undelivered_qty': auto_undelivered_qty,
'created_by': row['created_by'],
'created_at': row['created_at'],
'updated_at': row['updated_at']
@ -5220,6 +5235,49 @@ def update_customer_order(order_id):
return jsonify({'ok': True, 'message': '订单更新成功'})
@app.patch('/api/customer-orders/<int:order_id>/undelivered')
@require_login
@require_any_role('superadmin')
def update_undelivered_qty(order_id):
"""更新客户订单的未交订单数量"""
data = request.get_json() or {}
undelivered_qty = data.get('undelivered_qty')
# 如果传入null或空则清除手动设置恢复自动计算
if undelivered_qty is None or undelivered_qty == '':
manual_qty = None
else:
try:
manual_qty = int(undelivered_qty)
if manual_qty < 0:
return jsonify({'error': '未交订单数量不能为负数'}), 400
except (ValueError, TypeError):
return jsonify({'error': '未交订单数量必须是整数'}), 400
conn = get_db()
c = conn.cursor()
# 检查订单是否存在
c.execute('SELECT id FROM customer_orders WHERE id=?', (order_id,))
if not c.fetchone():
conn.close()
return jsonify({'error': '订单不存在'}), 404
# 更新未交订单数量
now = get_beijing_time()
c.execute('''UPDATE customer_orders
SET manual_undelivered_qty=?, updated_at=?
WHERE id=?''',
(manual_qty, now, order_id))
conn.commit()
conn.close()
log('update_undelivered_qty', f'订单ID: {order_id}, 未交数量: {manual_qty}')
return jsonify({'ok': True, 'message': '未交订单数量已更新'})
@app.delete('/api/customer-orders/<int:order_id>')
@require_login
@require_any_role('superadmin')