# 发票申请删 amount · 门槛放宽 · 开票/重传核单门槛(管理后台) - **接口**:POST /v3/admin/order/{orderId}/invoice/apply + PUT /v3/admin/order/invoice/{id}/issue + PUT /v3/admin/order/invoice/{id}/reupload - **变更类型**:修改接口,破坏性变更(申请入参删 amount;新增错误码 581524/581525;门槛扩展) - **端类型**:管理后台 - **日期**:2026-06-25 - **Issue**:[#4353](https://git.1814.love:8443/wx/HL/issues/4353) - **PR**:[#4360](https://git.1814.love:8443/wx/HL/pulls/4360) 本次三项改动:1. 申请入参删 amount,开票时后端自动算;2. 申请门槛放宽至定制中及以后;3. 开票/重传新增核单门槛。 --- ## 1 接口背景 发票模块本次 PR #4360 三项改动: 1. **申请入参删 amount**:开票金额不再前端传入,开票时后端按规则自动计算(正常单=应收总额;取消单=净实收)。 2. **申请门槛放宽**:原仅允许 COMPLETED 订单申请;放宽为定制中及以后均可申请,同时支持已取消但净实收 > 0 的订单。 3. **开票/重传新增核单门槛**:完成开票(issue)和重新上传(reupload)操作要求订单核单完成(reviewStatus=COMPLETED),防止未结算的订单开出金额有误的发票。 --- ## 2 变更清单 | # | 变更项 | 变更前 | 变更后 | |---|--------|--------|--------| | 1 | POST apply 入参 amount | 必填字段,前端传开票金额 | 已删除,后端开票时自动算 | | 2 | POST apply 申请门槛(正常订单) | 订单状态必须 COMPLETED | 状态 ∈ {CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED} 均可申请 | | 3 | POST apply 申请门槛(取消订单) | 不允许 | CANCELLED 且净实收(已付 - 已退)> 0 时可申请 | | 4 | PUT issue 操作门槛 | 仅检查发票状态为 REQUESTED | 增加:订单核单完成(reviewStatus=COMPLETED)才可开票,否则 581525 | | 5 | PUT reupload 操作门槛 | 仅检查发票状态为 ISSUED/PUSHED | 增加:订单核单完成才可重传,否则 581525 | | 6 | 新错误码 581524 | 无 | INVOICE_ORDER_NOT_APPLICABLE:订单不满足申请条件 | | 7 | 新错误码 581525 | 无 | INVOICE_ORDER_NOT_REVIEWED:订单核单未完成,不可开票/重传 | --- ## 3 接口详情 ### 3.1 代客申请发票 | 属性 | 值 | |------|----| | 方法 | POST | | 路径 | /v3/admin/order/{orderId}/invoice/apply | | 描述 | 财务或定制师代客提交开票申请(申请阶段不填金额,开票时后端自动算) | | 认证 | Bearer JWT(管理员) | | 幂等性 | 非幂等,重复提交触发 581511(一单一票) | | 限流 | 无特殊限制 | ### 3.2 完成开票 | 属性 | 值 | |------|----| | 方法 | PUT | | 路径 | /v3/admin/order/invoice/{id}/issue | | 描述 | 财务上传发票 PDF 并完成开票,状态 REQUESTED -> ISSUED;需订单核单完成 | | 认证 | Bearer JWT(管理员) | | 幂等性 | 状态守卫,非幂等 | | 限流 | 无特殊限制 | ### 3.3 重新上传发票 | 属性 | 值 | |------|----| | 方法 | PUT | | 路径 | /v3/admin/order/invoice/{id}/reupload | | 描述 | 财务重新上传发票文件(ISSUED/PUSHED 均可);需订单核单完成 | | 认证 | Bearer JWT(管理员) | | 幂等性 | 覆盖写,非幂等 | | 限流 | 无特殊限制 | --- ## 4 接口入参 ### 4.1 路径参数 | 接口 | 参数名 | 类型 | 必填 | 说明 | |------|--------|------|------|------| | POST apply | orderId | string(Long 雪花) | 是 | 订单 ID | | PUT issue | id | string(Long 雪花) | 是 | 发票记录 ID | | PUT reupload | id | string(Long 雪花) | 是 | 发票记录 ID | ### 4.2 请求体字段 **POST /v3/admin/order/{orderId}/invoice/apply(AdminInvoiceApplyReqVO,⚠️ amount 已删)** 当前完整字段列表(无 amount): | 字段 | 类型 | 必填条件 | 说明 | |------|------|---------|------| | invoiceType | string | 始终必填 | 发票类型,只有 VAT_NORMAL / VAT_SPECIAL 两值 | | titleType | string | 始终必填 | 抬头类型:COMPANY(单位)/ PERSONAL(个人);VAT_SPECIAL 只能 COMPANY | | titleName | string | 始终必填 | 发票抬头(企业全称或个人姓名) | | taxNo | string | titleType=COMPANY 或 invoiceType=VAT_SPECIAL 时必填 | 纳税人识别号 | | bankName | string | invoiceType=VAT_SPECIAL 时必填 | 开户银行名称 | | bankAccount | string | invoiceType=VAT_SPECIAL 时必填 | 银行账号 | | registAddress | string | invoiceType=VAT_SPECIAL 时必填 | 注册地址 | | registPhone | string | invoiceType=VAT_SPECIAL 时必填 | 注册电话 | | email | string | 始终必填 | 收件邮箱,需通过邮箱格式校验 | | remark | string | 选填 | 申请备注 | **PUT /v3/admin/order/invoice/{id}/issue** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | fileUrl | string | 是 | upload 接口返回的 OSS 地址 | | pdfName | string | 否 | PDF 文件名(展示用) | | pdfSize | Long | 否 | PDF 文件大小(字节) | | invoiceNo | string | 否 | 发票号码 | **PUT /v3/admin/order/invoice/{id}/reupload** | 字段 | 类型 | 必填 | 说明 | |------|------|------|------| | fileUrl | string | 是 | 新发票 PDF 的 OSS 地址 | | pdfName | string | 否 | 新 PDF 文件名 | | pdfSize | Long | 否 | 新 PDF 文件大小(字节) | --- ## 5 出参字段 **POST apply 响应**:成功返回新建发票 ID(字符串,雪花 ID)。 **PUT issue / reupload 响应**:操作成功返回 HTTP 200,data 为 null。 --- ## 6 枚举 / 数据字典 ### 6.1 发票类型(invoiceType) | code | 说明 | |------|------| | VAT_NORMAL | 增值税普通发票 | | VAT_SPECIAL | 增值税专用发票 | ### 6.2 抬头类型(titleType) | code | 说明 | 限制 | |------|------|------| | COMPANY | 单位 | 普票/专票均可 | | PERSONAL | 个人 | 只能选 VAT_NORMAL;选 VAT_SPECIAL 返回 581523 | ### 6.3 订单状态与可申请关系 | 订单状态(code) | 中文 | 可申请? | 说明 | |----------------|------|---------|------| | CUSTOMIZING | 定制中 | 是 | 放宽新增 | | PENDING_DEPARTURE | 待出行 | 是 | 放宽新增 | | TRAVELLING | 出行中 | 是 | 放宽新增 | | COMPLETED | 已完成 | 是 | 原有 | | CANCELLED | 已取消 | 条件是 | 净实收(已付 - 已退)> 0 时可申请 | | 其他状态 | - | 否 | 报 581524 | ### 6.4 开票时金额自动计算规则 | 订单类型 | 金额计算公式 | 说明 | |----------|-------------|------| | 正常单(非 CANCELLED) | 应收总额 = 订单总价 + 增项 - 优惠 | 申请时不确定,开票时算 | | 取消单(CANCELLED) | 净实收 = 已付金额 - 已退金额 | 净实收 = 0 则拒绝申请 | --- ## 7 错误码 | 错误码 | 常量 | 触发场景 | |--------|------|----------| | 581502 | INVOICE_CANNOT_ISSUE | 发票状态非 REQUESTED,不允许开票 | | 581503 | INVOICE_CANNOT_REUPLOAD | 发票状态非 ISSUED/PUSHED,不允许重新上传 | | 581511 | INVOICE_ALREADY_EXISTS | 该订单已有有效发票,不可重复申请(一单一票) | | 581513 | INVOICE_TYPE_INVALID | 发票类型枚举值非法 | | 581514 | INVOICE_TAX_NO_REQUIRED | 公司抬头/专票时税号为必填 | | 581515 | INVOICE_VAT_SPECIAL_FIELDS_REQUIRED | 专票时开户行/银行账号/注册地址/注册电话为必填 | | 581516 | INVOICE_EMAIL_REQUIRED | 收件邮箱为必填或格式错误(HTTP 400) | | 581523 | INVOICE_VAT_SPECIAL_PERSONAL_FORBIDDEN | 专票只能开给单位 | | 581524 | INVOICE_ORDER_NOT_APPLICABLE | ✨ 新增:订单状态不在可申请集合内,或取消单净实收为 0 | | 581525 | INVOICE_ORDER_NOT_REVIEWED | ✨ 新增:开票/重传时订单核单未完成(reviewStatus != COMPLETED) | --- ## 8 示例 ### 8.1 典型成功——普票申请(无 amount 字段) 请求 ``` POST /v3/admin/order/1920000000000000001/invoice/apply Authorization: Bearer Content-Type: application/json ``` ```json { "invoiceType": "VAT_NORMAL", "titleType": "PERSONAL", "titleName": "李四", "email": "lisi@example.com", "remark": "报销使用" } ``` 响应 ```json {"code": 200, "msg": "success", "data": "1934567890123456789"} ``` ### 8.2 边界情况——取消订单且净实收 > 0 可申请 说明:订单已取消,实付 3000 元、退款 1000 元,净实收 2000 元 > 0,申请通过。开票时金额自动计算为净实收 2000 元。 请求(结构同 8.1,orderId 换为该取消订单 ID) 响应 ```json {"code": 200, "msg": "success", "data": "1934567890123456700"} ``` ### 8.3 业务失败——订单核单未完成,完成开票被拒 请求 ``` PUT /v3/admin/order/invoice/1934567890123456789/issue Authorization: Bearer Content-Type: application/json ``` ```json { "fileUrl": "https://oss.example.com/invoice/2026/06/xxx.pdf", "pdfName": "发票_北京科技.pdf", "invoiceNo": "12345678" } ``` 响应(订单 reviewStatus 非 COMPLETED) ```json {"code": 581525, "msg": "订单核单未完成,不可开具发票", "data": null} ``` --- ## 9 业务边界 适用: - POST apply:订单状态 ∈ {CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED},或 CANCELLED 且净实收 > 0 - PUT issue:发票状态为 REQUESTED,且订单 reviewStatus = COMPLETED - PUT reupload:发票状态为 ISSUED/PUSHED,且订单 reviewStatus = COMPLETED 不适用: - POST apply:订单为 PAID(已支付但未进入定制阶段)、或 CANCELLED 且净实收 = 0,报 581524 - PUT issue/reupload:订单核单未完成(reviewStatus != COMPLETED),报 581525 特殊边界: - 取消单开票金额 = 净实收(已付 - 已退),不是订单总价;正常单 = 应收总额 - 一单一票:同一订单只允许一张有效发票(REQUESTED/ISSUED/PUSHED 状态),若已有则报 581511 --- ## 10 修改前后对比 ### apply 入参字段对比 | 字段 | 变更前 | 变更后 | |------|--------|--------| | amount | 始终必填,JSON number,单位元 | ⚠️ 已删除,后端开票时自动算 | ### apply 申请门槛对比 | 场景 | 变更前 | 变更后 | |------|--------|--------| | 正常订单 | 仅 COMPLETED 可申请,其余报 581510 | CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED 均可申请 | | 取消订单 | 不允许 | 净实收 > 0 时可申请,= 0 报 581524 | | 不满足时错误码 | 581510 INVOICE_ORDER_NOT_COMPLETED(已废弃) | 581524 INVOICE_ORDER_NOT_APPLICABLE(新增) | ### issue / reupload 门槛对比 | 操作 | 变更前 | 变更后 | |------|--------|--------| | PUT issue | 仅检查发票状态为 REQUESTED | 增加:订单核单完成(reviewStatus=COMPLETED),否则 581525 | | PUT reupload | 仅检查发票状态为 ISSUED/PUSHED | 增加:订单核单完成,否则 581525 | --- ## 11 影响评估 / 回滚 破坏兼容性:是 - 前端申请表单必须删除开票金额输入框,不再传 amount 字段 - 前端申请成功弹窗/提示中不展示金额(金额在开票时才确定) - 财务点击「完成开票」,若订单未核单则收到 581525,前端需展示「订单核单未完成,不可开票」提示 前端同步上线:申请表单删 amount 必须在本次后端部署后同步更新;财务开票侧 581525 错误提示须同步适配。 回滚方案:回滚后端至 #4360 前版本,amount 字段恢复必填,申请门槛收回到仅 COMPLETED,581524/581525 不再存在。 --- ## 12 注意事项 1. **amount 字段已删**:前端申请表单必须移除金额输入框,不传 amount。传入后端会忽略,导致用户困惑(以为填了金额实际无效)。 2. **开票时金额自动写入**:发票金额由后端在 issue 操作时根据订单类型自动计算,财务无需手填金额。 3. **申请门槛放宽的注意点**:定制中/待出行/出行中提前申请的发票,开票时(issue)后端按当时应收总额计算金额。财务须在核单完成后再开票(581525 已强制保证)。 4. **核单门槛(581525)**:完成开票和重传均要求核单完成。财务若收到此错误,需先在核单管理页完成核单,再回来开票。 5. **取消单申请**:CANCELLED 订单申请发票,开票金额为净实收(已付 - 已退)。若净实收为 0,申请被 581524 拒绝。 --- ## 13 关联 / 联系人 - **Issue**:[#4353 发票模块门槛放宽与开票规则优化](https://git.1814.love:8443/wx/HL/issues/4353) - **PR**:[#4360](https://git.1814.love:8443/wx/HL/pulls/4360) - **后端负责人**:腰苏图(yaosutu)