模块: 09-发票管理
版本: v1.0
日期: 2025-10-03
关联文档:
- 需求文档:
docs/09-发票管理模块需求.md- 原型图:
yuanxing/09-发票管理原型图.md- 数据库:
sql/crm/09-crm_invoice.sql
接口地址: GET /crm/invoice/list
接口描述: 分页查询发票列表,支持多条件筛选
权限标识: crm:invoice:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | pageNum | Integer | 否 | 页码,默认1 | | pageSize | Integer | 否 | 每页条数,默认10 | | invoiceNo | String | 否 | 发票编号(模糊查询) | | invoiceNumber | String | 否 | 发票号码(模糊查询) | | contractId | Long | 否 | 合同ID | | contractNo | String | 否 | 合同编号(模糊查询) | | customerId | Long | 否 | 客户ID | | customerName | String | 否 | 客户名称(模糊查询) | | paymentId | Long | 否 | 回款ID | | invoiceType | String | 否 | 发票类型 | | invoiceTitle | String | 否 | 发票抬头(模糊查询) | | status | String | 否 | 发票状态 | | expressStatus | String | 否 | 快递状态 | | ownerUserId | Long | 否 | 负责人ID | | applyBy | Long | 否 | 申请人ID | | invoiceDateBegin | String | 否 | 开票日期开始 | | invoiceDateEnd | String | 否 | 开票日期结束 | | beginTime | String | 否 | 创建开始时间 | | endTime | String | 否 | 创建结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"rows": [
{
"id": 1,
"invoiceNo": "INV20250125001",
"invoiceCode": "044001800104",
"invoiceNumber": "12345678",
"contractId": 1,
"contractNo": "CON20250115001",
"customerId": 1,
"customerName": "华为技术有限公司",
"paymentId": 1,
"paymentNo": "PAY20250120001",
"invoiceType": "special",
"invoiceTitle": "华为技术有限公司",
"taxNo": "91440300xxxx",
"companyAddress": "深圳市龙岗区xxx",
"companyPhone": "0755-12345678",
"bankName": "中国工商银行深圳分行",
"bankAccount": "4000 0000 0000 1234",
"amount": 38207.55,
"taxRate": 6.00,
"taxAmount": 2292.45,
"totalAmount": 40500.00,
"invoiceDate": "2025-01-25",
"status": "invoiced",
"invoiceFile": "/upload/invoice/INV20250125001.pdf",
"invoiceImage": "/upload/invoice/INV20250125001.jpg",
"expressCompany": "顺丰速运",
"expressNo": "SF1234567890",
"expressStatus": "sent",
"expressTime": "2025-01-26 10:00:00",
"mailAddress": "深圳市龙岗区xxx",
"mailContact": "张伟",
"mailMobile": "13800138000",
"ownerUserId": 1,
"ownerUserName": "张三",
"applyBy": 1,
"applyByName": "张三",
"applyTime": "2025-01-25 09:00:00",
"approveBy": 2,
"approveByName": "李四",
"approveTime": "2025-01-25 10:00:00",
"invoiceBy": 3,
"invoiceByName": "王五",
"createTime": "2025-01-25 09:00:00",
"remark": ""
}
],
"total": 125
}
接口地址: GET /crm/invoice/{id}
接口描述: 根据ID查询发票详细信息
权限标识: crm:invoice:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID |
返回示例: 同1.1列表返回的单条数据,包含完整字段
接口地址: POST /crm/invoice
接口描述: 新增发票申请信息
权限标识: crm:invoice:add
请求体:
{
"invoiceNo": "INV20250125001",
"contractId": 1,
"customerId": 1,
"paymentId": 1,
"invoiceType": "special",
"invoiceTitle": "华为技术有限公司",
"taxNo": "91440300xxxx",
"companyAddress": "深圳市龙岗区xxx",
"companyPhone": "0755-12345678",
"bankName": "中国工商银行深圳分行",
"bankAccount": "4000 0000 0000 1234",
"amount": 38207.55,
"taxRate": 6.00,
"mailAddress": "深圳市龙岗区xxx",
"mailContact": "张伟",
"mailMobile": "13800138000",
"ownerUserId": 1,
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | invoiceNo | String | 是 | 发票编号,唯一,格式:INV+yyyyMMdd+4位流水号 | | contractId | Long | 是 | 合同ID | | customerId | Long | 是 | 客户ID | | paymentId | Long | 否 | 回款ID | | invoiceType | String | 是 | 发票类型:special/normal/electronic | | invoiceTitle | String | 是 | 发票抬头,最大200字符 | | taxNo | String | 是 | 纳税人识别号,最大50字符 | | companyAddress | String | 否 | 企业地址,最大255字符 | | companyPhone | String | 否 | 企业电话,最大50字符 | | bankName | String | 否 | 开户银行,最大100字符 | | bankAccount | String | 否 | 银行账号,最大100字符 | | amount | BigDecimal | 是 | 开票金额(不含税),保留2位小数 | | taxRate | BigDecimal | 是 | 税率(百分比),默认6.00 | | mailAddress | String | 是 | 邮寄地址,最大255字符 | | mailContact | String | 是 | 收件人,最大50字符 | | mailMobile | String | 是 | 收件电话,最大20字符 | | ownerUserId | Long | 否 | 负责人ID,默认当前用户 | | remark | String | 否 | 备注,最大500字符 |
校验规则:
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"id": 1,
"invoiceNo": "INV20250125001"
}
}
接口地址: PUT /crm/invoice
接口描述: 修改发票申请信息
权限标识: crm:invoice:edit
请求体: 同新增接口,但需要传入id
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: DELETE /crm/invoice/{ids}
接口描述: 删除发票(支持批量)
权限标识: crm:invoice:remove
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ids | Long[] | 是 | 发票ID数组 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/invoice/approve
接口描述: 审批发票申请
权限标识: crm:invoice:approve
请求体:
{
"id": 1,
"approved": true,
"rejectReason": "",
"remark": "审批通过"
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | approved | Boolean | 是 | 是否通过:true-通过,false-驳回 | | rejectReason | String | 否 | 驳回原因(驳回时必填),最大500字符 | | remark | String | 否 | 审批说明 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/invoice/issue
接口描述: 开具发票
权限标识: crm:invoice:issue
请求体:
{
"id": 1,
"invoiceCode": "044001800104",
"invoiceNumber": "12345678",
"invoiceDate": "2025-01-25",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | invoiceCode | String | 是 | 发票代码,最大50字符 | | invoiceNumber | String | 是 | 发票号码,最大50字符 | | invoiceDate | String | 是 | 开票日期 yyyy-MM-dd | | remark | String | 否 | 开票说明 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/invoice/express
接口描述: 寄出发票
权限标识: crm:invoice:express
请求体:
{
"id": 1,
"expressCompany": "顺丰速运",
"expressNo": "SF1234567890",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | expressCompany | String | 是 | 快递公司,最大50字符 | | expressNo | String | 是 | 快递单号,最大100字符 | | remark | String | 否 | 寄出说明 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/invoice/receive
接口描述: 确认发票签收
权限标识: crm:invoice:receive
请求体:
{
"id": 1,
"remark": "客户已签收"
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | remark | String | 否 | 签收说明 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: PUT /crm/invoice/invalid
接口描述: 作废发票
权限标识: crm:invoice:invalid
请求体:
{
"id": 1,
"invalidReason": "客户要求作废",
"remark": ""
}
请求参数说明: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | invalidReason | String | 是 | 作废原因,最大500字符 | | remark | String | 否 | 备注 |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功"
}
接口地址: POST /crm/invoice/uploadFile
接口描述: 上传发票PDF文件
权限标识: crm:invoice:edit
请求参数: multipart/form-data | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | file | File | 是 | 发票文件(PDF) |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"fileUrl": "/upload/invoice/INV20250125001.pdf"
}
}
接口地址: POST /crm/invoice/uploadImage
接口描述: 上传发票扫描件图片
权限标识: crm:invoice:edit
请求参数: multipart/form-data | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | id | Long | 是 | 发票ID | | file | File | 是 | 发票图片(JPG/PNG) |
业务规则:
返回示例:
{
"code": 200,
"msg": "操作成功",
"data": {
"fileUrl": "/upload/invoice/INV20250125001.jpg"
}
}
接口地址: GET /crm/invoice/contract/{contractId}
接口描述: 查询指定合同的所有发票
权限标识: crm:invoice:query
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | contractId | Long | 是 | 合同ID |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"id": 1,
"invoiceNo": "INV20250125001",
"invoiceNumber": "12345678",
"invoiceType": "special",
"totalAmount": 40500.00,
"invoiceDate": "2025-01-25",
"status": "delivered"
}
]
}
接口地址: POST /crm/invoice/export
接口描述: 导出发票数据到Excel
权限标识: crm:invoice:export
请求体: 查询参数同1.1列表查询接口
返回: Excel文件流
接口地址: GET /crm/invoice/statistics/overview
接口描述: 获取发票统计概览数据
权限标识: crm:invoice:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | ownerUserId | Long | 否 | 负责人ID,不传则查询当前用户 | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": {
"totalCount": 125,
"applyingCount": 15,
"approvedCount": 8,
"invoicedCount": 65,
"deliveredCount": 25,
"receivedCount": 10,
"rejectedCount": 2,
"invalidCount": 0,
"totalAmount": 5680000.00,
"avgAmount": 45440.00
}
}
接口地址: GET /crm/invoice/statistics/status
接口描述: 按状态统计发票分布情况
权限标识: crm:invoice:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"status": "invoiced",
"statusName": "已开票",
"count": 65,
"amount": 3850000.00,
"percent": 52.00
}
]
}
接口地址: GET /crm/invoice/statistics/type
接口描述: 按发票类型统计分布情况
权限标识: crm:invoice:list
请求参数: 无
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"invoiceType": "special",
"typeName": "增值税专用发票",
"count": 85,
"amount": 4580000.00,
"percent": 68.00
},
{
"invoiceType": "normal",
"typeName": "增值税普通发票",
"count": 35,
"amount": 950000.00,
"percent": 28.00
}
]
}
接口地址: GET /crm/invoice/statistics/trend
接口描述: 分析发票数量和金额趋势
权限标识: crm:invoice:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | type | String | 否 | 统计类型:month/quarter/year,默认month | | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"period": "2025-01",
"totalCount": 28,
"totalAmount": 1580000.00,
"invoicedCount": 22,
"invoicedAmount": 1250000.00
}
]
}
接口地址: GET /crm/invoice/statistics/owner
接口描述: 按负责人统计发票情况
权限标识: crm:invoice:list
请求参数: | 参数名 | 类型 | 必填 | 说明 | |--------|------|------|------| | beginTime | String | 否 | 开始时间 | | endTime | String | 否 | 结束时间 |
返回示例:
{
"code": 200,
"msg": "查询成功",
"data": [
{
"ownerUserId": 1,
"ownerUserName": "张三",
"totalCount": 35,
"totalAmount": 1850000.00,
"invoicedCount": 28,
"invoicedAmount": 1450000.00
}
]
}
public class CrmInvoiceBo extends BaseEntity {
/** 主键ID */
@NotNull(message = "主键不能为空", groups = { EditGroup.class })
private Long id;
/** 发票编号 */
@NotBlank(message = "发票编号不能为空", groups = { AddGroup.class, EditGroup.class })
private String invoiceNo;
/** 发票代码 */
private String invoiceCode;
/** 发票号码 */
private String invoiceNumber;
/** 合同ID */
@NotNull(message = "合同ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long contractId;
/** 客户ID */
@NotNull(message = "客户ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long customerId;
/** 回款ID */
private Long paymentId;
/** 发票类型 */
@NotBlank(message = "发票类型不能为空", groups = { AddGroup.class, EditGroup.class })
private String invoiceType;
/** 发票抬头 */
@NotBlank(message = "发票抬头不能为空", groups = { AddGroup.class, EditGroup.class })
private String invoiceTitle;
/** 纳税人识别号 */
@NotBlank(message = "纳税人识别号不能为空", groups = { AddGroup.class, EditGroup.class })
private String taxNo;
/** 企业地址 */
private String companyAddress;
/** 企业电话 */
private String companyPhone;
/** 开户银行 */
private String bankName;
/** 银行账号 */
private String bankAccount;
/** 开票金额(不含税) */
@NotNull(message = "开票金额不能为空", groups = { AddGroup.class, EditGroup.class })
@Min(value = 0, message = "开票金额必须大于0")
private BigDecimal amount;
/** 税率 */
@NotNull(message = "税率不能为空", groups = { AddGroup.class, EditGroup.class })
private BigDecimal taxRate;
/** 开票日期 */
private String invoiceDate;
/** 邮寄地址 */
@NotBlank(message = "邮寄地址不能为空", groups = { AddGroup.class, EditGroup.class })
private String mailAddress;
/** 收件人 */
@NotBlank(message = "收件人不能为空", groups = { AddGroup.class, EditGroup.class })
private String mailContact;
/** 收件电话 */
@NotBlank(message = "收件电话不能为空", groups = { AddGroup.class, EditGroup.class })
private String mailMobile;
/** 负责人ID */
private Long ownerUserId;
/** 备注 */
private String remark;
}
public class CrmInvoiceVo implements Serializable {
/** 主键ID */
@ExcelProperty(value = "ID")
private Long id;
/** 发票编号 */
@ExcelProperty(value = "发票编号")
private String invoiceNo;
/** 发票代码 */
@ExcelProperty(value = "发票代码")
private String invoiceCode;
/** 发票号码 */
@ExcelProperty(value = "发票号码")
private String invoiceNumber;
/** 合同ID */
private Long contractId;
/** 合同编号 */
@ExcelProperty(value = "合同编号")
private String contractNo;
/** 客户ID */
private Long customerId;
/** 客户名称 */
@ExcelProperty(value = "客户名称")
private String customerName;
/** 回款ID */
private Long paymentId;
/** 回款编号 */
private String paymentNo;
/** 发票类型 */
@ExcelProperty(value = "发票类型", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_invoice_type")
private String invoiceType;
/** 发票抬头 */
@ExcelProperty(value = "发票抬头")
private String invoiceTitle;
/** 纳税人识别号 */
@ExcelProperty(value = "纳税人识别号")
private String taxNo;
/** 企业地址 */
private String companyAddress;
/** 企业电话 */
private String companyPhone;
/** 开户银行 */
private String bankName;
/** 银行账号 */
private String bankAccount;
/** 开票金额(不含税) */
@ExcelProperty(value = "开票金额")
private BigDecimal amount;
/** 税率 */
@ExcelProperty(value = "税率")
private BigDecimal taxRate;
/** 税额 */
@ExcelProperty(value = "税额")
private BigDecimal taxAmount;
/** 价税合计 */
@ExcelProperty(value = "价税合计")
private BigDecimal totalAmount;
/** 开票日期 */
@ExcelProperty(value = "开票日期")
private String invoiceDate;
/** 发票状态 */
@ExcelProperty(value = "发票状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_invoice_status")
private String status;
/** 驳回原因 */
private String rejectReason;
/** 作废原因 */
private String invalidReason;
/** 作废时间 */
private LocalDateTime invalidTime;
/** 发票PDF文件 */
private String invoiceFile;
/** 发票图片 */
private String invoiceImage;
/** 快递公司 */
@ExcelProperty(value = "快递公司")
private String expressCompany;
/** 快递单号 */
@ExcelProperty(value = "快递单号")
private String expressNo;
/** 快递状态 */
@ExcelProperty(value = "快递状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "crm_express_status")
private String expressStatus;
/** 寄出时间 */
private LocalDateTime expressTime;
/** 签收时间 */
private LocalDateTime receiveTime;
/** 邮寄地址 */
private String mailAddress;
/** 收件人 */
@ExcelProperty(value = "收件人")
private String mailContact;
/** 收件电话 */
private String mailMobile;
/** 负责人ID */
private Long ownerUserId;
/** 负责人姓名 */
@ExcelProperty(value = "负责人")
private String ownerUserName;
/** 申请人ID */
private Long applyBy;
/** 申请人姓名 */
@ExcelProperty(value = "申请人")
private String applyByName;
/** 申请时间 */
@ExcelProperty(value = "申请时间")
private LocalDateTime applyTime;
/** 审批人ID */
private Long approveBy;
/** 审批人姓名 */
@ExcelProperty(value = "审批人")
private String approveByName;
/** 审批时间 */
private LocalDateTime approveTime;
/** 开票人ID */
private Long invoiceBy;
/** 开票人姓名 */
@ExcelProperty(value = "开票人")
private String invoiceByName;
/** 创建时间 */
@ExcelProperty(value = "创建时间")
private LocalDateTime createTime;
/** 备注 */
@ExcelProperty(value = "备注")
private String remark;
}
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| special | 增值税专用发票 | 可抵扣进项税 |
| normal | 增值税普通发票 | 不可抵扣 |
| electronic | 电子发票 | 电子版发票 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| applying | 申请中 | 发票申请中 |
| approved | 已审批 | 审批通过 |
| invoiced | 已开票 | 已开具发票 |
| delivered | 已寄出 | 已寄出发票 |
| received | 已签收 | 客户已签收 |
| rejected | 已驳回 | 审批驳回 |
| invalid | 已作废 | 发票作废 |
| 字典值 | 字典标签 | 说明 |
|---|---|---|
| no | 未寄出 | 尚未寄出 |
| sent | 已寄出 | 已寄出 |
| received | 已签收 | 已签收 |
| 权限标识 | 说明 |
|---|---|
| crm:invoice:list | 发票列表查询 |
| crm:invoice:query | 发票详情查询 |
| crm:invoice:add | 发票新增 |
| crm:invoice:edit | 发票修改 |
| crm:invoice:remove | 发票删除 |
| crm:invoice:export | 发票导出 |
| crm:invoice:approve | 发票审批 |
| crm:invoice:issue | 发票开具 |
| crm:invoice:express | 发票寄出 |
| crm:invoice:receive | 确认签收 |
| crm:invoice:invalid | 发票作废 |
文档结束