fix(cost): 历史订单成本固化,只有预估→实际允许升级

- 已有快照不再因成本版本变更而替换,历史订单成交价与成本保持固定
- 仅接单平台「预估 → 实际」履约结算允许 supersede
- 补配价格只影响没有快照的订单;文档同步口径
This commit is contained in:
yml2213
2026-11-01 14:53:11 +08:00
parent 00fa4d7f0a
commit fed459c7aa
3 changed files with 27 additions and 2 deletions
@@ -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)
})
@@ -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
}
+4 -2
View File
@@ -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 或商品版本自动入账;