docs: 统一财务与履约口径并新增总纲

This commit is contained in:
yml2213
2026-10-23 13:17:30 +08:00
parent fac34c8420
commit fb80cad695
11 changed files with 798 additions and 118 deletions
+59 -33
View File
@@ -1,9 +1,10 @@
# order_site 文档索引
> 最后校准:2026-10-23,代码基线 `dev @ a7f60414 + 财务 103–113`。
> 本文是 `docs/` 的唯一入口。新增文档必须在上表登记状态;过期文档不再直接改写结论,统一移入 `docs/archive/`。
> 最后校准:2026-10-23,代码基线 `dev @ fac34c84 + 迁移 103–113`。
> 本文是 `docs/` 的唯一入口。**财务与履约口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准**;接手改造先读 [财务履约改造交接总结-2026-10-23.md](财务履约改造交接总结-2026-10-23.md),本文只做索引与速查。
> 新增文档必须在下表登记状态;过期文档不再直接改写结论,统一移入 `docs/archive/`。
## 一、先记住五条口径
## 一、先记住六条口径
### 1. 来源只有三个,不是四个
@@ -15,14 +16,26 @@
**不要把 91 与快手 send-code 拆成两个来源、两个适配器或两条业务线。** 产品匹配层的唯一来源键是 `kuaishou-91`(`apps/backend/src/services/product-match/types.ts`),适配器是 `adapters/kuaishou-91.ts`。但订单表与成本映射里存储的 `provider` 仍是 `orders.provider = 91kaquan`;成本中心会把 `91 / open_91 / kuaishou-91` 这些别名归一为 `91kaquan`,映射键必须按落库值建。
### 2. 一个订单 = 骨架 + 买家凭证 + 一个执行载体
### 2. 一个订单 = 骨架 + 买家凭证 + 一个履约方式
- 骨架:`orders` + `order_items`
- 买家凭证:快手电子凭证(`kuaishou_industry_vouchers`)或抖音券(`redeem_certificates`)
- 执行载体只有三种:`affiliate_dash` 自动发货、`work_orders` 工单、`manual` 人工客服
- 三者业务上互斥;无自动任务且无工单的已支付订单项就是人工块,必须进入人工 case,不能静默消失
- 履约方式只有四种:`affiliate_dash` 自动发货、`work_orders` 接单平台、`legacy_platform` 已下线历史平台、`manual` 人工
- **电子凭证只是核销码,不参与履约方式判定**;有核销码的订单仍可能是自动发货/接单平台/人工
- 判定顺序固定:工单 → 自动发货任务 → 历史平台任务 → 其余为人工
- 人工块必须进入人工支出与成本快照,不能静默消失
### 3. 成本只在一个地方改
### 3. 履约方式与成本模型必须分开
- `fulfillment_mode` 回答“谁执行”;`cost_model` 回答“成本怎么算”
- 自动发货:`affiliate_purchase`
- 接单平台:`work_order_reward`
- 人工:`manual_actual`(逐单实际金额)
- 历史平台:`legacy_cost`(历史未采集则显式标记)
- 电子凭证不建成本项,也不作为成本类型
- 未确认成本时保持 `cost_missing`,不默认 0
### 4. 成本只在一个地方改
- 商品主数据:`product_catalog`
- 渠道 SKU 映射:`channel_product_mappings`
@@ -30,16 +43,22 @@
- 订单/工单/人工单只保存 `cost_snapshot_id`,历史成本永不回写
- 自动发货、接单平台、人工客服共用同一个成本解析器;匹配不到成本进入 `cost_missing`,不默认 0
### 4. 两个“人工支出”不是一回事
### 5. 两个“人工支出”不是一回事
| 名称 | 业务含义 | 当前状态 |
| --- | --- | --- |
| `manual_expenses` | **客服备用金支出明细**:客服从自己的备用金里充值/采购/赔偿,减少客服余额 | 已实施(迁移 100) |
| `manual_expense_records` | **公司人工支出/垫付登记**:按订单/成本项记录公司实际付出去的钱、付款人、收款人、凭证、审批 | 已实施 P0(迁移 111) |
后续做财务系统时,**不要把 `manual_expenses` 直接改名或复用为 `manual_expense_records`**。两者业务含义、审批链和报表口径不同,应通过 `fund_expense_id` 建立关联。
后续做财务系统时,**不要把 `manual_expenses` 直接改名或复用为 `manual_expense_records`**。两者业务含义、审批链和报表口径不同,必须通过 `fund_expense_id` 关联;新人工支出登记会先扣备用金,再写人工支出记录。
### 5. 文档状态
### 6. 历史人工支出不补录,上线后必须全覆盖
- 历史人工支出只取少量样本做校准,不补录、不阻塞上线;
- 上线后每一笔人工单必须有处理人、实际金额、备用金支出与成本快照;
- 人工单无成本记录、人工支出无 `fund_expense_id`、金额与备用金流水不一致,均不允许关账。
### 7. 文档状态
| 状态 | 含义 |
| --- | --- |
@@ -52,29 +71,32 @@
| 模块 | 状态 | 事实 |
| --- | --- | --- |
| 客服备用金台账 | **已实施 P0** | `fund_accounts` / `fund_entries` / `manual_expenses` / `fund_reconciliations`;后台页面、权限、审计已完成 |
| 客服备用金台账 | **已实施 P0** | 迁移 `100/101/113`;账户、流水、支出、对账;编号 `FA-<用户名>`;客服只能使用本人账户 |
| 后台用户中文显示名 | **已实施** | `admin_users.display_name`,备用金姓名触发器同步;审计、登录记录、任务处理人优先显示中文名 |
| 商品主数据 + 渠道 SKU 映射 | **已实施 P0(dev 分支)** | 迁移 `103`(映射消歧在 `106`);`product_catalog` / `channel_product_mappings`,后台「商品与成本」页支持维护、历史订单项回填与按载体分区 |
| 成本中心 | **已实施 P0(dev 分支)** | 迁移 `104/105/107/108/110/112`;`cost_items` / `cost_versions` / `cost_snapshots` / `cost_exceptions` 支持版本、预览、快照、异常队列、自动发货价格导入;自动发货订单项按履约任务 SKU 直接归一为 `aff:<skuCode>`(不建渠道映射),接单平台按工单模板回填商品并为 662 个模板生成规则级发单金额版本,人工按支出登记写入实际快照 |
| 销售事实 / 订单毛利 | **已实施 P0(dev 分支)** | 迁移 `109`;`sales_daily_facts` 按店铺×日期×标准商品×载体汇总,未归一商品单独成行;后台新增「销售毛利」页,成本缺失会显式告警 |
| 公司人工支出登记 | **已实施 P0(dev 分支)** | 迁移 `111`;`manual_expense_records` / `manual_expense_events`,后台「人工支出」页支持登记、审批、驳回、报销;与客服备用金 `manual_expenses` 通过 `fund_expense_id` 关联 |
| 商品主数据 + 渠道 SKU 映射 | **已实施 P0(dev 分支)** | 745 个标准商品、4,426 条映射;只覆盖自动发货 38 个 + 接单平台 707 个。人工 11,349 项虽已归一但全部挂 `tpl:*` 模板商品,需在 P1 复核;人工/历史平台专用商品尚未建立 |
| 成本中心 | **已实施 P0(dev 分支)** | 迁移 `104/105/107/108/110/112`;38 个商品级版本 + 662 个规则级版本;人工走 `sys_manual_service` 实际快照 |
| 销售事实 / 订单毛利 | **已实施 P0(dev 分支)** | 迁移 `109`;94 天事实收入 ¥2,650.87 万、成本 ¥950.27 万;人工/历史平台分类口径待统一 |
| 公司人工支出登记 | **已实施 P0(dev 分支)** | 迁移 `111`;登记时强制扣客服备用金并写 `fund_expense_id`;客服只能使用本人账户 |
| 履约方式统一 | **未实施** | 订单页、销售事实、成本快照仍各自推断;需要持久化 `fulfillment_mode` |
| 影子总账 | 未实施 | 现有财务页仍是 `UNION ALL` 业务事件拼装,不是复式总账 |
| 平台对账 / 期间关账 | 未实施 | 仅方案 |
| 退款冲减 / 平台对账 / 期间关账 | 未实施 | 仅方案 |
> 分支状态(2026-10-22):商品主数据、渠道映射、成本中心、成本快照运行层、销售事实与公司人工支出登记在 `dev` 分支(原 `feat/cost-center-wip` 已合并),**尚未合并 `main`、未部署生产**;生产口径仍以 `main` 为准。
> 分支状态(2026-10-23):`dev` 领先 `main` 20 个提交,迁移 `103–113` 尚未合并、未部署生产;生产口径仍以 `main` 为准。
> 财务与履约口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。
## 三、统一实施顺序
```text
1. 商品主数据 + 渠道 SKU 映射 已完成 P0
2. 成本中心 + 销售事实(先接收入与标准成本) 已完成 P0(迁移 108/109)
3. 订单毛利 / 店铺日报 已完成 P0(销售毛利页,成本缺失显式告警)
4. 公司人工支出登记(桥接现有客服备用金支出) 已完成 P0(迁移 111)
5. 影子总账(订单、工单、押金、提现、寄卖、售后) 未实施
6. 平台对账、报表、期间关账 未实施
0. 统一履约事实(fulfillment_mode + 成本模型) 未实施,最高优先级
1. 商品主数据 + 渠道 SKU 映射 已完成 P0(仅自动发货/接单平台)
2. 成本中心 + 销售事实 已完成 P0
3. 人工商品归一与上线后人工成本全覆盖 未实施
4. 退款冲减与异常队列 未实施
5. 影子总账(订单、工单、押金、提现、寄卖、售后) 未实施
6. 平台对账、报表、期间关账 未实施
```
**不要跳过 1–3 直接做影子总账。** 没有商品归一、成本版本和订单损益,影子账只能记“总额”,无法回答“哪个店铺、哪个商品、哪条履约链路赚钱”。
**不要跳过第 0 步直接做人工成本或总账。** 没有统一履约事实,人工、电子凭证、历史平台会继续互相串味。第 0 步的迁移、回填、代码清单和验收 SQL 已写到 [总纲 §八](财务与履约口径总纲-2026-10-23.md#八p0-开工清单统一履约事实),按那份清单逐项执行即可。
## 四、当前必须拍板
@@ -84,21 +106,25 @@
| 2 | 收入确认时点 | ✅ 先按 `paid_at` 归属销售日;成本在快照生成后回填 | 利润表与预收账款 |
| 3 | 打手报酬 | ✅ 支出/成本,总额法 | 履约成本 |
| 4 | 退款/仲裁 | ✅ 暂不实现,只预留反向凭证 | 后续冲减 |
| 5 | 人工支出如何入账 | ✅ 以人工支出登记为入口;需与客服备用金支出桥接 | 人工成本与应付垫付人 |
| 6 | 平台服务费/支付通道费 | ⏳ 待定 | 费用科目与对账 |
| 7 | 寄卖货款成本确认时点 | ⏳ 待定 | 存货/采购成本 |
| 8 | 是否需要发票/税务、是否多币种 | ⏳ 待定 | 当前按 CNY、无税务实现 |
| 5 | 人工支出如何入账 | ✅ 逐单实际金额;从客服备用金扣款;客服只能用本人账户 | 人工成本与备用金 |
| 6 | 历史人工支出 | ✅ **不补录**,只取少量样本校准 | 上线范围 |
| 7 | 电子凭证是否作为分类 | ✅ **不作为履约/成本分类**,只是核销码状态 | 报表口径 |
| 8 | 平台服务费/支付通道费 | ⏳ 待定 | 费用科目与对账 |
| 9 | 寄卖货款成本确认时点 | ⏳ 待定 | 存货/采购成本 |
| 10 | 是否需要发票/税务、是否多币种 | ⏳ 待定 | 当前按 CNY、无税务实现 |
## 五、现行与参考文档
| 文档 | 状态 | 用途 |
| --- | --- | --- |
| [项目全量分析报告-2026-10-17.md](项目全量分析报告-2026-10-17.md) | 参考 | 当前架构、业务链路、风险与改进路线;代码基线早于备用金实现,涉及财务部分以本索引为准 |
| [财务系统集成方案.md](财务系统集成方案.md) | 现行方案(未实施) | 财务系统边界、复式记账模型、核心表、API、分期落地 |
| [成本中心与三块履约模型.md](成本中心与三块履约模型.md) | 现行(P0 已实施) | 自动发货/接单平台/人工客服三块、商品映射、成本中心与销售事实 |
| [人工支出登记落地方案-2026-10-17.md](人工支出登记落地方案-2026-10-17.md) | 现行(P0 已实施) | 公司人工支出登记:字段、入口、状态机、权限、与备用金/成本中心/总账的衔接 |
| [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) | **现行唯一口径** | 履约方式、成本模型、人工全覆盖、历史策略、P0 迁移/回填/代码清单、运营日检与验收 SQL |
| [财务履约改造交接总结-2026-10-23.md](财务履约改造交接总结-2026-10-23.md) | **交接摘要** | 本次改了什么、关键数字、下一步 P0、运营硬要求、验证命令与阅读顺序 |
| [项目全量分析报告-2026-10-17.md](项目全量分析报告-2026-10-17.md) | 历史参考 | 2026-10-17 架构与风险;财务口径以总纲为准 |
| [财务系统集成方案.md](财务系统集成方案.md) | 现行方案(总账未实施) | 复式记账、总账表、对账、关账 |
| [成本中心与履约方式模型.md](成本中心与三块履约模型.md) | 现行(文件名保留) | 商品、成本快照、销售事实;履约分类以总纲为准 |
| [人工支出登记落地方案-2026-10-17.md](人工支出登记落地方案-2026-10-17.md) | 现行(P0 已实施) | 人工支出字段、入口、状态机、权限、备用金衔接 |
| [影子总账说明.md](影子总账说明.md) | 现行说明(未实施) | 影子总账是什么、和钱包流水的区别、对账与转正标准 |
| [客服备用金台账实施方案-2026-10-18.md](客服备用金台账实施方案-2026-10-18.md) | **已实施 P0** | 客服备用金账户、流水、支出、对账、权限与上线记录 |
| [客服备用金台账实施方案-2026-10-18.md](客服备用金台账实施方案-2026-10-18.md) | **已实施 P0** | 备用金账户、流水、支出、对账、本人账户与编号规则 |
| [接单模板匹配引擎重构设计.md](接单模板匹配引擎重构设计.md) | 现行(P0/P1 已实施) | `kuaishou-91 / pdd / douyin-mashida` 三来源匹配引擎 |
| [履约配置/多发货平台与电子凭证整体链路.md](履约配置/多发货平台与电子凭证整体链路.md) | 现行 | 进单、履约、领取、电子凭证的整体链路 |
| [affiliate-dash-发货平台/affiliate-dash对接.md](affiliate-dash-发货平台/affiliate-dash对接.md) | 现行 | affiliate-dash 自动履约接口与字段 |
@@ -2,14 +2,16 @@
> 状态:**已实施 P0(`dev` 分支,迁移 111;订单页人工录单、备用金扣款为 2026-10-22/23 增量)**
> 最后校准:2026-10-23
> 代码基线:`dev @ a7f60414 + 财务 108–113`
> 代码基线:`dev @ fac34c84 + 迁移 103–113`
> **总口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。**
> 依据:业务口径「人工块当前不以 SLA 监管为目标,先把流程搞清楚:哪个客服处理了哪些东西、付了多少钱、什么项目、关联哪个订单」。
> 数据来源:本地只读克隆(生产快照,数据截至 2026-10-14)+ 现有代码盘点。
> 数据来源:本地生产快照克隆(数据截至 2026-10-14)。
> 重要:**客服备用金支出已经实施**(`manual_expenses`);**公司人工支出登记**(`manual_expense_records`)也已实施,两张表通过 `fund_expense_id` 关联,不是同一张表。
> 重要:人工成本**逐单、按实际金额**录入,入口在订单界面;**不按小时,也不按固定单量/固定单价**。
> 重要:**人工支出实际上都走客服备用金**——登记时必须选择备用金账户,系统在同一事务内扣减余额、写 `fund_entries` 与 `manual_expenses`,再回填 `manual_expense_records.fund_expense_id`。
> 重要:**客服角色只能使用自己的备用金账户**(默认自动带入,不能选择别人);后端会强制校验,越权传别人的账户直接 403。管理员/财务可按需代录到指定客服账户。
> 相关文档:[成本中心与三块履约模型.md](成本中心与三块履约模型.md) §8 · [财务系统集成方案.md](财务系统集成方案.md) §八 · [客服备用金台账实施方案](客服备用金台账实施方案-2026-10-18.md) ·
> 重要:**客服角色只能使用自己的备用金账户**(默认自动带入,不能选择别人);后端越权直接 403。
> 重要:**历史人工支出不补录**,只取少量样本做校准;**上线后每一笔人工单必须 100% 有成本记录**,否则不允许关账。
> 相关文档:[成本中心与履约方式模型.md](成本中心与三块履约模型.md) §8 · [财务系统集成方案.md](财务系统集成方案.md) §八 · [客服备用金台账实施方案](客服备用金台账实施方案-2026-10-18.md) ·
> DDL 草案 [drafts/2026-10-17-人工支出登记-DDL草案.sql](drafts/2026-10-17-人工支出登记-DDL草案.sql)
---
@@ -69,7 +71,7 @@
| 数量 1 · 关联工单 1 | `totalQuantity` / `workOrderCount` | 订单项 / 工单 | 数量与载体提示 |
| ¥ 10.30 CNY | `totalAmount` / `totalAmountFen` | 订单表 | **用户实付(= 收入)**,用于算毛利 |
| 2026-10-17 22:24:21 | `paidAt` / `createdAt` | 订单表 | 支出时间不能早于支付时间 |
| 打手履约 / ⚡自动直发 / 🎫电子凭证 | `fulfillmentType` + `workOrderCount` | 派生 | 提示该单走哪条链路 |
| 接单平台 / ⚡自动发货 / 🎫核销码 | `fulfillment_mode` + `voucher_state`(当前为 `fulfillmentType`) | 派生 | 履约方式与核销码必须分开显示;核销码不参与判定 |
| 已支付 | `payStatus` | 订单表 | 只有已支付才允许登记支出 |
| 待准备资源 / 未开始 | `resourceStatus` / `customerStatus` | 任务/工单派生 | 提示当前进度 |
| (未在列表显示)操作人 | 后台会话 `userId` / `username` / `role` | `admin_sessions` | 默认「登记人」 |
@@ -145,6 +147,15 @@
- **一笔转账覆盖多个订单**(例如一次拿货 5 单)→ 建议拆成 5 条明细,用 `expense_batch_no` 归组(可选字段),避免把成本摊错订单。
- **实际支出 vs 标准成本**:`cost_snapshots` 记的是**标准成本**(成本中心算出来的),人工支出记的是**实际发生**。二者要能对比,差异就是"人工议价空间/异常"的信号 —— 这是后续做成本优化的关键数据,不要在实现时用其中一个覆盖另一个。
### 3.4 上线后人工全覆盖(硬要求)
- 人工判定:已支付订单项没有工单、没有自动发货任务、没有历史平台任务;**有没有快手核销码不影响判定**。
- 每一笔人工单必须有:处理人、实际金额、费用项目、收款方、付款方式、备用金账户、`fund_expense_id`、凭证或备注、成本快照。
- 新人工单必须在 **24 小时内**录入成本;超过 24 小时进入异常队列。
- 每日检查:人工单无成本记录 = 0;人工支出无 `fund_expense_id` = 0;人工支出与备用金流水金额不一致 = 0。
- 存在上述缺口时**不允许关账**。
- 历史人工支出不补录,只在报表中标记“历史未采集”,不纳入上线后的监控缺口。
---
## 四、交互落地(三个入口,一套数据)
@@ -290,16 +301,18 @@ reversed(预留) 反向登记 + 反向凭证,原凭证不动
| --- | --- | --- |
| 1 | 审批人是谁? | 财务(`finance` 角色)审批,管理员兜底 |
| 2 | 单笔二级审批阈值? | ¥5,000 起需管理员 |
| 3 | 区分「垫付」与「公司直付」吗? | 需要:垫付要报销,公司直付只记账 |
| 3 | 区分「垫付」与「公司直付」吗? | **不再区分**:人工支出一律从客服备用金账户扣款;公司直付不是默认路径 |
| 4 | 必须支持无订单支出吗? | **必须**(平台充值/赔偿/运费无处可记) |
| 5 | 允许一单多笔支出吗? | **必须** |
| 6 | 凭证必须吗? | **必须**(且支持多张) |
| 7 | 迟登记如何处理? | 24h 内正常,超时标记,超 7 天需审批 |
| 8 | 是否与现有 7 类财务流水合并展示? | 合并展示、分类筛选 |
| 9 | 客服备用金支出是否自动生成公司人工支出? | **不自动**;提供“从备用金支出带入”,人工确认后生成,避免重复入账 |
| 9 | 人工支出与备用金支出的关系? | **人工支出登记时自动生成备用金支出并扣款**;不是从备用金页反向带入 |
| 10 | 实际付款人必须等于登录人吗? | 允许代付;实际付款人必填,默认登录人,便于按人日结对账 |
| 11 | `cost_item_id` 为空能否提交? | 可暂存/提交,但不得 `approved`;进“未归类”异常队列 |
| 12 | 人工成本按单还是按小时? | **已确认(2026-10-22):逐单按实际金额**,不按小时、不按固定单量/固定单价;工时只作可选分析字段 |
| 13 | 历史人工支出是否补录? | **不补录**,只取少量样本校准;上线后人工单必须 100% 有成本记录 |
| 14 | 电子凭证是否作为分类? | **不作为履约/成本分类**;只是快手核销码状态 |
---
@@ -1,8 +1,9 @@
# 客服备用金台账实施方案(2026-10-18)
> 状态:**已实施 P0**(迁移 100/101,前端/后端已上线到代码库;未部署生产)。
> 最后校准:2026-10-18
> 代码基线:`main @ 7542d547`
> 状态:**已实施 P0**(迁移 100/101/113;代码已上线到代码库,未部署生产)。
> 最后校准:2026-10-23
> 代码基线:`dev @ fac34c84 + 迁移 103–113`
> **总口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。**
> 背景:两位主管向客服发放备用金,客服用于充值、采购、赔偿等;目前全部走人工,无法监控每笔资金用途和每个客服余额。
> 结论:这不是报销系统,而是**客服备用金(周转金)台账**。P0 已完成“账户 + 流水 + 支出 + 对账”,不做复杂审批和总账。
@@ -18,7 +19,7 @@
| 前端 | `AdminFundPage.tsx`、`FundManagementView.tsx`、`FundSupportView.tsx`;管理端与客服端分流 |
| 权限 | `fund.view / fund.issue / fund.expense / fund.reconcile / fund.adjust / fund.export` |
| 审计 | 所有写接口接入 `createJsonHandler({ audit })` |
| 测试 | 后端 738 通过 / 3 跳过;前端 143 通过;两端 typecheck、lint、build 通过 |
| 测试 | 最近全量校验(`fac34c84`):后端 760 项,757 通过 / 3 跳过;前端 `npm run check` 通过 |
**与本文原方案的差异:**
@@ -29,8 +30,11 @@
**后续衔接:**
- 财务侧公司人工支出登记应新建 `manual_expense_records`,通过 `fund_expense_id` 关联本表的 `manual_expenses.id`,不要复用或改名。
- 备用金支出可作为“钱从客服备用金出”的来源证据;公司直付、垫付报销仍走公司人工支出登记。
- 财务侧公司人工支出登记使用 `manual_expense_records`,通过 `fund_expense_id` 关联本表的 `manual_expenses.id`。
- 新人工支出登记会**先扣客服备用金**、写 `fund_entries` 与 `manual_expenses`,再写 `manual_expense_records`,全程同一事务。
- 客服角色只能使用本人备用金账户;管理员/财务可代录到指定客服账户。
- 历史人工支出不补录,只取少量样本校准;上线后每一笔人工单必须有关联的备用金支出与成本快照。
- 备用金账户编号统一为 `FA-<大写用户名>`(如 `FA-KF_1`、`FA-JIAJIALI`),重名追加 `-2`、`-3`。
---
@@ -49,7 +53,7 @@
- 不做垫付/直付的复杂资金性质区分;
- 不做多级审批流;
- 不做成本中心、订单毛利、复式总账;
- 不做成本中心、订单毛利、复式总账(这是备用金 P0 当时的边界;成本中心与销售事实已在后续迁移 `103–112` 完成);
- 不做平台账户独立钱包(当前口径:充值后立即用于订单/消耗,直接记支出);
- 不做多币种、发票、税务。
@@ -253,6 +257,8 @@ P0 四张表即可。
- 生成一条 `expense` 流水;
- 生成一条 `manual_expenses` 明细。
人工支出登记使用同一条资金流:先扣客服余额、写 `fund_entries` 与 `manual_expenses`,再写 `manual_expense_records.fund_expense_id`。客服角色默认并强制使用本人账户。
### 5.5 客服缴回
客服或主管发起缴回,主管确认后:
@@ -1,6 +1,7 @@
# 多发货平台与电子凭证整体链路
> 状态:现行。最后校准:2026-10-18(`main @ 7542d547`;仅基线更新,业务逻辑未变)。
> 状态:现行。最后校准:2026-10-23(代码基线 `dev @ fac34c84 + 迁移 103–113`;业务逻辑未变,仅口径校正)。
> 财务与履约总口径见 [财务与履约口径总纲-2026-10-23.md](../财务与履约口径总纲-2026-10-23.md)。
> 来源口径:本系统只有三个订单来源——`kuaishou-91`、`pdd`、`douyin-mashida`。**91 回调与快手 send-code 是同一个 `kuaishou-91` 来源的两条数据通道**(91=骨架权威源,快手 send-code=补全/兜底源),不是一个来源加另一个来源。
## 1. 当前结论
@@ -23,17 +24,23 @@ kuaishou-cloud / kuaishou-feifei 通道已下线,affiliate-dash 负责真实
快手电子凭证负责把领取页里的核销码输入步骤自动化
```
### 1.1 执行载体三块
### 1.1 履约方式(不是“三块”)
真正决定成本和监管方式的是订单项的**执行载体**:
真正决定成本和监管方式的是订单项的**履约方式**;电子凭证只是可选核销码:
| 载体 | 当前实现 | 说明 |
| 履约方式 | 当前实现 | 说明 |
| --- | --- | --- |
| `affiliate_dash` | 自动履约任务 | 上游固定成本,适合店铺/日期/皮肤销量统计 |
| `work_order` | 接单平台工单 | 变动成本,适合影子总账与打手结算 |
| `manual` | 目前没有显式 case | 无自动任务且无工单的已支付订单项;当前最大监管缺口 |
| `work_order` | 接单平台工单 | 变动成本,适合打手结算与毛利 |
| `legacy_platform` | `kuaishou_ct_assisted` / `kuaishou_feifei` 历史任务 | 通道已下线,只保留历史报表;不得混入人工 |
| `manual` | 无工单、无自动发货任务、无历史平台任务,人工处理 | 逐单实际金额;有没有核销码不影响判定 |
| `voucher_state` | 快手电子凭证 | 只是核销码状态,不参与上表判定 |
详细设计见 [../成本中心与三块履约模型.md](../成本中心与三块履约模型.md)。
判定顺序:`work_order` → `affiliate_dash` → `legacy_platform` → `manual`。
当前真实数据:work_order 118,053 / affiliate_dash 41,676 / legacy_platform 53,901 / manual 102,955,合计 316,585。
详细设计见 [../财务与履约口径总纲-2026-10-23.md](../财务与履约口径总纲-2026-10-23.md) 与 [../成本中心与履约方式模型.md](../成本中心与三块履约模型.md)。
## 2. 目标链路
+6 -5
View File
@@ -1,15 +1,16 @@
# 影子总账到底是什么意思
> 状态:**现行说明(未实施)**
> 最后校准:2026-10-18
> 代码基线:`main @ 7542d547`
> 最后校准:2026-10-23
> 代码基线:`dev @ fac34c84 + 迁移 103–113`
> 配套方案:[财务系统集成方案.md](财务系统集成方案.md)
> 前置:商品主数据、渠道映射、成本中心与销售事实;实施顺序以 [README.md](README.md) §三 为准。
> **总口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。**
> 前置:统一履约事实、商品主数据、渠道映射、成本中心与销售事实;实施顺序以总纲第六节为准。
> 一句话:**影子总账是一套“跟着真实业务跑、但暂时不接管余额”的复式会计账。** 它先把每一笔业务翻译成借贷分录,和现有钱包/订单/提现数据逐日对账;确认长期对得平之后,再让它成为正式总账。
>
> 边界:影子总账解决“钱怎么记、怎么对”,不负责“商品是什么、成本是多少、人工谁处理”。商品与成本见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md),人工履约见同一文档第八节。
> 边界:影子总账解决“钱怎么记、怎么对”,不负责“商品是什么、成本是多少、人工谁处理”。商品与成本见 [成本中心与履约方式模型.md](成本中心与三块履约模型.md),人工履约见同一文档第八节。
> 当前进度:客服备用金、商品与成本中心、成本快照运行层、销售事实、公司人工支出登记已实施;`finance_*` 表、分录、对账、转正均未实施。
> 当前进度:客服备用金、商品与成本中心、成本快照、销售事实、公司人工支出登记已实施;`finance_*` 表、分录、对账、转正均未实施。人工支出现在强制走客服备用金;历史人工支出不补录,上线后必须全覆盖。
---
+44 -29
View File
@@ -1,10 +1,11 @@
# 成本中心与三块履约模型
# 成本中心与履约方式模型(文件名保留)
> 状态:**现行(P0 已实施)**。商品主数据、渠道映射、成本中心、成本快照运行层、销售事实与公司人工支出登记(含订单页逐单实际金额「人工录单」)已落地;轻量人工 case 与影子总账仍为后续方案。
> 状态:**现行(P0 已实施,履约方式待统一)**。商品主数据、成本中心、成本快照、销售事实、人工支出已落地;`fulfillment_mode` 持久化与人工全覆盖仍待实施。
> 最后校准:2026-10-23
> 代码基线:`dev @ a7f60414 + 财务 108–113`
> 数据基线:2026-10-17 生产库只读聚合(`default_transaction_read_only=on`,仅 SELECT,无写入)
> 实施顺序以 [README.md](README.md) §三 为准;本文负责“商品、成本、销售事实”的数据模型,不重复总账与页面设计。
> 代码基线:`dev @ fac34c84 + 迁移 103–113`
> 数据基线:本地生产快照克隆(数据截至 2026-10-14)
> **财务与履约口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准**;本文只负责商品、成本、销售事实的数据模型。
> 文件名保留是为了历史链接稳定,正文不再使用“三块”作为分类依据。
> 关联:
> - [项目全量分析报告-2026-10-17.md](项目全量分析报告-2026-10-17.md)
> - [影子总账说明.md](影子总账说明.md)
@@ -48,24 +49,28 @@
---
## 一、先纠正一个口径:三块不是“订单来源”,是“执行载体”
## 一、先纠正口径:履约方式不是订单来源,也不是核销码
同一个来源的订单,可能走自动发货、接单平台或人工。正确的拆分维度是**订单项最终由谁履约**:
同一个来源的订单,可能走自动发货、接单平台、历史平台或人工。电子凭证只是快手核销码,不参与履约方式判定。
| 块 | 执行载体 | 成本特征 | 监管重点 |
| 履约方式 | 实现 | 成本模型 | 监管重点 |
| --- | --- | --- | --- |
| ① 自动发货 | `affiliate_dash` 履约任务 | 固定成本,按皮肤/SKU,很少变化 | 上游成本、店铺/日期/皮肤销量 |
| ② 接单平台 | `work_orders` + 拼单份额 | 变动成本,按模板/单价/固定价/拼单 | 发单价格、打手提现、售后追缴、毛利 |
| ③ 人工客服 | 目前**无载体** | 逐单**实际金额**(不按工时、不按固定单量) | 谁处理、何时完成、证据、成本、异常 |
| `affiliate_dash` | affiliate-dash 自动履约任务 | 上游 SKU 固定成本版本 | 上游成本、店铺/日期/皮肤销量 |
| `work_order` | `work_orders` + 拼单份额 | 模板规则版本 / 工单实际报酬 | 发单价格、打手提现、售后追缴 |
| `legacy_platform` | `kuaishou_ct_assisted` / `kuaishou_feifei` 历史任务 | 历史成本未采集则显式标记 | 仅历史报表,不再新增 |
| `manual` | 无工单、无自动发货任务、无历史平台任务,人工处理 | 逐单实际金额(不按工时/单量) | 处理人、实际支出、备用金、凭证 |
硬不变式:
```text
一个订单项 = 订单骨架 + 买家凭证 + 一个执行载体
执行载体 ∈ { affiliate_dash, work_order, manual }
一个订单项 = 订单骨架 + 可选核销码 + 一个履约方式
履约方式 ∈ { affiliate_dash, work_order, legacy_platform, manual }
voucher_state 是附加状态,不参与履约方式判定
```
当前代码对抖音有 `snapshot.classification` 守卫,但 91/PDD 仍然主要靠 `isConfigured=false` 隐式表达“人工”。这会导致人工单在系统里没有显式状态,也没有处理记录。
当前真实数据:work_order 118,053 / affiliate_dash 41,676 / legacy_platform 53,901 / manual 102,955;四项相加 = 316,585。
> 人工 102,955 项中,11,349 项已有 `canonical_product_id`,但全部指向 `tpl:*` 接单模板商品——只说明 SKU 命中过模板,**不代表有工单**;成本模型仍按 `manual`。真正未归一的人工项是 91,606 项。
---
@@ -364,7 +369,7 @@ orders + order_items + channel_product_mappings + cost_snapshots
后台入口:`/admin/sales-profit`。回填脚本:`npm run finance:sync -- --days=14 --mode=all`;定时任务 `cost-finance-sync` 每 5 分钟处理最近 7 天。成本缺失会写入 `cost_missing_count`,毛利在补齐成本前只作参考。
数据校验:本地 2026-07-10 ~ 2026-10-14 共 316,597 个已支付订单,其中 12 单没有订单项(¥2,480.60),无法归集到商品/成本,销售事实会排除并在页面单独提示;其余 316,585 个订单项收入与订单总额完全对平。
全量回填后(2026-07-10 ~ 2026-10-10):接单平台 95,870 条 `actual`(¥3,773,401.32)+ 21,217 条 `estimated`(¥754,912.45);自动发货 41,676 条 `estimated`(¥4,974,059.00)。自动发货与接单平台收入归一率均为 100%,未归一收入只剩人工块 145,507 项 / ¥15,253,660.86。接单平台仍有 966 个订单项没有成本快照:601 个已取消工单 + 365 个零报酬未验收工单,保留在成本异常队列等业务确认,不自动记 0。人工客服仍因缺少商品与正式单价显示成本缺失。
全量回填后(2026-07-10 ~ 2026-10-10):接单平台 95,870 条 `actual`(¥3,773,401.32)+ 21,217 条 `estimated`(¥754,912.45);自动发货 41,676 条 `estimated`(¥4,974,059.00)。自动发货与接单平台收入归一率均为 100%。未归一 145,507 项 / ¥1,608.33 万按正确履约口径拆分为:**人工未归一 91,606 项 / ¥1,219.20 万**,历史平台 53,901 项 / ¥389.12 万。人工全量是 102,955 项,另外 11,349 项已归一但全部挂 `tpl:*` 商品,不代表有工单;不能把 91,606 当成人工全量。接单平台仍有 966 个订单项没有成本快照:601 个已取消工单 + 365 个零报酬未验收工单,保留在成本异常队列等业务确认,不自动记 0。
---
@@ -388,7 +393,7 @@ orders + order_items + channel_product_mappings + cost_snapshots
- 店铺/日期/皮肤统计必须走 `canonical_product_id`,不能按原始 `sku_code`。
- 统计件数要解析 SKU 文本中的数量。
实测(本地 2026-10-22):41,676 个自动发货订单项全部回填,未归一收入 ¥5,089,712.26 → ¥0.00;采购成本 ¥4,974,059.00(毛利率 2.27%)。整体未归一收入占比 79.67% → 57.54%,剩余 100% 来自人工块(145,507 项 / ¥15,253,660.86),需要业务先确定人工 SKU/商品口径。
实测(本地 2026-10-22):41,676 个自动发货订单项全部回填,未归一收入 ¥5,089,712.26 → ¥0.00;采购成本 ¥4,974,059.00(毛利率 2.27%)。剩余未归一 145,507 项 / ¥1,608.33 万,其中**人工未归一 91,606 项 / ¥1,219.20 万**,历史平台 53,901 项 / ¥389.12 万;人工全量为 102,955 项。人工需要逐单实际成本,历史平台需要历史结算口径。
---
@@ -525,6 +530,8 @@ pending_assign -> assigned -> processing -> waiting_customer
- 有审核权限(`admin` / `finance` / `finance.review`)且选择立即生效时,当场审批并写 `actual` 快照;否则留在 `submitted`,由财务在「人工支出」页审核。
- 快照以 `subject_type='manual_case'`、`subject_id=expense.id` 写入并携带 `order_item_id`;销售事实按 `cost_snapshots.order_item_id` 汇总,同一订单项的多笔支出(渠道费 + 加急费 + 补差价)累加。
- 工时只作为后续可选的分析字段,不参与成本计算;不设「按单固定单价」。
- **历史人工支出不补录**,只取少量样本做校准;历史缺口在报表中标记“历史未采集”,不计入上线后的监控缺口。
- **上线后人工单必须 100% 有成本记录**:人工单无支出、支出无 `fund_expense_id`、支出与备用金流水不一致,均不允许关账。
- 未录入成本的订单项保持 `cost_missing`,进入成本异常队列,不默认记 0。
### 8.6 监管指标(阶段③,延后)
@@ -547,26 +554,30 @@ pending_assign -> assigned -> processing -> waiting_customer
---
## 九、三块统一数据流
## 九、履约方式统一数据流
```mermaid
flowchart LR
O["订单项"] --> C["载体判定"]
C -->|命中自动| A["affiliate_dash 任务"]
C -->|命中模板| W["work_order"]
C -->|都没有| M["manual_case"]
O["订单项"] --> C["统一履约判定 fulfillment_mode"]
C -->|有自动任务| A["affiliate_dash"]
C -->|有工单| W["work_order"]
C -->|历史 ct/feifei| L["legacy_platform"]
C -->|都没有| M["manual"]
A --> CS["cost_snapshots"]
W --> CS
L --> CS
M --> CS
CS --> F["sales_daily_facts / 毛利"]
A --> L["影子总账"]
W --> L
M --> L
A --> GL["影子总账"]
W --> GL
L --> GL
M --> GL
P["product_catalog + channel_product_mappings"] --> F
V["cost_items + cost_versions"] --> CS
Q["voucher_state(可选核销码)"] -.不参与判定.-> C
```
---
@@ -588,13 +599,14 @@ flowchart LR
- 人工客服:系统成本项 `sys_manual_service` + 人工支出登记通过后写实际快照;正式单价版本待财务填写
- 缺失成本进异常队列
### P2:公司人工支出登记 + 轻量处理记录——支出登记已完成,轻量 case 未实施
### P2:人工全覆盖与轻量处理记录——支出登记已完成,轻量 case 未实施
- 载体判定(`carrier_type`)
- 先统一 `fulfillment_mode`,人工只在无工单、无自动任务、无历史平台任务时出现
- 无载体自动建轻量 case(谁在处理哪个订单项)
- **公司人工支出登记**:付款项目 / 金额 / 订单号 / 付款人 / 收款人 / 付款方式 / 凭证 / 审批
- 通过 `fund_expense_id` 关联客服备用金支出,避免重复记账
- 人工支出进成本快照与影子总账(借:履约成本-人工,贷:其他应付-垫付人)
- 登记时强制从客服备用金扣款,并写 `fund_expense_id`;客服只能使用本人账户
- **历史人工支出不补录**,只取少量样本校准;上线后人工单必须 100% 有成本记录
- 人工支出进成本快照与影子总账(借:履约成本-人工,贷:备用金/其他应付)
- 延后(阶段③):SLA、超时升级、主管复核阈值、监管指标
### P3:影子总账
@@ -618,6 +630,9 @@ flowchart LR
| 5 | 收入确认时点? | **2026-10-22 确认:先按 `paid_at` 归属销售日**;成本在快照生成后回填到同一销售日,后续再评估验收/售后期口径 | 业务确认 |
| 6 | 未归一商品怎么进报表? | **2026-10-22 确认:允许 `canonical_product_id` 为空,单独成“未归一”行**,保证总收入与订单总额对平 | 业务确认 |
| 7 | 人工成本怎么归集? | **逐单、按实际金额**:关联订单号录入本单实际付款,**不按小时、不按固定单量/固定单价**;同一订单项多笔可累加 | 业务确认(2026-10-22) |
| 8 | 电子凭证算不算履约类型? | **不算**:电子凭证只是快手核销码,订单仍按工单/自动任务/历史平台/人工判定 | 业务确认(2026-10-23) |
| 9 | 历史人工支出是否补录? | **不补录**,只取少量样本校准;上线后人工单必须全覆盖 | 业务确认(2026-10-23) |
| 10 | 历史 ct_assisted/feifei 怎么处理? | **单列 `legacy_platform`**,不混入人工;历史成本未采集则显式标记 | 业务确认(2026-10-23) |
### 11.2 仍待确认
@@ -641,4 +656,4 @@ flowchart LR
## 十三、一句话结论
**自动发货解决“固定成本 + 店铺/日期/皮肤销量”,接单平台解决“变动成本 + 影子总账”,人工客服解决“支出可追溯(谁付的、什么项目、多少钱、哪个订单)+ 处理人可见 + 人工成本”;三者共用一套商品主数据、成本中心和销售事实表,成本改价只发生在 `cost_versions` 一个地方。**
**自动发货解决“固定成本 + 店铺/日期/皮肤销量”,接单平台解决“变动成本 + 打手结算”,历史平台只做历史报表,人工解决“逐单实际支出 + 处理人可见 + 备用金扣款”;四者共用一套商品主数据、成本中心和销售事实表,成本改价只发生在 `cost_versions` 一个地方。电子凭证只是核销码,不是分类。**
+1 -1
View File
@@ -1,6 +1,6 @@
# 接单模板匹配引擎重构设计
> 目标读者:后端 / 后台运营。状态:**P0 + P1 已实施;P2 / P3 待办**。最后校准:2026-10-18(`main @ 7542d547`;仅基线更新,业务逻辑未变)。
> 目标读者:后端 / 后台运营。状态:**P0 + P1 已实施;P2 / P3 待办**。最后校准:2026-10-23(代码基线 `dev @ fac34c84 + 迁移 103–113`;业务逻辑未变)。
>
> 一句话:把「订单信息 → 接单模板(价格/拼单/要求)」的匹配,从散落的多个消费点、平台判断和"店铺必填"的绑定表里,收敛为**一个平台无关的引擎**:匹配条件完全由**订单信息**表达,**店铺只是可选条件之一**,新增来源只加一个适配器 + 一组配置。
>
@@ -0,0 +1,479 @@
# 财务与履约口径总纲(2026-10-23)
> 状态:**现行唯一口径**。本文是财务、成本、履约分类的总入口;其他文档与本文件冲突时,以本文为准。
> 代码基线:`dev @ fac34c84 + 迁移 103–113`。历史文档中的旧基线、旧三块模型、按小时人工成本等表述全部作废。
> 适用范围:订单接入、履约判定、商品归一、成本快照、销售事实、人工支出、备用金、后续总账与对账。
---
## 〇、六条硬口径
1. **电子凭证不是履约方式,也不是成本类型。** 快手电子凭证只是核销码/兑换码,抖音 DYC 券(`redeem_certificates`)同理;一个有核销码的 91/快手或抖音订单,仍可能走自动发货、接单平台或人工。
2. **旧平台通道已经下线。** `kuaishou_ct_assisted`、`kuaishou_feifei` 只保留历史数据,报表中统一标记为 `legacy_platform`,不得混入人工。
3. **人工的定义只有一个:** 已支付订单项,没有工单、没有自动发货任务、没有历史平台任务,即为人工;有没有核销码不影响判定。
4. **人工成本逐单、按实际金额。** 不按小时、不按固定单量、不按固定单价;必须从客服备用金账户扣款,客服只能使用本人账户。
5. **历史人工支出不补录。** 只取少量样本做校准参考;历史缺口统一标记为“历史未采集”,不阻塞上线。
6. **上线后人工账单必须 100% 可监控。** 每一笔人工单必须有处理人、实际金额、备用金支出与成本快照;缺失即进入异常队列,不允许关账。
---
## 一、履约方式与成本模型必须分开
当前系统把“怎么交付”和“成本怎么算”混在同一个 `carrier_type` 里,导致同一订单在订单页、成本快照、销售事实中出现不同分类。正确模型拆成三个正交维度:
| 维度 | 取值 | 回答的问题 |
| --- | --- | --- |
| `fulfillment_mode` | `work_order` / `affiliate_dash` / `legacy_platform` / `manual` | 这单最终由谁执行 |
| `voucher_state` | 无 / 已发码 / 已核销 / 已销毁 | 有没有快手核销码、走到哪一步 |
| `cost_model` | `work_order_reward` / `affiliate_purchase` / `manual_actual` / `legacy_cost` / `none` | 这单成本怎么算 |
硬规则:
- `voucher_state` **不参与** `fulfillment_mode` 判定;
- `fulfillment_mode` 必须在履约决策时写入 `order_items`,不能每次查询临时推断;
- 成本快照、销售事实、后台订单页只读同一份 `fulfillment_mode`;
- 一个订单项只能有一个 `fulfillment_mode`,核销码可以有多个状态事件。
---
## 二、当前真实履约构成(本地库,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` | 人工 | 订单页逐单录入的实际支出 | 已实现,历史未采集 |
| `legacy_cost` | 历史平台 | 旧平台结算数据(如可得) | 未采集,历史单列 |
| `none` | 无成本或平台承担 | 业务确认后显式标记 | 暂不默认 |
硬规则:
- 未确认成本时保持 `cost_missing`,**绝不默认记 0**;
- 人工成本只走 `sys_manual_service`,通过 `manual_expense_records` 写 `actual` 快照;
- 自动发货、接单平台继续使用现有成本版本与规则版本;
- 电子凭证不建成本项,也不建“凭证载体”成本;
- 历史平台成本单独标记 `legacy_cost`,不要混入人工成本。
---
## 四、上线后人工账单全覆盖规则
### 4.1 判定
每个已支付订单项按以下顺序判定,命中即停止:
```text
有 work_orders -> work_order
有 affiliate_dash 任务 -> affiliate_dash
有 ct_assisted/feifei -> legacy_platform
其余 -> manual
```
核销码只写入 `voucher_state`,不改变上述结果。
### 4.2 人工单必须有的数据
每一笔 `manual` 订单项必须具备:
- 处理人(客服/操作人);
- 实际付款金额(分);
- 费用项目;
- 收款方与付款方式;
- 备用金账户与 `fund_expense_id`;
- 凭证或备注;
- 成本快照(`manual_case` / `actual`)。
### 4.3 监控指标(上线后每日检查)
| 指标 | 目标 |
| --- | ---: |
| 已支付订单项无 `fulfillment_mode` | 0 |
| 人工订单项无人工支出记录 | 0 |
| 人工支出无 `fund_expense_id` | 0(历史样本除外) |
| 人工支出无成本快照 | 0 |
| 人工支出金额与备用金流水不一致 | 0 |
| 成本异常 open 中人工项 | 0(历史标记除外) |
### 4.4 关账门槛
- 存在人工单无成本记录:**不允许关账**;
- 存在人工支出未关联备用金:**不允许关账**;
- 存在人工支出与备用金流水金额不一致:**不允许关账**。
---
## 五、历史数据处理
- 历史人工支出**不补录**,只允许抽取样本做成本校准;
- 历史人工单在销售事实中保留收入,成本标记为 `historical_missing`,不计入“上线后监控缺口”;
- 历史平台 `legacy_platform` 单独成行,成本未采集的标记 `legacy_missing`;
- 自动发货、接单平台历史成本不回写、不重算版本,沿用现有快照。
---
## 六、实施顺序
### P0:统一履约事实(先做)
> 执行细节、迁移 SQL、回填脚本与代码清单见 §八;本节只保留顺序。
1. `order_items` 增加 `fulfillment_mode`、`fulfillment_decided_at`、`fulfillment_reason`;
2. 按第四节顺序回填 316,585 个订单项;
3. 后台、成本快照、销售事实统一读取该字段;
4. 移除 `provider === '91kaquan' -> voucher_only` 的兜底;
5. 验收:work_order 118,053 / affiliate_dash 41,676 / legacy_platform 53,901 / manual 102,955 四项相加等于总数。
### P1:人工商品与成本入口
1. 人工单按 `spec.skuId` 建 `man:<skuId>` 商品,PDD 用 `sku_code/sku_name` 兜底;
2. 订单页人工录单保持逐单实际金额;
3. 上线后新人工单必须在 24 小时内录入成本;
4. 验收:人工单无成本记录 = 0。
### P2:退款与异常闭环
1. 订单增加退款字段;
2. 销售事实接入退款金额与成本冲减;
3. 人工/接单平台异常队列按履约方式拆分;
4. 验收:退款订单收入、成本、毛利可对平。
### P3:影子总账与对账
1. 建 `finance_accounts / finance_business_events / finance_journal_*`;
2. 先接订单支付、工单结算、押金、提现、人工支出五类事件;
3. 每日对账:钱包余额 vs 总账余额;
4. 验收:试算平衡、钱包差异为 0,再谈关账与报表。
---
## 七、当前实施进度(2026-10-23)
| 能力 | 状态 | 证据 |
| --- | --- | --- |
| 订单页人工录单,按单实际金额 | **已实施** | `74b62f34` |
| 人工支出从客服备用金扣款 | **已实施** | `2aeb789e` |
| 客服只能使用本人备用金账户 | **已实施** | `a7f60414` |
| 备用金账户编号 `FA-<用户名>` | **已实施** | `fac34c84` + 迁移 `113` |
| 订单页只展示平台判断 SKU | **已实施** | `c38cd8ca`、`cd9bb141` |
| 财务与履约文档统一口径 | **本轮完成** | 本文件 + 交接总结 + 关联文档 |
| `order_items.fulfillment_mode` 持久化 | **未实施** | 本文 §八 |
| 人工商品归一 `man:<skuId>` | **未实施** | 本文 §八、§九 |
| 退款冲减、异常闭环 | **未实施** | 本文 §六 P2 |
| 影子总账、对账、关账 | **未实施** | 本文 §六 P3 |
> 分支事实:以上实现均在 `dev`,迁移 `103–113` 尚未合并/部署生产;生产仍以 `main` 为准。
> 下一阶段**只做 §八 的 P0**,不要同时改人工商品、退款或总账。
---
## 八、P0 开工清单:统一履约事实
目标:让订单页、成本快照、销售事实读同一个 `order_items.fulfillment_mode`,不再各自推断。完成后才允许做人工商品归一。
### 8.1 迁移 `114_order_item_fulfillment_mode.sql`
四张表的 `carrier_type` 约束目前都不接受 `legacy_platform`;只加 `order_items` 字段会在写销售事实/成本快照时直接失败。迁移必须同时放宽:
- `product_catalog.chk_product_catalog_carrier_type`
- `cost_items.chk_cost_items_carrier_type`
- `cost_snapshots.chk_cost_snapshots_carrier_type`
- `sales_daily_facts.chk_sales_daily_facts_carrier`(保留 `unknown`)
```sql
ALTER TABLE order_items
ADD COLUMN IF NOT EXISTS fulfillment_mode text,
ADD COLUMN IF NOT EXISTS fulfillment_decided_at timestamptz,
ADD COLUMN IF NOT EXISTS fulfillment_reason text;
ALTER TABLE order_items
ADD CONSTRAINT chk_order_items_fulfillment_mode
CHECK (
fulfillment_mode IS NULL
OR fulfillment_mode IN ('work_order', 'affiliate_dash', 'legacy_platform', 'manual')
);
CREATE INDEX IF NOT EXISTS idx_order_items_fulfillment_mode
ON order_items (fulfillment_mode, order_id);
CREATE INDEX IF NOT EXISTS idx_order_items_fulfillment_pending
ON order_items (order_id)
WHERE fulfillment_mode IS NULL;
ALTER TABLE product_catalog DROP CONSTRAINT IF EXISTS chk_product_catalog_carrier_type;
ALTER TABLE product_catalog ADD CONSTRAINT chk_product_catalog_carrier_type
CHECK (carrier_type IN ('affiliate_dash', 'work_order', 'legacy_platform', 'manual', 'shared'));
ALTER TABLE cost_items DROP CONSTRAINT IF EXISTS chk_cost_items_carrier_type;
ALTER TABLE cost_items ADD CONSTRAINT chk_cost_items_carrier_type
CHECK (carrier_type IN ('affiliate_dash', 'work_order', 'legacy_platform', 'manual', 'shared'));
ALTER TABLE cost_snapshots DROP CONSTRAINT IF EXISTS chk_cost_snapshots_carrier_type;
ALTER TABLE cost_snapshots ADD CONSTRAINT chk_cost_snapshots_carrier_type
CHECK (carrier_type IN ('affiliate_dash', 'work_order', 'legacy_platform', 'manual', 'shared'));
ALTER TABLE sales_daily_facts DROP CONSTRAINT IF EXISTS chk_sales_daily_facts_carrier;
ALTER TABLE sales_daily_facts ADD CONSTRAINT chk_sales_daily_facts_carrier
CHECK (carrier_type IN ('affiliate_dash', 'work_order', 'legacy_platform', 'manual', 'shared', 'unknown'));
```
### 8.2 回填(只填 NULL,可重复执行)
> 迁移 `114` 只做 DDL;回填单独执行,不要塞进启动迁移。31 万行 `UPDATE` 放进迁移会拖慢部署,也无法在中途失败后按批重试。建议写成一次性脚本,每 5,000 行提交一次。
判定顺序固定:**工单 → 自动发货任务 → 历史平台任务 → 人工**。当前库中三种载体没有重叠,顺序不影响结果;后续出现重叠必须告警,不能静默覆盖。
```sql
WITH decided AS (
SELECT
oi.id,
CASE
WHEN EXISTS (SELECT 1 FROM work_orders wo WHERE wo.order_item_id = oi.id)
THEN 'work_order'
WHEN EXISTS (
SELECT 1 FROM fulfillment_tasks ft
WHERE ft.order_item_id = oi.id AND ft.executor_key = 'affiliate_dash'
) THEN 'affiliate_dash'
WHEN EXISTS (
SELECT 1 FROM fulfillment_tasks ft
WHERE ft.order_item_id = oi.id
AND ft.executor_key IN ('kuaishou_ct_assisted', 'kuaishou_feifei')
) THEN 'legacy_platform'
ELSE 'manual'
END AS fulfillment_mode
FROM order_items oi
)
UPDATE order_items oi
SET fulfillment_mode = d.fulfillment_mode,
fulfillment_decided_at = now(),
fulfillment_reason = 'backfill_2026_10_23'
FROM decided d
WHERE oi.id = d.id
AND oi.fulfillment_mode IS NULL;
```
回填后的验收数字(本地 2026-07-10 ~ 2026-10-14):
| fulfillment_mode | 订单项 | 收入 | 无成本项 |
| --- | ---: | ---: | ---: |
| `work_order` | 118,053 | ¥533.57 万 | 966 |
| `affiliate_dash` | 41,676 | ¥508.97 万 | 0 |
| `legacy_platform` | 53,901 | ¥389.12 万 | 53,901 |
| `manual` | 102,955 | ¥1,219.20 万 | 102,954 |
| **合计** | **316,585** | **¥2,650.87 万** | **157,821** |
> 以上 SQL 已在本地库用事务完整试跑并回滚:`UPDATE 316585`,四项计数与本表一致,`NULL` 为 0;耗时约 45 秒。这进一步说明回填应单独分批执行,不要放进启动迁移。
### 8.3 代码改造清单
| 文件 | 当前问题 | 必须改成 |
| --- | --- | --- |
| `apps/backend/src/services/admin/admin-order-read-helpers.ts` | `vouchers.length > 0 || provider === '91kaquan'` 判为 `voucher_only`;`provider === 'douyin_mashida'` 直接判自动发货;自动任务优先于工单 | 只读 `fulfillment_mode`;四种模式独立映射;核销码单独返回 `voucherState`;删除 provider 兜底 |
| `apps/backend/src/services/finance/sales-facts-service.ts` | `CASE` 临时推断,且没有 `legacy_platform` | 直接读 `oi.fulfillment_mode`,不得再复制判定 |
| `apps/backend/src/services/cost-center/cost-snapshot-sync-service.ts` | 又复制一套三选一判定 | 直接读 `oi.fulfillment_mode` |
| 新增 `apps/backend/src/services/fulfillment/fulfillment-mode-service.ts` | 没有统一决策入口 | 提供 `decideOrderItemFulfillmentMode()` 与 `reconcilePendingFulfillmentModes()`;工单/任务状态变化时调用,定时任务兜底 |
| `apps/frontend/src/pages/admin/AdminOrdersPage.tsx` 及类型 | 前端只有 `auto_claim / worker_dispatch / voucher_only / manual` | 增加 `legacy_platform`;核销码改为独立标签;人工按钮只认 `manual` |
### 8.4 决策边界(写进代码注释与测试)
1. `fulfillment_mode` 允许短暂为 `NULL`;订单项创建时通常还没有工单/任务,不能强制非空。
2. 上线后新订单项超过 **24 小时**仍为 `NULL`,进入异常队列;不是要求在创建瞬间就有值。
3. 模式可以因业务改派而变化,但每次变化必须写原因与操作人(审计日志或后续事件表),禁止静默 `UPDATE`。
4. `product_catalog.carrier_type` 只描述商品来源,**不能反推履约方式**。当前 11,349 个人工项指向 `tpl:*` 商品,就是典型反例。
5. `voucher_state` 只做展示与核销流程,不参与任何成本/履约判断。
6. 三种载体同时命中的订单项必须先告警人工复核;当前本地为 0。
### 8.5 完成标准
- 迁移 `114` 可在空库与现有库各跑一次,重复执行不报错;
- 回填后 §8.2 四项数字完全一致;
- 订单页、成本快照、销售事实对同一订单项返回同一模式;
- 人工单带核销码仍显示人工;抖音无任务单仍显示人工;历史平台不再落入人工;
- 后端 `npm run check`、前端 `npm run check` 通过;
- 上线后 §九 的日检 SQL 连续 7 天为 0,才进入 P1。
---
## 九、上线后运营规则(人工账单必须 100% 可监控)
### 9.1 角色与时限
| 动作 | 角色 | 时限 | 系统要求 |
| --- | --- | --- | --- |
| 人工单录成本 | 客服本人 | 支付后 24 小时内 | 自动带入本人备用金账户;逐单实际金额 |
| 代录 | 管理员/财务 | 同上 | 必须明确选择客服备用金账户 |
| 日检 | 客服主管 | 每日 10:00 | 未分类、无成本、无备用金关联均为 0 |
| 周对账 | 财务 | 每周一 | 人工支出 vs 备用金流水金额一致 |
| 月结 | 财务负责人 | 每月 3 日前 | 上线后区间零缺口才允许关账 |
### 9.2 每日必查
1. 上线日之后支付的订单项,超过 24 小时仍无 `fulfillment_mode`:**0**。
2. `manual` 订单项无有效成本快照:**0**。
3. `manual_expense_records.fund_expense_id IS NULL`(上线后):**0**。
4. `manual_expense_records.amount_fen <> manual_expenses.amount_fen`:**0**。
5. 人工项仍挂 `tpl:*` 商品的数量:P1 前允许存在,P1 后应改为 `man:*` 或经业务确认保留。
### 9.3 异常处理
- 发现人工单无成本:当天补录;补录仍走备用金扣款,不允许直接改余额。
- 备用金余额不足:拒绝登记,由财务充值后再录;不允许负余额。
- 同一订单项多笔支出:允许累加(渠道费、加急费、补差价),但每笔都必须有独立备用金流水。
- 历史人工单:标记 `historical_missing`,不补录、不计入上线后缺口。
### 9.4 历史样本校准(只做参考)
- 每个来源抽 20–30 单,覆盖低价/中价/高价;
- 只用于估算历史人工成本区间与毛利率,不写 `cost_snapshots`;
- 样本结论单独记录,禁止回填成逐单成本;
- 历史平台 53,901 项同理,只有拿到旧平台结算数据才允许按 `legacy_cost` 入账。
---
## 十、常见错误(防回归)
| # | 错误 | 后果 | 正确做法 |
| --- | --- | --- | --- |
| 1 | 把“有快手核销码”当成 `voucher_only` | 91 人工单全部从订单页消失 | 核销码只做状态展示 |
| 2 | 按 `provider=91kaquan/douyin_mashida` 直接判自动发货 | 人工单被藏进自动发货 | 只看工单/任务/历史任务 |
| 3 | 用 `product_catalog.carrier_type` 反推履约方式 | 11,349 个人工项被误认为接单平台 | 只读 `order_items.fulfillment_mode` |
| 4 | 只加 `order_items` 字段,不改四张表的 CHECK | 写 `legacy_platform` 时运行时报错 | 按 §8.1 同时放宽 |
| 5 | 只回填已支付历史单,不做增量兜底 | 新单长期停在 `NULL` | 24 小时定时归集 + 异常队列 |
| 6 | 把“人工 91,606”当成“人工全量” | 与 102,955 对不上,报表互相矛盾 | 写清“全量 102,955 / 未归一 91,606” |
| 7 | 把 `manual_expenses` 当公司人工支出表 | 两套语义混用,无法对账 | 用 `manual_expense_records` + `fund_expense_id` |
| 8 | 用均价、工时、固定单价补历史人工 | 违背逐单实际金额,毛利失真 | 历史不补录,只做样本区间 |
| 9 | 只改订单页,不改成本/销售事实 | 三处口径继续分叉 | 同一字段、同一决策入口 |
| 10 | 在旧文档里保留“三块/按小时/公司直付”旧结论 | 下次开发又按旧口径实现 | 旧文只留追溯标记,现行结论只写本文件 |
---
## 十一、文档地图与维护规则
| 文档 | 唯一职责 | 状态 |
| --- | --- | --- |
| **本文件** | 履约/成本/人工口径、实施顺序、验收 SQL | **现行唯一口径** |
| [成本中心与履约方式模型.md](成本中心与三块履约模型.md) | 商品、成本版本、快照、销售事实的数据模型 | 现行(文件名保留) |
| [人工支出登记落地方案-2026-10-17.md](人工支出登记落地方案-2026-10-17.md) | 人工支出字段、入口、审批、权限 | 现行(P0 已实施) |
| [客服备用金台账实施方案-2026-10-18.md](客服备用金台账实施方案-2026-10-18.md) | 备用金账户、流水、支出、编号 | 现行(P0 已实施) |
| [财务系统集成方案.md](财务系统集成方案.md) | `finance_*` 复式总账层 | 方案(未实施) |
| [影子总账说明.md](影子总账说明.md) | 影子总账概念、对账、转正 | 说明(未实施) |
| [履约配置/多发货平台与电子凭证整体链路.md](履约配置/多发货平台与电子凭证整体链路.md) | 进单、履约、领取、核销链路 | 现行 |
| [README.md](README.md) | 文档索引、分支与进度 | 现行入口 |
维护规则:
1. 履约分类只在本文定义;其他文档只链接,不复制判定 SQL。
2. 任何数字必须注明口径:全量 / 已归一 / 未归一 / 上线后。
3. 每次迁移后更新本文的代码基线、数据基线、§七 进度和 §8.2 数字。
4. 新文档必须登记到 `README.md`;过期文档移入 `docs/archive/`,不得在现行文档里保留冲突结论。
5. 不再使用“三块履约”作为分类名;`成本中心与三块履约模型.md` 只是历史文件名。
6. 代码评审必须检查:订单页、`sales-facts-service`、`cost-snapshot-sync-service` 是否都读同一字段。
---
## 十二、上线验收查询
> 以下 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 manual_expense_records
WHERE fund_expense_id IS NULL
AND created_at >= DATE '2026-11-01';
-- 5. 人工支出与备用金流水金额不一致:应为 0
SELECT r.id, r.expense_no, r.amount_fen, me.amount_fen
FROM manual_expense_records r
JOIN manual_expenses me ON me.id = r.fund_expense_id
WHERE r.amount_fen <> me.amount_fen
AND r.created_at >= DATE '2026-11-01';
-- 6. 人工单未归一商品:P1 完成后应为 0
SELECT COUNT(*)
FROM order_items oi
JOIN orders o ON o.id = oi.order_id
WHERE oi.fulfillment_mode = 'manual'
AND oi.canonical_product_id IS NULL
AND COALESCE(o.paid_at, o.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.carrier_type = 'work_order';
```
@@ -0,0 +1,125 @@
# 财务/履约人工块修复总结(交接用)
> 状态:**交接摘要**。详细口径、迁移 SQL、代码清单与验收查询以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。
> 代码基线:`dev @ fac34c84 + 迁移 103–113`。
> 数据基线:本地生产快照克隆,数据截至 2026-10-14。
> 分支:`dev` 领先 `main` 20 个提交;迁移 `103–113` 未部署生产;本轮只做本地提交,暂不推送。
---
## 一、这次到底修了什么
1. **人工成本改为逐单、按实际金额**:订单页「人工录单」直接关联订单项,不再按小时、固定单价或固定单量估算。
2. **人工支出改为从客服备用金扣款**:登记时同一事务扣余额、写 `fund_entries`、写 `manual_expenses`、回填 `manual_expense_records.fund_expense_id`。
3. **客服只能用自己的备用金账户**:客服角色默认自动带入本人账户,传别人账户后端直接 403;管理员/财务可代录。
4. **备用金账户编号可读**:统一为 `FA-<大写用户名>`,重名追加 `-2/-3`;迁移 `113` 已回填本地历史账户。
5. **订单页只展示平台判断 SKU**:人工录单弹窗不再混入内部规格文本。
## 二、关键事实(先记住这几个数)
| 履约方式 | 订单项 | 收入 | 当前成本 |
| --- | ---: | ---: | --- |
| `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 万** | 成本 ¥950.27 万 |
两个容易写错的数字:
- **人工全量 = 102,955 项**;
- **人工未归一 = 91,606 项**(另外 11,349 项挂在 `tpl:*` 模板商品,只表示 SKU 命中,不代表有工单)。
## 三、电子凭证的正确位置
快手电子凭证只是**核销码**,不是履约方式,也不是成本类型。
真实数据:91/快手人工单 100,618 项,**全部有凭证记录**(`CONSUMED` 87,310 / `DESTROYED` 13,247 / `UNUSED` 61),但它们仍然是人工单。
旧代码的根因:
```text
vouchers.length > 0 || provider === '91kaquan' -> voucher_only
provider === 'douyin_mashida' -> 自动发货
```
这两条兜底会把人工单从订单页隐藏。P0 必须删除,只读 `order_items.fulfillment_mode`。
## 四、现在不要做什么
- 不要补录历史人工支出:历史只取 20–30 单/来源做区间校准,不写 `cost_snapshots`。
- 不要用均价、工时或固定单价补齐人工成本。
- 不要把 `manual_expenses` 改名或复用为 `manual_expense_records`。
- 不要用 `product_catalog.carrier_type` 反推履约方式。
- 不要在 `fulfillment_mode` 落地前做人工商品归一或总账。
- 不要把 `legacy_platform` 混回人工。
## 五、下一步只做 P0:统一履约事实
完整 SQL 与代码清单见 [总纲 §八](财务与履约口径总纲-2026-10-23.md#八p0-开工清单统一履约事实)。
1. 新增迁移 `114_order_item_fulfillment_mode.sql`:三列 + 索引 + 放宽四张表的 `carrier_type` CHECK(`product_catalog`、`cost_items`、`cost_snapshots`、`sales_daily_facts`)。
2. 单独跑回填脚本(不要塞进启动迁移),按 `work_order → affiliate_dash → legacy_platform → manual` 填 316,585 项。
3. 改三处判定:订单页、`sales-facts-service.ts`、`cost-snapshot-sync-service.ts`,全部只读 `fulfillment_mode`。
4. 新增统一决策服务:工单/任务状态变化时写模式;24 小时兜底归集未分类订单项。
5. 前端增加 `legacy_platform`,核销码改成独立标签,人工按钮只认 `manual`。
6. 跑后端/前端 `npm run check`,核对总纲 §8.2 四项数字,再连续观察 7 天日检。
## 六、上线后运营(硬要求)
- 人工单支付后 **24 小时内**必须录成本;
- 客服只能用自己的备用金账户,余额不足直接拒绝;
- 每日 10:00 检查:
- 超过 24 小时仍未分类的订单项 = 0;
- 人工单无成本 = 0;
- 人工支出无 `fund_expense_id` = 0;
- 人工支出与备用金流水金额不一致 = 0;
- 上述任一不为 0,**不允许关账**;
- 历史人工单标记 `historical_missing`,不计入上线后缺口。
## 七、验证命令
```bash
# 后端
cd apps/backend && npm run check
# 前端
cd apps/frontend && npm run check
# 文档相对链接(在仓库根目录)
python3 - <<'PY'
import os,re
files=[]
for base,_,names in os.walk('docs'):
for n in names:
if n.endswith('.md'): files.append(os.path.join(base,n))
missing=[]
for f in files:
text=open(f,encoding='utf-8').read()
for m in re.finditer(r'(?<!\!)\[[^\]]*\]\(([^)]+)\)',text):
link=m.group(1).strip()
if link.startswith(('http://','https://','mailto:','#')): continue
p=link.split('#')[0]
if p and not os.path.exists(os.path.normpath(os.path.join(os.path.dirname(f),p))):
missing.append((f,link))
print('missing',len(missing))
for x in missing: print(x)
PY
```
## 八、文档阅读顺序
1. [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md):唯一口径 + P0 执行清单。
2. [成本中心与履约方式模型.md](成本中心与三块履约模型.md):商品、成本版本、快照、销售事实。
3. [人工支出登记落地方案-2026-10-17.md](人工支出登记落地方案-2026-10-17.md):人工录单字段、审批、权限。
4. [客服备用金台账实施方案-2026-10-18.md](客服备用金台账实施方案-2026-10-18.md):备用金账户与流水。
5. [财务系统集成方案.md](财务系统集成方案.md) / [影子总账说明.md](影子总账说明.md):后续总账层。
6. [README.md](README.md):文档索引与分支状态。
## 九、交接注意
- 生产部署脚本 `deploy/ubuntu-deploy.sh` 不会自动 `git pull`;部署前必须先拉代码。
- 生产域名变更要同步 DCAuth `appRegistry`,否则客户端会报“不允许访问”。
- 成本映射的 `provider` 用 `orders.provider = 91kaquan`,不是匹配层的 `kuaishou-91`。
- 显式单号检索必须忽略排他筛选,这是前后端共同约定。
- Git 操作必须先获得用户确认;当前只保留本地提交,不推送、不部署。
+31 -24
View File
@@ -1,10 +1,10 @@
# 财务系统集成方案
> 状态:**现行方案(总账层未实施)**。本文描述 `finance_*` 总账层;当前 `finance_*` 尚未建表,现有财务页仍是 `UNION ALL` 业务事件拼装。商品、成本中心、销售事实与公司人工支出登记已完成 P0。
> 最后校准:2026-10-22
> 代码基线:`dev @ 15989af8 + 财务 108/109/110/111`
> 前置:商品主数据、渠道 SKU 映射、成本中心、销售事实与公司人工支出登记必须先完成;这些前置已完成 P0。实施顺序以 [README.md](README.md) §三 为准。
> 目标:在**不破坏现有抢单、押金、结算、寄卖、提现链路**的前提下,为 order_site 增加一套可对账、可关账、可追溯的财务系统。
> 状态:**现行方案(总账层未实施)**。本文只描述 `finance_*` 总账层;当前总账表尚未建立,现有财务页仍是 `UNION ALL` 业务事件拼装。
> 最后校准:2026-10-23
> 代码基线:`dev @ fac34c84 + 迁移 103–113`
> **履约方式、成本模型、人工全覆盖口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准**;本文不重复分类设计。
> 目标:在**不破坏现有抢单、押金、结算、寄卖、提现链路**的前提下,增加可对账、可关账、可追溯的复式记账底座。
> 结论先说:**不要直接在 `worker_wallets` / `worker_wallet_ledgers` 上继续加字段。** 现有表是业务钱包与流水,不是会计总账;正确做法是新增复式记账底座,让现有钱包成为它的“余额投影”。
>
> 如果只想知道“影子总账”是什么、为什么先跑影子账:先读 [影子总账说明.md](影子总账说明.md)。
@@ -15,15 +15,16 @@
| 能力 | 状态 |
| --- | --- |
| 客服备用金台账(`fund_*` / 备用金 `manual_expenses`) | **已实施 P0**,见 [客服备用金台账实施方案](客服备用金台账实施方案-2026-10-18.md) |
| 商品主数据 / 渠道 SKU 映射 / 成本中心 | **已实施 P0(`dev` 分支)**,迁移 `103/104/105/106/107/108`,含商品履约载体、自动发货价格版本与成本快照运行层,见 [成本中心与三块履约模型](成本中心与三块履约模型.md) |
| `sales_daily_facts` / 订单级毛利 | **已实施 P0(`dev` 分支)**,迁移 `109`;后台「销售毛利」页按店铺/商品/载体汇总,未归一商品单独成行 |
| 公司人工支出登记 `manual_expense_records` | **已实施 P0(`dev` 分支)**,迁移 `111`;后台「人工支出」页支持登记、审批、驳回、报销,通过后写 `cost_snapshots` |
| 客服备用金台账(`fund_*` / 备用金 `manual_expenses`) | **已实施 P0**;账户编号 `FA-<用户名>`;客服只能使用本人账户 |
| 商品主数据 / 渠道 SKU 映射 / 成本中心 | **已实施 P0(`dev` 分支)**,迁移 `103/104/105/106/107/108/110/112`;标准商品 745 个,只覆盖自动发货 38 + 接单平台 707 |
| `sales_daily_facts` / 订单级毛利 | **已实施 P0(`dev` 分支)**,迁移 `109`;94 天收入 ¥2,650.87 万、成本 ¥950.27 万 |
| 公司人工支出登记 `manual_expense_records` | **已实施 P0(`dev` 分支)**,迁移 `111`;登记时强制扣客服备用金并写 `fund_expense_id` |
| 履约方式统一 `fulfillment_mode` | **未实施**;订单页、成本快照、销售事实仍各自推断 |
| `finance_accounts` / `finance_business_events` / `finance_journal_*` | 未实施 |
| `finance_settlements` / `finance_reconciliations` | 未实施 |
| 现有 `admin-finance-service.ts` | 仍是业务事件 `UNION ALL`,不是总账 |
**不要复用客服备用金的 `manual_expenses` 作为公司人工支出表。** 两者口径不同;公司人工支出应使用 `manual_expense_records`,并通过 `fund_expense_id` 关联备用金支出。
**不要复用客服备用金的 `manual_expenses` 作为公司人工支出表。** 公司人工支出使用 `manual_expense_records`,并通过 `fund_expense_id` 关联备用金支出;新登记会先扣备用金。
---
@@ -73,27 +74,31 @@
| 寄卖货款结算 | `consignment-deposit-repo.ts` | 采购成本/存货 → 应付卖家 |
| 平台服务费/通道费 | 目前未单独建模 | 销售费用/财务费用 |
### 1.4 三块履约与成本中心的前置关系
### 1.4 履约方式与成本中心的前置关系
财务总账不是孤立存在的,它需要先知道“这笔钱属于哪个商品、哪个店铺、哪个执行载体、用了哪个成本版本”:
财务总账需要先知道“这笔钱属于哪个商品、哪个店铺、哪种履约方式、用了哪个成本版本”:
| 块 | 载体 | 成本来源 | 财务侧 |
| 履约方式 | 实现 | 成本模型 | 财务侧 |
| --- | --- | --- | --- |
| 自动发货 | `affiliate_dash` | 上游固定 `cost_amount` → `cost_versions` | 采购成本 |
| 接单平台 | `work_orders` | 模板价格 → `cost_versions` / 工单实际报酬快照 | 履约成本 + 押金负债 |
| 人工客服 | `manual_fulfillment_cases` | 按单/按小时人工成本 | 人工成本 + 履约完成 |
| 自动发货 | `affiliate_dash` | 上游固定成本 → `cost_versions` | 采购成本 |
| 接单平台 | `work_orders` | 模板规则版本 / 工单实际报酬 | 履约成本 + 押金负债 |
| 历史平台 | `ct_assisted` / `feifei`(已下线) | 历史成本未采集则单列 | 仅历史报表 |
| 人工 | 无工单、无自动发货任务、无历史平台任务 | **逐单实际金额**,从客服备用金扣款 | 人工成本 + 备用金 |
电子凭证只是核销码,不进入上述分类。
因此建议顺序是:
```text
商品主数据 + 渠道SKU映射
统一履约事实(fulfillment_mode + 成本模型)
-> 商品主数据 + 渠道SKU映射
-> 成本中心(cost_items / cost_versions / cost_snapshots)
-> 销售事实(sales_daily_facts)
-> 影子总账(finance_*)
-> 对账 / 转正
```
详细设计见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md)。
详细设计见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 与 [成本中心与履约方式模型.md](成本中心与三块履约模型.md)。
---
@@ -433,11 +438,11 @@ POST /periods/:key/close
## 六、总账层分期
> 项目级顺序以 [README.md](README.md) §三 为准;本节只描述“总账层”内部阶段。
> 进入 P0 前应已完成:商品主数据、渠道 SKU 映射、成本中心、销售事实、公司人工支出登记(`manual_expense_records`)。
> 进入 P0 前应已完成:统一履约事实、商品主数据、渠道 SKU 映射、成本中心、销售事实、公司人工支出登记(`manual_expense_records`)。
### P0:影子总账(1–2 周)
> 前置:先完成商品主数据、渠道 SKU 映射、成本中心与销售事实(见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md))。否则影子总账只能记“总额”,无法按店铺/皮肤/成本版本出毛利。
> 前置:先完成统一履约事实、商品主数据、渠道 SKU 映射、成本中心与销售事实(见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md))。否则影子总账只能记“总额”,无法按店铺/皮肤/成本版本出毛利。
- 建 `finance_accounts / business_events / journal_entries / journal_lines / periods`。
- 先接高频且核心的事件:订单支付、工单结算、押金冻结/释放/没收、提现,以及公司人工支出的 `approved / reimbursed`。
@@ -490,7 +495,7 @@ POST /periods/:key/close
## 八、需要业务拍板的问题
> 2026-10-17 已确认 4 条口径,2026-10-22 追加确认收入时点与未归一商品口径;明细见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md) §11。
> 2026-10-17 已确认 4 条口径,2026-10-22/23 追加确认收入时点、人工支出与电子凭证口径;明细见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md)。
| # | 问题 | 状态 | 影响 |
| --- | --- | --- | --- |
@@ -501,9 +506,11 @@ POST /periods/:key/close
| 5 | 寄卖货款的采购成本确认时点 | ⏳ 待定 | 决定存货/成本 |
| 6 | 提现通道费、平台服务费由谁承担 | ⏳ 待定 | 决定费用科目与结算 |
| 7 | 退款、拒付、仲裁的冲减规则 | ✅ **已确认:暂未接入,先预留**(字段 + `reversal_of_id` + 事件类型,不实现逻辑) | 决定售后与收入冲回 |
| 8 | 人工支出如何入账 | ✅ **已确认:以「人工支出登记」为入口**(付款项目/金额/订单号/付款人/凭证),SLA 监管延后 | 决定人工成本科目与总账事件 |
| 9 | 是否需要发票/税务 | ⏳ 待定 | 决定发票表与税额 |
| 10 | 是否多币种 | ⏳ 待定(当前 CNY) | 若否则需汇率与汇兑损益 |
| 8 | 人工支出如何入账 | ✅ **已确认:逐单实际金额,从客服备用金扣款,客服只能用本人账户**;历史人工支出不补录 | 决定人工成本与备用金 |
| 9 | 电子凭证是否作为履约/成本分类 | ✅ **不参与分类**;电子凭证只是快手核销码 | 决定报表口径 |
| 10 | 历史人工支出 | ✅ **不补录**,只取少量样本校准;上线后人工单必须 100% 有成本记录 | 决定上线范围 |
| 11 | 是否需要发票/税务 | ⏳ 待定 | 决定发票表与税额 |
| 12 | 是否多币种 | ⏳ 待定(当前 CNY) | 若否则需汇率与汇兑损益 |
---
+6 -5
View File
@@ -1,6 +1,7 @@
# order_site 项目全量分析报告(2026-10-17)
> 状态:**参考(2026-10-17 全量分析快照)**。架构与风险结论可参考;财务、备用金、显示名等实施状态以 [README.md](README.md) 为准。
> 状态:**历史参考(2026-10-17 全量分析快照)**。架构与风险结论可参考;财务、履约分类、人工支出、备用金等口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准。
> 本文中的“三块履约”“按小时人工成本”“公司直付”等表述已过期,不得作为实现依据。
> 代码基线:`main @ 6850bc64`(分析当时;当前基线见 README)
> 复核范围:后端、前端、数据库迁移、部署脚本、现行文档
> 复核方式:源码走查 + 类型检查 + Lint + 格式检查 + 全量测试 + 生产构建 + 2026-10-17 生产库只读聚合(`default_transaction_read_only=on`)
@@ -160,7 +161,7 @@ pending_material → unassigned → open → in_progress
- 同一个商品名对应多个 `sku_code`:「指挥官密钥1个」27 个、「苏尔南钻石100颗」35 个,不能直接按 `sku_code` 做销量统计。
- 当前 30 天订单基本是一单一品,但模型必须按订单项设计。
详细设计见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md)。
详细设计见 [成本中心与履约方式模型.md](成本中心与三块履约模型.md)。
---
@@ -249,7 +250,7 @@ pending_material → unassigned → open → in_progress
- 匹配引擎:P0 + P1 已实施,P2/P3 待办。
- 抖音三层分流:抖音侧已实施。
6. 校正过期测试数:抖音总览中的 636/115 改为 2026-10-17 复核的 740/143。
7. 2026-10-17 增补:新增 [成本中心与三块履约模型.md](成本中心与三块履约模型.md),把自动发货/接单平台/人工客服三块、统一成本中心、销售事实与人工 case 纳入现行设计;本报告同步补入生产只读数据与 C1~C5 风险。
7. 2026-10-17 增补:新增 [成本中心与履约方式模型.md](成本中心与三块履约模型.md),把自动发货/接单平台/人工客服三块、统一成本中心、销售事实与人工 case 纳入现行设计;本报告同步补入生产只读数据与 C1~C5 风险。
仍保留为“设计/待办”的内容:
@@ -278,7 +279,7 @@ pending_material → unassigned → open → in_progress
5. 余额是可变字段,缺少“由流水重算”的校验。
6. 资金事件缺少统一的 `business_event_id` 幂等键。
建议:先建**影子总账 + 对账**,不要一开始替换钱包。成本金额与商品维度单独由成本中心负责,人工块由人工 case 负责;三者关系见 [成本中心与三块履约模型.md](成本中心与三块履约模型.md),总账细节见 [财务系统集成方案.md](财务系统集成方案.md) 与 [影子总账说明.md](影子总账说明.md)。
建议:先建**影子总账 + 对账**,不要一开始替换钱包。成本金额与商品维度单独由成本中心负责,人工块由人工 case 负责;三者关系见 [成本中心与履约方式模型.md](成本中心与三块履约模型.md),总账细节见 [财务系统集成方案.md](财务系统集成方案.md) 与 [影子总账说明.md](影子总账说明.md)。
---
@@ -297,7 +298,7 @@ pending_material → unassigned → open → in_progress
2. `/collect`、`/redeem` 增加一次性 token/二次校验;上传前校验订单存在。
3. 生产强制 `CLAIM_TOKEN_TTL_HOURS > 0`;私有文件短期签名;补安全响应头。
4. 先落商品主数据 + 销售事实:`product_catalog`、`channel_product_mappings`、`sales_daily_facts`,解决店铺/日期/皮肤销量与数量归一。
5. 再落成本中心影子期:`cost_items / cost_versions / cost_snapshots`,自动发货接上游固定成本,工单接现有规则金额,人工接按单/按小时成本。
5. 再落成本中心影子期:`cost_items / cost_versions / cost_snapshots`,自动发货接上游固定成本,工单接现有规则金额,人工**逐单按实际金额**(旧稿的“按单/按小时”已作废,见 [总纲](财务与履约口径总纲-2026-10-23.md))。
### 中期(1 个月)