From 9ca1d52419e11f2b53eec37a97d93848048bac34 Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sat, 31 Oct 2026 20:17:28 +0800 Subject: [PATCH] =?UTF-8?q?fix(finance):=20=E4=BA=BA=E5=B7=A5=E7=A1=AE?= =?UTF-8?q?=E8=AE=A4=E5=90=8E=E6=8C=89=E4=B8=9A=E5=8A=A1=E6=97=A5=E9=87=8D?= =?UTF-8?q?=E7=AE=97=E4=BA=8B=E5=AE=9E=E4=B8=8E=E6=80=BB=E8=B4=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 确认履约写入 manual_case 快照后,立即按该订单业务日期重算销售事实并同步总账, 避免较早订单的成本只等 3 天窗口而永远入不了报表。 --- .../finance/manual-fulfillment-service.ts | 36 ++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/apps/backend/src/services/finance/manual-fulfillment-service.ts b/apps/backend/src/services/finance/manual-fulfillment-service.ts index 75b33d69..2087ef75 100644 --- a/apps/backend/src/services/finance/manual-fulfillment-service.ts +++ b/apps/backend/src/services/finance/manual-fulfillment-service.ts @@ -14,6 +14,8 @@ import { PLATFORM_CREDIT_KEYS, type PlatformCreditKey, } from '../cost-center/platform-credit-service.js' +import { rebuildChannelSalesDailyFacts } from './sales-facts-channel-service.js' +import { syncFinanceLedgerFromFacts } from './finance-ledger-service.js' /** * 人工履约待办与「确认履约」。 @@ -204,7 +206,7 @@ export async function confirmManualFulfillment(payload: JsonObject, session: Adm // 钱与成本必须同一事务:先锁订单项、复查未履约,再扣款、最后写快照。 // 任何一步失败(余额不足、成本版本缺失、重复快照)都整体回滚,不会留下「扣了钱没记成本」。 - return withTransaction(async (client) => { + const result = await withTransaction(async (client) => { // 配置可能晚于订单创建:确认前按 SKU 再匹配一次(与定时 rematch 同口径), // 保证「配置好之后的新单」不需要人工再点一次「重新匹配」。 await matchManualFulfillmentConfigWithClient(client, orderItemId, now) @@ -507,6 +509,38 @@ export async function confirmManualFulfillment(payload: JsonObject, session: Adm balanceAfter, } }) + + // 成本已落快照,立刻重算该业务日的事实与总账;失败不阻断确认(定时任务会兜底)。 + await refreshFactsForOrderItem(orderItemId) + return result +} + +/** + * 人工确认后按「订单业务日期」重算销售事实与总账。 + * + * 事实任务默认只回看最近几天,确认一笔较早的订单时不会自动覆盖, + * 所以这里按该订单自己的业务日期精确重算。 + */ +async function refreshFactsForOrderItem(orderItemId: number): Promise { + try { + const basis = await query<{ stat_date: string | null }>( + ` + SELECT (COALESCE(o.paid_at, o.created_at) AT TIME ZONE 'Asia/Shanghai')::date::text + AS stat_date + FROM order_items oi + JOIN orders o ON o.id = oi.order_id + WHERE oi.id = $1 + `, + [orderItemId], + ) + const statDate = basis.rows[0]?.stat_date + if (!statDate) return + await rebuildChannelSalesDailyFacts({ dateFrom: statDate, dateTo: statDate }) + await syncFinanceLedgerFromFacts({ dateFrom: statDate, dateTo: statDate }) + } catch (error) { + // 成本已入账,事实重算失败只影响报表时效,交给定时任务兜底。 + void error + } } /** 客服自己的备用金账户(管理员代录时用指定账户)。 */