Files
order_site/docs/财务与履约口径总纲-2026-10-23.md
T
yml2213 a78cc1f984 feat(finance): P1 人工商品归一,按内部 SKU 生成 man 商品并回填
- 新服务 manual-product-normalization-service:skuNick → outSkuSn → sku_code 兜底,按内部 SKU 生成 man:<sku> 商品
- 订单项按 id 分批(2 万/批)回填,避免 15s 语句超时;同步补齐人工成本快照的商品维度
- 新增 POST /cost-center/manual-products/normalize(支持 dryRun/日期窗口),成本中心「标准商品」加「归一人工商品」按钮
- 每日 cost_finance_sync 接入最近 N 天的增量归一
- 渠道映射未归一队列增加人工单提示:人工单不能用平台 sku_code 建映射
- 文档同步:P1 标记完成,补充归一接口契约与销售事实重算结果
2026-10-24 16:54:55 +08:00

17 KiB
Raw Blame History

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

状态:现行唯一口径。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。 最后校准:2026-10-23 代码基线:dev @ 43f402cf + 迁移 103–109(已合并到 dev,未推送、未部署生产) 数据基线:本地生产快照克隆(数据截至 2026-10-14),已按新 schema 原地改造 适用范围:订单接入、履约判定、商品归一、成本快照、销售事实、人工录单、备用金、后续总账与对账。


〇、八条硬口径

  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-07-10 ~ 2026-10-14)

履约方式 订单项 收入 当前成本情况
接单平台 work_order 118,053 ¥533.57 万 基本完整,缺 966 项
自动发货 affiliate_dash 41,676 ¥508.97 万 完整
历史平台 legacy_platform 53,901 ¥389.12 万 历史成本未采集,单列
人工 manual 102,955 ¥1,219.20 万 仅 1 条 ¥320,历史基本未采集
合计 316,585 ¥2,650.87 万 —

数字口径必须先分清"全量"和"未归一"。 人工全量是 102,955 项;其中 11,349 项已有 canonical_product_id,但这 11,349 项全部指向 tpl:* 接单模板商品,只说明 SKU 命中过模板,不代表它们有工单或走过接单平台。真正未归一的是 91,606 项。历史平台 53,901 项全部未归一。两者相加才是报表里的"未归一 145,507 项"。

人工 102,955 项的来源与核销码事实:

来源 订单项 收入 核销码事实
91/快手 100,618 ¥1,177.45 万 全部有快手凭证记录:CONSUMED 87,310 / DESTROYED 13,247 / UNUSED 61
拼多多 2,335 ¥41.74 万 无快手凭证
抖音 2 ¥62.90 无快手凭证

这张表直接说明:91 人工单全部有核销码,但仍然是人工。 把"有核销码"当成 voucher_only 或自动发货,是订单页漏掉人工块的直接原因。

归一潜力:人工 102,955 项中 100,529 项有 spec_json.skuId(97.6%),共 3,561 个 SKU;剩余 2,426 项主要来自拼多多,可用 sku_code/sku_name 建立映射。人工商品归一后,商品编码统一用 man:<skuId>;不得因为复用了 tpl:* 商品就把履约方式改成 work_order。

成本现状:人工 102,955 项里只有 1 条实际成本快照(¥320),其余 102,954 项没有成本;历史平台 53,901 项全部没有成本。这两块是当前毛利失真的主要原因,但历史人工不补录,只做样本校准。


三、成本模型规则

成本模型 适用履约方式 成本来源 现状
affiliate_purchase 自动发货 上游 SKU 固定成本版本 38 个商品,全部有 active 版本
work_order_reward 接单平台 模板规则级版本 / 工单实际报酬 662 个规则版本,966 项异常
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 关账门槛

  • 存在人工单无成本记录:不允许关账;
  • 存在人工单未扣备用金:不允许关账;
  • 存在人工支出与备用金流水金额不一致:不允许关账。

五、历史数据处理

  • 历史人工支出找不回来,直接放弃:不补录、不做样本回填、不做均价估算;
  • 历史人工单在销售事实中保留收入,成本标记为 historical_missing,不计入"上线后监控缺口";
  • 历史平台 legacy_platform 单独成行,成本未采集的标记 legacy_missing;
  • 自动发货、接单平台历史成本不回写、不重算版本,沿用现有快照。

六、实施顺序

阶段 内容 状态
P0 统一履约事实(fulfillment_mode)+ 成本模型正交 已完成(本地,未部署生产)
P0.5 人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」(下线登记/审批/报销) 已完成(本地,未部署生产)
P1 人工商品归一(man:<skuId>)+ 上线后人工成本全覆盖 已完成(本地,未部署生产)
P2 退款冲减与异常闭环 未实施
P3 影子总账与对账 未实施

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

下一步只做 P2,不要同时改总账。


七、当前实施进度(2026-10-24 更新)

能力 状态 证据
订单页人工录单,按单实际金额 已实施 74b62f34
人工支出从客服备用金扣款 已实施 2aeb789e
客服只能使用本人备用金账户 已实施 a7f60414
备用金账户编号 FA-<用户名> 已实施 fac34c84 + 迁移 106_fund_account_no_readable
订单页只展示平台判断 SKU 已实施 c38cd8ca、cd9bb141
order_items.fulfillment_mode 持久化 已实施 迁移 107 + 统一决策服务
订单页 / 成本快照 / 销售事实统一读字段 已实施 admin-order-read-helpers、sales-facts-service、cost-snapshot-sync-service
本地 316,585 项回填 已实施 fulfillment:backfill,四项计数与 §二 一致
成本模型与履约方式正交(去 carrier_type) 已实施(本地库已改造) 迁移 103–107 重写;cost_items.cost_model
12 个迁移合并为 5 个 已实施 103_product_catalog ~ 107_order_item_fulfillment_mode
人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」 已实施(本地库已改造) 迁移 108_drop_manual_expense_records;下线登记单与审批报销
人工商品归一 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: 商品归集
退款冲减、异常闭环 未实施 §六 P2
影子总账、对账、关账 未实施 §六 P3

历史平台仍有 53,901 个订单项(¥389.12 万)未归一,不属于 P1 范围;人工历史成本按既定口径不补录,因此人工毛利当前是“收入已归商品、成本仅新单有”的状态。 分支事实:以上实现已快进合并到 dev;迁移 103–111 未推送远端、未部署生产。生产口径仍以 main 为准。


八、上线验收查询

以下 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. 人工订单项没有备用金支出单:上线后应为 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
  );

-- 5. 人工成本快照金额与备用金支出金额不一致:应为 0
SELECT cs.id, me.expense_no, me.amount_fen, cs.total_amount_fen
FROM cost_snapshots cs
JOIN manual_expenses me ON me.id = cs.subject_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. 人工项误挂接单模板商品: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_* 复式总账层(未实施) 方案
README.md 文档索引、分支与进度 现行入口

维护规则:

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