Files
geMoldInsight/docs/MOLD_ERP_ANALYSIS_REPORT.md
T
2026-03-25 23:59:29 +08:00

16 KiB
Raw Blame History

模具制造进销存核心模块分析报告(代码基线:geMoldInsight)

0. 范围与术语映射

  • 客户订单(Customer Order):本仓库实现为 SalesOrder(销售订单),其业务语义更贴近“模具订单/按单生产订单”。对应表:sales_orders、sales_order_items。
  • 物料采购订单(Purchase Order):本仓库实现为 PurchaseOrder。对应表:purchase_orders、purchase_order_items。
  • 物料清单(BOM):本仓库实现为 product_materials(ProductMaterial),以“成品(模具)→ 物料”的多行明细表达。
  • 成品库存:系统数据模型存在 products.item_type="finished",但库存管理接口仅面向 item_type="material",且“交付”不产生任何成品入库/出库流水;因此可视为“零成品库存”设计。

1. 业务流程梳理

1.1 端到端流程图(客户下单→交付)

flowchart LR
  A[客户下单\nSalesOrder: POST /api/sales-orders] --> B{BOM 是否存在?}
  B -- 否 --> B1[订单创建成功\nproduction_status=bom_missing\n不扣料]
  B -- 是 --> C[计算物料需求\n按 BOM×订单数量×(1+loss_rate)]
  C --> D{默认仓库库存是否足够?}
  D -- 否 --> D1[创建失败/回滚\n提示缺料明细]
  D -- 是 --> E[创建时自动扣减物料\nInventory.quantity-=需求\nStockMovement=issue_to_production]
  B1 --> F[物料需求计划\nGET /api/sales-orders/{id}/production-plan]
  E --> F
  F --> G[采购下单(手工)\nPurchaseOrder: POST /api/purchase-orders]
  G --> H[到货入库\nPOST /api/purchase-orders/{id}/receive\nStockMovement=purchase_in]
  H --> I[生产领料(可选)\nPOST /api/sales-orders/{id}/issue-materials]
  I --> J[交付客户\nPATCH /api/sales-orders/{id}/status=delivered]

1.2 节点级状态、角色、输入输出、异常与回退

节点 负责角色 单据/状态 输入 输出 异常分支 回退机制
客户下单 销售/项目 SalesOrder.status=manufacturing(创建时强制) customer_id、成品(模具)明细、交期 sales_orders + sales_order_items 默认仓库未配置;BOM 存在但库存不足(创建失败) 事务回滚;不会产生库存/流水
物料需求计算 计划/工艺 production-plan 订单明细 + BOM 需求汇总(含 loss_rate)+ 缺口 BOM 缺失(返回空计划) 无(纯查询)
采购申请 采购 未实现专用单据 需求缺口 采购单(手工创建) 无 无
供应商采购 采购 PurchaseOrder.status=draft supplier_id + 物料行 purchase_orders + items 物料不是 material;cost_price<=0 事务回滚
到货质检 质检 未实现专用质检单 到货信息 无 无 无
到货入库 仓库 PurchaseOrder.status=partial_received/received 入库仓库、入库数量 Inventory.quantity 增加;StockMovement=purchase_in;received_quantity 累加 入库超收;仓库不存在/停用 事务回滚
模具生产/领料 生产/仓库 production_status=material_issued 发料仓库 Inventory.quantity 扣减;StockMovement=issue_to_production BOM 缺失;库存不足;已自动扣料禁止重复领料 若后续“修改/删除订单”,会回补已扣料(return_from_production)
交付客户 销售/项目 status=delivered order_id status 更新 状态值非法 无

关键落地差异(与“采购申请/到货质检/生产工序/交付签收”相比):

  • 采购申请、到货质检、生产工序、交付签收均未建模为独立单据;当前系统以“库存流水 + 订单状态”覆盖部分过程。

2. 数据模型与字段审计

2.1 核心表字段、主外键关系、索引概览(以 ORM 为准)

以下字段来源于 database.py。

客户订单(SalesOrder / 模具订单)

  • 表:sales_orders
    • PK:id
    • 关键字段:order_no(unique,index)、customer_id(FK,index)、status、production_status(index)、production_no(index)、planned_material_cost、actual_material_cost、total_amount、received_amount、delivery_date、operator_id(FK)
  • 表:sales_order_items
    • PK:id
    • FK:order_id(→sales_orders.id)、product_id(→products.id)
    • 关键字段:quantity、delivered_quantity、unit_price、amount

物料清单(BOM)

  • 表:product_materials
    • PK:id
    • FK:finished_product_id(→products.id,index)、material_product_id(→products.id,index)
    • 关键字段:quantity、loss_rate
    • 约束:唯一约束 uq_product_material_unique(finished_product_id, material_product_id)

采购订单(PurchaseOrder)

  • 表:purchase_orders
    • PK:id
    • 关键字段:order_no(unique,index)、supplier_id(FK,index)、status、total_amount、paid_amount、expected_date、operator_id(FK)
  • 表:purchase_order_items
    • PK:id
    • FK:order_id(→purchase_orders.id)、product_id(→products.id)
    • 关键字段:quantity、received_quantity、unit_price、amount

供应商/客户主数据

  • 表:suppliers:code(unique,index)、name、is_active
  • 表:customers:code(unique,index)、name、credit_limit

到货明细表(现状映射)

系统未实现独立“到货单/质检单”表:

  • 到货数量沉淀在 purchase_order_items.received_quantity
  • 到货批次/明细流水沉淀在 stock_movements(其中 movement_type="purchase_in"、reference_type="purchase_order")

2.2 问题验证与整改清单(以代码检出为依据)

A. 缺失约束/索引(高优先)

  • inventory 缺少数据库层唯一约束 (product_id, warehouse_id):接口层有重复校验,但并发下可能插入重复行。
  • purchase_order_items.order_id / product_id、sales_order_items.order_id / product_id 缺少索引:高数据量下会拖慢订单详情/联动查询。
  • stock_movements.warehouse_id、stock_movements(reference_type, reference_id) 缺少索引:对“按单追溯”与仓库流水分页不友好。
  • 修复脚本:见 scripts/db/002_indexes_and_constraints.sql。

B. 枚举值不一致(中高优先)

  • sales_orders.status:
    • DB 默认 draft
    • 创建/更新强制 manufacturing
    • 发料接口可能写入 pending(但状态更新接口不允许 pending)
  • sales_orders.production_status:
    • DB 默认 not_started
    • 业务中出现 bom_missing/material_issued/completed,但缺少统一枚举约束
  • 数据修复:pending→manufacturing 示例脚本见 scripts/db/003_data_fixups.sql。

C. 金额精度风险(高优先)

  • 金额/单价字段大量使用 Float(IEEE 754),在财务口径下会出现精度丢失与累计误差。
  • 建议将以下字段迁移为 NUMERIC(18,4) 或 NUMERIC(18,2):
    • products.cost_price/sale_price
    • purchase_orders.total_amount/paid_amount
    • purchase_order_items.unit_price/amount
    • sales_orders.planned_material_cost/actual_material_cost/total_amount/received_amount
    • sales_order_items.unit_price/amount
    • stock_movements.unit_price/total_amount

D. 冗余字段(中优先)

  • sales_orders.received_amount 与 purchase_orders.paid_amount 可能与财务分摊表(finance_allocations)重复表达;若以财务分摊为准,应明确哪一方为“源数据”。

3. 业务规则验证(以当前实现为准)

3.1 新增模具订单是否自动触发物料需求计算

  • 结论:会触发“物料需求计算”,并在满足条件时会进一步触发“自动扣料”。
  • 证据:
    • 创建销售单后调用 _issue_materials_for_order_creation(),内部调用 _build_material_plan() 进行需求汇总,并按默认仓库扣减库存(issue_to_production)。
    • 需求计算考虑 loss_rate;不考虑安全库存、在途量。

覆盖情况:

  • 损耗率:已支持(qty * (1 + loss_rate))。
  • 安全库存:未支持(products.min_stock 仅用于低库存查询,不参与需求计算)。
  • 在途量:未支持(系统无“采购在途数量”的统一口径,需通过采购单未收数量推导,但当前逻辑未做)。

3.2 修改模具订单对已生成采购订单的联动策略

  • 结论:当前系统不存在“客户订单↔采购订单”的显式关联,因此无法实现联动策略与版本控制。
  • 现状:
    • 修改销售单会回补该销售单曾扣减的物料(按 stock_movements 追溯),然后删除旧明细、重建明细、重新计算并扣料。
    • 采购单创建完全独立,未记录来源销售单/需求行。
  • 建议(整改方向):
    • 增加 sales_order_material_demands(需求行)并记录来源、版本号;采购单行引用需求行;修改订单时按版本触发“采购重确认”。
    • 为 sales_orders 增加 row_version(int/bigint)乐观锁;更新时带版本号校验。

3.3 禁止对已交付模具订单进行任何字段修改(双重约束)

  • 应用层:已实现(PUT/DELETE/PATCH status/issue-materials)对 status="delivered" 的拒绝。
  • 数据库层(PostgreSQL):提供触发器脚本,禁止更新/删除订单头与增删改订单明细。
    • 脚本:scripts/db/001_freeze_delivered_sales_orders.sql

4. 库存与成本逻辑审查

4.1 库存扣减策略

  • 到货入库:purchase-orders/{id}/receive 会实时写入 Inventory.quantity += receive_quantity,并记 StockMovement=purchase_in。
  • 生产领料:
    • 创建模具订单时,若 BOM 存在且库存充足,会实时扣减库存并记 StockMovement=issue_to_production。
    • 也支持按单领料接口(若未自动扣料)。
  • 结论:系统是“实时更新库存现存量”,不是“到货时更新快照”。

4.2 成本核算方式与附加成本

  • 现状:无移动加权/FIFO/标准成本核算引擎。
    • 采购单明细单价强制取 products.cost_price;入库流水记录 unit_price,但不会更新 products.cost_price 的移动平均。
    • 发料成本按 products.cost_price 估值,不含运费、关税、质检费等分摊。
  • 建议:
    • 增加“入库批次成本层”或“库存成本层”表以支持 FIFO/移动加权;
    • 增加 purchase_landed_costs(关税/运费/质检费)并按行/金额比例分摊到入库批次。

4.3 “零成品库存”证明与对月结影响

证明要点(以代码行为为准):

  • 库存查询接口仅返回物料(item_type=="material"),成品库存不在日常库存口径。
  • 交付(status=delivered)仅更新订单状态,不产生成品入库/出库、也没有销售出库流水。
  • 系统未实现销售退货/成品出库/成品调拨等模块。

对财务月结影响(需补齐口径):

  • 目前可获得的成本数据是“发料成本(按物料成本价)”,更接近制造费用的材料消耗口径;
  • 若要形成完整月结(在制/完工/成本结转),需补齐工序报工、完工入库(或完工确认)、制造费用分摊与结转规则。

5. 接口与集成盘点

  • 对外系统集成(ERP/财务/供应商/客户门户):当前代码未发现任何外部同步实现(无外部 HTTP client、无 webhook、无 MQ、无定时同步、无文件交换)。
  • 系统对外提供接口:FastAPI REST(HTTP/JSON),前缀 /api。

建议交付模板(用于后续对接):

  • 《接口清单》:接口名、协议、调用方、频率、数据量、重试/幂等键(例如 order_no)。
  • 《字段映射表》:源字段、目标字段、单位/币种换算、枚举映射、空值策略。

6. 权限与审计

6.1 RBAC 现状与推荐矩阵

现状:

  • 仅实现“管理员 is_superuser”级别拦截;大部分写接口只要求登录。
  • 数据模型已存在 Role/Permission 以及 has_permission(),但未在业务路由中落地。

推荐矩阵(目标态,需用 permission_code 落地):

角色 客户订单 采购订单 库存/流水 主数据(产品/客户/供应商) 财务 用户/角色
销售 C/R/U R R R R -
采购 R C/R/U R R R -
仓库 R R C/R/U R - -
财务 R R R R C/R/U -
管理员 C/R/U/D C/R/U/D C/R/U/D C/R/U/D C/R/U/D C/R/U/D

按“表级 CRUD”口径(用于落地 RBAC 与审计范围界定):

表 销售 采购 仓库 财务 管理员
customers R R R R C/R/U/D
suppliers R R R R C/R/U/D
products R R R R C/R/U/D
product_materials R R R R C/R/U/D
sales_orders / sales_order_items C/R/U R R R C/R/U/D
purchase_orders / purchase_order_items R C/R/U R R C/R/U/D
inventory R R C/R/U R C/R/U/D
stock_movements R R C/R R C/R/U/D

6.2 RLS(行级安全)检查与建议

  • 现状:订单/进销存未体现“事业部/组织”字段,数据库未启用 RLS;部分接口甚至未鉴权(与业务无关的历史记录接口)。
  • 建议:
    • 增加 org_id 字段至订单、主数据、库存等关键表;
    • Postgres 层启用 RLS,并通过应用设置 SET app.current_org_id = ... 绑定会话变量实现隔离。
    • 参考脚本:scripts/db/011_rls_template.sql

6.3 操作日志 180 天可追溯

  • 现状:存在 user_activities 与 system_logs 表,但缺少统一落库策略与留存/归档脚本。
  • 建议:
    • 关键操作(登录、下单、收货、领料、交付、权限变更)落库记录 old/new、操作人、时间、IP、UA;
    • 增加分区表或归档表,并提供 180 天归档/清理策略。
    • 参考脚本:scripts/db/020_audit_trail.sql(依赖应用层设置会话变量 current_user_id/ip/ua)

7. 性能与扩展性评估(方向性建议)

7.1 千万级订单量分库分表(建议)

  • 优先建议:先做 PostgreSQL 原生分区(比跨库路由成本低)
    • 拆分键:sales_orders.order_date / purchase_orders.order_date 按月 Range 分区
    • 历史迁移:INSERT INTO ... SELECT ... WHERE order_date < ...,并校验行数/校验和
    • 灰度切换:读写双写→读切换→写切换→下线旧表
  • 若必须分库:按 org_id + 时间 组合路由(hash(org_id)→库,时间→表)
  • 分区模板脚本:scripts/db/010_partitioning_template.sql

7.2 慢 SQL 风险点与优化脚本

  • 高风险点:
    • 订单详情按 order_id 读取明细、追溯库存流水、汇总库存,若缺少索引会退化为全表扫。
    • BOM 需求计算需要避免 N+1(已在 _build_material_plan 做批量化)。
  • 索引脚本:见 scripts/db/002_indexes_and_constraints.sql。

7.3 并发下新增/修改模具订单锁冲突

风险源:

  • 并发创建订单并同时扣减同一物料库存,会竞争同一 inventory 行。
  • 并发修改订单会先回补再扣减,冲突窗口更大。

建议:

  • 在扣减库存时采用“原子更新”语句:UPDATE inventory SET quantity = quantity - :qty WHERE id=:id AND quantity >= :qty,并检查 affected_rows,实现乐观并发控制;
  • 或引入 row_version(乐观锁)到 inventory/sales_orders。

8. 测试与验收标准(仓库内已落地的最小集合)

  • 已新增接口自动化用例(示例,覆盖“交付后冻结”核心规则):
    • tests/test_sales_order_delivered_freeze.py
    • tests/test_api_inventory_orders.py(参数化用例,覆盖正常/异常/边界/幂等与约束)
  • 建议在 CI 中运行:
    • pytest -q
    • 将 scripts/db/*.sql 纳入数据库初始化后的约束/索引校验步骤

9. 交付物结构建议

  • 报告(建议以 Markdown→Word/PDF 转换)
    • docs/MOLD_ERP_ANALYSIS_REPORT.md
    • docs/UAT_CHECKLIST.md
    • docs/INTERFACE_INTEGRATION_CATALOG_TEMPLATE.md
    • docs/CONFLUENCE_ARCHIVE_STRUCTURE.md
  • 可执行脚本
    • scripts/db/001_freeze_delivered_sales_orders.sql
    • scripts/db/002_indexes_and_constraints.sql
    • scripts/db/003_data_fixups.sql
    • scripts/db/010_partitioning_template.sql
    • scripts/db/011_rls_template.sql
    • scripts/db/020_audit_trail.sql

如需输出 Word+PDF,可使用 pandoc 将本报告导出为 .docx 与 .pdf,并将生成物纳入 CI 产物归档。