# CRM 开票功能 - 前端联调方案 ## 涉及页面 - 新增:`views/crm/invoice/index.vue`(开票列表)、`views/crm/invoice/modules/form.vue`(开票表单)、`views/crm/invoice/detail/index.vue`(开票详情) - 新增:`src/api/crm/invoice/index.ts`(开票接口) - 修改:`views/crm/receivable/modules/form.vue`(回款表单增加「先开票后回款」提示与校验) - 修改:回款列表/详情(展示开票情况提示,或跳转开票) > 后端菜单已新增「营销管理 -> 开票管理」(component = `crm/invoice/index`),前端只需创建对应目录与页面组件。 ## 业务流程与数据带入 1. 合同审批通过 -> 创建开票:开票时选择合同,客户、合同名称从合同带入 2. 开票提交审批 -> 审批通过:审批通过后开票才生效,才允许参与回款校验 3. 审批通过 -> 上传发票附件:通过附件机制上传发票扫描件/电子票 4. 发票已上传附件 -> 创建回款:回款创建时后端校验「合同累计已开票且已上传附件的金额 >= 累计回款金额(含本次)」 ## API ### 开票接口 | 方法 | 路径 | 说明 | |------|------|------| | POST | `/crm/invoice/create` | 创建开票 | | PUT | `/crm/invoice/update` | 更新开票(仅草稿/审批中) | | DELETE | `/crm/invoice/delete` | 删除开票(审批通过不可删) | | GET | `/crm/invoice/get` | 获得开票详情(含附件列表) | | GET | `/crm/invoice/page` | 获得开票分页 | | GET | `/crm/invoice/page-by-customer` | 基于客户的开票分页 | | GET | `/crm/invoice/export-excel` | 导出开票 Excel | | PUT | `/crm/invoice/submit` | 提交开票审批 | | GET | `/crm/invoice/audit-count` | 待审核开票数量 | **创建/更新请求参数:** | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | id | Long | 否 | 更新时必填 | | contractId | Long | 是 | 合同编号,选择合同后客户自动带入 | | ownerUserId | Long | 否 | 负责人,默认当前用户 | | invoiceType | Integer | 是 | 发票类型,字典 `crm_invoice_type`(1专用 2普通 3电子) | | invoiceNo | String | 否 | 发票号码 | | invoiceTitle | String | 否 | 发票抬头 | | price | BigDecimal | 是 | 开票金额(元) | | invoiceTime | LocalDateTime | 是 | 开票日期 | | remark | String | 否 | 备注 | | blobIds | Long[] | 否 | 发票附件 BlobId 列表 | **响应字段新增:** | 字段 | 类型 | 说明 | |------|------|------| | no | String | 开票编号(KP+日期+序号,保存时后端生成) | | customerId / customerName | Long / String | 客户 | | contract | CrmContractRespVO | 合同信息 | | ownerUserName / ownerUserDeptName | String | 负责人/部门 | | auditStatus | Integer | 审批状态(字典 `crm_audit_status`) | | attachmentList | StorageBlobRespDTO[] | 发票附件列表 | | processInstanceId | String | 工作流编号 | **响应示例:** ```json { "code": 0, "data": { "id": 1, "no": "KP20260810000001", "contractId": 100, "contract": { "id": 100, "no": "HT2026...", "name": "..." }, "customerId": 200, "customerName": "XX科技", "invoiceType": 1, "invoiceNo": "02564178", "invoiceTitle": "XX科技有限公司", "price": 9000.00, "invoiceTime": "2026-08-10", "auditStatus": 0, "attachmentList": [{ "id": 1, "url": "...", "name": "发票.pdf" }] } } ``` ### 附件接口(沿用 system 模块) | 方法 | 路径 | 说明 | |------|------|------| | POST | `/system/storage-blob/upload` | 上传发票文件,获得 `blobId` | | POST | `/system/storage-attachment/bind` | 绑定附件到开票(`recordType=crm_invoice`、`recordId=开票id`) | | GET | `/system/storage-attachment/list` | 查询开票附件列表 | | DELETE | `/system/storage-attachment/delete` | 删除附件 | > 上传与保存分离:表单先选文件上传拿到 `blobId`,随创建/更新接口一起提交 `blobIds`;后端绑定。详情接口返回的 `attachmentList` 可直接展示/预览。 ## 字段展示规则 | 字段 | 展示位置 | 说明 | |------|----------|------| | no 开票编号 | 列表、详情 | 跳转详情 | | customerName 客户 | 列表、详情 | 跳转客户详情 | | contract 合同 | 列表、详情 | 跳转合同详情 | | invoiceType 发票类型 | 列表、详情 | 字典展示 | | invoiceNo 发票号码 | 列表、详情 | - | | price 开票金额 | 列表、详情 | 金额格式化 | | invoiceTime 开票日期 | 列表、详情 | 日期格式化 | | auditStatus 审批状态 | 列表、详情 | 字典 `crm_audit_status` | | attachmentList 发票附件 | 详情 | 附件列表,可预览/下载 | | ownerUserName 负责人 | 列表 | - | | remark 备注 | 列表、详情 | - | ## 业务规则说明 | 场景 | 规则 | |------|------| | 创建回款 | 后端校验:同合同「审批通过且已上传发票附件」的累计开票金额 >= 累计回款金额(含本次),否则报错「合同累计开票金额不足,目前可回款 X 元」 | | 开票金额 | 后端校验:开票金额不能超过合同金额减去已审批通过的开票金额 | | 开票编辑 | 仅草稿、审批中可编辑;客户、合同、负责人不可修改 | | 开票删除 | 审批通过后不可删除 | | 开票审批 | 提交后走 BPM 流程(key=`crm-invoice-audit`),审批通过才生效 | | 待办 | 后端 `audit-count` 可用于待办红点 | ## 注意事项 - 发票类型字典 `crm_invoice_type` 后端已配置,前端需在常量/字典中注册(1增值税专用 2增值税普通 3电子) - 菜单权限:管理员需将新菜单(`crm:invoice:*`)分配给相应角色 - 回款表单在用户选择合同后,可提示「请先完成开票并上传发票附件」,若未开票则创建回款会收到后端错误提示 - 附件必须走 `/system/storage-*` 接口,`recordType` 固定为 `crm_invoice`,禁止在业务表新增文件地址字段 - 上传的发票文件建议限制图片/PDF 格式