-- ============================================================================= -- 扁平 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;