Explorar el Código

docs: specify transport plan required brand as preference only

Document that feedback/archive brand qty entry uses any-mode multi-select with preferred-brand preselect, removing the single-brand hard lock.

Co-authored-by: Cursor <cursoragent@cursor.com>
zlwu hace 3 meses
padre
commit
b3efbe8781

+ 98 - 0
docs/superpowers/specs/2026-07-31-pl-required-brand-as-preference-design.md

@@ -0,0 +1,98 @@
+# 运输计划「指定品牌」回填改为偏好提示设计
+
+**日期:** 2026-07-31  
+**状态:** 已确认待实现  
+**关联:** 修订 `docs/superpowers/specs/2026-07-31-pl-feedback-brand-qty-design.md` 中 `required`「仅指定品牌可选」硬锁语义  
+**选定方案:** 方案 1 — 回填侧统一按 `any` 规则校验与交互;计划侧仍保留 `brandMode=required` + `brandId`/`brandName` 作偏好展示
+
+## 背景与目标
+
+运输计划产品行可选「指定品牌」(`brandMode=required`)。原设计在司机链接、现场链接、归档弹窗等回填路径上硬锁为:只能填写该品牌一行,且数量必须等于实际数量。
+
+实际业务中,计划指定品牌仅表示偏好/提示;回填时常需按多品牌拆分实装量,且不必强制包含计划指定品牌。
+
+本变更取消回填硬限制,保留计划侧偏好语义与默认预选。
+
+## 决策摘要
+
+| 项 | 决策 |
+|----|------|
+| 指定品牌含义 | 计划侧提示/偏好;回填不必包含该品牌 |
+| 回填交互 | 与「不限品牌」相同:多选芯片、可取消 |
+| 默认选中 | 打开回填时预选计划指定品牌(数量可改、可取消),仍可再选其他品牌 |
+| 存库语义 | 不改计划创建:选品牌仍为 `required`+id/name;清空仍为 `any` |
+| 校验 | `required` 与 `any` 共用:至少一正数量行,合计 = 实际数量 |
+| 范围外 | 不改表结构、不迁历史、不做「必须包含指定品牌」 |
+
+## 行为规则
+
+### 不变
+
+- 计划 / 报价 / 合同建单:品牌下拉选中 → `brandMode=required` + 快照;清空 → `any`
+- `brandMode` 为空:回填不展示品牌区,只填总量
+- 有 `brandMode`(`any` / `required`):至少 1 个品牌 `qty > 0`,且各品牌合计等于该产品实际数量
+- 现场 / 司机 / 归档三套品牌数量仍互相独立;归档最终量仍为库存入账权威来源
+
+### 变更后回填规则
+
+适用范围:司机公开页、现场入/出库公开页、运输计划归档弹窗、管理端司机回传补录、计划详情内现场回传编辑等所有品牌填量入口。
+
+| 计划约定 | 芯片交互 | 默认选中 | 是否必须包含指定品牌 |
+|---------|---------|---------|---------------------|
+| `required` + 某品牌 | 与 `any` 相同:多选可取消 | 预选该偏好品牌(已有已保存明细则优先回显) | 否 |
+| `any` | 多选 | 仅产品仅有一个启用品牌时预填(现状) | 不适用 |
+| 无 `brandMode` | 不展示品牌区 | — | — |
+
+### 展示
+
+- 计划与回填区可继续展示「指定品牌 · xxx」类提示标签(偏好说明)
+- `BrandQtyChipsField` 标题统一为「选择品牌并填量」,不再使用「指定品牌填量」以免暗示单品牌锁定
+
+## 实现要点
+
+### 后端
+
+- `backend/app/services/pl_brand_archive.py` → `validate_brand_qty_rows`:删除 `required` 分支中「只能填写该品牌」「单行数量必须等于实际数量」的校验;`required` 与 `any` 均校验正数量合计等于 `actual_qty`
+- 调用方(归档 `pl_plan_service`、`pl_feedback_brand_qty.require_brand_qtys_if_needed` 等)签名可保持;`required_brand_id` 不再用于硬锁校验(可保留参数以免大范围改签名,或后续清理)
+- 错误文案:去掉「指定品牌时只能填写该品牌数量」路径
+
+### 前端
+
+- `BrandQtyChipsField`:`required` 不再渲染锁定芯片(`chip--locked`);统一走多选芯片;`requiredBrandId` 仅供父级默认预选,组件内不禁止取消
+- 父级初始化(至少覆盖):
+  - `pl-plan-archive-dialog`
+  - `views/public/pl/transport/index.vue`
+  - `views/public/pl/_shared/PublicIoOrderPage.vue`
+  - `pl-plan-driver-feedback-dialog`
+  - `pl-plan-view-tabs` 内现场品牌编辑
+- `required` 初始化:`brandOptions` 加载产品全部启用品牌(与 `any` 相同);无已保存明细时 `brandQtyRows` 预填指定品牌一行
+- 删除归档弹窗 `syncRequiredBrandQty`:不再把实际数量强制同步到唯一指定品牌行;有品牌时实际数量由品牌合计驱动(与现 `any` 一致)
+
+### 文档 / Demo
+
+- 修正 `docs/demos/pl-site-driver-brand-qty-demo.html` 中「指定品牌时仅允许该品牌一行」说明
+- 本文件为权威变更说明;原 `2026-07-31-pl-feedback-brand-qty-design.md` 中 `required` 硬锁描述以本文件为准覆盖
+
+## 测试
+
+### 后端
+
+- `required` + 多品牌合计等于实际数量 → 通过
+- `required` + 仅非指定品牌且合计正确 → 通过
+- `required` + 合计 ≠ 实际数量 → 失败
+- `required` + 完成态无正数量行 → 失败
+- `any` 原有用例行为不变
+
+### 前端手工验收
+
+1. 计划产品指定品牌 → 司机链接可多选、可取消预选并保存成功
+2. 同上现场链接
+3. 运输计划归档弹窗同上
+4. 管理端司机回传补录同上
+5. `any` / 无品牌产品行为与现网一致
+
+## 风险
+
+- 历史已保存回传/归档数据不受影响;仅新提交走新校验
+- 外部若曾依赖「指定品牌必填该品牌」,需业务知悉;仓库内无额外集成硬依赖
+- `brandMode=required` 命名与回填行为不完全同义:文档与 UI 提示须写清「仅偏好」