# Proposal ## Why 客户一次运输常对应**多份合同**或**同一合同内同品同包装多行**(单价不同)。现行合同禁止同品同包装重复,运输计划每段只能选一份合同,用量按 `(productId, packageSpec)` 匹配,无法按合同明细行记账。计划员出车前已知道各合同行的目标量,归档时再确认实际量;司机与现场只需对货、不必见合同。 ## What Changes - 合同产品明细:**允许**同一合同内同一产品、同一包装规格出现多行(不同数量/单价)。一行多包装共享数量池仍保留。 - 运输计划装货/送货段:**允许选择多份合同**(须属于该段客户)。段产品行绑定 `contract_id` + `contract_product_id`,填写**目标量**(`plan_qty`)。 - 选品交互:从合同明细行选择(合同号、包装、单价、剩余),不按「仅产品名」合并。 - 用量与硬拦:可安排量、安排运输硬拦、合同详情挂量均按**合同产品行 ID** 池,不再用包装唯一性做解析。 - 保存计划:目标量**允许**暂时超过该行剩余;**安排运输**时超过则硬拦。 - 归档:在已拆好的行上填**实际量**;有本车实发时各行合计须对齐。某行实际量超过该行剩余时红字高亮、指出行、二次确认后仍可归档,超交只记该行。 - 司机/现场端:**不展示**合同号、单价、合同行;只展示产品与数量。 - **BREAKING**:放开「同品包装不重复」后,未绑合同行的计划无法正确分账;新保存的段产品必须带合同行 ID(无合同/内部客户除外)。历史未绑行数据按旧复合键回退。 UI 示意:`docs/superpowers/demos/2026-10-05-contract-plan-line-qty-demo.html`。 ## Capabilities ### New Capabilities - `contract-line-plan-qty-bind`:合同同品同包装多行;计划段多合同并绑定合同产品行;目标量/实际量分阶段校验;司机现场不暴露合同。 ### Modified Capabilities - 无基线 `openspec/specs/`。落地后覆盖 `crm-contract-multi-package-shared-qty` 中「同一 productId 下包装规格全局不重复」及「计划不绑 contractProductId」的假定;一行多包装共享池仍有效。 ## Impact - 后端:合同包装唯一校验;`pl_plan_load_product` / `pl_plan_delivery_product`(及必要的 `pl_plan_product`)增列;`assert_at_most_one_contract`;`pl_contract_plan_qty.py` 主键;安排硬拦;归档超交确认;arrange-qty API。 - 前端 web-admin:`ContractFormDialog` 去重;`PlPlanFormDialog` 多合同与合同行选品;归档弹窗实际量、地磅合计、超交二次确认;合同详情按行挂量。 - 小程序/现场:默认不改交互;若有合同号展示则隐藏。 - 数据库:`docs/sql/` + 迁移脚本。 - 测试:合同多行保存、多合同计划、安排硬拦、归档超交确认、历史回退匹配。