fix: sync actual order quantity to order backup
This commit is contained in:
parent
b46717065d
commit
18b3fd7ff7
@ -349,6 +349,25 @@ def _select_confirm_bom(request_bom, saved_bom, base_bom):
|
|||||||
return base_bom
|
return base_bom
|
||||||
|
|
||||||
|
|
||||||
|
def _extract_product_actual_qty(items, product_code):
|
||||||
|
"""从 BOM 明细中读取明确填写的成品实际下单数量。
|
||||||
|
|
||||||
|
返回 None 表示请求没有携带成品行;返回 0 也表示用户明确填写了 0,
|
||||||
|
不能再回退到旧的正式下单数量。"""
|
||||||
|
target_code = f'成品_{product_code}'
|
||||||
|
for item in items or []:
|
||||||
|
if not isinstance(item, dict) or item.get('material_code') != target_code:
|
||||||
|
continue
|
||||||
|
value = item.get('actual_quantity')
|
||||||
|
if value in (None, ''):
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return max(0, int(float(value)))
|
||||||
|
except (TypeError, ValueError):
|
||||||
|
return 0
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
def _normalize_pending_bom_items(items):
|
def _normalize_pending_bom_items(items):
|
||||||
"""规范化待下单 BOM,同时保留下单前/后包材数据。"""
|
"""规范化待下单 BOM,同时保留下单前/后包材数据。"""
|
||||||
normalized = []
|
normalized = []
|
||||||
@ -1076,7 +1095,9 @@ def purchase_update_pending():
|
|||||||
bom_items = data.get('bom_items')
|
bom_items = data.get('bom_items')
|
||||||
if bom_items is not None:
|
if bom_items is not None:
|
||||||
import json
|
import json
|
||||||
bom_str = json.dumps(bom_items, ensure_ascii=False)
|
normalized_bom_items = _normalize_pending_bom_items(bom_items)
|
||||||
|
product_actual_qty = _extract_product_actual_qty(normalized_bom_items, product_code)
|
||||||
|
bom_str = json.dumps(normalized_bom_items, ensure_ascii=False)
|
||||||
try:
|
try:
|
||||||
old_bom_items = json.loads(current.get('bom_items') or '[]')
|
old_bom_items = json.loads(current.get('bom_items') or '[]')
|
||||||
except (TypeError, ValueError):
|
except (TypeError, ValueError):
|
||||||
@ -1141,9 +1162,15 @@ def purchase_update_pending():
|
|||||||
changes.append(
|
changes.append(
|
||||||
f'{display_code} {label}:{old_display} → {new_display}'
|
f'{display_code} {label}:{old_display} → {new_display}'
|
||||||
)
|
)
|
||||||
|
if product_actual_qty is None:
|
||||||
query("UPDATE pending_orders SET bom_items=%s WHERE product_code=%s AND status='pending'", (bom_str, product_code))
|
query("UPDATE pending_orders SET bom_items=%s WHERE product_code=%s AND status='pending'", (bom_str, product_code))
|
||||||
|
else:
|
||||||
|
old_formal_qty = int((current.get('formal_order_qty') or 0))
|
||||||
|
if old_formal_qty != product_actual_qty:
|
||||||
|
changes.append(f'成品实际下单数量:{old_formal_qty} → {product_actual_qty}')
|
||||||
|
query("UPDATE pending_orders SET formal_order_qty=%s, bom_items=%s WHERE product_code=%s AND status='pending'", (product_actual_qty, bom_str, product_code))
|
||||||
# 待下单详情中的“下单前包材”与计划预测共用 bom_packaging 真源,保存时同步更新。
|
# 待下单详情中的“下单前包材”与计划预测共用 bom_packaging 真源,保存时同步更新。
|
||||||
_sync_pending_pre_packaging(product_code, _normalize_pending_bom_items(bom_items))
|
_sync_pending_pre_packaging(product_code, normalized_bom_items)
|
||||||
|
|
||||||
detail = f'更新 {product_code}'
|
detail = f'更新 {product_code}'
|
||||||
if changes:
|
if changes:
|
||||||
@ -1227,6 +1254,13 @@ def purchase_confirm_order():
|
|||||||
if not rows:
|
if not rows:
|
||||||
continue
|
continue
|
||||||
row = rows[0]
|
row = rows[0]
|
||||||
|
request_bom = bom_data_map.get(pc)
|
||||||
|
request_product_actual_qty = _extract_product_actual_qty(request_bom, pc)
|
||||||
|
if request_product_actual_qty is not None:
|
||||||
|
# 用户在下单信息弹窗中明确填写的成品实际下单数量优先于旧快照。
|
||||||
|
# 这一步必须发生在生成国内/海外订单之前,否则订单和下单备份会沿用旧数量。
|
||||||
|
row['formal_order_qty'] = request_product_actual_qty
|
||||||
|
query("UPDATE pending_orders SET formal_order_qty=%s WHERE id=%s AND status='pending'", (request_product_actual_qty, row['id']))
|
||||||
|
|
||||||
# 1.5 原子抢占:先标记为已下单,防止重复点击/并发请求重复生成订单。
|
# 1.5 原子抢占:先标记为已下单,防止重复点击/并发请求重复生成订单。
|
||||||
# 只有抢占成功(status 从 pending 变为 ordered)的请求才会继续插入订单,
|
# 只有抢占成功(status 从 pending 变为 ordered)的请求才会继续插入订单,
|
||||||
@ -1344,7 +1378,6 @@ def purchase_confirm_order():
|
|||||||
|
|
||||||
# 当前确认下单请求携带的 bom_data_map 是本次弹窗的最新数据源;
|
# 当前确认下单请求携带的 bom_data_map 是本次弹窗的最新数据源;
|
||||||
# 未携带时使用待下单记录中已保存的 BOM,确保刷新页面后确认下单仍能带上包材数量和备注。
|
# 未携带时使用待下单记录中已保存的 BOM,确保刷新页面后确认下单仍能带上包材数量和备注。
|
||||||
request_bom = bom_data_map.get(pc)
|
|
||||||
bom_list = _select_confirm_bom(request_bom, db_bom_list, [])
|
bom_list = _select_confirm_bom(request_bom, db_bom_list, [])
|
||||||
if not bom_list:
|
if not bom_list:
|
||||||
# 待下单记录尚未保存 BOM 时,才回退到当前产品 BOM 基础清单。
|
# 待下单记录尚未保存 BOM 时,才回退到当前产品 BOM 基础清单。
|
||||||
@ -1357,11 +1390,9 @@ def purchase_confirm_order():
|
|||||||
bom_list = _normalize_backup_bom(bom_list)
|
bom_list = _normalize_backup_bom(bom_list)
|
||||||
# 成品实际下单数量优先取下单信息弹窗中的“成品实际下单数量”,
|
# 成品实际下单数量优先取下单信息弹窗中的“成品实际下单数量”,
|
||||||
# 未填写时才回退到待下单记录的正式下单数量。
|
# 未填写时才回退到待下单记录的正式下单数量。
|
||||||
|
product_actual_qty = _extract_product_actual_qty(bom_list, pc)
|
||||||
|
if product_actual_qty is None:
|
||||||
product_actual_qty = int(qty or 0)
|
product_actual_qty = int(qty or 0)
|
||||||
for bm in bom_list:
|
|
||||||
if bm.get('material_code') == f'成品_{pc}' and bm.get('actual_quantity') is not None:
|
|
||||||
product_actual_qty = int(bm.get('actual_quantity') or 0)
|
|
||||||
break
|
|
||||||
logging.warning(f'[SYNC_PACKAGING] pc={pc}, formal_qty={qty}, product_actual_qty={product_actual_qty}, bom_list_len={len(bom_list)}')
|
logging.warning(f'[SYNC_PACKAGING] pc={pc}, formal_qty={qty}, product_actual_qty={product_actual_qty}, bom_list_len={len(bom_list)}')
|
||||||
|
|
||||||
# BOM用量以 bom 表为权威来源:前端 bom_items 里的 quantity 可能被「实际下单数量」污染,
|
# BOM用量以 bom 表为权威来源:前端 bom_items 里的 quantity 可能被「实际下单数量」污染,
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user