--- schema: "hl-changelog/v2" ticket: "7164" title: "往来期初建账(客户应收 / 供应商应付 / 员工往来 三本账)" consumer: "admin" author: "yst" change_type: "新增接口" backend_status: "deployed" gateway_status: "verified" frontend_status: "verified" frontend_owner: "mmg" frontend_ref: "60ca7002" target_release: "" verified_at: "2026-09-10" status_note: "verified 2026-09-10 mmg: 推翻 fin-init 预埋替换为真实契约三端点。opening.js 重写(getOpeningBalancePage/createOpeningBalance/adjustOpeningBalance)+纯函数;fin-init/index.vue 整页重写 segment 三账套页签(切签注入必填 ledgerType),INIT 新建(refId 字符串透传/应收应付至少填一项/FileUpload 佐证必填),ADJUST 调整仅 INIT 行触发存全量值非差额(evidenceUrl+reason 必填);移除现金银行预埋。opening-balance.spec 锁端点/至少填一项/调整全量。checkpoint 全绿含 Vitest 全量+生产构建。" updated_at: "2026-09-06" base: "dev-v3" --- # 往来期初建账 ## 1. 接口背景 财务域「期初建账」:系统启用财务模块时,对客户应收、供应商应付、员工往来三本账做期初余额的一次性录入与后续调整。期初金额必须附佐证材料(影像 URL),期初基准日由后端取当前未封账账期的起始日,前端无需传。 - 路径前缀:`/admin/finance/opening-balances`(3 个新端点) - 服务:hl-finance(财务服务) ## 2. 变更清单 | 类型 | 接口 | 说明 | |---|---|---| | 新增 | GET `/admin/finance/opening-balances/page` | 往来期初分页(按账套过滤,支持类别/往来对象名筛选) | | 新增 | POST `/admin/finance/opening-balances` | 往来期初一次录入(同往来对象仅可录入一次初始期初) | | 新增 | POST `/admin/finance/opening-balances/adjust` | 往来期初调整单(须已录入初始期初;存调整全量值非差额) | ## 3. 接口详情 ### 3.1 期初分页 GET /page - 使用场景:管理后台「期初建账」三个页签(客户应收 / 供应商应付 / 员工往来)的列表查询,页签切换 = 换 `ledgerType`。 - 认证:管理后台管理员 Token(`Authorization: Bearer `),未登录返 401。 - 幂等性:查询接口,天然幂等。 - 限流:网关默认限流,无特殊配置。 ### 3.2 期初一次录入 POST / - 使用场景:首次为某往来对象建立期初余额(INIT 行)。同一 `(ledgerType, refId)` 仅允许一条 INIT,重复录入报 598401。 - 认证:同上。幂等性:非幂等(重复提交报 598401 不会产生重复 INIT 行,前端提交后应禁用按钮)。 - 限流:网关默认。 ### 3.3 期初调整单 POST /adjust - 使用场景:已录入 INIT 后修正期初金额。**存的是调整后全量值(非差额)**,每次调整落一行 `kind=ADJUST`,当前净额 = INIT + ΣADJUST 由后端现算。 - 认证:同上。幂等性:非幂等(每次提交落一行调整记录)。 - 限流:网关默认。 ## 4. 接口入参 ### 4.1 分页 Query(OpeningBalancePageReqVO) | 字段 | 类型 | 必填 | 约束 | 说明 | |---|---|---:|---|---| | `ledgerType` | String | 是 | `CUSTOMER`/`SUPPLIER`/`STAFF` | 账套:CUSTOMER 客户应收 / SUPPLIER 供应商应付 / STAFF 员工往来 | | `kind` | String | 否 | `INIT`/`ADJUST` | 类别:INIT 初始 / ADJUST 期初调整;空=全部 | | `refName` | String | 否 | 模糊匹配 | 往来对象名筛选;空=不限 | | `page` | Number | 否 | ≥1,默认 1 | 页码 | | `pageSize` | Number | 否 | 默认 20 | 每页条数 | ### 4.2 一次录入 Body(OpeningBalanceCreateReqVO) | 字段 | 类型 | 必填 | 约束 | 说明 | |---|---|---:|---|---| | `ledgerType` | String | 是 | 同上枚举 | 账套 | | `refId` | Number | 是 | — | 往来对象 ID(客户/供应商/员工主键,按账套对应) | | `refName` | String | 是 | ≤128 字 | 往来对象名称(冗余快照,落库后不随主数据改名) | | `openingPayable` | Number | 条件 | >0 | 期初应付(我欠他);与 openingReceivable 至少填一项 | | `openingReceivable` | Number | 条件 | >0 | 期初应收(他欠我们);与 openingPayable 至少填一项 | | `evidenceUrl` | String | 是 | — | 佐证材料影像 URL(必传,缺失抛 598402) | | `remark` | String | 否 | ≤200 字 | 备注 | ### 4.3 调整单 Body(OpeningBalanceAdjustReqVO) | 字段 | 类型 | 必填 | 约束 | 说明 | |---|---|---:|---|---| | `ledgerType` | String | 是 | 同上枚举 | 账套 | | `refId` | Number | 是 | — | 往来对象 ID(须已存在 INIT 行,否则 598404) | | `openingPayable` | Number | 否 | >0 | 调整后**全量**期初应付(不调整传 null) | | `openingReceivable` | Number | 否 | >0 | 调整后**全量**期初应收(不调整传 null) | | `evidenceUrl` | String | 是 | — | 佐证材料影像 URL(必传,缺失抛 598402) | | `reason` | String | 是 | ≤200 字 | 调整原因(必填留痕) | ## 5. 出参字段 ### 5.1 分页行(OpeningBalanceRowRespVO) | 字段 | 类型 | 说明 | |---|---|---| | `id` | String | 期初行 ID(雪花 ID,Long 序列化为字符串防 JS 精度丢失) | | `ledgerType` | String | 账套码(CUSTOMER/SUPPLIER/STAFF) | | `refId` | String | 往来对象 ID(雪花字符串) | | `refName` | String | 往来对象名称快照 | | `openingDate` | String | 期初基准日(yyyy-MM-dd,= 当前未封账账期起始日,后端带入) | | `kind` | String | 类别:INIT 初始 / ADJUST 期初调整 | | `openingPayable` | Number | 期初应付(无 = null) | | `openingReceivable` | Number | 期初应收(无 = null) | | `evidenceUrl` | String | 佐证材料影像 URL | | `recordedByName` | String | 录入人姓名快照 | | `createTime` | String | 创建时间(yyyy-MM-dd HH:mm:ss) | 分页包裹:`data.records[]` / `data.total` / `data.page` / `data.pageSize`。 ### 5.2 录入 / 调整响应(OpeningBalanceIdRespVO) | 字段 | 类型 | 说明 | |---|---|---| | `id` | String | 新落库期初行 ID(雪花字符串) | ## 6. 枚举 / 数据字典 ### `ledgerType`(账套,代码枚举) | 值 | 中文 | 说明 | |---|---|---| | `CUSTOMER` | 客户应收 | 客户欠我们的期初 | | `SUPPLIER` | 供应商应付 | 我们欠供应商的期初 | | `STAFF` | 员工往来 | 员工借款/备用金期初 | ### `kind`(期初行类别,代码枚举) | 值 | 中文 | 说明 | |---|---|---| | `INIT` | 初始期初 | 首次录入,同往来对象唯一 | | `ADJUST` | 期初调整 | 调整单,存调整后全量值,可多条 | ## 7. 错误码(段位 598400-598499) | 错误码 | 含义 | 触发场景 | |---|---|---| | 598401 | 该往来对象期初已录入 | 同 `(ledgerType, refId)` 重复 POST 录入 INIT | | 598402 | 佐证材料必传 | `evidenceUrl` 为空 | | 598403 | 账期已封账,不允许录入或调整期初 | 当前账期已封账 / 无未封账账期(视同未开账) | | 598404 | 该往来对象尚未录入初始期初,不可调整 | POST /adjust 时无 INIT 行 | | 598405 | 期初应付/应收须至少填一项 | 两项均空或均 ≤0 | | 598406 | 账套类型非法,须为 SUPPLIER、CUSTOMER 或 STAFF | `ledgerType` 传其他值 | ## 8. 示例 ### 8.1 典型成功(供应商应付期初录入) ```http POST /admin/finance/opening-balances Authorization: Bearer Content-Type: application/json { "ledgerType": "SUPPLIER", "refId": 2094123456789012345, "refName": "云山景区管理有限公司", "openingPayable": 50000.00, "openingReceivable": null, "evidenceUrl": "https://oss.example.com/evidence/abc.jpg", "remark": "2026 年 8 月对账单确认" } ``` ```json {"code":200,"message":"成功","success":true,"data":{"id":"2094300011122233445"}} ``` 分页查询: ```http GET /admin/finance/opening-balances/page?ledgerType=SUPPLIER&page=1&pageSize=20 ``` ```json {"code":200,"success":true,"data":{"records":[{"id":"2094300011122233445","ledgerType":"SUPPLIER","refId":"2094123456789012345","refName":"云山景区管理有限公司","openingDate":"2026-09-01","kind":"INIT","openingPayable":50000.00,"openingReceivable":null,"evidenceUrl":"https://oss.example.com/evidence/abc.jpg","recordedByName":"腰苏图","createTime":"2026-09-06 10:00:00"}],"total":1,"page":1,"pageSize":20}} ``` ### 8.2 边界情况(期初调整为零 / 双项同调) 调整单传全量值,可把某项调成新值、另一项保持 null 不动: ```json { "ledgerType": "SUPPLIER", "refId": 2094123456789012345, "openingPayable": 48000.00, "openingReceivable": null, "evidenceUrl": "https://oss.example.com/evidence/def.jpg", "reason": "对方减免 2000 元尾款" } ``` ```json {"code":200,"success":true,"data":{"id":"2094300099988877766"}} ``` 空分页:无数据时 `records=[]`、`total=0`,HTTP 200,前端正常渲染空列表。 ### 8.3 业务失败(重复录入 / 未录初始就调整) ```http POST /admin/finance/opening-balances # 同一 refId 再次录入 ``` ```json {"code":598401,"message":"该往来对象期初已录入","success":false,"data":null} ``` ```http POST /admin/finance/opening-balances/adjust # 该对象从未录入 INIT ``` ```json {"code":598404,"message":"该往来对象尚未录入初始期初,不可调整","success":false,"data":null} ``` 缺佐证材料:`{"code":598402,"message":"佐证材料必传"}`;两项金额均空:`{"code":598405,"message":"期初应付/应收须至少填一项"}`。 ## 9. 业务边界 **适用**: - 财务模块上线初始化时,批量为三本账建立期初基准。 - 期初金额有误时,通过调整单修正(保留全部调整留痕)。 **不适用 / 限制**: - 同往来对象只能录入一次 INIT,后续一律走调整单。 - 账期封账后禁止录入与调整(598403);未开账(无未封账账期)同样拦截。 - 期初基准日不可指定,由后端取当前未封账账期的 `start_date`。 - 期初数据**暂未回流勾稽到往来账**(方案 A,留 TODO 接缝),本期仅作独立台账。 **特殊边界**: - `refName` 是冗余快照,主数据改名不影响历史期初行显示。 - 调整单存**全量值**不是差额,前端表单应展示「调整后金额」而非「调增/调减」。 ## 10. 注意事项 - 所有雪花 ID(`id` / `refId`)均已序列化为字符串,前端按 String 处理,勿转 Number。 - `openingDate` 由后端带入,入参里没有该字段。 - 前端三个页签(客户应收 / 供应商应付 / 员工往来)对应 `ledgerType` 三值,建议页签切换即重置筛选条件重新查询。 - 新表 `fin_opening_balance`(Flyway `V20260906_101`),部署时自动执行,无需前端动作。 ## 11. 关联 / 联系人 - Issue:https://git.1814.love:8443/wx/HL/issues/7164 - PR:https://git.1814.love:8443/wx/HL/pulls/7166 - Commit:https://git.1814.love:8443/wx/HL/commit/d0f06fa2e8 - Epic:https://git.1814.love:8443/wx/HL/issues/7163 - 负责人:腰苏图(yst)