Compare commits
2 Commits
e5a7cb2736
...
b8561f69f7
| Author | SHA1 | Date | |
|---|---|---|---|
| b8561f69f7 | |||
| f702a70fe5 |
@ -356,7 +356,7 @@ def verify_scanned(scanned_items, approved_items):
|
||||
return normalized
|
||||
|
||||
|
||||
def restore_then_deduct(scanned_items, approved_items):
|
||||
def restore_then_deduct(scanned_items, approved_items, deduct_stock=True):
|
||||
"""
|
||||
★ Phase 3 再平衡:先释放全部预占,再对实扫行扣减。
|
||||
|
||||
@ -366,12 +366,27 @@ def restore_then_deduct(scanned_items, approved_items):
|
||||
逻辑要分情况讨论、极易出错。统一走
|
||||
「全量释放 → 全量扣减」,路径单一,且与批次是否相同无关。
|
||||
|
||||
净效果:available -= 实扫量,stock -= 实扫量。
|
||||
参数
|
||||
----
|
||||
deduct_stock:
|
||||
True (默认,出库)—— 物品**永久离开**仓库,实物数与可用数同时扣。
|
||||
False(借库) —— 借出是**可逆的**(会归还),只冻结可用数,
|
||||
实物数不动。理由:
|
||||
· 归还只加 available(现有实现),若借出扣了 stock,
|
||||
一借一还后 stock 会永久少一份;
|
||||
· 借库转报废(scrap_borrow)在确认损失时扣 stock,
|
||||
其注释明确假设「可用库存已在借出时冻结」,
|
||||
若借出已扣 stock 会重复扣减。
|
||||
语义:stock = 账面实物(含借出未还),
|
||||
available = 实际可取用。
|
||||
|
||||
净效果:
|
||||
出库 —— available -= 实扫量,stock -= 实扫量
|
||||
借库 —— available -= 实扫量,stock 不变
|
||||
|
||||
并发与一致性说明:
|
||||
· 释放与扣减都在同一事务内,行级锁由 with_for_update 保证;
|
||||
· 释放后重新读取行对象,避免使用到已过期的内存快照;
|
||||
· stock_quantity 只在此处扣减一次(货真正离开仓库)。
|
||||
· 释放后重新读取行对象,避免使用到已过期的内存快照。
|
||||
"""
|
||||
# 1. 释放申请时锁定的全部批次(available_quantity 归还池子)
|
||||
release_reserved(approved_items)
|
||||
@ -443,14 +458,16 @@ def restore_then_deduct(scanned_items, approved_items):
|
||||
if not row:
|
||||
raise ValueError(f"库存记录已不存在({st}#{sid})")
|
||||
|
||||
stock = float(row.stock_quantity or 0)
|
||||
if qty > stock:
|
||||
raise ValueError(
|
||||
f"物料【{identity_label(stock_identity(row))}】该批次实物不足"
|
||||
f"(需 {qty},实剩 {stock}),无法出库"
|
||||
)
|
||||
if deduct_stock:
|
||||
stock = float(row.stock_quantity or 0)
|
||||
if qty > stock:
|
||||
raise ValueError(
|
||||
f"物料【{identity_label(stock_identity(row))}】该批次实物不足"
|
||||
f"(需 {qty},实剩 {stock}),无法出库"
|
||||
)
|
||||
row.stock_quantity = stock - qty
|
||||
|
||||
row.available_quantity = float(row.available_quantity or 0) - qty
|
||||
row.stock_quantity = stock - qty
|
||||
|
||||
|
||||
def _sum_available_for_identity(key):
|
||||
|
||||
@ -92,8 +92,14 @@ class TransService:
|
||||
# 借库申请阶段已预占具体批次;工人实扫的可能是同物料的另一批次。
|
||||
# 1. 校验实扫身份/数量未超批准范围(base_id 主键,允许换批次)
|
||||
# 2. 释放全部预占
|
||||
# 3. 对实扫批次扣减 available_quantity 与 stock_quantity
|
||||
# 3. 对实扫批次扣减 available_quantity(★ 不动 stock_quantity)
|
||||
# 下方主循环只写 TransBorrow 流水,不再重复扣库存。
|
||||
#
|
||||
# ★ deduct_stock=False:借出是**可逆的**(会归还),只冻结可用数。
|
||||
# 若此处扣了实物,会与两处现有实现冲突:
|
||||
# · 归还(process_return)只加 available —— 一借一还后 stock 永久少一份;
|
||||
# · 借库转报废(scrap_borrow)在确认损失时扣 stock,其注释明确假设
|
||||
# 「可用库存已在借出时冻结」—— 若借出已扣会重复扣减。
|
||||
# ==============================================================
|
||||
_scanned_for_check = [
|
||||
{'source_table': i.get('source_table'), 'stock_id': i.get('id'),
|
||||
@ -102,7 +108,7 @@ class TransService:
|
||||
]
|
||||
if _scanned_for_check:
|
||||
verify_scanned(_scanned_for_check, approved_items)
|
||||
restore_then_deduct(_scanned_for_check, approved_items)
|
||||
restore_then_deduct(_scanned_for_check, approved_items, deduct_stock=False)
|
||||
|
||||
# 累计本次扫码出库量(用于下方防线4的二次校验)
|
||||
dispatch_acc = {}
|
||||
|
||||
@ -239,7 +239,7 @@ const handleLogout = () => {
|
||||
<footer v-if="!isLoginPage" class="app-footer">
|
||||
<span class="version-tag">
|
||||
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
|
||||
当前版本:V3.76
|
||||
当前版本:V3.77
|
||||
</span>
|
||||
</footer>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user