# 财务与履约口径总纲(2026-10-23 重写版) > 状态:**现行唯一口径**。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。 > 最后校准:2026-10-26 > 代码基线:`dev`(合并 dev1023 + 人工成本完整性修复,迁移到 `111`) > 数据基线:§二/§七 的数字来自 2026-10-22 生产 dump 的**历史快照**,只用于理解历史缺口,**不再作为上线验收依据**; > 上线初始化与验收以 [财务上线初始化与验收-2026-10-26.md](财务上线初始化与验收-2026-10-26.md) 为准。 > 适用范围:订单接入、履约判定、商品归一、成本快照、销售事实、人工录单、备用金、后续总账与对账。 --- ## 〇、八条硬口径 1. **电子凭证不是履约方式,也不是成本类型。** 快手电子凭证只是核销码/兑换码,抖音 DYC 券(`redeem_certificates`)同理;一个有核销码的 91/快手或抖音订单,仍可能走自动发货、接单平台或人工。 2. **旧平台通道已经下线。** `kuaishou_ct_assisted`、`kuaishou_feifei` 只保留历史数据,报表中统一标记为 `legacy_platform`,不得混入人工。 3. **人工的定义只有一个:** 已支付订单项,没有工单、没有自动发货任务、没有历史平台任务,即为人工;有没有核销码不影响判定。 4. **人工成本逐单、按实际金额。** 不按小时、不按固定单量、不按固定单价;必须从客服备用金账户扣款,客服只能使用本人账户。 5. **人工成本只走「人工录单 → 扣备用金 → 写成本快照」一条路。** 没有登记单、没有审批、没有报销环节,录入即计入毛利。 6. **历史人工支出找不回来,直接放弃。** 历史缺口统一标记为"历史未采集",不补录、不做样本回填;只保证上线后的新单不丢。 7. **上线后人工账单必须 100% 可监控。** 每一笔人工单必须有处理人、实际金额、备用金支出与成本快照;缺失即进入异常队列,不允许关账。 8. **履约方式只有一个真源。** `order_items.fulfillment_mode` 是唯一事实来源,商品表、成本项表、快照表、异常表、销售事实表都不再各自存一份"载体"。 --- ## 一、三个正交维度 系统过去把"怎么交付"和"成本怎么算"混在同一个 `carrier_type` 里,导致同一订单在订单页、成本快照、销售事实中出现不同分类。正确模型拆成三个正交维度: | 维度 | 取值 | 回答的问题 | 存储位置 | | --- | --- | --- | --- | | `fulfillment_mode` | `work_order` / `affiliate_dash` / `legacy_platform` / `manual` | 这单最终由谁执行 | `order_items.fulfillment_mode`(唯一真源) | | `voucher_state` | 无 / 已发码 / 已核销 / 已销毁 | 有没有核销码、走到哪一步 | 订单读取时单独计算 | | `cost_model` | `affiliate_purchase` / `work_order_reward` / `manual_actual` / `platform_fee` / `payment_fee` / `after_sales_loss` / `other` | 这单成本怎么算 | `cost_items.cost_model` | 硬规则: - `voucher_state` **不参与** `fulfillment_mode` 判定; - `fulfillment_mode` 必须在履约决策时写入 `order_items`,不能每次查询临时推断; - 成本快照、销售事实、后台订单页只读同一份 `fulfillment_mode`; - 一个订单项只能有一个 `fulfillment_mode`,核销码可以有多个状态事件; - **商品来源不由履约方式推导**:商品的来源由 `canonical_sku` 前缀表达(`aff:` / `tpl:` / `wo:` / `man:`),不能反推履约方式。 --- ## 二、历史履约构成(2026-10-22 dump,仅作历史基线) > 本节数字是 2026-10-22 的历史快照。当前库只跑了近 30 天增量,**不要拿本节数字做上线验收**;上线验收用新手册的 SQL。 | 履约方式 | 订单项 | 收入 | 当前成本情况 | | --- | ---: | ---: | --- | | 接单平台 `work_order` | 153,148 | ¥680.27 万 | 151,939 条快照,成本 ¥572.95 万;1,209 项缺成本(取消/0 报酬等) | | 自动发货 `affiliate_dash` | 53,204 | ¥627.20 万 | 商品已归一;上游采购成本版本未配置,全部进入 `cost_missing` | | 历史平台 `legacy_platform` | 53,901 | ¥389.12 万 | 已下线通道,历史成本未采集,商品与成本均保持缺口 | | **人工 `manual`** | **115,194** | **¥1,447.01 万** | 商品已归一为 1,195 个 `man:*`;历史实际支出未采集,全部进入 `cost_missing` | | 合计 | 375,447 | ¥3,143.61 万 | 成本 ¥572.95 万,毛利 ¥2,570.66 万 | 平台分布:91kaquan 368,343 项 / ¥3,023.996 万;pdd 5,563 项 / ¥105.051 万;douyin_mashida 1,541 项 / ¥14.560 万(2026-10-12 ~ 2026-10-22)。 > **商品前缀只表示归一来源,不反推履约方式。** `tpl:` 710 个接单模板商品、`man:` 1,195 个人工内部 SKU、`aff:` 49 个自动发货 SKU、`wo:` 63 个工单长尾商品;渠道映射共 4,897 条(接单模板 4,748 + 工单长尾 149),自动发货与人工不写渠道映射。 成本缺口口径:`cost_missing` 共 223,508 项,其中人工 115,194、自动发货 53,204、历史平台 53,901、接单平台 1,209。人工与历史平台属于明确不补录的历史缺口;自动发货需配置上游采购成本版本;工单 1,209 项主要是取消/0 报酬,不属于商品未归一。 异常队列:`product_mapping_missing` 仅剩历史平台 53,901 条,属于已下线通道的预期缺口;工单、自动发货、人工的商品归一均为 0 缺失。 --- ## 三、成本模型规则 | 成本模型 | 适用履约方式 | 成本来源 | 现状 | | --- | --- | --- | --- | | `affiliate_purchase` | 自动发货 | 上游 SKU 固定成本版本 | 49 个商品,本次快照恢复未配置采购成本版本 | | `work_order_reward` | 接单平台 | 模板规则级版本 / 工单实际报酬 | 710 个规则版本;1,209 项取消/0 报酬缺口 | | `manual_actual` | 人工 | 订单页人工录单的实际支出(直接扣备用金) | 已实现,历史未采集 | | `platform_fee` / `payment_fee` / `after_sales_loss` / `other` | 全部履约方式 | 通用成本项 | 未配置 | 硬规则: - 未确认成本时保持 `cost_missing`,**绝不默认记 0**; - 人工成本只走 `sys_manual_service`,由订单页「人工录单」写 `actual` 快照(主体 = `manual_expenses.id`); - 自动发货、接单平台继续使用现有成本版本与规则版本; - 电子凭证不建成本项,也不建"凭证载体"成本; - 计价方式只保留三种:`per_item`(按件)、`per_order`(按单)、`percent_of_revenue`(按收入比例)。**已删除 `per_hour` / `fixed_daily`**——人工成本逐单按实际金额,不按工时; - 历史平台成本单独标记,不混入人工成本。 --- ## 四、上线后人工账单全覆盖规则 ### 4.1 判定 每个已支付订单项按以下顺序判定,命中即停止: ```text 有 work_orders -> work_order 有 affiliate_dash 任务 -> affiliate_dash 有 ct_assisted/feifei -> legacy_platform 其余 -> manual ``` 核销码只写入 `voucher_state`,不改变上述结果。 ### 4.2 人工单必须有的数据 每一笔 `manual` 订单项必须具备: - 处理人(客服/操作人); - 实际付款金额(分); - 费用项目; - 收款方与付款方式; - 备用金账户与备用金支出单(`manual_expenses.id`); - 凭证或备注; - 成本快照(`manual_case` / `actual`)。 录入路径只有一条:订单页「人工录单」→ 扣客服备用金 → 写 `actual` 快照。**没有单独的登记单,也没有审批与报销环节**;钱与成本在同一次操作里落地。 ### 4.3 监控指标(上线后每日检查) | 指标 | 目标 | | --- | ---: | | 已支付订单项无 `fulfillment_mode` | 0 | | 人工订单项无备用金支出单 | 0 | | 人工订单项无成本快照 | 0 | | 人工支出金额与备用金流水不一致 | 0 | | 成本异常 open 中人工项 | 0(历史标记除外) | ### 4.4 关账门槛 - 存在人工单无成本记录:**不允许关账**; - 存在人工单未扣备用金:**不允许关账**; - 存在人工支出与备用金流水金额不一致:**不允许关账**。 --- ## 五、历史数据处理 - 历史人工支出**找不回来,直接放弃**:不补录、不做样本回填、不做均价估算; - 历史人工单在销售事实里保留收入,成本缺口计入 `cost_missing_count`;**不做特殊 stage、不补录**,不计入「上线后监控缺口」; - 历史平台 `legacy_platform` 单独成行,成本缺口同样计入 `cost_missing_count`;`cost_stage` 由快照覆盖情况决定(actual / estimated / partial / missing); - 自动发货、接单平台历史成本不回写、不重算版本,沿用现有快照。 --- ## 六、实施顺序 | 阶段 | 内容 | 状态 | | --- | --- | --- | | P0 | 统一履约事实(`fulfillment_mode`)+ 成本模型正交 | **已完成(本地,未部署生产)** | | P0.5 | 人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」(下线登记/审批/报销) | **已完成(本地,未部署生产)** | | P1 | 人工商品归一(`man:`)+ 上线后人工成本全覆盖 | **已完成(本地,未部署生产)** | | P2 | 退款冲减与异常闭环 | **已完成(手动登记 + 快手平台退款;抖音/拼多多仍手工)** | | P3 | 影子总账与对账 | **已完成 P0(2026-10-26)**:总账/试算平衡/期间关账/手工账单对账;平台自动拉单待接 | P0 的具体迁移、回填、代码清单与验收 SQL 见 [成本中心与履约模型](成本中心与履约模型.md);接口契约见 [成本中心API契约](成本中心API契约.md)。 > 下一步只做 P3,不要同时改总账与平台对接。 --- ## 七、当前实施进度(2026-10-24 更新) | 能力 | 状态 | 证据 | | --- | --- | --- | | 订单页人工录单,按单实际金额 | **已实施** | `74b62f34` | | 人工支出从客服备用金扣款 | **已实施** | `2aeb789e` | | 客服只能使用本人备用金账户 | **已实施** | `a7f60414` | | 备用金账户编号 `FA-<用户名>` | **已实施** | `fac34c84` + 迁移 `107_manual_fulfillment` | | 订单页只展示平台判断 SKU | **已实施** | `c38cd8ca`、`cd9bb141` | | `order_items.fulfillment_mode` 持久化 | **已实施** | 迁移 `105` + 统一决策服务 | | 订单页 / 成本快照 / 销售事实统一读字段 | **已实施** | `admin-order-read-helpers`、`sales-facts-service`、`cost-snapshot-sync-service` | | 本地 316,585 项回填 | **已实施** | `fulfillment:backfill`,四项计数与 §二 一致 | | 成本模型与履约方式正交(去 `carrier_type`) | **已实施(本地库已改造)** | 迁移 `103`–`108`;`cost_items.cost_model` | | 10 个迁移收敛为 5 个 | **已实施** | `103_product_catalog` ~ `107_manual_fulfillment`(后续新增 `108`–`111` 增量) | | 人工确认原子性/防重复 | **已实施(2026-10-26)** | 单事务 + 订单项行锁 + `uq_cost_snapshots_manual_order_item`(迁移 `112`);集成测试覆盖并发/回滚 | | 人工 SKU 配置自动匹配 | **已实施(2026-10-26)** | 保存配置立即回填 + 定时任务 rematch + 确认时按 SKU 兜底匹配 | | 人工缺成本监控 | **已实施(2026-10-26)** | `manual_cost_missing`,从首次人工配置时间起算,确认后自动收口 | | 人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」 | **已实施(本地库已改造)** | 迁移 `104`;登记单与审批报销已下线,口径固化在成本中心迁移 | | 人工商品归一 `man:` | **已实施(本地库已归一)** | `manual-product-normalization-service` + `POST /cost-center/manual-products/normalize`;1,116 个 `man:` 商品、91,606 个人工订单项归一,人工未归一 = 0 | | 人工商品归一后的销售事实 | **已实施** | 94 天分 30 天窗口重算:26,507 行;人工 ¥1,219.20 万收入全部按 `man:` 商品归集 | | 退款冲减、异常闭环 | **已实施(手动登记先行)** | 迁移 `106_order_refunds`;订单页「退款登记」(整单/指定商品、可撤销);销售事实按有效退款冲减;全额退完后自动关闭该订单项成本异常并写 `resolution_note` | | 平台退款 API 接入 | **部分实施** | 快手行业(91)同意退款已接入,落 `source='platform'` + `external_ref`;抖音/拼多多仍手工 | | 影子总账、对账、关账 | **已实施 P0** | 迁移 `112`;销售事实按日凭证 + 手工凭证 + 试算平衡 + 账单导入对账 + 关账/反关账 | > 历史平台仍有 53,901 个订单项(¥389.12 万)未归一,不属于 P1 范围;人工历史成本按既定口径不补录,因此人工毛利当前是“收入已归商品、成本仅新单有”的状态。 > 分支事实:以上实现已合并到 `dev`;迁移 `103`–`111` **未推送远端、未部署生产**。生产口径仍以 `main` 为准;上线前按 [财务上线初始化与验收-2026-10-26.md](财务上线初始化与验收-2026-10-26.md) 初始化资金与人工 SKU。 --- ## 八、上线验收查询 > 以下 SQL 在 `fulfillment_mode` 落地后使用;`DATE '2026-11-01'` 替换为实际上线日。 ```sql -- 1. 履约方式覆盖:四项相加应等于订单项总数 SELECT fulfillment_mode, COUNT(*) FROM order_items GROUP BY fulfillment_mode ORDER BY 2 DESC; -- 2. 上线后超过 24 小时仍未分类:应为 0 SELECT COUNT(*) FROM order_items oi JOIN orders o ON o.id = oi.order_id WHERE oi.fulfillment_mode IS NULL AND COALESCE(o.paid_at, o.created_at) < now() - interval '24 hours' AND COALESCE(o.paid_at, o.created_at) >= DATE '2026-11-01'; -- 3. 人工单无成本:上线后应为 0 SELECT COUNT(*) FROM order_items oi JOIN orders o ON o.id = oi.order_id WHERE oi.fulfillment_mode = 'manual' AND COALESCE(o.paid_at, o.created_at) >= DATE '2026-11-01' AND NOT EXISTS ( SELECT 1 FROM cost_snapshots cs WHERE cs.order_item_id = oi.id AND cs.superseded_at IS NULL ); -- 4. 人工订单项没有资金流水(现金 manual_expenses 或积分 creditEntryId):上线后应为 0 SELECT COUNT(*) FROM order_items oi WHERE oi.fulfillment_mode = 'manual' AND oi.created_at >= DATE '2026-11-01' AND NOT EXISTS (SELECT 1 FROM manual_expenses me WHERE me.order_item_id = oi.id) AND NOT EXISTS ( SELECT 1 FROM cost_snapshots cs WHERE cs.subject_type = 'manual_case' AND cs.order_item_id = oi.id AND cs.superseded_at IS NULL AND cs.metadata_json ? 'creditEntryId' ); -- 5. 现金人工成本快照金额与备用金支出金额不一致:应为 0 -- subject_id 已统一为 order_item_id,资金来源按 order_item_id 关联。 SELECT cs.id, me.expense_no, me.amount_fen, cs.total_amount_fen FROM cost_snapshots cs JOIN manual_expenses me ON me.order_item_id = cs.order_item_id WHERE cs.subject_type = 'manual_case' AND cs.superseded_at IS NULL AND cs.total_amount_fen <> me.amount_fen AND me.created_at >= DATE '2026-11-01'; -- 6. 人工单未归一商品:P1 完成后应为 0 SELECT COUNT(*) FROM order_items oi WHERE oi.fulfillment_mode = 'manual' AND oi.canonical_product_id IS NULL AND oi.created_at >= DATE '2026-11-01'; -- 7. 核销码不得改变履约方式:带快手凭证的 91 人工单仍应为 manual SELECT oi.fulfillment_mode, COUNT(*) FROM order_items oi JOIN orders o ON o.id = oi.order_id WHERE o.provider = '91kaquan' AND EXISTS ( SELECT 1 FROM kuaishou_industry_vouchers v WHERE v.oid = o.platform_order_id ) GROUP BY oi.fulfillment_mode; -- 8. 历史平台不得混入人工:应为 0 SELECT COUNT(*) FROM order_items oi WHERE oi.fulfillment_mode = 'manual' AND EXISTS ( SELECT 1 FROM fulfillment_tasks ft WHERE ft.order_item_id = oi.id AND ft.executor_key IN ('kuaishou_ct_assisted', 'kuaishou_feifei') ); -- 9. 人工成本缺失异常:上线后应随确认履约自动收口 SELECT status, COUNT(*) FROM cost_exceptions WHERE reason = 'manual_cost_missing' GROUP BY status; -- 10. 人工项误挂接单模板商品:P1 完成后应为 0 SELECT COUNT(*) FROM order_items oi JOIN product_catalog pc ON pc.id = oi.canonical_product_id WHERE oi.fulfillment_mode = 'manual' AND pc.canonical_sku LIKE 'tpl:%'; ``` --- ## 九、常见错误(防回归) | # | 错误 | 后果 | 正确做法 | | --- | --- | --- | --- | | 1 | 把"有快手核销码"当成 `voucher_only` | 91 人工单全部从订单页消失 | 核销码只做状态展示 | | 2 | 按 `provider=91kaquan/douyin_mashida` 直接判自动发货 | 人工单被藏进自动发货 | 只看工单/任务/历史任务 | | 3 | 用 `product_catalog` 的来源反推履约方式 | 11,349 个人工项被误认为接单平台 | 只读 `order_items.fulfillment_mode` | | 4 | 在商品/成本项/快照/异常四张表各存一份载体 | 四处口径分叉 | 载体只存 `order_items.fulfillment_mode` | | 5 | 只回填已支付历史单,不做增量兜底 | 新单长期停在 `NULL` | 24 小时定时归集 + 异常队列 | | 6 | 把"人工 91,606"当成"人工全量" | 与 102,955 对不上,报表互相矛盾 | 写清"全量 102,955 / 未归一 91,606" | | 7 | 给人工成本再造一张「登记单 + 审批 + 报销」表 | 两套资金语义混用,客服和财务都要录两遍 | 人工录单直接扣备用金并写快照,`manual_expenses` 是唯一资金台账 | | 8 | 用均价、工时、固定单价补历史人工 | 违背逐单实际金额,毛利失真 | 历史不补录,只做样本区间 | | 9 | 只改订单页,不改成本/销售事实 | 三处口径继续分叉 | 同一字段、同一决策入口 | | 10 | 在旧文档里保留"三块/按小时/公司直付"旧结论 | 下次开发又按旧口径实现 | 旧文只留追溯标记,现行结论只写本文件 | --- ## 十、文档地图与维护规则 | 文档 | 唯一职责 | 状态 | | --- | --- | --- | | **本文件** | 履约/成本/人工口径、实施顺序、验收 SQL | **现行唯一口径** | | [成本中心与履约模型.md](成本中心与履约模型.md) | 商品、成本版本、快照、销售事实的数据模型与迁移 | 现行 | | [成本中心API契约.md](成本中心API契约.md) | 成本中心后端/前端接口字段与模块边界 | 现行 | | [人工支出与备用金.md](人工支出与备用金.md) | 人工录单、客服备用金台账与权限 | 现行(P0 已实施) | | [财务系统集成方案.md](财务系统集成方案.md) | `finance_*` 复式总账层(P0 已实施) | 现行方案 | | [README.md](README.md) | 文档索引、分支与进度 | 现行入口 | 维护规则: 1. 履约分类只在本文定义;其他文档只链接,不复制判定 SQL。 2. 任何数字必须注明口径:全量 / 已归一 / 未归一 / 上线后。 3. 每次迁移后更新本文的代码基线、数据基线、§七 进度和 §二 数字。 4. 新文档必须登记到 `README.md`;过期文档移入 `docs/archive/`,不得在现行文档里保留冲突结论。 5. 不再使用"三块履约"作为分类名。 6. 代码评审必须检查:订单页、`sales-facts-service`、`cost-snapshot-sync-service` 是否都读同一字段。