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。
This commit is contained in:
yml2213
2026-10-26 13:32:47 +08:00
parent f0eb8a5e05
commit 62763ddf0c
31 changed files with 2961 additions and 55 deletions
+37
View File
@@ -8,6 +8,7 @@ import {
} from '../../services/admin/write/order-actions.js'
import {
createManualRefund,
createPlatformRefund,
listOrderRefunds,
voidOrderRefund,
} from '../../services/finance/order-refund-service.js'
@@ -103,6 +104,42 @@ router.get(
),
)
router.post(
'/orders/:orderId/refunds/platform',
requireAdminPermission('finance.config'),
createJsonHandler(
(req) => {
const body = req.body as Record<string, unknown>
return createPlatformRefund(
{
orderId: String((req.params as AdminOrderRouteParams).orderId || ''),
orderItemId: body?.orderItemId as string | undefined,
amountFen: body?.amountFen as string | undefined,
platformRefundId: String(body?.platformRefundId || ''),
reason: body?.reason as string | undefined,
note: body?.note as string | undefined,
refundedAt: body?.refundedAt as string | undefined,
sellerId: body?.sellerId as string | undefined,
refundHandingWay: body?.refundHandingWay,
desc: body?.desc as string | undefined,
},
req.adminSession || null,
)
},
{
successMessage: '平台退款已提交并登记',
errorMessage: '平台退款失败',
scope: '[admin/orders/:orderId/refunds/platform]',
audit: (req, data) => ({
action: 'order_platform_refund_created',
targetType: 'order',
targetId: String((req.params as AdminOrderRouteParams).orderId || ''),
data: data && typeof data === 'object' ? (data as Record<string, unknown>) : {},
}),
},
),
)
router.post(
'/orders/:orderId/refunds',
requireAdminPermission('finance.config'),