@spottoai/types-package 1.0.2-dev1138.2 → 1.0.2-dev1138.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (80) hide show
  1. package/dist/azure/financialAnalytics.d.ts +68 -2
  2. package/dist/azure/financialAnalytics.d.ts.map +1 -1
  3. package/dist/azure/financialAnalyticsDelivery.d.ts +2 -0
  4. package/dist/azure/financialAnalyticsDelivery.d.ts.map +1 -1
  5. package/dist/azure/financialAnalyticsDelivery.js +4 -1
  6. package/dist/azure/financialAnalyticsDelivery.js.map +1 -1
  7. package/dist/azure/financialAnalyticsDisplayKernel.d.ts +12 -0
  8. package/dist/azure/financialAnalyticsDisplayKernel.d.ts.map +1 -0
  9. package/dist/azure/financialAnalyticsDisplayKernel.js +144 -0
  10. package/dist/azure/financialAnalyticsDisplayKernel.js.map +1 -0
  11. package/dist/azure/financialAnalyticsValidation.d.ts.map +1 -1
  12. package/dist/azure/financialAnalyticsValidation.js +61 -7
  13. package/dist/azure/financialAnalyticsValidation.js.map +1 -1
  14. package/dist/azure/financialAuthorityView.d.ts +4 -0
  15. package/dist/azure/financialAuthorityView.d.ts.map +1 -1
  16. package/dist/azure/financialAuthorityView.js.map +1 -1
  17. package/dist/azure/financialAuthorityViewValidation.d.ts.map +1 -1
  18. package/dist/azure/financialAuthorityViewValidation.js +39 -0
  19. package/dist/azure/financialAuthorityViewValidation.js.map +1 -1
  20. package/dist/azure/financialChargeComposition.d.ts +81 -0
  21. package/dist/azure/financialChargeComposition.d.ts.map +1 -0
  22. package/dist/azure/financialChargeComposition.js +41 -0
  23. package/dist/azure/financialChargeComposition.js.map +1 -0
  24. package/dist/azure/financialChargeCompositionKernel.d.ts +3 -0
  25. package/dist/azure/financialChargeCompositionKernel.d.ts.map +1 -0
  26. package/dist/azure/financialChargeCompositionKernel.js +49 -0
  27. package/dist/azure/financialChargeCompositionKernel.js.map +1 -0
  28. package/dist/azure/financialChargeCompositionValidation.d.ts +8 -0
  29. package/dist/azure/financialChargeCompositionValidation.d.ts.map +1 -0
  30. package/dist/azure/financialChargeCompositionValidation.js +164 -0
  31. package/dist/azure/financialChargeCompositionValidation.js.map +1 -0
  32. package/dist/azure/financialDataflow.d.ts +8 -1
  33. package/dist/azure/financialDataflow.d.ts.map +1 -1
  34. package/dist/azure/financialDataflow.js.map +1 -1
  35. package/dist/azure/financialDataflowKernel.d.ts +2 -0
  36. package/dist/azure/financialDataflowKernel.d.ts.map +1 -1
  37. package/dist/azure/financialDataflowKernel.js +59 -7
  38. package/dist/azure/financialDataflowKernel.js.map +1 -1
  39. package/dist/azure/financialDataflowValidation.d.ts +1 -1
  40. package/dist/azure/financialDataflowValidation.d.ts.map +1 -1
  41. package/dist/azure/financialDataflowValidation.js +146 -28
  42. package/dist/azure/financialDataflowValidation.js.map +1 -1
  43. package/dist/azure/financialPolicy.d.ts +3 -0
  44. package/dist/azure/financialPolicy.d.ts.map +1 -1
  45. package/dist/azure/financialPolicy.js.map +1 -1
  46. package/dist/azure/financialPolicyValidation.d.ts.map +1 -1
  47. package/dist/azure/financialPolicyValidation.js +5 -0
  48. package/dist/azure/financialPolicyValidation.js.map +1 -1
  49. package/dist/azure/financialScope.d.ts +4 -0
  50. package/dist/azure/financialScope.d.ts.map +1 -1
  51. package/dist/azure/financialScope.js +4 -0
  52. package/dist/azure/financialScope.js.map +1 -1
  53. package/dist/azure/metrics.d.ts +2 -2
  54. package/dist/azure/metrics.d.ts.map +1 -1
  55. package/dist/azure/prices.d.ts +16 -3
  56. package/dist/azure/prices.d.ts.map +1 -1
  57. package/dist/azure/recommendations.d.ts +12 -4
  58. package/dist/azure/recommendations.d.ts.map +1 -1
  59. package/dist/azure/reports.d.ts +3 -2
  60. package/dist/azure/reports.d.ts.map +1 -1
  61. package/dist/azure/reports.js.map +1 -1
  62. package/dist/azure/resources.d.ts +12 -0
  63. package/dist/azure/resources.d.ts.map +1 -1
  64. package/dist/azure/subscriptions.d.ts +4 -2
  65. package/dist/azure/subscriptions.d.ts.map +1 -1
  66. package/dist/azure/views.d.ts +7 -2
  67. package/dist/azure/views.d.ts.map +1 -1
  68. package/dist/azure/views.js.map +1 -1
  69. package/dist/esm/azure/financialAnalyticsDelivery.js +2 -0
  70. package/dist/esm/azure/financialAnalyticsDisplayKernel.js +139 -0
  71. package/dist/esm/azure/financialAnalyticsValidation.js +61 -7
  72. package/dist/esm/azure/financialAuthorityViewValidation.js +39 -0
  73. package/dist/esm/azure/financialChargeComposition.js +36 -0
  74. package/dist/esm/azure/financialChargeCompositionKernel.js +44 -0
  75. package/dist/esm/azure/financialChargeCompositionValidation.js +155 -0
  76. package/dist/esm/azure/financialDataflowKernel.js +59 -7
  77. package/dist/esm/azure/financialDataflowValidation.js +146 -28
  78. package/dist/esm/azure/financialPolicyValidation.js +5 -0
  79. package/dist/esm/azure/financialScope.js +4 -0
  80. package/package.json +3 -2
@@ -2,6 +2,8 @@ import { sha256Utf8 } from '../common/sha256.js';
2
2
  import { formatExactDecimalValue, sumCanonicalDecimals } from '../common/exactDecimal.js';
3
3
  import { toCanonicalEstimateLensV1 } from './costComposition.js';
4
4
  import { isCanonicalExactMoney } from './financialValidationPrimitives.js';
5
+ import { AZURE_BILLED_ALL_CHARGES_POLICY_V1, resolveFinancialChargeInclusionPolicyV1, } from './financialChargeComposition.js';
6
+ import { isFinancialChargeCompositionV1, selectFinancialChargesV1 } from './financialChargeCompositionValidation.js';
5
7
  import { isCompleteFinancialBaselinePeriodV2 } from './financialScopeBaseline.js';
6
8
  import { isFinancialScopeBaselineEnvelopeV2 } from './financialScopeBaselineValidation.js';
7
9
  import { FINANCIAL_CURRENT_SPEND_COMPOSITION_CONTRACT_VERSION_V1, FINANCIAL_DATAFLOW_SCHEMA_VERSION_V1, } from './financialDataflow.js';
@@ -361,7 +363,18 @@ export const isFinancialDataflowScopeSelectorV1 = (value) => {
361
363
  };
362
364
  export const isFinancialDataflowCoordinateV1 = (value) => {
363
365
  if (!isFinancialDataflowRecordV1(value) ||
364
- !hasFinancialDataflowExactFieldsV1(value, ['companyId', 'provider', 'providerAccountRefs', 'scope', 'periodRole', 'period', 'costBasis', 'estimateLens', 'accountingCurrency'], ['requestedCurrencyCode']) ||
366
+ !hasFinancialDataflowExactFieldsV1(value, [
367
+ 'companyId',
368
+ 'provider',
369
+ 'providerAccountRefs',
370
+ 'scope',
371
+ 'periodRole',
372
+ 'period',
373
+ 'costBasis',
374
+ 'estimateLens',
375
+ 'accountingCurrency',
376
+ 'chargeInclusionPolicyRef',
377
+ ], ['requestedCurrencyCode']) ||
365
378
  !isFinancialDataflowIdentityV1(value.companyId) ||
366
379
  value.provider !== 'azure' ||
367
380
  !isFinancialDataflowSortedUniqueStringsV1(value.providerAccountRefs, FINANCIAL_DATAFLOW_LIMITS_V1.maximumProviderAccounts) ||
@@ -375,7 +388,12 @@ export const isFinancialDataflowCoordinateV1 = (value) => {
375
388
  typeof value.estimateLens !== 'string' ||
376
389
  !ESTIMATE_LENSES.has(value.estimateLens) ||
377
390
  (value.requestedCurrencyCode !== undefined && !isFinancialDataflowCurrencyV1(value.requestedCurrencyCode)) ||
378
- !isFinancialDataflowRecordV1(value.accountingCurrency)) {
391
+ !isFinancialDataflowRecordV1(value.accountingCurrency) ||
392
+ !isFinancialDataflowRecordV1(value.chargeInclusionPolicyRef) ||
393
+ !hasFinancialDataflowExactFieldsV1(value.chargeInclusionPolicyRef, ['policyId', 'policyDigest']) ||
394
+ !isFinancialDataflowIdentityV1(value.chargeInclusionPolicyRef.policyId) ||
395
+ !isFinancialDataflowHashV1(value.chargeInclusionPolicyRef.policyDigest) ||
396
+ resolveFinancialChargeInclusionPolicyV1(value.chargeInclusionPolicyRef) === undefined) {
379
397
  return false;
380
398
  }
381
399
  if (value.accountingCurrency.status === 'resolved') {
@@ -403,7 +421,9 @@ const isMember = (value) => {
403
421
  if (!isFinancialDataflowRecordV1(value) || !isFinancialDataflowIdentityV1(value.memberScopeId))
404
422
  return false;
405
423
  if (value.status === 'included') {
406
- return hasFinancialDataflowExactFieldsV1(value, ['memberScopeId', 'baselineId', 'status']) && isFinancialDataflowHashV1(value.baselineId);
424
+ return (hasFinancialDataflowExactFieldsV1(value, ['memberScopeId', 'baselineId', 'chargeCompositionId', 'status']) &&
425
+ isFinancialDataflowHashV1(value.baselineId) &&
426
+ isFinancialDataflowHashV1(value.chargeCompositionId));
407
427
  }
408
428
  return (value.status === 'unavailable' &&
409
429
  hasFinancialDataflowExactFieldsV1(value, ['memberScopeId', 'status', 'reasonCode']) &&
@@ -421,15 +441,7 @@ export const canonicalizeCurrentSpendMembershipV1 = (members) => {
421
441
  export const createCurrentSpendMembershipDigestV1 = (members) => `sha256:${sha256Utf8(canonicalizeCurrentSpendMembershipV1(members))}`;
422
442
  const isCompositionIdentity = (value) => {
423
443
  if (!isFinancialDataflowRecordV1(value) ||
424
- !hasFinancialDataflowExactFieldsV1(value, [
425
- 'schemaVersion',
426
- 'contractVersion',
427
- 'coordinate',
428
- 'members',
429
- 'amount',
430
- 'membershipDigest',
431
- 'algorithmVersion',
432
- ]) ||
444
+ !hasFinancialDataflowExactFieldsV1(value, ['schemaVersion', 'contractVersion', 'coordinate', 'members', 'amount', 'membershipDigest', 'algorithmVersion'], ['chargeSelection']) ||
433
445
  value.schemaVersion !== FINANCIAL_DATAFLOW_SCHEMA_VERSION_V1 ||
434
446
  value.contractVersion !== FINANCIAL_CURRENT_SPEND_COMPOSITION_CONTRACT_VERSION_V1 ||
435
447
  !isFinancialDataflowCoordinateV1(value.coordinate) ||
@@ -445,11 +457,45 @@ const isCompositionIdentity = (value) => {
445
457
  return false;
446
458
  }
447
459
  const currency = value.coordinate.accountingCurrency.status === 'resolved' ? value.coordinate.accountingCurrency.currencyCode : undefined;
460
+ const isChargeSelection = (selection) => {
461
+ if (!isFinancialDataflowRecordV1(selection) ||
462
+ !hasFinancialDataflowExactFieldsV1(selection, [
463
+ 'status',
464
+ 'includedAmount',
465
+ 'excludedAmount',
466
+ 'withheldAmount',
467
+ 'forecastEligibleAmount',
468
+ 'oneTimeAmount',
469
+ 'unknownRecurrenceAmount',
470
+ 'forecastStatus',
471
+ 'currencyCode',
472
+ ], ['reasonCodes', 'forecastReasonCodes']) ||
473
+ (selection.status !== 'available' && selection.status !== 'partial') ||
474
+ (selection.forecastStatus !== 'available' && selection.forecastStatus !== 'partial') ||
475
+ !isFinancialDataflowCurrencyV1(selection.currencyCode) ||
476
+ ![
477
+ selection.includedAmount,
478
+ selection.excludedAmount,
479
+ selection.withheldAmount,
480
+ selection.forecastEligibleAmount,
481
+ selection.oneTimeAmount,
482
+ selection.unknownRecurrenceAmount,
483
+ ].every(amount => isCanonicalExactMoney({ amount, currencyCode: selection.currencyCode })))
484
+ return false;
485
+ return ((selection.status === 'available' ? selection.reasonCodes === undefined : isReasonCodes(selection.reasonCodes)) &&
486
+ (selection.forecastStatus === 'available'
487
+ ? selection.forecastReasonCodes === undefined
488
+ : isReasonCodes(selection.forecastReasonCodes)));
489
+ };
448
490
  if (value.amount.status === 'available') {
449
491
  return (currency !== undefined &&
450
492
  hasFinancialDataflowExactFieldsV1(value.amount, ['status', 'amount', 'currencyCode']) &&
451
493
  isCanonicalExactMoney({ amount: value.amount.amount, currencyCode: value.amount.currencyCode }) &&
452
494
  value.amount.currencyCode === currency &&
495
+ isChargeSelection(value.chargeSelection) &&
496
+ value.chargeSelection.status === 'available' &&
497
+ value.chargeSelection.currencyCode === currency &&
498
+ value.chargeSelection.includedAmount === value.amount.amount &&
453
499
  value.members.length > 0 &&
454
500
  value.members.every(member => member.status === 'included'));
455
501
  }
@@ -459,11 +505,17 @@ const isCompositionIdentity = (value) => {
459
505
  isCanonicalExactMoney({ amount: value.amount.knownAmount, currencyCode: value.amount.currencyCode }) &&
460
506
  value.amount.currencyCode === currency &&
461
507
  isReasonCodes(value.amount.reasonCodes) &&
508
+ isChargeSelection(value.chargeSelection) &&
509
+ value.chargeSelection.currencyCode === currency &&
510
+ value.chargeSelection.includedAmount === value.amount.knownAmount &&
462
511
  value.members.some(member => member.status === 'included') &&
463
- (value.members.some(member => member.status === 'unavailable') || value.amount.reasonCodes.includes('coverage-incomplete')));
512
+ (value.members.some(member => member.status === 'unavailable') ||
513
+ value.amount.reasonCodes.includes('coverage-incomplete') ||
514
+ value.chargeSelection.status === 'partial'));
464
515
  }
465
516
  const unresolvedCurrency = value.coordinate.accountingCurrency;
466
517
  return (value.amount.status === 'unavailable' &&
518
+ value.chargeSelection === undefined &&
467
519
  hasFinancialDataflowExactFieldsV1(value.amount, ['status', 'reasonCodes']) &&
468
520
  isReasonCodes(value.amount.reasonCodes) &&
469
521
  (currency !== undefined || (unresolvedCurrency.status === 'unresolved' && value.amount.reasonCodes.includes(unresolvedCurrency.reasonCode))));
@@ -480,6 +532,19 @@ export const canonicalizeCurrentSpendCompositionIdentityV1 = (value) => {
480
532
  },
481
533
  members: [...value.members].sort((left, right) => left.memberScopeId.localeCompare(right.memberScopeId)),
482
534
  amount: value.amount.status === 'available' ? value.amount : { ...value.amount, reasonCodes: [...value.amount.reasonCodes].sort() },
535
+ ...(value.chargeSelection === undefined
536
+ ? {}
537
+ : {
538
+ chargeSelection: {
539
+ ...value.chargeSelection,
540
+ ...(value.chargeSelection.reasonCodes === undefined
541
+ ? {}
542
+ : { reasonCodes: [...value.chargeSelection.reasonCodes].sort() }),
543
+ ...(value.chargeSelection.forecastReasonCodes === undefined
544
+ ? {}
545
+ : { forecastReasonCodes: [...value.chargeSelection.forecastReasonCodes].sort() }),
546
+ },
547
+ }),
483
548
  };
484
549
  return canonicalizeFinancialDataflowJsonV1(canonical);
485
550
  };
@@ -496,7 +561,7 @@ export const isCurrentSpendCompositionV1 = (value) => {
496
561
  'amount',
497
562
  'membershipDigest',
498
563
  'algorithmVersion',
499
- ])) {
564
+ ], ['chargeSelection'])) {
500
565
  return false;
501
566
  }
502
567
  const { compositionId, ...identity } = value;
@@ -511,65 +576,118 @@ const hasCompatibleBaselineCoordinate = (coordinate, baseline) => baseline.provi
511
576
  toCanonicalEstimateLensV1(baseline.estimateLens) === coordinate.estimateLens &&
512
577
  baseline.requestedCurrencyCode === coordinate.requestedCurrencyCode;
513
578
  /** Proves that composition membership and money reconcile to exact V2 baseline envelopes. */
514
- export const isCurrentSpendCompositionCompatibleV1 = (composition, baselines) => {
579
+ export const isCurrentSpendCompositionCompatibleV1 = (composition, baselines, chargeCompositions) => {
515
580
  if (!isCurrentSpendCompositionV1(composition) ||
516
581
  !Array.isArray(baselines) ||
517
582
  baselines.length !== composition.members.length ||
518
583
  baselines.length > FINANCIAL_DATAFLOW_LIMITS_V1.maximumMembers ||
519
- !baselines.every(isFinancialScopeBaselineEnvelopeV2)) {
584
+ !baselines.every(isFinancialScopeBaselineEnvelopeV2) ||
585
+ !Array.isArray(chargeCompositions) ||
586
+ !chargeCompositions.every(isFinancialChargeCompositionV1)) {
520
587
  return false;
521
588
  }
522
589
  const typedBaselines = baselines;
590
+ const typedChargeCompositions = chargeCompositions;
591
+ const chargeCompositionById = new Map(typedChargeCompositions.map(value => [value.chargeCompositionId, value]));
592
+ if (chargeCompositionById.size !== typedChargeCompositions.length)
593
+ return false;
523
594
  const baselineByScopeId = new Map(typedBaselines.map(baseline => [baseline.scopeId, baseline]));
524
595
  if (baselineByScopeId.size !== typedBaselines.length ||
525
596
  new Set(typedBaselines.filter(baseline => baseline.status === 'available').map(baseline => baseline.baselineId)).size !==
526
597
  typedBaselines.filter(baseline => baseline.status === 'available').length) {
527
598
  return false;
528
599
  }
529
- const includedAmounts = [];
600
+ const includedSelections = [];
530
601
  const expectedReasonCodes = new Set();
531
602
  for (const member of composition.members) {
532
603
  const baseline = baselineByScopeId.get(member.memberScopeId);
533
604
  if (baseline === undefined || !hasCompatibleBaselineCoordinate(composition.coordinate, baseline))
534
605
  return false;
535
606
  if (member.status === 'included') {
536
- if (baseline.status !== 'available' || member.baselineId !== baseline.baselineId)
607
+ if (baseline.status !== 'available' ||
608
+ baseline.baselineKind !== 'owner' ||
609
+ member.baselineId !== baseline.baselineId ||
610
+ canonicalizeFinancialDataflowJsonV1(baseline.chargeInclusionPolicyRef) !==
611
+ canonicalizeFinancialDataflowJsonV1(AZURE_BILLED_ALL_CHARGES_POLICY_V1.policyRef))
612
+ return false;
613
+ const chargeComposition = chargeCompositionById.get(member.chargeCompositionId);
614
+ if (chargeComposition === undefined ||
615
+ chargeComposition.baselineId !== baseline.baselineId ||
616
+ chargeComposition.ownerScopeId !== baseline.scopeId ||
617
+ chargeComposition.period.windowKind !== baseline.period.windowKind ||
618
+ canonicalizeFinancialDataflowJsonV1(chargeComposition.period.requested) !== canonicalizeFinancialDataflowJsonV1(baseline.period.requested) ||
619
+ chargeComposition.costBasis !== baseline.costBasis ||
620
+ chargeComposition.estimateLens !== baseline.estimateLens ||
621
+ chargeComposition.accountingCurrencyCode !== baseline.total.currencyCode ||
622
+ chargeComposition.reconciliation.sourceTotal !== baseline.total.amount)
537
623
  return false;
538
624
  if (composition.coordinate.accountingCurrency.status === 'resolved' &&
539
625
  baseline.total.currencyCode !== composition.coordinate.accountingCurrency.currencyCode) {
540
626
  return false;
541
627
  }
542
- includedAmounts.push(baseline.total.amount);
628
+ includedSelections.push(selectFinancialChargesV1(chargeComposition, composition.coordinate.chargeInclusionPolicyRef));
629
+ includedSelections[includedSelections.length - 1].reasonCodes?.forEach(reason => expectedReasonCodes.add(reason));
543
630
  if (baseline.status === 'available' && !isCompleteFinancialBaselinePeriodV2(baseline.period)) {
544
631
  expectedReasonCodes.add('coverage-incomplete');
545
632
  }
546
633
  continue;
547
634
  }
548
- if (baseline.status !== 'unavailable' || member.reasonCode !== baseline.unavailableReason)
549
- return false;
550
- expectedReasonCodes.add(baseline.unavailableReason);
635
+ if (baseline.status === 'unavailable') {
636
+ if (member.reasonCode !== baseline.unavailableReason)
637
+ return false;
638
+ expectedReasonCodes.add(baseline.unavailableReason);
639
+ }
640
+ else {
641
+ const expectedDerivedReason = baseline.baselineKind === 'aggregate'
642
+ ? 'charge-composition-requires-owner-baseline'
643
+ : 'charge-composition-unavailable';
644
+ if (member.reasonCode !== expectedDerivedReason)
645
+ return false;
646
+ expectedReasonCodes.add(expectedDerivedReason);
647
+ }
551
648
  }
552
649
  if (composition.coordinate.accountingCurrency.status === 'unresolved') {
553
650
  expectedReasonCodes.add(composition.coordinate.accountingCurrency.reasonCode);
554
651
  }
652
+ if (typedChargeCompositions.length !== includedSelections.length)
653
+ return false;
555
654
  const hasExpectedReasonCodes = (reasonCodes) => reasonCodes.length === expectedReasonCodes.size && reasonCodes.every(reasonCode => expectedReasonCodes.has(reasonCode));
556
655
  if (composition.coordinate.accountingCurrency.status === 'unresolved') {
557
656
  return composition.amount.status === 'unavailable' && hasExpectedReasonCodes(composition.amount.reasonCodes);
558
657
  }
559
- if (includedAmounts.length === 0) {
658
+ if (includedSelections.length === 0) {
560
659
  return composition.amount.status === 'unavailable' && hasExpectedReasonCodes(composition.amount.reasonCodes);
561
660
  }
562
- let includedTotal;
661
+ let aggregateSelection;
563
662
  try {
564
- includedTotal = formatExactDecimalValue(sumCanonicalDecimals(includedAmounts));
663
+ const reasonCodes = new Set(includedSelections.flatMap(selection => selection.reasonCodes ?? []));
664
+ const forecastReasonCodes = new Set(includedSelections.flatMap(selection => selection.forecastReasonCodes ?? []));
665
+ aggregateSelection = {
666
+ status: includedSelections.some(selection => selection.status === 'partial') ? 'partial' : 'available',
667
+ includedAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.includedAmount))),
668
+ excludedAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.excludedAmount))),
669
+ withheldAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.withheldAmount))),
670
+ forecastEligibleAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.forecastEligibleAmount))),
671
+ oneTimeAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.oneTimeAmount))),
672
+ unknownRecurrenceAmount: formatExactDecimalValue(sumCanonicalDecimals(includedSelections.map(selection => selection.unknownRecurrenceAmount))),
673
+ forecastStatus: includedSelections.some(selection => selection.forecastStatus === 'partial') ? 'partial' : 'available',
674
+ currencyCode: composition.coordinate.accountingCurrency.currencyCode,
675
+ ...(reasonCodes.size === 0 ? {} : { reasonCodes: [...reasonCodes].sort() }),
676
+ ...(forecastReasonCodes.size === 0
677
+ ? {}
678
+ : { forecastReasonCodes: [...forecastReasonCodes].sort() }),
679
+ };
565
680
  }
566
681
  catch {
567
682
  return false;
568
683
  }
569
- if (includedAmounts.length === composition.members.length && expectedReasonCodes.size === 0) {
570
- return composition.amount.status === 'available' && composition.amount.amount === includedTotal;
684
+ const sameSelection = canonicalizeFinancialDataflowJsonV1(composition.chargeSelection) === canonicalizeFinancialDataflowJsonV1(aggregateSelection);
685
+ if (!sameSelection)
686
+ return false;
687
+ if (includedSelections.length === composition.members.length && expectedReasonCodes.size === 0) {
688
+ return composition.amount.status === 'available' && composition.amount.amount === aggregateSelection.includedAmount;
571
689
  }
572
690
  return (composition.amount.status === 'partial' &&
573
- composition.amount.knownAmount === includedTotal &&
691
+ composition.amount.knownAmount === aggregateSelection.includedAmount &&
574
692
  hasExpectedReasonCodes(composition.amount.reasonCodes));
575
693
  };
@@ -1,6 +1,7 @@
1
1
  import { multiplyExactDecimalValues, parseCanonicalDecimal, subtractExactDecimalValues } from '../common/exactDecimal.js';
2
2
  import { sha256Utf8 } from '../common/sha256.js';
3
3
  import { isCanonicalExactMoney } from './financialValidationPrimitives.js';
4
+ import { isFinancialChargeInclusionPolicyRefV1 } from './financialChargeCompositionValidation.js';
4
5
  import { FINANCIAL_POLICY_DEFINITION_CONTRACT_VERSION_V1, FINANCIAL_POLICY_EVALUATION_CONTRACT_VERSION_V1, } from './financialPolicy.js';
5
6
  import { FINANCIAL_DATAFLOW_LIMITS_V1, canonicalizeFinancialDataflowJsonV1, canonicalizeFinancialDataflowCoordinateV1, hasFinancialDataflowExactFieldsV1, isFinancialDataflowCurrencyV1, createFinancialDataflowCoordinateIdV1, isFinancialDataflowHashV1, isFinancialDataflowIdentityV1, isFinancialDataflowIsoInstantV1, isFinancialDataflowRecordV1, isFinancialDataflowScopeSelectorV1, isFinancialDataflowSortedUniqueStringsV1, isFinancialDataflowValueWithinLimitsV1, } from './financialDataflowValidation.js';
6
7
  import { isCurrentSpendCompositionV1 } from './financialDataflowValidation.js';
@@ -85,6 +86,7 @@ const isCoordinateRequest = (value, companyId) => isFinancialDataflowRecordV1(va
85
86
  'period',
86
87
  'costBasis',
87
88
  'estimateLens',
89
+ 'chargeInclusionPolicyRef',
88
90
  'requiredAccountingCurrencyCode',
89
91
  ]) &&
90
92
  value.provider === 'azure' &&
@@ -107,6 +109,7 @@ const isCoordinateRequest = (value, companyId) => isFinancialDataflowRecordV1(va
107
109
  COST_BASES.has(value.costBasis) &&
108
110
  typeof value.estimateLens === 'string' &&
109
111
  ESTIMATE_LENSES.has(value.estimateLens) &&
112
+ isFinancialChargeInclusionPolicyRefV1(value.chargeInclusionPolicyRef) &&
110
113
  isFinancialDataflowCurrencyV1(value.requiredAccountingCurrencyCode) &&
111
114
  isFinancialDataflowIdentityV1(companyId);
112
115
  const isCriteria = (value, currencyCode) => {
@@ -418,6 +421,8 @@ export const isFinancialPolicyEvaluationCompatibleV1 = (evaluation, definition,
418
421
  request.scope.scopeId !== coordinate.scope.scopeId ||
419
422
  request.costBasis !== coordinate.costBasis ||
420
423
  request.estimateLens !== coordinate.estimateLens ||
424
+ canonicalizeFinancialDataflowJsonV1(request.chargeInclusionPolicyRef) !==
425
+ canonicalizeFinancialDataflowJsonV1(coordinate.chargeInclusionPolicyRef) ||
421
426
  coordinate.requestedCurrencyCode !== request.requiredAccountingCurrencyCode ||
422
427
  (currency === undefined
423
428
  ? currentSpendComposition.amount.status !== 'unavailable'
@@ -2,6 +2,9 @@ export * from './financialScopeEvidence.js';
2
2
  export * from './financialScopeBaseline.js';
3
3
  export * from './financialScopeBaselineIdentity.js';
4
4
  export * from './financialScopeBaselineValidation.js';
5
+ export * from './financialChargeComposition.js';
6
+ export * from './financialChargeCompositionValidation.js';
7
+ export * from './financialChargeCompositionKernel.js';
5
8
  export * from './financialEvidenceAssessmentValidation.js';
6
9
  export * from './financialProjection.js';
7
10
  export * from './financialProjectionValidation.js';
@@ -18,6 +21,7 @@ export * from './financialDataflowValidation.js';
18
21
  export * from './financialDataflowKernel.js';
19
22
  export * from './financialAnalytics.js';
20
23
  export * from './financialAnalyticsValidation.js';
24
+ export * from './financialAnalyticsDisplayKernel.js';
21
25
  export * from './financialAnalyticsDelivery.js';
22
26
  export * from './financialAnalyticsDeliveryValidation.js';
23
27
  export * from './financialPolicy.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spottoai/types-package",
3
- "version": "1.0.2-dev1138.2",
3
+ "version": "1.0.2-dev1138.3",
4
4
  "description": "Shared TypeScript interfaces for SpottoAI",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -86,10 +86,11 @@
86
86
  "check:financial-projection-contracts": "node scripts/check-financial-projection-contracts.mjs",
87
87
  "check:financial-authority-view-contracts": "node scripts/check-financial-authority-view-contracts.mjs",
88
88
  "check:financial-dataflow-contracts": "node scripts/check-financial-dataflow-contracts.mjs",
89
+ "check:financial-charge-composition-contracts": "node scripts/check-financial-charge-composition-contracts.mjs",
89
90
  "check:capability-passport-contracts": "node scripts/check-capability-passport-contracts.mjs",
90
91
  "check:commitments-planning": "node scripts/check-commitments-planning.mjs",
91
92
  "prepublishOnly": "npm run clean && npm run build && npm test",
92
- "test": "npm run build && npm run check:artifact-evidence-contracts && npm run check:billing-artifact-object-boundaries && npm run check:billing-cost-analysis-read-responses && npm run check:aws-examples && npm run check:plugin-public-artifacts && npm run check:portal-public-artifacts && npm run check:relationship-public-artifacts && npm run check:azure-view-set-contracts && npm run check:dev-1135-latest-contracts && npm run check:financial-scope-baseline-contracts && npm run check:financial-projection-contracts && npm run check:financial-authority-view-contracts && npm run check:financial-dataflow-contracts && npm run check:capability-passport-contracts && npm run check:commitments-planning && npm run check:packed-exports",
93
+ "test": "npm run build && npm run check:artifact-evidence-contracts && npm run check:billing-artifact-object-boundaries && npm run check:billing-cost-analysis-read-responses && npm run check:aws-examples && npm run check:plugin-public-artifacts && npm run check:portal-public-artifacts && npm run check:relationship-public-artifacts && npm run check:azure-view-set-contracts && npm run check:dev-1135-latest-contracts && npm run check:financial-scope-baseline-contracts && npm run check:financial-projection-contracts && npm run check:financial-authority-view-contracts && npm run check:financial-charge-composition-contracts && npm run check:financial-dataflow-contracts && npm run check:capability-passport-contracts && npm run check:commitments-planning && npm run check:packed-exports",
93
94
  "lint": "eslint src --ext .ts",
94
95
  "lint:fix": "eslint src --ext .ts --fix",
95
96
  "format": "prettier --write \"src/**/*.{ts,js,json}\"",