# business 模块结构与设计说明 本文档记录业务模块当前的结构、关键约定以及未来演进需要遵循的决策背景,防止在迭代中遗失设计意图。 ## 1. 模块职责 `business` 负责“业务单据”领域,目前包含采购单(PurchaseOrder)与销售单(SalesOrder),同时保留可扩展至退货单等更多对象的通用框架。核心职责: - 聚合与校验业务明细数据 - 通过服务层触发库存(stock)、财务等下游模块 - 统一处理审批、作废等状态流转 ## 2. 模型抽象 ### 2.1 OrderItemsAggregationMixin 封装订单明细聚合逻辑,默认访问 `items` 关联,可通过 `order_items_accessor` 指定其他关联名。提供: - `get_total_amount()` - `get_total_quantity()` - `get_total_diff_quantity()` 这些方法仅关心明细结构,不关心订单方向。 ### 2.2 OrderDirectionMixin 用于获取带方向的金额,约定: - `get_direction()` 返回 `1`(正向/入库)或 `-1`(负向/出库) - `get_signed_total_amount()` 在内部调用 `get_total_amount()` 并乘以方向 任何需要“正负金额”的业务(财务统计、库存红冲)都应依赖此能力,而不是重复编写正负逻辑。 ### 2.3 OrderCounterpartyMixin 对外暴露统一的 `get_counterparty()` 接口,通过 `get_counterparty_field_name()`(或 `counterparty_field_name` 属性)确定具体业务主体字段。这样采购单/销售单分别返回 `Supplier` 与 `Customer`,但调用方只需面对一个接口。 ### 2.4 PurchaseOrder / SalesOrder 两类订单模型均继承上述三个 Mixin: - 采购单:`get_direction()` 返回 `1`,`get_counterparty_field_name()` 返回 `supplier` - 销售单:`get_direction()` 返回 `-1`,`get_counterparty_field_name()` 返回 `customer` - 其余字段(`merchant/warehouse/operator/status/items`)保持一致,便于服务、序列化与统计逻辑复用 通过 mixin,两个模型天然具备: - 金额聚合与带方向金额计算 - 统一的业务主体读取接口 - 与库存/财务交互时一致的 `StockFlowService` payload ## 3. 服务层约定 - `business.services` 负责 orchestration,与 API 解耦。审批、作废、触发库存等流程必须先在服务层实现,再暴露给 API。 - 任何涉及库存的逻辑都必须通过 `stock.services.StockFlowService`,不得直接操作库存模型,保持模块边界清晰。 - 红冲/对冲等高级动作应由业务模块提供入口(例如 `PurchaseOrder` 红冲),但最终仍调用库存服务完成实际库存变动。 ## 4. 未来演进建议 1. **新增单据**:若未来出现调拨单、退货单等,优先继承 `OrderItemsAggregationMixin + OrderDirectionMixin + OrderCounterpartyMixin`,仅通过 `get_direction()` / `get_counterparty_field_name()` 区别方向与主体,减少重复实现。 2. **审批/状态机**:采购与销售如需共享状态流转,可提炼状态机或 service 层 mixin,而无需在模型层合并。 3. **统计与报表**:财务/库存统计应依赖 `get_signed_total_amount()` / `get_direction()`,确保采购/销售、退货/正向都能通过统一接口处理。 4. **文档同步**:新增单据或服务时必须更新本文件,描述新增模型如何复用 mixin、如何影响下游模块,保持设计透明。 以上约定的目标是:**保持采购单与未来销售单等业务对象的独立性,同时通过 mixin/服务层抽象复用绝大多数公共逻辑**。如需变更此架构(例如重新合并模型或修改核心 mixin 行为),请在评估后更新本文件,明确变化原因与迁移方案。