原 9 篇 3,647 行,履约分类口径在总纲、模型文档、交接总结里重复三遍, 且仍留着"三块履约""按小时人工成本"等已作废表述。本次收敛为 5 篇 1,778 行: - 财务与履约口径总纲-2026-10-23.md(300 行):唯一口径来源 七条硬口径、三个正交维度、真实履约构成、成本模型规则、人工全覆盖、 实施顺序、进度、验收 SQL、防回归清单 - 成本中心与履约模型.md(471 行):数据模型、迁移 103–107、履约回填、 销售事实、本地库原地改造、索引与性能 - 成本中心API契约.md(270 行):字段替换总表、枚举、前后端模块边界 - 人工支出与备用金.md(218 行):人工支出登记字段/状态机/权限 + 客服备用金台账 - 财务系统集成方案.md:复式总账层(未实施) 旧的 4 篇(成本中心与三块履约模型、人工支出登记落地方案、 客服备用金台账实施方案、财务履约改造交接总结)移入 docs/archive/2026-10-23/ 并加归档提示。 履约分类判定 SQL 只在总纲保留一份,其它文档改为链接。
10 KiB
人工支出与客服备用金
状态:现行(P0 已实施,未部署生产)。 最后校准:2026-10-23 代码基线:
feat/finance-rewrite @ 7557de6d + 迁移 100/101/106业务口径以 财务与履约口径总纲-2026-10-23.md 为准;本文只负责人工支出的字段、流程、权限与备用金台账。 本文合并自原《人工支出登记落地方案-2026-10-17.md》与《客服备用金台账实施方案-2026-10-18.md》。
〇、先分清两张表
| 表 | 业务含义 | 状态 | 关键字段 |
|---|---|---|---|
manual_expenses |
客服备用金支出:客服从自己的备用金账户里充值/采购/赔偿 | 已实施(迁移 100) |
account_id / fund_entry_id / category / payee_* / evidence_json |
manual_expense_records |
公司人工支出登记:按订单/成本项记录公司实际付出去的钱 | 已实施 P0(迁移 106) |
order_id / order_item_id / cost_item_id / paid_by_admin_user_id / status / cost_snapshot_id |
结论:
- 客服侧已收口的是"备用金余额与用途",解决"客服手里有多少钱、这笔钱花到哪"。
- 公司口径的实际支出已落地:后台「人工支出」页(
/admin/manual-expenses)支持登记、审批、驳回、报销;通过后按成本项写cost_snapshots,没有有效成本版本时进入异常队列。 - 订单界面已接入「人工录单」:订单列表对人工单显示按钮、订单详情始终可录,弹窗锁定订单号、选择备用金账户并逐单填写实际金额,不存在小时单价或固定单量计价。
- 两张表通过
fund_expense_id关联,且该字段由系统写入:人工支出先从客服备用金账户扣款并生成manual_expenses,再写manual_expense_records。 - 不要把
manual_expenses直接改名为manual_expense_records,也不要让两张表共用索引/约束名。
一、核心口径
- 人工成本逐单、按实际金额。 不按小时、不按固定单量、不按固定单价。
- 人工支出从客服备用金账户扣款。 余额不足直接拒绝,不允许负余额。
- 客服角色只能使用自己的备用金账户。 后端越权直接 403;管理员/财务可代录到指定客服账户。
- 历史人工支出不补录。 只取少量样本做校准;历史缺口标记
historical_missing,不阻塞上线。 - 上线后每一笔人工单必须 100% 有成本记录,否则不允许关账。
- 登记必须"发生在垫付当天"才有监管价值;事后补记只能算账,不能算监管。
- 客服备用金是周转金台账,不是报销系统。 P0 只做"账户 + 流水 + 支出 + 对账",不做复杂审批和总账。
二、客服备用金台账
2.1 落地结果
| 项 | 实际实现 |
|---|---|
| 数据库 | 迁移 100_fund_accounts.sql:fund_accounts / fund_entries / manual_expenses / fund_reconciliations |
| 显示名 | 迁移 101_admin_display_name.sql:admin_users.display_name,备用金 owner_name 由触发器同步 |
| 账户编号 | 迁移 106_manual_expense_records.sql:FA-<大写用户名>(如 FA-KF_1、FA-JIAJIALI),重名追加 -2、-3 |
| 后端 | admin-fund-service.ts、routes/admin/fund.ts;金额全部用分,写操作使用事务与行锁 |
| 前端 | AdminFundPage.tsx、FundManagementView.tsx、FundSupportView.tsx;管理端与客服端分流 |
| 权限 | fund.view / fund.issue / fund.expense / fund.reconcile / fund.adjust / fund.export |
| 审计 | 所有写接口接入 createJsonHandler({ audit }) |
2.2 系统要回答的四个问题
- 公司现在有多少钱在主管和客服手里?
- 每个客服当前余额是多少?
- 每一笔钱花到哪、为什么花、有没有凭证?
- 系统余额和客服实际余额对不对得上?
2.3 账户
fund_accounts 一行 = 一个备用金账户,归属一个管理员(客服或主管)。account_no 必须是 FA-<大写用户名>,重名时按 id 顺序追加 -2、-3。
2.4 流水
fund_entries 是只追加的账户流水。每笔备用金变动写一行,正数=入账,负数=出账,带 entry_type(issue 发放 / expense 支出 / reconcile 对账调整 / adjust 人工调整)与 balance_after 快照。
2.5 支出
manual_expenses 记录一次具体支出:账户、金额(分)、分类(purchase 采购 / compensation 赔偿 / freight 运费 / platform_recharge 平台充值 / other)、收款方信息、凭证、关联的 fund_entry_id。
2.6 对账
fund_reconciliations 记录期初/期末账面余额、实际盘点余额与差异说明。差异必须由主管确认并留痕。
2.7 P0 未实现(仍在 P1)
异常队列、批量发放/对账、导出、低余额提醒、大额支出主管确认;支出凭证目前只校验数组非空,未校验对象存储 key 是否真实存在。
三、公司人工支出登记
3.1 落点
- 迁移:
apps/backend/src/db/migrations/106_manual_expense_records.sql - 服务:
apps/backend/src/services/finance/manual-expense-service.ts;备用金扣款唯一写入口admin-fund-service.ts的writeFundExpense - 接口:
apps/backend/src/routes/admin/finance.ts的/finance/manual-expenses*;订单页专用POST /finance/manual-order-costs - 页面:
apps/frontend/src/pages/admin/AdminManualExpensePage.tsx,入口/admin/manual-expenses - 订单入口:订单列表/详情「人工录单」打开
ManualOrderCostModal,锁定订单号、选择订单项与备用金账户、填写本单实际金额;支持深链/admin/orders/:orderId?manualCost=1 - 权限:查看
finance.view/fund.view,登记fund.expense/finance.config,审核finance.review
3.2 资金流(同一事务)
锁定 fund_accounts 行
→ 校验余额
→ 扣减余额
→ 写 fund_entries
→ 写 manual_expenses
→ 写 manual_expense_records.fund_expense_id
余额不足直接拒绝,不允许负余额。
3.3 成本快照
通过后以 subject_type='manual_case'、subject_id=expense.id 写 actual 快照,金额 = 登记的实际金额。同一订单项多笔支出可累加,不会互相覆盖。
没有匹配到有效成本版本时,写 manual_expense_cost_missing 异常(status='open'),不允许静默记 0。
3.4 状态机
draft ──> submitted ──> approved ──> reimbursed
│
└──> rejected
| 状态 | 含义 | 可执行动作 |
|---|---|---|
draft |
草稿 | 编辑、提交 |
submitted |
待审批 | 审批通过 / 驳回 |
approved |
已通过 | 标记已报销;此时写 actual 成本快照 |
rejected |
已驳回 | 需填 reject_reason |
reimbursed |
已报销 | 终态 |
reversed |
已冲正 | 预留,关联 reversal_of_id |
状态变更只追加到 manual_expense_events,不更新、不删除历史事件。
3.5 字段
manual_expense_records
expense_no -- 唯一编号
order_id, order_item_id, work_order_id
fund_expense_id -- 关联 manual_expenses.id;系统写入
cost_item_id -- 付款项目 -> cost_items.cost_model
expense_category -- goods_purchase / compensation / refund_advance /
-- service_fee / freight / recharge / other
amount_fen -- 实际金额(分),> 0
currency
paid_by_admin_user_id, paid_by_name
payee_type, payee_name, payee_account
payment_method
paid_at
status, approved_by, approved_at, rejected_by, rejected_at, reject_reason
reimbursed_by, reimbursed_at
evidence_json -- 凭证:支付截图对象 key、流水号、聊天记录 key
cost_snapshot_id
reversal_of_id
expense_batch_no, note
3.6 凭证
evidence_json 至少留一项可追溯凭证(对象存储 key、流水号或聊天记录 key),否则拒绝提交。
四、订单页「人工录单」
订单里的数据足够预填 90%:店铺、商品、规格、数量、用户实付、支付时间、平台单号、履约标签、状态,后台订单列表/详情已经全部具备。客服实际只需要补 4 件事:渠道/项目、金额、收款方、凭证(外加默认带出的「谁、什么时候」)。
弹窗规则:
- 锁定订单号,不允许改;
- 必须选择备用金账户(客服角色自动带入本人账户且不可改);
- 金额按本单实际金额填写,没有小时单价或固定单量;
- 付款时间不能晚于当前时间,也不能早于订单支付时间。
五、每日必查与关账门槛
| 指标 | 目标 |
|---|---|
| 人工订单项无人工支出记录 | 0 |
人工支出无 fund_expense_id(上线后) |
0 |
| 人工支出无成本快照 | 0 |
| 人工支出金额与备用金流水不一致 | 0 |
| 成本异常 open 中人工项 | 0(历史标记除外) |
关账门槛:
- 存在人工单无成本记录:不允许关账;
- 存在人工支出未关联备用金:不允许关账;
- 存在人工支出与备用金流水金额不一致:不允许关账。
核对 SQL 见 财务与履约口径总纲-2026-10-23.md §八。
六、异常处理
- 发现人工单无成本:当天补录;补录仍走备用金扣款,不允许直接改余额。
- 备用金余额不足:拒绝登记,由财务充值后再录;不允许负余额。
- 同一订单项多笔支出:允许累加(渠道费、加急费、补差价),但每笔都必须有独立备用金流水。
- 历史人工单:标记
historical_missing,不补录、不计入上线后缺口。
七、历史样本校准(只做参考)
- 每个来源抽 20–30 单,覆盖低价/中价/高价;
- 只用于估算历史人工成本区间与毛利率,不写
cost_snapshots; - 样本结论单独记录,禁止回填成逐单成本;
- 历史平台 53,901 项同理,只有拿到旧平台结算数据才允许按历史成本入账。
八、关联文档
- 业务口径: 财务与履约口径总纲-2026-10-23.md
- 数据模型: 成本中心与履约模型.md
- 影子总账: 财务系统集成方案.md
- 订单读取与履约展示: 成本中心与履约模型.md §六