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>
This commit is contained in:
zlwuandCursor committed 2026-07-31 06:50:21 +08:00
1 parent 6dcc279cc9
commit b3efbe8781
1 file changed
+98
@@ -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 提示须写清「仅偏好」