Files
order_site/docs/财务与履约口径总纲-2026-10-23.md
yml2213 0de0fd7b58 chore(db): 将 108–115 补丁迁移收敛为 108–111
- 原 108/111/112/113/114 合并为 108_finance_cost_integrity:
  成本异常归档、人工成本完整性、影子总账、商品页/模板查询索引
- 原 115 注释迁移改为 111_work_order_share_cancel_reason_comment
- 新增 reconcile-migration-history.sql,用于已跑旧 112–115 的库
  重建 schema_migrations 顺序,不重复执行 DDL
- 已验证:001→111、101→111 两条升级路径;
  旧 115 与新 111 的 pg_dump schema 完全一致
- 测试/文档迁移编号同步到 103–111

验证:backend check 853(849 pass / 4 skip);frontend check 172 + build。
2026-10-27 12:37:21 +08:00

19 KiB
Raw Permalink Blame History

财务与履约口径总纲(2026-10-23 重写版)

状态:现行唯一口径。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。 最后校准:2026-10-26 代码基线:dev(合并 dev1023 + 人工成本完整性修复,迁移到 111) 数据基线:§二/§七 的数字来自 2026-10-22 生产 dump 的历史快照,只用于理解历史缺口,不再作为上线验收依据; 上线初始化与验收以 财务上线初始化与验收-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 判定

每个已支付订单项按以下顺序判定,命中即停止:

有 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:<skuId>)+ 上线后人工成本全覆盖 已完成(本地,未部署生产)
P2 退款冲减与异常闭环 已完成(手动登记 + 快手平台退款;抖音/拼多多仍手工)
P3 影子总账与对账 已完成 P0(2026-10-26):总账/试算平衡/期间关账/手工账单对账;平台自动拉单待接

P0 的具体迁移、回填、代码清单与验收 SQL 见 成本中心与履约模型;接口契约见 成本中心API契约。

下一步只做 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:<skuId> 已实施(本地库已归一) 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 初始化资金与人工 SKU。


八、上线验收查询

以下 SQL 在 fulfillment_mode 落地后使用;DATE '2026-11-01' 替换为实际上线日。

-- 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 商品、成本版本、快照、销售事实的数据模型与迁移 现行
成本中心API契约.md 成本中心后端/前端接口字段与模块边界 现行
人工支出与备用金.md 人工录单、客服备用金台账与权限 现行(P0 已实施)
财务系统集成方案.md finance_* 复式总账层(P0 已实施) 现行方案
README.md 文档索引、分支与进度 现行入口

维护规则:

  1. 履约分类只在本文定义;其他文档只链接,不复制判定 SQL。
  2. 任何数字必须注明口径:全量 / 已归一 / 未归一 / 上线后。
  3. 每次迁移后更新本文的代码基线、数据基线、§七 进度和 §二 数字。
  4. 新文档必须登记到 README.md;过期文档移入 docs/archive/,不得在现行文档里保留冲突结论。
  5. 不再使用"三块履约"作为分类名。
  6. 代码评审必须检查:订单页、sales-facts-service、cost-snapshot-sync-service 是否都读同一字段。