28 KiB
schema, ticket, title, consumer, author, change_type, backend_status, gateway_status, frontend_status, frontend_owner, frontend_ref, target_release, verified_at, status_note, updated_at, base
| schema | ticket | title | consumer | author | change_type | backend_status | gateway_status | frontend_status | frontend_owner | frontend_ref | target_release | verified_at | status_note | updated_at | base |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| hl-changelog/v2 | invoice-in-domain-launch | 收票(进项发票)域上线——登记/编辑/核对/作废 + 关联勾选 + 供应商对账(8 个端点) | admin | yst(GIT) | 新增接口 | deployed | verified | verified | mmg | f5751df4cc81568229bc1ae9fa770b6bf28a2120 | 2026-09-17 | 财务域收票(进项发票)首次交付:登记收票 → 核对 → 作废主链 + 按供应商捞可勾选应付/预付 + 供应商收票对账共 8 个新端点。一张票可挂多笔应付/预付/费用报销(ΣmatchAmount ≤ invoiceAmount,允许部分匹配)也可零关联纯挂供应商;不管进项税抵扣、不接税务查验。后端已合并 dev-v3 并部署测试服,行为级验证通过。菜单「收票管理」(path=/finance/invoice/in)已挂「发票管理」目录下并授权 SUPER_ADMIN/ADMIN/FINANCE。前端此前无收票页面,按本文一次对接即可。[mmg 2026-09-17 交付] ①api/finance/invoice-in.js 8 端点+RECEIVED/VERIFIED/VOIDED 三态字典(写四函数关去重;599400-599411 拦截器透 message 不建字典;docstring 标注全删重插/Σ≤票面 599406/599408 非 400/biz-candidates 不捞 EXPENSE);②finance/invoice/in 页内三视图:台账列表(发票号模糊+供应商弹窗精确+状态+收票日期区间)|供应商收票对账(应收=应付+预付生效合计/已收只认 VERIFIED/未收>0 警示)|详情只读(留痕快照+关联明细空态「纯挂供应商」);③行操作按 status 原码:RECEIVED=[查看,编辑|核对,作废] renderActionsWithMore,VERIFIED=[查看,作废],VOIDED=[查看];作废原因 NForm 必填 trim≤500;④登记/编辑弹窗:选定供应商即拉 biz-candidates 换供应商重拉;编辑拉详情三路合并建行——候选行/详情 PAYMENT·PREPAY 行(上限=remainingAmount+本票原 matchAmount)/EXPENSE 只读保留行(仅可移除,整包回传防全删重插静默清掉);ΣmatchAmount≤invoiceAmount NForm 预校验;create 无勾选=纯挂供应商不带 relations,update 恒带整包;invoiceType 走字典 fin_invoice_in_type,xxxName 后端回填直渲;⑤原型状态选项旧版以契约三态为准,原型无对账/详情布局按 PayableDetailPanel 模式自建。api spec 8 例+页面 spec 7 例+弹窗 spec 6 例,定向 21/21,scoped checkpoint 13 项全绿。 | 2026-09-17 | dev-v3 |
收票(进项发票)域上线(管理后台)
服务: hl-order-service-v3(hl-finance 模块,端口 8086);字典 / 菜单在 hl-user-service 类型: 🆕 新增(本期首次交付,前端首次对接) 日期: 2026-09-17 影响范围: 管理后台财务域「发票管理 - 收票管理」页面(新页面) 覆盖 Epic: #7857(代码 PR-1 #7860 地基两表 / PR-2 #7874 核心接口+字典种子 / PR-3 #7880 核对/作废+对账+菜单 / 文档 PR #7891)
一、接口背景
收票(进项发票) = 供应商开给我方的发票台账。业务主链:
登记收票(RECEIVED 已收票) → 核对(VERIFIED 已核对)
↘ 作废(VOIDED 已作废,终态,RECEIVED/VERIFIED 均可作废)
核心口径:
- 供应商是对账锚点:每张票必挂一个开票供应商(反查资源域,无效 → 599410 fail-fast)
- 一张票可挂多笔业务:应付 / 预付 / 费用报销均可关联(N:N 关系表),
ΣmatchAmount ≤ invoiceAmount(应用层校验,允许部分匹配);也可零关联纯挂供应商(票先到、业务后补挂的场景走编辑补关联) - 不管进项税抵扣:
taxRate/taxAmount仅记录可空,不做税额勾稽 - 不接税务查验:纯内部台账,无外部税局接口
- 核对 / 作废全留痕:操作人 ID + 姓名快照 + 时间落库,详情接口可见
配套查询:供应商收票对账(每行一供应商:应收票额 = 应付+预付已生效合计,已收 = VERIFIED 票合计,未收 = 差额),用于追供应商欠票。
二、变更清单
| # | 变更 | 类型 | 说明 |
|---|---|---|---|
| 1 | GET /admin/finance/invoice-in/page |
✨ 新增 | 进项发票分页(供应商 / 状态 / 发票号码模糊 / 收票日期区间筛选) |
| 2 | GET /admin/finance/invoice-in/{id} |
✨ 新增 | 进项发票详情(主表全字段 + relations 关联列表 + 核对/作废留痕快照) |
| 3 | POST /admin/finance/invoice-in/create |
✨ 新增 | 登记进项发票(落 RECEIVED;可挂多笔应付/预付/费用报销,也可纯挂供应商) |
| 4 | PUT /admin/finance/invoice-in/update |
✨ 新增 | 编辑进项发票(仅 RECEIVED 态;关联全删重插) |
| 5 | POST /admin/finance/invoice-in/verify |
✨ 新增 | 核对进项发票(RECEIVED→VERIFIED;CAS 守卫 + 核对人快照) |
| 6 | POST /admin/finance/invoice-in/void |
✨ 新增 | 作废进项发票(RECEIVED/VERIFIED→VOIDED 终态;作废原因必填 599408) |
| 7 | GET /admin/finance/invoice-in/biz-candidates |
✨ 新增 | 按供应商捞可勾选业务单据(已生效且未收齐票的应付/预付,供登记关联勾选) |
| 8 | GET /admin/finance/invoice-in/supplier-recon/page |
✨ 新增 | 供应商收票对账分页(应收 / 已收 / 未收三列) |
| 9 | 错误码段位 599400-599499 | ✨ 新增 | 5994 段首次落码(hl-finance),启用 599400-599411 |
| 10 | 字典 fin_invoice_in_type |
✨ 新增 | 发票类型字典(dict_type_id=10163):SPECIAL 专票 / NORMAL 普票 / ELECTRONIC 电子票 |
| 11 | 菜单「收票管理」 | ✨ 新增 | menu_id=2026090900000000901,path=/finance/invoice/in,挂「发票管理」(...0900) 目录下,已授权 SUPER_ADMIN / ADMIN / FINANCE |
三、接口详情
| 项 | 说明 |
|---|---|
| 使用场景 | 管理后台财务域「发票管理 - 收票管理」:收票登记 / 核对 / 作废 + 供应商欠票对账 |
| 认证 | 管理后台 JWT(网关统一鉴权),需「收票管理」菜单权限(SUPER_ADMIN / ADMIN / FINANCE 已授权) |
| 幂等性 | 查询类(page / detail / biz-candidates / supplier-recon)只读幂等;写类:登记靠票号唯一约束防重(599401),核对走 CAS 状态守卫(重复核对 599403),作废走状态机守卫(已作废 599404),重复提交安全 |
| 限流 | 走网关统一限流,无模块特殊限流 |
| ID 序列化 | 所有 Long 型 ID(id / supplierId / bizId / verifyBy / voidBy / 关联行 id)序列化为 String,防 JS 精度丢失 |
| 状态机 | RECEIVED 已收票 → VERIFIED 已核对;RECEIVED / VERIFIED → VOIDED 已作废(终态,任何写操作拦截 599404) |
四、接口入参
分页参数(page / supplier-recon 两个分页端点通用,继承统一分页基类):
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
page |
Integer | 否 | 页码,默认 1,最小 1(兼容别名 pageNo) |
pageSize |
Integer | 否 | 每页条数,默认 20,范围 1-100 |
4.1 GET /page 进项发票分页 Query 入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
invoiceNo |
String | 否 | 发票号码(模糊);空 = 不限 |
supplierId |
Long | 否 | 开票供应商ID 精确;空 = 不限 |
status |
String | 否 | 单据状态:RECEIVED / VERIFIED / VOIDED;空 = 全部 |
receiveDateStart |
Date | 否 | 收票日期起(闭区间),格式 yyyy-MM-dd;空 = 不限 |
receiveDateEnd |
Date | 否 | 收票日期止(闭区间),格式 yyyy-MM-dd;空 = 不限 |
4.2 GET /{id} 详情 路径入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
id |
Long | ✅ | 进项发票ID(路径变量;不存在或已软删 → 599400) |
4.3 POST /create 登记 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
invoiceNo |
String | ✅ | 发票号码,≤ 40 字符;全局唯一,重复 → 599401 |
invoiceCode |
String | 否 | 发票代码(纸质票有,电子票可空),≤ 20 字符 |
invoiceType |
String | ✅ | 发票类型,字典 fin_invoice_in_type 取值:SPECIAL / NORMAL / ELECTRONIC;域外值 → 599409 |
supplierId |
Long | ✅ | 开票供应商ID;反查资源域无效 / 不可付款 / 服务降级 → 599410(涉钱 fail-fast) |
invoiceAmount |
BigDecimal | ✅ | 价税合计,须 > 0,否则 → 599405 |
taxRate |
BigDecimal | 否 | 税率%(仅记录,不管进项税抵扣) |
taxAmount |
BigDecimal | 否 | 税额(仅记录,不管进项税抵扣) |
invoiceDate |
Date | 否 | 开票日期,格式 yyyy-MM-dd |
receiveDate |
Date | 否 | 收票日期,格式 yyyy-MM-dd |
voucherUrl |
String | 否 | 发票影像URL,≤ 500 字符 |
remark |
String | 否 | 备注,≤ 500 字符 |
relations |
Array | 否 | 关联业务源列表;可空 = 纯挂供应商;一张票可挂多笔。元素结构见下 |
relations[] 元素(InvoiceInRelReqVO):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
bizType |
String | ✅ | 业务类型:PAYMENT 应付款 / PREPAY 预付款 / EXPENSE 费用报销 |
bizId |
Long | ✅ | 业务单据ID;查无此单 / 归属不符 → 599407 |
matchAmount |
BigDecimal | ✅ | 本票对该笔的匹配金额,须 > 0(否则 599405);同票 ΣmatchAmount ≤ invoiceAmount(否则 599406) |
同一张票重复关联同一笔业务单据(同 bizType+bizId)→ 599411。
4.4 PUT /update 编辑 请求体
字段同 4.3 登记请求体,额外加:
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
invoiceInId |
Long | ✅ | 进项发票ID;仅 RECEIVED 态可编辑(VERIFIED → 599402/599403,VOIDED → 599404) |
关联走全删重插:
relations传什么就是什么;传空数组 / 不传 = 清空全部关联(变纯挂供应商票)。校验规则同登记。
4.5 POST /verify 核对 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
invoiceInId |
Long | ✅ | 进项发票ID;仅 RECEIVED → VERIFIED(CAS 守卫;已核对 → 599403,已作废 → 599404) |
4.6 POST /void 作废 请求体
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
invoiceInId |
Long | ✅ | 进项发票ID;RECEIVED / VERIFIED → VOIDED 终态(已作废 → 599404) |
voidReason |
String | ✅ | 作废原因,≤ 500 字符;空(含纯空白)→ 599408(不加 @NotBlank,空原因落业务错误码而非 400) |
4.7 GET /biz-candidates 可勾选业务单据 Query 入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
supplierId |
Long | ✅ | 供应商ID;捞该供应商名下已生效且未收齐票的应付/预付单据 |
4.8 GET /supplier-recon/page 供应商收票对账 Query 入参
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
supplierName |
String | 否 | 供应商名称(模糊);空 = 不限 |
五、出参字段
统一外层 Result<T>:code(0=成功,否则业务错误码)/ message / data。分页端点 data 为 PageResult:records / total / page / pageSize。
POST /create返回data = { "id": "String" }(新票 ID,状态 RECEIVED)PUT /update、POST /verify、POST /void返回data = null(Result<Void>)
5.1 列表行 InvoiceInRespVO(/page 的 records 元素)
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String | 进项发票ID(Long → String) |
invoiceNo |
String | 发票号码 |
invoiceCode |
String | 发票代码(可空) |
invoiceType |
String | 发票类型:SPECIAL / NORMAL / ELECTRONIC |
invoiceTypeName |
String | 发票类型中文名(字典 label 回填:专票 / 普票 / 电子票) |
supplierId |
String | 开票供应商ID(Long → String) |
supplierName |
String | 供应商名称快照 |
invoiceAmount |
BigDecimal | 价税合计 |
taxRate |
BigDecimal | 税率%(可空) |
taxAmount |
BigDecimal | 税额(可空) |
invoiceDate |
Date | 开票日期(可空) |
receiveDate |
Date | 收票日期(可空) |
voucherUrl |
String | 发票影像URL(可空) |
remark |
String | 备注(可空) |
status |
String | 单据状态:RECEIVED / VERIFIED / VOIDED |
statusName |
String | 状态中文名(已收票 / 已核对 / 已作废) |
voidReason |
String | 作废原因(仅 status=VOIDED 时有值,其余为 null) |
createTime |
DateTime | 创建时间 |
5.2 详情 InvoiceInDetailRespVO(GET /{id})
= 5.1 全部字段 + 以下追加:
| 字段 | 类型 | 说明 |
|---|---|---|
verifyBy |
String | 核对人ID(status=VERIFIED 时有值,Long → String) |
verifyByName |
String | 核对人姓名快照 |
verifyTime |
DateTime | 核对时间 |
voidBy |
String | 作废人ID(status=VOIDED 时有值,Long → String) |
voidByName |
String | 作废人姓名快照 |
voidTime |
DateTime | 作废时间 |
relations |
Array | 关联业务源列表(按登记顺序升序;纯挂供应商票 = 空列表 [],不为 null) |
relations[] 元素(InvoiceInRelRespVO):
| 字段 | 类型 | 说明 |
|---|---|---|
id |
String | 关联行ID(Long → String) |
bizType |
String | 业务类型:PAYMENT / PREPAY / EXPENSE |
bizTypeName |
String | 业务类型中文名(应付款 / 预付款 / 费用报销) |
bizId |
String | 业务单据ID(Long → String) |
bizNo |
String | 业务单号快照 |
matchAmount |
BigDecimal | 本票对该笔的匹配金额 |
5.3 可勾选业务单据 InvoiceInBizCandidateRespVO(/biz-candidates,返回 List,非分页)
| 字段 | 类型 | 说明 |
|---|---|---|
bizType |
String | 业务类型:PAYMENT 应付款 / PREPAY 预付款(本端点只捞这两类,不含 EXPENSE) |
bizTypeName |
String | 业务类型中文名 |
bizId |
String | 业务单据ID(Long → String) |
bizNo |
String | 业务单号 |
amount |
BigDecimal | 单据金额 |
matchedAmount |
BigDecimal | 已被有效发票(非作废)匹配金额合计 |
remainingAmount |
BigDecimal | 剩余可收票金额 = amount − matchedAmount(> 0 才返回,收齐票的单据不出现) |
5.4 供应商收票对账行 InvoiceInSupplierReconRespVO(/supplier-recon/page 的 records 元素)
| 字段 | 类型 | 说明 |
|---|---|---|
supplierId |
String | 供应商ID(Long → String) |
supplierName |
String | 供应商名称(快照) |
payableAmount |
BigDecimal | 应收票额 = Σ该供应商应付款(APPROVED/PAID).实付金额 + Σ预付款(APPROVED/PAID).金额 |
receivedAmount |
BigDecimal | 已收票额 = Σ该供应商 VERIFIED 已核对票的 invoice_amount(RECEIVED 未核对不计入) |
unreceivedAmount |
BigDecimal | 未收票额 = payableAmount − receivedAmount |
六、枚举 / 数据字典
status 单据状态(状态机枚举)
| 值 | 中文名 | 含义 | 可执行动作 |
|---|---|---|---|
RECEIVED |
已收票 | 登记落库初始态 | 编辑 / 核对 / 作废 |
VERIFIED |
已核对 | 核对完成 | 作废(不可编辑) |
VOIDED |
已作废 | 终态 | 无(任何写操作 → 599404) |
bizType 业务类型(FinInvoiceInBizTypeEnum)
| 值 | 中文名 | 说明 |
|---|---|---|
PAYMENT |
应付款 | 关联 fin_payment 单据 |
PREPAY |
预付款 | 关联 fin_prepay 单据 |
EXPENSE |
费用报销 | 关联费用报销单(登记/编辑 relations 可用;biz-candidates 端点不捞此类) |
invoiceType 发票类型(数据字典 fin_invoice_in_type,dict_type_id=10163)
| 值 | label | 说明 |
|---|---|---|
SPECIAL |
专票 | 增值税专用发票 |
NORMAL |
普票 | 增值税普通发票 |
ELECTRONIC |
电子票 | 电子发票 |
取值域外的
invoiceType→ 599409。invoiceTypeName由后端按字典 label 回填,前端直接渲染即可;状态statusName/ 业务类型bizTypeName同理后端回填。
七、错误码
5994 段首次落码(owner = hl-finance,段位 599400-599499):
| 错误码 | 含义 | 触发场景 |
|---|---|---|
| 599400 | 进项发票不存在 | 详情 / 编辑 / 核对 / 作废传了不存在(或已软删)的 ID |
| 599401 | 发票号码已存在 | 登记 / 编辑时 invoiceNo 撞号(应用侧查重 + 并发撞号兜底同码) |
| 599402 | 仅已收票状态可编辑/核对 | 对 VERIFIED 票执行编辑或核对 |
| 599403 | 发票已核对 | 重复核对(CAS 守卫拦截) |
| 599404 | 发票已作废 | 对 VOIDED 终态票执行任何写操作 |
| 599405 | 发票金额非法 | invoiceAmount ≤ 0,或 matchAmount ≤ 0(同码) |
| 599406 | 匹配金额合计超过票面金额 | 同票 ΣmatchAmount > invoiceAmount |
| 599407 | 关联业务单据不存在 | relations 里 bizType+bizId 回链查无此单 / 归属不符 |
| 599408 | 作废原因必填 | 作废时 voidReason 为空或纯空白 |
| 599409 | 发票类型非法 | invoiceType 不在字典取值域(SPECIAL/NORMAL/ELECTRONIC 之外) |
| 599410 | 开票供应商无效 | 反查资源域:查无此供应商 / 不可付款 / 服务降级(涉钱 fail-fast) |
| 599411 | 同一张票重复关联同一业务单据 | 同请求内 relations 出现重复 bizType+bizId |
参数非空 / 长度 / 格式类校验失败走 Spring 400(如 invoiceNo 为空、receiveDate 格式错),不占 5994 段。
八、示例
8.1 典型成功
8.1.1 登记一张挂两笔业务的专票:
POST /admin/finance/invoice-in/create
Content-Type: application/json
{
"invoiceNo": "24317000000123456789",
"invoiceType": "SPECIAL",
"supplierId": 2096854417461403650,
"invoiceAmount": 12600.00,
"taxRate": 6,
"taxAmount": 713.21,
"invoiceDate": "2026-09-10",
"receiveDate": "2026-09-15",
"voucherUrl": "https://oss.example.com/invoice/24317000000123456789.pdf",
"remark": "9月团住宿费票",
"relations": [
{ "bizType": "PAYMENT", "bizId": 2100159999888777666, "matchAmount": 10000.00 },
{ "bizType": "PREPAY", "bizId": 2100178000111222333, "matchAmount": 2600.00 }
]
}
响应(code=0):
{ "code": 0, "message": "success", "data": { "id": "2101200000000000001" } }
8.1.2 分页查询:
GET /admin/finance/invoice-in/page?supplierId=2096854417461403650&status=RECEIVED&page=1&pageSize=20
{
"code": 0,
"data": {
"records": [
{
"id": "2101200000000000001",
"invoiceNo": "24317000000123456789",
"invoiceCode": null,
"invoiceType": "SPECIAL",
"invoiceTypeName": "专票",
"supplierId": "2096854417461403650",
"supplierName": "示例酒店A",
"invoiceAmount": 12600.00,
"taxRate": 6,
"taxAmount": 713.21,
"invoiceDate": "2026-09-10",
"receiveDate": "2026-09-15",
"voucherUrl": "https://oss.example.com/invoice/24317000000123456789.pdf",
"remark": "9月团住宿费票",
"status": "RECEIVED",
"statusName": "已收票",
"voidReason": null,
"createTime": "2026-09-15 10:23:45"
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}
8.1.3 核对:
POST /admin/finance/invoice-in/verify
{ "invoiceInId": 2101200000000000001 }
{ "code": 0, "message": "success", "data": null }
核对后详情(GET /admin/finance/invoice-in/2101200000000000001)节选——留痕快照 + relations:
{
"code": 0,
"data": {
"id": "2101200000000000001",
"invoiceNo": "24317000000123456789",
"status": "VERIFIED",
"statusName": "已核对",
"verifyBy": "1",
"verifyByName": "腰苏图",
"verifyTime": "2026-09-15 11:05:12",
"voidBy": null,
"voidByName": null,
"voidTime": null,
"relations": [
{
"id": "2101200000000000011",
"bizType": "PAYMENT",
"bizTypeName": "应付款",
"bizId": "2100159999888777666",
"bizNo": "FK20260916003",
"matchAmount": 10000.00
},
{
"id": "2101200000000000012",
"bizType": "PREPAY",
"bizTypeName": "预付款",
"bizId": "2100178000111222333",
"bizNo": "YF20260916001",
"matchAmount": 2600.00
}
]
}
}
8.1.4 供应商收票对账:
GET /admin/finance/invoice-in/supplier-recon/page?supplierName=示例&page=1&pageSize=20
{
"code": 0,
"data": {
"records": [
{
"supplierId": "2096854417461403650",
"supplierName": "示例酒店A",
"payableAmount": 20000.00,
"receivedAmount": 12600.00,
"unreceivedAmount": 7400.00
}
],
"total": 1,
"page": 1,
"pageSize": 20
}
}
8.2 边界情况
8.2.1 零关联纯挂供应商票(票先到、业务后补挂)——relations 不传:
{
"invoiceNo": "04300160011199887766",
"invoiceType": "ELECTRONIC",
"supplierId": 2096854417461403650,
"invoiceAmount": 500.00,
"receiveDate": "2026-09-16"
}
登记成功;详情 relations 返回空列表 [](不为 null)。后续通过 PUT /update 补挂关联(全删重插口径,传新数组即可)。
8.2.2 部分匹配——票面 12600 只匹配 10000(ΣmatchAmount < invoiceAmount,允许):
{
"invoiceNo": "24317000000199998888",
"invoiceType": "NORMAL",
"supplierId": 2096854417461403650,
"invoiceAmount": 12600.00,
"relations": [
{ "bizType": "PAYMENT", "bizId": 2100159999888777666, "matchAmount": 10000.00 }
]
}
登记成功(差额 2600 可留在后续票继续匹配该笔应付,直至 biz-candidates 里 remainingAmount 归 0 不再出现)。
8.2.3 biz-candidates 已收齐票单据不出现:
GET /admin/finance/invoice-in/biz-candidates?supplierId=2096854417461403650
{
"code": 0,
"data": [
{
"bizType": "PAYMENT",
"bizTypeName": "应付款",
"bizId": "2100159999888777666",
"bizNo": "FK20260916003",
"amount": 20000.00,
"matchedAmount": 10000.00,
"remainingAmount": 10000.00
}
]
}
remainingAmount > 0 才返回;若某笔已被有效票(RECEIVED/VERIFIED)匹配满额,该行不出现。作废票释放匹配额度后会重新出现。
8.3 业务失败
8.3.1 匹配金额合计超票面 → 599406:
{
"invoiceNo": "24317000000177776666",
"invoiceType": "SPECIAL",
"supplierId": 2096854417461403650,
"invoiceAmount": 1000.00,
"relations": [
{ "bizType": "PAYMENT", "bizId": 2100159999888777666, "matchAmount": 1500.00 }
]
}
{ "code": 599406, "message": "匹配金额合计超过票面金额", "data": null }
8.3.2 对已核对票再核对 → 599403:
POST /admin/finance/invoice-in/verify
{ "invoiceInId": 2101200000000000001 }
{ "code": 599403, "message": "发票已核对", "data": null }
8.3.3 作废不传原因 → 599408:
POST /admin/finance/invoice-in/void
{ "invoiceInId": 2101200000000000001, "voidReason": " " }
{ "code": 599408, "message": "作废原因必填", "data": null }
8.3.4 票号重复 → 599401:
{ "code": 599401, "message": "发票号码已存在", "data": null }
九、业务边界
适用:
- 供应商发票的登记 / 编辑 / 核对 / 作废全生命周期管理
- 一张票分摊到多笔应付 / 预付 / 费用报销(含部分匹配)
- 票先到业务后补挂(零关联登记 → 编辑补挂)
- 按供应商追欠票(supplier-recon 对账页)
不适用:
- 进项税抵扣核算(taxRate/taxAmount 仅记录,不做税额勾稽,不对接财务记账)
- 税务查验 / 真伪核验(不接税局接口)
- 销项发票(开给客户的发票走发票管理既有功能,不在本域)
- 历史票批量导入(无导入端点)
特殊边界:
- 编辑全删重插:
PUT /update的 relations 是"最终态"语义,不是增量——要保留旧关联必须在请求里带上 - VOIDED 是终态:作废后不可恢复、不可再编辑;作废票不占 biz-candidates 的 matchedAmount(额度释放)
- 已收票额只认 VERIFIED:supplier-recon 的 receivedAmount 只合计已核对票;RECEIVED 未核对票不算"已收",这是催核对的设计意图
- biz-candidates 只捞 PAYMENT/PREPAY:EXPENSE 费用报销不出现在勾选列表,但 relations 里手工传 EXPENSE+bizId 合法
- 发票号码全局唯一:不区分供应商——同一 invoiceNo 即使不同供应商也撞 599401
- 核对是 CAS 守卫:并发两人同时点核对,一人成功一人 599403,无脏写
十、修改前后对比
本期为新增接口,无修改前后对比(前端此前无收票管理任何页面,按本文一次对接即可)。
十一、影响评估 / 回滚
| 项 | 说明 |
|---|---|
| 破坏兼容 | 无——纯新增 8 个端点,不改任何既有接口 |
| 前端同步上线 | 无强依赖——前端未对接不影响任何既有功能;「收票管理」菜单已上架并授权 SUPER_ADMIN / ADMIN / FINANCE,页面就绪前菜单点进去为空属预期 |
| 回滚方案 | 后端回滚 = 下线 8 个端点 + 隐藏菜单(前端不调用即无感);DDL 回滚 = DROP fin_invoice_in / fin_invoice_in_rel 两表(本期新建,无历史数据包袱) |
十二、注意事项
- Long ID 全 String:
id/supplierId/bizId/verifyBy/voidBy/ 关联行id均为 String 输出,直接当字符串用,不要Number()转换(雪花 ID 超 JS 安全整数)。 - 三个 xxxName 后端已回填:
invoiceTypeName(字典)/statusName(状态机枚举)/bizTypeName(业务类型枚举)随响应直接给,前端不用再查字典渲染。 - 编辑即覆盖:
PUT /update关联全删重插,前端编辑页打开时先把详情 relations 回填到勾选态,提交时整包回传,否则旧关联会被清空。 - 作废原因前端必填校验也要做:空原因后端落 599408(不是 400),但建议前端表单层就拦掉,少一次往返。
- 金额校验顺序:invoiceAmount ≤ 0 → 599405;matchAmount ≤ 0 → 599405(同码);ΣmatchAmount > invoiceAmount → 599406。前端可按
0 < ΣmatchAmount ≤ invoiceAmount预校验。 - 日期格式严格
yyyy-MM-dd:invoiceDate / receiveDate / receiveDateStart / receiveDateEnd 传时间戳或2026/09/01会 400。 - supplier-recon 口径别算反:payableAmount 只看 APPROVED/PAID 的应付+预付(草稿/已撤销不计);receivedAmount 只认 VERIFIED。两列差额 = 供应商欠票,是本页的核心 actionable 列。
- biz-candidates 在登记/编辑弹窗里联动调用:选定供应商后立即调该端点拉勾选列表;换供应商要重拉(候选按供应商隔离)。
- 幂等重试安全:登记撞票号 599401、核对重复 599403、作废重复 599404,均为明确业务码,前端可据码提示而非笼统报错。
十三、关联 / 联系人
| 项 | 链接 |
|---|---|
| Epic Issue | https://git.1814.love:8443/wx/HL/issues/7857 |
| 代码 PR-1(地基:两表 + DO/Mapper/枚举/错误码) | https://git.1814.love:8443/wx/HL/pulls/7860 |
| 代码 PR-2(核心接口 + 字典种子) | https://git.1814.love:8443/wx/HL/pulls/7874 |
| 代码 PR-3(核对/作废/供应商对账 + 菜单) | https://git.1814.love:8443/wx/HL/pulls/7880 |
| 文档 PR(SRS / API / DM / 详设同步) | https://git.1814.love:8443/wx/HL/pulls/7891 |
| 代码基线(dev-v3 HEAD) | https://git.1814.love:8443/wx/HL/commit/1d0cd8d965e123a1650d921646648c247f3ad669 |
| 后端负责人 | 腰苏图(yst) |