feat(stocktake): 跨公司扫码给精准报错,0 库存盘盈在明细中可见
【跨公司拦截】 get_stock_info 带 company_name 过滤后,扫到别家公司的货会直接查不到, 返回 404「未找到物料」—— 与「条码根本不存在」完全无法区分,现场人员 既不知道是扫错了还是扫了别人的货。 新增 find_stock_owner_company():不施加公司过滤地全局查条码归属; get_stock_info 未命中时由 _classify_missing_stock() 复核,区分两种情形: · 条码存在但属于别家公司 → 403「条码 [X] 属于【LICA】,请勿跨公司盘点」 · 条码不存在 → 404「未找到该物料库存: X」 该方案的前提「SKU/条码全系统唯一」已核验成立:三张库存表的表内重复、 跨公司重复、跨表重复六项检查均为 0,故归属至多命中一条,无歧义。 /scan 与 /draft/add 两个入口均已接入。 【0 库存盘盈可见性】 merged-list 的 union_sql 原本严格要求 stock_quantity > 0,导致账面为 0、 但已被扫入的盘盈物料不出现在明细抽屉里,而 total_scanned 却把它计入 「已盘」—— 工人看到已盘 +1 却在明细里找不到,以为系统丢了数据。 现改为 stock_quantity > 0 OR id IN (本会话该表的草稿 stock_id)。 条件严格限定在**本会话**的草稿,不会把全库 0 库存物料都放出来。 实测: IRIS 扫 LICA 条码 → 403「条码 [0000002097] 属于【LICA】,请勿跨公司盘点」 扫不存在条码 → 404「未找到该物料库存: NOPE-99999」 已扫的 0 库存物料 → 明细可见(账面 0 / 实盘 5 / 差异 +5 / 库位 Y3/3/3/4) 未扫的 0 库存物料 → 明细 0 行(范围正确)
This commit is contained in:
@ -261,6 +261,50 @@ def _get_session_scope(session_id):
|
||||
return STOCKTAKE_SCOPE_ACTIVE, list(locs)
|
||||
|
||||
|
||||
def find_stock_owner_company(uuid_or_barcode):
|
||||
"""全局查找该条码/SKU 属于哪个公司 —— **不施加任何公司过滤**。
|
||||
|
||||
只在 get_stock_info 未命中时调用,用来区分两种「查不到」:
|
||||
· 条码根本不存在 → 让工人核对是否扫错 / 敲错
|
||||
· 条码存在但属于别家公司 → 明确告知跨公司,而不是笼统的「未找到物料」
|
||||
|
||||
前提:SKU / 条码在全系统全局唯一。已核验三张库存表「表内重复、跨公司重复、
|
||||
跨表重复」六项检查均为 0,因此这里至多命中一条,不存在归属歧义。
|
||||
|
||||
:return: 公司名;查不到则 None
|
||||
"""
|
||||
code = str(uuid_or_barcode).strip()
|
||||
if not code:
|
||||
return None
|
||||
|
||||
checks = [
|
||||
(StockProduct, lambda m, c: db.or_(m.barcode == c, m.sku == c, m.serial_number == c)),
|
||||
(StockSemi, lambda m, c: db.or_(m.barcode == c, m.sku == c, m.serial_number == c)),
|
||||
(StockBuy, lambda m, c: db.or_(m.barcode == c, m.sku == c)),
|
||||
]
|
||||
for model, cond_fn in checks:
|
||||
if not model:
|
||||
continue
|
||||
row = db.session.query(MaterialBase.company_name).join(
|
||||
model, model.base_id == MaterialBase.id
|
||||
).filter(cond_fn(model, code)).first()
|
||||
if row and row[0]:
|
||||
return row[0]
|
||||
return None
|
||||
|
||||
|
||||
def _classify_missing_stock(code, company_name):
|
||||
"""把「查不到库存」细分为「不存在」与「跨公司」。
|
||||
|
||||
:return: (msg, http_status) —— 调用方按自己的响应结构包装
|
||||
"""
|
||||
if company_name and company_name != '__NO_COMPANY__':
|
||||
owner = find_stock_owner_company(code)
|
||||
if owner and owner != company_name:
|
||||
return f'条码 [{code}] 属于【{owner}】,请勿跨公司盘点', 403
|
||||
return f'未找到该物料库存: {code}', 404
|
||||
|
||||
|
||||
def get_stock_info(uuid_or_barcode, company_name=None):
|
||||
"""
|
||||
根据 uuid 或 barcode 查询库存信息(★ 精确匹配优先,性能与准确性兼顾)
|
||||
@ -631,9 +675,12 @@ def scan_stock_by_barcode():
|
||||
if not barcode:
|
||||
return jsonify({'code': 400, 'msg': 'barcode 不能为空'}), 400
|
||||
|
||||
item, source_table, stock_id = get_stock_info(barcode, get_current_company_filter())
|
||||
company_limit = get_current_company_filter()
|
||||
item, source_table, stock_id = get_stock_info(barcode, company_limit)
|
||||
if not item:
|
||||
return jsonify({'code': 404, 'msg': f'未找到该物料库存: {barcode}'}), 404
|
||||
# 区分「条码不存在」与「属于别家公司」,给现场人员可执行的提示
|
||||
msg, status = _classify_missing_stock(barcode, company_limit)
|
||||
return jsonify({'code': status, 'msg': msg}), status
|
||||
|
||||
d = item.to_dict()
|
||||
d['stock_type'] = source_table.replace('stock_', '')
|
||||
@ -796,7 +843,9 @@ def add_draft():
|
||||
# 获取库存信息
|
||||
item, source_table, stock_id = get_stock_info(uuid, company_name)
|
||||
if not item:
|
||||
return jsonify({"message": "未找到对应的库存记录"}), 404
|
||||
# 区分「条码不存在」与「属于别家公司」
|
||||
msg, status = _classify_missing_stock(uuid, company_name)
|
||||
return jsonify({"message": msg}), status
|
||||
|
||||
# 账面总库存
|
||||
stock_qty = float(item.stock_quantity) if item.stock_quantity else 0
|
||||
@ -1974,18 +2023,31 @@ def get_draft_merged_list():
|
||||
company_cond_bare = ' AND company_name = :company' if company_name is not None else ''
|
||||
|
||||
# ── 公共 CTE / 子查询片段 ──
|
||||
# ★ 0 库存盘盈可见性:账面为 0、但本会话已被扫入的物料也要进明细列表。
|
||||
# 否则工人扫到货架上账面为 0 的实物(盘盈)时,「已盘」计数会 +1,
|
||||
# 明细里却找不到这一行,看起来像系统丢了数据。
|
||||
# 注意条件必须限定在**本会话**的草稿,不能是库容里存在过草稿就放行。
|
||||
union_sql = """
|
||||
SELECT id, 'stock_buy' AS source_table, sku,
|
||||
stock_quantity AS stock_qty, warehouse_location, base_id
|
||||
FROM stock_buy WHERE stock_quantity > 0
|
||||
FROM stock_buy
|
||||
WHERE stock_quantity > 0
|
||||
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
|
||||
WHERE sd.source_table = 'stock_buy' AND sd.session_id = :sid)
|
||||
UNION ALL
|
||||
SELECT id, 'stock_semi', sku,
|
||||
stock_quantity, warehouse_location, base_id
|
||||
FROM stock_semi WHERE stock_quantity > 0
|
||||
FROM stock_semi
|
||||
WHERE stock_quantity > 0
|
||||
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
|
||||
WHERE sd.source_table = 'stock_semi' AND sd.session_id = :sid)
|
||||
UNION ALL
|
||||
SELECT id, 'stock_product', sku,
|
||||
stock_quantity, warehouse_location, base_id
|
||||
FROM stock_product WHERE stock_quantity > 0
|
||||
FROM stock_product
|
||||
WHERE stock_quantity > 0
|
||||
OR id IN (SELECT sd.stock_id FROM stocktake_draft sd
|
||||
WHERE sd.source_table = 'stock_product' AND sd.session_id = :sid)
|
||||
"""
|
||||
|
||||
# ── 构建 WHERE 条件 ──
|
||||
|
||||
Reference in New Issue
Block a user