日期: 2026-10-15
状态: 待实现
方案: A — 完全对齐列表展平行(flattenPlanArchivePairRows)
范围: 运输计划列表 + 库存管理入库单/出库单(共用同一套导出逻辑)
列表导出 Excel 的行模型与列表 UI 一致:
stockRole=inbound|outbound)与运输计划列表走同一后端导出构建函数| # | 项 | 决策 |
|---|---|---|
| D1 | 行结构 | 方案 A:段次×产品多行,对齐 flattenPlanArchivePairRows |
| D2 | 作用面 | 运输计划列表 + 入库单 + 出库单,共用逻辑 |
| D3 | 段次列 | 新增「段次」列:装货 / 送货 #1 / 送货 #2… |
| D4 | 客户/地址列 | 装货行只填装货侧;送货行只填对应送货段;不再用 / 拼接多送客户 |
| D5 | 计划级字段 | 计划编号等每行重复(Excel 不做合并单元格) |
| D6 | 归档导出 | 不改归档配对宽表(build_plan_archive_export_rows) |
| D7 | 文件名 / Sheet | 保持现有:入库单 / 出库单 / 计划列表 |
plan 列表导出入口| 层 | 现状 | 问题 |
|---|---|---|
| 列表 | flattenPlanArchivePairRows:装货产品行 → 各送货段产品行;planQty/actualQty 取本段产品 |
正确 |
| 列表导出 | _build_plan_list_export_rows:按计划级 PlPlanProduct 展开;装/送客户、地址用 / 汇总整单 |
一装多送时一行;数量无法按段对照列表 |
对每个计划,按 stops_by_plan(load_plan_stops_grouped)展开:
装货段.products[] → 行:段次=装货,产品/量=装货段产品
送货段1.products[] → 行:段次=送货 #1,产品/量=该送货段产品
送货段2.products[] → 行:段次=送货 #2,…
seq 依次展开各送货段产品行(与列表一致)。PlPlanProduct 一行(段次为空),兼容旧数据。stockRole、列头筛选等)仍由现有列表查询决定「导出哪些计划」;本设计只改单计划内行展开。计划 PO20261009016:装货 1 个产品、送货 2 段各 1 个产品 → 导出 3 行:
| 段次 | 装货客户 | 送货客户 | 产品 | 计划量 |
|---|---|---|---|---|
| 装货 | A | (空) | 尿素 | 30 |
| 送货 #1 | (空) | B1 | 尿素 | 20 |
| 送货 #2 | (空) | B2 | 尿素 | 10 |
| field | 表头 | 取值 |
|---|---|---|
stopSegment |
段次 | 装货;送货 #{seq}(seq 缺省时用展开序号) |
默认导出列顺序:建议紧挨「计划编号」之后。前端可见列导出映射:若列表无独立段次 prop,仍由后端默认列包含 stopSegment;前端 planExportColumns 增加 { prop: 'stopSegment', label: '段次' }(或等价别名),以便「按可见列导出」时可选。
| 列 | 装货行 | 送货行 |
|---|---|---|
| 计划级(编号、归属公司、跟进状态、计划日期、单据类型、车牌、人员、跟进人、供方、需方、司机、运输/归档状态、ERP、备注等) | 重复填计划值 | 同左 |
送货时间(deliveryTimes) |
空 | 本送货段日期(单值,不再整单 / 拼接) |
| 装货客户 / 装货地址时间 / 装货地磅 | 本装货段 | 空 |
| 送货客户 / 送货地址时间 / 送货地磅 | 空 | 本送货段 |
合同(productContract) |
本装货段合同号 | 本送货段合同号 |
| 产品名称 / 品牌 / 别名 / 型号规格 / 包装规格 / 单位 | 本行段产品(CRM 标准名/别名解析保持现有) | 同左 |
| 计划量 / 实际量 | 本行段产品 planQty;实际量语义对齐列表 getRowActualQty(装货读 inbound 侧实际量,送货读 outbound 侧实际量) |
同左 |
枚举中文标签(跟进状态、单据类型、运输/归档状态、归属公司等)与文件名/Sheet 标题逻辑保持现有实现。
dict_type 列仍走现有 export_rows_to_xlsx_response 字典翻译。_PLAN_EXPORT_COLUMN_ALIASES:如前端 prop 与 field 不一致则补映射;stopSegment 本身可直接作为 prop。ListExportDropdown(运输计划 / 入库 / 出库)
→ POST 现有 plan 导出 API(带 filters + stockRole + columns)
→ 查询 plans(不变)
→ load_plan_stops_grouped
→ _build_plan_list_export_rows 【改为段次×产品展开】
→ PLAN_EXPORT_COLUMNS(+ stopSegment)
→ xlsx(filename/sheet 按 stockRole)
| 单元 | 职责 | 依赖 |
|---|---|---|
_build_plan_list_export_rows |
单计划 → 多行 dict;段次与侧栏字段按行写入 | stops_by_plan、CRM 别名 map、段产品 |
PLAN_EXPORT_COLUMNS |
列定义含 stopSegment |
— |
_apply_plan_list_export_stop_fields |
改为按单侧 stop填写,或拆成 load/delivery 专用 helper,避免整单拼接 | 单条 load 或单条 delivery |
web-admin plan/index.vue(及库存页复用) |
导出列清单增加段次;文件名已按 stockRole | 共用 PlanListPage |
tests/test_pl_plan_list_export.py |
一装多送行数、段次文案、侧栏字段空/非空、qty | — |
建议:段次展开逻辑可抽到 backend/app/services/pl/ 下小函数(如 iter_plan_list_export_segments),便于单测且与 plan.py 路由层解耦;若改动面小,也可先内联在 _build_plan_list_export_rows,以最小必要为准。
列表:
getRowPlanQty ← 本行 PlanStopProduct.planQtygetRowActualQty ← getStopArchiveActualQty:装货用 inboundQty,送货用 outboundQty导出 MUST 使用同一语义读取段产品字段(后端 stop product 模型上的对应属性),不得再写计划级 PlPlanProduct.actual_qty 作为有段计划的实际量来源。
至少覆盖:
装货/送货 #1/送货 #2;各行 planQty 为段量;装货行 deliveryCustomer 为空、送货行 loadCustomer 为空。stockRole → 入库单_ / 出库单_ / 计划列表_(现有用例保留)。stopSegment 时出现在导出列;别名 normalize 不破坏既有 prop。PO20261009016 类一装多送计划:导出 Excel 行数与列表展平行数一致(同筛选、同计划集合前提下)。test_pl_plan_archive_export 全绿)。