日期: 2026-08-26
状态: 待审阅(brainstorming 已确认核心决策)
前置:docs/运输计划管理-需求说明文档.md、docs/superpowers/specs/2026-06-25-pl-plan-archive-design.md、docs/superpowers/specs/2026-06-21-pl-arrange-transport-refine-design.md
示意稿:docs/fresh-mint-pl-multi-stop-understanding-demo.html、docs/fresh-mint-pl-multi-stop-print-rules-demo.html
支持一车多卸:一个运输计划可包含 1 个装货段 + N 个送货段;保留计划级基础信息、产品总量、运输信息与运费;将原「供方/需方」拆为装货/送货停靠段,并联动归档、地磅、司机/现场链接、打印与运费结算。
| # | 决策 | 说明 |
|---|---|---|
| D1 | 装货仅 1 段 | 不支持一车多装;取货/装货信息唯一 |
| D2 | 送货多段 | 支持多客户、多地址、多合同 |
| D3 | 数量校验 | 装货合计 / 送货合计 / 计划产品总量不平衡时 仅告警,允许暂存 |
| D4 | 内部主体 | 安庆市山峡化工有限公司、安徽云栋化工有限公司(is_internal_customer_name) |
| D5 | 装货 + 内部主体 | 免合同;对公司=出库 → 默认勾选 地磅出库单 |
| D6 | 送货 + 内部主体 | 免合同;对公司=入库 → 默认勾选 地磅入库单 |
| D7 | 归档字段命名 | 装货段固定 入库数量/单价/金额;送货段固定 出库数量/单价/金额(不因客户切换字段名) |
| D8 | 归档粒度 | 整单一起归档;所有段数据齐备后一次提交 |
| D9 | 司机链接 | 1 个/计划;页内按装货 + 各送货段分别上传附件 |
| D10 | 现场链接 | 1 个/计划;仅展示山峡/云栋且勾选地磅的段;外部客户段不展示;先数量后附件 |
| D11 | 送提货单打印 | 多送货客户时先选客户;同客户多段合并一张;地址/联系人取该客户 第一个送货段;地址不同不拆单 |
| D12 | 入库单打印 | 送货单位 = 装货客户(唯一) |
| D13 | 分装单打印 | 不考虑客户信息 |
| D14 | 运费 | 仍计划级;新增 freight_remark 运费备注 |
| D15 | 历史迁移 | 旧计划自动拆为 1 装货 + 1 送货 |
供应商装 30t 硫酸 → A 客户 10t → B 客户 15t → 山峡入库 5t。
pl_plan(计划总量 30t)
├── pl_plan_load(装货 ×1) XX 供应商 · 30t
└── pl_plan_delivery(送货 ×N)
├── #1 A 客户 · 10t
├── #2 B 客户 · 15t
└── #3 山峡 · 5t(内部入库地磅)
| 区域 | 处理 |
|---|---|
| 基础信息 | 保留于 pl_plan |
| 产品信息 | 保留于 pl_plan_product(计划总量主数据) |
| 运输信息 | 保留于 pl_plan + pl_transport(安排后) |
| 供方/需方 | 废弃 UI;数据迁入装货/送货段 |
| 取送地址 | 下沉至装货段 / 各送货段 |
| 运费 | 保留计划级 + freight_remark |
plan_type 处理pl_plan.plan_type 保留列(兼容报表/迁移),由段推导写入或置 direct 兜底。pl_plan_load(装货段,plan 1:1)| 字段 | 类型 | 说明 |
|---|---|---|
| id | VARCHAR(64) PK | UUID |
| plan_id | VARCHAR(64) | 计划 ID,唯一(每计划一条) |
| customer_id | VARCHAR(64) | 装货客户 |
| customer_name | VARCHAR(200) | 冗余 |
| address | VARCHAR(500) | 装货地址 |
| lng/lat | DECIMAL | 坐标 |
| load_time | DATETIME | 装货时间 |
| contact | VARCHAR(200) | 联系方式 |
| notes | VARCHAR(500) | 注意事项 |
| generate_dibang | TINYINT | 是否生成地磅(内部默认 1) |
| dibang_direction | VARCHAR(10) | outbound(内部装货)/ none |
| 公共字段 | … | tenant_no … del_flag |
pl_plan_load_product(装货产品行)| 字段 | 说明 |
|---|---|
| load_id / plan_id | 外键 |
| plan_product_id | 关联计划产品主行 |
| product_id / product_name / model / unit | 快照 |
| plan_qty | 本段计划数量 |
| package_spec | 包装规格(可覆盖) |
| delivery_method | 送货方式(可覆盖) |
| remark | 段内备注 |
| inbound_qty / inbound_price / inbound_amount | 归档(装货侧入库字段) |
pl_plan_delivery(送货段,plan 1:N)| 字段 | 类型 | 说明 |
|---|---|---|
| id | VARCHAR(64) PK | UUID |
| plan_id | VARCHAR(64) | 计划 ID |
| seq | INT | 顺序(打印、链接展示) |
| customer_id / customer_name | 送货客户 | |
| address / lng / lat | 送货地址 | |
| delivery_time | DATETIME | 送货时间 |
| contact / notes | 联系 / 注意事项 | |
| generate_dibang | TINYINT | 内部默认 1 |
| dibang_direction | VARCHAR(10) | inbound(内部送货)/ none |
| 公共字段 | … |
pl_plan_delivery_product(送货产品行)| 字段 | 说明 |
|---|---|
| delivery_id / plan_id | 外键 |
| plan_product_id | 关联计划产品 |
| plan_qty / package_spec / delivery_method / remark | 段内可编辑 |
| outbound_qty / outbound_price / outbound_amount | 归档(送货侧出库字段) |
pl_plan 新增字段| 字段 | 说明 |
|---|---|
| freight_remark | VARCHAR(512) 运费备注 |
扩展 pl_plan_contract:
| 变更 | 说明 |
|---|---|
side |
新增枚举 load / delivery(保留 supplier/demander 只读兼容旧数据) |
stop_id |
VARCHAR(64),装货段 ID 或送货段 ID |
规则:内部客户段 不强制合同;外部客户段 至少选一个合同(与现逻辑一致)。
司机附件(按段) — 新增 pl_transport_driver_feedback 或扩展现有司机回传 JSON:
| 字段 | 说明 |
|---|---|
| transport_id / plan_id | |
| stop_type | load / delivery |
| stop_id | 段 ID |
| attachment_urls | TEXT JSON |
| remark | 可选 |
现场回传(仅内部+地磅段) — 扩展 pl_transport_site_link 或等价结构:
| 字段 | 说明 |
|---|---|
| stop_type / stop_id | 关联装货或送货段 |
| quantity | 先填数量 |
| site_feedback_images | 后传附件 |
| business_type | 入库/出库(地磅方向) |
Token 策略(D9/D10):
pl_transport.driver_access_token:1 个/运输单generate_dibang=1 且内部客户的段渲染分区;不展示外部段pl_transport_db_link 增加 stop_type、stop_id,按段生成地磅计划:
| 段 | 客户 | generate_dibang | 地磅业务类型 |
|---|---|---|---|
| load | 外部 | 0 | — |
| load | 内部 | 1 | 出库 |
| delivery | 外部 | 0 | — |
| delivery | 内部 | 1 | 入库 |
安排运输弹窗:按段列出勾选状态,可改;调整运输时沿用现有 revoke 规则。
PlPlanFormDialog)基础信息 → 产品信息 → 装货信息(单段)→ 送货信息(可增删)→ 运输信息(安排前只读/占位)
pl_plan_product 带出,可改 plan_qty、包装、送货方式、备注seq)| 校验 | 行为 |
|---|---|
| 装货段缺失 | 阻断 |
| 送货段 0 条 | 阻断 |
| 数量不平衡 | ElMessage.warning,允许保存 |
| 外部段无合同 | 阻断(提交/安排时) |
装货: XX / 送货: A,B,山峡 或主送货客户 + 等N家create_transport_for_plangenerate_dibanghazard_cert_ready:仍计划级,安排前校验[装货] {customerName} — 附件上传区
[送货 #1] {customerName} — 附件上传区
[送货 #2] ...
仅渲染满足 is_internal_customer_name && generate_dibang 的段:
[装货·山峡出库] 数量 → 附件
[送货·山峡入库] 数量 → 附件
外部客户段 不出现。
| 单据 | 入口 | 数据规则 |
|---|---|---|
| 送(提)货单 | 计划列表/查看/安排 | 送货客户数 ≥2 → PlPlanDeliveryCustomerSelectDialog;同 customer_id 合并产品行;地址/联系人取 seq 最小 的送货段;忽略地址差异 |
| 入库单 | 同上 | 送货单位 = pl_plan_load.customer_name;产品取装货段或计划总量(实现时与模板对齐) |
| 分装单 | 同上 | 不读客户;按计划产品 |
涉及文件:
web-admin/src/components/business/pl-delivery-slip-print-dialog/web-admin/src/components/business/pl-inbound-slip-print-dialog/web-admin/src/components/business/pl-subpack-slip-print-dialog/web-admin/src/utils/pl-delivery-slip-template.ts 等PlPlanArchiveDialog)上区
├── 装货归档(入库数量/单价/金额 × 产品行)
├── 送货归档 #1 … #N(出库数量/单价/金额 × 产品行)
└── 运费(计划级 + freight_remark)
下区:PlPlanViewTabs(含分段链接回传)
archive_status=archived + follow_status=completedapply_archive_product_quantities 思路,改为按段聚合)pl_plan_product 汇总字段可由段归档回写或仅展示段级(实现时 _row_to_dict 为权威)pl_freight_settlement 仍关联计划/运输;准入「已归档 + has_freight」freight_remark脚本:backend/scripts/migrate_pl_plan_to_multi_stop.py(新建)
对每条 pl_plan(del_flag=0):
pl_plan_load:supplier_id/name → customer;pickup_* → address/time/contact/notespl_plan_delivery seq=1:customer_id/name、destination_* → 送货地址pl_plan_product → load_product + delivery_product(数量用原 inbound_estimate/outbound_estimate 或 quantity)pl_plan_contract:supplier→load,demander→delivery(挂 stop_id)generate_dibangpl_plan_product.inbound_* / outbound_* 写入对应段产品行)迁移后 pl_plan 上 supplier_* / customer_* / pickup_* / destination_* 保留冗余(只读兼容),新写入走段表。
| 方法 | 路径 | 说明 |
|---|---|---|
| GET | /transport/plan/{id} |
返回 load + deliveries + products |
| POST | /transport/plan/create-full |
Body 含 load、deliveries[] |
| PUT | /transport/plan/{id} |
全量更新段 |
| POST | /transport/plan/{id}/archive |
Body 含各段归档明细 + freight_remark |
| GET | /transport/plan/{id}/print-delivery-slip?customerId= |
按客户聚合送提货单数据 |
Schema 权威:_row_to_dict + Pydantic PlanLoadRead / PlanDeliveryRead;跑 check_field_consistency.py。
| 场景 | 期望 |
|---|---|
| 30=10+15+5 保存 | 成功;不平衡时 warning |
| 装货山峡 | 默认出库地磅 |
| 送货山峡 | 默认入库地磅 |
| 送提货单 3 客户 | 弹出客户选择 |
| 同客户 2 段 | 合并 1 张;地址取第一段 |
| 司机页 | 全段可见;现场页仅内部段 |
| 归档 | 缺一段字段 → 阻断 |
| 迁移旧单 | 1 装货 + 1 送货;打印/归档可用 |
PlPlanFormDialog 扩展PlPlanArchiveDialog| 客户表述 | 实现口径 |
|---|---|
| 取货信息叫「入库」 | 归档字段名 inbound_*;内部装货时地磅为 出库 |
| 送货信息叫「出库」 | 归档字段名 outbound_*;内部送货时地磅为 入库 |
| 一个计划一个现场/司机链接 | transport 级 token;页内按段分区 |
| 送提货单同客户 | 合并;地址取首段 |