Add tenant-safe value, telemetry, connector, commercial, and production-readiness foundations.
270 lines
14 KiB
JavaScript
270 lines
14 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
|
|
import {
|
|
buildAnalyticsWindow,
|
|
buildCommercialMutation,
|
|
buildCommercialValueLedgers,
|
|
buildPricingScenarioPayload,
|
|
buildPricingScenarioView,
|
|
buildQuotaRows,
|
|
classifyCommercialAccountState,
|
|
createDefaultPricingScenarioForm,
|
|
formatCurrencyAmount,
|
|
formatMetricValues,
|
|
formatRatio
|
|
} from '../src/components/commercial/commercialWorkspaceModel.js'
|
|
|
|
test('客户价值账和平台经营账保持严格分离', () => {
|
|
const ledgers = buildCommercialValueLedgers({
|
|
verifiedCashSavings: metric('verified_cash_savings', '客户财务确认现金节省', [{ currency: 'CNY', amount: '100' }]),
|
|
customerCharges: metric('customer_charges', '客户合同收费', [{ currency: 'CNY', amount: '40' }], 'partial'),
|
|
customerRoi: { ...metric('customer_roi', '客户现金 ROI', [], 'partial'), ratios: [{ currency: 'CNY', ratio: '1.5', numerator: '60', denominator: '40' }] },
|
|
customerLaborValue: unavailable('customer_labor_value', '客户认可工时价值'),
|
|
internalCosts: metric('internal_costs', '平台内部成本', [{ currency: 'CNY', amount: '12' }]),
|
|
contributionMargin: metric('contribution_margin', '贡献毛利', [{ currency: 'CNY', amount: '28' }], 'partial')
|
|
})
|
|
|
|
assert.deepEqual(ledgers.customer.map((item) => item.category), [
|
|
'verifiedCashSavings', 'customerCharges', 'customerRoi', 'customerLaborValue'
|
|
])
|
|
assert.deepEqual(ledgers.platform.map((item) => item.category), ['internalCosts', 'contributionMargin'])
|
|
assert.equal(ledgers.customer[2].displayValue, '150.0%')
|
|
assert.equal(ledgers.platform[0].displayValue, formatCurrencyAmount('12', 'CNY'))
|
|
})
|
|
|
|
test('不同币种逐行保留,绝不在前端求和或换算', () => {
|
|
const values = formatMetricValues(metric('verified', '已核验', [
|
|
{ currency: 'CNY', amount: '100' },
|
|
{ currency: 'USD', amount: '20' }
|
|
]))
|
|
assert.deepEqual(values.map((item) => item.currency), ['CNY', 'USD'])
|
|
assert.deepEqual(values.map((item) => item.amount), [100, 20])
|
|
assert.match(values[0].displayValue, /100/u)
|
|
assert.match(values[1].displayValue, /20/u)
|
|
assert.equal(formatRatio('0.25'), '25.0%')
|
|
})
|
|
|
|
test('没有真实财务证据时明确不可用,不把未知伪装成零', () => {
|
|
const [cash, labor] = buildCommercialValueLedgers({
|
|
verifiedCashSavings: unavailable('verified_cash_savings', '客户财务确认现金节省'),
|
|
customerCharges: unavailable('customer_charges', '客户合同收费'),
|
|
customerRoi: unavailable('customer_roi', '客户现金 ROI'),
|
|
customerLaborValue: unavailable('customer_labor_value', '客户认可工时价值'),
|
|
internalCosts: unavailable('internal_costs', '平台内部成本'),
|
|
contributionMargin: unavailable('contribution_margin', '贡献毛利')
|
|
}).customer
|
|
|
|
assert.equal(cash.status, 'unavailable')
|
|
assert.equal(cash.displayValue, '不可用')
|
|
assert.deepEqual(cash.values, [])
|
|
assert.equal(labor.displayValue, '不可用')
|
|
assert.deepEqual(labor.requiredInputs, ['真实财务证据'])
|
|
})
|
|
|
|
test('账户状态完整区分加载、权限、错误、未配置、部分与就绪', () => {
|
|
assert.equal(classifyCommercialAccountState({ loading: true }), 'loading')
|
|
assert.equal(classifyCommercialAccountState({ error: { status: 403 } }), 'permission')
|
|
assert.equal(classifyCommercialAccountState({ error: new Error('offline') }), 'error')
|
|
assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'unavailable' } }), 'unavailable')
|
|
assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'partial' } }), 'partial')
|
|
assert.equal(classifyCommercialAccountState({ account: { dataStatus: 'available' } }), 'ready')
|
|
})
|
|
|
|
test('权益列表保持失败关闭语义并区分不限量和未知硬上限', () => {
|
|
const rows = buildQuotaRows({ quotas: [
|
|
{ status: 'unlimited', usedQuantity: '12', hardLimitRemaining: null, commerciallyAllowed: true, entitlement: { id: 'e1', entitlementKey: 'ocr', metricKey: 'ocr_calls', unit: '次', entitlementType: 'unlimited', version: 1 } },
|
|
{ status: 'inactive', usedQuantity: '0', hardLimitRemaining: null, commerciallyAllowed: false, reason: '订阅暂停', entitlement: { id: 'e2', entitlementKey: 'ai', metricKey: 'ai_calls', unit: '次', entitlementType: 'metered', version: 2 } }
|
|
] })
|
|
assert.equal(rows[0].remainingLabel, '不限量')
|
|
assert.equal(rows[1].remainingLabel, '未配置硬上限')
|
|
assert.equal(rows[1].commerciallyAllowed, false)
|
|
assert.equal(rows[1].reason, '订阅暂停')
|
|
})
|
|
|
|
test('分析窗口生成显式时区并拒绝倒序和超过两年', () => {
|
|
const window = buildAnalyticsWindow({ start: '2026-04-01', end: '2026-07-01' })
|
|
assert.match(window.start, /(Z|[+-]\d{2}:\d{2})$/u)
|
|
assert.match(window.end, /(Z|[+-]\d{2}:\d{2})$/u)
|
|
assert.throws(() => buildAnalyticsWindow({ start: '2026-07-01', end: '2026-04-01' }), /早于/u)
|
|
assert.throws(() => buildAnalyticsWindow({ start: '2023-01-01', end: '2026-07-01' }), /731/u)
|
|
})
|
|
|
|
test('定价场景将百分比和时间窗转换为后端精确契约并执行前置校验', () => {
|
|
const defaults = createDefaultPricingScenarioForm(
|
|
{ start: '2026-04-01', end: '2026-07-01' },
|
|
new Date('2026-07-02T08:30:00Z')
|
|
)
|
|
assert.equal(defaults.targetContributionMarginPercent, '65')
|
|
assert.equal(defaults.maxVerifiedSavingsSharePercent, '25')
|
|
|
|
const payload = buildPricingScenarioPayload({
|
|
...defaults,
|
|
asOf: '2026-07-02T08:30:00Z',
|
|
targetContributionMarginPercent: '65',
|
|
maxVerifiedSavingsSharePercent: '25'
|
|
})
|
|
assert.match(payload.start, /(Z|[+-]\d{2}:\d{2})$/u)
|
|
assert.match(payload.end, /(Z|[+-]\d{2}:\d{2})$/u)
|
|
assert.equal(payload.asOf, '2026-07-02T08:30:00.000Z')
|
|
assert.equal(payload.targetContributionMarginRate, '0.65')
|
|
assert.equal(payload.maxVerifiedSavingsShare, '0.25')
|
|
|
|
assert.throws(() => buildPricingScenarioPayload({
|
|
...defaults, targetContributionMarginPercent: '95'
|
|
}), /小于 95/u)
|
|
assert.throws(() => buildPricingScenarioPayload({
|
|
...defaults, targetContributionMarginPercent: '94.999999'
|
|
}), /小于 95/u)
|
|
assert.equal(buildPricingScenarioPayload({
|
|
...defaults, targetContributionMarginPercent: '94.9999'
|
|
}).targetContributionMarginRate, '0.949999')
|
|
assert.throws(() => buildPricingScenarioPayload({
|
|
...defaults, maxVerifiedSavingsSharePercent: '0'
|
|
}), /大于 0/u)
|
|
assert.throws(() => buildPricingScenarioPayload({
|
|
...defaults, asOf: '2026-03-01T00:00:00Z'
|
|
}), /不能早于/u)
|
|
})
|
|
|
|
test('定价结果逐币种展示价格走廊且绝不把缺失事实显示为零', () => {
|
|
const view = buildPricingScenarioView({
|
|
tenantId: 'tenant-a',
|
|
start: '2026-04-01T00:00:00Z',
|
|
end: '2026-07-01T23:59:59Z',
|
|
asOf: '2026-07-02T00:00:00Z',
|
|
targetContributionMarginRate: '0.65',
|
|
maxVerifiedSavingsShare: '0.25',
|
|
recommendedModel: 'hybrid',
|
|
evidenceStatus: 'partial',
|
|
scenarios: [
|
|
{
|
|
currency: 'CNY', status: 'feasible', internalCost: '20', verifiedCashSavings: '200',
|
|
minimumSustainableCharge: '57.1429', maximumValueAlignedCharge: '50',
|
|
maximumSuccessFee: '0', customerRoiAtMinimumCharge: '2.5',
|
|
contributionMarginAtValueCeiling: '0.6', reason: '独立 CNY 走廊'
|
|
},
|
|
{
|
|
currency: 'USD', status: 'cost_only', internalCost: '5', verifiedCashSavings: null,
|
|
minimumSustainableCharge: '14.2857', maximumValueAlignedCharge: null,
|
|
maximumSuccessFee: null, customerRoiAtMinimumCharge: null,
|
|
contributionMarginAtValueCeiling: null, reason: '缺少 USD 价值证据'
|
|
}
|
|
],
|
|
notes: ['结果不会自动写套餐']
|
|
})
|
|
|
|
assert.equal(view.recommendedModelLabel, '混合定价(基础订阅 + 封顶成功费)')
|
|
assert.equal(view.multiCurrency, true)
|
|
assert.deepEqual(view.rows.map((row) => row.currency), ['CNY', 'USD'])
|
|
assert.match(view.rows[0].minimumSustainableChargeLabel, /57/u)
|
|
assert.equal(view.rows[1].maximumValueAlignedChargeLabel, '不可用')
|
|
assert.equal(view.rows[1].maximumSuccessFeeLabel, '不可用')
|
|
assert.equal(view.rows[1].customerRoiLabel, '—')
|
|
assert.deepEqual(view.rows[1].missingInputs, ['同币种财务确认现金节省'])
|
|
assert.doesNotMatch(view.rows[1].maximumValueAlignedChargeLabel, /0/u)
|
|
})
|
|
|
|
test('完全无证据的定价响应保留 unavailable 与缺失说明', () => {
|
|
const view = buildPricingScenarioView({
|
|
recommendedModel: 'pilot_collecting', evidenceStatus: 'unavailable', scenarios: [], notes: []
|
|
})
|
|
assert.equal(view.evidenceStatus, 'unavailable')
|
|
assert.equal(view.evidenceStatusLabel, '不可用')
|
|
assert.equal(view.recommendedModelLabel, '试点采集证据')
|
|
assert.deepEqual(view.rows, [])
|
|
})
|
|
|
|
test('套餐、订阅与权益表单构造服务端事实时间和版本化载荷', () => {
|
|
const plan = buildCommercialMutation('createPlan', {
|
|
planCode: 'enterprise', name: '企业版', pricingModel: 'hybrid', billingInterval: 'annual',
|
|
currency: 'cny', baseFee: '1000', includedSeats: 20, overageEnabled: true,
|
|
effectiveFrom: '2026-07-16T09:30', effectiveTo: '', contractTermsJson: '{"approved_by":"cfo"}'
|
|
})
|
|
assert.equal(plan.currency, 'CNY')
|
|
assert.match(plan.effectiveFrom, /Z$/u)
|
|
assert.equal(plan.contractTermsJson.approved_by, 'cfo')
|
|
|
|
const subscription = buildCommercialMutation('createSubscription', {
|
|
subscriptionKey: 'sub-enterprise', status: 'active', startsAt: '2026-07-16T09:30',
|
|
currentPeriodStart: '2026-07-16T09:30', currentPeriodEnd: '2026-08-16T09:30',
|
|
seats: 20, metadataJson: '{}'
|
|
}, { plan: { id: 'plan-1' } })
|
|
assert.equal(subscription.planId, 'plan-1')
|
|
assert.match(subscription.currentPeriodEnd, /Z$/u)
|
|
|
|
const entitlement = buildCommercialMutation('upsertEntitlement', {
|
|
entitlementKey: 'ocr', metricKey: 'ocr_calls', entitlementType: 'metered', unit: '次',
|
|
includedQuantity: '100', hardLimitQuantity: '120', resetInterval: 'monthly',
|
|
overagePolicy: 'block', effectiveFrom: '2026-07-16T09:30', configJson: '{}'
|
|
}, { subscription: { id: 'sub-1' } })
|
|
assert.equal(entitlement.subscriptionId, 'sub-1')
|
|
assert.equal(entitlement.hardLimitQuantity, '120')
|
|
})
|
|
|
|
test('客户端在写入前拒绝不完整窗口、降配和无效 JSON', () => {
|
|
assert.throws(() => buildCommercialMutation('createPlan', {
|
|
planCode: 'p', name: 'P', pricingModel: 'subscription', billingInterval: 'annual', currency: 'CNY',
|
|
baseFee: '0', includedSeats: 1, effectiveFrom: '2026-08-01T00:00', effectiveTo: '2026-07-01T00:00', contractTermsJson: '{}'
|
|
}), /晚于/u)
|
|
assert.throws(() => buildCommercialMutation('upsertEntitlement', {
|
|
entitlementKey: 'ocr', metricKey: 'ocr', entitlementType: 'metered', unit: '次', includedQuantity: '100',
|
|
hardLimitQuantity: '10', resetInterval: 'monthly', overagePolicy: 'block', effectiveFrom: '2026-07-01T00:00', configJson: '{}'
|
|
}, { subscription: { id: 'sub-1' } }), /不能小于/u)
|
|
assert.throws(() => buildCommercialMutation('createPlan', {
|
|
planCode: 'p', name: 'P', pricingModel: 'subscription', billingInterval: 'annual', currency: 'CNY',
|
|
baseFee: '0', includedSeats: 1, effectiveFrom: '2026-07-01T00:00', contractTermsJson: '{bad}'
|
|
}), /有效 JSON/u)
|
|
})
|
|
|
|
test('用量和成本事件在客户端执行幂等、符号、冲回与币种边界校验', () => {
|
|
const account = { subscription: { id: 'sub-1' } }
|
|
const usage = buildCommercialMutation('recordUsage', {
|
|
entitlementId: 'ent-1', eventType: 'usage', quantity: '1', occurredAt: '2026-07-16T09:30',
|
|
sourceSystem: 'web', idempotencyKey: 'usage-1', metadataJson: '{}'
|
|
}, account)
|
|
assert.equal(usage.subscriptionId, 'sub-1')
|
|
assert.match(usage.occurredAt, /Z$/u)
|
|
|
|
assert.throws(() => buildCommercialMutation('recordUsage', {
|
|
entitlementId: 'ent-1', eventType: 'credit', quantity: '1', occurredAt: '2026-07-16T09:30',
|
|
sourceSystem: 'web', idempotencyKey: 'usage-2', metadataJson: '{}'
|
|
}, account), /必须小于 0/u)
|
|
assert.throws(() => buildCommercialMutation('recordUsage', {
|
|
entitlementId: 'ent-1', eventType: 'reversal', quantity: '-1', occurredAt: '2026-07-16T09:30',
|
|
sourceSystem: 'web', idempotencyKey: 'usage-3', metadataJson: '{}'
|
|
}, account), /被冲回/u)
|
|
|
|
const cost = buildCommercialMutation('recordCost', {
|
|
eventType: 'incurred', costCategory: 'ai_inference', quantity: '3', unit: '次', unitCost: '0.02',
|
|
originalCurrency: 'usd', reportingCurrency: 'cny', fxRate: '7.2', allocationKey: 'ai-july',
|
|
occurredAt: '2026-07-16T09:30', sourceSystem: 'billing', idempotencyKey: 'cost-1', metadataJson: '{}'
|
|
}, account)
|
|
assert.equal(cost.originalCurrency, 'USD')
|
|
assert.equal(cost.reportingCurrency, 'CNY')
|
|
assert.equal(cost.fxRate, '7.2')
|
|
})
|
|
|
|
test('订阅状态变更必须使用版本、受限目标状态和可审计原因', () => {
|
|
const transition = buildCommercialMutation('transitionSubscription', {
|
|
resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'suspended', reason: '客户合同暂停'
|
|
})
|
|
assert.deepEqual(transition, {
|
|
resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'suspended', reason: '客户合同暂停'
|
|
})
|
|
assert.throws(() => buildCommercialMutation('transitionSubscription', {
|
|
resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'active', reason: '恢复'
|
|
}), /目标订阅状态无效/u)
|
|
assert.throws(() => buildCommercialMutation('transitionSubscription', {
|
|
resourceId: 'sub-1', expectedVersion: 3, targetStatus: 'canceled', reason: 'x'
|
|
}), /至少填写 2/u)
|
|
})
|
|
|
|
function metric(key, label, values, status = 'available') {
|
|
return { key, label, status, values, ratios: [], reason: '真实事实口径', requiredInputs: [], notes: [] }
|
|
}
|
|
|
|
function unavailable(key, label) {
|
|
return { key, label, status: 'unavailable', values: [], ratios: [], reason: '缺少真实财务证据', requiredInputs: ['真实财务证据'], notes: [] }
|
|
}
|