@dainamite/cpq 0.4.0 → 0.6.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 +70 -0
- package/MIGRATION.md +101 -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/handleArcError.js +2 -2
- package/dist/modules/cpq/api/_helpers/handleArcError.js.map +2 -2
- 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]/activate/route.js +2 -2
- package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/[orderId]/route.js +19 -3
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/[orderId]/status/route.js +2 -2
- package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/route.js +49 -4
- package/dist/modules/cpq/api/orders/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/clone/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 +45 -5
- 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 +34 -4
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/route.js +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/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/api/quotes/route.js +2 -2
- package/dist/modules/cpq/api/quotes/route.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +22 -8
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +89 -232
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +1 -1
- 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/page.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +205 -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 +144 -221
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +1 -1
- 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/page.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +1 -1
- 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 +154 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/CpqListView.js.map +2 -2
- package/dist/modules/cpq/components/PriceWaterfall.js +66 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +117 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- 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/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 +34 -30
- 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 +339 -111
- 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/package.json +9 -9
- package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +8 -5
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -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/handleArcError.ts +12 -12
- 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]/activate/route.ts +2 -2
- package/src/modules/cpq/api/orders/[orderId]/route.ts +20 -2
- package/src/modules/cpq/api/orders/[orderId]/status/route.ts +2 -2
- package/src/modules/cpq/api/orders/route.ts +53 -3
- package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +2 -2
- 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 +51 -4
- 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 +42 -3
- package/src/modules/cpq/api/quotes/[quoteId]/route.ts +2 -2
- 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/api/quotes/route.ts +2 -2
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +1 -1
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +1 -1
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +32 -10
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +60 -269
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/orders/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- 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 +178 -266
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +1 -1
- 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 +201 -0
- package/src/modules/cpq/components/CpqListView.tsx +2 -1
- package/src/modules/cpq/components/PriceWaterfall.tsx +94 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- 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/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__/errorBrands.test.ts +72 -0
- 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 +32 -28
- package/src/modules/cpq/services/cpqPricingService.ts +8 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +388 -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
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Frozen Amend mirror lines.
|
|
3
|
+
*
|
|
4
|
+
* An Amend quote mirrors every live subscription item as a `modify` line.
|
|
5
|
+
* Engine-priced mirror lines used to be re-priced at today's catalog price on
|
|
6
|
+
* every recalculation, and activation wrote that price back to the
|
|
7
|
+
* subscription item — so amending one item silently moved every other item's
|
|
8
|
+
* price. A mirror line on an Amend quote is therefore created **frozen**: it
|
|
9
|
+
* keeps the source item's stored charges until a pricing input of that line
|
|
10
|
+
* changes, at which point it is re-priced as before.
|
|
11
|
+
*
|
|
12
|
+
* The flag lives in the line configuration jsonb next to the existing
|
|
13
|
+
* `_arcMirrored*` markers, so no schema change is needed.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
export const ARC_PRICE_FROZEN_KEY = '_arcPriceFrozen'
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Set when the server unfreezes an Amend mirror line because a pricing input
|
|
20
|
+
* changed — drives the "re-priced at the current catalog price" notice (an
|
|
21
|
+
* explicit marker, so Amend quotes created before freezing existed don't
|
|
22
|
+
* show it).
|
|
23
|
+
*/
|
|
24
|
+
export const ARC_REPRICED_KEY = '_arcRepriced'
|
|
25
|
+
|
|
26
|
+
/** Markers that describe quote-time pricing state and never leave the quote. */
|
|
27
|
+
const QUOTE_ONLY_ARC_KEYS = [ARC_PRICE_FROZEN_KEY, ARC_REPRICED_KEY] as const
|
|
28
|
+
|
|
29
|
+
const ARC_MARKER_PREFIX = '_arc'
|
|
30
|
+
|
|
31
|
+
export type ArcMirrorQuoteType = 'amend' | 'cancel' | 'renew'
|
|
32
|
+
|
|
33
|
+
export function isArcPriceFrozen(configuration: Record<string, unknown> | null | undefined): boolean {
|
|
34
|
+
return configuration?.[ARC_PRICE_FROZEN_KEY] === true
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Whether recalculation must keep a line's stored charges. Only server-made
|
|
39
|
+
* mirror lines on Amend quotes qualify — the flag alone is never trusted
|
|
40
|
+
* (client input is stripped of `_arc*` keys, and a clone of an Amend quote
|
|
41
|
+
* is a new quote).
|
|
42
|
+
*/
|
|
43
|
+
export function isArcMirrorLine(configuration: Record<string, unknown> | null | undefined, quoteType: string | null | undefined): boolean {
|
|
44
|
+
return (quoteType === 'amend' || quoteType === 'renew' || quoteType === 'cancel') && typeof configuration?._arcMirroredFromItemId === 'string'
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function isFrozenAmendMirrorLine(
|
|
48
|
+
configuration: Record<string, unknown> | null | undefined,
|
|
49
|
+
quoteType: string | null | undefined,
|
|
50
|
+
): boolean {
|
|
51
|
+
return (
|
|
52
|
+
quoteType === 'amend' &&
|
|
53
|
+
typeof configuration?._arcMirroredFromItemId === 'string' &&
|
|
54
|
+
isArcPriceFrozen(configuration)
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Configuration for a mirror line: the item's configuration plus the ARC
|
|
60
|
+
* markers. Only Amend lines are frozen — Renew re-prices by product decision
|
|
61
|
+
* and Cancel lines are not priced.
|
|
62
|
+
*/
|
|
63
|
+
export function buildArcMirrorConfiguration(
|
|
64
|
+
itemConfiguration: Record<string, unknown> | null | undefined,
|
|
65
|
+
item: { id: string; name: string },
|
|
66
|
+
quoteType: ArcMirrorQuoteType,
|
|
67
|
+
): Record<string, unknown> {
|
|
68
|
+
return {
|
|
69
|
+
// An item's configuration can carry markers from an earlier ARC quote
|
|
70
|
+
// (activation copies line configuration onto the item); never inherit them.
|
|
71
|
+
...stripArcMarkers(itemConfiguration),
|
|
72
|
+
_arcMirroredFromItemId: item.id,
|
|
73
|
+
_arcMirroredName: item.name,
|
|
74
|
+
...(quoteType === 'amend' ? { [ARC_PRICE_FROZEN_KEY]: true } : {}),
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Line configuration as it may leave the quote (order lines, subscription
|
|
80
|
+
* items, clones): quote-only pricing markers are dropped; the descriptive
|
|
81
|
+
* `_arcMirrored*` markers stay.
|
|
82
|
+
*/
|
|
83
|
+
export function stripQuoteOnlyArcMarkers(
|
|
84
|
+
configuration: Record<string, unknown> | null | undefined,
|
|
85
|
+
): Record<string, unknown> | null | undefined {
|
|
86
|
+
if (!configuration || !QUOTE_ONLY_ARC_KEYS.some((key) => key in configuration)) return configuration
|
|
87
|
+
const out: Record<string, unknown> = {}
|
|
88
|
+
for (const [key, value] of Object.entries(configuration)) {
|
|
89
|
+
if (!(QUOTE_ONLY_ARC_KEYS as readonly string[]).includes(key)) out[key] = value
|
|
90
|
+
}
|
|
91
|
+
return out
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Configuration without any `_arc*` marker. ARC markers are server-owned:
|
|
96
|
+
* client-supplied configuration goes through this before it is stored.
|
|
97
|
+
*/
|
|
98
|
+
export function stripArcMarkers(configuration: Record<string, unknown> | null | undefined): Record<string, unknown> {
|
|
99
|
+
const out: Record<string, unknown> = {}
|
|
100
|
+
for (const [key, value] of Object.entries(configuration ?? {})) {
|
|
101
|
+
if (!key.startsWith(ARC_MARKER_PREFIX)) out[key] = value
|
|
102
|
+
}
|
|
103
|
+
return out
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function canonical(value: unknown): string {
|
|
107
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`
|
|
108
|
+
if (value && typeof value === 'object') {
|
|
109
|
+
const entries = Object.keys(value as Record<string, unknown>)
|
|
110
|
+
.sort()
|
|
111
|
+
.map((key) => `${JSON.stringify(key)}:${canonical((value as Record<string, unknown>)[key])}`)
|
|
112
|
+
return `{${entries.join(',')}}`
|
|
113
|
+
}
|
|
114
|
+
return JSON.stringify(value ?? null)
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function isoDay(value: Date | string | null | undefined): string | null {
|
|
118
|
+
if (!value) return null
|
|
119
|
+
if (value instanceof Date) return value.toISOString().slice(0, 10)
|
|
120
|
+
return String(value).slice(0, 10)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
export type ArcLinePricingState = {
|
|
124
|
+
configuration: Record<string, unknown> | null | undefined
|
|
125
|
+
quantity: number
|
|
126
|
+
termMonths?: number | null
|
|
127
|
+
startDate?: Date | string | null
|
|
128
|
+
endDate?: Date | string | null
|
|
129
|
+
usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }> | null
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
export type ArcLinePricingUpdate = {
|
|
133
|
+
configuration: Record<string, unknown>
|
|
134
|
+
quantity?: number
|
|
135
|
+
termMonths?: number
|
|
136
|
+
startDate?: string
|
|
137
|
+
endDate?: string
|
|
138
|
+
usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }>
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Whether an update changes any input the pricing engine reads. The quote UI
|
|
143
|
+
* always resubmits the full configuration, so "a field was sent" is not a
|
|
144
|
+
* change — values are compared (ARC markers ignored, key order ignored).
|
|
145
|
+
* Fields the update omits keep their stored value.
|
|
146
|
+
*/
|
|
147
|
+
export function arcPricingInputsChanged(before: ArcLinePricingState, update: ArcLinePricingUpdate): boolean {
|
|
148
|
+
if (canonical(stripArcMarkers(before.configuration)) !== canonical(stripArcMarkers(update.configuration))) {
|
|
149
|
+
return true
|
|
150
|
+
}
|
|
151
|
+
if (update.quantity !== undefined && update.quantity !== before.quantity) return true
|
|
152
|
+
if (update.termMonths !== undefined && update.termMonths !== (before.termMonths ?? undefined)) return true
|
|
153
|
+
if (update.startDate !== undefined && isoDay(update.startDate) !== isoDay(before.startDate)) return true
|
|
154
|
+
if (update.endDate !== undefined && isoDay(update.endDate) !== isoDay(before.endDate)) return true
|
|
155
|
+
if (update.usageEstimates !== undefined) {
|
|
156
|
+
const normalize = (list: Array<{ chargeCode: string; estimatedQuantity: number }> | null | undefined) =>
|
|
157
|
+
canonical(
|
|
158
|
+
[...(list ?? [])]
|
|
159
|
+
.map((e) => ({ chargeCode: e.chargeCode, estimatedQuantity: e.estimatedQuantity }))
|
|
160
|
+
.sort((a, b) => a.chargeCode.localeCompare(b.chargeCode)),
|
|
161
|
+
)
|
|
162
|
+
if (normalize(update.usageEstimates) !== normalize(before.usageEstimates)) return true
|
|
163
|
+
}
|
|
164
|
+
return false
|
|
165
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { storedAdjustmentFailure } from './priceAdjustments/storedAdjustmentFailure'
|
|
2
|
+
import { withQuotePricingWrite } from './priceAdjustments/quotePricingWrite'
|
|
1
3
|
import type { EntityManager } from '@mikro-orm/postgresql'
|
|
2
4
|
import { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'
|
|
3
5
|
import type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'
|
|
@@ -25,6 +27,8 @@ import {
|
|
|
25
27
|
type TenantScope,
|
|
26
28
|
} from './types'
|
|
27
29
|
import { emitCpqEvent } from '../events'
|
|
30
|
+
import { stripQuoteOnlyArcMarkers } from './arcPriceFreeze'
|
|
31
|
+
import { summarizeOrderPricing, type OrderWaterfallSummary } from './orderPricingSummary'
|
|
28
32
|
|
|
29
33
|
// ─── Types ──────────────────────────────────────────────────────
|
|
30
34
|
|
|
@@ -89,11 +93,12 @@ interface OrderResult {
|
|
|
89
93
|
* order detail page to render the AMEND / RENEW / CANCEL badge. */
|
|
90
94
|
quoteType: string
|
|
91
95
|
currencyCode: string
|
|
96
|
+
/** NRC / MRC totals plus the source quote's list / discount / deal-value figures. */
|
|
92
97
|
pricingSummary: {
|
|
93
98
|
nrcTotal: number
|
|
94
99
|
mrcTotal: number
|
|
95
100
|
currencyCode: string
|
|
96
|
-
}
|
|
101
|
+
} & OrderWaterfallSummary
|
|
97
102
|
activatedAt: string | null
|
|
98
103
|
createdAt: string
|
|
99
104
|
lines: OrderLineResult[]
|
|
@@ -129,29 +134,12 @@ export class DefaultCpqOrderService {
|
|
|
129
134
|
// ─── Convert Quote to Order ─────────────────────────────────
|
|
130
135
|
|
|
131
136
|
async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
id: quoteId,
|
|
136
|
-
organizationId: scope.organizationId,
|
|
137
|
-
tenantId: scope.tenantId,
|
|
138
|
-
deletedAt: null,
|
|
139
|
-
})
|
|
140
|
-
if (!cpqConfig) {
|
|
141
|
-
const byQuoteId = await em.findOne(CpqQuoteConfiguration, {
|
|
142
|
-
quoteId,
|
|
143
|
-
organizationId: scope.organizationId,
|
|
144
|
-
tenantId: scope.tenantId,
|
|
145
|
-
deletedAt: null,
|
|
146
|
-
})
|
|
147
|
-
if (!byQuoteId) throw new OrderError(404, 'Quote not found')
|
|
148
|
-
return this.convertFromConfig(byQuoteId, scope)
|
|
149
|
-
}
|
|
150
|
-
return this.convertFromConfig(cpqConfig, scope)
|
|
137
|
+
return withQuotePricingWrite(this.em, quoteId, scope,
|
|
138
|
+
(em, quote) => this.convertFromConfig(quote, scope, em),
|
|
139
|
+
(status, message) => new OrderError(status, message))
|
|
151
140
|
}
|
|
152
141
|
|
|
153
|
-
private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope): Promise<OrderResult> {
|
|
154
|
-
const em = this.em
|
|
142
|
+
private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope, em: EntityManager): Promise<OrderResult> {
|
|
155
143
|
|
|
156
144
|
const blockedStatuses = ['cancelled', 'rejected']
|
|
157
145
|
if (blockedStatuses.includes(cpqConfig.cpqStatus)) {
|
|
@@ -186,6 +174,9 @@ export class DefaultCpqOrderService {
|
|
|
186
174
|
throw new OrderError(422, 'Cannot convert a quote with no line items')
|
|
187
175
|
}
|
|
188
176
|
|
|
177
|
+
const invalidAdjustment = storedAdjustmentFailure(cpqConfig, quoteLines)
|
|
178
|
+
if (invalidAdjustment) throw new OrderError(422, 'ADJUSTMENT_INVALID', invalidAdjustment)
|
|
179
|
+
|
|
189
180
|
const { number: orderNumber } = await this.numberGenerator.generate({
|
|
190
181
|
kind: 'order',
|
|
191
182
|
organizationId: scope.organizationId,
|
|
@@ -309,7 +300,8 @@ export class DefaultCpqOrderService {
|
|
|
309
300
|
startDate: ql.startDate,
|
|
310
301
|
termMonths: ql.termMonths ?? null,
|
|
311
302
|
endDate: ql.endDate,
|
|
312
|
-
|
|
303
|
+
// Quote-only ARC pricing markers stay on the quote.
|
|
304
|
+
configuration: { ...stripQuoteOnlyArcMarkers(ql.configuration), offeringName },
|
|
313
305
|
quantity: ql.quantity,
|
|
314
306
|
charges: ql.charges ? [...ql.charges] : null,
|
|
315
307
|
nrcTotal: ql.nrcTotal,
|
|
@@ -641,7 +633,7 @@ export class DefaultCpqOrderService {
|
|
|
641
633
|
offeringId: line.offeringId ?? undefined,
|
|
642
634
|
specId: line.specId ?? undefined,
|
|
643
635
|
name: this.getLineName(line),
|
|
644
|
-
configuration: line.configuration ?? {},
|
|
636
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
645
637
|
charges: (line.charges ?? []) as Array<Record<string, unknown>>,
|
|
646
638
|
mrcAmount: Number(line.mrcTotal),
|
|
647
639
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -711,7 +703,7 @@ export class DefaultCpqOrderService {
|
|
|
711
703
|
purchasePrice: Number(line.nrcTotal),
|
|
712
704
|
quantity: line.quantity,
|
|
713
705
|
purchaseDate: new Date().toISOString().split('T')[0],
|
|
714
|
-
configuration: line.configuration ?? {},
|
|
706
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
715
707
|
charges: (line.charges ?? []) as Array<Record<string, unknown>>,
|
|
716
708
|
sourceOrderId: orderConfig.id,
|
|
717
709
|
sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,
|
|
@@ -976,7 +968,7 @@ export class DefaultCpqOrderService {
|
|
|
976
968
|
action: lc.action,
|
|
977
969
|
parentLineId: lc.parentLineId ?? null,
|
|
978
970
|
quantity: lc.quantity,
|
|
979
|
-
configuration: lc.configuration ?? {},
|
|
971
|
+
configuration: stripQuoteOnlyArcMarkers(lc.configuration) ?? {},
|
|
980
972
|
startDate: lc.startDate?.toISOString() ?? null,
|
|
981
973
|
termMonths: lc.termMonths ?? null,
|
|
982
974
|
endDate: lc.endDate?.toISOString() ?? null,
|
|
@@ -1018,6 +1010,7 @@ export class DefaultCpqOrderService {
|
|
|
1018
1010
|
nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),
|
|
1019
1011
|
mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),
|
|
1020
1012
|
currencyCode: orderConfig.currencyCode,
|
|
1013
|
+
...summarizeOrderPricing(lines, quoteType, orderConfig.currencyCode),
|
|
1021
1014
|
},
|
|
1022
1015
|
activatedAt: orderConfig.activatedAt?.toISOString() ?? null,
|
|
1023
1016
|
createdAt: orderConfig.createdAt.toISOString(),
|
|
@@ -1045,16 +1038,27 @@ function pickArcSourceFromConfig(
|
|
|
1045
1038
|
|
|
1046
1039
|
// ─── Error class ──────────────────────────────────────────────────
|
|
1047
1040
|
|
|
1041
|
+
// Cross-realm brand — see QUOTING_ERROR_BRAND in cpqQuotingService.ts for why
|
|
1042
|
+
// route handlers must not rely on `instanceof`. Use `isOrderError`.
|
|
1043
|
+
const ORDER_ERROR_BRAND = Symbol.for('@dainamite/cpq/OrderError')
|
|
1044
|
+
|
|
1048
1045
|
export class OrderError extends Error {
|
|
1046
|
+
readonly [ORDER_ERROR_BRAND] = true
|
|
1047
|
+
|
|
1049
1048
|
constructor(
|
|
1050
1049
|
public status: number,
|
|
1051
1050
|
message: string,
|
|
1051
|
+
public details?: Record<string, unknown>,
|
|
1052
1052
|
) {
|
|
1053
1053
|
super(message)
|
|
1054
1054
|
this.name = 'OrderError'
|
|
1055
1055
|
}
|
|
1056
1056
|
}
|
|
1057
1057
|
|
|
1058
|
+
export function isOrderError(err: unknown): err is OrderError {
|
|
1059
|
+
return !!err && typeof err === 'object' && (err as Record<symbol, unknown>)[ORDER_ERROR_BRAND] === true
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1058
1062
|
// ─── ARC helpers (XD-250) ─────────────────────────────────────────
|
|
1059
1063
|
|
|
1060
1064
|
/**
|
|
@@ -1072,7 +1076,7 @@ function buildArcLineChange(
|
|
|
1072
1076
|
offeringId: line.offeringId ?? undefined,
|
|
1073
1077
|
specId: line.specId ?? undefined,
|
|
1074
1078
|
name: resolveLineName(line, specMap),
|
|
1075
|
-
configuration: line.configuration ?? {},
|
|
1079
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
|
|
1076
1080
|
charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,
|
|
1077
1081
|
mrcAmount: Number(line.mrcTotal),
|
|
1078
1082
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -1099,7 +1103,7 @@ function buildArcLineChange(
|
|
|
1099
1103
|
action,
|
|
1100
1104
|
sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,
|
|
1101
1105
|
modifyPatch: {
|
|
1102
|
-
configuration: line.configuration ?? undefined,
|
|
1106
|
+
configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? undefined,
|
|
1103
1107
|
charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,
|
|
1104
1108
|
mrcAmount: Number(line.mrcTotal),
|
|
1105
1109
|
nrcAmount: Number(line.nrcTotal),
|
|
@@ -119,6 +119,8 @@ export class DefaultCpqPricingService {
|
|
|
119
119
|
if (currencyCode) {
|
|
120
120
|
resolved.currencyCode = currencyCode
|
|
121
121
|
}
|
|
122
|
+
resolved.listUnitPrice = resolved.unitPrice
|
|
123
|
+
resolved.listTotalPrice = resolved.totalPrice
|
|
122
124
|
resolved.adjustments = []
|
|
123
125
|
results.push(resolved)
|
|
124
126
|
}
|
|
@@ -133,6 +135,11 @@ export class DefaultCpqPricingService {
|
|
|
133
135
|
this.applyPriceRules(results, rules, configuration)
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
for (const resolved of results) {
|
|
139
|
+
resolved.standardUnitPrice = resolved.unitPrice
|
|
140
|
+
resolved.standardTotalPrice = resolved.totalPrice
|
|
141
|
+
}
|
|
142
|
+
|
|
136
143
|
return results
|
|
137
144
|
}
|
|
138
145
|
|
|
@@ -219,6 +226,7 @@ export class DefaultCpqPricingService {
|
|
|
219
226
|
}
|
|
220
227
|
|
|
221
228
|
const adjustment: RuleAdjustment = {
|
|
229
|
+
origin: 'rule',
|
|
222
230
|
ruleCode: rule.code,
|
|
223
231
|
ruleName: rule.name,
|
|
224
232
|
ruleType: rule.ruleType,
|