Files
order_site/docs/财务与履约口径总纲-2026-10-23.md
T
yml2213 0099597b0d docs: 迁移编号同步为 103–107 并校准分支状态
- 全量替换文档里的旧迁移编号:106→107、107→105、108/109/111→104、112→106、110→107;
- 删除 113/114 的残留引用(这两个编号从未落地);
- 分支状态校准为 `dev @ 465a9f65 + 迁移收敛`,本地库按「清 103–112 记录后重跑」对齐;
- 成本中心与履约模型 §一 重写迁移基线表,§八 改为迁移基线与本地库同步;
- 归档原地改造脚本的索引补登记。
2026-10-25 11:38:18 +08:00

305 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 财务与履约口径总纲(2026-10-23 重写版)
> 状态:**现行唯一口径**。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。
> 最后校准:2026-10-24
> 代码基线:`dev @ 790de676 + 本地未提交修复`(**未推送、未部署生产**)
> 数据基线:生产 dump `order_site-20261022-092601.dump`(2026-07-10 ~ 2026-10-22);本地已升级到迁移 107,并完成履约方式、商品、成本快照与销售事实初始化
> 适用范围:订单接入、履约判定、商品归一、成本快照、销售事实、人工录单、备用金、后续总账与对账。
---
## 〇、八条硬口径
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-22)
| 履约方式 | 订单项 | 收入 | 当前成本情况 |
| --- | ---: | ---: | --- |
| 接单平台 `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 关账门槛
- 存在人工单无成本记录:**不允许关账**;
- 存在人工单未扣备用金:**不允许关账**;
- 存在人工支出与备用金流水金额不一致:**不允许关账**。
---
## 五、历史数据处理
- 历史人工支出**找不回来,直接放弃**:不补录、不做样本回填、不做均价估算;
- 历史人工单在销售事实中保留收入,成本标记为 `historical_missing`,不计入"上线后监控缺口";
- 历史平台 `legacy_platform` 单独成行,成本未采集的标记 `legacy_missing`;
- 自动发货、接单平台历史成本不回写、不重算版本,沿用现有快照。
---
## 六、实施顺序
| 阶段 | 内容 | 状态 |
| --- | --- | --- |
| P0 | 统一履约事实(`fulfillment_mode`)+ 成本模型正交 | **已完成(本地,未部署生产)** |
| P0.5 | 人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」(下线登记/审批/报销) | **已完成(本地,未部署生产)** |
| P1 | 人工商品归一(`man:<skuId>`)+ 上线后人工成本全覆盖 | **已完成(本地,未部署生产)** |
| P2 | 退款冲减与异常闭环 | **已完成(手动登记先行,平台退款 API 待接入)** |
| P3 | 影子总账与对账 | 未实施 |
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`–`107` 重写;`cost_items.cost_model` |
| 10 个迁移收敛为 5 个 | **已实施** | `103_product_catalog` ~ `107_manual_fulfillment` |
| 人工成本链路收敛为「人工录单 → 扣备用金 → 写快照」 | **已实施(本地库已改造)** | 迁移 `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 接入 | **未实施** | 预留 `order_refunds.source='api'/'platform'` 与 `external_ref` |
| 影子总账、对账、关账 | **未实施** | §六 P3 |
> 历史平台仍有 53,901 个订单项(¥389.12 万)未归一,不属于 P1 范围;人工历史成本按既定口径不补录,因此人工毛利当前是“收入已归商品、成本仅新单有”的状态。
> 分支事实:以上实现已快进合并到 `dev`;迁移 `103`–`107` **未推送远端、未部署生产**。生产口径仍以 `main` 为准。
---
## 八、上线验收查询
> 以下 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. 人工订单项没有备用金支出单:上线后应为 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](成本中心与履约模型.md) | 商品、成本版本、快照、销售事实的数据模型与迁移 | 现行 |
| [成本中心API契约.md](成本中心API契约.md) | 成本中心后端/前端接口字段与模块边界 | 现行 |
| [人工支出与备用金.md](人工支出与备用金.md) | 人工录单、客服备用金台账与权限 | 现行(P0 已实施) |
| [财务系统集成方案.md](财务系统集成方案.md) | `finance_*` 复式总账层(未实施) | 方案 |
| [README.md](README.md) | 文档索引、分支与进度 | 现行入口 |
维护规则:
1. 履约分类只在本文定义;其他文档只链接,不复制判定 SQL。
2. 任何数字必须注明口径:全量 / 已归一 / 未归一 / 上线后。
3. 每次迁移后更新本文的代码基线、数据基线、§七 进度和 §二 数字。
4. 新文档必须登记到 `README.md`;过期文档移入 `docs/archive/`,不得在现行文档里保留冲突结论。
5. 不再使用"三块履约"作为分类名。
6. 代码评审必须检查:订单页、`sales-facts-service`、`cost-snapshot-sync-service` 是否都读同一字段。