1
0
forked from erp-dev/erp
Files
erpnew/docs/purchase_order_approval_and_red_flush.md
2025-11-30 23:04:21 +08:00

5.6 KiB
Raw Blame History

采购单审批流程与红冲规划

1. 背景

  • 采购单在创建时默认进入 PENDING(审批中)状态,不再立即创建出入库记录。
  • business.services.review_purchase_order 统一处理审批通过 (APPROVED) 与作废 (CANCELLED) 的业务规则。
  • api_v1/views/business/purchase/views.py 暴露 POST /api/v1/purchase-orders/<id>/review/ 接口作为唯一入口,便于前端、自动化流程和第三方系统统一调用。

2. 审批流程总览

步骤 说明
1 前端在采购单详情页或审批面板触发 POST /api/v1/purchase-orders/<id>/review/,请求体只包含 {"action": "approve"}{"action": "cancel"}
2 API 层验证:当前用户必须具备员工身份、采购单必须属于当前商户;action 字段必须是 approve/cancel 之一
3 API 调用 business.services.review_purchase_order,根据 action 映射到 PurchaseOrderStatusEnum.APPROVEDCANCELLED
4 Service 内部基于状态决定调用 _approve_purchase_order_cancel_purchase_order,并在必要时抛出业务错误(例如重复审批、作废时已有出入库记录等)
5 API 返回最新的 PurchaseOrderSerializer 数据(包含总金额、总数量、明细与状态等)供前端刷新页面

3. 审批通过触发的事务

  1. _approve_purchase_order 先构建 stock_flow_items
    • 宽进仓:items=[{'product_id', 'value', 'num_of_rolls'}]
    • 严进/严进严出:items=[{'product_id', 'quantities': [...] }]
  2. 在数据库事务中,将 purchase_order.status 置为 APPROVED 并保存,保证状态更新原子性。
  3. 读取商户设置 auto_create_stock_change_tasks
    • 若未配置或为 False,审批仅改变状态,不会触发后续任务。
    • 若为 True,投递 business.tasks.create_purchase_order_stock_entries Celery 任务。
  4. Celery 任务执行 StockFlowService.stock_in
    • 依据仓库模式创建入库 StockChangeRecordStockChangeDetail
    • 后续在 stock.services.make_stock_change_completed 中写入 Inventory 并生成 StockSnapshot
  5. 任务返回 stock_change_record_id、明细数量等信息,并写日志用于追踪审计。

4. 作废流程约束

  1. 同样通过 POST /api/v1/purchase-orders/<id>/review/,但 {"action": "cancel"}
  2. Service 端调用 _cancel_purchase_order,首先查询是否存在 source_type = PURCHASEsource_id = 采购单IDStockChangeRecord
  3. 若已存在入库记录直接抛出错误“采购单已生成出入库记录无法作废”API 返回 400 并提示原因。
  4. 若未生成记录,则在事务内将状态更新为 CANCELLED。不会触发 Celery 任务。

5. 常见异常与返回

  • 缺少 action 字段:400 + DRF 序列化错误。
  • 审批已是目标状态:直接返回现状(幂等)。
  • 已作废的单据请求审批:ValueError('作废状态的采购单无法再次审批') -> API 返回 400
  • 作废时已有出入库记录:ValueError('采购单已生成出入库记录,无法作废') -> API 返回 400
  • ID 不存在或不属于当前商户API 返回 404

6. 下一阶段:库存红冲(对冲)方案规划

6.1 目标与原则

  • 目标:允许在采购单及其衍生的出入库记录完成后,通过“红冲”方式撤销或抵消错误的库存变动,同时保持库存快照与主库存的可追溯性。
  • 原则
    • 采用“新增反向记录”方式对冲,而非直接修改原记录;保留每一次实际发生的库存变更。
    • 红冲记录需要指向原 StockChangeRecord / StockSnapshot(如 offset_tooffset_id 字段),便于审计。
    • 红冲动作必须与业务流程挂钩(例如采购单作废或红冲),避免孤立的库存操作。

6.2 行动步骤

  1. 业务入口确定

    • 定义哪些业务对象可以触发红冲(采购单、销售单等)。
    • 明确触发条件:如采购单审批通过后,允许“创建红冲请求”并指向原采购单。
  2. 库存服务层改造

    • stock.services.StockFlowService 或相关函数中增加创建红冲记录的能力:
      • 根据原 StockChangeRecord 生成反向 StockChangeRecord 和明细。
      • 更新 StockSnapshotoffset_to / offset_id / cancelled 字段,确保链路闭环。
    • 确保 Inventory 调整遵循相同的原子逻辑(事务 + 快照)。
  3. 业务服务与模型扩展

    • business.services 中引入“红冲采购单”或“撤销审批”的新方法:
      • 校验:原单是否允许红冲、是否存在未处理的红冲记录。
      • 调用库存红冲服务并记录关联。
    • 必要时在 PurchaseOrder 或新模型中记录红冲状态/引用。
  4. API 与文档更新

    • 设计新的红冲 API例如 /api/v1/purchase-orders/<id>/offset/),请求体注明原因、操作者。
    • 文档中要说明红冲流程与限制(只能对已审批单据、需管理员权限等)。
  5. 测试与审计

    • 单元测试:业务 Service、库存 Service、API 都要覆盖正常流程与错误场景。
    • 集成测试:验证红冲后库存数量恢复、快照关系正确、消息/通知链路是否需要补充。
    • 如有需要,增加日志或审计表,记录红冲操作人、时间、关联记录。

通过以上步骤,审批流程与红冲机制可以衔接:审批负责“正向入库”与任务分发,红冲负责在业务需要时生成配对的反向库存记录,保持库存账实一致,同时具备可回溯、可审计的业务闭环。