@dainamite/cpq 0.5.0 → 0.7.0
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.
- package/CHANGELOG.md +64 -0
- package/MIGRATION.md +74 -0
- package/dist/modules/cpq/acl.js +1 -0
- package/dist/modules/cpq/acl.js.map +2 -2
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/route.js +47 -2
- package/dist/modules/cpq/api/orders/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/price/route.js +43 -3
- package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +3 -3
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +174 -184
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +140 -280
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +208 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/ce.js +8 -0
- package/dist/modules/cpq/ce.js.map +2 -2
- package/dist/modules/cpq/cli.js +34 -1
- package/dist/modules/cpq/cli.js.map +2 -2
- package/dist/modules/cpq/commands/price-adjustments.js +107 -0
- package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
- package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
- package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
- package/dist/modules/cpq/data/entities.js +97 -0
- package/dist/modules/cpq/data/entities.js.map +2 -2
- package/dist/modules/cpq/data/price-adjustments.js +70 -0
- package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/data/validators.js +4 -0
- package/dist/modules/cpq/data/validators.js.map +2 -2
- package/dist/modules/cpq/di.js +6 -0
- package/dist/modules/cpq/di.js.map +2 -2
- package/dist/modules/cpq/events.js +47 -0
- package/dist/modules/cpq/events.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/index.js +1 -1
- package/dist/modules/cpq/index.js.map +2 -2
- package/dist/modules/cpq/lib/auditActor.js +10 -0
- package/dist/modules/cpq/lib/auditActor.js.map +7 -0
- package/dist/modules/cpq/lib/formatMoney.js +7 -0
- package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
- package/dist/modules/cpq/lib/quotePermissions.js +13 -0
- package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
- package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
- package/dist/modules/cpq/services/cpqOrderService.js +25 -28
- package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
- package/dist/modules/cpq/services/cpqPricingService.js +7 -0
- package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
- package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
- package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
- package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
- package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
- package/dist/modules/cpq/services/types.js.map +2 -2
- package/dist/modules/cpq/setup.js +6 -0
- package/dist/modules/cpq/setup.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/acl.ts +1 -0
- package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
- package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
- package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
- package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
- package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
- package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
- package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
- package/src/modules/cpq/api/orders/route.ts +51 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
- package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
- package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
- package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
- package/src/modules/cpq/api/quotes/price/route.ts +28 -2
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +3 -3
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +164 -120
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +122 -308
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/ce.ts +8 -0
- package/src/modules/cpq/cli.ts +48 -1
- package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
- package/src/modules/cpq/commands/price-adjustments.ts +90 -0
- package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
- package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
- package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
- package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
- package/src/modules/cpq/data/entities.ts +83 -0
- package/src/modules/cpq/data/price-adjustments.ts +67 -0
- package/src/modules/cpq/data/validators.ts +3 -0
- package/src/modules/cpq/di.ts +6 -0
- package/src/modules/cpq/events.ts +53 -2
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/index.ts +1 -1
- package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
- package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
- package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
- package/src/modules/cpq/lib/auditActor.ts +13 -0
- package/src/modules/cpq/lib/formatMoney.ts +4 -0
- package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
- package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
- package/src/modules/cpq/lib/quotePermissions.ts +22 -0
- package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
- package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
- package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
- package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
- package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
- package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
- package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
- package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
- package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
- package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
- package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
- package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
- package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
- package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
- package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
- package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
- package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
- package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
- package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
- package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
- package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
- package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
- package/src/modules/cpq/services/cpqOrderService.ts +22 -28
- package/src/modules/cpq/services/cpqPricingService.ts +8 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
- package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
- package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
- package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
- package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
- package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
- package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
- package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
- package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
- package/src/modules/cpq/services/types.ts +34 -1
- package/src/modules/cpq/setup.ts +7 -1
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../src/modules/cpq/services/arcPriceFreeze.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Frozen Amend mirror lines.\n *\n * An Amend quote mirrors every live subscription item as a `modify` line.\n * Engine-priced mirror lines used to be re-priced at today's catalog price on\n * every recalculation, and activation wrote that price back to the\n * subscription item \u2014 so amending one item silently moved every other item's\n * price. A mirror line on an Amend quote is therefore created **frozen**: it\n * keeps the source item's stored charges until a pricing input of that line\n * changes, at which point it is re-priced as before.\n *\n * The flag lives in the line configuration jsonb next to the existing\n * `_arcMirrored*` markers, so no schema change is needed.\n */\n\nexport const ARC_PRICE_FROZEN_KEY = '_arcPriceFrozen'\n\n/**\n * Set when the server unfreezes an Amend mirror line because a pricing input\n * changed \u2014 drives the \"re-priced at the current catalog price\" notice (an\n * explicit marker, so Amend quotes created before freezing existed don't\n * show it).\n */\nexport const ARC_REPRICED_KEY = '_arcRepriced'\n\n/** Markers that describe quote-time pricing state and never leave the quote. */\nconst QUOTE_ONLY_ARC_KEYS = [ARC_PRICE_FROZEN_KEY, ARC_REPRICED_KEY] as const\n\nconst ARC_MARKER_PREFIX = '_arc'\n\nexport type ArcMirrorQuoteType = 'amend' | 'cancel' | 'renew'\n\nexport function isArcPriceFrozen(configuration: Record<string, unknown> | null | undefined): boolean {\n return configuration?.[ARC_PRICE_FROZEN_KEY] === true\n}\n\n/**\n * Whether recalculation must keep a line's stored charges. Only server-made\n * mirror lines on Amend quotes qualify \u2014 the flag alone is never trusted\n * (client input is stripped of `_arc*` keys, and a clone of an Amend quote\n * is a new quote).\n */\nexport function isArcMirrorLine(configuration: Record<string, unknown> | null | undefined, quoteType: string | null | undefined): boolean {\n return (quoteType === 'amend' || quoteType === 'renew' || quoteType === 'cancel') && typeof configuration?._arcMirroredFromItemId === 'string'\n}\n\nexport function isFrozenAmendMirrorLine(\n configuration: Record<string, unknown> | null | undefined,\n quoteType: string | null | undefined,\n): boolean {\n return (\n quoteType === 'amend' &&\n typeof configuration?._arcMirroredFromItemId === 'string' &&\n isArcPriceFrozen(configuration)\n )\n}\n\n/**\n * Configuration for a mirror line: the item's configuration plus the ARC\n * markers. Only Amend lines are frozen \u2014 Renew re-prices by product decision\n * and Cancel lines are not priced.\n */\nexport function buildArcMirrorConfiguration(\n itemConfiguration: Record<string, unknown> | null | undefined,\n item: { id: string; name: string },\n quoteType: ArcMirrorQuoteType,\n): Record<string, unknown> {\n return {\n // An item's configuration can carry markers from an earlier ARC quote\n // (activation copies line configuration onto the item); never inherit them.\n ...stripArcMarkers(itemConfiguration),\n _arcMirroredFromItemId: item.id,\n _arcMirroredName: item.name,\n ...(quoteType === 'amend' ? { [ARC_PRICE_FROZEN_KEY]: true } : {}),\n }\n}\n\n/**\n * Line configuration as it may leave the quote (order lines, subscription\n * items, clones): quote-only pricing markers are dropped; the descriptive\n * `_arcMirrored*` markers stay.\n */\nexport function stripQuoteOnlyArcMarkers(\n configuration: Record<string, unknown> | null | undefined,\n): Record<string, unknown> | null | undefined {\n if (!configuration || !QUOTE_ONLY_ARC_KEYS.some((key) => key in configuration)) return configuration\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(configuration)) {\n if (!(QUOTE_ONLY_ARC_KEYS as readonly string[]).includes(key)) out[key] = value\n }\n return out\n}\n\n/**\n * Configuration without any `_arc*` marker. ARC markers are server-owned:\n * client-supplied configuration goes through this before it is stored.\n */\nexport function stripArcMarkers(configuration: Record<string, unknown> | null | undefined): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(configuration ?? {})) {\n if (!key.startsWith(ARC_MARKER_PREFIX)) out[key] = value\n }\n return out\n}\n\nfunction canonical(value: unknown): string {\n if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`\n if (value && typeof value === 'object') {\n const entries = Object.keys(value as Record<string, unknown>)\n .sort()\n .map((key) => `${JSON.stringify(key)}:${canonical((value as Record<string, unknown>)[key])}`)\n return `{${entries.join(',')}}`\n }\n return JSON.stringify(value ?? null)\n}\n\nfunction isoDay(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString().slice(0, 10)\n return String(value).slice(0, 10)\n}\n\nexport type ArcLinePricingState = {\n configuration: Record<string, unknown> | null | undefined\n quantity: number\n termMonths?: number | null\n startDate?: Date | string | null\n endDate?: Date | string | null\n usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }> | null\n}\n\nexport type ArcLinePricingUpdate = {\n configuration: Record<string, unknown>\n quantity?: number\n termMonths?: number\n startDate?: string\n endDate?: string\n usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }>\n}\n\n/**\n * Whether an update changes any input the pricing engine reads. The quote UI\n * always resubmits the full configuration, so \"a field was sent\" is not a\n * change \u2014 values are compared (ARC markers ignored, key order ignored).\n * Fields the update omits keep their stored value.\n */\nexport function arcPricingInputsChanged(before: ArcLinePricingState, update: ArcLinePricingUpdate): boolean {\n if (canonical(stripArcMarkers(before.configuration)) !== canonical(stripArcMarkers(update.configuration))) {\n return true\n }\n if (update.quantity !== undefined && update.quantity !== before.quantity) return true\n if (update.termMonths !== undefined && update.termMonths !== (before.termMonths ?? undefined)) return true\n if (update.startDate !== undefined && isoDay(update.startDate) !== isoDay(before.startDate)) return true\n if (update.endDate !== undefined && isoDay(update.endDate) !== isoDay(before.endDate)) return true\n if (update.usageEstimates !== undefined) {\n const normalize = (list: Array<{ chargeCode: string; estimatedQuantity: number }> | null | undefined) =>\n canonical(\n [...(list ?? [])]\n .map((e) => ({ chargeCode: e.chargeCode, estimatedQuantity: e.estimatedQuantity }))\n .sort((a, b) => a.chargeCode.localeCompare(b.chargeCode)),\n )\n if (normalize(update.usageEstimates) !== normalize(before.usageEstimates)) return true\n }\n return false\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
var _a, _b;
|
|
2
|
+
import { storedAdjustmentFailure } from "./priceAdjustments/storedAdjustmentFailure.js";
|
|
3
|
+
import { withQuotePricingWrite } from "./priceAdjustments/quotePricingWrite.js";
|
|
2
4
|
import { SalesOrder, SalesOrderLine } from "@open-mercato/core/modules/sales/data/entities";
|
|
3
5
|
import {
|
|
4
6
|
CpqQuoteConfiguration,
|
|
@@ -14,6 +16,8 @@ import {
|
|
|
14
16
|
CPQ_ORDER_TRANSITIONS
|
|
15
17
|
} from "./types.js";
|
|
16
18
|
import { emitCpqEvent } from "../events.js";
|
|
19
|
+
import { stripQuoteOnlyArcMarkers } from "./arcPriceFreeze.js";
|
|
20
|
+
import { summarizeOrderPricing } from "./orderPricingSummary.js";
|
|
17
21
|
const CPQ_TO_SALES_ORDER_STATUS = {
|
|
18
22
|
draft: "draft",
|
|
19
23
|
pending_activation: "pending",
|
|
@@ -29,27 +33,15 @@ class DefaultCpqOrderService {
|
|
|
29
33
|
}
|
|
30
34
|
// ─── Convert Quote to Order ─────────────────────────────────
|
|
31
35
|
async convertQuoteToOrder(quoteId, scope) {
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (!cpqConfig) {
|
|
40
|
-
const byQuoteId = await em.findOne(CpqQuoteConfiguration, {
|
|
41
|
-
quoteId,
|
|
42
|
-
organizationId: scope.organizationId,
|
|
43
|
-
tenantId: scope.tenantId,
|
|
44
|
-
deletedAt: null
|
|
45
|
-
});
|
|
46
|
-
if (!byQuoteId) throw new OrderError(404, "Quote not found");
|
|
47
|
-
return this.convertFromConfig(byQuoteId, scope);
|
|
48
|
-
}
|
|
49
|
-
return this.convertFromConfig(cpqConfig, scope);
|
|
36
|
+
return withQuotePricingWrite(
|
|
37
|
+
this.em,
|
|
38
|
+
quoteId,
|
|
39
|
+
scope,
|
|
40
|
+
(em, quote) => this.convertFromConfig(quote, scope, em),
|
|
41
|
+
(status, message) => new OrderError(status, message)
|
|
42
|
+
);
|
|
50
43
|
}
|
|
51
|
-
async convertFromConfig(cpqConfig, scope) {
|
|
52
|
-
const em = this.em;
|
|
44
|
+
async convertFromConfig(cpqConfig, scope, em) {
|
|
53
45
|
const blockedStatuses = ["cancelled", "rejected"];
|
|
54
46
|
if (blockedStatuses.includes(cpqConfig.cpqStatus)) {
|
|
55
47
|
throw new OrderError(409, `Cannot convert a quote in '${cpqConfig.cpqStatus}' status`);
|
|
@@ -75,6 +67,8 @@ class DefaultCpqOrderService {
|
|
|
75
67
|
if (quoteLines.length === 0) {
|
|
76
68
|
throw new OrderError(422, "Cannot convert a quote with no line items");
|
|
77
69
|
}
|
|
70
|
+
const invalidAdjustment = storedAdjustmentFailure(cpqConfig, quoteLines);
|
|
71
|
+
if (invalidAdjustment) throw new OrderError(422, "ADJUSTMENT_INVALID", invalidAdjustment);
|
|
78
72
|
const { number: orderNumber } = await this.numberGenerator.generate({
|
|
79
73
|
kind: "order",
|
|
80
74
|
organizationId: scope.organizationId,
|
|
@@ -189,7 +183,8 @@ class DefaultCpqOrderService {
|
|
|
189
183
|
startDate: ql.startDate,
|
|
190
184
|
termMonths: ql.termMonths ?? null,
|
|
191
185
|
endDate: ql.endDate,
|
|
192
|
-
|
|
186
|
+
// Quote-only ARC pricing markers stay on the quote.
|
|
187
|
+
configuration: { ...stripQuoteOnlyArcMarkers(ql.configuration), offeringName },
|
|
193
188
|
quantity: ql.quantity,
|
|
194
189
|
charges: ql.charges ? [...ql.charges] : null,
|
|
195
190
|
nrcTotal: ql.nrcTotal,
|
|
@@ -457,7 +452,7 @@ class DefaultCpqOrderService {
|
|
|
457
452
|
offeringId: line.offeringId ?? void 0,
|
|
458
453
|
specId: line.specId ?? void 0,
|
|
459
454
|
name: this.getLineName(line),
|
|
460
|
-
configuration: line.configuration ?? {},
|
|
455
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
461
456
|
charges: line.charges ?? [],
|
|
462
457
|
mrcAmount: Number(line.mrcTotal),
|
|
463
458
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -519,7 +514,7 @@ class DefaultCpqOrderService {
|
|
|
519
514
|
purchasePrice: Number(line.nrcTotal),
|
|
520
515
|
quantity: line.quantity,
|
|
521
516
|
purchaseDate: (/* @__PURE__ */ new Date()).toISOString().split("T")[0],
|
|
522
|
-
configuration: line.configuration ?? {},
|
|
517
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
523
518
|
charges: line.charges ?? [],
|
|
524
519
|
sourceOrderId: orderConfig.id,
|
|
525
520
|
sourceQuoteLineId: line.sourceQuoteLineId ?? void 0
|
|
@@ -712,7 +707,7 @@ class DefaultCpqOrderService {
|
|
|
712
707
|
action: lc.action,
|
|
713
708
|
parentLineId: lc.parentLineId ?? null,
|
|
714
709
|
quantity: lc.quantity,
|
|
715
|
-
configuration: lc.configuration ?? {},
|
|
710
|
+
configuration: stripQuoteOnlyArcMarkers(lc.configuration) ?? {},
|
|
716
711
|
startDate: lc.startDate?.toISOString() ?? null,
|
|
717
712
|
termMonths: lc.termMonths ?? null,
|
|
718
713
|
endDate: lc.endDate?.toISOString() ?? null,
|
|
@@ -745,7 +740,8 @@ class DefaultCpqOrderService {
|
|
|
745
740
|
pricingSummary: {
|
|
746
741
|
nrcTotal: Number(pricing.nrcTotal ?? 0),
|
|
747
742
|
mrcTotal: Number(pricing.mrcTotal ?? 0),
|
|
748
|
-
currencyCode: orderConfig.currencyCode
|
|
743
|
+
currencyCode: orderConfig.currencyCode,
|
|
744
|
+
...summarizeOrderPricing(lines, quoteType, orderConfig.currencyCode)
|
|
749
745
|
},
|
|
750
746
|
activatedAt: orderConfig.activatedAt?.toISOString() ?? null,
|
|
751
747
|
createdAt: orderConfig.createdAt.toISOString(),
|
|
@@ -768,9 +764,10 @@ function pickArcSourceFromConfig(cfg, map) {
|
|
|
768
764
|
}
|
|
769
765
|
const ORDER_ERROR_BRAND = /* @__PURE__ */ Symbol.for("@dainamite/cpq/OrderError");
|
|
770
766
|
class OrderError extends (_b = Error, _a = ORDER_ERROR_BRAND, _b) {
|
|
771
|
-
constructor(status, message) {
|
|
767
|
+
constructor(status, message, details) {
|
|
772
768
|
super(message);
|
|
773
769
|
this.status = status;
|
|
770
|
+
this.details = details;
|
|
774
771
|
this[_a] = true;
|
|
775
772
|
this.name = "OrderError";
|
|
776
773
|
}
|
|
@@ -786,7 +783,7 @@ function buildArcLineChange(line, specMap) {
|
|
|
786
783
|
offeringId: line.offeringId ?? void 0,
|
|
787
784
|
specId: line.specId ?? void 0,
|
|
788
785
|
name: resolveLineName(line, specMap),
|
|
789
|
-
configuration: line.configuration ?? {},
|
|
786
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
790
787
|
charges: line.charges ?? null,
|
|
791
788
|
mrcAmount: Number(line.mrcTotal),
|
|
792
789
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -812,7 +809,7 @@ function buildArcLineChange(line, specMap) {
|
|
|
812
809
|
action,
|
|
813
810
|
sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,
|
|
814
811
|
modifyPatch: {
|
|
815
|
-
configuration: line.configuration ?? void 0,
|
|
812
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? void 0,
|
|
816
813
|
charges: line.charges ?? void 0,
|
|
817
814
|
mrcAmount: Number(line.mrcTotal),
|
|
818
815
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/services/cpqOrderService.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'\nimport type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'\nimport {\n CpqQuoteConfiguration,\n CpqQuoteLineConfiguration,\n CpqQuoteTargetSubscription,\n CpqInventorySubscription,\n CpqOrderConfiguration,\n CpqOrderLineConfiguration,\n CpqProductOffering,\n CpqProductSpecification,\n CpqInventorySubscriptionItem,\n} from '../data/entities'\nimport type {\n ApplyArcResult,\n ApplyMergeRenewalResult,\n ArcLineChange,\n CreateSubscriptionItemInput,\n DefaultCpqInventoryService,\n} from './cpqInventoryService'\nimport {\n CPQ_ORDER_TRANSITIONS,\n type CpqOrderStatus,\n type TenantScope,\n} from './types'\nimport { emitCpqEvent } from '../events'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface OrderListFilters {\n customerId?: string\n sourceQuoteId?: string\n salesOrderId?: string\n cpqStatus?: string | string[]\n currencyCode?: string\n search?: string\n sortField?: 'createdAt' | 'updatedAt' | 'cpqStatus' | 'activatedAt' | 'currencyCode'\n sortDir?: 'asc' | 'desc'\n page?: number\n pageSize?: number\n}\n\ninterface PaginatedResult<T> {\n items: T[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ninterface OrderLineResult {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: Array<Record<string, unknown>>\n sourceQuoteLineId: string | null\n /** XD-250 ARC: snapshot of the source subscription item's current state.\n * Null for non-ARC lines and for fresh `add` lines on amend quotes. */\n arcSource: {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n } | null\n}\n\ninterface OrderResult {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n /** XD-250 ARC quote type carried over from the source quote \u2014 used by the\n * order detail page to render the AMEND / RENEW / CANCEL badge. */\n quoteType: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n currencyCode: string\n }\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\ninterface OrderServiceDeps {\n em: EntityManager\n cpqInventoryService: DefaultCpqInventoryService\n salesDocumentNumberGenerator: SalesDocumentNumberGenerator\n}\n\nconst CPQ_TO_SALES_ORDER_STATUS: Record<CpqOrderStatus, string> = {\n draft: 'draft',\n pending_activation: 'pending',\n active: 'active',\n cancelled: 'cancelled',\n fulfilled: 'fulfilled',\n}\n\n// \u2500\u2500\u2500 Service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class DefaultCpqOrderService {\n private em: EntityManager\n private inventoryService: DefaultCpqInventoryService\n private numberGenerator: SalesDocumentNumberGenerator\n\n constructor(deps: OrderServiceDeps) {\n this.em = deps.em\n this.inventoryService = deps.cpqInventoryService\n this.numberGenerator = deps.salesDocumentNumberGenerator\n }\n\n // \u2500\u2500\u2500 Convert Quote to Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const cpqConfig = await em.findOne(CpqQuoteConfiguration, {\n id: quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!cpqConfig) {\n const byQuoteId = await em.findOne(CpqQuoteConfiguration, {\n quoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!byQuoteId) throw new OrderError(404, 'Quote not found')\n return this.convertFromConfig(byQuoteId, scope)\n }\n return this.convertFromConfig(cpqConfig, scope)\n }\n\n private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const blockedStatuses = ['cancelled', 'rejected']\n if (blockedStatuses.includes(cpqConfig.cpqStatus)) {\n throw new OrderError(409, `Cannot convert a quote in '${cpqConfig.cpqStatus}' status`)\n }\n\n // Idempotency guard: a quote may only convert once. If an order with\n // this quote as its `sourceQuoteId` already exists, the UI should\n // surface \"Go to order\" instead of re-running conversion \u2014 return\n // 409 with the existing id so the caller can redirect.\n const existingOrder = await em.findOne(CpqOrderConfiguration, {\n sourceQuoteId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (existingOrder) {\n throw new OrderError(\n 409,\n `Quote has already been converted to order ${existingOrder.orderId}`,\n )\n }\n\n const quoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (quoteLines.length === 0) {\n throw new OrderError(422, 'Cannot convert a quote with no line items')\n }\n\n const { number: orderNumber } = await this.numberGenerator.generate({\n kind: 'order',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n const salesOrderId = crypto.randomUUID()\n const pricingSummary = cpqConfig.pricingSummary as Record<string, unknown> | null\n const nrcTotal = Number(pricingSummary?.nrcTotal ?? 0)\n const mrcTotal = Number(pricingSummary?.mrcTotal ?? 0)\n const grandTotal = nrcTotal + mrcTotal\n\n const salesOrder = em.create(SalesOrder, {\n id: salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderNumber,\n customerEntityId: cpqConfig.customerId,\n currencyCode: cpqConfig.currencyCode,\n status: 'draft',\n subtotalNetAmount: String(grandTotal),\n subtotalGrossAmount: String(grandTotal),\n discountTotalAmount: '0',\n taxTotalAmount: '0',\n shippingNetAmount: '0',\n shippingGrossAmount: '0',\n surchargeTotalAmount: '0',\n paidTotalAmount: '0',\n grandTotalNetAmount: String(grandTotal),\n grandTotalGrossAmount: String(grandTotal),\n lineItemCount: quoteLines.length,\n metadata: { sourceQuoteId: cpqConfig.id },\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(salesOrder)\n\n const cpqOrderConfigId = crypto.randomUUID()\n const cpqOrderConfig = em.create(CpqOrderConfiguration, {\n id: cpqOrderConfigId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderId: salesOrderId,\n sourceQuoteId: cpqConfig.id,\n customerId: cpqConfig.customerId,\n cpqStatus: 'draft',\n currencyCode: cpqConfig.currencyCode,\n pricingSummary: {\n nrcTotal,\n mrcTotal,\n currencyCode: cpqConfig.currencyCode,\n },\n })\n em.persist(cpqOrderConfig)\n\n const quoteLineIdToOrderLineId = new Map<string, string>()\n\n for (let i = 0; i < quoteLines.length; i++) {\n const ql = quoteLines[i]\n\n const perInstanceNrc = Number(ql.nrcTotal)\n const perInstanceMrc = Number(ql.mrcTotal)\n const unitPriceNet = (perInstanceNrc + perInstanceMrc) / ql.quantity\n const totalNet = perInstanceNrc + perInstanceMrc\n\n const orderLineId = crypto.randomUUID()\n quoteLineIdToOrderLineId.set(ql.quoteLineId, orderLineId)\n\n const offeringName = await this.resolveOfferingName(\n em,\n ql.offeringId,\n scope,\n ql.configuration as Record<string, unknown> | null,\n )\n\n const orderLine = em.create(SalesOrderLine, {\n id: orderLineId,\n order: salesOrder,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n lineNumber: i + 1,\n kind: 'product',\n productId: ql.productId ?? null,\n name: offeringName,\n quantity: String(ql.quantity),\n unitPriceNet: String(unitPriceNet),\n unitPriceGross: String(unitPriceNet),\n totalNetAmount: String(totalNet),\n totalGrossAmount: String(totalNet),\n discountAmount: '0',\n discountPercent: '0',\n taxRate: '0',\n taxAmount: '0',\n currencyCode: cpqConfig.currencyCode,\n configuration: {\n offeringId: ql.offeringId ?? null,\n action: ql.action,\n nrcTotal: perInstanceNrc,\n mrcTotal: perInstanceMrc,\n parentLineId: ql.parentLineId ?? null,\n },\n normalizedQuantity: String(ql.quantity),\n reservedQuantity: '0',\n fulfilledQuantity: '0',\n invoicedQuantity: '0',\n returnedQuantity: '0',\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(orderLine)\n const orderLineConfig = em.create(CpqOrderLineConfiguration, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderLineId,\n orderConfigurationId: cpqOrderConfig.id,\n offeringId: ql.offeringId ?? null,\n productId: ql.productId ?? null,\n specId: ql.specId ?? null,\n action: ql.action,\n parentLineId: null,\n startDate: ql.startDate,\n termMonths: ql.termMonths ?? null,\n endDate: ql.endDate,\n configuration: { ...ql.configuration, offeringName },\n quantity: ql.quantity,\n charges: ql.charges ? [...ql.charges] : null,\n nrcTotal: ql.nrcTotal,\n mrcTotal: ql.mrcTotal,\n sourceQuoteLineId: ql.quoteLineId,\n })\n em.persist(orderLineConfig)\n }\n\n await em.flush()\n\n // Resolve parent line references using the quote\u2192order line ID map\n const orderLineConfigs = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: cpqOrderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n for (const olc of orderLineConfigs) {\n if (olc.sourceQuoteLineId) {\n const sourceQl = quoteLines.find((ql) => ql.quoteLineId === olc.sourceQuoteLineId)\n if (sourceQl?.parentLineId) {\n const mappedParent = quoteLineIdToOrderLineId.get(sourceQl.parentLineId)\n if (mappedParent) olc.parentLineId = mappedParent\n }\n }\n }\n\n await em.flush()\n\n return this.buildOrderResult(em, cpqOrderConfig, salesOrder, orderLineConfigs, scope)\n }\n\n // \u2500\u2500\u2500 Activate Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async activateOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n if (currentStatus !== 'draft' && currentStatus !== 'pending_activation') {\n throw new OrderError(409, `Cannot activate order in '${currentStatus}' status`)\n }\n\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const orderLines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n // Branch on the source quote's quoteType. ARC quotes mutate existing\n // subscriptions; new-sale quotes materialize a fresh subscription.\n const sourceQuote = orderConfig.sourceQuoteId\n ? await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : null\n\n const quoteType = sourceQuote?.quoteType ?? 'new'\n const postCommitEvents: Array<() => Promise<void>> = []\n\n if (quoteType === 'new') {\n const inventory = await this.createInventoryFromOrder(orderConfig, orderLines, scope)\n if (inventory) {\n postCommitEvents.push(() => emitActivatedEvent(orderConfig, inventory, scope))\n }\n } else {\n postCommitEvents.push(\n ...(await this.applyArcOrder(orderConfig, sourceQuote!, orderLines, scope)),\n )\n }\n\n orderConfig.cpqStatus = 'active'\n orderConfig.activatedAt = new Date()\n salesOrder.status = 'active'\n salesOrder.placedAt = new Date()\n\n await em.flush()\n\n // Emit lifecycle events AFTER the activation transaction commits.\n for (const fire of postCommitEvents) {\n try {\n await fire()\n } catch (err) {\n // Don't roll back the activation just because a subscriber blew up \u2014\n // events are best-effort once the DB state is committed.\n console.error('[cpq.arc] event emission failed', err)\n }\n }\n\n return this.buildOrderResult(em, orderConfig, salesOrder, orderLines, scope)\n }\n\n // \u2500\u2500\u2500 ARC activation branch (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // Reads the source quote's targets + lines, then dispatches per target to\n // cpqInventoryService.apply* primitives. Returns the list of post-commit\n // event emissions so the caller can fire them outside the activation TX.\n private async applyArcOrder(\n orderConfig: CpqOrderConfiguration,\n sourceQuote: CpqQuoteConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<Array<() => Promise<void>>> {\n const em = this.em\n const quoteType = sourceQuote.quoteType as 'amend' | 'renew' | 'cancel'\n\n // Load attached targets.\n const targets = await em.find(CpqQuoteTargetSubscription, {\n quoteId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (targets.length === 0) {\n throw new OrderError(\n 409,\n `ARC quote ${sourceQuote.id} has no target subscriptions to activate`,\n )\n }\n\n const inventory = this.inventoryService\n\n // Group order lines by target_subscription_id \u2014 ARC order lines mirror the\n // quote lines' ARC fields (target_subscription_id is preserved through\n // convertQuoteToOrder via line.configuration when needed).\n // Source quote lines are the source of truth for ARC line data because\n // CpqOrderLineConfiguration doesn't carry target/source-item fields.\n const sourceQuoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n const linesByTarget = new Map<string | null, CpqQuoteLineConfiguration[]>()\n for (const line of sourceQuoteLines) {\n const key = line.targetSubscriptionId ?? null\n const list = linesByTarget.get(key) ?? []\n list.push(line)\n linesByTarget.set(key, list)\n }\n\n // Look up specs for `add` lines so we can build itemInput correctly.\n const specIds = [\n ...new Set(sourceQuoteLines.map((l) => l.specId).filter((x): x is string => Boolean(x))),\n ]\n const specs =\n specIds.length > 0\n ? await em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const events: Array<() => Promise<void>> = []\n const eventBaseEnvelope = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n } as const\n\n if (quoteType === 'amend') {\n for (const target of targets) {\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyAmendment(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitAmendedEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'cancel') {\n for (const target of targets) {\n const result = await inventory.applyCancel(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n etfAmount: sourceQuote.arcEtfAmount ?? null,\n etfCurrency: sourceQuote.arcEtfCurrency ?? null,\n reasonCode: sourceQuote.arcReasonCode ?? null,\n reasonText: sourceQuote.arcReasonText ?? null,\n },\n scope,\n )\n events.push(() => emitCancelledEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'renew') {\n const standalone = targets.filter((t) => t.mergeAction === 'standalone')\n const absorb = targets.filter((t) => t.mergeAction === 'absorb')\n\n if (standalone.length > 0 && absorb.length === 0) {\n for (const target of standalone) {\n if (!target.newTermStart || !target.newTermEnd) {\n throw new OrderError(\n 409,\n `Standalone renew target ${target.id} missing newTermStart/newTermEnd`,\n )\n }\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyRenewal(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: target.newTermStart,\n newTermEnd: target.newTermEnd,\n newTermMonths: target.newTermMonths ?? null,\n },\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitRenewedEvent(result, eventBaseEnvelope))\n }\n } else if (absorb.length >= 2 && standalone.length === 0) {\n if (!sourceQuote.arcMergeNewTermStart || !sourceQuote.arcMergeNewTermEnd) {\n throw new OrderError(\n 409,\n `Merge renew quote ${sourceQuote.id} missing arc_merge_new_term_*`,\n )\n }\n // Lines targeting null are operator-edits on the new merge sub M.\n const mergeLines = linesByTarget.get(null) ?? []\n const merge = await inventory.applyMergeRenewal(\n {\n sourceIds: absorb.map((t) => t.subscriptionId),\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: sourceQuote.arcMergeNewTermStart,\n newTermEnd: sourceQuote.arcMergeNewTermEnd,\n newTermMonths: sourceQuote.arcMergeNewTermMonths ?? null,\n },\n mergeMeta: {\n newSubCode: sourceQuote.arcMergeNewSubCode ?? null,\n newSubName: sourceQuote.arcMergeNewSubName ?? null,\n },\n lineChanges: mergeLines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n\n // One event per source (`superseded`) + one for M (`merged`).\n const mergedFromSubscriptionIds = absorb.map((t) => t.subscriptionId)\n events.push(() =>\n emitMergedEvent(merge, eventBaseEnvelope, mergedFromSubscriptionIds),\n )\n for (const sourceLog of merge.sourceChangeLogs) {\n events.push(() =>\n emitSupersededEvent(\n sourceLog,\n iso10(merge.mergedSubscription.currentTermStart),\n eventBaseEnvelope,\n ),\n )\n }\n } else {\n throw new OrderError(\n 409,\n `Inconsistent renew targets \u2014 must be either single standalone or \u22652 absorb`,\n )\n }\n } else {\n throw new OrderError(500, `Unknown quote_type '${quoteType}' for ARC activation`)\n }\n\n return events\n }\n\n // \u2500\u2500\u2500 Inventory Creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async createInventoryFromOrder(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<ActivatedInventory | null> {\n if (orderLines.length === 0) return null\n\n const specIds = [...new Set(orderLines.map((l) => l.specId).filter(Boolean))] as string[]\n const specs = specIds.length > 0\n ? await this.em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const subscriptionLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return !spec?.isAssetizable\n })\n const assetLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return spec?.isAssetizable === true\n })\n\n const hasSubscriptionContent = subscriptionLines.some(\n (l) => Number(l.mrcTotal) > 0 || Number(l.nrcTotal) > 0,\n )\n if (!hasSubscriptionContent && assetLines.length === 0) return null\n\n const subscriptionName = await this.buildSubscriptionName(orderConfig, orderLines, scope)\n\n const lineWithDates = orderLines.find((l) => l.startDate != null) ?? orderLines[0]\n const lineWithTerm = orderLines.find((l) => l.termMonths != null) ?? orderLines[0]\n const subscriptionItems = subscriptionLines.map((line) => ({\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n currencyCode: orderConfig.currencyCode,\n quantity: line.quantity,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n sortOrder: orderLines.indexOf(line),\n }))\n\n const subscription = await this.inventoryService.createSubscription(\n {\n customerId: orderConfig.customerId,\n name: subscriptionName,\n billingCycle: 'monthly',\n currencyCode: orderConfig.currencyCode,\n startDate: lineWithDates?.startDate?.toISOString?.() ?? undefined,\n termMonths: lineWithTerm?.termMonths ?? undefined,\n sourceQuoteId: orderConfig.sourceQuoteId ?? undefined,\n sourceOrderId: orderConfig.id,\n items: subscriptionItems.length > 0\n ? subscriptionItems\n : [{ name: subscriptionName, mrcAmount: 0, nrcAmount: 0 }],\n },\n scope,\n )\n\n const orderLineToSubItem = new Map<string, string>()\n const allSubItems = await this.em.find(CpqInventorySubscriptionItem, {\n subscriptionId: subscription.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, { orderBy: { sortOrder: 'asc' } })\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n orderLineToSubItem.set(subscriptionLines[i].orderLineId, allSubItems[i].id)\n }\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n const orderLine = subscriptionLines[i]\n if (orderLine.parentLineId) {\n const parentSubItemId = orderLineToSubItem.get(orderLine.parentLineId)\n if (parentSubItemId) {\n allSubItems[i].parentItemId = parentSubItemId\n }\n }\n }\n await this.em.flush()\n\n for (const line of assetLines) {\n let subscriptionItemId: string | undefined\n if (line.parentLineId) {\n subscriptionItemId = orderLineToSubItem.get(line.parentLineId) ?? undefined\n }\n\n await this.inventoryService.createAsset(\n {\n customerId: orderConfig.customerId,\n subscriptionId: subscription.id,\n subscriptionItemId,\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n assetType: 'one_time',\n currencyCode: orderConfig.currencyCode,\n purchasePrice: Number(line.nrcTotal),\n quantity: line.quantity,\n purchaseDate: new Date().toISOString().split('T')[0],\n configuration: line.configuration ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n sourceOrderId: orderConfig.id,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n },\n scope,\n )\n }\n\n // Pair each subscription line with its created subscription item \u2014\n // input for the `cpq.subscription.activated` billing-onboarding event.\n const eventItems = subscriptionLines\n .slice(0, allSubItems.length)\n .map((line, i) => ({\n subscriptionItemId: allSubItems[i].id,\n productName: this.getLineName(line),\n mrcTotal: Number(line.mrcTotal),\n nrcTotal: Number(line.nrcTotal),\n }))\n // Only lines with a billable charge \u2014 a zero-value line yields\n // no Billing Item anyway; emitting it would just make the\n // connector create a needless shell account.\n .filter((it) => it.mrcTotal > 0 || it.nrcTotal > 0)\n return { subscriptionId: subscription.id, items: eventItems }\n }\n\n // \u2500\u2500\u2500 Get / List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n async listOrders(filters: OrderListFilters, scope: TenantScope) {\n const page = Math.max(1, filters.page ?? 1)\n const pageSize = Math.min(100, Math.max(1, filters.pageSize ?? 50))\n const offset = (page - 1) * pageSize\n\n const where: Record<string, unknown> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (filters.customerId) where.customerId = filters.customerId\n if (filters.sourceQuoteId) where.sourceQuoteId = filters.sourceQuoteId\n if (filters.salesOrderId) where.orderId = filters.salesOrderId\n if (filters.currencyCode) where.currencyCode = filters.currencyCode\n if (filters.cpqStatus) {\n where.cpqStatus = Array.isArray(filters.cpqStatus)\n ? { $in: filters.cpqStatus }\n : filters.cpqStatus\n }\n\n const search = filters.search?.trim()\n if (search) {\n where.$or = [\n { orderId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const sortField = filters.sortField ?? 'createdAt'\n const sortDir = filters.sortDir === 'asc' ? 'asc' : 'desc'\n\n const [configs, total] = await this.em.findAndCount(\n CpqOrderConfiguration,\n where,\n { limit: pageSize, offset, orderBy: { [sortField]: sortDir } },\n )\n\n const orderIds = configs.map((c) => c.orderId)\n const salesOrders = orderIds.length > 0\n ? await this.em.find(SalesOrder, { id: { $in: orderIds } })\n : []\n const salesOrderMap = new Map(salesOrders.map((o) => [o.id, o]))\n\n const items = configs.map((c) => ({\n ...c,\n orderNumber: salesOrderMap.get(c.orderId)?.orderNumber ?? null,\n }))\n\n return { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) }\n }\n\n // \u2500\u2500\u2500 Status Transition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async transitionOrderStatus(orderId: string, targetStatus: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n const allowed = CPQ_ORDER_TRANSITIONS[currentStatus] ?? []\n\n if (!allowed.includes(targetStatus as CpqOrderStatus)) {\n throw new OrderError(409, `Cannot transition from '${currentStatus}' to '${targetStatus}'`)\n }\n\n if (targetStatus === 'active') {\n return this.activateOrder(orderId, scope)\n }\n\n orderConfig.cpqStatus = targetStatus\n salesOrder.status = CPQ_TO_SALES_ORDER_STATUS[targetStatus as CpqOrderStatus] ?? targetStatus\n\n await em.flush()\n\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n // \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async loadOrderConfig(em: EntityManager, orderId: string, scope: TenantScope): Promise<CpqOrderConfiguration> {\n let config = await em.findOne(CpqOrderConfiguration, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!config) {\n config = await em.findOne(CpqOrderConfiguration, {\n orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n }\n if (!config) throw new OrderError(404, 'Order not found')\n return config\n }\n\n private async loadSalesOrder(em: EntityManager, orderId: string, scope: TenantScope): Promise<SalesOrder> {\n const order = await em.findOne(SalesOrder, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!order) throw new OrderError(404, 'Sales order not found')\n return order\n }\n\n private async resolveOfferingInfo(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<{ name: string; type: string | null }> {\n if (offeringId) {\n const offering = await em.findOne(CpqProductOffering, {\n id: offeringId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (offering) return { name: offering.name, type: offering.offeringType ?? 'simple' }\n }\n const cfg = fallbackConfig ?? null\n const fallbackName =\n (cfg?._arcMirroredName as string | undefined) ??\n (cfg?.offeringName as string | undefined) ??\n 'Configured Item'\n return { name: fallbackName, type: null }\n }\n\n private async resolveOfferingName(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<string> {\n const info = await this.resolveOfferingInfo(em, offeringId, scope, fallbackConfig)\n return info.name\n }\n\n private getLineName(line: CpqOrderLineConfiguration): string {\n const config = line.configuration as Record<string, unknown> | null\n return (\n (config?._arcMirroredName as string | undefined) ??\n (config?.offeringName as string | undefined) ??\n (config?.name as string | undefined) ??\n 'Configured Item'\n )\n }\n\n private async buildSubscriptionName(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<string> {\n if (orderLines.length === 1 && orderLines[0].offeringId) {\n return this.resolveOfferingName(this.em, orderLines[0].offeringId, scope)\n }\n const names: string[] = []\n for (const line of orderLines.slice(0, 3)) {\n if (line.offeringId) {\n names.push(await this.resolveOfferingName(this.em, line.offeringId, scope))\n }\n }\n if (names.length > 0) {\n const suffix = orderLines.length > 3 ? ` (+${orderLines.length - 3} more)` : ''\n return names.join(', ') + suffix\n }\n return `Order ${orderConfig.orderId.slice(0, 8)}`\n }\n\n private async buildOrderResult(\n em: EntityManager,\n orderConfig: CpqOrderConfiguration,\n salesOrder: SalesOrder,\n lineConfigs: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<OrderResult> {\n const lines: OrderLineResult[] = []\n\n // XD-250 ARC: batch-load source subscription items so we can render the\n // before/after diff per line on the order detail page.\n const arcItemIds = new Set<string>()\n for (const lc of lineConfigs) {\n const m = (lc.configuration as Record<string, unknown> | null)?._arcMirroredFromItemId\n if (typeof m === 'string') arcItemIds.add(m)\n }\n const arcItemMap = new Map<string, CpqInventorySubscriptionItem>()\n if (arcItemIds.size > 0) {\n const items = await em.find(CpqInventorySubscriptionItem, {\n id: { $in: Array.from(arcItemIds) },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n for (const it of items) arcItemMap.set(it.id, it)\n }\n\n for (const lc of lineConfigs) {\n const offeringInfo = await this.resolveOfferingInfo(\n em,\n lc.offeringId,\n scope,\n lc.configuration as Record<string, unknown> | null,\n )\n const arcSource = pickArcSourceFromConfig(lc.configuration as Record<string, unknown> | null, arcItemMap)\n lines.push({\n lineId: lc.orderLineId,\n offeringId: lc.offeringId ?? null,\n offeringName: offeringInfo.name,\n offeringType: offeringInfo.type,\n productId: lc.productId ?? null,\n specId: lc.specId ?? null,\n action: lc.action,\n parentLineId: lc.parentLineId ?? null,\n quantity: lc.quantity,\n configuration: lc.configuration ?? {},\n startDate: lc.startDate?.toISOString() ?? null,\n termMonths: lc.termMonths ?? null,\n endDate: lc.endDate?.toISOString() ?? null,\n nrcTotal: Number(lc.nrcTotal),\n mrcTotal: Number(lc.mrcTotal),\n charges: (lc.charges ?? []) as Array<Record<string, unknown>>,\n sourceQuoteLineId: lc.sourceQuoteLineId ?? null,\n arcSource,\n })\n }\n\n // Source quote's quoteType \u2014 surfaced to the order header so the UI can\n // render an AMEND / RENEW / CANCEL badge alongside the order number.\n // CpqOrderConfiguration.sourceQuoteId stores CpqQuoteConfiguration.id\n // (see convertFromConfig \u2014 sourceQuoteId: cpqConfig.id), NOT the\n // SalesQuote.id, so we look it up by `id` rather than by `quoteId`.\n let quoteType = 'new'\n if (orderConfig.sourceQuoteId) {\n const sourceCfg = await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (sourceCfg?.quoteType) quoteType = sourceCfg.quoteType\n }\n\n const pricing = orderConfig.pricingSummary ?? {}\n\n return {\n id: orderConfig.id,\n orderId: orderConfig.orderId,\n orderNumber: salesOrder.orderNumber ?? '',\n customerId: orderConfig.customerId,\n cpqStatus: orderConfig.cpqStatus,\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n quoteType,\n currencyCode: orderConfig.currencyCode,\n pricingSummary: {\n nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),\n mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),\n currencyCode: orderConfig.currencyCode,\n },\n activatedAt: orderConfig.activatedAt?.toISOString() ?? null,\n createdAt: orderConfig.createdAt.toISOString(),\n lines,\n }\n }\n}\n\nfunction pickArcSourceFromConfig(\n cfg: Record<string, unknown> | null,\n map: Map<string, CpqInventorySubscriptionItem>,\n): OrderLineResult['arcSource'] {\n const m = cfg?._arcMirroredFromItemId\n if (typeof m !== 'string') return null\n const item = map.get(m)\n if (!item) return null\n return {\n subscriptionItemId: item.id,\n name: item.name,\n mrcAmount: Number(item.mrcAmount),\n nrcAmount: Number(item.nrcAmount),\n quantity: item.quantity,\n }\n}\n\n// \u2500\u2500\u2500 Error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Cross-realm brand \u2014 see QUOTING_ERROR_BRAND in cpqQuotingService.ts for why\n// route handlers must not rely on `instanceof`. Use `isOrderError`.\nconst ORDER_ERROR_BRAND = Symbol.for('@dainamite/cpq/OrderError')\n\nexport class OrderError extends Error {\n readonly [ORDER_ERROR_BRAND] = true\n\n constructor(\n public status: number,\n message: string,\n ) {\n super(message)\n this.name = 'OrderError'\n }\n}\n\nexport function isOrderError(err: unknown): err is OrderError {\n return !!err && typeof err === 'object' && (err as Record<symbol, unknown>)[ORDER_ERROR_BRAND] === true\n}\n\n// \u2500\u2500\u2500 ARC helpers (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Build a service-layer ArcLineChange from a CpqQuoteLineConfiguration.\n * For 'add' actions, materializes the itemInput from line + spec data.\n */\nfunction buildArcLineChange(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): ArcLineChange {\n const action = line.action as 'add' | 'modify' | 'cancel'\n if (action === 'add') {\n const itemInput: Omit<CreateSubscriptionItemInput, 'name'> & { name: string } = {\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: resolveLineName(line, specMap),\n configuration: line.configuration ?? {},\n charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n sourceQuoteLineId: line.quoteLineId,\n }\n return {\n quoteLineId: line.quoteLineId,\n action,\n itemInput,\n parentQuoteLineId: line.parentLineId ?? null,\n }\n }\n if (action === 'cancel') {\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n }\n }\n // modify\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n modifyPatch: {\n configuration: line.configuration ?? undefined,\n charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n },\n }\n}\n\nfunction resolveLineName(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): string {\n const cfg = line.configuration as Record<string, unknown> | null\n const cfgName = (cfg?._arcMirroredName as string | undefined) ?? null\n if (cfgName) return cfgName\n if (line.specId) {\n const spec = specMap.get(line.specId)\n if (spec?.name) return spec.name\n }\n return 'Configured Item'\n}\n\ninterface ArcEventEnvelope {\n organizationId: string\n tenantId: string\n sourceQuoteId: string\n sourceOrderId: string\n}\n\n// \u2500\u2500\u2500 Subscription-activated event (new-sale billing onboarding) \u2500\u2500\n\ntype ActivatedInventory = {\n subscriptionId: string\n items: Array<{\n subscriptionItemId: string\n productName: string\n /** Line totals \u2014 CPQ pricing has already applied line quantity. */\n mrcTotal: number\n nrcTotal: number\n }>\n}\n\n/**\n * Emit `cpq.subscription.activated` after a new-sale order activation.\n *\n * Payload matches the `@dainamite/cpq-billing-connector`\n * `cpq-subscription-activated` subscriber contract: one CPQ charge per\n * non-zero MRC / NRC total on each subscription item. The connector\n * get-or-creates the Billing Account (keyed on `customerId`) and turns\n * each charge into a Billing Item.\n */\nasync function emitActivatedEvent(\n orderConfig: CpqOrderConfiguration,\n inventory: ActivatedInventory,\n scope: TenantScope,\n): Promise<void> {\n const items = inventory.items.map((it) => ({\n subscriptionItemId: it.subscriptionItemId,\n productName: it.productName,\n // `mrcTotal` / `nrcTotal` are line totals \u2014 CPQ pricing already\n // applied the line quantity. Quantity is therefore 1 here so\n // `unitPrice \u00D7 quantity` stays internally consistent no matter\n // how the connector treats the multiplier.\n quantity: 1,\n charges: [\n ...(it.mrcTotal > 0\n ? [{ type: 'recurring', unitPrice: it.mrcTotal, description: 'Recurring charge' }]\n : []),\n ...(it.nrcTotal > 0\n ? [{ type: 'one_time', amount: it.nrcTotal, description: 'One-time charge' }]\n : []),\n ],\n }))\n await emitCpqEvent('cpq.subscription.activated' as never, {\n subscriptionId: inventory.subscriptionId,\n customerId: orderConfig.customerId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n currencyCode: orderConfig.currencyCode,\n timestamp: new Date().toISOString(),\n activationDate: new Date().toISOString().slice(0, 10),\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n sourceOrderId: orderConfig.id,\n items,\n } as never)\n}\n\n/** Coerce a Date or `date`-column string to a `YYYY-MM-DD` string. */\nfunction iso10(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString().slice(0, 10)\n return String(value).slice(0, 10)\n}\n\ntype ArcChargeItem = {\n subscriptionItemId: string\n productName: string\n quantity: number\n charges: Array<Record<string, unknown>>\n}\n\n/**\n * Build the connector's `addedItems` from a change log's `add` line\n * changes. `mrcAmount` / `nrcAmount` are line totals (CPQ pricing has\n * already applied quantity) \u2192 emit `quantity: 1` with the totals as the\n * charge rate, exactly as `emitActivatedEvent`.\n */\nfunction buildArcAddedItems(changeLog: ApplyArcResult['changeLog']): ArcChargeItem[] {\n const after = changeLog.afterSnapshot as\n | { items?: Array<{ id?: string; name?: string }> }\n | null\n const nameById = new Map(\n (after?.items ?? []).map((i) => [String(i.id), i.name ?? '']),\n )\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n mrcAmount?: unknown\n nrcAmount?: unknown\n }>\n const out: ArcChargeItem[] = []\n for (const change of summary) {\n if (change.action !== 'add' || typeof change.itemId !== 'string') continue\n const mrc = Number(change.mrcAmount)\n const nrc = Number(change.nrcAmount)\n out.push({\n subscriptionItemId: change.itemId,\n productName: nameById.get(change.itemId) || 'Configured item',\n quantity: 1,\n charges: [\n ...(mrc > 0\n ? [{ type: 'recurring', unitPrice: mrc, description: 'Recurring charge' }]\n : []),\n ...(nrc > 0\n ? [{ type: 'one_time', amount: nrc, description: 'One-time charge' }]\n : []),\n ],\n })\n }\n return out\n}\n\n/** Subscription-item ids removed by a change log's `cancel` line changes. */\nfunction buildArcRemovedItemIds(changeLog: ApplyArcResult['changeLog']): string[] {\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n }>\n return summary\n .filter((c) => c.action === 'cancel' && typeof c.itemId === 'string')\n .map((c) => c.itemId as string)\n}\n\nasync function emitAmendedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.amended' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n // No operator-chosen amend date today \u2014 the activation moment is\n // the effective date. The billing connector computes the mid-cycle\n // proration from this date + the account's billing cycle.\n effectiveDate: iso10(result.changeLog.effectiveAt),\n addedItems: buildArcAddedItems(result.changeLog),\n removedSubscriptionItemIds: buildArcRemovedItemIds(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitRenewedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.renewed' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n newTermStart: iso10(result.subscription.currentTermStart),\n newTermEnd: iso10(result.subscription.currentTermEnd),\n addedItems: buildArcAddedItems(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitCancelledEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.cancelled' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n effectiveDate: iso10(result.changeLog.effectiveAt),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitMergedEvent(\n merge: ApplyMergeRenewalResult,\n envelope: ArcEventEnvelope,\n sourceSubscriptionIds: string[],\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.merged' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n mergedSubscriptionId: merge.mergedSubscription.id,\n sourceSubscriptionIds,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitSupersededEvent(\n sourceLog: { id: string; subscriptionId: string },\n effectiveDate: string | null,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.superseded' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: sourceLog.subscriptionId,\n effectiveDate,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import { storedAdjustmentFailure } from './priceAdjustments/storedAdjustmentFailure'\nimport { withQuotePricingWrite } from './priceAdjustments/quotePricingWrite'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'\nimport type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'\nimport {\n CpqQuoteConfiguration,\n CpqQuoteLineConfiguration,\n CpqQuoteTargetSubscription,\n CpqInventorySubscription,\n CpqOrderConfiguration,\n CpqOrderLineConfiguration,\n CpqProductOffering,\n CpqProductSpecification,\n CpqInventorySubscriptionItem,\n} from '../data/entities'\nimport type {\n ApplyArcResult,\n ApplyMergeRenewalResult,\n ArcLineChange,\n CreateSubscriptionItemInput,\n DefaultCpqInventoryService,\n} from './cpqInventoryService'\nimport {\n CPQ_ORDER_TRANSITIONS,\n type CpqOrderStatus,\n type TenantScope,\n} from './types'\nimport { emitCpqEvent } from '../events'\nimport { stripQuoteOnlyArcMarkers } from './arcPriceFreeze'\nimport { summarizeOrderPricing, type OrderWaterfallSummary } from './orderPricingSummary'\n\n// \u2500\u2500\u2500 Types \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\ninterface OrderListFilters {\n customerId?: string\n sourceQuoteId?: string\n salesOrderId?: string\n cpqStatus?: string | string[]\n currencyCode?: string\n search?: string\n sortField?: 'createdAt' | 'updatedAt' | 'cpqStatus' | 'activatedAt' | 'currencyCode'\n sortDir?: 'asc' | 'desc'\n page?: number\n pageSize?: number\n}\n\ninterface PaginatedResult<T> {\n items: T[]\n total: number\n page: number\n pageSize: number\n totalPages: number\n}\n\ninterface OrderLineResult {\n lineId: string\n offeringId: string | null\n offeringName: string\n offeringType: string | null\n productId: string | null\n specId: string | null\n action: string\n parentLineId: string | null\n quantity: number\n configuration: Record<string, unknown>\n startDate: string | null\n termMonths: number | null\n endDate: string | null\n nrcTotal: number\n mrcTotal: number\n charges: Array<Record<string, unknown>>\n sourceQuoteLineId: string | null\n /** XD-250 ARC: snapshot of the source subscription item's current state.\n * Null for non-ARC lines and for fresh `add` lines on amend quotes. */\n arcSource: {\n subscriptionItemId: string\n name: string\n mrcAmount: number\n nrcAmount: number\n quantity: number\n } | null\n}\n\ninterface OrderResult {\n id: string\n orderId: string\n orderNumber: string\n customerId: string\n cpqStatus: string\n sourceQuoteId: string | null\n /** XD-250 ARC quote type carried over from the source quote \u2014 used by the\n * order detail page to render the AMEND / RENEW / CANCEL badge. */\n quoteType: string\n currencyCode: string\n /** NRC / MRC totals plus the source quote's list / discount / deal-value figures. */\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n currencyCode: string\n } & OrderWaterfallSummary\n activatedAt: string | null\n createdAt: string\n lines: OrderLineResult[]\n}\n\ninterface OrderServiceDeps {\n em: EntityManager\n cpqInventoryService: DefaultCpqInventoryService\n salesDocumentNumberGenerator: SalesDocumentNumberGenerator\n}\n\nconst CPQ_TO_SALES_ORDER_STATUS: Record<CpqOrderStatus, string> = {\n draft: 'draft',\n pending_activation: 'pending',\n active: 'active',\n cancelled: 'cancelled',\n fulfilled: 'fulfilled',\n}\n\n// \u2500\u2500\u2500 Service \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\nexport class DefaultCpqOrderService {\n private em: EntityManager\n private inventoryService: DefaultCpqInventoryService\n private numberGenerator: SalesDocumentNumberGenerator\n\n constructor(deps: OrderServiceDeps) {\n this.em = deps.em\n this.inventoryService = deps.cpqInventoryService\n this.numberGenerator = deps.salesDocumentNumberGenerator\n }\n\n // \u2500\u2500\u2500 Convert Quote to Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {\n return withQuotePricingWrite(this.em, quoteId, scope,\n (em, quote) => this.convertFromConfig(quote, scope, em),\n (status, message) => new OrderError(status, message))\n }\n\n private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope, em: EntityManager): Promise<OrderResult> {\n\n const blockedStatuses = ['cancelled', 'rejected']\n if (blockedStatuses.includes(cpqConfig.cpqStatus)) {\n throw new OrderError(409, `Cannot convert a quote in '${cpqConfig.cpqStatus}' status`)\n }\n\n // Idempotency guard: a quote may only convert once. If an order with\n // this quote as its `sourceQuoteId` already exists, the UI should\n // surface \"Go to order\" instead of re-running conversion \u2014 return\n // 409 with the existing id so the caller can redirect.\n const existingOrder = await em.findOne(CpqOrderConfiguration, {\n sourceQuoteId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (existingOrder) {\n throw new OrderError(\n 409,\n `Quote has already been converted to order ${existingOrder.orderId}`,\n )\n }\n\n const quoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: cpqConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n if (quoteLines.length === 0) {\n throw new OrderError(422, 'Cannot convert a quote with no line items')\n }\n\n const invalidAdjustment = storedAdjustmentFailure(cpqConfig, quoteLines)\n if (invalidAdjustment) throw new OrderError(422, 'ADJUSTMENT_INVALID', invalidAdjustment)\n\n const { number: orderNumber } = await this.numberGenerator.generate({\n kind: 'order',\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n\n const salesOrderId = crypto.randomUUID()\n const pricingSummary = cpqConfig.pricingSummary as Record<string, unknown> | null\n const nrcTotal = Number(pricingSummary?.nrcTotal ?? 0)\n const mrcTotal = Number(pricingSummary?.mrcTotal ?? 0)\n const grandTotal = nrcTotal + mrcTotal\n\n const salesOrder = em.create(SalesOrder, {\n id: salesOrderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderNumber,\n customerEntityId: cpqConfig.customerId,\n currencyCode: cpqConfig.currencyCode,\n status: 'draft',\n subtotalNetAmount: String(grandTotal),\n subtotalGrossAmount: String(grandTotal),\n discountTotalAmount: '0',\n taxTotalAmount: '0',\n shippingNetAmount: '0',\n shippingGrossAmount: '0',\n surchargeTotalAmount: '0',\n paidTotalAmount: '0',\n grandTotalNetAmount: String(grandTotal),\n grandTotalGrossAmount: String(grandTotal),\n lineItemCount: quoteLines.length,\n metadata: { sourceQuoteId: cpqConfig.id },\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(salesOrder)\n\n const cpqOrderConfigId = crypto.randomUUID()\n const cpqOrderConfig = em.create(CpqOrderConfiguration, {\n id: cpqOrderConfigId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderId: salesOrderId,\n sourceQuoteId: cpqConfig.id,\n customerId: cpqConfig.customerId,\n cpqStatus: 'draft',\n currencyCode: cpqConfig.currencyCode,\n pricingSummary: {\n nrcTotal,\n mrcTotal,\n currencyCode: cpqConfig.currencyCode,\n },\n })\n em.persist(cpqOrderConfig)\n\n const quoteLineIdToOrderLineId = new Map<string, string>()\n\n for (let i = 0; i < quoteLines.length; i++) {\n const ql = quoteLines[i]\n\n const perInstanceNrc = Number(ql.nrcTotal)\n const perInstanceMrc = Number(ql.mrcTotal)\n const unitPriceNet = (perInstanceNrc + perInstanceMrc) / ql.quantity\n const totalNet = perInstanceNrc + perInstanceMrc\n\n const orderLineId = crypto.randomUUID()\n quoteLineIdToOrderLineId.set(ql.quoteLineId, orderLineId)\n\n const offeringName = await this.resolveOfferingName(\n em,\n ql.offeringId,\n scope,\n ql.configuration as Record<string, unknown> | null,\n )\n\n const orderLine = em.create(SalesOrderLine, {\n id: orderLineId,\n order: salesOrder,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n lineNumber: i + 1,\n kind: 'product',\n productId: ql.productId ?? null,\n name: offeringName,\n quantity: String(ql.quantity),\n unitPriceNet: String(unitPriceNet),\n unitPriceGross: String(unitPriceNet),\n totalNetAmount: String(totalNet),\n totalGrossAmount: String(totalNet),\n discountAmount: '0',\n discountPercent: '0',\n taxRate: '0',\n taxAmount: '0',\n currencyCode: cpqConfig.currencyCode,\n configuration: {\n offeringId: ql.offeringId ?? null,\n action: ql.action,\n nrcTotal: perInstanceNrc,\n mrcTotal: perInstanceMrc,\n parentLineId: ql.parentLineId ?? null,\n },\n normalizedQuantity: String(ql.quantity),\n reservedQuantity: '0',\n fulfilledQuantity: '0',\n invoicedQuantity: '0',\n returnedQuantity: '0',\n createdAt: new Date(),\n updatedAt: new Date(),\n } as never)\n em.persist(orderLine)\n const orderLineConfig = em.create(CpqOrderLineConfiguration, {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n orderLineId,\n orderConfigurationId: cpqOrderConfig.id,\n offeringId: ql.offeringId ?? null,\n productId: ql.productId ?? null,\n specId: ql.specId ?? null,\n action: ql.action,\n parentLineId: null,\n startDate: ql.startDate,\n termMonths: ql.termMonths ?? null,\n endDate: ql.endDate,\n // Quote-only ARC pricing markers stay on the quote.\n configuration: { ...stripQuoteOnlyArcMarkers(ql.configuration), offeringName },\n quantity: ql.quantity,\n charges: ql.charges ? [...ql.charges] : null,\n nrcTotal: ql.nrcTotal,\n mrcTotal: ql.mrcTotal,\n sourceQuoteLineId: ql.quoteLineId,\n })\n em.persist(orderLineConfig)\n }\n\n await em.flush()\n\n // Resolve parent line references using the quote\u2192order line ID map\n const orderLineConfigs = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: cpqOrderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n for (const olc of orderLineConfigs) {\n if (olc.sourceQuoteLineId) {\n const sourceQl = quoteLines.find((ql) => ql.quoteLineId === olc.sourceQuoteLineId)\n if (sourceQl?.parentLineId) {\n const mappedParent = quoteLineIdToOrderLineId.get(sourceQl.parentLineId)\n if (mappedParent) olc.parentLineId = mappedParent\n }\n }\n }\n\n await em.flush()\n\n return this.buildOrderResult(em, cpqOrderConfig, salesOrder, orderLineConfigs, scope)\n }\n\n // \u2500\u2500\u2500 Activate Order \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async activateOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n if (currentStatus !== 'draft' && currentStatus !== 'pending_activation') {\n throw new OrderError(409, `Cannot activate order in '${currentStatus}' status`)\n }\n\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const orderLines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n\n // Branch on the source quote's quoteType. ARC quotes mutate existing\n // subscriptions; new-sale quotes materialize a fresh subscription.\n const sourceQuote = orderConfig.sourceQuoteId\n ? await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : null\n\n const quoteType = sourceQuote?.quoteType ?? 'new'\n const postCommitEvents: Array<() => Promise<void>> = []\n\n if (quoteType === 'new') {\n const inventory = await this.createInventoryFromOrder(orderConfig, orderLines, scope)\n if (inventory) {\n postCommitEvents.push(() => emitActivatedEvent(orderConfig, inventory, scope))\n }\n } else {\n postCommitEvents.push(\n ...(await this.applyArcOrder(orderConfig, sourceQuote!, orderLines, scope)),\n )\n }\n\n orderConfig.cpqStatus = 'active'\n orderConfig.activatedAt = new Date()\n salesOrder.status = 'active'\n salesOrder.placedAt = new Date()\n\n await em.flush()\n\n // Emit lifecycle events AFTER the activation transaction commits.\n for (const fire of postCommitEvents) {\n try {\n await fire()\n } catch (err) {\n // Don't roll back the activation just because a subscriber blew up \u2014\n // events are best-effort once the DB state is committed.\n console.error('[cpq.arc] event emission failed', err)\n }\n }\n\n return this.buildOrderResult(em, orderConfig, salesOrder, orderLines, scope)\n }\n\n // \u2500\u2500\u2500 ARC activation branch (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n //\n // Reads the source quote's targets + lines, then dispatches per target to\n // cpqInventoryService.apply* primitives. Returns the list of post-commit\n // event emissions so the caller can fire them outside the activation TX.\n private async applyArcOrder(\n orderConfig: CpqOrderConfiguration,\n sourceQuote: CpqQuoteConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<Array<() => Promise<void>>> {\n const em = this.em\n const quoteType = sourceQuote.quoteType as 'amend' | 'renew' | 'cancel'\n\n // Load attached targets.\n const targets = await em.find(CpqQuoteTargetSubscription, {\n quoteId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (targets.length === 0) {\n throw new OrderError(\n 409,\n `ARC quote ${sourceQuote.id} has no target subscriptions to activate`,\n )\n }\n\n const inventory = this.inventoryService\n\n // Group order lines by target_subscription_id \u2014 ARC order lines mirror the\n // quote lines' ARC fields (target_subscription_id is preserved through\n // convertQuoteToOrder via line.configuration when needed).\n // Source quote lines are the source of truth for ARC line data because\n // CpqOrderLineConfiguration doesn't carry target/source-item fields.\n const sourceQuoteLines = await em.find(CpqQuoteLineConfiguration, {\n quoteConfigurationId: sourceQuote.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n const linesByTarget = new Map<string | null, CpqQuoteLineConfiguration[]>()\n for (const line of sourceQuoteLines) {\n const key = line.targetSubscriptionId ?? null\n const list = linesByTarget.get(key) ?? []\n list.push(line)\n linesByTarget.set(key, list)\n }\n\n // Look up specs for `add` lines so we can build itemInput correctly.\n const specIds = [\n ...new Set(sourceQuoteLines.map((l) => l.specId).filter((x): x is string => Boolean(x))),\n ]\n const specs =\n specIds.length > 0\n ? await em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const events: Array<() => Promise<void>> = []\n const eventBaseEnvelope = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n } as const\n\n if (quoteType === 'amend') {\n for (const target of targets) {\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyAmendment(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitAmendedEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'cancel') {\n for (const target of targets) {\n const result = await inventory.applyCancel(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n etfAmount: sourceQuote.arcEtfAmount ?? null,\n etfCurrency: sourceQuote.arcEtfCurrency ?? null,\n reasonCode: sourceQuote.arcReasonCode ?? null,\n reasonText: sourceQuote.arcReasonText ?? null,\n },\n scope,\n )\n events.push(() => emitCancelledEvent(result, eventBaseEnvelope))\n }\n } else if (quoteType === 'renew') {\n const standalone = targets.filter((t) => t.mergeAction === 'standalone')\n const absorb = targets.filter((t) => t.mergeAction === 'absorb')\n\n if (standalone.length > 0 && absorb.length === 0) {\n for (const target of standalone) {\n if (!target.newTermStart || !target.newTermEnd) {\n throw new OrderError(\n 409,\n `Standalone renew target ${target.id} missing newTermStart/newTermEnd`,\n )\n }\n const lines = linesByTarget.get(target.subscriptionId) ?? []\n const result = await inventory.applyRenewal(\n {\n subscriptionId: target.subscriptionId,\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: target.newTermStart,\n newTermEnd: target.newTermEnd,\n newTermMonths: target.newTermMonths ?? null,\n },\n lineChanges: lines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n events.push(() => emitRenewedEvent(result, eventBaseEnvelope))\n }\n } else if (absorb.length >= 2 && standalone.length === 0) {\n if (!sourceQuote.arcMergeNewTermStart || !sourceQuote.arcMergeNewTermEnd) {\n throw new OrderError(\n 409,\n `Merge renew quote ${sourceQuote.id} missing arc_merge_new_term_*`,\n )\n }\n // Lines targeting null are operator-edits on the new merge sub M.\n const mergeLines = linesByTarget.get(null) ?? []\n const merge = await inventory.applyMergeRenewal(\n {\n sourceIds: absorb.map((t) => t.subscriptionId),\n sourceQuoteId: sourceQuote.id,\n sourceOrderId: orderConfig.id,\n performedByUserId: null,\n term: {\n newTermStart: sourceQuote.arcMergeNewTermStart,\n newTermEnd: sourceQuote.arcMergeNewTermEnd,\n newTermMonths: sourceQuote.arcMergeNewTermMonths ?? null,\n },\n mergeMeta: {\n newSubCode: sourceQuote.arcMergeNewSubCode ?? null,\n newSubName: sourceQuote.arcMergeNewSubName ?? null,\n },\n lineChanges: mergeLines.map((l) => buildArcLineChange(l, specMap)),\n },\n scope,\n )\n\n // One event per source (`superseded`) + one for M (`merged`).\n const mergedFromSubscriptionIds = absorb.map((t) => t.subscriptionId)\n events.push(() =>\n emitMergedEvent(merge, eventBaseEnvelope, mergedFromSubscriptionIds),\n )\n for (const sourceLog of merge.sourceChangeLogs) {\n events.push(() =>\n emitSupersededEvent(\n sourceLog,\n iso10(merge.mergedSubscription.currentTermStart),\n eventBaseEnvelope,\n ),\n )\n }\n } else {\n throw new OrderError(\n 409,\n `Inconsistent renew targets \u2014 must be either single standalone or \u22652 absorb`,\n )\n }\n } else {\n throw new OrderError(500, `Unknown quote_type '${quoteType}' for ARC activation`)\n }\n\n return events\n }\n\n // \u2500\u2500\u2500 Inventory Creation \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async createInventoryFromOrder(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<ActivatedInventory | null> {\n if (orderLines.length === 0) return null\n\n const specIds = [...new Set(orderLines.map((l) => l.specId).filter(Boolean))] as string[]\n const specs = specIds.length > 0\n ? await this.em.find(CpqProductSpecification, {\n id: { $in: specIds },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n : []\n const specMap = new Map(specs.map((s) => [s.id, s]))\n\n const subscriptionLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return !spec?.isAssetizable\n })\n const assetLines = orderLines.filter((l) => {\n const spec = l.specId ? specMap.get(l.specId) : null\n return spec?.isAssetizable === true\n })\n\n const hasSubscriptionContent = subscriptionLines.some(\n (l) => Number(l.mrcTotal) > 0 || Number(l.nrcTotal) > 0,\n )\n if (!hasSubscriptionContent && assetLines.length === 0) return null\n\n const subscriptionName = await this.buildSubscriptionName(orderConfig, orderLines, scope)\n\n const lineWithDates = orderLines.find((l) => l.startDate != null) ?? orderLines[0]\n const lineWithTerm = orderLines.find((l) => l.termMonths != null) ?? orderLines[0]\n const subscriptionItems = subscriptionLines.map((line) => ({\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n currencyCode: orderConfig.currencyCode,\n quantity: line.quantity,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n sortOrder: orderLines.indexOf(line),\n }))\n\n const subscription = await this.inventoryService.createSubscription(\n {\n customerId: orderConfig.customerId,\n name: subscriptionName,\n billingCycle: 'monthly',\n currencyCode: orderConfig.currencyCode,\n startDate: lineWithDates?.startDate?.toISOString?.() ?? undefined,\n termMonths: lineWithTerm?.termMonths ?? undefined,\n sourceQuoteId: orderConfig.sourceQuoteId ?? undefined,\n sourceOrderId: orderConfig.id,\n items: subscriptionItems.length > 0\n ? subscriptionItems\n : [{ name: subscriptionName, mrcAmount: 0, nrcAmount: 0 }],\n },\n scope,\n )\n\n const orderLineToSubItem = new Map<string, string>()\n const allSubItems = await this.em.find(CpqInventorySubscriptionItem, {\n subscriptionId: subscription.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }, { orderBy: { sortOrder: 'asc' } })\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n orderLineToSubItem.set(subscriptionLines[i].orderLineId, allSubItems[i].id)\n }\n\n for (let i = 0; i < subscriptionLines.length && i < allSubItems.length; i++) {\n const orderLine = subscriptionLines[i]\n if (orderLine.parentLineId) {\n const parentSubItemId = orderLineToSubItem.get(orderLine.parentLineId)\n if (parentSubItemId) {\n allSubItems[i].parentItemId = parentSubItemId\n }\n }\n }\n await this.em.flush()\n\n for (const line of assetLines) {\n let subscriptionItemId: string | undefined\n if (line.parentLineId) {\n subscriptionItemId = orderLineToSubItem.get(line.parentLineId) ?? undefined\n }\n\n await this.inventoryService.createAsset(\n {\n customerId: orderConfig.customerId,\n subscriptionId: subscription.id,\n subscriptionItemId,\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: this.getLineName(line),\n assetType: 'one_time',\n currencyCode: orderConfig.currencyCode,\n purchasePrice: Number(line.nrcTotal),\n quantity: line.quantity,\n purchaseDate: new Date().toISOString().split('T')[0],\n configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},\n charges: (line.charges ?? []) as Array<Record<string, unknown>>,\n sourceOrderId: orderConfig.id,\n sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,\n },\n scope,\n )\n }\n\n // Pair each subscription line with its created subscription item \u2014\n // input for the `cpq.subscription.activated` billing-onboarding event.\n const eventItems = subscriptionLines\n .slice(0, allSubItems.length)\n .map((line, i) => ({\n subscriptionItemId: allSubItems[i].id,\n productName: this.getLineName(line),\n mrcTotal: Number(line.mrcTotal),\n nrcTotal: Number(line.nrcTotal),\n }))\n // Only lines with a billable charge \u2014 a zero-value line yields\n // no Billing Item anyway; emitting it would just make the\n // connector create a needless shell account.\n .filter((it) => it.mrcTotal > 0 || it.nrcTotal > 0)\n return { subscriptionId: subscription.id, items: eventItems }\n }\n\n // \u2500\u2500\u2500 Get / List \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async getOrder(orderId: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n async listOrders(filters: OrderListFilters, scope: TenantScope) {\n const page = Math.max(1, filters.page ?? 1)\n const pageSize = Math.min(100, Math.max(1, filters.pageSize ?? 50))\n const offset = (page - 1) * pageSize\n\n const where: Record<string, unknown> = {\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n }\n\n if (filters.customerId) where.customerId = filters.customerId\n if (filters.sourceQuoteId) where.sourceQuoteId = filters.sourceQuoteId\n if (filters.salesOrderId) where.orderId = filters.salesOrderId\n if (filters.currencyCode) where.currencyCode = filters.currencyCode\n if (filters.cpqStatus) {\n where.cpqStatus = Array.isArray(filters.cpqStatus)\n ? { $in: filters.cpqStatus }\n : filters.cpqStatus\n }\n\n const search = filters.search?.trim()\n if (search) {\n where.$or = [\n { orderId: { $ilike: `%${search}%` } },\n { customerId: { $ilike: `%${search}%` } },\n ]\n }\n\n const sortField = filters.sortField ?? 'createdAt'\n const sortDir = filters.sortDir === 'asc' ? 'asc' : 'desc'\n\n const [configs, total] = await this.em.findAndCount(\n CpqOrderConfiguration,\n where,\n { limit: pageSize, offset, orderBy: { [sortField]: sortDir } },\n )\n\n const orderIds = configs.map((c) => c.orderId)\n const salesOrders = orderIds.length > 0\n ? await this.em.find(SalesOrder, { id: { $in: orderIds } })\n : []\n const salesOrderMap = new Map(salesOrders.map((o) => [o.id, o]))\n\n const items = configs.map((c) => ({\n ...c,\n orderNumber: salesOrderMap.get(c.orderId)?.orderNumber ?? null,\n }))\n\n return { items, total, page, pageSize, totalPages: Math.ceil(total / pageSize) }\n }\n\n // \u2500\u2500\u2500 Status Transition \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n async transitionOrderStatus(orderId: string, targetStatus: string, scope: TenantScope): Promise<OrderResult> {\n const em = this.em\n const orderConfig = await this.loadOrderConfig(em, orderId, scope)\n const salesOrder = await this.loadSalesOrder(em, orderConfig.orderId, scope)\n\n const currentStatus = orderConfig.cpqStatus as CpqOrderStatus\n const allowed = CPQ_ORDER_TRANSITIONS[currentStatus] ?? []\n\n if (!allowed.includes(targetStatus as CpqOrderStatus)) {\n throw new OrderError(409, `Cannot transition from '${currentStatus}' to '${targetStatus}'`)\n }\n\n if (targetStatus === 'active') {\n return this.activateOrder(orderId, scope)\n }\n\n orderConfig.cpqStatus = targetStatus\n salesOrder.status = CPQ_TO_SALES_ORDER_STATUS[targetStatus as CpqOrderStatus] ?? targetStatus\n\n await em.flush()\n\n const lines = await em.find(CpqOrderLineConfiguration, {\n orderConfigurationId: orderConfig.id,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n return this.buildOrderResult(em, orderConfig, salesOrder, lines, scope)\n }\n\n // \u2500\u2500\u2500 Helpers \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n private async loadOrderConfig(em: EntityManager, orderId: string, scope: TenantScope): Promise<CpqOrderConfiguration> {\n let config = await em.findOne(CpqOrderConfiguration, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (!config) {\n config = await em.findOne(CpqOrderConfiguration, {\n orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n }\n if (!config) throw new OrderError(404, 'Order not found')\n return config\n }\n\n private async loadSalesOrder(em: EntityManager, orderId: string, scope: TenantScope): Promise<SalesOrder> {\n const order = await em.findOne(SalesOrder, {\n id: orderId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (!order) throw new OrderError(404, 'Sales order not found')\n return order\n }\n\n private async resolveOfferingInfo(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<{ name: string; type: string | null }> {\n if (offeringId) {\n const offering = await em.findOne(CpqProductOffering, {\n id: offeringId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n if (offering) return { name: offering.name, type: offering.offeringType ?? 'simple' }\n }\n const cfg = fallbackConfig ?? null\n const fallbackName =\n (cfg?._arcMirroredName as string | undefined) ??\n (cfg?.offeringName as string | undefined) ??\n 'Configured Item'\n return { name: fallbackName, type: null }\n }\n\n private async resolveOfferingName(\n em: EntityManager,\n offeringId: string | null | undefined,\n scope: TenantScope,\n fallbackConfig?: Record<string, unknown> | null,\n ): Promise<string> {\n const info = await this.resolveOfferingInfo(em, offeringId, scope, fallbackConfig)\n return info.name\n }\n\n private getLineName(line: CpqOrderLineConfiguration): string {\n const config = line.configuration as Record<string, unknown> | null\n return (\n (config?._arcMirroredName as string | undefined) ??\n (config?.offeringName as string | undefined) ??\n (config?.name as string | undefined) ??\n 'Configured Item'\n )\n }\n\n private async buildSubscriptionName(\n orderConfig: CpqOrderConfiguration,\n orderLines: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<string> {\n if (orderLines.length === 1 && orderLines[0].offeringId) {\n return this.resolveOfferingName(this.em, orderLines[0].offeringId, scope)\n }\n const names: string[] = []\n for (const line of orderLines.slice(0, 3)) {\n if (line.offeringId) {\n names.push(await this.resolveOfferingName(this.em, line.offeringId, scope))\n }\n }\n if (names.length > 0) {\n const suffix = orderLines.length > 3 ? ` (+${orderLines.length - 3} more)` : ''\n return names.join(', ') + suffix\n }\n return `Order ${orderConfig.orderId.slice(0, 8)}`\n }\n\n private async buildOrderResult(\n em: EntityManager,\n orderConfig: CpqOrderConfiguration,\n salesOrder: SalesOrder,\n lineConfigs: CpqOrderLineConfiguration[],\n scope: TenantScope,\n ): Promise<OrderResult> {\n const lines: OrderLineResult[] = []\n\n // XD-250 ARC: batch-load source subscription items so we can render the\n // before/after diff per line on the order detail page.\n const arcItemIds = new Set<string>()\n for (const lc of lineConfigs) {\n const m = (lc.configuration as Record<string, unknown> | null)?._arcMirroredFromItemId\n if (typeof m === 'string') arcItemIds.add(m)\n }\n const arcItemMap = new Map<string, CpqInventorySubscriptionItem>()\n if (arcItemIds.size > 0) {\n const items = await em.find(CpqInventorySubscriptionItem, {\n id: { $in: Array.from(arcItemIds) },\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n deletedAt: null,\n })\n for (const it of items) arcItemMap.set(it.id, it)\n }\n\n for (const lc of lineConfigs) {\n const offeringInfo = await this.resolveOfferingInfo(\n em,\n lc.offeringId,\n scope,\n lc.configuration as Record<string, unknown> | null,\n )\n const arcSource = pickArcSourceFromConfig(lc.configuration as Record<string, unknown> | null, arcItemMap)\n lines.push({\n lineId: lc.orderLineId,\n offeringId: lc.offeringId ?? null,\n offeringName: offeringInfo.name,\n offeringType: offeringInfo.type,\n productId: lc.productId ?? null,\n specId: lc.specId ?? null,\n action: lc.action,\n parentLineId: lc.parentLineId ?? null,\n quantity: lc.quantity,\n configuration: stripQuoteOnlyArcMarkers(lc.configuration) ?? {},\n startDate: lc.startDate?.toISOString() ?? null,\n termMonths: lc.termMonths ?? null,\n endDate: lc.endDate?.toISOString() ?? null,\n nrcTotal: Number(lc.nrcTotal),\n mrcTotal: Number(lc.mrcTotal),\n charges: (lc.charges ?? []) as Array<Record<string, unknown>>,\n sourceQuoteLineId: lc.sourceQuoteLineId ?? null,\n arcSource,\n })\n }\n\n // Source quote's quoteType \u2014 surfaced to the order header so the UI can\n // render an AMEND / RENEW / CANCEL badge alongside the order number.\n // CpqOrderConfiguration.sourceQuoteId stores CpqQuoteConfiguration.id\n // (see convertFromConfig \u2014 sourceQuoteId: cpqConfig.id), NOT the\n // SalesQuote.id, so we look it up by `id` rather than by `quoteId`.\n let quoteType = 'new'\n if (orderConfig.sourceQuoteId) {\n const sourceCfg = await em.findOne(CpqQuoteConfiguration, {\n id: orderConfig.sourceQuoteId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n })\n if (sourceCfg?.quoteType) quoteType = sourceCfg.quoteType\n }\n\n const pricing = orderConfig.pricingSummary ?? {}\n\n return {\n id: orderConfig.id,\n orderId: orderConfig.orderId,\n orderNumber: salesOrder.orderNumber ?? '',\n customerId: orderConfig.customerId,\n cpqStatus: orderConfig.cpqStatus,\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n quoteType,\n currencyCode: orderConfig.currencyCode,\n pricingSummary: {\n nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),\n mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),\n currencyCode: orderConfig.currencyCode,\n ...summarizeOrderPricing(lines, quoteType, orderConfig.currencyCode),\n },\n activatedAt: orderConfig.activatedAt?.toISOString() ?? null,\n createdAt: orderConfig.createdAt.toISOString(),\n lines,\n }\n }\n}\n\nfunction pickArcSourceFromConfig(\n cfg: Record<string, unknown> | null,\n map: Map<string, CpqInventorySubscriptionItem>,\n): OrderLineResult['arcSource'] {\n const m = cfg?._arcMirroredFromItemId\n if (typeof m !== 'string') return null\n const item = map.get(m)\n if (!item) return null\n return {\n subscriptionItemId: item.id,\n name: item.name,\n mrcAmount: Number(item.mrcAmount),\n nrcAmount: Number(item.nrcAmount),\n quantity: item.quantity,\n }\n}\n\n// \u2500\u2500\u2500 Error class \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n// Cross-realm brand \u2014 see QUOTING_ERROR_BRAND in cpqQuotingService.ts for why\n// route handlers must not rely on `instanceof`. Use `isOrderError`.\nconst ORDER_ERROR_BRAND = Symbol.for('@dainamite/cpq/OrderError')\n\nexport class OrderError extends Error {\n readonly [ORDER_ERROR_BRAND] = true\n\n constructor(\n public status: number,\n message: string,\n public details?: Record<string, unknown>,\n ) {\n super(message)\n this.name = 'OrderError'\n }\n}\n\nexport function isOrderError(err: unknown): err is OrderError {\n return !!err && typeof err === 'object' && (err as Record<symbol, unknown>)[ORDER_ERROR_BRAND] === true\n}\n\n// \u2500\u2500\u2500 ARC helpers (XD-250) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\n\n/**\n * Build a service-layer ArcLineChange from a CpqQuoteLineConfiguration.\n * For 'add' actions, materializes the itemInput from line + spec data.\n */\nfunction buildArcLineChange(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): ArcLineChange {\n const action = line.action as 'add' | 'modify' | 'cancel'\n if (action === 'add') {\n const itemInput: Omit<CreateSubscriptionItemInput, 'name'> & { name: string } = {\n productId: line.productId ?? undefined,\n offeringId: line.offeringId ?? undefined,\n specId: line.specId ?? undefined,\n name: resolveLineName(line, specMap),\n configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},\n charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n sourceQuoteLineId: line.quoteLineId,\n }\n return {\n quoteLineId: line.quoteLineId,\n action,\n itemInput,\n parentQuoteLineId: line.parentLineId ?? null,\n }\n }\n if (action === 'cancel') {\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n }\n }\n // modify\n return {\n quoteLineId: line.quoteLineId,\n action,\n sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,\n modifyPatch: {\n configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? undefined,\n charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,\n mrcAmount: Number(line.mrcTotal),\n nrcAmount: Number(line.nrcTotal),\n quantity: line.quantity,\n },\n }\n}\n\nfunction resolveLineName(\n line: CpqQuoteLineConfiguration,\n specMap: Map<string, CpqProductSpecification>,\n): string {\n const cfg = line.configuration as Record<string, unknown> | null\n const cfgName = (cfg?._arcMirroredName as string | undefined) ?? null\n if (cfgName) return cfgName\n if (line.specId) {\n const spec = specMap.get(line.specId)\n if (spec?.name) return spec.name\n }\n return 'Configured Item'\n}\n\ninterface ArcEventEnvelope {\n organizationId: string\n tenantId: string\n sourceQuoteId: string\n sourceOrderId: string\n}\n\n// \u2500\u2500\u2500 Subscription-activated event (new-sale billing onboarding) \u2500\u2500\n\ntype ActivatedInventory = {\n subscriptionId: string\n items: Array<{\n subscriptionItemId: string\n productName: string\n /** Line totals \u2014 CPQ pricing has already applied line quantity. */\n mrcTotal: number\n nrcTotal: number\n }>\n}\n\n/**\n * Emit `cpq.subscription.activated` after a new-sale order activation.\n *\n * Payload matches the `@dainamite/cpq-billing-connector`\n * `cpq-subscription-activated` subscriber contract: one CPQ charge per\n * non-zero MRC / NRC total on each subscription item. The connector\n * get-or-creates the Billing Account (keyed on `customerId`) and turns\n * each charge into a Billing Item.\n */\nasync function emitActivatedEvent(\n orderConfig: CpqOrderConfiguration,\n inventory: ActivatedInventory,\n scope: TenantScope,\n): Promise<void> {\n const items = inventory.items.map((it) => ({\n subscriptionItemId: it.subscriptionItemId,\n productName: it.productName,\n // `mrcTotal` / `nrcTotal` are line totals \u2014 CPQ pricing already\n // applied the line quantity. Quantity is therefore 1 here so\n // `unitPrice \u00D7 quantity` stays internally consistent no matter\n // how the connector treats the multiplier.\n quantity: 1,\n charges: [\n ...(it.mrcTotal > 0\n ? [{ type: 'recurring', unitPrice: it.mrcTotal, description: 'Recurring charge' }]\n : []),\n ...(it.nrcTotal > 0\n ? [{ type: 'one_time', amount: it.nrcTotal, description: 'One-time charge' }]\n : []),\n ],\n }))\n await emitCpqEvent('cpq.subscription.activated' as never, {\n subscriptionId: inventory.subscriptionId,\n customerId: orderConfig.customerId,\n organizationId: scope.organizationId,\n tenantId: scope.tenantId,\n currencyCode: orderConfig.currencyCode,\n timestamp: new Date().toISOString(),\n activationDate: new Date().toISOString().slice(0, 10),\n sourceQuoteId: orderConfig.sourceQuoteId ?? null,\n sourceOrderId: orderConfig.id,\n items,\n } as never)\n}\n\n/** Coerce a Date or `date`-column string to a `YYYY-MM-DD` string. */\nfunction iso10(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString().slice(0, 10)\n return String(value).slice(0, 10)\n}\n\ntype ArcChargeItem = {\n subscriptionItemId: string\n productName: string\n quantity: number\n charges: Array<Record<string, unknown>>\n}\n\n/**\n * Build the connector's `addedItems` from a change log's `add` line\n * changes. `mrcAmount` / `nrcAmount` are line totals (CPQ pricing has\n * already applied quantity) \u2192 emit `quantity: 1` with the totals as the\n * charge rate, exactly as `emitActivatedEvent`.\n */\nfunction buildArcAddedItems(changeLog: ApplyArcResult['changeLog']): ArcChargeItem[] {\n const after = changeLog.afterSnapshot as\n | { items?: Array<{ id?: string; name?: string }> }\n | null\n const nameById = new Map(\n (after?.items ?? []).map((i) => [String(i.id), i.name ?? '']),\n )\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n mrcAmount?: unknown\n nrcAmount?: unknown\n }>\n const out: ArcChargeItem[] = []\n for (const change of summary) {\n if (change.action !== 'add' || typeof change.itemId !== 'string') continue\n const mrc = Number(change.mrcAmount)\n const nrc = Number(change.nrcAmount)\n out.push({\n subscriptionItemId: change.itemId,\n productName: nameById.get(change.itemId) || 'Configured item',\n quantity: 1,\n charges: [\n ...(mrc > 0\n ? [{ type: 'recurring', unitPrice: mrc, description: 'Recurring charge' }]\n : []),\n ...(nrc > 0\n ? [{ type: 'one_time', amount: nrc, description: 'One-time charge' }]\n : []),\n ],\n })\n }\n return out\n}\n\n/** Subscription-item ids removed by a change log's `cancel` line changes. */\nfunction buildArcRemovedItemIds(changeLog: ApplyArcResult['changeLog']): string[] {\n const summary = (changeLog.lineChanges ?? []) as Array<{\n action?: string\n itemId?: string\n }>\n return summary\n .filter((c) => c.action === 'cancel' && typeof c.itemId === 'string')\n .map((c) => c.itemId as string)\n}\n\nasync function emitAmendedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.amended' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n // No operator-chosen amend date today \u2014 the activation moment is\n // the effective date. The billing connector computes the mid-cycle\n // proration from this date + the account's billing cycle.\n effectiveDate: iso10(result.changeLog.effectiveAt),\n addedItems: buildArcAddedItems(result.changeLog),\n removedSubscriptionItemIds: buildArcRemovedItemIds(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitRenewedEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.renewed' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n newTermStart: iso10(result.subscription.currentTermStart),\n newTermEnd: iso10(result.subscription.currentTermEnd),\n addedItems: buildArcAddedItems(result.changeLog),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitCancelledEvent(\n result: ApplyArcResult,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.cancelled' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: result.subscription.id,\n customerId: result.subscription.customerId,\n effectiveDate: iso10(result.changeLog.effectiveAt),\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitMergedEvent(\n merge: ApplyMergeRenewalResult,\n envelope: ArcEventEnvelope,\n sourceSubscriptionIds: string[],\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.merged' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n mergedSubscriptionId: merge.mergedSubscription.id,\n sourceSubscriptionIds,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n\nasync function emitSupersededEvent(\n sourceLog: { id: string; subscriptionId: string },\n effectiveDate: string | null,\n envelope: ArcEventEnvelope,\n): Promise<void> {\n await emitCpqEvent('cpq.subscription.superseded' as never, {\n tenantId: envelope.tenantId,\n organizationId: envelope.organizationId,\n subscriptionId: sourceLog.subscriptionId,\n effectiveDate,\n sourceQuoteId: envelope.sourceQuoteId,\n sourceOrderId: envelope.sourceOrderId,\n timestamp: new Date().toISOString(),\n } as never)\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -63,6 +63,8 @@ class DefaultCpqPricingService {
|
|
|
63
63
|
if (currencyCode) {
|
|
64
64
|
resolved.currencyCode = currencyCode;
|
|
65
65
|
}
|
|
66
|
+
resolved.listUnitPrice = resolved.unitPrice;
|
|
67
|
+
resolved.listTotalPrice = resolved.totalPrice;
|
|
66
68
|
resolved.adjustments = [];
|
|
67
69
|
results.push(resolved);
|
|
68
70
|
}
|
|
@@ -74,6 +76,10 @@ class DefaultCpqPricingService {
|
|
|
74
76
|
if (rules.length > 0) {
|
|
75
77
|
this.applyPriceRules(results, rules, configuration);
|
|
76
78
|
}
|
|
79
|
+
for (const resolved of results) {
|
|
80
|
+
resolved.standardUnitPrice = resolved.unitPrice;
|
|
81
|
+
resolved.standardTotalPrice = resolved.totalPrice;
|
|
82
|
+
}
|
|
77
83
|
return results;
|
|
78
84
|
}
|
|
79
85
|
async loadPriceRules(params) {
|
|
@@ -141,6 +147,7 @@ class DefaultCpqPricingService {
|
|
|
141
147
|
charge.totalPrice = unitPriceAfter;
|
|
142
148
|
}
|
|
143
149
|
const adjustment = {
|
|
150
|
+
origin: "rule",
|
|
144
151
|
ruleCode: rule.code,
|
|
145
152
|
ruleName: rule.name,
|
|
146
153
|
ruleType: rule.ruleType,
|