19 KiB
人工支出登记落地方案(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 |
结论:
- 客服侧已经收口的是“备用金余额与用途”,解决“客服手里有多少钱、这笔钱花到哪”。
- 财务侧还缺的是“公司口径的实际支出/垫付”,解决“公司为哪个订单、哪个成本项、付给谁、谁垫的、有没有审批和凭证”。
- 两张表通过
fund_expense_id关联:如果这笔公司支出来源于客服备用金,就填;公司直付则留空。 - 不要把
manual_expenses直接改名为manual_expense_records,也不要让两张表共用索引/约束名。
一、结论先行
- 订单里的数据足够预填 90%:店铺、商品、规格、数量、用户实付、支付时间、平台单号、履约标签、状态,后台订单列表/详情已经全部具备。客服实际只需要补 4 件事:渠道/项目、金额、收款方、凭证(外加默认带出的「谁、什么时候」)。
- 现有财务页没有“支出”这一侧。财务页现有 7 个账目类别(订单收款、订单退款、打手报酬、打手提现、打手充值、打手资金回收、售后追缴退还)全部是收款或打手侧资金;客服备用金支出已经落在
fund模块,但还没有汇总进财务页,也没有公司口径的人工支出/垫付登记。 - 落地成本低、风险小:不需要改订单主表,不需要新角色体系(现有
admin/finance/operator/support四角色 +finance.*权限即可覆盖),只需新增「公司人工支出登记」表 + 订单侧入口 + 财务侧新类别。 - **登记必须“发生在垫付当天”**才有监管价值;事后补记只能算账,不能算监管。
- 本轮只补公司口径:客服备用金台账已完成,不重复做一套客服余额系统;通过
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)。新增支出接口时建议按财务口径加权限点,避免客服看到全公司支出。
审计双留痕(现有约定即可满足):
- 路由层
createJsonHandler({ audit })→ 写admin_audit_logs(admin-audit-service.ts:11); - 业务层写
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 报表口径的两个坑(已在别处确认,实施时必须遵守)
- 数量:
order_items.quantity不可信(316,585 项里 316,578 项 = 1,而名称带「N个」的有 104,710 项,按名称解析总量是它的 7.3 倍)→ 支出若要按件摊,必须用normalized_quantity。 - 商品:同一商品名对应多个
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;进“未归类”异常队列 |
十、一句话小结
订单里已经有"卖了什么、卖了多少钱、什么时候卖"的全部事实;缺的只是"我们为了这单付出去多少钱、付给谁、谁垫的、有没有凭证"。把这一件事做成当天可填、必附凭证、不可篡改、能进财务页的登记表,人工块就从"系统盲区"变成"可查账的成本项"。