docs: add transport plan arrange upsert cascade design.
Capture confirmed rules for stable-id updates, dibang inventory/weighing field sync, and monthly plan remap on plan rearrange. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
1 parent
fbf830172f
commit
410d293287
1 file changed
+206
@@ -0,0 +1,206 @@
|
||||
# 运输计划调整 · 全量子数据 Upsert 级联
|
||||
|
||||
**日期:** 2026-09-06
|
||||
**状态:** 已确认(brainstorming)
|
||||
**触发案例:** 生产 `PO20260903001` — 选错车后改车;出入库/过磅单车牌未同步;安排返回「无法随运输安排自动重建」
|
||||
**相关:**
|
||||
- `2026-09-05-pl-stop-stable-id-reuse-links-design.md`(段/产品稳定 ID + link 复用)
|
||||
- `2026-08-30-pl-plan-save-sync-dibang-date-design.md`
|
||||
- `2026-06-20-pl-arrange-dibang-design.md`
|
||||
|
||||
---
|
||||
|
||||
## 1. 背景
|
||||
|
||||
### 1.1 现象
|
||||
|
||||
- 计划/运输单已改为新车牌,地磅出入库、过磅单仍为旧车牌。
|
||||
- 用户重安排时接口 400:`已关联地磅单,无法随运输安排自动重建`。
|
||||
- 出入库单 id 已发给司机,不能因调整而换新 id。
|
||||
|
||||
### 1.2 根因(叠加)
|
||||
|
||||
1. **旧安排路径**:重安排先 `revoke` 再建;已过磅则整单失败(生产曾未及时加载 upsert 版本)。
|
||||
2. **新 upsert 路径仍偏保守**:已关联过磅单时跳过出入库车牌/产品等正文同步。
|
||||
3. **过磅单正文**:安排/保存从不回写 `vehicle_no` / 产品等。
|
||||
4. **段产品换 UUID**(见 09-05 文档):`plan_product_id` 变化导致 link 对齐困难。
|
||||
5. **月计划**:产品/往来变更后未稳定「利旧或新建并改挂」。
|
||||
|
||||
### 1.3 产品诉求(已确认)
|
||||
|
||||
| # | 决策 |
|
||||
|---|------|
|
||||
| D1 | 整体采用 **方案 2:运输计划调整统一 Upsert 契约** |
|
||||
| D2 | **有 id 则更新,无 id 则创建**;禁止为改业务而删旧建新换主键 |
|
||||
| D3 | 出入库单 **id 永不因调整而更换**(司机已持有) |
|
||||
| D4 | 已有过磅单:强制同步 **业务正文**(车牌/产品/往来等);**称重数字一律不动** |
|
||||
| D5 | **新产生**的地磅需求(无既有出入库)→ **新建**出入库单 |
|
||||
| D6 | 出入库产品/往来变更 → 月计划 **利旧或新建**,并改挂 `purchase_plan_id` |
|
||||
| D7 | 计划保存与安排/重安排共用同一套下游 sync;主流程成功,下游问题以 `warnings` 回报(破坏性取消勾选等仍可硬阻断) |
|
||||
|
||||
---
|
||||
|
||||
## 2. 目标与非目标
|
||||
|
||||
### 2.1 目标
|
||||
|
||||
| # | 目标 |
|
||||
|---|------|
|
||||
| G1 | 已安排后调整车辆/产品/往来/数量/日期等,关联子数据按 id upsert 完成更新 |
|
||||
| G2 | 司机所持出入库 id、运输单 token 在调整后仍有效 |
|
||||
| G3 | 已过磅时仍同步出入库与过磅单业务字段;称重不变 |
|
||||
| G4 | 新勾选地磅 → 新建出入库;旧关联 → 原地更新 |
|
||||
| G5 | 产品/往来变更 → 月计划 find-or-create 并改挂 |
|
||||
| G6 | 废除安排前整批 revoke;部署后不得再出现「无法随运输安排自动重建」整单 400 |
|
||||
|
||||
### 2.2 非目标
|
||||
|
||||
- 不改造地磅硬件/过磅采集流程。
|
||||
- 不为「改计划」凭空创建过磅单(过磅单仍由现场过磅产生)。
|
||||
- 不自动作废已有过磅单要求重磅(除非未来单独产品决策)。
|
||||
- 复制计划仍全新生成下游单据(不带走源单 id)。
|
||||
- 运费结算已锁定单据的深度回写(若冲突,警告,本期不强制改结算单)。
|
||||
|
||||
---
|
||||
|
||||
## 3. 总契约:Upsert by ID
|
||||
|
||||
```
|
||||
请求/关联中存在有效 id → UPDATE 业务字段(主键不变)
|
||||
不存在 id / 不可复用 → CREATE
|
||||
禁止:为改业务而 soft-delete + 新 UUID 重建(稳定白名单对象)
|
||||
```
|
||||
|
||||
### 3.1 稳定 id 白名单
|
||||
|
||||
- `db_inbound_outbound.id`(及已发司机依赖的 `batch_no` / `site_access_token` 默认不 rotate)
|
||||
- `pl_transport.id` 与司机/现场 `*_access_token`(默认不 rotate)
|
||||
- 已存在的 `db_weighing_slip.id`(只改正文)
|
||||
- 前端回传的 `pl_plan_load` / `pl_plan_delivery` / 段产品行 id(有则保留)
|
||||
- `pl_transport_db_link`:更新指向,不换 inventory id
|
||||
|
||||
### 3.2 两条主入口,一个 sync 引擎
|
||||
|
||||
```
|
||||
PUT update_plan POST arrange_transport
|
||||
\ /
|
||||
\ /
|
||||
v v
|
||||
计划内核 upsert 运输单 upsert
|
||||
\ /
|
||||
v v
|
||||
下游 sync orchestrator
|
||||
- 地磅出入库(有则改 / 无则建)
|
||||
- 过磅单正文(有则改)
|
||||
- 月计划利旧或新建并改挂
|
||||
- 现场/IO 快照(token 稳定)
|
||||
```
|
||||
|
||||
| 入口 | 职责 |
|
||||
|------|------|
|
||||
| 保存计划 | 段/产品/合同等内核 upsert;若已 `arranged` 触发下游 sync |
|
||||
| 安排/重安排 | 运输单有则更新无则创建;勾选地磅走同一 sync;**不再**先 revoke |
|
||||
|
||||
---
|
||||
|
||||
## 4. 字段与对象矩阵
|
||||
|
||||
### 4.1 地磅出入库 `db_inbound_outbound`
|
||||
|
||||
| 情形 | 行为 |
|
||||
|------|------|
|
||||
| 已有 link → inventory | **UPDATE** 同 id |
|
||||
| 新勾选/新段产品无 inventory | **CREATE** 新出入库 + link |
|
||||
| `id` / `batch_no` / `site_access_token` | 冻结(默认) |
|
||||
| `vehicle_no` / 产品快照 / `partner_company` / `quantity` / `date` / `business_type` | 强制同步 |
|
||||
| `status` | 一般不因改车重置 |
|
||||
|
||||
### 4.2 过磅单 `db_weighing_slip`
|
||||
|
||||
| 字段类 | 行为 |
|
||||
|--------|------|
|
||||
| 按 `inbound_outbound_id` 找到未作废单 | **UPDATE** 正文 |
|
||||
| `vehicle_no` / 产品 / 往来 / `plan_no` / `batch_no` / `business_type` | 强制同步 |
|
||||
| `gross/tare/net/deduction/*_time` | **永不改** |
|
||||
| 尚无过磅单 | 不创建;等现场过磅 |
|
||||
|
||||
### 4.3 地磅月计划 `db_purchase_plan`
|
||||
|
||||
匹配键:业务类型 + 往来 + 产品(+规格) + 出入库业务日所在月。
|
||||
|
||||
| 结果 | 行为 |
|
||||
|------|------|
|
||||
| 找到可复用月计划 | 出入库改挂该 `purchase_plan_id`(利旧);数量 delta:旧减新加 |
|
||||
| 找不到 | **新建**月计划并挂靠 |
|
||||
| 仅车牌等变更、产品/往来未变 | 不改挂;可回写月计划 `vehicle_no` |
|
||||
| 过磅单上的 `purchase_plan_id` / `plan_no` | 随新挂靠同步引用;称重不动 |
|
||||
|
||||
### 4.4 计划内核 / 运输执行
|
||||
|
||||
- 段与段产品:有 id → upsert(对齐 `2026-09-05` 文档);消除无故换 `plan_product_id`。
|
||||
- 未出现在 payload 的旧段/产品 → 软删;若已过磅关联 → **硬阻断取消勾选/删除类破坏操作**。
|
||||
- `pl_transport`:已有则更新车辆司机等;token 稳定。
|
||||
|
||||
---
|
||||
|
||||
## 5. 错误处理与前端
|
||||
|
||||
| 场景 | 行为 |
|
||||
|------|------|
|
||||
| 主数据可保存 | 200 成功;下游问题进 `warnings` |
|
||||
| 部分出入库同步失败 | 已成功保留;warnings 列清单号与原因 |
|
||||
| 取消已过磅地磅 / 删除已过磅段产品 | 硬阻断 + 明确提示 |
|
||||
| 部署门禁 | 安排不得再走整批 revoke;不得返回「无法随运输安排自动重建」整单 400 |
|
||||
|
||||
前端:成功无警告正常提示;有 warnings 则成功+列表;硬阻断用弹窗。
|
||||
|
||||
---
|
||||
|
||||
## 6. 分期落地
|
||||
|
||||
### 一期(止血,对应该事故)
|
||||
|
||||
1. 安排/已安排保存:有出入库 → 强制回写车牌/产品/往来等(含已过磅)。
|
||||
2. 有过磅单 → 同步正文,称重不动。
|
||||
3. 去掉安排前 revoke;去掉「已过磅跳过正文同步」。
|
||||
4. 产品/往来变 → 月计划 find-or-create 改挂。
|
||||
|
||||
### 二期(根治断链)
|
||||
|
||||
1. `replace_plan_stops` 真·按 id upsert(落地/补齐 `2026-09-05`)。
|
||||
2. link 匹配键稳定;保存与安排共用 sync。
|
||||
|
||||
### 三期(体验与审计)
|
||||
|
||||
1. 调整结果摘要(新建/更新/改挂计数)。
|
||||
2. 可选写入 `pl_plan_change`。
|
||||
|
||||
---
|
||||
|
||||
## 7. 验收场景
|
||||
|
||||
1. 已安排 + 有出入库、未过磅:改车 → 出入库车牌变,**id 不变**。
|
||||
2. 已安排 + 有出入库 + 已过磅:改车 → 出入库与过磅单车牌都变,称重不变,出入库 id 不变。
|
||||
3. 新勾选生成地磅 → **新建**出入库单。
|
||||
4. 改产品或往来 → 出入库原地更新;月计划利旧或新建并改挂。
|
||||
5. 取消已过磅段地磅勾选 → 阻断。
|
||||
6. 司机原出入库 id / 现场 token 改车后仍可用。
|
||||
7. 重安排不再因「已关联地磅」整单 400(正文可同步时)。
|
||||
|
||||
---
|
||||
|
||||
## 8. 与既有文档关系
|
||||
|
||||
- **继承** `2026-09-05-pl-stop-stable-id-reuse-links-design.md` 的段/产品稳定 ID 与 link 复用目标。
|
||||
- **扩展/修正**:已过磅时仍强制同步出入库与过磅单**业务正文**(09-05 中「过磅单只读关联、不因保存重建」仍然成立;但允许**改正文字段**)。
|
||||
- **废除**:安排路径依赖 `revoke_regeneratable_dibang_for_transport` 的整批重建(该函数可保留给作废计划等场景)。
|
||||
|
||||
---
|
||||
|
||||
## 9. 实现触点(供后续 plan)
|
||||
|
||||
- `backend/app/services/pl_dibang_bridge_service.py`(`_update_existing_stop_inventory`、月计划改挂、过磅单正文同步)
|
||||
- `backend/app/api/pl/transport.py`(安排不再 revoke)
|
||||
- `backend/app/api/pl/plan.py`(已安排保存触发 sync)
|
||||
- `backend/app/services/pl_plan_stop_service.py`(`replace_plan_stops` upsert)
|
||||
- 测试:在既有 `test_pl_dibang_upsert_on_arrange.py` 上扩展已过磅改正文、月计划改挂用例
|
||||
Reference in new issue
Block a user