@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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/modules/cpq/services/cpqPricingService.ts"],
|
|
4
|
-
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CpqProductCharge, CpqPricingTable, CpqPricingTableEntry, CpqPriceRule } from '../data/entities'\nimport type { ResolvedCharge, RuleAdjustment, TierBreakdown } from './types'\nimport { getBaseCurrencyCode } from './types'\nimport { normalizeChargePricing } from '../data/charge-pricing'\n\nexport type { ResolvedCharge, RuleAdjustment, TierBreakdown }\n\nexport interface QuotePricingSummary {\n lines: Array<{\n lineId: string\n productId: string\n charges: ResolvedCharge[]\n }>\n totals: {\n nrcTotal: number\n mrcTotal: number\n usageCharges: Array<{ chargeCode: string; unitPrice: number; note: string }>\n }\n currencyCode: string\n}\n\ninterface ApplicabilityCondition {\n attribute: string\n operator: 'eq' | 'neq'\n value: string\n}\n\nfunction evaluateApplicability(\n condition: Record<string, unknown> | null | undefined,\n configuration: Record<string, unknown>,\n): boolean {\n if (!condition) return true\n const cond = condition as unknown as ApplicabilityCondition\n if (!cond.attribute || !cond.operator) return true\n const configValue = String(configuration[cond.attribute] ?? '')\n const conditionValue = String(cond.value ?? '')\n if (cond.operator === 'eq') return configValue === conditionValue\n if (cond.operator === 'neq') return configValue !== conditionValue\n return true\n}\n\nfunction entryMatchesDimensions(\n entry: CpqPricingTableEntry,\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): boolean {\n return dimensions.every((dim) => {\n const configVal = String(configuration[dim.key] ?? '')\n const entryVal = String(entry.dimensionValues[dim.key] ?? '')\n return configVal.toLowerCase() === entryVal.toLowerCase()\n })\n}\n\nfunction matchEntry(\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): CpqPricingTableEntry | null {\n return entries.find((entry) => entryMatchesDimensions(entry, dimensions, configuration)) ?? null\n}\n\nexport class DefaultCpqPricingService {\n private em: EntityManager\n\n constructor(em: EntityManager) {\n this.em = em\n }\n\n /**\n * Resolve charges for an offering (v2) or product (v1 fallback).\n * When offeringId is provided, charges are loaded by offeringId.\n * When only productId is provided, falls back to loading by productId.\n */\n async resolveProductCharges(params: {\n offeringId?: string | null\n productId?: string | null\n configuration: Record<string, unknown>\n currencyCode?: string\n tenantId: string\n organizationId: string\n }): Promise<ResolvedCharge[]> {\n const { offeringId, productId, configuration, tenantId, organizationId, currencyCode } = params\n\n const chargeFilter: Record<string, unknown> = {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n\n if (offeringId) {\n chargeFilter.offeringId = offeringId\n } else if (productId) {\n chargeFilter.productId = productId\n } else {\n return []\n }\n\n const charges = await this.em.find(CpqProductCharge, chargeFilter, {\n orderBy: { sortOrder: 'asc' },\n })\n\n const applicableCharges = charges.filter((charge) =>\n evaluateApplicability(charge.applicabilityCondition, configuration),\n )\n\n const results: ResolvedCharge[] = []\n for (const charge of applicableCharges) {\n const resolved = await this.calculateCharge({\n charge,\n configuration,\n tenantId,\n organizationId,\n currencyCode,\n })\n // R-CPRICE-8: quote-level currency overrides entry-level currency for consistency.\n // Entry filtering by currencyCode (line 274) ensures price values already match.\n if (currencyCode) {\n resolved.currencyCode = currencyCode\n }\n resolved.adjustments = []\n results.push(resolved)\n }\n\n const rules = await this.loadPriceRules({\n offeringId: offeringId ?? null,\n tenantId,\n organizationId,\n })\n\n if (rules.length > 0) {\n this.applyPriceRules(results, rules, configuration)\n }\n\n return results\n }\n\n private async loadPriceRules(params: {\n offeringId: string | null | undefined\n tenantId: string\n organizationId: string\n }): Promise<CpqPriceRule[]> {\n const { offeringId, tenantId, organizationId } = params\n\n const rules = await this.em.find(\n CpqPriceRule,\n {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n $or: [\n { productOfferingId: null },\n ...(offeringId ? [{ productOfferingId: offeringId }] : []),\n ],\n },\n { orderBy: { sortOrder: 'asc' } },\n )\n\n return rules\n }\n\n private applyPriceRules(\n charges: ResolvedCharge[],\n rules: CpqPriceRule[],\n configuration: Record<string, unknown>,\n ): void {\n for (const rule of rules) {\n if (!evaluateApplicability(rule.applicabilityCondition, configuration)) continue\n\n for (const charge of charges) {\n if (rule.chargeCodeFilter && rule.chargeCodeFilter !== charge.chargeCode) continue\n if (rule.chargeTypeFilter && rule.chargeTypeFilter !== charge.chargeType) continue\n\n const unitPriceBefore = charge.unitPrice\n const ruleValue = Number(rule.value)\n let unitPriceAfter: number\n let totalPriceOverride: number | undefined\n\n switch (rule.ruleType) {\n case 'discount_percent':\n unitPriceAfter = Math.max(0, unitPriceBefore * (1 - ruleValue / 100))\n break\n case 'discount_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = Math.max(0, currentTotal - ruleValue)\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'surcharge_percent':\n unitPriceAfter = unitPriceBefore * (1 + ruleValue / 100)\n break\n case 'surcharge_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = currentTotal + ruleValue\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'price_override':\n unitPriceAfter = ruleValue\n break\n default:\n continue\n }\n\n charge.unitPrice = unitPriceAfter\n\n if (charge.chargeType === 'usage') {\n // totalPrice stays null for usage charges\n } else if (totalPriceOverride !== undefined) {\n charge.totalPrice = totalPriceOverride\n } else if (charge.quantity !== null) {\n charge.totalPrice = unitPriceAfter * charge.quantity\n } else {\n charge.totalPrice = unitPriceAfter\n }\n\n const adjustment: RuleAdjustment = {\n ruleCode: rule.code,\n ruleName: rule.name,\n ruleType: rule.ruleType,\n value: ruleValue,\n unitPriceBefore,\n unitPriceAfter,\n delta: unitPriceAfter - unitPriceBefore,\n }\n\n if (!charge.adjustments) {\n charge.adjustments = [adjustment]\n } else {\n charge.adjustments.push(adjustment)\n }\n }\n }\n }\n\n async calculateCharge(params: {\n charge: CpqProductCharge\n configuration: Record<string, unknown>\n tenantId: string\n organizationId: string\n currencyCode?: string\n }): Promise<ResolvedCharge> {\n const { charge, configuration, tenantId, organizationId, currencyCode } = params\n\n // Resolve the two axes (chargeModel \u00D7 pricingSource), tolerating legacy\n // rows that still carry a combined pricingMethod (flat | per_unit | tiered).\n const { model, source } = normalizeChargePricing(charge)\n\n const fallbackCurrency = currencyCode ?? charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId })\n const base: ResolvedCharge = {\n chargeCode: charge.code,\n chargeName: charge.name,\n chargeType: charge.chargeType as ResolvedCharge['chargeType'],\n pricingMethod: model,\n unitPrice: 0,\n quantity: 1,\n totalPrice: 0,\n currencyCode: fallbackCurrency,\n }\n\n // \u2500\u2500 Fixed price: unit price is the charge's own fixedPrice. Also the\n // fallback when a table-sourced charge has no table configured yet. \u2500\u2500\n if (source === 'fixed' || !charge.pricingTableId) {\n const unitPrice = Number(charge.fixedPrice ?? 0)\n if (model === 'per_unit') {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity }\n }\n // flat (volume/tiered without a table degrade to a single flat price).\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice }\n }\n\n // \u2500\u2500 Table lookup \u2500\u2500\n const table = await this.em.findOne(CpqPricingTable, {\n id: charge.pricingTableId,\n tenantId,\n organizationId,\n })\n if (!table) return base\n\n const entryFilters: Record<string, unknown> = {\n pricingTableId: table.id,\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n if (currencyCode) {\n entryFilters.currencyCode = currencyCode\n }\n\n const entries = await this.em.find(CpqPricingTableEntry, entryFilters)\n\n const priceKey = charge.priceColumnKey ?? ''\n\n if (model === 'flat') {\n return this.calculateFlat(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'per_unit') {\n return this.calculatePerUnit(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'volume') {\n return this.calculateVolume(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'tiered') {\n return this.calculateTiered(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n\n return base\n }\n\n private calculateFlat(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entry.currencyCode,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice, currencyCode: entry.currencyCode }\n }\n\n private calculatePerUnit(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n const entryCurrency = entry.currencyCode\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entryCurrency,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity, currencyCode: entryCurrency }\n }\n\n private calculateTiered(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n // tiered must filter to the dimension-matched slice first; flat/per_unit do this via matchEntry.\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n // No row matches the configured dimensions \u2192 unpriced; fall back to base\n // (quantity 1, total 0), consistent with flat/per_unit's null-match path.\n if (matched.length === 0) return base\n\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n\n let allocated = 0\n let total = 0\n const tiers: TierBreakdown['tiers'] = []\n let entryCurrency = base.currencyCode\n\n for (const entry of sorted) {\n if (allocated >= quantity) break\n const to = entry.rangeTo ? Number(entry.rangeTo) : Infinity\n const tierCeil = to === Infinity ? quantity : Math.min(to, quantity)\n const qty = tierCeil - allocated\n if (qty <= 0) continue\n const price = entry.prices[priceKey] ?? 0\n entryCurrency = entry.currencyCode\n tiers.push({\n tier: entry.tierNumber ?? 0,\n from: allocated + 1,\n to: allocated + qty,\n pricePerUnit: price,\n quantity: qty,\n subtotal: qty * price,\n })\n total += qty * price\n allocated += qty\n }\n\n return {\n ...base,\n unitPrice: quantity > 0 ? total / quantity : 0,\n quantity,\n totalPrice: total,\n breakdown: { tiers },\n currencyCode: entryCurrency,\n }\n }\n\n // Volume pricing: the WHOLE quantity is billed at the single rate of the tier\n // whose range contains the total quantity \u2014 unlike tiered, which bills each\n // slice at its own rate. Dimension-filtered first, like tiered.\n private calculateVolume(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n if (matched.length === 0) return base\n\n // Ranges are [rangeFrom, rangeTo]; rangeTo null = \u221E. Fall back to the top\n // tier if the quantity overflows every defined range.\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n const entry =\n sorted.find((e) => {\n const from = e.rangeFrom != null ? Number(e.rangeFrom) : 0\n const to = e.rangeTo != null ? Number(e.rangeTo) : Infinity\n return quantity >= from && quantity <= to\n }) ?? sorted[sorted.length - 1]\n\n const unitPrice = entry.prices[priceKey] ?? 0\n return {\n ...base,\n unitPrice,\n quantity,\n totalPrice: unitPrice * quantity,\n currencyCode: entry.currencyCode,\n }\n }\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { CpqProductCharge, CpqPricingTable, CpqPricingTableEntry, CpqPriceRule } from '../data/entities'\nimport type { ResolvedCharge, RuleAdjustment, TierBreakdown } from './types'\nimport { getBaseCurrencyCode } from './types'\nimport { normalizeChargePricing } from '../data/charge-pricing'\n\nexport type { ResolvedCharge, RuleAdjustment, TierBreakdown }\n\nexport interface QuotePricingSummary {\n lines: Array<{\n lineId: string\n productId: string\n charges: ResolvedCharge[]\n }>\n totals: {\n nrcTotal: number\n mrcTotal: number\n usageCharges: Array<{ chargeCode: string; unitPrice: number; note: string }>\n }\n currencyCode: string\n}\n\ninterface ApplicabilityCondition {\n attribute: string\n operator: 'eq' | 'neq'\n value: string\n}\n\nfunction evaluateApplicability(\n condition: Record<string, unknown> | null | undefined,\n configuration: Record<string, unknown>,\n): boolean {\n if (!condition) return true\n const cond = condition as unknown as ApplicabilityCondition\n if (!cond.attribute || !cond.operator) return true\n const configValue = String(configuration[cond.attribute] ?? '')\n const conditionValue = String(cond.value ?? '')\n if (cond.operator === 'eq') return configValue === conditionValue\n if (cond.operator === 'neq') return configValue !== conditionValue\n return true\n}\n\nfunction entryMatchesDimensions(\n entry: CpqPricingTableEntry,\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): boolean {\n return dimensions.every((dim) => {\n const configVal = String(configuration[dim.key] ?? '')\n const entryVal = String(entry.dimensionValues[dim.key] ?? '')\n return configVal.toLowerCase() === entryVal.toLowerCase()\n })\n}\n\nfunction matchEntry(\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n): CpqPricingTableEntry | null {\n return entries.find((entry) => entryMatchesDimensions(entry, dimensions, configuration)) ?? null\n}\n\nexport class DefaultCpqPricingService {\n private em: EntityManager\n\n constructor(em: EntityManager) {\n this.em = em\n }\n\n /**\n * Resolve charges for an offering (v2) or product (v1 fallback).\n * When offeringId is provided, charges are loaded by offeringId.\n * When only productId is provided, falls back to loading by productId.\n */\n async resolveProductCharges(params: {\n offeringId?: string | null\n productId?: string | null\n configuration: Record<string, unknown>\n currencyCode?: string\n tenantId: string\n organizationId: string\n }): Promise<ResolvedCharge[]> {\n const { offeringId, productId, configuration, tenantId, organizationId, currencyCode } = params\n\n const chargeFilter: Record<string, unknown> = {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n\n if (offeringId) {\n chargeFilter.offeringId = offeringId\n } else if (productId) {\n chargeFilter.productId = productId\n } else {\n return []\n }\n\n const charges = await this.em.find(CpqProductCharge, chargeFilter, {\n orderBy: { sortOrder: 'asc' },\n })\n\n const applicableCharges = charges.filter((charge) =>\n evaluateApplicability(charge.applicabilityCondition, configuration),\n )\n\n const results: ResolvedCharge[] = []\n for (const charge of applicableCharges) {\n const resolved = await this.calculateCharge({\n charge,\n configuration,\n tenantId,\n organizationId,\n currencyCode,\n })\n // R-CPRICE-8: quote-level currency overrides entry-level currency for consistency.\n // Entry filtering by currencyCode (line 274) ensures price values already match.\n if (currencyCode) {\n resolved.currencyCode = currencyCode\n }\n resolved.listUnitPrice = resolved.unitPrice\n resolved.listTotalPrice = resolved.totalPrice\n resolved.adjustments = []\n results.push(resolved)\n }\n\n const rules = await this.loadPriceRules({\n offeringId: offeringId ?? null,\n tenantId,\n organizationId,\n })\n\n if (rules.length > 0) {\n this.applyPriceRules(results, rules, configuration)\n }\n\n for (const resolved of results) {\n resolved.standardUnitPrice = resolved.unitPrice\n resolved.standardTotalPrice = resolved.totalPrice\n }\n\n return results\n }\n\n private async loadPriceRules(params: {\n offeringId: string | null | undefined\n tenantId: string\n organizationId: string\n }): Promise<CpqPriceRule[]> {\n const { offeringId, tenantId, organizationId } = params\n\n const rules = await this.em.find(\n CpqPriceRule,\n {\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n $or: [\n { productOfferingId: null },\n ...(offeringId ? [{ productOfferingId: offeringId }] : []),\n ],\n },\n { orderBy: { sortOrder: 'asc' } },\n )\n\n return rules\n }\n\n private applyPriceRules(\n charges: ResolvedCharge[],\n rules: CpqPriceRule[],\n configuration: Record<string, unknown>,\n ): void {\n for (const rule of rules) {\n if (!evaluateApplicability(rule.applicabilityCondition, configuration)) continue\n\n for (const charge of charges) {\n if (rule.chargeCodeFilter && rule.chargeCodeFilter !== charge.chargeCode) continue\n if (rule.chargeTypeFilter && rule.chargeTypeFilter !== charge.chargeType) continue\n\n const unitPriceBefore = charge.unitPrice\n const ruleValue = Number(rule.value)\n let unitPriceAfter: number\n let totalPriceOverride: number | undefined\n\n switch (rule.ruleType) {\n case 'discount_percent':\n unitPriceAfter = Math.max(0, unitPriceBefore * (1 - ruleValue / 100))\n break\n case 'discount_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = Math.max(0, currentTotal - ruleValue)\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'surcharge_percent':\n unitPriceAfter = unitPriceBefore * (1 + ruleValue / 100)\n break\n case 'surcharge_absolute': {\n const currentTotal = charge.totalPrice ?? unitPriceBefore\n totalPriceOverride = currentTotal + ruleValue\n const qty = charge.quantity && charge.quantity > 0 ? charge.quantity : 1\n unitPriceAfter = totalPriceOverride / qty\n break\n }\n case 'price_override':\n unitPriceAfter = ruleValue\n break\n default:\n continue\n }\n\n charge.unitPrice = unitPriceAfter\n\n if (charge.chargeType === 'usage') {\n // totalPrice stays null for usage charges\n } else if (totalPriceOverride !== undefined) {\n charge.totalPrice = totalPriceOverride\n } else if (charge.quantity !== null) {\n charge.totalPrice = unitPriceAfter * charge.quantity\n } else {\n charge.totalPrice = unitPriceAfter\n }\n\n const adjustment: RuleAdjustment = {\n origin: 'rule',\n ruleCode: rule.code,\n ruleName: rule.name,\n ruleType: rule.ruleType,\n value: ruleValue,\n unitPriceBefore,\n unitPriceAfter,\n delta: unitPriceAfter - unitPriceBefore,\n }\n\n if (!charge.adjustments) {\n charge.adjustments = [adjustment]\n } else {\n charge.adjustments.push(adjustment)\n }\n }\n }\n }\n\n async calculateCharge(params: {\n charge: CpqProductCharge\n configuration: Record<string, unknown>\n tenantId: string\n organizationId: string\n currencyCode?: string\n }): Promise<ResolvedCharge> {\n const { charge, configuration, tenantId, organizationId, currencyCode } = params\n\n // Resolve the two axes (chargeModel \u00D7 pricingSource), tolerating legacy\n // rows that still carry a combined pricingMethod (flat | per_unit | tiered).\n const { model, source } = normalizeChargePricing(charge)\n\n const fallbackCurrency = currencyCode ?? charge.currencyCode ?? await getBaseCurrencyCode(this.em, { tenantId, organizationId })\n const base: ResolvedCharge = {\n chargeCode: charge.code,\n chargeName: charge.name,\n chargeType: charge.chargeType as ResolvedCharge['chargeType'],\n pricingMethod: model,\n unitPrice: 0,\n quantity: 1,\n totalPrice: 0,\n currencyCode: fallbackCurrency,\n }\n\n // \u2500\u2500 Fixed price: unit price is the charge's own fixedPrice. Also the\n // fallback when a table-sourced charge has no table configured yet. \u2500\u2500\n if (source === 'fixed' || !charge.pricingTableId) {\n const unitPrice = Number(charge.fixedPrice ?? 0)\n if (model === 'per_unit') {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity }\n }\n // flat (volume/tiered without a table degrade to a single flat price).\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice }\n }\n\n // \u2500\u2500 Table lookup \u2500\u2500\n const table = await this.em.findOne(CpqPricingTable, {\n id: charge.pricingTableId,\n tenantId,\n organizationId,\n })\n if (!table) return base\n\n const entryFilters: Record<string, unknown> = {\n pricingTableId: table.id,\n tenantId,\n organizationId,\n isActive: true,\n deletedAt: null,\n }\n if (currencyCode) {\n entryFilters.currencyCode = currencyCode\n }\n\n const entries = await this.em.find(CpqPricingTableEntry, entryFilters)\n\n const priceKey = charge.priceColumnKey ?? ''\n\n if (model === 'flat') {\n return this.calculateFlat(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'per_unit') {\n return this.calculatePerUnit(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'volume') {\n return this.calculateVolume(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n if (model === 'tiered') {\n return this.calculateTiered(base, charge, entries, table.dimensions, configuration, priceKey)\n }\n\n return base\n }\n\n private calculateFlat(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entry.currencyCode,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n return { ...base, unitPrice, quantity: 1, totalPrice: unitPrice, currencyCode: entry.currencyCode }\n }\n\n private calculatePerUnit(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const entry = matchEntry(entries, dimensions, configuration)\n if (!entry) return base\n const unitPrice = entry.prices[priceKey] ?? 0\n const entryCurrency = entry.currencyCode\n\n if (charge.chargeType === 'usage') {\n return {\n ...base,\n unitPrice,\n quantity: null,\n totalPrice: null,\n currencyCode: entryCurrency,\n note: charge.description ?? 'Usage-based charge \u2014 billed on actual consumption.',\n }\n }\n\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n return { ...base, unitPrice, quantity, totalPrice: unitPrice * quantity, currencyCode: entryCurrency }\n }\n\n private calculateTiered(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n // tiered must filter to the dimension-matched slice first; flat/per_unit do this via matchEntry.\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n // No row matches the configured dimensions \u2192 unpriced; fall back to base\n // (quantity 1, total 0), consistent with flat/per_unit's null-match path.\n if (matched.length === 0) return base\n\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n\n let allocated = 0\n let total = 0\n const tiers: TierBreakdown['tiers'] = []\n let entryCurrency = base.currencyCode\n\n for (const entry of sorted) {\n if (allocated >= quantity) break\n const to = entry.rangeTo ? Number(entry.rangeTo) : Infinity\n const tierCeil = to === Infinity ? quantity : Math.min(to, quantity)\n const qty = tierCeil - allocated\n if (qty <= 0) continue\n const price = entry.prices[priceKey] ?? 0\n entryCurrency = entry.currencyCode\n tiers.push({\n tier: entry.tierNumber ?? 0,\n from: allocated + 1,\n to: allocated + qty,\n pricePerUnit: price,\n quantity: qty,\n subtotal: qty * price,\n })\n total += qty * price\n allocated += qty\n }\n\n return {\n ...base,\n unitPrice: quantity > 0 ? total / quantity : 0,\n quantity,\n totalPrice: total,\n breakdown: { tiers },\n currencyCode: entryCurrency,\n }\n }\n\n // Volume pricing: the WHOLE quantity is billed at the single rate of the tier\n // whose range contains the total quantity \u2014 unlike tiered, which bills each\n // slice at its own rate. Dimension-filtered first, like tiered.\n private calculateVolume(\n base: ResolvedCharge,\n charge: CpqProductCharge,\n entries: CpqPricingTableEntry[],\n dimensions: Array<{ key: string }>,\n configuration: Record<string, unknown>,\n priceKey: string,\n ): ResolvedCharge {\n const quantity = Number(configuration[charge.quantityAttributeCode ?? ''] ?? 0)\n if (quantity <= 0) return { ...base, quantity: 0, totalPrice: 0 }\n\n const matched = entries.filter((entry) => entryMatchesDimensions(entry, dimensions, configuration))\n if (matched.length === 0) return base\n\n // Ranges are [rangeFrom, rangeTo]; rangeTo null = \u221E. Fall back to the top\n // tier if the quantity overflows every defined range.\n const sorted = [...matched].sort((a, b) => (a.tierNumber ?? 0) - (b.tierNumber ?? 0))\n const entry =\n sorted.find((e) => {\n const from = e.rangeFrom != null ? Number(e.rangeFrom) : 0\n const to = e.rangeTo != null ? Number(e.rangeTo) : Infinity\n return quantity >= from && quantity <= to\n }) ?? sorted[sorted.length - 1]\n\n const unitPrice = entry.prices[priceKey] ?? 0\n return {\n ...base,\n unitPrice,\n quantity,\n totalPrice: unitPrice * quantity,\n currencyCode: entry.currencyCode,\n }\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["entry"]
|
|
7
7
|
}
|