# 财务系统集成方案 > 状态:**现行方案(影子总账 P0 已实施,2026-10-26)**。`finance_accounts / journal_entries / journal_lines / periods / reconciliations` 已建;总账不接管钱包余额;对账当前为手工账单导入,平台自动拉单待接。 > 最后校准:2026-10-26 > 代码基线:`dev`(迁移到 `111`,**未推送、未部署生产**) > 说明:本文是总账/对账/关账的目标架构与分期;当前已实施 P0(销售事实凭证、手工凭证、试算平衡、期间关账、手工账单对账)与快手平台退款。初始化步骤见 [财务上线初始化与验收-2026-10-26.md](财务上线初始化与验收-2026-10-26.md)。 > **履约方式、成本模型、人工全覆盖口径以 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 为准**;本文不重复分类设计。 > 目标:在**不破坏现有抢单、押金、结算、寄卖、提现链路**的前提下,增加可对账、可关账、可追溯的复式记账底座。 > 结论先说:**不要直接在 `worker_wallets` / `worker_wallet_ledgers` 上继续加字段。** 现有表是业务钱包与流水,不是会计总账;正确做法是新增复式记账底座,让现有钱包成为它的“余额投影”。 > > 如果只想知道“影子总账”是什么、为什么先跑影子账:先读 [影子总账说明.md](影子总账说明.md)。 --- ## 〇、当前实施状态 | 能力 | 状态 | | --- | --- | | 客服备用金台账(`fund_*` / 备用金 `manual_expenses`) | **已实施 P0**;账户编号 `FA-<用户名>`;客服只能使用本人账户 | | 商品主数据 / 渠道 SKU 映射 / 成本中心 | **已实施 P0(`dev` 分支)**,迁移 `103/104`;标准商品 745 个,只覆盖自动发货 38 + 接单平台 707 | | `sales_daily_facts` / 订单级毛利 | **已实施 P0(`dev` 分支)**,迁移 `105`;94 天收入 ¥2,650.87 万、成本 ¥950.27 万 | | 人工成本录入(确认履约 → 扣积分/备用金 → 写快照) | **已实施 P0(`dev` 分支)**,迁移 `104/107`;2026-10-24 人工履约页下线、入口收敛到订单页;**无登记单、无审批、无报销** | | 履约方式统一 `fulfillment_mode` | **已实施 P0(`dev` 分支)**;迁移 `105`,316,585 项已回填;订单页、成本快照、销售事实统一读该字段 | | `finance_accounts` / `finance_journal_entries` / `finance_journal_lines` | **已实施 P0(迁移 108)** | | `finance_periods` / `finance_reconciliations` / `finance_reconciliation_items` | **已实施 P0(迁移 108)** | | `finance_business_events` / `finance_settlements` | 未实施(当前用条目唯一键幂等) | | 现有 `admin-finance-service.ts` | 仍是业务事件 `UNION ALL`,不是总账 | **人工成本只有一条链路**:订单列表/详情点「确认履约」→ 扣客服积分或备用金 → 写 `cost_snapshots`(`subject_type='manual_case'`)。没有独立的人工支出登记表,也没有审批与报销环节。 --- ## 一、现有财务能力盘点 ### 1.1 已有表 | 表 | 作用 | 现状 | | --- | --- | --- | | `worker_wallets` | 打手资金汇总 | `available_amount` / `frozen_deposit_amount` / `pending_unfreeze_amount` / `frozen_withdraw_amount` / `total_credited_amount` / `total_settled_amount` | | `worker_wallet_ledgers` | 打手资金流水 | `ledger_type` / `amount` / `balance_after` / `frozen_after` / `audit_status` / `payload_json` | | `worker_deposit_unfreezes` | 押金/报酬延迟解冻队列 | 到点后由调度任务入账 | | `worker_finance_requests` | 充值/提现申请 | `request_type` / `status` / `amount` / `frozen_amount` | | `worker_withdrawal_accounts` | 提现账户 | 多渠道、二维码、黑名单 | | `worker_after_sales_cases` / `worker_after_sales_debts` | 售后追责与欠款 | 欠款可从后续报酬抵扣 | | `worker_wallet_ledgers.payload_json` | 寄卖发布押金等扩展信息 | `kind` / `consignmentSubmissionId` 等 | | `consignment_submissions` / `consignment_inventories` | 寄卖发布押金与货款 | 押金冻结/释放/没收、供货货款结算 | ### 1.2 已有后台财务页 `apps/backend/src/services/admin/admin-finance-service.ts` 目前用一条大 `UNION ALL` 把订单收款、退款、打手报酬、提现、充值、售后追缴拼成“财务事件”,输出日汇总与流水。 这不是总账: - 没有借贷方向,无法验证每笔业务是否平衡。 - 没有会计科目、期间、关账。 - 没有平台侧账单与对账差异。 - 没有订单级收入/成本/毛利。 - 没有业务事件幂等键,重放可能重复统计。 - 余额是可变字段,无法从流水独立重算并校验。 ### 1.3 现有资金动作(必须全部纳入财务) | 业务动作 | 代码入口 | 财务性质 | | --- | --- | --- | | 订单支付 | `services/order/order-service.ts` | 收入/预收 + 平台应收 | | 订单退款 | 订单状态回流、售后 | 冲减收入/应收 | | 工单验收结算 | `repositories/worker-platform/work-order-settlement-repo.ts` | 履约成本 + 应付打手 | | 抢单冻结押金 | `work-order-grab-repo.ts` | 打手余额 → 押金负债 | | 验收释放押金 | `work-order-settlement-repo.ts` | 押金负债 → 打手余额 | | 取消/超时没收押金 | `work-order-cancel-repo.ts` | 押金负债 → 违约金收入 | | 拼单份额结算 | `work-order-settlement-repo.ts` | 按份额拆分成本/押金 | | 售后扣款/欠款抵扣 | `after-sales-repo.ts` | 冲减应付打手 / 其他应收 | | 充值审核通过 | `worker-finance-request-service.ts` | 银行存款 → 打手余额 | | 提现申请/审核 | 同上 | 打手余额 → 银行存款 | | 寄卖发布押金 | `consignment-deposit-repo.ts` | 打手余额 → 押金负债 | | 寄卖货款结算 | `consignment-deposit-repo.ts` | 采购成本/存货 → 应付卖家 | | 平台服务费/通道费 | 目前未单独建模 | 销售费用/财务费用 | ### 1.4 履约方式与成本中心的前置关系 财务总账需要先知道“这笔钱属于哪个商品、哪个店铺、哪种履约方式、用了哪个成本版本”: | 履约方式 | 实现 | 成本模型 | 财务侧 | | --- | --- | --- | --- | | 自动发货 | `affiliate_dash` | 上游固定成本 → `cost_versions` | 采购成本 | | 接单平台 | `work_orders` | 模板规则版本 / 工单实际报酬 | 履约成本 + 押金负债 | | 历史平台 | `ct_assisted` / `feifei`(已下线) | 历史成本未采集则单列 | 仅历史报表 | | 人工 | 无工单、无自动发货任务、无历史平台任务 | **逐单实际金额**,确认履约直接扣积分/备用金 | 人工成本 + 备用金 | 电子凭证只是核销码,不进入上述分类。 因此建议顺序是: ```text 统一履约事实(fulfillment_mode + 成本模型) -> 商品主数据 + 渠道SKU映射 -> 成本中心(cost_items / cost_versions / cost_snapshots) -> 销售事实(sales_daily_facts) -> 影子总账(finance_*) -> 对账 / 转正 ``` 详细设计见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md) 与 [成本中心与履约模型.md](成本中心与履约模型.md)。 --- ## 二、建设原则 1. **复式记账**:每笔业务至少两行分录,借方合计 = 贷方合计;不平不允许提交。 2. **不可变**:已过账分录禁止 UPDATE / DELETE;冲正用反向分录。 3. **业务事件幂等**:每个业务动作有唯一 `business_event_id`,重复投递只入账一次。 4. **金额只用整数分**:数据库 `BIGINT`,禁止浮点。 5. **钱包是投影**:`worker_wallets` 的余额必须能由总账分录重算出来;差异告警。 6. **同一事务**:业务状态变更与会计分录在同一数据库事务内提交,避免“货发了、账没记”。 7. **期间可关账**:关账后禁止补记;必须用调整分录并留原因。 8. **对账优先**:先做影子账 + 对账,再切换钱包读模型,不做一次性大爆炸迁移。 --- ## 三、目标架构 ```mermaid flowchart LR BIZ["业务动作
订单/工单/寄卖/提现/售后"] --> OUTBOX["finance_business_events
幂等事件"] OUTBOX --> POST["finance-posting-service
借贷规则"] POST --> JE["finance_journal_entries"] JE --> JL["finance_journal_lines"] JL --> ACC["finance_accounts
科目表"] JL --> PROJ["余额投影
worker_wallets / 平台应收应付"] EXT["平台账单 / 支付流水"] --> REC["finance_reconciliations"] PROJ --> REC REC --> REPORT["试算平衡 / 利润表 / 订单损益"] ``` ### 3.1 科目表(建议) | 编码 | 科目 | 类型 | 正常方向 | | --- | --- | --- | --- | | 1001 | 银行存款 | 资产 | 借 | | 1002 | 支付通道待结算 | 资产 | 借 | | 1101 | 应收账款-平台 | 资产 | 借 | | 1201 | 预付账款-供货平台 | 资产 | 借 | | 1301 | 其他应收-打手欠款 | 资产 | 借 | | 2001 | 应付打手报酬 | 负债 | 贷 | | 2002 | 应付卖家货款 | 负债 | 贷 | | 2003 | 打手押金 | 负债 | 贷 | | 2004 | 打手可提现余额 | 负债 | 贷 | | 2005 | 其他应付-垫付人 | 负债 | 贷 | | 2101 | 应付平台服务费 | 负债 | 贷 | | 2201 | 预收账款 | 负债 | 贷 | | 3001 | 本年利润 | 权益 | 贷 | | 4001 | 商品销售收入 | 收入 | 贷 | | 4101 | 平台服务费收入 | 收入 | 贷 | | 4201 | 违约金/没收押金收入 | 收入 | 贷 | | 5001 | 商品采购成本 | 成本 | 借 | | 5101 | 履约成本-打手报酬 | 成本 | 借 | | 5102 | 履约成本-人工客服 | 成本 | 借 | | 5201 | 支付通道费 | 费用 | 借 | | 5301 | 售后损失 | 费用 | 借 | | 5401 | 平台服务费 | 费用 | 借 | > 科目可按实际业务裁剪,但“资产/负债/权益/收入/成本费用”五类与借贷方向必须先定死。 ### 3.2 核心表 ```sql -- 科目 CREATE TABLE finance_accounts ( id BIGSERIAL PRIMARY KEY, account_code TEXT NOT NULL UNIQUE, account_name TEXT NOT NULL, account_type TEXT NOT NULL CHECK (account_type IN ('asset','liability','equity','revenue','expense')), normal_side TEXT NOT NULL CHECK (normal_side IN ('debit','credit')), parent_id BIGINT REFERENCES finance_accounts(id), status TEXT NOT NULL DEFAULT 'active', created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL ); -- 业务事件收件箱:幂等闸门 CREATE TABLE finance_business_events ( id BIGSERIAL PRIMARY KEY, business_event_id TEXT NOT NULL UNIQUE, event_type TEXT NOT NULL, source_type TEXT NOT NULL, source_id TEXT NOT NULL, occurred_at TIMESTAMPTZ NOT NULL, payload_json JSONB NOT NULL DEFAULT '{}'::jsonb, status TEXT NOT NULL DEFAULT 'received' CHECK (status IN ('received','posted','failed','ignored')), attempts INTEGER NOT NULL DEFAULT 0, last_error TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL, processed_at TIMESTAMPTZ ); -- 结算单(打手报酬 / 卖家货款 / 平台服务费) CREATE TABLE finance_settlements ( id BIGSERIAL PRIMARY KEY, settlement_no TEXT NOT NULL UNIQUE, counterparty_type TEXT NOT NULL CHECK (counterparty_type IN ('worker','supplier','platform','other')), counterparty_id TEXT NOT NULL DEFAULT '', period_key TEXT NOT NULL, amount_fen BIGINT NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','approved','paid','cancelled')), approved_at TIMESTAMPTZ, paid_at TIMESTAMPTZ, created_at TIMESTAMPTZ NOT NULL, updated_at TIMESTAMPTZ NOT NULL ); CREATE TABLE finance_settlement_items ( id BIGSERIAL PRIMARY KEY, settlement_id BIGINT NOT NULL REFERENCES finance_settlements(id) ON DELETE CASCADE, source_type TEXT NOT NULL, source_id TEXT NOT NULL, amount_fen BIGINT NOT NULL DEFAULT 0, created_at TIMESTAMPTZ NOT NULL ); -- 凭证头 CREATE TABLE finance_journal_entries ( id BIGSERIAL PRIMARY KEY, entry_no TEXT NOT NULL UNIQUE, business_event_id TEXT NOT NULL UNIQUE REFERENCES finance_business_events(business_event_id), entry_type TEXT NOT NULL, occurred_at TIMESTAMPTZ NOT NULL, posted_at TIMESTAMPTZ NOT NULL, period_key TEXT NOT NULL, currency TEXT NOT NULL DEFAULT 'CNY', memo TEXT NOT NULL DEFAULT '', status TEXT NOT NULL DEFAULT 'posted' CHECK (status IN ('draft','posted','reversed')), reversal_of_id BIGINT REFERENCES finance_journal_entries(id), created_by TEXT NOT NULL DEFAULT 'system', created_at TIMESTAMPTZ NOT NULL ); -- 凭证行 CREATE TABLE finance_journal_lines ( id BIGSERIAL PRIMARY KEY, entry_id BIGINT NOT NULL REFERENCES finance_journal_entries(id) ON DELETE RESTRICT, line_no INTEGER NOT NULL, account_id BIGINT NOT NULL REFERENCES finance_accounts(id), debit_fen BIGINT NOT NULL DEFAULT 0, credit_fen BIGINT NOT NULL DEFAULT 0, order_id BIGINT, work_order_id BIGINT, worker_id BIGINT, shop_id TEXT NOT NULL DEFAULT '', provider TEXT NOT NULL DEFAULT '', settlement_id BIGINT REFERENCES finance_settlements(id), memo TEXT NOT NULL DEFAULT '', dimensions_json JSONB NOT NULL DEFAULT '{}'::jsonb, CHECK (debit_fen >= 0 AND credit_fen >= 0), CHECK ((debit_fen > 0) <> (credit_fen > 0)), UNIQUE (entry_id, line_no) ); -- 会计期间 CREATE TABLE finance_periods ( period_key TEXT PRIMARY KEY, -- 2026-10 starts_at TIMESTAMPTZ NOT NULL, ends_at TIMESTAMPTZ NOT NULL, status TEXT NOT NULL DEFAULT 'open' CHECK (status IN ('open','closed')), closed_at TIMESTAMPTZ, closed_by TEXT NOT NULL DEFAULT '' ); -- 对账批次 / 明细 CREATE TABLE finance_reconciliations ( id BIGSERIAL PRIMARY KEY, channel TEXT NOT NULL, -- 91 / kuaishou / pdd / douyin / affiliate_dash / alipay period_key TEXT NOT NULL, expected_fen BIGINT NOT NULL DEFAULT 0, actual_fen BIGINT NOT NULL DEFAULT 0, diff_fen BIGINT NOT NULL DEFAULT 0, status TEXT NOT NULL DEFAULT 'pending' CHECK (status IN ('pending','matched','diff','resolved')), created_at TIMESTAMPTZ NOT NULL, resolved_at TIMESTAMPTZ ); CREATE TABLE finance_reconciliation_items ( id BIGSERIAL PRIMARY KEY, reconciliation_id BIGINT NOT NULL REFERENCES finance_reconciliations(id) ON DELETE CASCADE, source_type TEXT NOT NULL, source_id TEXT NOT NULL, external_no TEXT NOT NULL DEFAULT '', expected_fen BIGINT NOT NULL DEFAULT 0, actual_fen BIGINT NOT NULL DEFAULT 0, diff_fen BIGINT NOT NULL DEFAULT 0, resolution TEXT NOT NULL DEFAULT '', created_at TIMESTAMPTZ NOT NULL ); ``` ### 3.3 业务事件 → 分录规则(建议) > 冲正不修改原凭证:新建一条 `business_event_id = <原ID>:reverse` 的反向凭证,并让 `reversal_of_id` 指向原凭证;原凭证保持 `posted` 不变。 | 事件 | 借方 | 贷方 | 备注 | | --- | --- | --- | --- | | 订单支付 | 1002 支付通道待结算 / 1101 应收平台 | 2201 预收账款 | 金额 = 用户实付(已确认);确认时点待拍板 | | 履约完成/验收 | 2201 预收账款 | 4001 商品销售收入 | 或按交付时点确认 | | 工单验收结算 | 5101 履约成本 | 2001 应付打手报酬 | 按份额逐笔 | | 打手报酬转可提现 | 2001 应付打手报酬 | 2004 打手可提现余额 | 与现有 `reward_settlement` 对齐 | | 抢单冻结押金 | 2004 打手可提现余额 | 2003 打手押金 | 不产生损益 | | 释放押金 | 2003 打手押金 | 2004 打手可提现余额 | 不产生损益 | | 没收押金 | 2003 打手押金 | 4201 违约金收入 | 需原因与审计 | | 提现审核通过 | 2004 打手可提现余额 | 1001 银行存款 | 先冻结后扣减 | | 充值审核通过 | 1001 银行存款 | 2004 打手可提现余额 | 需支付流水号 | | 售后扣款 | 2001/2004 | 1301 其他应收-打手欠款 | 欠款不足转 `worker_after_sales_debts` | | 寄卖发布押金 | 2004 打手可提现余额 | 2003 打手押金 | 与普通押金分维度 | | 寄卖验收货款 | 5001 商品采购成本 | 2002 应付卖家货款 | 货款金额取 `cost_price_fen || asking_price_fen` | | 支付卖家货款 | 2002 应付卖家货款 | 1001 银行存款 | 需结算单 | | 平台服务费 | 5401 平台服务费 | 2101 应付平台服务费 | 从平台账单导入 | | 确认履约(客服垫付) | 5102 履约成本-人工客服(按 `cost_item_id` 映射科目) | 2005 其他应付-垫付人 | 扣客服积分/备用金时同步发生;不需要单独审批 | | 客服备用金发放 | 1001 银行存款 | 2005 其他应付-垫付人 | 主管发放时发生 | | 退款冲正(预留) | 反向凭证 `reversal_of_id` 指向原凭证 | — | 退款/仲裁暂未接入,先预留不实现 | > **已确认(2026-10-17)**:收入金额口径 = 用户实付;打手报酬 = 支出(总额法);退款/仲裁先预留;人工支出以登记表为入口。 > **仍待拍板**:收入**确认时点**(付款 / 发货 / 验收 / 售后期结束),它直接影响利润表与预收账款。 --- ## 四、与现有代码的接入方式 ### 4.1 唯一记账入口 新增 `apps/backend/src/services/finance/finance-posting-service.ts`: ```ts postBusinessEvent({ businessEventId, // 业务唯一键,如 work_order:123:settle eventType, // work_order_settled sourceType, // work_order sourceId, // 123 occurredAt, lines: [...], // 借贷行 }) ``` 约束: - 在业务事务内调用;使用同一个 `PoolClient`。 - `finance_business_events` 先 `INSERT ... ON CONFLICT DO NOTHING`。 - 已存在则直接返回,不重复入账。 - 校验借贷平衡;不平抛错并回滚业务事务。 - 写入 `finance_journal_entries` / `finance_journal_lines`。 - 更新钱包投影(或由投影任务异步更新,但同一事务更安全)。 ### 4.2 需要改造的写入点 | 位置 | 改造 | | --- | --- | | `services/order/order-service.ts` | 订单支付/退款发事件 | | `repositories/worker-platform/work-order-settlement-repo.ts` | 验收结算、押金释放、报酬入账发事件 | | `work-order-grab-repo.ts` | 抢单冻结押金发事件 | | `work-order-cancel-repo.ts` | 取消/超时没收押金发事件 | | `after-sales-repo.ts` | 售后扣款/欠款抵扣发事件 | | `worker-finance-request-service.ts` | 充值/提现审核发事件 | | `consignment-deposit-repo.ts` | 发布押金、货款结算发事件 | | `services/finance/manual-fulfillment-service.ts`(已实施) | 确认履约成功时发事件;主体是积分流水 / 备用金支出 | | `admin-finance-service.ts` | 改为读总账/试算平衡,不再用 `UNION ALL` 拼报表 | ### 4.3 钱包投影一致性 第一阶段保留现有钱包更新逻辑,同时写总账;第二阶段再切换: 1. **影子期**:业务照旧改钱包,同时写总账;每日比较“钱包余额”与“总账重算余额”。 2. **校验期**:差异为 0 连续 N 天后,后台展示“总账余额”为准。 3. **切换期**:钱包读接口改为读投影;旧字段保留一个版本用于回滚。 4. **清理期**:删除重复的余额更新代码,只保留总账 + 投影。 --- ## 五、后台页面与 API ### 5.1 页面 | 页面 | 内容 | | --- | --- | | 财务总览 | 收入、成本、毛利、押金负债、可提现余额、待结算 | | 凭证簿 | 按期间/事件/科目/订单筛选;查看借贷行 | | 科目表 | 科目树、启用/停用、期初余额 | | 结算中心 | 打手报酬、卖家货款、平台服务费结算单 | | 对账中心 | 平台账单导入、差异明细、处理状态 | | 期间关账 | 月末检查、关账、反关账(需高权限) | | 订单损益 | 单笔订单收入、采购成本、打手报酬、退款、毛利 | | 试算平衡 / 利润表 | 按期间导出 | ### 5.2 API 已实现(P0,销售事实、成本快照与人工支出): ```text GET /api/v1/admin/finance/sales-profit/overview GET /api/v1/admin/finance/sales-facts POST /api/v1/admin/finance/sales-facts/rebuild POST /api/v1/admin/cost-center/snapshots/sync GET /api/v1/admin/orders/:orderId/refunds POST /api/v1/admin/orders/:orderId/refunds POST /api/v1/admin/orders/:orderId/refunds/:refundId/void ``` > 2026-10-23 下线了 `/finance/manual-expenses*` 全套接口(登记/审批/报销)。 > 2026-10-24 下线 `/finance/manual-order-cost*`(人工录单),改为 `GET /finance/manual-fulfillment/pending` + `POST /finance/manual-fulfillment/confirm`。 > 2026-10-24 上线手动退款登记(`order_refunds`):整单退款按订单项拆条、共享 `batch_no`,销售事实按有效退款冲减;后续平台退款 API 只需写 `source='api'/'platform'` + `external_ref`,其余聚合与冲减逻辑复用。 总账层建议新增前缀 `/api/v1/admin/finance`: ```text GET /accounts POST /accounts GET /journal?period=&eventType=&orderId=&workOrderId= GET /journal/:id GET /trial-balance?period= GET /profit-loss?period= GET /order-profit/:orderId GET /settlements POST /settlements/:id/approve GET /reconciliations POST /reconciliations/import POST /reconciliations/:id/resolve GET /periods POST /periods/:key/close ``` 权限建议新增: - `finance.post`:手工分录 - `finance.close`:关账 - `finance.reconcile`:对账处理 - 现有 `finance.view / review / config / export` 继续使用 所有写操作必须写 `admin_audit_logs`;关账、手工分录、冲正必须填原因。 --- ## 六、总账层分期 > 项目级顺序以 [README.md](README.md) §三 为准;本节只描述“总账层”内部阶段。 > 进入 P0 前应已完成:统一履约事实、商品主数据、渠道 SKU 映射、成本中心、销售事实、确认履约(`manual-fulfillment-service`)。 ### P0:影子总账(1–2 周) > 前置:先完成统一履约事实、商品主数据、渠道 SKU 映射、成本中心与销售事实(见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md))。否则影子总账只能记“总额”,无法按店铺/皮肤/成本版本出毛利。 - 建 `finance_accounts / business_events / journal_entries / journal_lines / periods`。 - 先接高频且核心的事件:订单支付、工单结算、押金冻结/释放/没收、提现,以及确认履约。 - 写总账但不改现有钱包逻辑。 - 每日对账:钱包余额 vs 总账余额。 - 补金额守恒与幂等测试。 ### P1:寄卖、售后、充值与结算(1–2 周) - 接寄卖发布押金、货款结算、售后欠款/抵扣、打手充值。 - 建 `finance_settlements` / `finance_settlement_items`。 - 后台增加凭证簿与试算平衡。 ### P2:平台对账(2–3 周) - 接 91 / 快手 / PDD / 抖音 / affiliate-dash 账单或导出。 - 建 `finance_reconciliations` / `finance_reconciliation_items`。 - 差异必须能定位到订单/工单/提现单。 - 对账差异不自动改账,必须人工确认后生成调整分录。 ### P3:报表与关账(2 周) - 利润表、订单损益、现金流量简表。 - 期间关账、反关账、期初余额。 - 导出与权限审计。 ### P4:钱包切换(1 周,需灰度) - 钱包读模型切到总账投影。 - 旧字段保留一个版本;差异告警。 - 回滚方案:关闭投影读开关,回到旧钱包字段。 --- ## 七、必须补的测试 1. 每笔凭证借方合计 = 贷方合计。 2. 同一 `business_event_id` 重复投递只入账一次。 3. 冲正后原凭证 + 反向凭证净额为 0。 4. 钱包余额 = 总账重算余额。 5. 抢单冻结 / 验收释放 / 取消没收的押金守恒。 6. 拼单多份额:份额报酬 + 押金 + 欠款抵扣 = 钱包变化。 7. 提现:申请冻结、审核通过、拒绝解冻三条路径。 8. 寄卖:发布押金、驳回释放、验收货款、撤销处置。 9. 售后:扣款、欠款、后续报酬抵扣。 10. 关账后禁止补记;调整分录必须留原因。 11. 对账:平台金额、订单金额、钱包金额三方差异可定位。 --- ## 八、需要业务拍板的问题 > 2026-10-17 已确认 4 条口径,2026-10-22/23 追加确认收入时点、人工支出与电子凭证口径;明细见 [财务与履约口径总纲-2026-10-23.md](财务与履约口径总纲-2026-10-23.md)。 | # | 问题 | 状态 | 影响 | | --- | --- | --- | --- | | 1 | 收入金额口径 | ✅ **已确认:收入 = 用户实付(`orders.total_amount`)**,与快手 `paymentFen` 数据核验 100% 一致 | 收入基数 | | 2 | 收入确认时点:付款 / 发货 / 验收 / 售后期结束? | ✅ **先按 `paid_at` 归属销售日**;成本快照生成后回填到同一销售日 | 决定利润表与预收账款 | | 3 | 平台结算周期与账单口径(91、快手、PDD、抖音、affiliate-dash) | ⏳ 待定 | 决定对账与应收 | | 4 | 打手报酬是“成本”还是“代收代付”? | ✅ **已确认:支出/成本**(最终提现打款),按**总额法** | 收入总额法/净额法 | | 5 | 寄卖货款的采购成本确认时点 | ⏳ 待定 | 决定存货/成本 | | 6 | 提现通道费、平台服务费由谁承担 | ⏳ 待定 | 决定费用科目与结算 | | 7 | 退款、拒付、仲裁的冲减规则 | ✅ **已确认:暂未接入,先预留**(字段 + `reversal_of_id` + 事件类型,不实现逻辑) | 决定售后与收入冲回 | | 8 | 人工支出如何入账 | ✅ **已确认:逐单实际金额,确认履约直接扣积分/备用金并写成本快照;无登记单/审批/报销;历史人工支出找不回来、直接放弃** | 决定人工成本与备用金 | | 9 | 电子凭证是否作为履约/成本分类 | ✅ **不参与分类**;电子凭证只是快手核销码 | 决定报表口径 | | 10 | 历史人工支出 | ✅ **不补录**,只取少量样本校准;上线后人工单必须 100% 有成本记录 | 决定上线范围 | | 11 | 是否需要发票/税务 | ⏳ 待定 | 决定发票表与税额 | | 12 | 是否多币种 | ⏳ 待定(当前 CNY) | 若否则需汇率与汇兑损益 | --- ## 九、一句话结论 **财务系统应作为“总账层”接入,而不是继续扩展业务钱包。** 先把商品主数据、成本中心、人工 case 与销售事实补齐,再以影子总账 + 幂等业务事件 + 每日对账落地;确认钱包与总账长期一致后,再切换余额读模型。这样既能保护现有抢单/押金/寄卖链路,也能为平台结算、订单损益和关账留出正确底座。