diff --git a/changelogs-v2/2026-09/29_8507_应收期初供应商应收账套-修改接口-管理后台.md b/changelogs-v2/2026-09/29_8507_应收期初供应商应收账套-修改接口-管理后台.md new file mode 100644 index 00000000..d3739d84 --- /dev/null +++ b/changelogs-v2/2026-09/29_8507_应收期初供应商应收账套-修改接口-管理后台.md @@ -0,0 +1,353 @@ +--- +schema: "hl-changelog/v2" +ticket: "8507" +title: "往来期初 ledgerType 新增 SUPPLIER_RECV 供应商应收账套,支撑财务初始化「应收初始化」tab" +consumer: "admin" +author: "yst(GIT)" +change_type: "修改接口" +backend_status: "deployed" +gateway_status: "verified" +frontend_status: "pending" +frontend_owner: "mmg" +frontend_ref: "pending" +target_release: "v2.1" +verified_at: "2026-09-29" +status_note: "POST /admin/finance/opening-balances 的 ledgerType 枚举新增 SUPPLIER_RECV(供应商应收:他欠我们的杂项应收,如押金退还/赔偿款/口车费),配套应收性质字典 fin_recv_nature(DEPOSIT_REFUND/COMPENSATION/CAR_FEE/OTHER)+ 错误码 598409/598410;同时收紧金额方向:SUPPLIER 只认 openingPayable>0,CUSTOMER/SUPPLIER_RECV 只认 openingReceivable>0。测试环境已部署并行为验证通过。前端需新增财务初始化「应收初始化」tab(7 字段表单,字段映射见本文档 §三/§四)。" +updated_at: "2026-09-29" +base: "dev-v3" +--- + +# finance:往来期初新增供应商应收账套 SUPPLIER_RECV(应收初始化 tab 对接)(管理后台) + +**服务**: hl-order-service-v3(finance 模块,同进程) +**PR**: [#8509](https://git.1814.love/wx/HL/pulls/8509)、[#8513](https://git.1814.love/wx/HL/pulls/8513) +**Issue**: [#8507](https://git.1814.love/wx/HL/issues/8507) + +--- + +## 一、接口背景 + +管理后台「财务初始化」要新增一个**「应收初始化」tab**:记录**供应商欠我们**的杂项应收(押金退还、赔偿款、口车费等,不走订单流水的零散应收)。 + +支撑这个 tab,往来期初接口 `POST /admin/finance/opening-balances` 的账套枚举 `ledgerType` 新增第三个值 **`SUPPLIER_RECV`**(供应商应收)。 + +原有账套语义不变: + +| 账套值 | 中文 | 方向 | 谁欠谁 | +|---|---|---|---| +| `SUPPLIER` | 供应商应付 | 应付 | 我们欠供应商 | +| `CUSTOMER` | 客户应收 | 应收 | 客户欠我们 | +| `SUPPLIER_RECV` ✨ | 供应商应收 | 应收 | 供应商欠我们(本次新增) | + +> ⚠️ 枚举值是 `SUPPLIER_RECV`(13 字符),**不是** `SUPPLIER_RECEIVABLE`(18 字符超列宽,#8511 已改短)。前端写常量时别用长名。 + +## 二、变更清单 + +| # | 接口 | 方法 | 路径 | 变更类型 | 说明 | +|---|------|------|------|----------|------| +| 1 | 往来期初一次录入 | POST | `/admin/finance/opening-balances` | 修改接口 | `ledgerType` 新增 `SUPPLIER_RECV`;金额方向校验收紧(见 §十) | + +配套数据源接口(均**早已上线**,无变更,仅列出供新 tab 对接):供应商下拉 `GET /admin/supplier/items/list`、应收性质下拉 `GET /admin/finance/receipt/options`、所属公司下拉 `GET /v3/admin/travel-agency/enabled`,详见 §4.3 ~ §4.5。 + +## 三、接口详情 + +- **路径**:`POST /admin/finance/opening-balances` +- **认证**:网关 JWT(admin) +- **幂等性**:否。但同一往来对象同一账套**仅可录一次**初始期初,重复提交被 598401 拦截(要改金额走「期初调整」),不会产生脏数据 +- **限流**:无特殊限流 + +**应收初始化(SUPPLIER_RECV)表单最终形态(7 个元素)**: + +| # | 表单元素 | 控件 | 必填 | 提交字段 | +|---|---|---|---|---| +| 1 | 供应商 | 下拉(可搜索) | ✅ | `refId` + `refName` | +| 2 | 应收性质 | 下拉 | ✅ | `customerCategory`(复用此字段存应收性质码值) | +| 3 | 应收金额 | 数字输入 | ✅ | `openingReceivable` | +| 4 | 所属公司 | 下拉 | ✅ | `companyId` | +| 5 | 记账日期 | 只读展示 | — | **后端取,前端不传** | +| 6 | 佐证材料 | 图片上传 | ❌ 选填 | `evidenceUrl` | +| 7 | 备注 | 文本域 | ❌ 选填 | `remark`(≤ 200 字) | + +**本 tab 表单上不要出现的元素**: + +| 不要出现 | 原因 | +|---|---| +| 应付金额框 | 供应商应收账套只有应收方向,`openingPayable` 不传 | +| 供应商 ID / 名称手填框 | 必须走下拉,ID 与名称由选项带出 | +| 记账日期输入框 | 只读展示,后端取当前未封账账期 startDate 落库 | + +## 四、接口入参 + +### 4.1 路径参数 / Query 参数 + +无。 + +### 4.2 请求体字段(SUPPLIER_RECV 账套) + +| 字段 | 类型 | 必填 | 说明 | 校验规则 | +|------|------|------|------|----------| +| `ledgerType` | String | ✅ | 账套类型。本 tab **固定传 `SUPPLIER_RECV`** | 枚举,见 §6.1 | +| `refId` | String | ✅ | 供应商 ID(取下拉项的 `supplierId`) | 必须是存在的供应商 | +| `refName` | String | ✅ | 供应商名称(取下拉项的 `fullName`) | ≤ 128 字 | +| `customerCategory` | String | ✅ | 应收性质码值(复用该字段,取应收性质下拉的 `value`) | 必须是应收性质字典 `fin_recv_nature` 的值,见 §6.2;缺失 → 598409,取值非法 → 598410 | +| `companyId` | String | ✅ | 所属公司主体 ID(取下拉项的 `agencyId`) | 必须是启用中的公司主体,否则 598407 | +| `openingReceivable` | Number | ✅ | 应收期初金额(元) | 必填且 **> 0**,否则 598405 | +| `evidenceUrl` | String | ❌ | 佐证材料图片 URL | 可空 / 不传 | +| `remark` | String | ❌ | 备注 | ≤ 200 字 | + +**不要传的字段**: + +| 字段 | 说明 | +|---|---| +| `openingPayable` | 应付期初金额,应收账套(CUSTOMER / SUPPLIER_RECV)不传 | +| 记账日期相关字段 | 请求体里没有记账日期字段,后端自动取当前未封账账期 startDate | + +> ⚠️ `refId` / `companyId` 后端是 Long 大数(雪花 ID),JSON 序列化为字符串下发;前端 JS 一律**当字符串处理**,不要 `Number()` 转换,防精度丢失。 + +### 4.3 供应商下拉数据源 + +- **路径**:`GET /admin/supplier/items/list`(资源服务,已上线) +- **认证**:网关 JWT(admin) + +Query 参数(全部选填): + +| 字段 | 类型 | 必填 | 说明 | +|------|------|------|------| +| `status` | String | ❌ | 本表单**固定传 `ACTIVE`**(只在用供应商可录期初) | +| `keyword` | String | ❌ | 名称 / 编号模糊搜索 | +| `limit` | Integer | ❌ | 默认 50,最大 200 | + +响应 `data` 为数组,每项字段: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `supplierId` | String | 供应商 ID(Long 序列化字符串) | +| `fullName` | String | 供应商全称(下拉显示用) | +| `shortName` | String | 简称 | +| `supplierNo` | String | 供应商编号 | +| `statusName` | String | 状态中文名 | + +**取值映射**:下拉显示 `fullName`;选中后提交 `refId = supplierId`、`refName = fullName`。 + +### 4.4 应收性质下拉数据源 + +- **路径**:`GET /admin/finance/receipt/options`(#8501 已上线) +- **认证**:网关 JWT(admin) +- **入参**:无 +- **取数**:取响应 `data.recvNatures` 数组(每项 `value` / `label`) +- **取值映射**:下拉显示 `label`;选中后提交 `customerCategory = value`。字典 `fin_recv_nature` 值见 §6.2 + +### 4.5 所属公司下拉数据源 + +- **路径**:`GET /v3/admin/travel-agency/enabled`(order-v3,已上线) +- **认证**:网关 JWT(admin) +- **入参**:无 + +响应 `data` 为数组,每项字段: + +| 字段 | 类型 | 说明 | +|------|------|------| +| `agencyId` | String | 公司主体 ID(Long 序列化字符串) | +| `agencyName` | String | 公司名称(下拉显示用) | +| `isPrimary` | Integer | 是否主体公司:1 = 是,0 = 否 | + +**取值映射**:下拉显示 `agencyName`;`isPrimary = 1` 的主体公司**默认选中**;选中后提交 `companyId = agencyId`。公司名称由后端自取快照,前端不用传。 + +### 4.6 记账日期展示值来源(只读,不入参) + +记账日期 = 当前**未封账**账期的 `startDate`,后端落库时自动取,前端不传。若表单上要展示该值: + +- **路径**:`GET /admin/finance/account-periods/page` +- **取数**:取返回列表中 `isClosed = 0`(未封账)那一行的 `startDate` 展示即可 + +## 五、出参 + +| 字段 | 类型 | 说明 | +|------|------|------| +| `data` | String | 新建期初行 ID(Long 序列化字符串,JS 当字符串处理) | + +外层为标准响应包:`code` / `data` / `message` / `success`。 + +## 六、枚举 / 数据字典 + +### 6.1 ledgerType(账套类型) + +**所属字段**:请求体 `ledgerType` | **类型**:`String` | **必填**:✅ + +| 值 | 中文 | 说明 | +|----|------|------| +| `SUPPLIER` | 供应商应付 | 我们欠供应商;只传 `openingPayable` | +| `CUSTOMER` | 客户应收 | 客户欠我们;只传 `openingReceivable` | +| `SUPPLIER_RECV` ✨ | 供应商应收 | 供应商欠我们(杂项应收);只传 `openingReceivable`,且 `customerCategory` 必填 | + +### 6.2 customerCategory(应收性质,字典 fin_recv_nature) + +**所属字段**:请求体 `customerCategory` | **类型**:`String` | **必填**:✅(SUPPLIER_RECV 账套必填) + +| 值 | 中文 | 说明 | +|----|------|------| +| `DEPOSIT_REFUND` | 押金退还 | 供应商应退未退的押金 | +| `COMPENSATION` | 赔偿款 | 供应商应赔付的款项 | +| `CAR_FEE` | 口车费 | 供应商应付的口车费用 | +| `OTHER` | 其他应收 | 其他杂项应收 | + +> 数据源是 §4.4 的 `/admin/finance/receipt/options`(`recvNatures` 数组),**前端不要写死这 4 个值**,后端字典加值时下拉自动多一项。 + +## 七、错误码 + +| code | 含义 | 触发场景 | 前端 toast 建议文案 | +|------|------|----------|---------------------| +| 598409 | 应收性质必填 | SUPPLIER_RECV 账套未传 `customerCategory` | 请选择应收性质 | +| 598410 | 应收性质取值非法 | `customerCategory` 不在应收性质字典内 | 应收性质无效 | +| 598405 | 期初金额未填或 ≤ 0 | `openingReceivable` 缺失 / 为 0 / 负数 | 请填写正确的应收金额 | +| 598401 | 该往来对象期初已录过 | 同一供应商同账套重复提交(要改走「期初调整」) | 该供应商期初已录入,要改走「期初调整」 | +| 598407 | 所属公司非法或已停用 | `companyId` 不存在或公司主体已停用 | 所属公司无效 | +| 598403 | 无未封账账期 | 当前没有 `isClosed = 0` 的账期,无法落记账日期 | 当前无进行中账期 | +| 598406 | 账套非法 | `ledgerType` 不是 SUPPLIER / CUSTOMER / SUPPLIER_RECV 之一 | 账套须为 SUPPLIER/CUSTOMER/SUPPLIER_RECV | + +## 八、示例 + +### 8.1 典型成功 + +**请求**: + +```http +POST /admin/finance/opening-balances +Authorization: Bearer +Content-Type: application/json +``` + +```json +{ + "ledgerType": "SUPPLIER_RECV", + "refId": "1834567890123456789", + "refName": "内蒙古某车队有限公司", + "customerCategory": "DEPOSIT_REFUND", + "companyId": "1723456789012345678", + "openingReceivable": 1500.00, + "evidenceUrl": "https://oss.example.com/finance/evidence/x.jpg", + "remark": "2025 年度押金应退未退" +} +``` + +**响应**: + +```json +{ + "code": 200, + "data": "1956789012345678901", + "message": "成功", + "success": true +} +``` + +### 8.2 边界(佐证、备注均不传,金额最小值) + +**场景说明**:`evidenceUrl` / `remark` 选填,最小合法请求 6 个字段;金额边界 0.01。 + +**请求**: + +```json +{ + "ledgerType": "SUPPLIER_RECV", + "refId": "1834567890123456789", + "refName": "内蒙古某车队有限公司", + "customerCategory": "OTHER", + "companyId": "1723456789012345678", + "openingReceivable": 0.01 +} +``` + +**响应**: + +```json +{ + "code": 200, + "data": "1956789012345678902", + "message": "成功", + "success": true +} +``` + +### 8.3 业务失败(应收性质未传,触发 598409) + +**请求**: + +```json +{ + "ledgerType": "SUPPLIER_RECV", + "refId": "1834567890123456789", + "refName": "内蒙古某车队有限公司", + "companyId": "1723456789012345678", + "openingReceivable": 1500.00 +} +``` + +**响应**: + +```json +{ + "code": 598409, + "data": null, + "message": "应收性质必填", + "success": false +} +``` + +> 其他常见失败:重复提交同一供应商 → `598401`;`ledgerType` 拼错(如 `SUPPLIER_RECEIVABLE` 长名)→ `598406`。 + +## 九、业务边界 + +- ✅ **适用场景**:供应商存在杂项应收(押金退还 / 赔偿款 / 口车费 / 其他)首次录期初,且当前存在未封账账期、所属公司主体启用中。 +- ❌ **不适用场景**: + - 该供应商已录过本账套期初 → 598401,改金额请走「期初调整」,不要重复提交; + - 无未封账账期 → 598403; + - 所属公司已停用 → 598407。 +- ⚠️ **特殊边界**: + - 记账日期恒等于当前未封账账期 startDate(后端落库),不可指定历史 / 未来日期; + - 一账套一方向:SUPPLIER_RECV 只录应收,录应付请切「应付初始化」tab(`SUPPLIER` 账套)。 + +## 十、修改前后对比 + +### 10.1 字段 / 枚举级对比 + +| 字段 | 改前 | 改后 | +|------|------|------| +| `ledgerType` | 2 个值:`SUPPLIER` / `CUSTOMER` | 3 个值:新增 **`SUPPLIER_RECV`**(供应商应收) | +| `customerCategory` | 仅 CUSTOMER 账套使用 | SUPPLIER_RECV 账套**复用此字段**存应收性质码值(必填,字典 `fin_recv_nature`) | + +### 10.2 行为级对比(金额方向收紧,本次同步生效) + +| 账套 | 改前 | 改后 | +|------|------|------| +| `SUPPLIER` | 应收 / 应付金额校验相对宽松 | **只认 `openingPayable` > 0**,应收方向不传 | +| `CUSTOMER` | 同上 | **只认 `openingReceivable` > 0**,应付方向不传 | +| `SUPPLIER_RECV` | (不存在) | **只认 `openingReceivable` > 0**,且 `customerCategory` 必填 | + +## 十一、影响评估 / 回滚 + +- **是否破坏向后兼容**:基本否。`SUPPLIER` / `CUSTOMER` 已有表单若本来就按「一账套一方向」正确提交(只传一个方向金额),不受金额方向收紧影响;`SUPPLIER_RECV` 是纯新增枚举值,老代码不传它即可。 +- **前端是否必须同步上线**:是(针对「应收初始化」tab)。该 tab 未上线前后端能力空转无影响;tab 上线时必须按本文档字段映射对接。 +- **影响已有数据**:无(无需前端配合的数据迁移)。 +- **回滚方案**:后端 revert 两个 PR 即可;前端 tab 未上线则无需回滚。 + +## 十二、注意事项 + +- ⚠️ 枚举值是 **`SUPPLIER_RECV`(13 字符)**,不是 `SUPPLIER_RECEIVABLE`(18 字符超列宽,#8511 已改短)。拼错会触发 598406。 +- ⚠️ `refId` / `companyId` / 响应 `data` 均为 Long 大数序列化的字符串,JS 全程当字符串处理,禁止 `Number()` 转换。 +- ⚠️ `customerCategory` 字段在 SUPPLIER_RECV 账套下语义是「应收性质」,值必须来自 §4.4 下拉(字典 `fin_recv_nature`),**不要写死 4 个码值**。 +- ⚠️ 记账日期前端不传;如需展示,查 `GET /admin/finance/account-periods/page` 取 `isClosed = 0` 行的 `startDate`。 +- ⚠️ 佐证材料 `evidenceUrl` 是**选填**,不要加必填校验拦截提交。 +- 本次已部署测试服并行为验证通过,前端可立即联调。 + +## 十三、关联 / 联系人 + +### 13.1 链接 + +- **Issue**: [#8507](https://git.1814.love/wx/HL/issues/8507) +- **PR**: [#8509](https://git.1814.love/wx/HL/pulls/8509)、[#8513](https://git.1814.love/wx/HL/pulls/8513) +- **前置依赖**: [#8501](https://git.1814.love/wx/HL/issues/8501)(应收性质字典 + `/receipt/options` 接口)、[#8511](https://git.1814.love/wx/HL/issues/8511)(枚举值改短 SUPPLIER_RECEIVABLE → SUPPLIER_RECV) + +### 13.2 联系人 + +- **后端负责人**: yst