文件
hl-api-changelog/changelogs-v2/2026-09/17_7857_收票(进项发票)域上线-新增接口-管理后台.md
2026-09-17 19:51:40 +08:00

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 两表(本期新建,无历史数据包袱)

十二、注意事项

  1. Long ID 全 String:id / supplierId / bizId / verifyBy / voidBy / 关联行 id 均为 String 输出,直接当字符串用,不要 Number() 转换(雪花 ID 超 JS 安全整数)。
  2. 三个 xxxName 后端已回填:invoiceTypeName(字典)/ statusName(状态机枚举)/ bizTypeName(业务类型枚举)随响应直接给,前端不用再查字典渲染。
  3. 编辑即覆盖:PUT /update 关联全删重插,前端编辑页打开时先把详情 relations 回填到勾选态,提交时整包回传,否则旧关联会被清空。
  4. 作废原因前端必填校验也要做:空原因后端落 599408(不是 400),但建议前端表单层就拦掉,少一次往返。
  5. 金额校验顺序:invoiceAmount ≤ 0 → 599405;matchAmount ≤ 0 → 599405(同码);ΣmatchAmount > invoiceAmount → 599406。前端可按 0 < ΣmatchAmount ≤ invoiceAmount 预校验。
  6. 日期格式严格 yyyy-MM-dd:invoiceDate / receiveDate / receiveDateStart / receiveDateEnd 传时间戳或 2026/09/01 会 400。
  7. supplier-recon 口径别算反:payableAmount 只看 APPROVED/PAID 的应付+预付(草稿/已撤销不计);receivedAmount 只认 VERIFIED。两列差额 = 供应商欠票,是本页的核心 actionable 列。
  8. biz-candidates 在登记/编辑弹窗里联动调用:选定供应商后立即调该端点拉勾选列表;换供应商要重拉(候选按供应商隔离)。
  9. 幂等重试安全:登记撞票号 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)