Files
KCGL/db_migrations/phase14_defective_goods_location_snapshot.sql
yueli c5356a27ed fix(scrap): 补上遗漏的 phase14 迁移(不良品报废的库位/批次快照)
问题:前面几个报废提交里,inventory-backend/app/models/transaction.py 已经
引用了 db_migrations/phase14_defective_goods_location_snapshot.sql(两处注释
指向它),但**该文件本身没被提交** —— 代码与迁移对不上,别人拉下来跑不起来。

内容:为 trans_defective_goods 增加 warehouse_location / batch_number 两列,
并从源库存行回填存量。

为什么需要这两列:报废台账页与报废申请单明细上,凡是不良品来源的行,库位与
批次此前一律显示 "-" —— 因为应用层硬编码了空串,理由是「源库存行可能已被
物理删除」。该担忧对 material_name/spec_model 成立(所以它们有冗余快照),
但库位/批次当时**没有**快照列,一律置空等于放弃了「源行还在」这个绝大多数
情形。补上快照后,退回那一刻 stock_row 已 with_for_update() 锁在手里,
库位/批次随手可取,落库后即使源行日后被删除,报表依然有值。

口径:存「原库位」(这批货最初在哪),与三张库存表来源的报废记录一致;
不良品区的位置本系统未记录。batch_number 对 stock_product 来源存的是
serial_number(成品表无 batch_number 列),与 scrap.py 的 _from_stock 对齐。

安全性:纯追加式 DDL,无 NOT NULL、无 DEFAULT,幂等可重复执行。
已在本机执行验证:存量 1 行全部回填,源行已删取不到 0 行。
2026-09-23 17:16:08 +08:00

109 lines
5.6 KiB
PL/PgSQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- =============================================================================
-- 不良品在管台账:补「原库位 / 批次(序列号)」快照列
--
-- 背景
-- 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;