From fed459c7aa6dd44184310d01646649815b674a9e Mon Sep 17 00:00:00 2001 From: yml2213 Date: Sun, 1 Nov 2026 14:53:11 +0800 Subject: [PATCH] =?UTF-8?q?fix(cost):=20=E5=8E=86=E5=8F=B2=E8=AE=A2?= =?UTF-8?q?=E5=8D=95=E6=88=90=E6=9C=AC=E5=9B=BA=E5=8C=96=EF=BC=8C=E5=8F=AA?= =?UTF-8?q?=E6=9C=89=E9=A2=84=E4=BC=B0=E2=86=92=E5=AE=9E=E9=99=85=E5=85=81?= =?UTF-8?q?=E8=AE=B8=E5=8D=87=E7=BA=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 已有快照不再因成本版本变更而替换,历史订单成交价与成本保持固定 - 仅接单平台「预估 → 实际」履约结算允许 supersede - 补配价格只影响没有快照的订单;文档同步口径 --- .../cost-center/cost-snapshot-service.test.ts | 9 +++++++++ .../services/cost-center/cost-snapshot-service.ts | 14 ++++++++++++++ docs/成本中心与履约模型.md | 6 ++++-- 3 files changed, 27 insertions(+), 2 deletions(-) diff --git a/apps/backend/src/services/cost-center/cost-snapshot-service.test.ts b/apps/backend/src/services/cost-center/cost-snapshot-service.test.ts index f99c6aa7..b7d3a89c 100644 --- a/apps/backend/src/services/cost-center/cost-snapshot-service.test.ts +++ b/apps/backend/src/services/cost-center/cost-snapshot-service.test.ts @@ -3,6 +3,7 @@ import test from 'node:test' import { resolveCost, + shouldSupersedeSnapshot, resolveGenericCosts, selectCostVersion, type CostItemRow, @@ -354,3 +355,11 @@ test('比例费用按退款后的净收入计算', () => { assert.equal(noRefund[0]?.totalAmountFen, 300) assert.equal(withRefund[0]?.totalAmountFen, 180) }) + +test('历史订单成本固化:只有预估→实际允许替换', () => { + assert.equal(shouldSupersedeSnapshot(null, 'estimated'), true) + assert.equal(shouldSupersedeSnapshot('estimated', 'estimated'), false) + assert.equal(shouldSupersedeSnapshot('estimated', 'actual'), true) + assert.equal(shouldSupersedeSnapshot('actual', 'estimated'), false) + assert.equal(shouldSupersedeSnapshot('actual', 'actual'), false) +}) diff --git a/apps/backend/src/services/cost-center/cost-snapshot-service.ts b/apps/backend/src/services/cost-center/cost-snapshot-service.ts index 38df8dd3..81367d29 100644 --- a/apps/backend/src/services/cost-center/cost-snapshot-service.ts +++ b/apps/backend/src/services/cost-center/cost-snapshot-service.ts @@ -243,6 +243,18 @@ export function resolveGenericCosts( return result } +/** + * 历史订单成本固化:已有快照不再因成本版本变更而替换。 + * 唯一允许的变化是「预估 → 实际」的履约结算升级。 + */ +export function shouldSupersedeSnapshot( + currentStage: string | null | undefined, + nextStage: string, +): boolean { + if (!currentStage) return true + return currentStage !== 'actual' && nextStage === 'actual' +} + export type SyncCostSnapshotsResult = { scanned: number created: number @@ -351,6 +363,8 @@ export async function syncChannelCostSnapshots( continue } if (current) { + // 历史订单成本/成交价已固化,只有「预估→实际」允许升级。 + if (!shouldSupersedeSnapshot(current.snapshot_stage, resolution.stage)) continue supersedeIds.push(Number(current.id)) result.superseded += 1 } diff --git a/docs/成本中心与履约模型.md b/docs/成本中心与履约模型.md index 67895f4d..45ebfa6a 100644 --- a/docs/成本中心与履约模型.md +++ b/docs/成本中心与履约模型.md @@ -332,8 +332,10 @@ computed_at ### 5.2 快照重算与通用成本 -- 成本快照不是一次性的:`cost_snapshot` 每轮对比「当前解析结果」与「已有有效快照」, - 金额、版本或阶段变化时先 `superseded_at` 旧行再写新行(典型:接单平台 `estimated → actual`、补配价格)。 +- **历史订单成本固化**:订单一旦生成快照,后续成本版本变更不会再覆盖它; + 唯一允许的变化是接单平台 `estimated → actual` 的履约结算升级。 +- 改价只能「立即或未来生效」,接口拒绝 `retroactive` 与早于当前时间的 `effectiveFrom`; + 想做历史回填只能补「没有快照」的订单,已有快照不动。 - 全局 0 元版本视为占位,不产生快照;该订单项进入 `cost_missing` 异常。 - 通用成本分两类: - `platform_fee` / `payment_fee` 每单都会发生,可按 global 或商品版本自动入账;