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 或商品版本自动入账;