1
0
forked from erp-dev/erp
Files
erpnew/docs/sales_order_approval_and_red_flush.md
2026-06-22 22:27:28 +08:00

5.1 KiB
Raw Blame History

销售单审批流程与红冲规划

1. 背景

  • 销售单在创建时同样进入 PENDING 状态,审批通过后才会触发出库流程。
  • business.services.review_sales_order 统一处理审批通过 (APPROVED) 与作废 (CANCELLED) 的业务规则。
  • api_v1/views/business/sales/views.py 暴露 POST /api/v1/sales-orders/<id>/review/ 接口,供前端、自动化和第三方系统一致调用。

2. 审批流程总览

步骤 说明
1 前端调用 POST /api/v1/sales-orders/<id>/review/ body: {"action": "approve"}{"action": "cancel"}
2 API 层校验员工身份、订单归属商户、action 可选项
3 API 调用 review_sales_order,根据 action 映射到 SalesOrderStatusEnum.APPROVED / CANCELLED
4 Service 端根据状态调用 _approve_sales_order_cancel_sales_order,并在必要时抛出业务异常(如重复审批、已有出入库记录等)
5 API 返回最新的 SalesOrderSerializer 数据,供前端刷新详情

3. 审批通过触发的事务

  1. _approve_sales_order 构建 stock_flow_items(结构与采购单一致)。
  2. 在事务内将 sales_order.status 更新为 APPROVED
  3. 读取商户设置 auto_create_stock_change_tasks
    • 未配置或为 False 时,仅更新状态。
    • True 时投递 business.tasks.create_sales_order_stock_entries Celery 任务。
  4. Celery 任务执行 StockFlowService.stock_out
    • 依据仓库模式创建出库 StockChangeRecord / StockChangeDetail
    • stock.services.make_stock_change_completed 会在自动完成开启时写入 Inventory 并生成 StockSnapshot
  5. 任务返回 stock_change_record_id 与明细信息,并写日志用于审计。

4. 作废流程约束

  1. 仍通过 POST /api/v1/sales-orders/<id>/review/ body: {"action": "cancel"}
  2. _cancel_sales_order 会检查 source_type = SALESsource_id = 销售单IDStockChangeRecord 是否存在。
  3. 若已经生成出库记录,则抛出 ValueError('销售单已生成出入库记录,无法作废')API 返回 400 提示前端。
  4. 若未生成记录,则在事务内将状态更新为 CANCELLED,且不会触发 Celery 任务。

5. 常见异常与返回

  • 缺少 customer 字段:400 + {"error": "缺少客户 ID"}
  • 审批重复:保持幂等,直接返回当前状态。
  • 已作废单据审批:ValueError('作废状态的销售单无法再次审批') -> 400
  • 作废时已有出库记录:ValueError('销售单已生成出入库记录,无法作废') -> 400
  • ID 不存在或不属于当前商户:404

6. 销售退货(销退)流程补充

  • API/api/v1/sales-return-orders/(创建、列表)与 /review/ 审批接口,与销售单保持完全一致的认证/权限。
  • 创建时字段改为 return_date,其余 items 结构与销售单一致(根据仓库 mode 填写 numbersquantity / num_of_rolls)。严进严出仓在退货场景下视为“入库”,故无需 consume_detail_ids
  • 审批通过:
    1. 事务锁定单据与明细。
    2. 状态置为 APPROVED
    3. 调用 BalanceService.adjust_customer_balance(delta=-total_amount, source=SALES_RETURN_ORDER),写 BalanceChangeRecord,减少客户欠款。
    4. 根据商户设置触发 create_sales_return_order_stock_entries,底层走 StockFlowService.stock_insource=SALES_RETURN)。
  • 作废:同销售单,只允许 PENDING 且未生成 StockChangeRecord 的单据作废;审批后不可逆。

7. 创建接口参数约定

销售单创建接口沿用采购单的参数格式,根据仓库模式填写 items

仓库模式 出库模式 items 必填字段
UNRESTRICTED(宽出) 无需指定入库明细 product_id, quantity, num_of_rolls, price, unit
RESTRICT_IN(严进宽出) 无需指定入库明细 product_id, numbers (list[int]), price, unit
RESTRICT_IN_OUT(严出) 必须指定要消耗的入库明细 product_id, consume_detail_ids (list[int]), quantity, price, unit

说明:在严出模式下,consume_detail_ids 列表中的 ID 必须为同仓库、同商户且未被消耗的入库明细 ID。系统会在审批通过时逐条扣减对应库存。

8. 红冲(对冲)落地状态

销售单已支持整单红冲。已审批销售单可通过 POST /api/v1/sales-orders/<id>/red-flush/ 触发,生成反向余额记录;若原销售单存在 source_type=SALES 的出库记录,则同步生成反向库存记录。关键原则:

  • 采用新增反向记录的方式,保留所有历史变动。
  • 存在库存记录时,红冲记录需要指向原 StockChangeRecord / StockSnapshot,保证审计可追溯。
  • 红冲需与业务对象绑定;没有库存记录的销售单只处理余额侧,不执行库存红冲。

红冲能力已补充独立 API 文档与定向测试,确保审批、出库、红冲形成闭环。详细 API 见 docs/2026-06-12_business_red_flush_api.md