Files
shanxia2.0/docs/superpowers/specs/2026-07-19-pl-site-io-order-status-select-design.md
T
zlwuandCursor aa5fbe7780 docs(pl): 出入库现场状态可选设计、实现计划与 D5 旁注
记录显式 status 提交流程,并标注旧出入库设计中现场自动完成规则已被覆盖。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-19 17:16:39 +08:00

6.6 KiB
Raw Blame History

出入库现场链接 · 单据状态可选设计

日期: 2026-07-19
状态: 已确认
范围: web-admin 公共现场页 PublicIoOrderPage + backend public_io_order / apply_site_feedback
关联: 修正 2026-07-19-pl-inbound-outbound-order-design.md 中 D5「现场首次提交 → 自动已完成」规则
界面模式: 公共页对齐既有 public/pl/inbound|outbound(Vant)


1. 背景与问题

安排运输后生成出入库单现场链接。当前「提交」只保存实际数量、备注、附件;状态变更依赖服务端隐式规则:

  • 首次提交且 status == in_progress → 自动改为 completed
  • 再次提交不改状态
  • 页面状态只读展示,无法选手动状态(含作废)

现场需要明确选择单据状态(含作废),且默认展示当前真实状态。


2. 已确认决策

# 决策 选择
D1 实现方案 提交体携带 status,服务端按所选写入(方案 1)
D2 可选状态 in_progress / completed / voided(进行中 / 已完成 / 已作废)
D3 选择器默认值 进入页时展示 当前单据真实状态(多为进行中)
D4 作废后链接 仍可打开 只读查看,不可再提交 / 上传
D5 作废影响范围 仅本出入库单 status = voided;不联动作废运输计划、运输单、地磅、司机链接
D6 隐式完成 删除「首次提交自动已完成」
D7 入库 / 出库 对称;共用 PublicIoOrderPage 与 apply_site_feedback

3. 前端(公共页)

文件:web-admin/src/views/public/pl/_shared/PublicIoOrderPage.vue

3.1 状态选择

  • 在「现场备注」区块上方增加「单据状态」选择,使用 van-radio-group 横向三项(与现有 Vant 公共页一致)
  • 选项文案:进行中 / 已完成 / 已作废,值分别为 in_progress / completed / voided
  • loadPage 后:selectedStatus = page.status
  • 可编辑态:顶部信息卡「状态」改为只读展示当前已保存状态;下方选择器为本次拟提交状态
  • 只读态:仅顶部信息卡展示状态标签,不展示选择器

3.2 提交

  • saveFeedback body 增加 status: selectedStatus(必填)
  • 当用户选择 voided 并点提交:先 showConfirmDialog(或等价)确认文案大致为「确认作废本出入库单?作废后不可再提交。」;取消则不请求
  • 提交成功后 loadPage() 刷新

3.3 只读态(page.voided === true)

判定保持后端现有:header.status == "voided" 或 计划 follow_status == "cancelled"。

  • 不再仅用 van-empty「单据已作废」遮住整页
  • 展示与可编辑态相同的信息结构(业务信息、取送货、产品数量、回执、备注、状态)
  • 数量输入、上传、备注、状态选择、提交按钮全部禁用或隐藏
  • 顶部增加只读提示条:「单据已作废,仅可查看」

3.4 类型

  • web-admin/src/api/pl/public-io-order.ts:saveFeedback 参数增加 status

4. 后端

4.1 Schema

PublicIoOrderFeedback(backend/app/schemas/pl/io_order.py)增加:

status: str  # 必填;alias 可选;合法值 in_progress | completed | voided

非法值 → 业务 400「无效状态」。

4.2 apply_site_feedback

文件:backend/app/services/pl_io_order_service.py

  1. 若 header.status == "voided" → 仍抛 ValueError("单据已作废,无法提交")(调用方在 commit 前已用 page["voided"] 拦计划取消场景)
  2. 校验 status ∈ {in_progress, completed, voided}
  3. 写入 header.status = status(显式,不再隐式升级)
  4. 照旧写明细 actual_qty、site_feedback_*、site_feedback_time
  5. site_first_submitted_at:若为空则写入当前时间(与本次所选状态无关)
  6. 删除以下逻辑:
if not header.site_first_submitted_at:
    ...
    if header.status == "in_progress":
        header.status = "completed"

(首次提交时间与状态写入顺序:先写 feedback 字段与 status,再按需补 site_first_submitted_at。)

4.3 公共 API

backend/app/api/pl/public_io_order.py 的 save_feedback:把 payload.status 传入 apply_site_feedback。
page["voided"] 时拒绝提交 / 上传的既有逻辑不变。

4.4 非目标

  • 不修改管理端 PATCH 出入库单改状态逻辑
  • 不调用计划作废(void_plan)及其级联(运输取消、地磅、现场旧链 revoke 等)
  • 不改司机公共页、旧 pl_transport_site_link 现场页
  • 无 DDL 变更

5. 数据流

现场页 load → GET /public/pl/{inbound|outbound}/{token}
  → selectedStatus = page.status

用户编辑数量/备注/附件/状态 → 提交
  →(若 status=voided)确认弹窗
  → POST .../feedback { status, items, remark, images }
  → apply_site_feedback 写 status + 回执字段
  → 若写为 voided:下次 GET voided=true → 只读

6. 错误处理

情况 行为
链接无效 404 / 前端空态(既有)
已 voided 或计划已取消仍提交 400「单据已作废,无法提交」
status 缺失或非法 400「无效状态」
作废确认取消 不发请求

7. 验收

# 场景 期望
1 安排后首次打开现场链接 状态选择器为「进行中」
2 选「已完成」提交 单据 completed;管理端列表一致;可再次进入继续改数量/状态
3 选「进行中」提交(曾已完成) 单据回到 in_progress
4 选「已作废」并确认提交 仅本单 voided;运输计划跟进状态不变
5 作废后再打开 可查看历史回执与数量;不可提交/上传
6 管理端作废计划后打开现场链接 只读(既有 voided)
7 回归 无 status 的旧客户端若仍存在会失败;本期仅本仓库公共页,同步改前端即可

8. 改动文件清单

层 文件
Schema backend/app/schemas/pl/io_order.py
Service backend/app/services/pl_io_order_service.py
API backend/app/api/pl/public_io_order.py
前端 API web-admin/src/api/pl/public-io-order.ts
公共页 web-admin/src/views/public/pl/_shared/PublicIoOrderPage.vue
文档 本文件;实现后可在关联设计 D5 旁注「已被本设计覆盖」

9. 测试建议

  • 后端:对 apply_site_feedback 单测或手工:首次提交 completed / voided / in_progress;已 voided 再调应失败
  • 前端:入库与出库各走一遍上述验收表 1–5