Files
shanxia2.0/openspec/changes/void-cancel-reason-required/design.md
T
2026-10-02 09:28:00 +08:00

3.3 KiB
Raw Blame History

Context

合同与调价函已强制作废原因。报价单仅有可选 remark 拼写;运输/车辆/结算/地磅的 cancel/void 无 body、无专用字段。用户确认采用独立字段三元组(方案 A),覆盖所列全部作废入口(含小程序);并明确:作废原因非必填,有则落库展示,无则可直接作废。

Goals / Non-Goals

Goals:

  • 作废支持填写原因并持久化独立字段,详情可展示(有则显示)。
  • 原因可选:留空不阻塞作废。
  • CRM 报价单对齐合同/调价函命名(invalid_*)。
  • PL 业务对齐 /cancel 语义(cancel_*)。
  • 地磅对齐 is_voided / /void(void_*)。
  • Web + 小程序相关入口行为一致。

Non-Goals:

  • 不改造合同、调价函既有「必填原因」流程。
  • 不改造公开页出入库单状态改「已作废」(本变更范围外)。
  • 不作废原因字典(自由文本即可)。
  • 不回填历史已作废单据的原因。

Decisions

D1 — 字段命名分域

域 字段 理由
CRM 报价单 invalid_reason / invalid_time / invalid_by 与合同、调价函一致
PL(计划、加油、充值、维保、尿素、结算) cancel_reason / cancel_time / cancel_by 与 /cancel、cancelled 一致
地磅 db_weighing_slip void_reason / void_time / void_by 与 is_voided、POST .../void 一致

*_reason:varchar(500),nullable。
*_time / *_by:作废成功时写入(即使原因为空,仍记录何时、何人作废)。

D2 — 原因可选(产品确认)

  • 前端弹窗提供「作废原因」输入框,不设 required;可直接确认作废。
  • API body 中原因字段 Optional;trim 后为空则存 NULL,不返回 400。
  • 非 BREAKING(相对「仅确认」的旧客户端):无 body / 无原因字段仍可作废;建议新客户端传 body 以便扩展。

D3 — API 契约

  • 报价单 invalid:可选 invalidReason → 写 invalid_*(原因可空;时间/人必写)。
  • PL cancel:可选 body { "cancelReason": "..." }。
  • 地磅 void:可选 body { "voidReason": "..." }。

D4 — 报价单迁移行为

  • 新作废写入 invalid_*,不再向 remark 追加 【作废原因】。
  • 历史 remark 拼接不自动迁移。

D5 — 加油作废恢复

restore 时清空 cancel_reason / cancel_time / cancel_by。

D6 — UI

  • Web:作废对话框含可选 textarea(placeholder 提示「选填」);列表与详情共用。
  • 小程序:可输入原因的弹层;允许空提交。
  • 查看态:有原因则展示;时间、操作人有则展示。

D7 — SQL 交付

  • 增量迁移 + 更新 docs/sql/shanxia2.0.sql 与 models。

Risks / Trade-offs

  • [原因常空] → 接受;至少有 *_time/*_by 可追溯操作。
  • [三套字段前缀] → 文档与 DTO 写清;UI 文案统一「作废原因」。
  • [公开出入库未纳入] → 另开变更。

Migration Plan

  1. ALTER 加列(均可 NULL)。
  2. 部署后端(接受可选原因)。
  3. 部署 web-admin + miniprogram(弹窗选填)。
  4. 回滚:列可保留;UI 可退回仅确认。

Open Questions

  • (无)地磅命名 void_*;原因非必填已确认。