diff --git a/doc/docs/业务流程/2026-11-03-金蝶仓储费用独立分录与仓储物料映射设计.md b/doc/docs/业务流程/2026-11-03-金蝶仓储费用独立分录与仓储物料映射设计.md new file mode 100644 index 00000000..767350d4 --- /dev/null +++ b/doc/docs/业务流程/2026-11-03-金蝶仓储费用独立分录与仓储物料映射设计.md @@ -0,0 +1,130 @@ +# 金蝶仓储费用独立分录与仓储物料映射设计(修订版) + +> 日期:2026-11-03(修订:去掉名称关键字兜底;明确仅字典+Nacos 配置不生效,需最小代码改动) +> 范围:财务应付单(A102-2)、财务应收单(A302-2)、付款申请(A103-1)分录拆分口径(三方共用 `resolveEntryGroups`,一处改动三方生效) +> 关联文档:`2026-10-13-金蝶应付应收收票单明细分录拆分与物料映射逻辑梳理.md` + +--- + +## 1. 背景与现状问题 + +当前分录拆分维度为 **货物 → 费用大类(装卸/运输)→ 税率**,费用大类只有二分类: +- 装卸大类:费用项档案分类编码 = A10 +- 运输大类:**其余全部**(含仓储等) + +| # | 场景 | 现状行为 | 问题 | +| --- | --- | --- | --- | +| 1 | 结算明细行的仓储费用项(如「仓储堆存保管费」,档案分类 A02) | 落入「运输费」组,映射运输物料(XDWL11010001 等),数量取运单运输量 | 物料错误、数量口径错误 | +| 2 | 结算合计手动添加的仓储费用 | 非运费类统一映射装卸物料 XDWL11002004 | 物料错误 | + +数据事实(本地库 transport):业务字典 `fee_category` 无「仓储」编码(现有 A01/A02/A03/A04/A09/A10);档案「仓储堆存保管费」挂在 A02。 + +--- + +## 2. 为什么仅加字典 + Nacos 配置不生效(结论先行) + +代码里仓储分支不存在,新增的字典编码与配置项没有消费方: + +| # | 证据(`KingdeeApBillSyncSupport` / `BfiProperties`) | 后果 | +| --- | --- | --- | +| 1 | `BfiProperties.Material` 只有 `domestic`/`international`/`loading` 三个属性(BfiProperties.java L211-233),**无 `warehouse` 字段**;Spring 对未知配置项静默忽略 | Nacos 加 `material.warehouse` 为死配置,不报错也不生效 | +| 2 | `splitFeeItems` 大类判定仅 `isLoadingCategory`(A10)二分类(L682-712),仓储费用项仍落入运输费组 | 明细行仓储费照旧映射运输物料、数量取运输量 | +| 3 | `resolveMaterialNumber` 仅 装卸/国际/国内 三分支(L573-599);手动合计行非运费类固定走 `loading`(L546-547) | 手动仓储费照旧映射装卸物料 XDWL11002004 | +| 4 | 字典编码 A01/A10 为硬编码常量(L104-106),档案里的 A05 无代码比较它 | 字典加了也没有判定逻辑消费 | + +因此:**字典 + Nacos 是必要的数据/配置部分,但必须配合最小代码改动**(约 6 处、20 行以内,见第 5 节)。 + +--- + +## 3. 需求口径 + +1. **仓储大类费用**:独立成组生成一行明细;同大类税率不同按税率拆多行 +2. **结算合计手动添加费用**(manual_flag=1):每行单独一条分录(现状保留,仅物料映射修正) +3. **一车多货**:按货物拆分多行(现状保留,仓储同样适用) +4. **仓储物料编码**:`XDWL11002001`(Nacos `thirdparty.bfi.material.warehouse` 可覆盖) + +--- + +## 4. 目标方案 + +### 4.1 大类判定(无关键字兜底,纯数据驱动) + +- 费用项档案 `blade_fee_item.fee_category = A05` → 仓储大类(新增字典) +- **不做名称关键字兜底**:档案未配 A05 的费用项照旧走现有分类(运输/装卸),分类准确性由档案维护负责 +- 代码侧仅新增常量 `FEE_CATEGORY_WAREHOUSE = "A05"` 与档案编码比较 + +### 4.2 拆分与合并(`splitFeeItems`) + +- 分组键 `大类@税率` 扩为三态:`运输费@税率` / `仓储@税率` / `装卸@税率` +- 税率来源不变(合同计费方案),未配置归该大类无税率组;残差仍并入运输费无税率组 +- 同货物行内同大类同税率合并一行、不同税率拆行 —— 满足需求 1 +- 分录标识:`单据号-货物序号-仓储[-税率]` +- `FeeLineSplit.boolean loading` 升级为三态枚举 `FeeCategory{FREIGHT, WAREHOUSE, LOADING}`,保留 `isLoading()` 便捷方法 + +### 4.3 分录字段口径 + +| 分录字段 | 运输大类行 | 仓储大类行(新增) | 装卸大类行 | 结算合计手动费用行 | +| --- | --- | --- | --- | --- | +| 物料编码 | 国际/国内按运输方式映射 | **`material.warehouse`(XDWL11002001)** | `material.loading` | 运费类→同运输行;仓储类(档案A05)→`material.warehouse`;其余→`material.loading` | +| 数量 | 运单运输量(兜底1) | **1**(同装卸) | 1 | 1 | +| 运输货物/车号 | 货物名/车号 | 货物名/车号 | 同左 | 不传 | +| 不含税金额 | 仅运输类首行携带 | 不传(同装卸) | 不传 | 不传 | +| 分录标识 | 单据号-货物序号-运输费[-税率] | 单据号-货物序号-仓储[-税率] | 单据号-货物序号-装卸[-税率] | 结算单号-费用类型[-费用项] | + +收票单(A303-2)按票行生成、不经 `splitFeeItems`,不受影响。 + +--- + +## 5. 改动清单 + +| # | 类型 | 位置 | 内容 | +| --- | --- | --- | --- | +| 1 | SQL | `doc/sql/transport/lqj/temp/`(可重复执行) | 字典 `fee_category` 新增 A05 仓储(见第 6 节) | +| 2 | SQL | 同上 | 档案修正:仓储类费用项 A02→A05(无兜底后此步为分类生效前提) | +| 3 | Nacos | `doc/nacos/blade-dev.yaml`、`doc/nacos/third-party-api.yaml` | `material.warehouse: XDWL11002001` | +| 4 | 代码 | `BfiProperties.Material` | +1 字段 `warehouse = "XDWL11002001"` | +| 5 | 代码 | `KingdeeApBillSyncSupport` | +常量 `FEE_CATEGORY_WAREHOUSE="A05"`;`splitFeeItems`/`splitGroupKey` 三分类;`appendCargoLines` 仓储行物料取 `warehouse`、数量 1;`appendSummaryEntry` 仓储判定映射仓储物料;`resolveMaterialNumber` +仓储分支(参数 boolean→大类枚举) | +| 6 | 代码 | `KingdeeEntrySplitTest` | 追加仓储用例(第 7 节) | + +> 第 4、5 项为代码不可省的最小集;缺任一项则 XDWL11002001 永远不会被推送(见第 2 节)。 + +--- + +## 6. 数据脚本(可重复执行) + +```sql +-- 1. 业务字典 fee_category 新增「仓储 A05」 +INSERT INTO blade_dict_biz (id, tenant_id, parent_id, code, dict_key, dict_value, sort, remark, is_sealed, status, is_deleted) +SELECT 2084286398513100005, '000000', -1, 'fee_category', 'A05', '仓储', 1, '金蝶分录仓储大类', 0, 1, 0 +FROM DUAL +WHERE NOT EXISTS (SELECT 1 FROM (SELECT 1 FROM blade_dict_biz WHERE code = 'fee_category' AND dict_key = 'A05' AND is_deleted = 0) t); + +-- 2. 费用项档案:仓储类费用项归入 A05(无兜底方案下,分类生效的唯一数据来源) +UPDATE blade_fee_item +SET fee_category = 'A05' +WHERE is_deleted = 0 + AND fee_category <> 'A05' + AND (name LIKE '%仓储%' OR name LIKE '%堆存%' OR name LIKE '%保管%'); +``` + +> 字典 id 为手工雪花段,落地前校验不冲突;`blade_fee_item` 在 system 与 transport 两库均有,按实际部署库执行。 + +--- + +## 7. 测试用例(`KingdeeEntrySplitTest` 追加) + +| 用例 | 输入要点 | 断言 | +| --- | --- | --- | +| 仓储同税率合并 | 同货物行:档案 A05 的仓储费 300 + 堆存费 200,税率 6% | 1 行仓储组金额 500 | +| 仓储不同税率拆行 | 仓储费 300@6% + 保管费 200@9%(均档案 A05) | 2 行,后缀 -6 / -9 | +| 仓储不混入运输组 | 运输费 1000@9% + 仓储费(档案A05) 500@6% | 2 行:运输组 1000、仓储组 500 | +| 档案未配 A05 不兜底 | 「仓储服务费」档案 A02 | 归运输组(无兜底,纯档案驱动) | +| 残差勾稽不变 | 运输费 2000@9% + 仓储费 500,结算金额 2520 | 残差 20 仍入运输无税率组 | + +--- + +## 8. 待确认项 + +1. **仓储字典编码**:建议 A05(A05~A08 空缺),业务确认后定稿 +2. **仓储行数量**:默认固定 1(同装卸);如需按吨/方计量另行明确 +3. **档案批量 UPDATE**:按名称关键字一次性归仓,执行前需核对命中清单(含「保管」字样但非仓储的费用项需剔除)