1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
-- =============================================================================
-- 扁平 BOM + 工序统一领料 + 报工领料门禁 数据库迁移脚本
-- 适用版本:2026-08-12 需求(简化BOM配置 / 工艺路线按工序生成 / 领料下沉到工序报工)
-- 执行顺序:先执行本脚本(加字段 + 回填 + 数据检查),再发布后端代码。
-- 说明:
--   1. 本脚本不物理删除 technology_bom_structure / production_bom_structure 的
--      历史层级与数量列(parent_id / unit_quantity / demanded_quantity / unit),
--      仅停止使用其业务语义,保证历史订单与报表可回溯。
--   2. 第 3 部分是“数据检查”,需要人工确认后再决定是否执行对应的清理语句。
-- =============================================================================
 
-- -----------------------------------------------------------------------------
-- 1. 领料主记录增加工序工单归属:领料从订单级下沉到工序(工单)级
-- -----------------------------------------------------------------------------
ALTER TABLE production_order_pick
    ADD COLUMN production_operation_task_id BIGINT NULL COMMENT '生产工单id(工序任务),领料归属的工序执行单元' AFTER production_order_id;
 
CREATE INDEX idx_pop_operation_task ON production_order_pick (production_operation_task_id);
CREATE INDEX idx_pop_order_operation ON production_order_pick (production_order_id, technology_operation_id);
 
-- 回填历史领料的工单归属:
-- 只回填能够唯一确认的记录(同一条领料的流水里工单ID唯一)。
-- 无法唯一确认的历史数据保持为 NULL:它们仍会出现在订单领料汇总中,
-- 但不会被当作某道工序的领料,因此不会错误解锁工序报工。
UPDATE production_order_pick pop
    JOIN (SELECT pick_id,
                 MIN(production_operation_task_id) AS task_id
          FROM production_order_pick_record
          WHERE production_operation_task_id IS NOT NULL
          GROUP BY pick_id
          HAVING COUNT(DISTINCT production_operation_task_id) = 1) r
    ON r.pick_id = pop.id
SET pop.production_operation_task_id = r.task_id
WHERE pop.production_operation_task_id IS NULL;
 
-- -----------------------------------------------------------------------------
-- 2. 报工投入记录来源领料:保证同一笔领料只被一次报工归集,避免重复投入
-- -----------------------------------------------------------------------------
ALTER TABLE production_product_input
    ADD COLUMN production_order_pick_id BIGINT NULL COMMENT '来源领料记录id(production_order_pick.id)' AFTER product_model_id;
 
-- 唯一索引:MySQL 允许多个 NULL,历史投入记录(无来源领料)不受影响,
-- 新数据则保证一条领料不会被两条报工投入重复引用。
CREATE UNIQUE INDEX uk_ppi_production_order_pick ON production_product_input (production_order_pick_id);
 
-- -----------------------------------------------------------------------------
-- 3. 历史 BOM 数据检查与清理(先查,再由业务确认后执行清理)
-- -----------------------------------------------------------------------------
 
-- 3.1 检查:旧的 BOM 成品根节点(无父节点、无工序,且规格等于 BOM 成品规格)。
-- 这些行不是物料,新版本查询已将其过滤,建议确认后删除。
SELECT tbs.id,
       tbs.bom_id,
       tb.bom_no,
       tbs.product_model_id
FROM technology_bom_structure tbs
         JOIN technology_bom tb ON tbs.bom_id = tb.id
WHERE tbs.parent_id IS NULL
  AND tbs.operation_id IS NULL
  AND tbs.product_model_id = tb.product_model_id;
 
-- 3.1 清理(确认无误后执行)
-- DELETE tbs
-- FROM technology_bom_structure tbs
--          JOIN technology_bom tb ON tbs.bom_id = tb.id
-- WHERE tbs.parent_id IS NULL
--   AND tbs.operation_id IS NULL
--   AND tbs.product_model_id = tb.product_model_id;
 
-- 3.2 检查:缺少消耗工序的物料行。
-- 扁平 BOM 要求每个物料都必须指定消耗工序,否则该 BOM 无法生成工艺路线,
-- 必须在“产品结构详情”页面补齐,不能默认给一个工序。
SELECT tbs.id,
       tbs.bom_id,
       tb.bom_no,
       tbs.product_model_id
FROM technology_bom_structure tbs
         JOIN technology_bom tb ON tbs.bom_id = tb.id
WHERE tbs.operation_id IS NULL
  AND NOT (tbs.parent_id IS NULL AND tbs.product_model_id = tb.product_model_id);
 
-- 3.3 检查:同一 BOM 下重复的“物料规格 + 消耗工序”。
-- 新版本保存时会拒绝重复配置,历史重复数据需要先合并。
SELECT bom_id,
       product_model_id,
       operation_id,
       COUNT(*) AS duplicate_count
FROM technology_bom_structure
WHERE operation_id IS NOT NULL
GROUP BY bom_id, product_model_id, operation_id
HAVING COUNT(*) > 1;
 
-- 3.4 检查:历史多层 BOM 中曾作为“上一道工序产出”的中间节点。
-- 这些行原本代表自制半成品产出,不是需要领用的物料。
-- 扁平化后它们会变成可领用物料,需要业务确认是否删除。
SELECT child.id,
       child.bom_id,
       tb.bom_no,
       child.product_model_id,
       child.operation_id
FROM technology_bom_structure child
         JOIN technology_bom tb ON child.bom_id = tb.id
WHERE EXISTS (SELECT 1
              FROM technology_bom_structure sub
              WHERE sub.parent_id = child.id);
 
-- 3.5 可选:将历史层级降为一级(确认 3.1 / 3.4 处理完成后再执行)。
-- 新代码不再读取 parent_id 的业务语义,本步骤只是让数据与模型保持一致。
-- UPDATE technology_bom_structure SET parent_id = NULL WHERE parent_id IS NOT NULL;
 
-- 3.6 检查:历史订单 BOM 快照中的成品根节点(快照保持独立,默认不改写)。
-- 新版本领料建议已按“工序非空 + 规格不等于成品规格”过滤,历史快照可保持原样。
SELECT pbs.id,
       pbs.production_order_id,
       pbs.production_order_bom_id,
       pbs.product_model_id
FROM production_bom_structure pbs
         JOIN production_order_bom pob ON pbs.production_order_bom_id = pob.id
WHERE pbs.parent_id IS NULL
  AND pbs.technology_operation_id IS NULL
  AND pbs.product_model_id = pob.product_model_id;