diff --git a/docs/superpowers/specs/2026-08-03-crm-contract-no-follow-sign-date-design.md b/docs/superpowers/specs/2026-08-03-crm-contract-no-follow-sign-date-design.md new file mode 100644 index 000000000..c3d44d992 --- /dev/null +++ b/docs/superpowers/specs/2026-08-03-crm-contract-no-follow-sign-date-design.md @@ -0,0 +1,96 @@ +# CRM 合同编号日期段跟随签订日期 — 设计 + +**日期**:2026-08-03 +**范围**:`web-admin` 合同管理新增/编辑;`backend` 合同 create/update(草稿) +**状态**:已评审,待实现 + +## 背景 + +合同编号格式为 `{供方编码}/X-JT{yyyyMMdd}{3位序号}`(例:`SX/X-JT20250803001`)。 +日期段来自签订日期,但当前改签订日期时编号不更新;选报价单带入 `quoteDate` 会改签订日,编号仍可能与日期不一致。 + +## 目标 + +1. 签订日期变更后,标准格式合同编号中的 **日期段** 跟随更新。 +2. **前端即时预览** + **后端保存时校验/校正**;撞号时由后端定最终号。 +3. 合同名称末尾括号内编号与合同编号同步。 + +## 非目标 + +- 不新增合同编号预览 API。 +- 不改合同编号租户内唯一索引 / 既有迁移脚本。 +- 不改续签命名规则。 +- 不做历史数据批量重刷。 +- 非草稿合同仍不可编辑(沿用现有约束)。 + +## 行为规则 + +### 标准格式 + +``` +^([A-Z]+)/X-JT(\d{8})(\d{3})$ +``` + +仅当当前编号匹配该格式时才联动;手改成非标准格式则 **不联动**。 + +### 改号策略(方案 A + 权威后端) + +1. 用签订日的 `yyyyMMdd` 替换日期段,**保留原 3 位序号**。 +2. 后端若候选号已被占用(租户内,**含软删**;编辑时排除本条),则序号 +1 直至可用(重试上限与现有 `generate_contract_no` 一致,约 50 次)。 +3. 编号为空:仍走现有「保存时按签订日全新生成」。 +4. 编号日期段已与签订日一致:前后端均 no-op。 + +### 名称同步 + +编号变更后,合同名称末尾 `(…)` / `(…)` 替换为新编号;与现有 `generate_contract_name`、前端名称重建习惯一致。 + +### 触发点 + +| 场景 | 行为 | +|------|------| +| 表单改签订日期 | 前端即时替换日期段 + 同步名称 | +| 选报价单带入 `quoteDate` | 经同一签订日变更路径,自然联动 | +| 换交易主体 | 仍只换供方编码前缀(现有逻辑),不因本需求改策略 | +| create / update(草稿)保存 | 后端 align;撞号顺延;再生成/校正合同名称 | + +## 实现要点 + +### 前端 + +主路径:`web-admin/src/views/crm/contract/components/ContractFormDialog.vue` +必要时对齐遗留页:`form.vue`。 + +- 在 `handleSignDateChange` 中,更新合同期限后调用 `syncContractNoDateFromSignDate`。 +- 匹配标准正则则替换 8 位日期,保留序号;再同步名称括号内编号。 +- 前端 **不** 做撞号查询;预览号可能与保存结果不同,以接口返回为准。 + +### 后端 + +文件:`backend/app/crud/crm/contract.py` + +- 新增 `align_contract_no_to_sign_date(...)`:标准格式换日期段;再按 `contract_no_exists` 撞号顺延。 +- **create**:有编号则先 align 再查重/落库;无编号则 `generate_contract_no(..., sign_date=…)`。 +- **update**:对提交编号(或原号)align,写回 `contract_no`,再用 `generate_contract_name` 校正名称。 +- 唯一性口径不变:租户内含软删、删除后不复用。 + +## 边界与错误 + +- 签订日清空:不改编号;合同期限按现有逻辑清空。 +- 同时改签订日 + 手改编号:后端按 **最终提交的编号** + **最终签订日** align;已非标准则不 align。 +- 序号顺延耗尽:抛错「生成合同编号失败,请稍后重试」,不静默写脏数据。 +- 非草稿 update:仍拒绝修改。 + +## 验收用例 + +1. 改签订日 → 编号日期段变、序号不变;名称括号同步。 +2. 目标号已占用 → 保存后序号顺延为可用号。 +3. 手改非标准编号 → 改签订日编号不变。 +4. 选报价单改签订日 → 编号日期段跟随(标准格式时)。 +5. 换交易主体 → 仅供方前缀变化,日期/序号策略与现网一致。 +6. 编号为空新增保存 → 仍按签订日生成完整新号。 + +## 决策记录 + +- 序号:保留原序号,撞号再顺延(A)。 +- 职责:前端预览 + 后端权威校正(两边都做)。 +- 名称:编号变更时名称内编号同步更新。