diff --git a/docs/README.md b/docs/README.md index 48289c82..5199639d 100644 --- a/docs/README.md +++ b/docs/README.md @@ -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:`(不建渠道映射),接单平台按工单模板回填商品并为 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 自动履约接口与字段 | diff --git a/docs/人工支出登记落地方案-2026-10-17.md b/docs/人工支出登记落地方案-2026-10-17.md index 2128b07c..cbf9cbcf 100644 --- a/docs/人工支出登记落地方案-2026-10-17.md +++ b/docs/人工支出登记落地方案-2026-10-17.md @@ -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 | 电子凭证是否作为分类? | **不作为履约/成本分类**;只是快手核销码状态 | --- diff --git a/docs/客服备用金台账实施方案-2026-10-18.md b/docs/客服备用金台账实施方案-2026-10-18.md index d6c04b27..fee025af 100644 --- a/docs/客服备用金台账实施方案-2026-10-18.md +++ b/docs/客服备用金台账实施方案-2026-10-18.md @@ -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 客服缴回 客服或主管发起缴回,主管确认后: diff --git a/docs/履约配置/多发货平台与电子凭证整体链路.md b/docs/履约配置/多发货平台与电子凭证整体链路.md index 7697cccd..cf049516 100644 --- a/docs/履约配置/多发货平台与电子凭证整体链路.md +++ b/docs/履约配置/多发货平台与电子凭证整体链路.md @@ -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. 目标链路 diff --git a/docs/影子总账说明.md b/docs/影子总账说明.md index 2afa4366..3cc03904 100644 --- a/docs/影子总账说明.md +++ b/docs/影子总账说明.md @@ -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_*` 表、分录、对账、转正均未实施。人工支出现在强制走客服备用金;历史人工支出不补录,上线后必须全覆盖。 --- diff --git a/docs/成本中心与三块履约模型.md b/docs/成本中心与三块履约模型.md index 0738db1e..f47e74ac 100644 --- a/docs/成本中心与三块履约模型.md +++ b/docs/成本中心与三块履约模型.md @@ -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` 一个地方。电子凭证只是核销码,不是分类。** diff --git a/docs/接单模板匹配引擎重构设计.md b/docs/接单模板匹配引擎重构设计.md index 5e97e6db..09ad3546 100644 --- a/docs/接单模板匹配引擎重构设计.md +++ b/docs/接单模板匹配引擎重构设计.md @@ -1,6 +1,6 @@ # 接单模板匹配引擎重构设计 -> 目标读者:后端 / 后台运营。状态:**P0 + P1 已实施;P2 / P3 待办**。最后校准:2026-10-18(`main @ 7542d547`;仅基线更新,业务逻辑未变)。 +> 目标读者:后端 / 后台运营。状态:**P0 + P1 已实施;P2 / P3 待办**。最后校准:2026-10-23(代码基线 `dev @ fac34c84 + 迁移 103–113`;业务逻辑未变)。 > > 一句话:把「订单信息 → 接单模板(价格/拼单/要求)」的匹配,从散落的多个消费点、平台判断和"店铺必填"的绑定表里,收敛为**一个平台无关的引擎**:匹配条件完全由**订单信息**表达,**店铺只是可选条件之一**,新增来源只加一个适配器 + 一组配置。 > diff --git a/docs/财务与履约口径总纲-2026-10-23.md b/docs/财务与履约口径总纲-2026-10-23.md new file mode 100644 index 00000000..90133db0 --- /dev/null +++ b/docs/财务与履约口径总纲-2026-10-23.md @@ -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:`;不得因为复用了 `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:` 商品,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:` | **未实施** | 本文 §八、§九 | +| 退款冲减、异常闭环 | **未实施** | 本文 §六 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'; +``` diff --git a/docs/财务履约改造交接总结-2026-10-23.md b/docs/财务履约改造交接总结-2026-10-23.md new file mode 100644 index 00000000..cfe7615a --- /dev/null +++ b/docs/财务履约改造交接总结-2026-10-23.md @@ -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'(? 状态:**现行方案(总账层未实施)**。本文描述 `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) | 若否则需汇率与汇兑损益 | --- diff --git a/docs/项目全量分析报告-2026-10-17.md b/docs/项目全量分析报告-2026-10-17.md index 66994e5d..7d7eff19 100644 --- a/docs/项目全量分析报告-2026-10-17.md +++ b/docs/项目全量分析报告-2026-10-17.md @@ -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 个月)