已发货数量匹配逻辑修改
This commit is contained in:
parent
49d5dd38a1
commit
5e220be272
@ -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, '"')}" 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
|
||||
};
|
||||
|
||||
@ -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')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user