-- ============================================================================= -- 不良品在管台账:补「原库位 / 批次(序列号)」快照列 -- -- 背景 -- trans_defective_goods 只冗余了 material_name / spec_model,却没有存库位与 -- 批次。于是报废台账页、报废申请单明细上,凡是不良品来源的行,库位与 -- 批次一律显示 "-" —— 而它们的源库存行往往**还在**,本该取得到。 -- -- 应用层原先的处置是在 _resolve_materials 与 DefectiveScrapAdapter.snapshot -- 里直接硬编码空串,注释理由是「源库存行可能已被物理删除」。这个担忧对 -- material_name/spec_model 成立(所以它们有冗余快照),但库位/批次**没有** -- 快照列,一律置空等于放弃了「源行还在」这个绝大多数情形。 -- -- 本次改动(A+B) -- A. 应用层改为「快照优先 + 回查源库存行兜底」(本次只加列,逻辑在代码侧) -- B. 本文件补两列,把「源行可能被删」这个担忧真正解决掉:退回那一刻 -- stock_row 已 with_for_update() 锁在手里,库位/批次随手可取, -- 落库后即使源行日后被物理删除,报表依然有值。 -- -- 口径(2026-09-23 与业务确认) -- 存的是「**原库位**」—— 这批货最初在哪,而不是坏件当前所在的不良品区。 -- 与三张库存表来源的报废记录口径一致。不良品区的位置本系统未记录。 -- -- ★ batch_number 列对 stock_product 来源存的是 serial_number: -- 成品表没有 batch_number 列(见 information_schema),取值口径沿用 -- 应用层的 COALESCE(batch_number, serial_number),与 _from_stock 一致。 -- 故本列语义为「批次或序列号」,与前端列名「批次/序列号」对齐。 -- -- 安全性:纯追加式 DDL,无 NOT NULL、无 DEFAULT,既有行取值 NULL。 -- 幂等:IF NOT EXISTS + 带 WHERE 的 UPDATE,可重复执行。 -- -- 执行 -- docker exec -i inventory_db psql -U test -d inventory_system < 本文件 -- ============================================================================= BEGIN; -- --------------------------------------------------------------------------- -- 1) 快照列(长度与三张库存表对齐,均为 varchar(100)) -- --------------------------------------------------------------------------- ALTER TABLE trans_defective_goods ADD COLUMN IF NOT EXISTS warehouse_location varchar(100), ADD COLUMN IF NOT EXISTS batch_number varchar(100); COMMENT ON COLUMN trans_defective_goods.warehouse_location IS '原库位快照(退回时取自源库存行)。源行被物理删除时此处仍有值,故报表优先用它'; COMMENT ON COLUMN trans_defective_goods.batch_number IS '批次/序列号快照(退回时取自源库存行的 batch_number,成品表缺失时取 serial_number)'; -- --------------------------------------------------------------------------- -- 2) 存量回填:按 source_table + stock_id 回查源库存行 -- 三张表分别 UPDATE(多态关联,无法用一条语句表达)。 -- WHERE warehouse_location IS NULL 保证幂等,且不覆盖已回填过的值。 -- -- 回填不到的(源行已被物理删除)保持 NULL —— 刻意不写成空串, -- 这样下面的核对能数出「真正取不到」的行数,与「已确认无库位」区分开。 -- --------------------------------------------------------------------------- UPDATE trans_defective_goods g SET warehouse_location = COALESCE(b.warehouse_location, ''), batch_number = COALESCE(NULLIF(b.batch_number, ''), b.serial_number, '') FROM stock_buy b WHERE g.source_table = 'stock_buy' AND g.stock_id = b.id AND g.warehouse_location IS NULL; UPDATE trans_defective_goods g SET warehouse_location = COALESCE(s.warehouse_location, ''), batch_number = COALESCE(NULLIF(s.batch_number, ''), s.serial_number, '') FROM stock_semi s WHERE g.source_table = 'stock_semi' AND g.stock_id = s.id AND g.warehouse_location IS NULL; -- ★ 成品表无 batch_number 列,只取 serial_number UPDATE trans_defective_goods g SET warehouse_location = COALESCE(p.warehouse_location, ''), batch_number = COALESCE(p.serial_number, '') FROM stock_product p WHERE g.source_table = 'stock_product' AND g.stock_id = p.id AND g.warehouse_location IS NULL; COMMIT; -- ============================================================================= -- 执行后核对 -- ============================================================================= SELECT '=== 1) 回填结果:已回填 / 源行已删取不到 / 无需回填 ===' AS "核对项"; SELECT count(*) FILTER (WHERE warehouse_location IS NOT NULL) AS 已回填, count(*) FILTER (WHERE warehouse_location IS NULL) AS 源行已删_取不到, count(*) AS 台账总行数 FROM trans_defective_goods; SELECT '=== 2) ★ 源行已删的明细(取不到库位属预期,应逐条确认是否为真)===' AS "核对项"; SELECT id, source_table, stock_id, sku, status FROM trans_defective_goods WHERE warehouse_location IS NULL ORDER BY id; -- ============================================================================= -- 回滚段(仅撤销本迁移;回填值与「原本就是空」无法区分) -- ============================================================================= -- BEGIN; -- ALTER TABLE trans_defective_goods DROP COLUMN IF EXISTS warehouse_location; -- ALTER TABLE trans_defective_goods DROP COLUMN IF EXISTS batch_number; -- COMMIT;