Files
order_site/docs/人工支出登记落地方案-2026-10-17.md
T

19 KiB
Raw Blame History

人工支出登记落地方案(2026-10-18 修订)

状态:现行方案(未实施) 最后校准:2026-10-18 代码基线:main @ 7542d547 依据:业务口径「人工块当前不以 SLA 监管为目标,先把流程搞清楚:哪个客服处理了哪些东西、付了多少钱、什么项目、关联哪个订单」。 数据来源:本地只读克隆(生产快照,数据截至 2026-10-14)+ 现有代码盘点。 重要:客服备用金支出已经实施(manual_expenses),但本文的公司人工支出登记(manual_expense_records)尚未实施;两张表不是一回事,不能直接复用或改名。 相关文档:成本中心与三块履约模型.md §8 · 财务系统集成方案.md §八 · 客服备用金台账实施方案 · DDL 草案 drafts/2026-10-17-人工支出登记-DDL草案.sql


〇、先分清两张表

表 业务含义 当前状态 关键字段
manual_expenses 客服备用金支出:客服从自己的备用金账户里充值/采购/赔偿 已实施 account_id / fund_entry_id / category / payee_* / evidence_json
manual_expense_records 公司人工支出/垫付登记:按订单/成本项记录公司实际付出去的钱 仅草案 order_id / order_item_id / cost_item_id / paid_by_admin_user_id / status / cost_snapshot_id

结论:

  1. 客服侧已经收口的是“备用金余额与用途”,解决“客服手里有多少钱、这笔钱花到哪”。
  2. 财务侧还缺的是“公司口径的实际支出/垫付”,解决“公司为哪个订单、哪个成本项、付给谁、谁垫的、有没有审批和凭证”。
  3. 两张表通过 fund_expense_id 关联:如果这笔公司支出来源于客服备用金,就填;公司直付则留空。
  4. 不要把 manual_expenses 直接改名为 manual_expense_records,也不要让两张表共用索引/约束名。

一、结论先行

  1. 订单里的数据足够预填 90%:店铺、商品、规格、数量、用户实付、支付时间、平台单号、履约标签、状态,后台订单列表/详情已经全部具备。客服实际只需要补 4 件事:渠道/项目、金额、收款方、凭证(外加默认带出的「谁、什么时候」)。
  2. 现有财务页没有“支出”这一侧。财务页现有 7 个账目类别(订单收款、订单退款、打手报酬、打手提现、打手充值、打手资金回收、售后追缴退还)全部是收款或打手侧资金;客服备用金支出已经落在 fund 模块,但还没有汇总进财务页,也没有公司口径的人工支出/垫付登记。
  3. 落地成本低、风险小:不需要改订单主表,不需要新角色体系(现有 admin/finance/operator/support 四角色 + finance.* 权限即可覆盖),只需新增「公司人工支出登记」表 + 订单侧入口 + 财务侧新类别。
  4. **登记必须“发生在垫付当天”**才有监管价值;事后补记只能算账,不能算监管。
  5. 本轮只补公司口径:客服备用金台账已完成,不重复做一套客服余额系统;通过 fund_expense_id 关联即可。

二、现状盘点(逐字段,来自代码而非猜测)

2.1 订单已经带了什么(可直接预填)

订单列表行数据由 mapAdminOrderListItem 组装(apps/backend/src/services/admin/admin-order-read-helpers.ts:17),字段与截图中看到的元素一一对应:

界面元素(截图) 数据字段 来源 支出登记用途
快手 / 91卡券 provider / platform 订单表 自动带出,决定默认渠道选项
2629001926827116 platformOrderId 订单表 对账主键,必带
小鱼儿游戏服务店 shopName / shopId 订单表 统计维度(店铺)
和平精英…雪山精灵兔 itemTitle / items[] 订单项 + 快照 商品/皮肤维度
指挥客密钥1个、(2进1)暗夜风云/蓝调溯影 skuNick / skuCode 订单项 规格维度(注意口径问题见 §7.4)
数量 1 · 关联工单 1 totalQuantity / workOrderCount 订单项 / 工单 数量与载体提示
¥ 10.30 CNY totalAmount / totalAmountFen 订单表 用户实付(= 收入),用于算毛利
2026-10-17 22:24:21 paidAt / createdAt 订单表 支出时间不能早于支付时间
打手履约 / ⚡自动直发 / 🎫电子凭证 fulfillmentType + workOrderCount 派生 提示该单走哪条链路
已支付 payStatus 订单表 只有已支付才允许登记支出
待准备资源 / 未开始 resourceStatus / customerStatus 任务/工单派生 提示当前进度
(未在列表显示)操作人 后台会话 userId / username / role admin_sessions 默认「登记人」

补充事实:克隆库里后台账号 29 个,其中 support(客服)20 个,finance(财务)角色独立存在 —— 登记人群与审批人是现成的。

说明:截图上的标签文案在代码里是「待准备资源」(resourceStatus = pending_prepare)、「未开始」(customerStatus = not_started),见 apps/frontend/src/components/admin/StatusTag.tsx:39,48。

2.2 订单里没有的(必须人工补,这也是登记表的全部价值)

# 缺失信息 为什么订单里不可能有
1 实际走哪个渠道/找谁履约 订单只记录平台来源(快手/PDD/抖音),不记录我方线下找的上游、打手、代购
2 实际付了多少钱 订单只有用户付给我们的钱,没有我们付出去的钱
3 收款方(微信号/支付宝/银行卡/账号/昵称) 完全在系统外
4 凭证(转账截图、聊天记录、订单截图) 完全在系统外
5 实际垫付人 可能与登录账号不同(主管代付、他人代垫)
6 备注/原因(为什么赔、加急、补差价) 无结构可依

2.3 现有财务流水的类别缺口(关键发现)

AdminFinancePage.tsx:39 的账目类别:

订单收款(order_paid) · 订单退款(order_refund) · 打手报酬(worker_reward) · 打手提现(withdraw_paid)
打手充值(recharge) · 打手资金回收(worker_recovery) · 售后追缴退还(after_sales_refund)

没有任何一类描述“我方公司支出”。后端 admin-finance-service.ts 的 allowedCategories 同样只有这 7 类,且方向判定只把 order_paid / recharge / worker_recovery 视为流入。客服备用金页已有自己的 manual_expenses 明细,但那是 fund 口径,不会自动进入财务页;即使今天记了公司人工支出,财务页也没有地方落。

结论:人工支出登记必须同时补「财务流水类别」,否则只是把数据存进了另一张看不见的表。


三、登记模型:以订单为入口,人工只做最小输入

3.1 字段级分工(自动带出 vs 人工填写)

字段 谁提供 必填 说明
订单号 / 订单项 自动(订单) 是(无订单支出除外) 从订单详情/列表发起时锁定
客服备用金来源 人工关联 否 若这笔钱从客服备用金出,关联 manual_expenses.id;公司直付留空
平台 / 店铺 / 商品 / 规格 / 数量 自动(订单) 是 只读展示,避免手抄错
用户实付 自动(订单) 是 只读,用于毛利
支付时间 自动(订单) 是 校验支出时间不早于它
付款项目(渠道/成本项) 人工选 是 对应 cost_items,例如「上游拿货-A 渠道」「代练打手」「平台充值」;没有可选项时走"新建项目"审批流
支出金额 人工填 是 单位分;> 阈值触发二级审批
收款方类型 + 名称 人工填 是 打手 / 上游 / 客户 / 平台 / 其他
收款账号 人工填 否 微信/支付宝/银行卡号,用于事后核对
付款方式 人工选 是 微信 / 支付宝 / 银行 / 现金 / 平台余额
付款时间 人工填 是 默认当前时间,可回填(当天)
凭证 人工传 是 转账截图/聊天记录,支持多张
登记人 + 登记时间 自动(会话) — 默认登录账号,可改(代付场景)
备注 / 原因 人工填 否 赔偿、差价、加急等必须写
报销状态 流程 — 待审 / 已通过 / 已驳回 / 已报销

3.2 两种登记形态都要支持

形态 场景 订单号 占比预估
有订单支出 客服看到订单 → 去别的渠道拿货/找打手 → 登记 必填 绝大多数
无订单支出 平台充值、批量拿货、客户赔偿、运费、办公 允许为空 少数但必须支持,否则这部分钱永远查不到

无订单支出必须单独可见(财务页筛选项 + 异常队列统计),不能混进订单支出里被平均掉。

3.3 一对多关系(必须允许)

  • 一个订单项可以有多笔支出:渠道费 + 加急费 + 补差价 → 逐笔登记,汇总到订单损益。
  • 一笔转账覆盖多个订单(例如一次拿货 5 单)→ 建议拆成 5 条明细,用 expense_batch_no 归组(可选字段),避免把成本摊错订单。
  • 实际支出 vs 标准成本:cost_snapshots 记的是标准成本(成本中心算出来的),人工支出记的是实际发生。二者要能对比,差异就是"人工议价空间/异常"的信号 —— 这是后续做成本优化的关键数据,不要在实现时用其中一个覆盖另一个。

四、交互落地(三个入口,一套数据)

4.1 订单列表(就是截图这一页)

  • 行操作加一个「登记支出」按钮(放在「详情」旁边;现在该位置只在自动直发时显示 H5 链接,不冲突)。
  • 新增一列/一个标签:本单已登记支出合计(无则留空)。让主管扫一眼就能看出哪些单垫了钱、垫了多少。
  • 筛选条件增加:有/无支出登记、支出待审批 —— 这是主管抽查与财务对账的入口。

4.2 订单详情页

AdminOrderDetailPage.tsx 现有四张卡片:基础信息 / 商品明细 / 任务 / 原始载荷。建议在商品明细与任务之间插入:

┌ 人工支出 ────────────────────────────────┐
│ 合计 ¥XX.XX(已通过) / 待审 ¥XX.XX        │
│ ┌────────┬──────┬────────┬──────┬──────┐ │
│ │项目     │金额  │收款方   │付款人 │状态  │ │
│ ├────────┼──────┼────────┼──────┼──────┤ │
│ │上游拿货 │¥8.50 │微信-xx  │客服A │待审  │ │
│ └────────┴──────┴────────┴──────┴──────┘ │
│ [+ 登记支出]                              │
│ 毛利预览:收入 ¥10.30 − 标准成本 ¥0 − 支出 ¥8.50 = ¥1.80 │
└──────────────────────────────────────────┘

「毛利预览」是这张卡片真正的管理价值:一眼看到这单是不是亏的。

4.3 财务页

  • 账目类别新增:人工支出(manual_expense)、人工垫付报销(manual_expense_reimburse)。
  • 新增「支出登记」页签:按 状态 / 客服 / 店铺 / 项目 / 日期 筛选,支持批量审批。
  • 汇总口径:谁付了多少钱(按登记人)、钱花在哪(按项目/店铺)、还有多少没报销。

4.4 时间要求(监管的生命线)

垫付当天登记。建议规则:

  • 超过 24 小时补登记 → 标记「迟登记」,进异常队列;
  • 超过 7 天 → 必须主管审批才能登记;
  • 未登记但有订单证据(如工单备注提到垫付)→ 系统提示补登。

理由:事后补记的金额与项目失真最快,而"当天登记"是可执行、可考核的硬约束。


五、状态机与权限(复用现有体系,不新增角色)

draft ──提交──> submitted ──审批──> approved ──报销──> reimbursed
                   │
                   └──驳回──> rejected ──修改后重提──> submitted
(预留)approved/reimbursed ──退款或仲裁──> reversed(反向登记,不改原记录)
动作 谁可以 现有能力
提交登记 客服 / 运营 / 管理员 requireAdminRoles(['admin','operator','support'])(routes/admin/orders.ts:14 已是这个组合)
审批 / 驳回 财务(或管理员) 角色 finance;权限点 finance.review 已存在
维护付款项目 财务 finance.config 已存在
查看支出 财务 / 管理员 finance.view 已存在
导出 财务 finance.export 已存在

注意:订单路由目前只做角色校验,没有权限点校验(routes/admin/orders.ts 全篇没有 requirePermission)。新增支出接口时建议按财务口径加权限点,避免客服看到全公司支出。

审计双留痕(现有约定即可满足):

  1. 路由层 createJsonHandler({ audit }) → 写 admin_audit_logs(admin-audit-service.ts:11);
  2. 业务层写 manual_expense_events(只追加,记录状态流转与操作人)。

不可变约束:approved 之后禁止 UPDATE 金额/项目/收款方;要改只能反向登记(reversal_of_id),这是为退款/仲裁预留的,也是财务能信这张表的前提。


六、校验与异常队列(不默认 0、不静默通过)

# 规则 不通过时
1 金额 > 0 拒绝提交
2 单笔 ≥ ¥5,000(阈值待定) 需管理员二级审批
3 凭证必传(至少 1 张) 拒绝提交
4 订单必须存在且 pay_status = paid 拒绝;无订单支出走独立形态
5 付款项目必选 允许暂存但不得 approved;进「未归类」队列
6 同订单 + 同项目 + 同金额 + 同收款方,24h 内重复 提示疑似重复,需填原因才可提交
7 付款时间不得晚于当前、不得早于订单支付时间 拒绝
8 迟登记(> 24h) 允许但标记,进异常队列
9 收款方为空/无意义(如「微信」) 提示补全

异常队列必须在财务页可见:未归类数、迟登记数、无订单支出占比、待审超期数。否则又会变成"看不见的账"。


七、与已有系统的衔接

7.1 载体判定(人工块显性化)

人工支出登记本身就是「这单是人工在做」的强证据。回填规则补充一条:存在 approved 人工支出的订单项,其 carrier_type 判定为 manual(与任务/工单存在性并列,冲突时以现有优先级为准并记异常)。

7.2 成本中心

  • cost_item_id 即"付款项目",与 cost_items(cost_type = 'manual_service' 或 'auto_purchase')打通;
  • approved 时写 cost_snapshots(source = 'manual_input'),与自动发货/工单的标准成本并列进入订单损益;
  • 匹配不到项目 → cost_exceptions,不默认 0。

7.3 影子总账(分录)

approved(垫付)    借:5102 履约成本-人工客服    贷:2005 其他应付-垫付人
reimbursed(报销)  借:2005 其他应付-垫付人   贷:1001 银行存款
reversed(预留)    反向登记 + 反向凭证,原凭证不动

幂等事件键:manual_expense:{expense_no}:approved|reimbursed|reversed。

7.4 报表口径的两个坑(已在别处确认,实施时必须遵守)

  1. 数量:order_items.quantity 不可信(316,585 项里 316,578 项 = 1,而名称带「N个」的有 104,710 项,按名称解析总量是它的 7.3 倍)→ 支出若要按件摊,必须用 normalized_quantity。
  2. 商品:同一商品名对应多个 sku_code(「苏尔南钻石100颗」47 个码)→ 必须经 channel_product_mappings 归一到 canonical_product_id。

八、在全局路线中的位置

全局顺序以 README.md §三 为准。本文只负责“公司人工支出登记”,不重复商品、成本中心、销售事实和影子总账的完整设计。

本文内部阶段 内容 前置
A. 登记表 + 入口 manual_expense_records / manual_expense_events、订单详情「登记支出」卡片、订单列表入口与标签 成本中心基础表已建;cost_item_id 可为空,但空值进异常队列
B. 审批 + 财务页 状态机、审批/驳回/报销、财务页新类别、「支出登记」页签、异常队列 A 完成
C. 成本快照 + 毛利 approved 写 cost_snapshots,订单损益预览 成本中心 cost_versions / cost_snapshots 可用
D. 总账入账 approved / reimbursed / reversed 生成影子总账事件 影子总账已落地

客服备用金支出已经完成 A 的一部分(记录钱和用途),但没有审批、成本项、成本快照和总账事件。 因此本轮不是重做客服台账,而是把公司口径补上并桥接。


九、待业务确认(实施前需拍板)

# 问题 建议默认
1 审批人是谁? 财务(finance 角色)审批,管理员兜底
2 单笔二级审批阈值? ¥5,000 起需管理员
3 区分「垫付」与「公司直付」吗? 需要:垫付要报销,公司直付只记账
4 必须支持无订单支出吗? 必须(平台充值/赔偿/运费无处可记)
5 允许一单多笔支出吗? 必须
6 凭证必须吗? 必须(且支持多张)
7 迟登记如何处理? 24h 内正常,超时标记,超 7 天需审批
8 是否与现有 7 类财务流水合并展示? 合并展示、分类筛选
9 客服备用金支出是否自动生成公司人工支出? 不自动;提供“从备用金支出带入”,人工确认后生成,避免重复入账
10 实际付款人必须等于登录人吗? 允许代付;实际付款人必填,默认登录人,便于按人日结对账
11 cost_item_id 为空能否提交? 可暂存/提交,但不得 approved;进“未归类”异常队列

十、一句话小结

订单里已经有"卖了什么、卖了多少钱、什么时候卖"的全部事实;缺的只是"我们为了这单付出去多少钱、付给谁、谁垫的、有没有凭证"。把这一件事做成当天可填、必附凭证、不可篡改、能进财务页的登记表,人工块就从"系统盲区"变成"可查账的成本项"。