Files
order_site/docs/人工支出与备用金.md
yml2213 62763ddf0c feat(finance): 支持代录指定客服账户,并上线影子总账/对账/关账/快手平台退款
- 人工确认履约:管理员/财务/运营可传 fundAccountId / creditOwnerAdminUserId
  指定客服备用金/积分账户;客服仍强制本人账户
- 新增迁移 112:finance_accounts / journal_entries / journal_lines /
  periods / reconciliations / reconciliation_items,种子 11 个科目
- 影子总账:销售事实按日生成收入/退款/成本凭证,支持手工凭证、试算平衡;
  不接管钱包余额;总账同步接入定时任务与退款重算
- 期间关账/反关账:关账先补凭证并校验试算平衡,closed 期间禁止新增凭证
- 平台对账:JSON 账单导入,按 provider + platform_order_id 匹配订单/退款,
  标记 matched/amount_mismatch/missing_order/missing_refund/duplicate
- 平台退款:快手行业(91)同意退款 API 接入,成功后落 source=platform;
  抖音/拼多多返回 501,保留手工退款
- 新增后台「财务 → 总账与对账」页面(总账/期间/对账三个 Tab)
- 集成测试扩展到代录、总账平衡、关账阻断、手工凭证、对账匹配

验证:backend check 844(840 pass / 4 skip);frontend check 172 + build;
迁移 112 在 scratch 库全量执行并重跑幂等;本地生产副本已升级到 112,
销售事实同步 56 条凭证,借贷合计 1,885,351,864 = 1,885,351,864。
2026-10-26 13:32:47 +08:00

12 KiB
Raw Permalink Blame History

人工支出与客服备用金

状态:现行(P0 已实施,待部署生产)。 最后校准:2026-10-26(人工确认改单事务、subject_id 归一、配置自动匹配、人工缺成本入队列) 2026-10-24 变更:独立「人工履约」页面已下线,订单列表/详情里的「人工录单」改为「确认履约」;/finance/manual-order-cost* 接口已删除。资金链路不变(扣积分/备用金 → 写 manual_case 快照)。 代码基线:dev @ 43f402cf + 迁移 100/101/104/107(已合并到 dev,未推送、未部署生产) 业务口径以 财务与履约口径总纲-2026-10-23.md 为准;本文只负责人工录单的字段、流程、权限与备用金台账。 本文合并自原《人工支出登记落地方案-2026-10-17.md》与《客服备用金台账实施方案-2026-10-18.md》。


〇、只有一条链路:确认履约(原人工录单)

2026-10-23 口径调整:公司人工支出登记单与审批报销状态机整链下线。人工成本现在只有一条路径。

订单列表/详情「确认履约」
  → 扣客服积分或备用金余额(写 fund_entries / platform_credit_entries)
  → 写 cost_snapshots(subject_type='manual_case',subject_id=order_item_id)
表 业务含义 状态 关键字段
manual_expenses 客服备用金支出:客服从自己的备用金账户里充值/采购/赔偿 已实施(迁移 100) account_id / fund_entry_id / category / payee_* / order_item_id / evidence_json
cost_snapshots 人工成本的唯一落点 已实施(迁移 104 + 完整性兜底 111) subject_type='manual_case' / subject_id=order_item_id / total_amount_fen / snapshot_stage='actual';资金来源写 metadata_json.fundExpenseId / creditEntryId

结论:

  1. 没有单独的登记单表。manual_expense_records 与 manual_expense_events 已随迁移 104 归并下线。
  2. 没有审批、没有报销。订单页「人工录单」一次操作完成"扣钱 + 记成本",录入即计入毛利。
  3. 客服侧收口的是"备用金余额与用途":钱从谁的备用金出、付给谁、有没有凭证,全在 manual_expenses + fund_entries。
  4. 客服角色只能用本人备用金账户(后端越权直接 403);管理员/财务可代录到指定客服账户。
  5. 余额不足直接拒绝,不允许负余额。
  6. 历史人工支出找不回来,直接放弃:不补录、不做样本回填、不做均价估算。

一、核心口径

  1. 人工成本逐单、按实际金额。 不按小时、不按固定单量、不按固定单价。
  2. 人工支出从客服备用金账户扣款。 余额不足直接拒绝,不允许负余额。
  3. 客服角色只能使用自己的备用金账户。 后端越权直接 403;管理员/财务可代录到指定客服账户。
  4. 历史人工支出找不回来,直接放弃。 不补录、不做样本校准;历史缺口只计入 cost_missing_count,不阻塞上线。
  5. 上线后每一笔人工单必须 100% 有成本记录,否则不允许关账。
  6. 录入必须"发生在垫付当天"才有监管价值;事后补记只能算账,不能算监管。
  7. 客服备用金是周转金台账,不是报销系统。 P0 只做"账户 + 流水 + 支出 + 对账",不做复杂审批和总账。
  8. 人工录单一录到底:没有登记单、没有审批、没有报销。钱与成本在同一次操作里落地。

二、客服备用金台账

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 由触发器同步
账户编号 迁移 107_manual_fulfillment.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 系统要回答的四个问题

  1. 公司现在有多少钱在主管和客服手里?
  2. 每个客服当前余额是多少?
  3. 每一笔钱花到哪、为什么花、有没有凭证?
  4. 系统余额和客服实际余额对不对得上?

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 是否真实存在。

原「异常队列」指人工支出登记单的审批异常队列,随登记链下线而取消。现在的人工异常统一为 manual_cost_missing:首次创建人工履约配置之后的订单项,只要还没有人工成本快照就进入队列;确认履约写入快照后自动 resolved。历史人工单不进入该队列。


三、确认履约

3.1 落点

  • 服务:apps/backend/src/services/finance/manual-fulfillment-service.ts
  • 接口:apps/backend/src/routes/admin/finance.ts 的 POST /finance/manual-fulfillment/confirm;待办 GET /finance/manual-fulfillment/pending
  • 组件:apps/frontend/src/components/admin/ManualFulfillmentConfirmModal.tsx
  • 订单入口:订单列表/详情「确认履约」
  • 权限:admin / operator / support;客服只能使用本人备用金/积分账户;管理员/财务/运营可在确认时通过 fundAccountId / creditOwnerAdminUserId 指定客服账户代录
  • 配置前置:商品与成本 → 人工 SKU 配置(manual_fulfillment_configs)

3.2 资金流与成本流(同一事务)

BEGIN
  锁定 order_items 行(FOR UPDATE OF oi)
  → 复查是否已有 manual_case 快照(有则 409,不扣款)
  → 按 SKU 自动匹配人工履约配置(配置晚于下单也能挂上)
  → 解析成本项/成本版本(版本缺失在扣款前失败)
  → 扣积分(platform_credit_entries)或扣备用金(fund_entries + manual_expenses)
  → 写 cost_snapshots(actual,subject_id = order_item_id)
COMMIT   -- 任一步失败整体回滚
  • 余额不足直接拒绝,不允许负余额;
  • 重复点击/并发请求:订单项行锁 + uq_cost_snapshots_manual_order_item 唯一索引双重兜底,只有一个请求能成功扣款;
  • 同一个订单项最多一条有效人工成本快照;不同订单项(包括现金与积分两条链路)不会因为 id 撞车互相覆盖。

3.3 成本快照

  • subject_type='manual_case'、subject_id=order_item_id、order_item_id 同值;
  • 金额:固定成本 = credit_quantity × 平台换算率;成本不固定 = 客服填写的实际金额;
  • metadata_json 记录 fundExpenseId / creditEntryId / subjectSource / platformKey / creditQuantity / costFen / paidBy 等;
  • uq_cost_snapshots_manual_order_item 保证一个订单项只有一条有效快照(迁移 111)。

3.4 录入字段

order_item_id     -- 订单项(必填;弹窗内锁定)
costFen           -- 仅成本不固定的 SKU 必填,> 0(分)
payeeName         -- 收款方(选填,默认「人工履约」)
note              -- 备注(选填)

平台、消耗积分与固定成本都来自人工 SKU 配置;客服不选账户、不改平台。管理员/财务/运营代录时额外传 fundAccountId(现金)或 creditOwnerAdminUserId(积分),未指定则回退本人账户。

3.5 配置与监控

  • 人工 SKU 的匹配键:spec_json.skuNick → spec_json.outSkuSn → sku_code → sku_name;没有前两级的历史单也能按后两级配置。
  • 保存配置后立即回填该 SKU 最近 30 天的订单项;定时任务每 5 分钟对最近窗口内未匹配项兜底;更早历史需显式「重新匹配」。
  • 未配置 SKU 的订单项进入 manual_cost_missing(detail.configMissing=true),配置后自动匹配;确认履约后异常自动收口。

四、订单页「确认履约」

  • 弹窗只展示已配置履约渠道的人工项;固定成本零输入,成本不固定才要求填金额;
  • 锁定订单与订单项,不允许改平台/账户;
  • 确认即扣钱并写成本,没有审核/报销步骤;
  • 若 SKU 还没配置,弹窗会提示先去「商品与成本 → 人工 SKU 配置」补配置;
  • 重复确认会被拒绝,不会重复扣款。

五、每日必查与关账门槛

指标 目标
人工订单项无资金流水(现金备用金支出 / 积分消耗流水) 0
人工订单项无有效成本快照 0
现金人工成本金额与 manual_expenses 不一致 0
manual_cost_missing open(上线后) 0(历史人工单不计入)

关账门槛:

  • 存在上线后人工单无成本记录:不允许关账;
  • 存在上线后人工单没有对应资金流水(现金或积分):不允许关账;
  • 存在现金人工支出与备用金流水金额不一致:不允许关账。

核对 SQL 见 财务与履约口径总纲-2026-10-23.md §八。


六、异常处理

  • 发现人工单无成本:确认履约;扣款与写成本同事务,失败整体回滚,不会出现「扣了钱没记成本」。
  • 备用金/积分不足:拒绝确认,由主管补拨/充值后再确认;不允许负余额。
  • 人工 SKU 未配置:进入 manual_cost_missing 队列,配置后自动回填订单项,再确认即可。
  • 确认后需要更正:当前不提供反审核;只能通过后续冲销/调整处理(P1),不要直接改库。
  • 历史人工单:找不回来、直接放弃,不补录、不计入上线后监控缺口。

七、历史人工:直接放弃

历史人工支出找不回来,口径明确为放弃:

  • 不补录、不回填 cost_snapshots;
  • 不做样本抽取与均价估算;
  • 不阻塞上线,历史缺口计入销售事实的 cost_missing_count;
  • 只保证上线后的新单不丢:人工单必须有备用金支出与成本快照。

历史平台 53,901 项同理,只有拿到旧平台结算数据才允许按历史成本入账。


八、关联文档