推 changelog:发票模块 #4337/#4349/#4360(ALL 改订单视角+封面图+申请删 amount+门槛放宽+开票核单门槛,管理后台+小程序端)
这个提交包含在:
父节点
3d9ec7021d
当前提交
76dcf10531
@ -0,0 +1,217 @@
|
|||||||
|
# 发票申请删 amount · 门槛放宽(小程序端)
|
||||||
|
|
||||||
|
- **接口**:POST /v3/internal/mp/order/{orderId}/invoice/apply
|
||||||
|
- **变更类型**:修改接口,破坏性变更(申请入参删 amount;申请门槛扩展;新增错误码 581524)
|
||||||
|
- **端类型**:小程序端
|
||||||
|
- **日期**: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 接口背景
|
||||||
|
|
||||||
|
小程序端发票申请接口(POST /v3/internal/mp/order/{orderId}/invoice/apply)本次同步两项改动:
|
||||||
|
|
||||||
|
1. **申请入参删 amount**:开票金额不再前端传入,开票时后端按规则自动计算(正常单=应收总额;取消单=净实收)。
|
||||||
|
2. **申请门槛放宽**:原门槛仅允许 COMPLETED(已完成)订单申请;放宽为定制中及以后均可申请,同时支持已取消但净实收 > 0 的订单申请。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 变更清单
|
||||||
|
|
||||||
|
| # | 变更项 | 变更前 | 变更后 |
|
||||||
|
|---|--------|--------|--------|
|
||||||
|
| 1 | 申请入参 amount | 必填字段,客户填写开票金额 | ⚠️ 已删除,后端开票时自动算 |
|
||||||
|
| 2 | 申请门槛(正常订单) | 订单状态必须 COMPLETED | 状态 ∈ {CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED} 均可申请 |
|
||||||
|
| 3 | 申请门槛(取消订单) | 不允许 | CANCELLED 且净实收(已付 - 已退)> 0 时可申请 |
|
||||||
|
| 4 | 不满足门槛的错误码 | 581510 INVOICE_ORDER_NOT_COMPLETED | ✨ 新增 581524 INVOICE_ORDER_NOT_APPLICABLE(更精确语义) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 接口详情
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|----|
|
||||||
|
| 方法 | POST |
|
||||||
|
| 路径 | /v3/internal/mp/order/{orderId}/invoice/apply |
|
||||||
|
| 描述 | 客户自主申请开票(申请阶段不填金额,开票时后端自动算) |
|
||||||
|
| 认证 | Bearer JWT(小程序用户) |
|
||||||
|
| 幂等性 | 非幂等,重复提交触发 581511(一单一票) |
|
||||||
|
| 限流 | 无特殊限制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 接口入参
|
||||||
|
|
||||||
|
### 4.1 路径参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| orderId | string(Long 雪花) | 是 | 订单 ID |
|
||||||
|
|
||||||
|
### 4.2 请求体字段(MpInvoiceApplyReqVO,⚠️ 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 | 选填 | 申请备注 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 出参字段
|
||||||
|
|
||||||
|
响应:成功返回新建发票 ID(字符串,雪花 ID)。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 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) | 净实收 = 已付金额 - 已退金额 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 错误码
|
||||||
|
|
||||||
|
| 错误码 | 常量 | 触发场景 |
|
||||||
|
|--------|------|----------|
|
||||||
|
| 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) |
|
||||||
|
| 581518 | INVOICE_FORBIDDEN | 无权访问该订单(IDOR 防护) |
|
||||||
|
| 581523 | INVOICE_VAT_SPECIAL_PERSONAL_FORBIDDEN | 专票只能开给单位 |
|
||||||
|
| 581524 | INVOICE_ORDER_NOT_APPLICABLE | ✨ 新增:订单状态不在可申请集合内,或取消单净实收为 0 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8 示例
|
||||||
|
|
||||||
|
### 8.1 典型成功——普票申请(无 amount 字段)
|
||||||
|
|
||||||
|
请求
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
响应
|
||||||
|
|
||||||
|
|
||||||
|
### 8.2 边界情况——订单处于定制中提前申请
|
||||||
|
|
||||||
|
说明:订单状态 CUSTOMIZING(定制中),本次放宽后允许申请。开票金额将在财务执行开票操作时按当时应收总额计算。
|
||||||
|
|
||||||
|
请求(结构同 8.1,orderId 为定制中订单 ID)
|
||||||
|
|
||||||
|
响应
|
||||||
|
|
||||||
|
|
||||||
|
### 8.3 业务失败——取消订单净实收为 0 被拒
|
||||||
|
|
||||||
|
说明:订单已取消,实付 1000 元且已全额退款,净实收 = 0,无法申请开票。
|
||||||
|
|
||||||
|
请求(结构同 8.1,orderId 为该取消订单 ID)
|
||||||
|
|
||||||
|
响应
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 业务边界
|
||||||
|
|
||||||
|
适用:
|
||||||
|
- 订单状态 ∈ {CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED} 均可申请
|
||||||
|
- CANCELLED 且净实收(已付 - 已退)> 0 时可申请
|
||||||
|
|
||||||
|
不适用:
|
||||||
|
- PAID(已支付但未进入定制阶段)等其他状态报 581524
|
||||||
|
- CANCELLED 且净实收 = 0 报 581524
|
||||||
|
- 订单不属于当前登录用户,报 581518(IDOR 防护)
|
||||||
|
|
||||||
|
特殊边界:
|
||||||
|
- 一单一票:同一订单只允许一张有效发票(REQUESTED/ISSUED/PUSHED 状态),重复申请报 581511
|
||||||
|
- 开票金额由后端在财务执行 issue 操作时自动计算,小程序端申请时不确定最终金额
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10 修改前后对比
|
||||||
|
|
||||||
|
| 字段 / 规则 | 变更前 | 变更后 |
|
||||||
|
|-------------|--------|--------|
|
||||||
|
| 申请入参 amount | 必填,客户填写开票金额 | ⚠️ 已删除,后端开票时自动算 |
|
||||||
|
| 正常订单申请门槛 | 仅 COMPLETED | CUSTOMIZING / PENDING_DEPARTURE / TRAVELLING / COMPLETED |
|
||||||
|
| 取消订单申请 | 不允许 | 净实收 > 0 时可申请 |
|
||||||
|
| 不满足门槛错误码 | 581510 INVOICE_ORDER_NOT_COMPLETED(已废弃) | 581524 INVOICE_ORDER_NOT_APPLICABLE(新增) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11 影响评估 / 回滚
|
||||||
|
|
||||||
|
破坏兼容性:是
|
||||||
|
|
||||||
|
- 小程序申请发票表单必须删除金额输入框,不再传 amount 字段
|
||||||
|
- 若现有流程中有基于 amount 的前端金额展示或校验逻辑,须同步移除
|
||||||
|
- 不满足门槛的错误提示由旧 581510 改为 581524,前端若 hardcode 了 581510 的处理逻辑需同步更新
|
||||||
|
|
||||||
|
前端同步上线:申请表单删 amount 必须与后端同期上线。
|
||||||
|
|
||||||
|
回滚方案:回滚后端至 #4360 前版本,amount 字段恢复必填,申请门槛收回到仅 COMPLETED,581524 不再存在。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12 注意事项
|
||||||
|
|
||||||
|
1. **amount 字段已删**:小程序申请发票表单必须移除金额输入框,不传 amount,传入后端会忽略。
|
||||||
|
2. **申请门槛放宽的注意点**:定制中等状态提前申请,开票时金额由财务执行 issue 操作时后端按当时应收总额计算,小程序无法在申请时预知最终开票金额。
|
||||||
|
3. **581524 vs 旧 581510**:新错误码 581524 取代了旧的 581510,含义更精确(包含取消单净实收为 0 的场景)。若前端有 581510 的 hardcode 判断需更新为 581524。
|
||||||
|
4. **开票金额由财务操作确定**:小程序端只负责申请,金额由后端在财务开票(PUT issue)时写入,前端无法在申请时展示最终金额。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13 关联 / 联系人
|
||||||
|
|
||||||
|
- **Issue**:[#4353 发票模块门槛放宽与开票规则优化](https://git.1814.love:8443/wx/HL/issues/4353)
|
||||||
|
- **PR**:[#4360](https://git.1814.love:8443/wx/HL/pulls/4360)
|
||||||
|
- **后端负责人**:腰苏图(yaosutu)
|
||||||
@ -0,0 +1,422 @@
|
|||||||
|
# 发票管理列表 - ALL 改订单视角 + 列表行新增封面图(管理后台)
|
||||||
|
|
||||||
|
- **接口**:GET /v3/admin/order/invoice/page
|
||||||
|
- **变更类型**:修改接口 破坏性变更(tab=ALL 列表语义 + tabCounts.ALL 计数口径均变)
|
||||||
|
- **端类型**:管理后台
|
||||||
|
- **日期**:2026-06-25
|
||||||
|
- **Issue**:[#4337](https://git.1814.love:8443/wx/HL/issues/4337)
|
||||||
|
- **PR**:[#4338](https://git.1814.love:8443/wx/HL/pulls/4338)(ALL 改订单视角)+ [#4349](https://git.1814.love:8443/wx/HL/pulls/4349)(行新增 productCoverImg)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1 接口背景
|
||||||
|
|
||||||
|
财务发票管理列表原先以发票记录为视角。ALL tab 只返回已有发票记录的订单行,tabCounts.ALL 计数只统计发票记录总数,导致:
|
||||||
|
|
||||||
|
- ALL 数量 < NONE(未申请)数量,前端展示出现矛盾
|
||||||
|
- ALL 列表无法让财务在一个页面纵览所有已完成订单的发票状态
|
||||||
|
|
||||||
|
本次将 ALL tab 切换为**订单视角**:以全部 COMPLETED(已完成)订单为数据源,有发票的行展示发票字段,无发票的行 status=NONE,对齐前端原型设计。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2 变更清单
|
||||||
|
|
||||||
|
| # | 变更项 | 变更前 | 变更后 |
|
||||||
|
|---|--------|--------|--------|
|
||||||
|
| 1 | tabCounts.ALL 计数口径 | 仅统计发票记录数(REQUESTED+ISSUED+PUSHED),不含未申请 | 统计全部 COMPLETED 订单数(含未申请),永远 >= 任何单个 tab |
|
||||||
|
| 2 | tab=ALL 列表数据源 | 只返回有发票记录的订单行 | 返回全部 COMPLETED 订单,无发票行 status=NONE |
|
||||||
|
| 3 | titleName/requestedBy 在 ALL/NONE tab 的行为 | 未定义(可能干扰结果) | 静默忽略(两字段仅在 REQUESTED/ISSUED/PUSHED 三个发票视角 tab 生效) |
|
||||||
|
| 4 | 列表行出参 productCoverImg | 无此字段 | ✨ 新增,返回产品封面图 URL,无封面时为 null |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3 接口详情
|
||||||
|
|
||||||
|
| 属性 | 值 |
|
||||||
|
|------|----|
|
||||||
|
| 方法 | GET |
|
||||||
|
| 路径 | /v3/admin/order/invoice/page |
|
||||||
|
| 描述 | 财务发票管理列表(分页),含 tab 计数与汇总统计 |
|
||||||
|
| 认证 | Bearer JWT(管理员) |
|
||||||
|
| 幂等性 | 只读,天然幂等 |
|
||||||
|
| 限流 | 无特殊限制 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4 接口入参
|
||||||
|
|
||||||
|
### 4.1 Query 参数
|
||||||
|
|
||||||
|
| 参数名 | 类型 | 必填 | 说明 |
|
||||||
|
|--------|------|------|------|
|
||||||
|
| tab | string | 否 | 当前 tab,枚举值见第 6 节;默认 ALL |
|
||||||
|
| pageNo | integer | 否 | 页码,默认 1 |
|
||||||
|
| pageSize | integer | 否 | 每页条数,默认 20 |
|
||||||
|
| keyword | string | 否 | 关键词搜索(订单号 / 产品名 / 联系人),所有 tab 均生效 |
|
||||||
|
| titleName | string | 否 | 发票抬头名称,**仅 REQUESTED/ISSUED/PUSHED tab 生效,ALL/NONE tab 静默忽略** |
|
||||||
|
| requestedBy | string | 否 | 申请人(定制师),**仅 REQUESTED/ISSUED/PUSHED tab 生效,ALL/NONE tab 静默忽略** |
|
||||||
|
| departureStartDate | string | 否 | 出发日期起,格式 YYYY-MM-DD |
|
||||||
|
| departureEndDate | string | 否 | 出发日期止,格式 YYYY-MM-DD |
|
||||||
|
|
||||||
|
### 4.2 请求体
|
||||||
|
|
||||||
|
无(GET 接口)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5 出参字段
|
||||||
|
|
||||||
|
响应外层结构:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"tabCounts": [...],
|
||||||
|
"stats": [...],
|
||||||
|
"records": [...],
|
||||||
|
"total": 0,
|
||||||
|
"pageNo": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.1 tabCounts(5 项固定顺序)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| code | string | tab 枚举值(见第 6 节) |
|
||||||
|
| name | string | tab 中文标签 |
|
||||||
|
| count | integer | 该 tab 对应的记录数 |
|
||||||
|
|
||||||
|
固定顺序:REQUESTED → ISSUED → PUSHED → NONE → ALL
|
||||||
|
|
||||||
|
> **ALL.count 语义已变**:现为全部 COMPLETED 订单数(含未申请);之前仅含发票记录数。
|
||||||
|
|
||||||
|
### 5.2 stats(4 项固定顺序)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| code | string | 枚举值(见第 6 节) |
|
||||||
|
| name | string | 统计项中文标签 |
|
||||||
|
| count | integer | 数量 |
|
||||||
|
| amount | number | 金额(元,JSON number) |
|
||||||
|
|
||||||
|
固定顺序:REQUESTED → ISSUED → PUSHED → CURRENT_MONTH_ISSUED
|
||||||
|
|
||||||
|
### 5.3 records 列表行(InvoicePageItemRespVO)
|
||||||
|
|
||||||
|
| 字段 | 类型 | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| id | string | 发票记录 ID(雪花);**无发票行为 null** |
|
||||||
|
| orderId | string | 订单 ID(雪花) |
|
||||||
|
| orderNo | string | 订单号 |
|
||||||
|
| productName | string | 产品名称 |
|
||||||
|
| productCoverImg | string | 产品封面图 URL(✨新增字段);无封面时为 null |
|
||||||
|
| tierName | string | 档期名称 |
|
||||||
|
| contactName | string | 联系人姓名 |
|
||||||
|
| customizerName | string | 定制师姓名 |
|
||||||
|
| departureDate | string | 出发日期,格式 YYYY-MM-DD |
|
||||||
|
| adultCount | integer | 成人数 |
|
||||||
|
| childCount | integer | 儿童数 |
|
||||||
|
| youngChildCount | integer | 婴幼儿数 |
|
||||||
|
| invoiceType | string | 发票类型枚举(见第 6 节);无发票行为 null |
|
||||||
|
| invoiceTypeText | string | 发票类型中文;无发票行为 null |
|
||||||
|
| titleType | string | 抬头类型枚举(见第 6 节);无发票行为 null |
|
||||||
|
| titleName | string | 抬头名称;无发票行为 null |
|
||||||
|
| taxNo | string | 税号;无发票行为 null |
|
||||||
|
| amount | number | 发票金额(元);无发票行为 null |
|
||||||
|
| email | string | 接收邮箱;无发票行为 null |
|
||||||
|
| status | string | 发票状态枚举(见第 6 节);**无发票行固定为 NONE** |
|
||||||
|
| statusText | string | 发票状态中文;**无发票行固定为 未申请** |
|
||||||
|
| requestedBy | string | 申请人(定制师);无发票行为 null |
|
||||||
|
| requestedAt | string | 申请时间 ISO 8601;无发票行为 null |
|
||||||
|
| issuedAt | string | 开票时间 ISO 8601;无发票行为 null |
|
||||||
|
| issuedBy | string | 开票人;无发票行为 null |
|
||||||
|
| invoiceNo | string | 发票号;无发票行为 null |
|
||||||
|
| fileUrl | string | 发票文件 URL;无发票行为 null |
|
||||||
|
| pdfName | string | PDF 文件名;无发票行为 null |
|
||||||
|
| pdfSize | string | PDF 文件大小(字符串,如 128KB);无发票行为 null |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
## 6 枚举 / 数据字典
|
||||||
|
|
||||||
|
### 6.1 tab 枚举(查询参数 & tabCounts.code)
|
||||||
|
|
||||||
|
| code | name | 说明 |
|
||||||
|
|------|------|------|
|
||||||
|
| REQUESTED | 待开票 | 已申请、待财务开票 |
|
||||||
|
| ISSUED | 待推送 | 已开票、待推送给客户 |
|
||||||
|
| PUSHED | 已推送 | 已推送给客户 |
|
||||||
|
| NONE | 未申请 | 已完成订单但尚未申请发票 |
|
||||||
|
| ALL | 全部 | 订单视角:全部 COMPLETED 订单(含无发票行) |
|
||||||
|
|
||||||
|
### 6.2 stats.code 枚举
|
||||||
|
|
||||||
|
| code | name |
|
||||||
|
|------|------|
|
||||||
|
| REQUESTED | 待开票 |
|
||||||
|
| ISSUED | 已开票(待推送) |
|
||||||
|
| PUSHED | 已推送 |
|
||||||
|
| CURRENT_MONTH_ISSUED | 本月已开票 |
|
||||||
|
|
||||||
|
### 6.3 发票状态枚举(records.status)
|
||||||
|
|
||||||
|
| code | statusText | 说明 |
|
||||||
|
|------|------------|------|
|
||||||
|
| REQUESTED | 待开票 | 已申请 |
|
||||||
|
| ISSUED | 待推送 | 已开票 |
|
||||||
|
| PUSHED | 已推送 | 已推送 |
|
||||||
|
| NONE | 未申请 | tab=ALL/NONE 时无发票行专用 |
|
||||||
|
|
||||||
|
### 6.4 发票类型枚举(invoiceType)
|
||||||
|
|
||||||
|
| code | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| VAT_NORMAL | 增值税普通发票 |
|
||||||
|
| VAT_SPECIAL | 增值税专用发票 |
|
||||||
|
|
||||||
|
### 6.5 抬头类型枚举(titleType)
|
||||||
|
|
||||||
|
| code | 说明 |
|
||||||
|
|------|------|
|
||||||
|
| PERSONAL | 个人 |
|
||||||
|
| COMPANY | 企业 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7 错误码
|
||||||
|
|
||||||
|
| 错误码 | 含义 | 前端处理建议 |
|
||||||
|
|--------|------|-------------|
|
||||||
|
| 200 | 成功 | 正常渲染 |
|
||||||
|
| 401 | 未认证 | 跳登录 |
|
||||||
|
| 403 | 无权限 | 提示无访问权限 |
|
||||||
|
| 500 | 服务端异常 | 通用错误提示 |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
## 8 示例
|
||||||
|
|
||||||
|
### 8.1 典型成功——tab=ALL,混合行(有发票 + 无发票)
|
||||||
|
|
||||||
|
**请求**
|
||||||
|
```
|
||||||
|
GET /v3/admin/order/invoice/page?tab=ALL&pageNo=1&pageSize=20
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应**
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"tabCounts": [
|
||||||
|
{"code": "REQUESTED", "name": "待开票", "count": 3,},
|
||||||
|
{"code": "ISSUED", "name": "待推送", "count": 1,},
|
||||||
|
{"code": "PUSHED", "name": "已推送", "count": 5,},
|
||||||
|
{"code": "NONE", "name": "未申请", "count": 12,},
|
||||||
|
{"code": "ALL", "name": "全部", "count": 21}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{"code": "REQUESTED", "name": "待开票", "count": 3, "amount": 8800.00},
|
||||||
|
{"code": "ISSUED", "name": "已开票", "count": 1, "amount": 3200.00},
|
||||||
|
{"code": "PUSHED", "name": "已推送", "count": 5, "amount": 15600.00},
|
||||||
|
{"code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 4, "amount": 12000.00}
|
||||||
|
],
|
||||||
|
"records": [
|
||||||
|
{
|
||||||
|
"id": "1923456789012345678",
|
||||||
|
"orderId": "1823456789012345678",
|
||||||
|
"orderNo": "HL20260620001",
|
||||||
|
"productName": "云南深度游7日",
|
||||||
|
"tierName": "2026-07-01班期",
|
||||||
|
"contactName": "张三",
|
||||||
|
"customizerName": "李定制",
|
||||||
|
"departureDate": "2026-07-01",
|
||||||
|
"adultCount": 2,
|
||||||
|
"childCount": 1,
|
||||||
|
"youngChildCount": 0,
|
||||||
|
"invoiceType": "VAT_NORMAL",
|
||||||
|
"invoiceTypeText": "增值税普通发票",
|
||||||
|
"titleType": "COMPANY",
|
||||||
|
"titleName": "北京科技有限公司",
|
||||||
|
"taxNo": "91110000123456789X",
|
||||||
|
"amount": 6800.00,
|
||||||
|
"email": "finance@example.com",
|
||||||
|
"status": "REQUESTED",
|
||||||
|
"statusText": "待开票",
|
||||||
|
"requestedBy": "李定制",
|
||||||
|
"requestedAt": "2026-06-20T10:30:00+08:00",
|
||||||
|
"issuedAt": null,
|
||||||
|
"issuedBy": null,
|
||||||
|
"invoiceNo": null,
|
||||||
|
"fileUrl": null,
|
||||||
|
"pdfName": null,
|
||||||
|
"pdfSize": null
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": null,
|
||||||
|
"orderId": "1823456789012345679",
|
||||||
|
"orderNo": "HL20260619002",
|
||||||
|
"productName": "西藏圣地朝圣8日",
|
||||||
|
"tierName": "2026-06-25班期",
|
||||||
|
"contactName": "王五",
|
||||||
|
"customizerName": "赵定制",
|
||||||
|
"departureDate": "2026-06-25",
|
||||||
|
"adultCount": 4,
|
||||||
|
"childCount": 0,
|
||||||
|
"youngChildCount": 0,
|
||||||
|
"invoiceType": null,
|
||||||
|
"invoiceTypeText": null,
|
||||||
|
"titleType": null,
|
||||||
|
"titleName": null,
|
||||||
|
"taxNo": null,
|
||||||
|
"amount": null,
|
||||||
|
"email": null,
|
||||||
|
"status": "NONE",
|
||||||
|
"statusText": "未申请",
|
||||||
|
"requestedBy": null,
|
||||||
|
"requestedAt": null,
|
||||||
|
"issuedAt": null,
|
||||||
|
"issuedBy": null,
|
||||||
|
"invoiceNo": null,
|
||||||
|
"fileUrl": null,
|
||||||
|
"pdfName": null,
|
||||||
|
"pdfSize": null
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"total": 21,
|
||||||
|
"pageNo": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 8.2 边界情况——tab=ALL 传了 titleName(被静默忽略)
|
||||||
|
|
||||||
|
**请求**
|
||||||
|
```
|
||||||
|
GET /v3/admin/order/invoice/page?tab=ALL&titleName=北京科技&pageNo=1&pageSize=20
|
||||||
|
```
|
||||||
|
|
||||||
|
**说明**:titleName 参数不参与过滤,接口正常返回全量 COMPLETED 订单分页。records 中仍会出现 status=NONE 的无发票行,响应结构与 8.1 相同,此处不重复。
|
||||||
|
|
||||||
|
### 8.3 业务失败——无已完成订单时 tab=ALL 返回空列表
|
||||||
|
|
||||||
|
**请求**
|
||||||
|
```
|
||||||
|
GET /v3/admin/order/invoice/page?tab=ALL&pageNo=1&pageSize=20
|
||||||
|
```
|
||||||
|
|
||||||
|
**响应**(无 COMPLETED 订单时)
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"code": 200,
|
||||||
|
"data": {
|
||||||
|
"tabCounts": [
|
||||||
|
{"code": "REQUESTED", "name": "待开票", "count": 0},
|
||||||
|
{"code": "ISSUED", "name": "待推送", "count": 0},
|
||||||
|
{"code": "PUSHED", "name": "已推送", "count": 0},
|
||||||
|
{"code": "NONE", "name": "未申请", "count": 0},
|
||||||
|
{"code": "ALL", "name": "全部", "count": 0}
|
||||||
|
],
|
||||||
|
"stats": [
|
||||||
|
{"code": "REQUESTED", "name": "待开票", "count": 0, "amount": 0},
|
||||||
|
{"code": "ISSUED", "name": "已开票", "count": 0, "amount": 0},
|
||||||
|
{"code": "PUSHED", "name": "已推送", "count": 0, "amount": 0},
|
||||||
|
{"code": "CURRENT_MONTH_ISSUED", "name": "本月已开票", "count": 0, "amount": 0}
|
||||||
|
],
|
||||||
|
"records": [],
|
||||||
|
"total": 0,
|
||||||
|
"pageNo": 1,
|
||||||
|
"pageSize": 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9 业务边界
|
||||||
|
|
||||||
|
**适用**:
|
||||||
|
- 订单状态为 COMPLETED(已完成)的订单才出现在 ALL/NONE tab
|
||||||
|
- REQUESTED/ISSUED/PUSHED 三个 tab 仍以发票记录为视角,只返回有对应状态发票的订单行
|
||||||
|
|
||||||
|
**不适用**:
|
||||||
|
- 进行中(未完成)订单不出现在任何 tab
|
||||||
|
- 已取消订单不出现
|
||||||
|
|
||||||
|
**特殊边界**:
|
||||||
|
- ALL tab 下:tabCounts.ALL.count = REQUESTED + ISSUED + PUSHED + NONE,四者无重叠
|
||||||
|
- 同一订单只有一张有效发票记录,不会重复出现
|
||||||
|
- titleName/requestedBy 在 ALL/NONE tab 传入后静默忽略,不报错、不影响结果
|
||||||
|
- productCoverImg 无封面时为 null,前端需做防空处理
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 10 修改前后对比
|
||||||
|
|
||||||
|
### 字段级对比
|
||||||
|
|
||||||
|
| 字段 | 变更前 | 变更后 |
|
||||||
|
|------|--------|--------|
|
||||||
|
| tabCounts.ALL.count | 仅 REQUESTED+ISSUED+PUSHED 发票记录数之和 | 全部 COMPLETED 订单数(含无发票订单) |
|
||||||
|
| records(tab=ALL 时) | 仅返回有发票记录的订单行,无 status=NONE 行 | 返回全部 COMPLETED 订单,无发票行 status=NONE |
|
||||||
|
| records[].id(tab=ALL 时) | 所有行均有值 | 无发票行为 null |
|
||||||
|
| records[].amount(tab=ALL 时) | 所有行均有值 | 无发票行为 null |
|
||||||
|
| records[].productCoverImg | 无此字段 | 新增,产品封面图 URL,无封面为 null |
|
||||||
|
|
||||||
|
### tab=ALL 计数对比(假设 2 张发票 + 12 个未申请)
|
||||||
|
|
||||||
|
变更前:
|
||||||
|
```
|
||||||
|
ALL.count = 2 (仅发票记录,ALL < NONE,矛盾)
|
||||||
|
NONE.count = 12
|
||||||
|
```
|
||||||
|
|
||||||
|
变更后:
|
||||||
|
```
|
||||||
|
ALL.count = 14 (= 2 + 12,ALL >= NONE,正确)
|
||||||
|
NONE.count = 12
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 11 影响评估 / 回滚
|
||||||
|
|
||||||
|
**破坏兼容性**:是
|
||||||
|
|
||||||
|
- 前端若假设 tab=ALL 列表行的 id 一定不为 null,需修改:无发票行 id 为 null
|
||||||
|
- 前端若用 id !== null 判断是否显示开票按钮,需改为判断 status 是否为 NONE
|
||||||
|
- tabCounts.ALL.count 现在总是 >= 之前的值,若前端有基于此的断言需同步更新
|
||||||
|
- productCoverImg 为新增字段,前端需在合适位置渲染,null 时不显示
|
||||||
|
- stats 块不受影响,无需改动
|
||||||
|
|
||||||
|
**前端同步上线**:建议与本次后端部署同期上线,避免显示数据矛盾窗口期。
|
||||||
|
|
||||||
|
**回滚方案**:回滚后端至上一个版本 jar 即可恢复旧行为;若前端已适配新结构则需同步回滚前端。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 12 注意事项
|
||||||
|
|
||||||
|
1. **无发票行的 id 字段为 null**,前端不可用 id 做有无发票判断,应改用 status 字段值是否为 NONE。
|
||||||
|
2. **amount 字段类型为 JSON number(元)**,不是字符串,渲染时直接用数值格式化。
|
||||||
|
3. **titleName/requestedBy 在 ALL/NONE tab 下静默忽略**,搜索框可保留,后端不过滤,行为符合预期。
|
||||||
|
4. **stats 块不受 tab 切换影响**,始终返回全局四项汇总统计,与当前 tab 无关。
|
||||||
|
5. **tabCounts 固定 5 项、顺序不变**,前端可按 code 匹配或按索引渲染。
|
||||||
|
6. **productCoverImg 为 null 时**,前端不显示图片占位,不报错,直接跳过渲染。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 13 关联 / 联系人
|
||||||
|
|
||||||
|
- **Issue**:[#4337 发票管理列表 ALL tab 改订单视角](https://git.1814.love:8443/wx/HL/issues/4337)
|
||||||
|
- **PR**:[#4338](https://git.1814.love:8443/wx/HL/pulls/4338)(ALL 改订单视角)+ [#4349](https://git.1814.love:8443/wx/HL/pulls/4349)(行新增 productCoverImg)
|
||||||
|
- **后端负责人**:腰苏图(yaosutu)
|
||||||
@ -0,0 +1,314 @@
|
|||||||
|
# 发票申请删 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 <token>
|
||||||
|
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 <token>
|
||||||
|
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)
|
||||||
正在加载...
x
在新工单中引用
屏蔽一个用户