diff --git a/doc/docs/业务流程/2026-09-20-应收结算与开票申请同步金蝶设计.md b/doc/docs/业务流程/2026-09-20-应收结算与开票申请同步金蝶设计.md new file mode 100644 index 00000000..443abb02 --- /dev/null +++ b/doc/docs/业务流程/2026-09-20-应收结算与开票申请同步金蝶设计.md @@ -0,0 +1,255 @@ +# 应收结算与开票申请同步金蝶设计(A302-2/A302-4 + A303-1/A303-5) + +> 日期:2026-09-20 +> 模块:tms-api(blade-transport、blade-service-api/blade-transport-api、blade-third-party-api/blade-bfi-api 零改动) +> 关联文档:`tms-api/doc/docs/业务流程/2026-09-20-应付单与付款申请同步金蝶全链路业务流程(A102-2保存-A102-4查询-A103-1下推-A103-2回写).md` +> 状态:待评审 + +--- + +## 1. 背景与目标 + +正式结算单的应收(receivable)链路与开票申请的金蝶同步目前未接入:应收结算单「同步金蝶」直接拦截报错;开票申请「同步金蝶」为模拟实现(生成 `K3INV+时间戳` 假单号,未真正调金蝶)。BFI 客户端侧 `saveArBill`(A302-2)、`queryArBill`(A302-4)、`saveInvoiceApply`(A303-1)、`queryInvoiceApply`(A303-4)、`queryInvoiceApplyInvoice`(A303-5) 均已实现但无调用方。 + +本次目标: + +1. **应收结算链路**:应收正式结算单「同步金蝶」→ A302-2 财务应收单保存(前置 A302-4 幂等预检),完全对齐应付链路(A102-2/A102-4)模式。 +2. **开票申请链路真实化**:开票申请「同步金蝶」替换为真实 A303-1 推送(前置校验关联应收单已生成 + A303-4 幂等预检)。 +3. **定时回写扩展**:复用 `PaymentKingdeeResultTask`,新增 ② A302-4 应收勾稽纠偏、③ A303-5 开票结果回写(发票结果落新表,状态推进 `invoiced` 终态)。 + +## 2. 范围 + +**做**:上述三条链路;新表 `blade_invoice_application_invoice`;`KingdeeBfiConstant`/`BfiProperties` 常量与配置扩展;定时任务编排扩展;手动触发接口。 + +**不做(后续迭代)**:收票 A303-2/A302-6、收款认领 A301-1/2/3、客商风险等级与高风险管控、履约计划、附件推送、收票认领先进先出规则、应付链路任何改动、BFI 客户端任何改动。 + +## 3. 总体原则(与既有链路一致) + +1. **先内后外**:单据先在 TMS 审批通过(approved)再推金蝶。 +2. **以金蝶侧实际生成为准**:每次推送前按防重键(awrp_src_sys=WLTMS + awrp_src_code)查金蝶幂等预检,已生成则回填跳过;推送失败可断点续传。 +3. **failed 纳入定时勾稽纠偏**:远端实际成功但本地回滚的单据由定时任务查到后自动纠偏,防止人工重推产生重复单据。 + +## 4. 业务流程 + +``` +【环节一】正式结算单「同步金蝶」(人工,单笔) + 审批通过的应收正式结算单 + → 校验:approved;settlementType=receivable(本次放行);非 synced;有结算明细 + → 组装防重键(正式结算单号_结算明细单据号…,与应付同格式) + → A302-4 幂等预检 ──已生成──> 幂等跳过保存,直接回填已有单号 + → A302-2 财务应收单保存(明细分录逐应收货物费用行) + → 回写结算单:kingdeeBillNo + synced + syncedTime + +【环节二】开票申请「同步金蝶」(人工,替换 K3INV 模拟) + 审批通过的开票申请 + → 前置校验:A302-4 校验关联应收结算单的应收单已生成(未生成拦截,提示先完成环节一) + → A303-4 幂等预检(billno=applicationNo)──已生成──> 直接回填,视为已同步 + → A303-1 开票申请单新增(购销方/商品行分录) + → 回写开票申请:kingdeeBillNo(金蝶真实单号)+ synced + +【环节三】PaymentKingdeeResultTask 定时(每10分钟,编排三个子流程,各自 try/catch 互不影响) + ① A103-2/A103-4 付款回写(现状不动) + ② A302-4 应收勾稽纠偏:轮询 approved+receivable+状态∈{synced,failed}, + 按防重键批量查询:查到→补金蝶单号、failed纠偏为synced;查不到→不变(不推进状态机) + ③ A303-5 开票结果回写:轮询 approved+kingdeeStatus=synced+金蝶单号非空的开票申请, + serialNo 批量查询(≤50/批)+ 销方税号:查到发票→逐张落新表(按发票号幂等) + → 申请单 kingdeeStatus=invoiced(终态);查不到→保持 synced 继续轮询 +``` + +收款由收款认领链路(A301-x)负责,不在本次范围;应收单的审核/核销状态不回写。 + +## 5. 状态机与防重键 + +### 5.1 状态机 + +``` +应收结算单 kingdeeSyncStatus(无新状态值): + unsynced ──A302-2成功──> synced(终态) + └──推送异常──> failed ──重推 或 定时A302-4勾稽纠偏──> synced + +开票申请 kingdeeStatus(新增枚举值 invoiced,varchar 无需 DDL): + unsynced ──A303-1成功──> synced ──A303-5查到发票──> invoiced(终态) + └──推送异常──> failed ──人工重推(A303-4 预检命中→幂等回填)──> synced + 注:定时任务只轮询 synced(failed 无真实金蝶单号,A303-5 查询无入口,纠偏走重推预检) +``` + +### 5.2 防重键体系(awrp_src_sys = WLTMS) + +| 单据 | awrp_src_code | 使用接口 | +| --- | --- | --- | +| 财务应收单 | 正式结算单号_结算明细单据号1_单据号2…(去重保序下划线拼接) | A302-2 / A302-4(预检+定时勾稽) | +| 开票申请单 | 开票申请单号 applicationNo(A303-1 billno 候选键) | A303-1 / A303-4(幂等)/ A303-5(serialNo) | +| 分录唯一标识 | 应收货物费用行 id(雪花 id,awrp_entry_code) | A302-2 明细 | + +## 6. 接口字段映射 + +### 6.1 A302-2 财务应收单保存(主表) + +| 接口字段 | 取数 | 备注 | +| --- | --- | --- | +| billtype_number | 常量「服务销售应收」→ arfin_sersal_BT_S | AR_BILL_BILLTYPE_MAP 已有;映射名新增常量,待金蝶确认 | +| org_number / recorg_number | 合同所属组织 deptCode | 复用 bfiOrgNumber | +| bizdate | 同步当日 yyyy-MM-dd HH:mm:ss | | +| asstacttype | bd_customer(固定客户) | | +| asstact_number | 结算单付款方 payerName → 客商档案(全称/简称)客商编码 | 未维护报错;与应付(合同乙方)对称 | +| paymode | CASH(现销) | 待金蝶确认 | +| payproperty_number | 1001(一般销售) | 与应付 2001 区分,待金蝶确认 | +| currency_number | 合同结算币种(RMB→CNY) | 复用 bfiCurrency | +| isincludetax / ispricetotal / isintax | true / true / false | 与应付一致 | +| awrp_src_sys / awrp_src_code / awrp_src_type | WLTMS / 防重键 /「TMS财务应收单」 | src_type 新常量,待金蝶确认 | +| awrp_src_bt / awrp_src_con_no | 本期不传 | 与应付一致,保留注释 | +| duedate / 结算方式 / 税率编码 | 本期不传 | 待业务确认(遗留清单) | + +### 6.2 A302-2 明细分录(entry,逐应收货物费用行,不聚合) + +| 接口字段 | 取数 | 备注 | +| --- | --- | --- | +| e_material_number | 货物类型→货物类型档案货物编码 | 联调期固定 SP0324(同应付) | +| e_measureunit_number | 运费计算单位(兜底数量单位) | 联调期固定 TON(同应付) | +| e_quantity | 运输数量,兜底 1 | | +| e_unitprice | 运输单价 | ispricetotal=true 仅参考 | +| e_recamount | 应收金额(含税)= 费用行调整后总金额 afterAmount | 对应应付 e_pricetaxtotal 语义 | +| e_amount | 金额(不含税)= 结算费用行不含税回链 | 兜底 0,与应付同源逻辑 | +| project_number | 联调期 999 | | +| awrp_entry_code | 应收货物费用行 id | 候选键 | + +收款计划 planentity 不传,由金蝶按明细自动汇总。 + +### 6.3 A303-1 开票申请单新增(主表) + +| 接口字段 | 取数 | 备注 | +| --- | --- | --- | +| orgid_number | 申请部门 deptCode | deptId→SysCache | +| billno | applicationNo | 候选键,天然唯一 | +| billdate | applicationDate yyyy-MM-dd HH:mm:ss | | +| billproperties | 1(正数) | | +| invoicetype | electronic_normal→10xdp、electronic_special→08xdp | 数电普票/专票 | +| taxationstyle | 0(普通征税) | | +| buyeremail | departmentEmails | 必填 | +| buyerproperty | 0(企业) | | +| buyertaxno / buyername | 申请单快照 taxpayerNo / receiverName | | +| salername / salertaxno | 申请单快照 issuerName(结算单收款方/开票单位);按开票单位查客商档案(全称/简称)取统一社会信用代码 | A303-5 的 sellerTaxpayerId 同源,不走 Nacos 配置 | +| totalamount | invoiceAmount(价税合计) | | +| invoiceamount / totaltax | 明细不含税合计 / 税额合计 | | +| customnameid_number | receiverCustomerId → 客商档案客商编码 | 未维护报错 | +| awrp_project_number | 联调期 999 | | +| awrp_orgfield_number | 申请部门 deptCode | | +| awrp_src_bt | ar_finarbill | 上游财务应收单 | +| awrp_src_con_no | 关联结算单金蝶应收单号 | 多张取第一张,拼接规则联调确认 | +| billsource / systemsource | 3(固定)/ 配置 srcSys | systemsource 待联调确认 | +| 合同号/合同金额/购方地址银行等选填 | 本期不传 | 保留注释(遗留清单) | + +### 6.4 A303-1 分录(sim_original_bill_item,发票商品行平铺) + +| 接口字段 | 取数 | 备注 | +| --- | --- | --- | +| goodsname / goodssimplename | line.goodsName / line.goodsCategory | 货物或服务简称 / 商品和服务分类 | +| taxratecodeid_number | line.taxClassificationCode(商品行快照,取自开票项目主数据税收分类编码) | 新增/编辑开票申请选择开票内容时随前端落库,缺失即报错提示重选 | +| num | line.quantity | | +| taxunitprice | line.amountWithTax ÷ quantity | 数量为 0 时兜底 1 | +| taxamount | line.totalAmount(价税合计) | | +| taxrate | line.taxRate ÷ 100(0-100 转小数) | | +| unit | 兜底「个」 | 行无单位字段,联调校准 | +| benchmark | 0 | | +| rowtype / awrp_src_con_eno | 待联调确认 | 先不传/示例值,见遗留清单 | +| awrp_src_type | 配置 thirdParty.bfi.invoiceApply.srcType(默认 0KzXs) | 与付款申请 payApply.srcType 对齐 | + +### 6.5 A302-4 / A303-5 回写取数 + +- **A302-4 勾稽**:按 awrp_src_code 归并,命中即以 `billno` 回填/纠偏,其余字段不取用。 +- **A303-5**:`serialNo`(金蝶开票申请单号,≤50/批逗号拼接)+ `sellerTaxpayerId`(与 A303-1 同源:按各申请单开票单位查客商档案统一社会信用代码,按税号分组分批查询,无法解析的申请单跳过并告警)。返回为票易通原文 `List`:整体 JSON 留档 `raw_json`,宽松提取发票号码/开票日期/价税合计三个字段(Jackson 泛型解析,具体字段名联调校准后收敛为常量映射)。 + +## 7. 代码组织与改动清单 + +### 7.1 blade-transport(主改动) + +| # | 文件 | 改动 | +| --- | --- | --- | +| 1 | `support/KingdeeArBillSyncSupport`(新建) | 应收单防重键组装(复用 ExternalSrcCodeSupport.buildSrcCode)、A302-4 批量查询 queryArBillExistNos、应收分录解析 resolveArEntryGroups(取数源同应付:blade_receivable_payable_cargo_fee)、pushArBill(A302-4 预检 + A302-2 组装保存)。公共档案取数(bfiOrgNumber/bfiCurrency/requireContract/客商编码查询)经注入 KingdeeApBillSyncSupport 复用,不复制不修改现有类 | +| 2 | `FormalSettlementServiceImpl.syncKingdee` | 移除应收拦截;按 settlementType 路由 payable→pushApBill / receivable→pushArBill;落库逻辑共用 | +| 3 | `FormalSettlementServiceImpl.syncKingdeeArResult`(新增) | A302-4 应收勾稽纠偏(轮询+批量查+回填纠偏),接口方法+Controller 手动触发端点 `/sync-kingdee-ar-result` | +| 4 | `InvoiceApplicationServiceImpl.syncKingdee` | 模拟实现替换为真实推送:A302-4 前置校验(关联应收单已生成)→ A303-4 幂等预检 → A303-1 保存 → 回写 | +| 5 | `InvoiceApplicationServiceImpl.syncKingdeeInvoiceResult`(新增) | A303-5 回写:轮询+批量查+发票落新表+invoiced 推进;Controller 手动触发端点 `/sync-kingdee-invoice-result` | +| 6 | `task/PaymentKingdeeResultTask` | 注入 IFormalSettlementService、IInvoiceApplicationService,编排 ①付款回写 ②应收勾稽 ③开票结果 三个子流程,各自独立 try/catch,汇总日志 | +| 7 | `constant/KingdeeBfiConstant` | 新增:BFI_AR_BILL_BILLTYPE_NAME「服务销售应收」、BFI_AR_BILL_PAY_PROPERTY=1001、BFI_AR_BILL_PAY_MODE_CASH=CASH、BFI_AR_BILL_SRC_TYPE「TMS财务应收单」、KINGDEE_STATUS_INVOICED=invoiced、发票类型映射(electronic_normal→10xdp / electronic_special→08xdp) | +| 8 | `config/BfiProperties` | 新增 invoiceApply 节点:srcType(默认 0KzXs)。销方名称/税号、税收分类编码不走配置:销方名称取申请单开票单位快照、税号按开票单位查客商档案统一社会信用代码、税收分类编码取商品行开票内容快照 | +| 9 | `mapper/InvoiceApplicationInvoiceMapper`(新建) | 接口 + XML 同包 | + +### 7.2 blade-transport-api + +| # | 文件 | 改动 | +| --- | --- | --- | +| 10 | `pojo/entity/InvoiceApplicationInvoice`(新建) | TenantEntity:invoiceApplicationId、serialNo、invoiceNo、invoiceDate、invoiceAmount、rawJson | +| 11 | `IFormalSettlementService` / `IInvoiceApplicationService` | 新增 syncKingdeeArResult / syncKingdeeInvoiceResult 接口方法 | +| 12 | 开票申请详情 VO | 附带发票结果列表(@TableField(exist=false) 或关联查询),前端展示开票结果 | + +## 8. 数据库变更 + +新表 `blade_invoice_application_invoice`(MySQL 版,正式提交时同步 doc/sql 全部 7 库脚本;无菜单 SQL): + +```sql +CREATE TABLE `blade_invoice_application_invoice` ( + `id` bigint NOT NULL COMMENT '主键', + `tenant_id` varchar(12) DEFAULT '000000' COMMENT '租户ID', + `invoice_application_id` bigint NOT NULL COMMENT '开票申请id', + `serial_no` varchar(64) DEFAULT NULL COMMENT '金蝶开票申请单编号', + `invoice_no` varchar(64) NOT NULL COMMENT '发票号码', + `invoice_date` datetime DEFAULT NULL COMMENT '开票日期', + `invoice_amount` decimal(18,2) DEFAULT NULL COMMENT '价税合计', + `raw_json` text COMMENT 'A303-5返回原文JSON留档', + `create_user` bigint DEFAULT NULL COMMENT '创建人', + `create_dept` bigint DEFAULT NULL COMMENT '创建部门', + `create_time` datetime DEFAULT NULL COMMENT '创建时间', + `update_user` bigint DEFAULT NULL COMMENT '修改人', + `update_time` datetime DEFAULT NULL COMMENT '修改时间', + `status` int DEFAULT NULL COMMENT '状态', + `is_deleted` int DEFAULT 0 COMMENT '是否已删除', + PRIMARY KEY (`id`), + UNIQUE KEY `uk_application_invoice` (`invoice_application_id`,`invoice_no`), + KEY `idx_iai_application_id` (`invoice_application_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='开票申请金蝶发票结果'; +``` + +落库幂等:按(invoice_application_id, invoice_no)唯一键,已存在跳过。开票结果不反写结算单——结算单开票状态仍由收票认领链路(InvoiceReceipt)驱动,避免双链路冲突。 + +## 9. 配置项(Nacos thirdParty.bfi.*) + +```yaml +thirdParty: + bfi: + srcSys: WLTMS # 已有 + payResult: + syncEnabled: true # 已有,任务总开关(三个子流程共用) + cron: 0 */10 * * * ? # 已有 + invoiceApply: + srcType: 0KzXs # 新增:分录外部单据类型,与 payApply.srcType 对齐(有默认值,一般无需配置) +``` + +销方名称/销方税号、税收分类编码**不再需要配置**:开票申请「新增」页选择结算单即带出开票单位(销方),销方税号按开票单位查客商档案统一社会信用代码;税收分类编码随「开票内容」选择快照到商品行(blade_invoice_application_line.tax_classification_code,增量脚本 `blade_invoice_application_line_tax_code_20260921.sql`)。 + +## 10. 异常与恢复场景 + +| # | 断点场景 | 金蝶侧实际 | 本地状态 | 恢复路径 | +| --- | --- | --- | --- | --- | +| 1 | 应收单推送前校验失败(档案缺失等) | 无应收单 | 结算单状态不变 | 补档案重新同步 | +| 2 | A302-2 成功但本地回滚 | 应收单已生成 | 未 synced | 重新同步 A302-4 命中→幂等回填;或定时任务纠偏 | +| 3 | 开票申请同步时应收单未生成 | — | 不变 | 拦截报错,先完成环节一 | +| 4 | A303-1 成功但本地回滚 | 开票申请单已生成 | 未 synced | 重推时 A303-4 命中→幂等回填 | +| 5 | 金蝶开票处理中 | 无发票 | synced | A303-5 每轮空查,直到开出票 | +| 6 | A303-5 查到发票 | 发票已开出 | synced | 落发票结果表→invoiced 终态 | + +任一子流程异常只计数不中断整轮(与付款回写一致)。 + +## 11. 验证 + +1. `mvn clean compile -DskipTests` 编译通过(AGENTS.md 要求;测试由用户执行)。 +2. 每个被改符号编辑前跑 GitNexus impact(upstream),提交前 detect_changes(仓库强制规范)。 +3. 联调验证顺序:应收结算单同步(A302-2/4)→ 开票申请同步(A303-1/4)→ 手动触发定时回写(A302-4 勾稽 + A303-5 回写)。 + +## 12. 遗留与待确认清单 + +1. **A302-2**:paymode CASH、款项性质 1001、单据类型「服务销售应收」、awrp_src_type「TMS财务应收单」、到期日/结算方式/税率编码——均待金蝶确认。 +2. **A303-1**:rowtype、awrp_src_con_eno、购方地址/银行展示、systemsource 取值、多结算单单号拼接规则、部门邮箱分隔符——联调校准。 +3. **A303-5**:返回原文字段名(发票号/开票日期/金额)联调后从宽松解析收敛为固定映射。 +4. ~~**销方主体**:当前走配置~~ 已改为单据取数:销方名称=开票申请开票单位快照(结算单收款方带出),销方税号=开票单位对应客商档案统一社会信用代码(要求我方开票主体在客商档案中维护)。 +5. **应收结算单开票完成状态**:本次不反写结算单;如业务需要"已开票"标识,后续在结算单侧增量。 diff --git a/doc/docs/业务流程/2026-09-21-应收结算与开票申请同步金蝶全链路业务流程(A302-2保存-A302-4勾稽-A303-1新增-A303-4预检-A303-5回写).md b/doc/docs/业务流程/2026-09-21-应收结算与开票申请同步金蝶全链路业务流程(A302-2保存-A302-4勾稽-A303-1新增-A303-4预检-A303-5回写).md new file mode 100644 index 00000000..3c09d24f --- /dev/null +++ b/doc/docs/业务流程/2026-09-21-应收结算与开票申请同步金蝶全链路业务流程(A302-2保存-A302-4勾稽-A303-1新增-A303-4预检-A303-5回写).md @@ -0,0 +1,235 @@ +# 应收结算与开票申请同步金蝶全链路业务流程(正式结算单同步 → 开票申请同步 → 应收勾稽与开票结果定时回写) + +> 适用系统:TMS(BladeX 微服务平台,服务:blade-transport) +> 对接系统:金蝶云·星瀚(业财融合 BFI,经 iPaaS 网关) +> 覆盖接口:A302-2 财务应收单保存、A302-4 财务应收单查询、A303-1 开票申请单新增、A303-4 开票申请单查询、A303-5 开票申请发票查询 +> 关联文档:`2026-09-20-应收结算与开票申请同步金蝶设计.md`(设计口径与遗留清单)、`2026-09-20-应付单与付款申请同步金蝶全链路业务流程(A102-2保存-A102-4查询-A103-1下推-A103-2回写).md`(应付侧对称链路) +> 最后更新:2026-09-21(按当前代码梳理;销方名称/销方税号/税收分类编码已改为随单据取数,不再使用 Nacos 配置) + +--- + +## 1. 整体业务链路 + +应收侧一笔结算到最终开出数电票,分**三个环节**,由两个页面 + 一个定时任务接力完成: + +``` +环节一:正式结算单「同步金蝶」(人工,正式收款单/结算单页面,单笔) + 审批通过的应收类正式结算单 → 推送金蝶财务应收单(A302-2,前置 A302-4 幂等预检) + → 回写结算单:金蝶单据编号 + 同步状态 synced + +环节二:开票申请「同步金蝶」(人工,开票申请页面,单笔) + 审批通过的开票申请 → 前置校验关联结算单的应收单已在金蝶生成(A302-4,缺一拦截) + → A303-4 幂等预检(billno=开票申请单号,命中直接勾稽回填) + → 推送金蝶开票申请单(A303-1,购销方信息 + 发票商品行分录) + → 回写开票申请:金蝶单据编号 + 同步状态 synced + +环节三:PaymentKingdeeResultTask 定时回写(自动,默认每10分钟,三子流程各自 try/catch 互不影响) + ① A103-2/A103-4 付款结果回写(应付链路,本文不展开) + ② A302-4 应收勾稽纠偏:轮询 approved+应收+{synced,failed},按防重键批量查询, + 查到 → 补金蝶单号、failed 纠偏为 synced;查不到 → 状态不变 + ③ A303-5 开票结果回写:轮询 approved+synced+金蝶单号非空的开票申请,按销方税号分组 + 分批(≤50/批)查询,查到发票 → 逐张落结果表(发票号幂等)→ 申请单推进 invoiced(终态) +``` + +核心设计原则(与应付链路一致): + +1. **先内后外**:单据先在 TMS 审批通过(approved)才允许推金蝶。 +2. **以金蝶侧实际生成为准**:每次推送前按防重键查询确认,已生成则幂等跳过/回填勾稽,未生成才推送;推送接口真正报错才算失败。 +3. **断点续传**:任一环节中断(远端成功、本地回滚)后重新触发或等定时勾稽,均从断点恢复,不产生重复单据。 +4. **配置最小化**:金蝶推送所需的业务取数一律随单据快照或走档案(客商/开票项目/货物类型/部门),Nacos 仅剩外部系统编号 srcSys 与分录外部单据类型 srcType(有默认值)。 + +--- + +## 2. 五个接口在链路中的角色 + +| 接口 | 名称 | 方向 | 触发环节 | 业务用途 | +| --- | --- | --- | --- | --- | +| A302-2 | 财务应收单保存 | TMS → 金蝶 | 环节一 | 一张应收正式结算单生成一张金蝶财务应收单(ar_finarbill) | +| A302-4 | 财务应收单查询 | TMS → 金蝶(查询) | 环节一前置 / 环节二前置 / 环节三② | ①推送前幂等预检;②开票申请推送前校验关联应收单已生成;③定时勾稽纠偏(防重键批量查询,50/批+自动翻页) | +| A303-1 | 开票申请单新增 | TMS → 金蝶 | 环节二 | 生成金蝶开票申请单(上游勾稽财务应收单),由金蝶/票易通完成实际开票 | +| A303-4 | 开票申请单查询 | TMS → 金蝶(查询) | 环节二前置 | 按 billno=开票申请单号 幂等预检,断链重推时命中即勾稽回填 | +| A303-5 | 开票申请发票查询 | TMS → 金蝶(查询) | 环节三③ | 按开票申请单号+销方税号查已开发票,落 `blade_invoice_application_invoice` 并推进开票完成 | + +收款认领(A301-x)、收票(A303-2/A302-6)、应收单核销状态回写、结算单开票完成反写均不在本链路(后续迭代)。 + +--- + +## 3. 环节一:正式结算单「同步金蝶」推送财务应收单(A302-2) + +代码入口:`FormalSettlementServiceImpl.syncKingdee` → 按结算类型路由,应收走 `KingdeeArBillSyncSupport.pushArBill`(应付走 A102-2,见应付侧文档)。 + +### 3.1 业务流程 + +1. 操作入口:正式结算单页面「同步金蝶」按钮,单笔。 +2. 前置校验(不满足直接报错,不调金蝶): + - 审批状态 = **approved**; + - 同步状态已是 **synced** → 幂等返回已有金蝶单号,不重复推送(failed 允许重推); + - 必须有结算明细(分录取数来源)。 +3. 组装财务应收单防重键(awrp_src_code):**正式结算单号_结算明细单据号1_单据号2…**(去重保序、下划线拼接),与应付侧同规则;一张结算单只生成一张金蝶应收单。 +4. 解析应收单明细分录(`resolveArEntryGroups`,逐**应收货物费用行**一条、不聚合): + - 结算明细 `sourceDetailId` 回链应收应付货物费用行(`blade_receivable_payable_cargo_fee`),无货物费用行直接报错拦截; + - 含税金额取费用行调整后总金额 `afterAmount`,不含税经结算货物费用行 `sourceFeeId` 回链(无映射兜底 0); + - 货物类型(「、/,」多类型不拆行,取第一种)必须在货物类型档案维护货物编码;计量单位取运费计算单位(兜底数量单位)。 +5. **A302-4 幂等预检**:按防重键批量查询,已生成 → 幂等跳过保存、直接取已有单号回写;未生成 → 调 A302-2 保存。 +6. **A302-2 保存**,主表入参来源: + + | 接口字段 | 含义 | 必填 | 来源 | 前端字段(来源页面) | + | --- | --- | --- | --- | --- | + | billtype_number | 单据类型.编码 | 是 | 常量名称「服务销售应收」经 `AR_BILL_BILLTYPE_MAP` 转 `arfin_sersal_BT_S`(待金蝶确认,改常量名即可换映射) | 无(后端常量) | + | org_number / recorg_number | 结算组织/收款组织.编码 | 是 | 合同所属组织 deptCode(未维护报错) | 合同管理页面「所属组织」→ 部门编码 | + | bizdate | 单据日期 | 是 | 同步当日 yyyy-MM-dd HH:mm:ss | 无(系统生成) | + | asstacttype | 往来类型 | 是 | 固定 bd_customer(应收对方必为客户) | 无(后端常量) | + | asstact_number | 往来户.编码 | 是 | 结算单**付款方** payerName → 客商档案按全称/简称匹配取客商编码(未维护报错) | 正式结算单页面「付款方」payerName → 客商档案「客商编号」customerCode | + | paymode | 付款方式 | 是 | 常量 CASH(现销,金蝶自动 100% 推收款) | 无(后端常量,待金蝶确认) | + | payproperty_number | 款项性质.编码 | 是 | 常量 1001(一般销售,与应付 2001 区分,待金蝶确认) | 无(后端常量) | + | currency_number | 结算币别.货币代码 | 是 | 合同结算币种(RMB→CNY) | 合同管理页面「结算币种」 | + | isincludetax / ispricetotal | 录入含税价/录入总价 | 否 | 固定 true / true(免单价换算) | 无(后端常量) | + | awrp_src_sys | 外部系统编号(候选键) | 是 | 配置 thirdParty.bfi.srcSys,默认 WLTMS | 无(后端配置) | + | awrp_src_code | 外部来源单据号(候选键) | 是 | 第 3 步防重键 | 系统组装:「结算单号」+结算明细「单据号」 | + | awrp_src_type | 外部单据类型 | 是 | 常量「TMS财务应收单」(待金蝶确认) | 无(后端常量) | + | 到期日/结算方式/税率编码 | — | 否 | 本期不传(TODO 待金蝶确认后补充) | — | + + 明细分录(entry,逐应收货物费用行): + + | 接口字段 | 含义 | 必填 | 来源 | + | --- | --- | --- | --- | + | e_material_number | 物料编码.编码 | 是 | 货物类型→货物类型档案货物编码;**联调期临时固定 SP0324**(pushArBill 内 `if (true)` 调试块覆盖,校准后移除) | + | e_measureunit_number | 计量单位.编码 | 是 | 运费计算单位(兜底数量单位,档案暂无编码传名称);**联调期临时固定 TON** | + | e_quantity / e_unitprice | 数量/单价 | 是/否 | 费用行运输数量(兜底 1)/运输单价(录入总价仅参考) | + | e_recamount | 应收金额(含税) | 是 | 费用行调整后总金额 afterAmount | + | e_amount | 金额(不含税) | 否 | 结算货物费用行不含税回链,兜底 0 | + | project_number | 项目.项目编码 | 是 | 联调期固定 999(真实来源=项目档案项目编码) | + | awrp_entry_code | 来源分录标识(候选键) | 是 | 应收货物费用行 id(雪花 id 天然唯一) | + + 收款计划 planentity 不传,由金蝶按明细自动汇总生成。 + +7. 回写落库:`result[0].number` → 结算单 `kingdeeBillNo` + `kingdeeSyncStatus=synced` + `syncedTime`,并记结算单变更历史「同步金蝶财务应收单{单号}」。失败抛业务异常,状态不变可重推。 + +### 3.2 状态机与勾稽纠偏 + +``` +kingdeeSyncStatus:unsynced ──A302-2成功──> synced(终态) + └──推送异常──> failed ──人工重推 或 定时②A302-4勾稽纠偏──> synced +``` + +定时子流程②(`FormalSettlementServiceImpl.syncKingdeeArResult`,手动端点 `POST /formal-settlement/sync-kingdee-ar-result`):轮询 approved+receivable+{synced,failed},按防重键批量查 A302-4;查到 → 补回缺失单号、failed 纠偏为 synced;查不到 → 不动。返回汇总:待查 X 笔、回写 Y 笔(纠偏 Z 笔)。 + +--- + +## 4. 环节二:开票申请「同步金蝶」推送开票申请单(A303-1) + +代码入口:`InvoiceApplicationServiceImpl.syncKingdee`;页面 `invoice-application-form.vue`(列表 `invoice-application.vue`)。 + +### 4.1 单据创建与审批(推送前) + +1. 新增开票申请:选择**应收正式结算单**(可多张,须同合同/项目/组织/收付款方)→ 自动带出所属项目、**开票单位**(=结算单收款方,即销方)、承办部门、可开票金额;受票方从付款方客商的开票信息(`blade_customer_invoice_info`)选择,纳税人识别号/开户行/账号/注册地址随选择带出并快照到申请单。 +2. 开票内容按「发票张」维护商品行:商品和服务分类 + 货物或服务简称均从**开票项目主数据**(`blade_invoice_item`)选择,选择时默认税率随带,并**快照税收分类编码**到商品行 `tax_classification_code`(A303-1 分录用,不走配置)。 +3. 商品行只能整体开「运费」或整体开非运费(运费不得与其他费用并票);本次开票金额=商品行含税合计,不得超过可开票金额。 +4. 状态流转:draft(草稿可编辑/删除)→ submit → reviewing → approve → approved(或 return 驳回、void 作废);仅 **approved** 允许同步金蝶。 +5. 结算单开票状态(unreceived/partial/completed)由申请单分摊金额实时刷新(作废/删除联动回退),金蝶回写不反写结算单。 + +### 4.2 同步金蝶流程 + +1. 校验:approved;已 synced/invoiced 幂等返回(invoiced 终态);申请单号非空。 +2. **A302-4 前置校验**:逐关联结算单组装应收单防重键批量查询,任一结算单的财务应收单未在金蝶生成 → 拦截报错「请先在正式付款单页面同步金蝶」(即先完成环节一)。 +3. **A303-4 幂等预检**:billno=开票申请单号查询,金蝶已生成 → 直接回填单号 + synced(勾稽回填,防断链重推重复)。 +4. 取数校验(任一不满足报错):开票单位(销方)非空;**销方税号=开票单位按全称/简称匹配客商档案的统一社会信用代码**;部门邮箱(发票推送邮箱)、购方识别号、申请部门编码、受票方客商编码均已维护;每条商品行已有税收分类编码快照(历史单据缺失时提示编辑重选开票内容)。 +5. **A303-1 新增**,主表入参来源: + + | 接口字段 | 含义 | 必填 | 来源 | 前端字段(来源页面) | + | --- | --- | --- | --- | --- | + | orgid_number / awrp_orgfield_number | 组织/申请部门.编码 | 是 | 承办部门 deptId → SysCache 部门编码(未维护报错) | 开票申请页「承办部门」(结算单带出) | + | billno | 业务单据编号(候选键) | 是 | 开票申请单号 applicationNo | 系统生成(TMSKV 规则单号) | + | billdate | 单据日期 | 是 | 申请日期(提交时置当日) | 页面「申请日期」 | + | billproperties | 单据性质 | 是 | 固定 1(正数) | 无 | + | invoicetype | 发票种类 | 是 | electronic_normal→10xdp(数电普票)、electronic_special→08xdp(数电专票) | 页面「发票类型」下拉 | + | taxationstyle | 征税方式 | 是 | 固定 0(普通征税) | 无 | + | buyeremail | 发票推送邮箱 | 是 | 部门邮箱(客商开票信息邮箱多选,`;` 拼接) | 页面「部门邮箱」 | + | buyerproperty / buyertaxno / buyername | 购方企业类型/税号/名称 | 是 | 固定 0(企业)/ 申请单快照 taxpayerNo / receiverName | 页面「受票方单位」选择带出 | + | salername | 销方名称 | 是 | 申请单快照开票单位 issuerName(=结算单收款方) | 页面「开票单位」(结算单带出) | + | salertaxno | 销方纳税人识别号 | 是 | 开票单位 → 客商档案统一社会信用代码(**A303-5 sellerTaxpayerId 同源**) | 客商档案「统一社会信用代码」 | + | totalamount / invoiceamount / totaltax | 价税合计/不含税合计/税额合计 | 是 | 申请单开票金额 / 商品行不含税合计 / 税额合计 | 页面「本次开票金额」、商品行自动计算 | + | customnameid_number | 客户.编码 | 是 | 受票方客商档案客商编码(未维护报错) | 客商档案「客商编号」 | + | awrp_project_number | 项目.项目编码 | 是 | 联调期 999 | — | + | billsource | 数据来源 | 是 | 固定 3 | 无 | + | awrp_src_bt / awrp_src_con_no | 上游来源单据/单号 | 是 | ar_finarbill / 关联结算单金蝶应收单号(多张取第一张,拼接规则联调确认) | 环节一回写的「金蝶单据编号」 | + | systemsource | 系统来源 | 否 | 暂不传(联调校准项) | — | + + 分录(sim_original_bill_item,发票张内商品行平铺): + + | 接口字段 | 含义 | 必填 | 来源 | + | --- | --- | --- | --- | + | rowtype / benchmark | 行性质/基准类型 | 是 | 固定 2(商品行)/ 0(数量基准)(rowtype 联调校准) | + | goodsname / goodssimplename | 货物名称/商品和服务分类 | 是 | 行 goodsName(货物或服务简称)/ goodsCategory(分类名称) | + | taxratecodeid_number | 税收分类编码.编码 | 是 | 行快照 tax_classification_code(开票项目主数据) | + | num / unit | 数量/计量单位 | 是 | 行数量(≤0 兜底 1)/ 单位(空兜底「个」) | + | taxunitprice / taxamount | 含税单价/含税金额 | 是 | 行价税合计÷数量、行价税合计 | + | amount / unitprice / tax | 不含税金额/单价/税额 | 是 | 行不含税金额、不含税单价、税额 | + | taxrate | 税率 | 是 | 行税率 0-100 转小数字符串(13→"0.13") | + | awrp_entry_code | 来源分录标识(候选键) | 是 | 商品行 id(雪花 id 唯一) | + | awrp_src_type | 分录外部单据类型 | 是 | 配置 thirdParty.bfi.invoiceApply.srcType,默认 0KzXs(有默认值,一般无需配置) | + +6. 回写落库:`result[0].number` → `kingdeeBillNo` + `kingdeeStatus=synced` + `syncedTime`,记录操作历史「同步金蝶」。 + +### 4.3 状态机 + +``` +kingdeeStatus:unsynced ──A303-1成功──> synced ──A303-5查到发票──> invoiced(开票完成,终态) + └──推送异常──> failed ──人工重推(A303-4 预检命中→幂等回填)──> synced +定时③只轮询 synced(failed 无真实金蝶单号,A303-5 无查询入口) +``` + +--- + +## 5. 环节三③:开票结果定时回写(A303-5) + +代码入口:`InvoiceApplicationServiceImpl.syncKingdeeInvoiceResult`,手动端点 `POST /invoice-application/sync-kingdee-invoice-result`。 + +1. 待回写集合:approved + synced + 金蝶单号非空,按 `kingdeeBillNo`(金蝶开票申请单号=serialNo)去重归并。 +2. **销方税号解析(与 A303-1 同源)**:逐单按开票单位查客商档案统一社会信用代码,**按税号分组**后组内 ≤50/批逗号拼接调 A303-5;解析不到(开票单位为空/客商档案未维护信用代码)的单跳过并 warn 计数,不阻断整轮。 +3. 返回为票易通原文 `data[]{billNo, invoiceList[]}`:按 billNo 归并回申请单,逐张宽松提取(候选键:发票号 invoiceNo/invoice_no/invoiceNumber;开票日期、价税合计同理),**原文整体 JSON 留档 `raw_json`**;字段名联调校准后收敛为固定映射。 +4. 落 `blade_invoice_application_invoice`:按(invoice_application_id, invoice_no)唯一键幂等,已存在跳过;该单有新增发票 → `kingdeeStatus=invoiced`(开票完成,前端详情展示发票列表)。 +5. 开票结果**不反写结算单**;结算单开票状态仍由申请单分摊驱动。 +6. 汇总文案:待查 X 笔(销方税号无法解析跳过 N 笔),回写发票 Y 张(开票完成 M 笔,失败批次 K 个)。 + +任务调度:`PaymentKingdeeResultTask`(`thirdParty.bfi.payResult.cron` 默认每 10 分钟,总开关 `thirdParty.bfi.payResult.syncEnabled`,三子流程共用;任一子流程异常仅记日志不中断整轮)。 + +--- + +## 6. 防重键与幂等体系汇总 + +| 单据/对象 | 防重键/幂等依据 | 使用接口 | +| --- | --- | --- | +| 财务应收单 | awrp_src_sys=WLTMS + awrp_src_code=正式结算单号_结算明细单据号拼接 | A302-2 / A302-4(预检+定时勾稽+开票前置校验) | +| 应收单分录 | awrp_entry_code=应收货物费用行 id | A302-2 | +| 开票申请单 | billno=开票申请单号 applicationNo | A303-1 / A303-4(预检)/ A303-5(serialNo) | +| 开票申请分录 | awrp_entry_code=商品行 id | A303-1 | +| 金蝶发票结果 | 唯一键(invoice_application_id, invoice_no) | A303-5 落库 | + +断点恢复场景:应收/开票推送成功但本地回滚 → 重推时 A302-4/A303-4 命中幂等回填;failed 应收单 → 定时勾稽自动纠偏;金蝶开票处理中 → ③每轮空查保持 synced,直到开出票。 + +--- + +## 7. 数据前提与上线操作 + +| # | 事项 | 说明 | +| --- | --- | --- | +| 1 | 建表/变更 SQL | `doc/sql/transport/blade_invoice_application_invoice_20260920.sql`(发票结果表);`blade_invoice_application_line_tax_code_20260921.sql`(商品行税收分类编码快照列,可重复执行) | +| 2 | Nacos 配置 | 仅需已有 `thirdParty.bfi.srcSys`(默认 WLTMS)与任务开关/`cron`;`invoiceApply.salerName/salerTaxNo/taxratecode` **已取消**,`invoiceApply.srcType` 有默认值无需配置 | +| 3 | 客商档案 | 我方开票单位(结算单收款方)与受票方客户均须入客商档案:我方须维护**统一社会信用代码**(销方税号),受票方须维护**客商编码**;受票方另需维护客商开票信息(税号、邮箱等) | +| 4 | 主数据 | 开票项目须维护税收分类编码(新增开票内容选择即快照);货物类型档案货物编码;部门编码 | +| 5 | 联调校准项 | A302-2:单据类型/款项性质 1001/CASH/src_type 文案、物料 SP0324+计量 TON+项目 999 临时值(`pushArBill` 调试块);A303-1:rowtype、systemsource、多结算单上游单号拼接;A303-5:原文字段名收敛 | +| 6 | 联调顺序 | 应收结算单同步(①)→ 开票申请同步(②)→ 手动触发 `sync-kingdee-ar-result` / `sync-kingdee-invoice-result` 验证回写 | + +--- + +## 8. 关键代码索引 + +| 环节 | 代码位置(blade-service/blade-transport) | +| --- | --- | +| 环节一推送 | `service/impl/FormalSettlementServiceImpl.syncKingdee` + `support/KingdeeArBillSyncSupport`(pushArBill / arBillSrcCode / queryArBillExistNos / resolveArEntryGroups),档案取数复用 `support/KingdeeApBillSyncSupport` | +| 环节二推送 | `service/impl/InvoiceApplicationServiceImpl.syncKingdee / buildInvoiceApplyEntry` | +| 环节三②/③ | `FormalSettlementServiceImpl.syncKingdeeArResult` / `InvoiceApplicationServiceImpl.syncKingdeeInvoiceResult`,编排 `task/PaymentKingdeeResultTask` | +| 常量/配置 | `constant/KingdeeBfiConstant`(AR 单据类型映射、1001、CASH、TMS财务应收单、ar_finarbill、10xdp/08xdp、invoiced)、`config/BfiProperties`(thirdParty.bfi.*) | +| 手动端点 | `POST /formal-settlement/sync-kingdee-ar-result`、`POST /invoice-application/sync-kingdee-invoice-result`(网关按服务名路由至 blade-transport) | +| 前端 | 正式结算单页面(同步金蝶按钮)、`views/payment/invoice-application-form.vue`(新增开票申请:选结算单带出销方、开票内容快照税收分类编码) |