@dainamite/cpq 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/MIGRATION.md +74 -0
- package/dist/modules/cpq/acl.js +1 -0
- package/dist/modules/cpq/acl.js.map +2 -2
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/route.js +47 -2
- package/dist/modules/cpq/api/orders/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/price/route.js +43 -3
- package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +3 -3
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +174 -184
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +140 -280
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +208 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/ce.js +8 -0
- package/dist/modules/cpq/ce.js.map +2 -2
- package/dist/modules/cpq/cli.js +34 -1
- package/dist/modules/cpq/cli.js.map +2 -2
- package/dist/modules/cpq/commands/price-adjustments.js +107 -0
- package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
- package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
- package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
- package/dist/modules/cpq/data/entities.js +97 -0
- package/dist/modules/cpq/data/entities.js.map +2 -2
- package/dist/modules/cpq/data/price-adjustments.js +70 -0
- package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/data/validators.js +4 -0
- package/dist/modules/cpq/data/validators.js.map +2 -2
- package/dist/modules/cpq/di.js +6 -0
- package/dist/modules/cpq/di.js.map +2 -2
- package/dist/modules/cpq/events.js +47 -0
- package/dist/modules/cpq/events.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/index.js +1 -1
- package/dist/modules/cpq/index.js.map +2 -2
- package/dist/modules/cpq/lib/auditActor.js +10 -0
- package/dist/modules/cpq/lib/auditActor.js.map +7 -0
- package/dist/modules/cpq/lib/formatMoney.js +7 -0
- package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
- package/dist/modules/cpq/lib/quotePermissions.js +13 -0
- package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
- package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
- package/dist/modules/cpq/services/cpqOrderService.js +25 -28
- package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
- package/dist/modules/cpq/services/cpqPricingService.js +7 -0
- package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
- package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
- package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
- package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
- package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
- package/dist/modules/cpq/services/types.js.map +2 -2
- package/dist/modules/cpq/setup.js +6 -0
- package/dist/modules/cpq/setup.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/acl.ts +1 -0
- package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
- package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
- package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
- package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
- package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
- package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
- package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
- package/src/modules/cpq/api/orders/route.ts +51 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
- package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
- package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
- package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
- package/src/modules/cpq/api/quotes/price/route.ts +28 -2
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +3 -3
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +164 -120
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +122 -308
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/ce.ts +8 -0
- package/src/modules/cpq/cli.ts +48 -1
- package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
- package/src/modules/cpq/commands/price-adjustments.ts +90 -0
- package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
- package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
- package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
- package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
- package/src/modules/cpq/data/entities.ts +83 -0
- package/src/modules/cpq/data/price-adjustments.ts +67 -0
- package/src/modules/cpq/data/validators.ts +3 -0
- package/src/modules/cpq/di.ts +6 -0
- package/src/modules/cpq/events.ts +53 -2
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/index.ts +1 -1
- package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
- package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
- package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
- package/src/modules/cpq/lib/auditActor.ts +13 -0
- package/src/modules/cpq/lib/formatMoney.ts +4 -0
- package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
- package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
- package/src/modules/cpq/lib/quotePermissions.ts +22 -0
- package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
- package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
- package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
- package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
- package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
- package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
- package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
- package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
- package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
- package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
- package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
- package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
- package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
- package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
- package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
- package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
- package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
- package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
- package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
- package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
- package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
- package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
- package/src/modules/cpq/services/cpqOrderService.ts +22 -28
- package/src/modules/cpq/services/cpqPricingService.ts +8 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
- package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
- package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
- package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
- package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
- package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
- package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
- package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
- package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
- package/src/modules/cpq/services/types.ts +34 -1
- package/src/modules/cpq/setup.ts +7 -1
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/cpq/backend/cpq/quotes/%5Bid%5D/_components/PriceAdjustmentsPanel.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Tag } from '@open-mercato/ui/primitives/tag'\nimport { InjectionSpot } from '@open-mercato/ui/backend/injection/InjectionSpot'\nimport { ADJUSTMENT_ERROR_FALLBACKS } from '../../../../../lib/priceAdjustmentView'\nimport { ManualAdjustmentActions, useAccessReasonText, usePriceAdjustmentUi } from './priceAdjustmentContext'\n\nexport const PRICE_ADJUSTMENTS_SPOT_ID = 'data-table:cpq.quote_price_adjustments'\n\nexport type StoredAdjustment = {\n id: string\n level: 'charge' | 'line'\n quoteLineId: string\n chargeCode?: string | null\n chargeTypeFilter?: 'nrc' | 'mrc' | null\n adjustmentType: 'discount_percent' | 'discount_absolute' | 'price_override'\n value: number\n currencyCode?: string | null\n reasonCode: string\n reasonNote?: string | null\n createdByUserId: string\n createdAt: string\n}\n\ntype LineRef = { lineId: string; offeringName: string; offeringId: string | null; configuration?: Record<string, unknown> | null; charges: Array<{ chargeCode: string; chargeName: string }> }\n\nconst TYPE_FALLBACKS: Record<StoredAdjustment['adjustmentType'], string> = {\n price_override: 'Price override',\n discount_percent: '% discount',\n discount_absolute: 'Amount off',\n}\n\nfunction money(amount: number, currency: string) {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(amount)\n}\n\nexport function PriceAdjustmentsPanel({ quoteId, items, total, lines, currency, errorsById }: {\n quoteId: string\n items: StoredAdjustment[]\n total: number\n lines: LineRef[]\n currency: string\n /** adjustmentId \u2192 first error code reported by the latest pricing. */\n errorsById: Map<string, string>\n}) {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n const lineById = React.useMemo(() => new Map(lines.map((line) => [line.lineId, line])), [lines])\n const accessText = useAccessReasonText()\n // Toggle off: stored adjustments still price the quote, so keep them visible (read-only).\n if (!ui || (ui.access.reason === 'disabled' && items.length === 0)) return null\n\n return (\n <div className=\"rounded-lg border bg-card\" data-testid=\"cpq-price-adjustments-panel\">\n <div className=\"flex items-center justify-between border-b px-4 py-3\">\n <h3 className=\"text-sm font-medium\">\n {t('cpq.priceAdjustments.panel.title', 'Price adjustments')}\n <span className=\"ml-2 text-xs text-muted-foreground\">{total}</span>\n </h3>\n {accessText && ui.access.reason !== 'loading' && (\n <span className=\"text-xs text-muted-foreground\" data-testid=\"cpq-price-adjustments-locked\">{accessText}</span>\n )}\n </div>\n {items.length === 0 ? (\n <p className=\"px-4 py-3 text-xs text-muted-foreground\">\n {t('cpq.priceAdjustments.panel.empty', 'No negotiated prices yet. Use \u201CAdjust price\u201D on a charge or \u201CDiscount line\u201D on a line.')}\n </p>\n ) : (\n <table className=\"w-full text-xs\">\n <thead>\n <tr className=\"text-muted-foreground\">\n <th className=\"px-4 py-1.5 text-left font-medium\">{t('cpq.priceAdjustments.panel.target', 'Applies to')}</th>\n <th className=\"py-1.5 text-left font-medium\">{t('cpq.priceAdjustments.panel.type', 'Type')}</th>\n <th className=\"py-1.5 text-right font-medium\">{t('cpq.priceAdjustments.panel.value', 'Value')}</th>\n <th className=\"py-1.5 pl-4 text-left font-medium\">{t('cpq.priceAdjustments.panel.reason', 'Reason')}</th>\n <th className=\"py-1.5 text-left font-medium\">{t('cpq.priceAdjustments.panel.author', 'Author')}</th>\n <th className=\"py-1.5 text-left font-medium\">{t('cpq.priceAdjustments.panel.date', 'Date')}</th>\n <th className=\"w-16\" />\n </tr>\n </thead>\n <tbody>\n {items.map((item) => {\n const line = lineById.get(item.quoteLineId)\n const chargeName = item.chargeCode ? line?.charges.find((charge) => charge.chargeCode === item.chargeCode)?.chargeName ?? item.chargeCode : null\n const scope = item.level === 'line'\n ? item.chargeTypeFilter === 'mrc' ? t('cpq.priceAdjustments.panel.lineMrc', 'whole line, recurring')\n : item.chargeTypeFilter === 'nrc' ? t('cpq.priceAdjustments.panel.lineNrc', 'whole line, one-time')\n : t('cpq.priceAdjustments.panel.line', 'whole line')\n : chargeName\n const error = errorsById.get(item.id)\n return (\n <tr key={item.id} className=\"border-t\" data-testid=\"cpq-price-adjustment-row\">\n <td className=\"px-4 py-1.5\">\n <span className=\"font-medium\">{line?.offeringName ?? t('cpq.priceAdjustments.panel.removedLine', 'Removed line')}</span>\n <span className=\"text-muted-foreground\"> \u00B7 {scope}</span>\n {error && (\n <Tag variant=\"warning\" className=\"ml-1.5 px-1.5 text-xs\">\n {t('cpq.priceAdjustments.invalid', 'Invalid')}: {t(`cpq.priceAdjustments.errors.${error}`, ADJUSTMENT_ERROR_FALLBACKS[error] ?? error)}\n </Tag>\n )}\n </td>\n <td className=\"py-1.5\">{t(`cpq.priceAdjustments.panel.types.${item.adjustmentType}`, TYPE_FALLBACKS[item.adjustmentType])}</td>\n <td className=\"py-1.5 text-right font-mono\">\n {item.adjustmentType === 'discount_percent' ? `${item.value}%` : money(item.value, item.currencyCode ?? currency)}\n </td>\n <td className=\"py-1.5 pl-4\">\n {ui.reasonLabels[item.reasonCode] ?? item.reasonCode}\n {item.reasonNote && <span className=\"block text-muted-foreground\">{item.reasonNote}</span>}\n </td>\n <td className=\"py-1.5 font-mono text-muted-foreground\" title={item.createdByUserId}>{item.createdByUserId.slice(0, 8)}</td>\n <td className=\"py-1.5 text-muted-foreground\">{new Date(item.createdAt).toLocaleDateString()}</td>\n <td className=\"py-1.5 pr-2 text-right\"><ManualAdjustmentActions adjustmentId={item.id} /></td>\n </tr>\n )\n })}\n </tbody>\n </table>\n )}\n <InjectionSpot spotId={PRICE_ADJUSTMENTS_SPOT_ID} context={{ quoteId, adjustments: items }} />\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": ";AAyDQ,SAEE,KAFF;AAvDR,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,WAAW;AACpB,SAAS,qBAAqB;AAC9B,SAAS,kCAAkC;AAC3C,SAAS,yBAAyB,qBAAqB,4BAA4B;AAE5E,MAAM,4BAA4B;AAmBzC,MAAM,iBAAqE;AAAA,EACzE,gBAAgB;AAAA,EAChB,kBAAkB;AAAA,EAClB,mBAAmB;AACrB;AAEA,SAAS,MAAM,QAAgB,UAAkB;AAC/C,SAAO,IAAI,KAAK,aAAa,SAAS,EAAE,OAAO,YAAY,SAAS,CAAC,EAAE,OAAO,MAAM;AACtF;AAEO,SAAS,sBAAsB,EAAE,SAAS,OAAO,OAAO,OAAO,UAAU,WAAW,GAQxF;AACD,QAAM,IAAI,KAAK;AACf,QAAM,KAAK,qBAAqB;AAChC,QAAM,WAAW,MAAM,QAAQ,MAAM,IAAI,IAAI,MAAM,IAAI,CAAC,SAAS,CAAC,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,KAAK,CAAC;AAC/F,QAAM,aAAa,oBAAoB;AAEvC,MAAI,CAAC,MAAO,GAAG,OAAO,WAAW,cAAc,MAAM,WAAW,EAAI,QAAO;AAE3E,SACE,qBAAC,SAAI,WAAU,6BAA4B,eAAY,+BACrD;AAAA,yBAAC,SAAI,WAAU,wDACb;AAAA,2BAAC,QAAG,WAAU,uBACX;AAAA,UAAE,oCAAoC,mBAAmB;AAAA,QAC1D,oBAAC,UAAK,WAAU,sCAAsC,iBAAM;AAAA,SAC9D;AAAA,MACC,cAAc,GAAG,OAAO,WAAW,aAClC,oBAAC,UAAK,WAAU,iCAAgC,eAAY,gCAAgC,sBAAW;AAAA,OAE3G;AAAA,IACC,MAAM,WAAW,IAChB,oBAAC,OAAE,WAAU,2CACV,YAAE,oCAAoC,4GAAwF,GACjI,IAEA,qBAAC,WAAM,WAAU,kBACf;AAAA,0BAAC,WACC,+BAAC,QAAG,WAAU,yBACZ;AAAA,4BAAC,QAAG,WAAU,qCAAqC,YAAE,qCAAqC,YAAY,GAAE;AAAA,QACxG,oBAAC,QAAG,WAAU,gCAAgC,YAAE,mCAAmC,MAAM,GAAE;AAAA,QAC3F,oBAAC,QAAG,WAAU,iCAAiC,YAAE,oCAAoC,OAAO,GAAE;AAAA,QAC9F,oBAAC,QAAG,WAAU,qCAAqC,YAAE,qCAAqC,QAAQ,GAAE;AAAA,QACpG,oBAAC,QAAG,WAAU,gCAAgC,YAAE,qCAAqC,QAAQ,GAAE;AAAA,QAC/F,oBAAC,QAAG,WAAU,gCAAgC,YAAE,mCAAmC,MAAM,GAAE;AAAA,QAC3F,oBAAC,QAAG,WAAU,QAAO;AAAA,SACvB,GACF;AAAA,MACA,oBAAC,WACE,gBAAM,IAAI,CAAC,SAAS;AACnB,cAAM,OAAO,SAAS,IAAI,KAAK,WAAW;AAC1C,cAAM,aAAa,KAAK,aAAa,MAAM,QAAQ,KAAK,CAAC,WAAW,OAAO,eAAe,KAAK,UAAU,GAAG,cAAc,KAAK,aAAa;AAC5I,cAAM,QAAQ,KAAK,UAAU,SACzB,KAAK,qBAAqB,QAAQ,EAAE,sCAAsC,uBAAuB,IAC/F,KAAK,qBAAqB,QAAQ,EAAE,sCAAsC,sBAAsB,IAC9F,EAAE,mCAAmC,YAAY,IACrD;AACJ,cAAM,QAAQ,WAAW,IAAI,KAAK,EAAE;AACpC,eACE,qBAAC,QAAiB,WAAU,YAAW,eAAY,4BACjD;AAAA,+BAAC,QAAG,WAAU,eACZ;AAAA,gCAAC,UAAK,WAAU,eAAe,gBAAM,gBAAgB,EAAE,0CAA0C,cAAc,GAAE;AAAA,YACjH,qBAAC,UAAK,WAAU,yBAAwB;AAAA;AAAA,cAAI;AAAA,eAAM;AAAA,YACjD,SACC,qBAAC,OAAI,SAAQ,WAAU,WAAU,yBAC9B;AAAA,gBAAE,gCAAgC,SAAS;AAAA,cAAE;AAAA,cAAG,EAAE,+BAA+B,KAAK,IAAI,2BAA2B,KAAK,KAAK,KAAK;AAAA,eACvI;AAAA,aAEJ;AAAA,UACA,oBAAC,QAAG,WAAU,UAAU,YAAE,oCAAoC,KAAK,cAAc,IAAI,eAAe,KAAK,cAAc,CAAC,GAAE;AAAA,UAC1H,oBAAC,QAAG,WAAU,+BACX,eAAK,mBAAmB,qBAAqB,GAAG,KAAK,KAAK,MAAM,MAAM,KAAK,OAAO,KAAK,gBAAgB,QAAQ,GAClH;AAAA,UACA,qBAAC,QAAG,WAAU,eACX;AAAA,eAAG,aAAa,KAAK,UAAU,KAAK,KAAK;AAAA,YACzC,KAAK,cAAc,oBAAC,UAAK,WAAU,+BAA+B,eAAK,YAAW;AAAA,aACrF;AAAA,UACA,oBAAC,QAAG,WAAU,0CAAyC,OAAO,KAAK,iBAAkB,eAAK,gBAAgB,MAAM,GAAG,CAAC,GAAE;AAAA,UACtH,oBAAC,QAAG,WAAU,gCAAgC,cAAI,KAAK,KAAK,SAAS,EAAE,mBAAmB,GAAE;AAAA,UAC5F,oBAAC,QAAG,WAAU,0BAAyB,8BAAC,2BAAwB,cAAc,KAAK,IAAI,GAAE;AAAA,aApBlF,KAAK,EAqBd;AAAA,MAEJ,CAAC,GACH;AAAA,OACF;AAAA,IAEF,oBAAC,iBAAc,QAAQ,2BAA2B,SAAS,EAAE,SAAS,aAAa,MAAM,GAAG;AAAA,KAC9F;AAEJ;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import * as React from "react";
|
|
3
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
4
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
5
|
+
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
6
|
+
import { isArcCancelledLine, removalCancelsItem } from "../../../../../services/arcLineRemoval.js";
|
|
7
|
+
const ArcLineRemovalContext = React.createContext(null);
|
|
8
|
+
const ArcLineRemovalProvider = ArcLineRemovalContext.Provider;
|
|
9
|
+
function useArcLineRemovalUi() {
|
|
10
|
+
return React.useContext(ArcLineRemovalContext);
|
|
11
|
+
}
|
|
12
|
+
function useArcLineRemoval({ quote, quoteType, onQuote, onError }) {
|
|
13
|
+
const t = useT();
|
|
14
|
+
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
15
|
+
const send = React.useCallback(async (url, method) => {
|
|
16
|
+
onError(null);
|
|
17
|
+
const call = await apiCall(url, { method });
|
|
18
|
+
if (call.ok && call.result) onQuote(call.result);
|
|
19
|
+
else onError(call.result?.error ?? t("cpq.quotes.lineUpdateFailed", "Could not update the line"));
|
|
20
|
+
}, [onError, onQuote, t]);
|
|
21
|
+
const remove = React.useCallback(async (lineId) => {
|
|
22
|
+
if (!quote) return;
|
|
23
|
+
const line = quote.lines.find((candidate) => candidate.lineId === lineId);
|
|
24
|
+
if (line && removalCancelsItem(line, quoteType)) {
|
|
25
|
+
const ok = await confirm({
|
|
26
|
+
title: t("cpq.quotes.arc.removeConfirm.title", "Remove {name} from the subscription?").replace("{name}", line.offeringName),
|
|
27
|
+
text: t(
|
|
28
|
+
"cpq.quotes.arc.removeConfirm.text",
|
|
29
|
+
'This item already exists on the subscription. Removing it here does not delete it from the quote \u2014 it stays as a "remove" line, and when the order is activated the item (and its components) is cancelled on the subscription and its recurring charge stops. You can restore it until then.'
|
|
30
|
+
),
|
|
31
|
+
confirmText: t("cpq.quotes.arc.removeConfirm.confirm", "Remove item"),
|
|
32
|
+
variant: "destructive"
|
|
33
|
+
});
|
|
34
|
+
if (!ok) return;
|
|
35
|
+
}
|
|
36
|
+
await send(`/api/cpq/quotes/${quote.id}/items/${lineId}`, "DELETE");
|
|
37
|
+
}, [confirm, quote, quoteType, send, t]);
|
|
38
|
+
const restore = React.useCallback((lineId) => {
|
|
39
|
+
if (!quote) return;
|
|
40
|
+
void send(`/api/cpq/quotes/${quote.id}/items/${lineId}/restore`, "POST");
|
|
41
|
+
}, [quote, send]);
|
|
42
|
+
const ui = React.useMemo(() => ({
|
|
43
|
+
isRemoved: (line) => isArcCancelledLine(line, quoteType),
|
|
44
|
+
restore
|
|
45
|
+
}), [quoteType, restore]);
|
|
46
|
+
return { remove, ui, ConfirmDialogElement };
|
|
47
|
+
}
|
|
48
|
+
export {
|
|
49
|
+
ArcLineRemovalProvider,
|
|
50
|
+
useArcLineRemoval,
|
|
51
|
+
useArcLineRemovalUi
|
|
52
|
+
};
|
|
53
|
+
//# sourceMappingURL=arcLineRemoval.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/cpq/backend/cpq/quotes/%5Bid%5D/_components/arcLineRemoval.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { isArcCancelledLine, removalCancelsItem, type ArcRemovalLine } from '../../../../../services/arcLineRemoval'\n\ntype RemovableLine = Pick<ArcRemovalLine, 'action' | 'configuration'> & { lineId: string; offeringName: string }\n\nexport type ArcLineRemovalUi = {\n /** The line is an existing subscription item marked for removal. */\n isRemoved: (line: Pick<RemovableLine, 'action'>) => boolean\n restore: (lineId: string) => void\n}\n\nconst ArcLineRemovalContext = React.createContext<ArcLineRemovalUi | null>(null)\n\nexport const ArcLineRemovalProvider = ArcLineRemovalContext.Provider\n\nexport function useArcLineRemovalUi(): ArcLineRemovalUi | null {\n return React.useContext(ArcLineRemovalContext)\n}\n\n/**\n * Line removal on the quote page. On an Amend / Renew quote, removing a line\n * that mirrors a live subscription item asks for confirmation and marks the\n * item for removal (server turns it into a `cancel` line); it can be restored.\n * Other lines are deleted as before.\n */\nexport function useArcLineRemoval<Q extends { id: string; lines: RemovableLine[] }>({ quote, quoteType, onQuote, onError }: {\n quote: Q | null\n quoteType: string\n onQuote: (quote: Q) => void\n onError: (message: string | null) => void\n}) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n\n const send = React.useCallback(async (url: string, method: 'DELETE' | 'POST') => {\n onError(null)\n const call = await apiCall<Q & { error?: string }>(url, { method })\n if (call.ok && call.result) onQuote(call.result)\n else onError(call.result?.error ?? t('cpq.quotes.lineUpdateFailed', 'Could not update the line'))\n }, [onError, onQuote, t])\n\n const remove = React.useCallback(async (lineId: string) => {\n if (!quote) return\n const line = quote.lines.find((candidate) => candidate.lineId === lineId)\n if (line && removalCancelsItem(line, quoteType)) {\n const ok = await confirm({\n title: t('cpq.quotes.arc.removeConfirm.title', 'Remove {name} from the subscription?').replace('{name}', line.offeringName),\n text: t(\n 'cpq.quotes.arc.removeConfirm.text',\n 'This item already exists on the subscription. Removing it here does not delete it from the quote \u2014 it stays as a \"remove\" line, and when the order is activated the item (and its components) is cancelled on the subscription and its recurring charge stops. You can restore it until then.',\n ),\n confirmText: t('cpq.quotes.arc.removeConfirm.confirm', 'Remove item'),\n variant: 'destructive',\n })\n if (!ok) return\n }\n await send(`/api/cpq/quotes/${quote.id}/items/${lineId}`, 'DELETE')\n }, [confirm, quote, quoteType, send, t])\n\n const restore = React.useCallback((lineId: string) => {\n if (!quote) return\n void send(`/api/cpq/quotes/${quote.id}/items/${lineId}/restore`, 'POST')\n }, [quote, send])\n\n const ui = React.useMemo<ArcLineRemovalUi>(() => ({\n isRemoved: (line) => isArcCancelledLine(line, quoteType),\n restore,\n }), [quoteType, restore])\n\n return { remove, ui, ConfirmDialogElement }\n}\n"],
|
|
5
|
+
"mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,YAAY;AACrB,SAAS,eAAe;AACxB,SAAS,wBAAwB;AACjC,SAAS,oBAAoB,0BAA+C;AAU5E,MAAM,wBAAwB,MAAM,cAAuC,IAAI;AAExE,MAAM,yBAAyB,sBAAsB;AAErD,SAAS,sBAA+C;AAC7D,SAAO,MAAM,WAAW,qBAAqB;AAC/C;AAQO,SAAS,kBAAoE,EAAE,OAAO,WAAW,SAAS,QAAQ,GAKtH;AACD,QAAM,IAAI,KAAK;AACf,QAAM,EAAE,SAAS,qBAAqB,IAAI,iBAAiB;AAE3D,QAAM,OAAO,MAAM,YAAY,OAAO,KAAa,WAA8B;AAC/E,YAAQ,IAAI;AACZ,UAAM,OAAO,MAAM,QAAgC,KAAK,EAAE,OAAO,CAAC;AAClE,QAAI,KAAK,MAAM,KAAK,OAAQ,SAAQ,KAAK,MAAM;AAAA,QAC1C,SAAQ,KAAK,QAAQ,SAAS,EAAE,+BAA+B,2BAA2B,CAAC;AAAA,EAClG,GAAG,CAAC,SAAS,SAAS,CAAC,CAAC;AAExB,QAAM,SAAS,MAAM,YAAY,OAAO,WAAmB;AACzD,QAAI,CAAC,MAAO;AACZ,UAAM,OAAO,MAAM,MAAM,KAAK,CAAC,cAAc,UAAU,WAAW,MAAM;AACxE,QAAI,QAAQ,mBAAmB,MAAM,SAAS,GAAG;AAC/C,YAAM,KAAK,MAAM,QAAQ;AAAA,QACvB,OAAO,EAAE,sCAAsC,sCAAsC,EAAE,QAAQ,UAAU,KAAK,YAAY;AAAA,QAC1H,MAAM;AAAA,UACJ;AAAA,UACA;AAAA,QACF;AAAA,QACA,aAAa,EAAE,wCAAwC,aAAa;AAAA,QACpE,SAAS;AAAA,MACX,CAAC;AACD,UAAI,CAAC,GAAI;AAAA,IACX;AACA,UAAM,KAAK,mBAAmB,MAAM,EAAE,UAAU,MAAM,IAAI,QAAQ;AAAA,EACpE,GAAG,CAAC,SAAS,OAAO,WAAW,MAAM,CAAC,CAAC;AAEvC,QAAM,UAAU,MAAM,YAAY,CAAC,WAAmB;AACpD,QAAI,CAAC,MAAO;AACZ,SAAK,KAAK,mBAAmB,MAAM,EAAE,UAAU,MAAM,YAAY,MAAM;AAAA,EACzE,GAAG,CAAC,OAAO,IAAI,CAAC;AAEhB,QAAM,KAAK,MAAM,QAA0B,OAAO;AAAA,IAChD,WAAW,CAAC,SAAS,mBAAmB,MAAM,SAAS;AAAA,IACvD;AAAA,EACF,IAAI,CAAC,WAAW,OAAO,CAAC;AAExB,SAAO,EAAE,QAAQ,IAAI,qBAAqB;AAC5C;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
|
+
import { Percent, Pencil, Trash2 } from "lucide-react";
|
|
6
|
+
import { IconButton } from "@open-mercato/ui/primitives/icon-button";
|
|
7
|
+
import { SimpleTooltip } from "@open-mercato/ui/primitives/tooltip";
|
|
8
|
+
import { lineAdjustmentBlock } from "../../../../../lib/priceAdjustmentView.js";
|
|
9
|
+
const Context = React.createContext(null);
|
|
10
|
+
const PriceAdjustmentProvider = Context.Provider;
|
|
11
|
+
const usePriceAdjustmentUi = () => React.useContext(Context);
|
|
12
|
+
function useAccessReasonText() {
|
|
13
|
+
const t = useT();
|
|
14
|
+
const ui = usePriceAdjustmentUi();
|
|
15
|
+
if (!ui) return null;
|
|
16
|
+
switch (ui.access.reason) {
|
|
17
|
+
case null:
|
|
18
|
+
return null;
|
|
19
|
+
case "locked":
|
|
20
|
+
return t("cpq.priceAdjustments.blocked.locked", "Adjustments are locked in status {status} \u2014 move the quote back to Ready to change them").replace("{status}", ui.cpqStatus);
|
|
21
|
+
case "converted":
|
|
22
|
+
return t("cpq.priceAdjustments.blocked.converted", "This quote has been converted to an order");
|
|
23
|
+
case "no_permission":
|
|
24
|
+
return t("cpq.priceAdjustments.blocked.permission", "You do not have permission to adjust prices");
|
|
25
|
+
case "disabled":
|
|
26
|
+
return t("cpq.priceAdjustments.blocked.disabled", "Manual price adjustments are turned off");
|
|
27
|
+
default:
|
|
28
|
+
return t("cpq.priceAdjustments.blocked.loading", "Loading\u2026");
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function useAdjustmentBlockReason(line) {
|
|
32
|
+
const t = useT();
|
|
33
|
+
const ui = usePriceAdjustmentUi();
|
|
34
|
+
const accessText = useAccessReasonText();
|
|
35
|
+
if (!ui) return null;
|
|
36
|
+
const block = lineAdjustmentBlock(line, ui.quoteType);
|
|
37
|
+
if (block === "mirror") return t("cpq.priceAdjustments.blocked.mirror", "Price changes on existing subscription items arrive with a later release");
|
|
38
|
+
if (block === "free_form") return t("cpq.priceAdjustments.blocked.freeForm", "Free-form lines cannot be adjusted");
|
|
39
|
+
return accessText;
|
|
40
|
+
}
|
|
41
|
+
function BlockedReason({ reason, children }) {
|
|
42
|
+
if (!reason) return /* @__PURE__ */ jsx(Fragment, { children });
|
|
43
|
+
return /* @__PURE__ */ jsx(SimpleTooltip, { content: reason, size: "sm", children: /* @__PURE__ */ jsx("span", { className: "inline-flex cursor-not-allowed", tabIndex: 0, "aria-label": reason, children }) });
|
|
44
|
+
}
|
|
45
|
+
function AdjustChargeAction({ line, charge }) {
|
|
46
|
+
const t = useT();
|
|
47
|
+
const ui = usePriceAdjustmentUi();
|
|
48
|
+
const reason = useAdjustmentBlockReason(line);
|
|
49
|
+
if (!ui || charge.chargeType === "usage") return null;
|
|
50
|
+
if (ui.access.reason === "disabled" || ui.access.reason === "no_permission" || ui.access.reason === "loading") return null;
|
|
51
|
+
const label = t("cpq.priceAdjustments.adjustPrice", "Adjust price");
|
|
52
|
+
return /* @__PURE__ */ jsx(BlockedReason, { reason, children: /* @__PURE__ */ jsx(
|
|
53
|
+
IconButton,
|
|
54
|
+
{
|
|
55
|
+
variant: "ghost",
|
|
56
|
+
size: "xs",
|
|
57
|
+
"aria-label": label,
|
|
58
|
+
title: reason ? void 0 : label,
|
|
59
|
+
disabled: !!reason,
|
|
60
|
+
onClick: () => ui.openCharge(line, charge),
|
|
61
|
+
"data-testid": "cpq-adjust-charge",
|
|
62
|
+
children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" })
|
|
63
|
+
}
|
|
64
|
+
) });
|
|
65
|
+
}
|
|
66
|
+
function DiscountLineAction({ line }) {
|
|
67
|
+
const t = useT();
|
|
68
|
+
const ui = usePriceAdjustmentUi();
|
|
69
|
+
const reason = useAdjustmentBlockReason(line);
|
|
70
|
+
if (!ui || ui.access.reason === "disabled" || ui.access.reason === "no_permission" || ui.access.reason === "loading") return null;
|
|
71
|
+
const label = t("cpq.priceAdjustments.discountLine", "Discount line");
|
|
72
|
+
return /* @__PURE__ */ jsx(BlockedReason, { reason, children: /* @__PURE__ */ jsx(
|
|
73
|
+
IconButton,
|
|
74
|
+
{
|
|
75
|
+
variant: "ghost",
|
|
76
|
+
size: "sm",
|
|
77
|
+
"aria-label": label,
|
|
78
|
+
title: reason ? void 0 : label,
|
|
79
|
+
disabled: !!reason,
|
|
80
|
+
onClick: () => ui.openLine(line),
|
|
81
|
+
"data-testid": "cpq-discount-line",
|
|
82
|
+
children: /* @__PURE__ */ jsx(Percent, { className: "h-4 w-4" })
|
|
83
|
+
}
|
|
84
|
+
) });
|
|
85
|
+
}
|
|
86
|
+
function ManualAdjustmentActions({ adjustmentId }) {
|
|
87
|
+
const t = useT();
|
|
88
|
+
const ui = usePriceAdjustmentUi();
|
|
89
|
+
if (!ui) return null;
|
|
90
|
+
const editable = ui.access.editable;
|
|
91
|
+
const deletable = editable || ui.canDeleteInvalid && ui.invalidIds.has(adjustmentId);
|
|
92
|
+
if (!editable && !deletable) return null;
|
|
93
|
+
return /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-0.5", children: [
|
|
94
|
+
editable && /* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "xs", "aria-label": t("cpq.priceAdjustments.edit", "Edit adjustment"), onClick: () => ui.edit(adjustmentId), children: /* @__PURE__ */ jsx(Pencil, { className: "h-3.5 w-3.5" }) }),
|
|
95
|
+
/* @__PURE__ */ jsx(IconButton, { variant: "ghost", size: "xs", "aria-label": t("cpq.priceAdjustments.delete", "Delete adjustment"), onClick: () => ui.remove(adjustmentId), children: /* @__PURE__ */ jsx(Trash2, { className: "h-3.5 w-3.5" }) })
|
|
96
|
+
] });
|
|
97
|
+
}
|
|
98
|
+
export {
|
|
99
|
+
AdjustChargeAction,
|
|
100
|
+
DiscountLineAction,
|
|
101
|
+
ManualAdjustmentActions,
|
|
102
|
+
PriceAdjustmentProvider,
|
|
103
|
+
useAccessReasonText,
|
|
104
|
+
useAdjustmentBlockReason,
|
|
105
|
+
usePriceAdjustmentUi
|
|
106
|
+
};
|
|
107
|
+
//# sourceMappingURL=priceAdjustmentContext.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/cpq/backend/cpq/quotes/%5Bid%5D/_components/priceAdjustmentContext.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { Percent, Pencil, Trash2 } from 'lucide-react'\nimport { IconButton } from '@open-mercato/ui/primitives/icon-button'\nimport { SimpleTooltip } from '@open-mercato/ui/primitives/tooltip'\nimport type { AdjustmentAccess, LineEligibilityInput, WaterfallChargeInput } from '../../../../../lib/priceAdjustmentView'\nimport { lineAdjustmentBlock } from '../../../../../lib/priceAdjustmentView'\n\nexport type AdjustableLine = LineEligibilityInput & { lineId: string; offeringName: string }\nexport type AdjustableCharge = WaterfallChargeInput & { chargeCode: string; chargeName: string }\n\nexport type PriceAdjustmentUi = {\n access: AdjustmentAccess\n quoteType: string\n cpqStatus: string\n reasonLabels: Record<string, string>\n /** Ids of adjustments currently flagged invalid (they keep Delete while locked). */\n invalidIds: Set<string>\n canDeleteInvalid: boolean\n openCharge: (line: AdjustableLine, charge: AdjustableCharge) => void\n openLine: (line: AdjustableLine) => void\n edit: (adjustmentId: string) => void\n remove: (adjustmentId: string) => void\n}\n\nconst Context = React.createContext<PriceAdjustmentUi | null>(null)\nexport const PriceAdjustmentProvider = Context.Provider\nexport const usePriceAdjustmentUi = () => React.useContext(Context)\n\n/** Quote-wide explanation for why adjustments are unavailable; null when editable. */\nexport function useAccessReasonText(): string | null {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n if (!ui) return null\n switch (ui.access.reason) {\n case null: return null\n case 'locked':\n return t('cpq.priceAdjustments.blocked.locked', 'Adjustments are locked in status {status} \u2014 move the quote back to Ready to change them')\n .replace('{status}', ui.cpqStatus)\n case 'converted': return t('cpq.priceAdjustments.blocked.converted', 'This quote has been converted to an order')\n case 'no_permission': return t('cpq.priceAdjustments.blocked.permission', 'You do not have permission to adjust prices')\n case 'disabled': return t('cpq.priceAdjustments.blocked.disabled', 'Manual price adjustments are turned off')\n default: return t('cpq.priceAdjustments.blocked.loading', 'Loading\u2026')\n }\n}\n\n/** Disabled-state explanation for one line; null when the action is available. */\nexport function useAdjustmentBlockReason(line: AdjustableLine): string | null {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n const accessText = useAccessReasonText()\n if (!ui) return null\n const block = lineAdjustmentBlock(line, ui.quoteType)\n if (block === 'mirror') return t('cpq.priceAdjustments.blocked.mirror', 'Price changes on existing subscription items arrive with a later release')\n if (block === 'free_form') return t('cpq.priceAdjustments.blocked.freeForm', 'Free-form lines cannot be adjusted')\n return accessText\n}\n\n/**\n * Disabled buttons swallow no pointer events, so a native `title` never shows.\n * Wrap a blocked action in a focusable span that carries the explanation.\n */\nfunction BlockedReason({ reason, children }: { reason: string | null; children: React.ReactNode }) {\n if (!reason) return <>{children}</>\n return (\n <SimpleTooltip content={reason} size=\"sm\">\n <span className=\"inline-flex cursor-not-allowed\" tabIndex={0} aria-label={reason}>{children}</span>\n </SimpleTooltip>\n )\n}\n\n/** Per-charge \"Adjust price\" action. Hidden entirely when the feature is off or not permitted. */\nexport function AdjustChargeAction({ line, charge }: { line: AdjustableLine; charge: AdjustableCharge }) {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n const reason = useAdjustmentBlockReason(line)\n if (!ui || charge.chargeType === 'usage') return null\n if (ui.access.reason === 'disabled' || ui.access.reason === 'no_permission' || ui.access.reason === 'loading') return null\n const label = t('cpq.priceAdjustments.adjustPrice', 'Adjust price')\n return (\n <BlockedReason reason={reason}>\n <IconButton\n variant=\"ghost\" size=\"xs\" aria-label={label} title={reason ? undefined : label} disabled={!!reason}\n onClick={() => ui.openCharge(line, charge)} data-testid=\"cpq-adjust-charge\"\n >\n <Pencil className=\"h-3.5 w-3.5\" />\n </IconButton>\n </BlockedReason>\n )\n}\n\n/** Line-level \"Discount line\" action. */\nexport function DiscountLineAction({ line }: { line: AdjustableLine }) {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n const reason = useAdjustmentBlockReason(line)\n if (!ui || ui.access.reason === 'disabled' || ui.access.reason === 'no_permission' || ui.access.reason === 'loading') return null\n const label = t('cpq.priceAdjustments.discountLine', 'Discount line')\n return (\n <BlockedReason reason={reason}>\n <IconButton\n variant=\"ghost\" size=\"sm\" aria-label={label} title={reason ? undefined : label} disabled={!!reason}\n onClick={() => ui.openLine(line)} data-testid=\"cpq-discount-line\"\n >\n <Percent className=\"h-4 w-4\" />\n </IconButton>\n </BlockedReason>\n )\n}\n\n/** Edit / delete actions on a manual waterfall row or panel row. */\nexport function ManualAdjustmentActions({ adjustmentId }: { adjustmentId: string }) {\n const t = useT()\n const ui = usePriceAdjustmentUi()\n if (!ui) return null\n const editable = ui.access.editable\n const deletable = editable || (ui.canDeleteInvalid && ui.invalidIds.has(adjustmentId))\n if (!editable && !deletable) return null\n return (\n <span className=\"inline-flex items-center gap-0.5\">\n {editable && (\n <IconButton variant=\"ghost\" size=\"xs\" aria-label={t('cpq.priceAdjustments.edit', 'Edit adjustment')} onClick={() => ui.edit(adjustmentId)}>\n <Pencil className=\"h-3.5 w-3.5\" />\n </IconButton>\n )}\n <IconButton variant=\"ghost\" size=\"xs\" aria-label={t('cpq.priceAdjustments.delete', 'Delete adjustment')} onClick={() => ui.remove(adjustmentId)}>\n <Trash2 className=\"h-3.5 w-3.5\" />\n </IconButton>\n </span>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { useT } from "@open-mercato/shared/lib/i18n/context";
|
|
5
|
+
import { apiCall } from "@open-mercato/ui/backend/utils/apiCall";
|
|
6
|
+
import { useConfirmDialog } from "@open-mercato/ui/backend/confirm-dialog";
|
|
7
|
+
import { flash } from "@open-mercato/ui/backend/FlashMessages";
|
|
8
|
+
import {
|
|
9
|
+
ADJUSTMENT_ERROR_FALLBACKS,
|
|
10
|
+
adjustmentAccess,
|
|
11
|
+
adjustmentErrorCode,
|
|
12
|
+
canDeleteInvalidAdjustment,
|
|
13
|
+
collectAdjustmentErrors
|
|
14
|
+
} from "../../../../../lib/priceAdjustmentView.js";
|
|
15
|
+
import { PriceAdjustmentDialog } from "./PriceAdjustmentDialog.js";
|
|
16
|
+
function usePriceAdjustments({ quote, quoteType, canAdjust, onQuote, reloadQuote }) {
|
|
17
|
+
const t = useT();
|
|
18
|
+
const { confirm, ConfirmDialogElement } = useConfirmDialog();
|
|
19
|
+
const [list, setList] = React.useState(null);
|
|
20
|
+
const [dialog, setDialog] = React.useState(null);
|
|
21
|
+
const quoteId = quote?.id ?? null;
|
|
22
|
+
const revision = quote?.pricingRevision ?? 0;
|
|
23
|
+
const loadList = React.useCallback(async () => {
|
|
24
|
+
if (!quoteId) return;
|
|
25
|
+
const call = await apiCall(`/api/cpq/quotes/${quoteId}/price-adjustments?pageSize=100`);
|
|
26
|
+
if (call.ok && call.result) setList(call.result);
|
|
27
|
+
else setList({ items: [], total: 0, pricingRevision: 0, enabled: false });
|
|
28
|
+
}, [quoteId]);
|
|
29
|
+
React.useEffect(() => {
|
|
30
|
+
void loadList();
|
|
31
|
+
}, [loadList, revision]);
|
|
32
|
+
const reasons = React.useMemo(() => list?.reasons ?? [], [list]);
|
|
33
|
+
const reasonLabels = React.useMemo(() => Object.fromEntries(reasons.map((reason) => [reason.value, reason.label])), [reasons]);
|
|
34
|
+
const errorsById = React.useMemo(() => collectAdjustmentErrors(quote), [quote]);
|
|
35
|
+
const accessInput = {
|
|
36
|
+
enabled: list ? list.enabled : null,
|
|
37
|
+
canAdjust,
|
|
38
|
+
cpqStatus: quote?.cpqStatus ?? "new",
|
|
39
|
+
convertedOrderId: quote?.convertedOrderId ?? null
|
|
40
|
+
};
|
|
41
|
+
const access = adjustmentAccess(accessInput);
|
|
42
|
+
const remove = React.useCallback(async (adjustmentId) => {
|
|
43
|
+
if (!quoteId) return;
|
|
44
|
+
const ok = await confirm({
|
|
45
|
+
title: t("cpq.priceAdjustments.deleteConfirm.title", "Remove this price adjustment?"),
|
|
46
|
+
text: t("cpq.priceAdjustments.deleteConfirm.text", "The charge goes back to its standard price."),
|
|
47
|
+
confirmText: t("cpq.priceAdjustments.deleteConfirm.confirm", "Remove"),
|
|
48
|
+
variant: "destructive"
|
|
49
|
+
});
|
|
50
|
+
if (!ok) return;
|
|
51
|
+
const call = await apiCall(`/api/cpq/quotes/${quoteId}/price-adjustments/${adjustmentId}?expectedRevision=${revision}`, { method: "DELETE" });
|
|
52
|
+
if (call.ok && call.result?.quote) {
|
|
53
|
+
onQuote(call.result.quote);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
const code = adjustmentErrorCode(call.status, call.result);
|
|
57
|
+
flash(t(`cpq.priceAdjustments.errors.${code}`, ADJUSTMENT_ERROR_FALLBACKS[code] ?? code), "error");
|
|
58
|
+
if (code === "STALE_REVISION") await reloadQuote();
|
|
59
|
+
}, [quoteId, revision, confirm, t, onQuote, reloadQuote]);
|
|
60
|
+
const ui = {
|
|
61
|
+
access,
|
|
62
|
+
quoteType,
|
|
63
|
+
cpqStatus: accessInput.cpqStatus,
|
|
64
|
+
reasonLabels,
|
|
65
|
+
invalidIds: new Set(errorsById.keys()),
|
|
66
|
+
canDeleteInvalid: canDeleteInvalidAdjustment(accessInput),
|
|
67
|
+
openCharge: (line, charge) => {
|
|
68
|
+
const existing = list?.items.find((item) => item.level === "charge" && item.quoteLineId === line.lineId && item.chargeCode === charge.chargeCode);
|
|
69
|
+
setDialog({
|
|
70
|
+
target: { level: "charge", quoteLineId: line.lineId, chargeCode: charge.chargeCode },
|
|
71
|
+
targetLabel: `${line.offeringName} \xB7 ${charge.chargeName}`,
|
|
72
|
+
charge,
|
|
73
|
+
existing: existing ?? null
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
openLine: (line) => {
|
|
77
|
+
const existing = list?.items.find((item) => item.level === "line" && item.quoteLineId === line.lineId);
|
|
78
|
+
setDialog({ target: { level: "line", quoteLineId: line.lineId }, targetLabel: line.offeringName, existing: existing ?? null });
|
|
79
|
+
},
|
|
80
|
+
edit: (adjustmentId) => {
|
|
81
|
+
const item = list?.items.find((entry) => entry.id === adjustmentId);
|
|
82
|
+
const line = quote?.lines.find((entry) => entry.lineId === item?.quoteLineId);
|
|
83
|
+
if (!item || !line) return;
|
|
84
|
+
const charge = item.chargeCode ? line.charges.find((entry) => entry.chargeCode === item.chargeCode) : void 0;
|
|
85
|
+
setDialog({
|
|
86
|
+
target: item.level === "charge" && item.chargeCode ? { level: "charge", quoteLineId: item.quoteLineId, chargeCode: item.chargeCode } : { level: "line", quoteLineId: item.quoteLineId },
|
|
87
|
+
targetLabel: charge ? `${line.offeringName} \xB7 ${charge.chargeName}` : line.offeringName,
|
|
88
|
+
charge,
|
|
89
|
+
existing: item
|
|
90
|
+
});
|
|
91
|
+
},
|
|
92
|
+
remove: (adjustmentId) => {
|
|
93
|
+
void remove(adjustmentId);
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
const dialogElement = quote && dialog ? /* @__PURE__ */ jsx(
|
|
97
|
+
PriceAdjustmentDialog,
|
|
98
|
+
{
|
|
99
|
+
open: true,
|
|
100
|
+
quoteId: quote.id,
|
|
101
|
+
currency: quote.currencyCode,
|
|
102
|
+
pricingRevision: revision,
|
|
103
|
+
target: dialog.target,
|
|
104
|
+
targetLabel: dialog.targetLabel,
|
|
105
|
+
charge: dialog.charge,
|
|
106
|
+
existing: dialog.existing,
|
|
107
|
+
storedAdjustmentIds: list?.items.map((item) => item.id) ?? [],
|
|
108
|
+
reasons,
|
|
109
|
+
onClose: () => setDialog(null),
|
|
110
|
+
onSaved: (next) => onQuote(next),
|
|
111
|
+
onStale: () => {
|
|
112
|
+
void reloadQuote();
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
) : null;
|
|
116
|
+
return {
|
|
117
|
+
ui,
|
|
118
|
+
items: list?.items ?? [],
|
|
119
|
+
total: list?.total ?? 0,
|
|
120
|
+
errorsById,
|
|
121
|
+
elements: /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
122
|
+
dialogElement,
|
|
123
|
+
ConfirmDialogElement
|
|
124
|
+
] })
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
export {
|
|
128
|
+
usePriceAdjustments
|
|
129
|
+
};
|
|
130
|
+
//# sourceMappingURL=usePriceAdjustments.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../../../../src/modules/cpq/backend/cpq/quotes/%5Bid%5D/_components/usePriceAdjustments.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { useConfirmDialog } from '@open-mercato/ui/backend/confirm-dialog'\nimport { flash } from '@open-mercato/ui/backend/FlashMessages'\nimport {\n ADJUSTMENT_ERROR_FALLBACKS,\n adjustmentAccess,\n adjustmentErrorCode,\n canDeleteInvalidAdjustment,\n collectAdjustmentErrors,\n type AdjustmentErrorSourceQuote,\n type AdjustmentTarget,\n} from '../../../../../lib/priceAdjustmentView'\nimport { PriceAdjustmentDialog, type AdjustmentReason, type ExistingAdjustment } from './PriceAdjustmentDialog'\nimport type { AdjustableCharge, AdjustableLine, PriceAdjustmentUi } from './priceAdjustmentContext'\nimport type { StoredAdjustment } from './PriceAdjustmentsPanel'\n\ntype ControllerQuote = {\n id: string\n cpqStatus: string\n currencyCode: string\n convertedOrderId: string | null\n pricingRevision?: number\n adjustmentErrors?: Array<{ adjustmentId: string; code: string }>\n lines: Array<AdjustableLine & { mrcTotal: number; nrcTotal: number; charges: AdjustableCharge[] }>\n}\n\ntype ListResponse = {\n items: StoredAdjustment[]\n total: number\n pricingRevision: number\n enabled: boolean\n reasons?: AdjustmentReason[]\n}\n\ntype DialogState = {\n target: AdjustmentTarget\n targetLabel: string\n charge?: AdjustableCharge\n existing?: ExistingAdjustment | null\n}\n\n/**\n * Owns negotiated-price state for the quote page: the adjustment list,\n * reason options, the dialog and delete confirmation. Every write returns the\n * repriced quote, which is handed back through `onQuote`.\n */\nexport function usePriceAdjustments<Q extends ControllerQuote>({ quote, quoteType, canAdjust, onQuote, reloadQuote }: {\n quote: Q | null\n quoteType: string\n canAdjust: boolean\n onQuote: (quote: Q) => void\n reloadQuote: () => Promise<void>\n}) {\n const t = useT()\n const { confirm, ConfirmDialogElement } = useConfirmDialog()\n const [list, setList] = React.useState<ListResponse | null>(null)\n const [dialog, setDialog] = React.useState<DialogState | null>(null)\n const quoteId = quote?.id ?? null\n const revision = quote?.pricingRevision ?? 0\n\n const loadList = React.useCallback(async () => {\n if (!quoteId) return\n const call = await apiCall<ListResponse>(`/api/cpq/quotes/${quoteId}/price-adjustments?pageSize=100`)\n if (call.ok && call.result) setList(call.result)\n else setList({ items: [], total: 0, pricingRevision: 0, enabled: false })\n }, [quoteId])\n\n // Refresh whenever the quote's pricing revision moves (own writes, line edits, recalculation).\n React.useEffect(() => { void loadList() }, [loadList, revision])\n\n const reasons = React.useMemo(() => list?.reasons ?? [], [list])\n const reasonLabels = React.useMemo(() => Object.fromEntries(reasons.map((reason) => [reason.value, reason.label])), [reasons])\n const errorsById = React.useMemo(() => collectAdjustmentErrors(quote as AdjustmentErrorSourceQuote | null), [quote])\n\n const accessInput = {\n enabled: list ? list.enabled : null,\n canAdjust,\n cpqStatus: quote?.cpqStatus ?? 'new',\n convertedOrderId: quote?.convertedOrderId ?? null,\n }\n const access = adjustmentAccess(accessInput)\n\n const remove = React.useCallback(async (adjustmentId: string) => {\n if (!quoteId) return\n const ok = await confirm({\n title: t('cpq.priceAdjustments.deleteConfirm.title', 'Remove this price adjustment?'),\n text: t('cpq.priceAdjustments.deleteConfirm.text', 'The charge goes back to its standard price.'),\n confirmText: t('cpq.priceAdjustments.deleteConfirm.confirm', 'Remove'),\n variant: 'destructive',\n })\n if (!ok) return\n const call = await apiCall<{ quote: Q }>(`/api/cpq/quotes/${quoteId}/price-adjustments/${adjustmentId}?expectedRevision=${revision}`, { method: 'DELETE' })\n if (call.ok && call.result?.quote) { onQuote(call.result.quote); return }\n const code = adjustmentErrorCode(call.status, call.result)\n flash(t(`cpq.priceAdjustments.errors.${code}`, ADJUSTMENT_ERROR_FALLBACKS[code] ?? code), 'error')\n if (code === 'STALE_REVISION') await reloadQuote()\n }, [quoteId, revision, confirm, t, onQuote, reloadQuote])\n\n const ui: PriceAdjustmentUi = {\n access,\n quoteType,\n cpqStatus: accessInput.cpqStatus,\n reasonLabels,\n invalidIds: new Set(errorsById.keys()),\n canDeleteInvalid: canDeleteInvalidAdjustment(accessInput),\n openCharge: (line, charge) => {\n const existing = list?.items.find((item) => item.level === 'charge' && item.quoteLineId === line.lineId && item.chargeCode === charge.chargeCode)\n setDialog({\n target: { level: 'charge', quoteLineId: line.lineId, chargeCode: charge.chargeCode },\n targetLabel: `${line.offeringName} \u00B7 ${charge.chargeName}`,\n charge,\n existing: existing ?? null,\n })\n },\n openLine: (line) => {\n const existing = list?.items.find((item) => item.level === 'line' && item.quoteLineId === line.lineId)\n setDialog({ target: { level: 'line', quoteLineId: line.lineId }, targetLabel: line.offeringName, existing: existing ?? null })\n },\n edit: (adjustmentId) => {\n const item = list?.items.find((entry) => entry.id === adjustmentId)\n const line = quote?.lines.find((entry) => entry.lineId === item?.quoteLineId)\n if (!item || !line) return\n const charge = item.chargeCode ? line.charges.find((entry) => entry.chargeCode === item.chargeCode) : undefined\n setDialog({\n target: item.level === 'charge' && item.chargeCode\n ? { level: 'charge', quoteLineId: item.quoteLineId, chargeCode: item.chargeCode }\n : { level: 'line', quoteLineId: item.quoteLineId },\n targetLabel: charge ? `${line.offeringName} \u00B7 ${charge.chargeName}` : line.offeringName,\n charge,\n existing: item,\n })\n },\n remove: (adjustmentId) => { void remove(adjustmentId) },\n }\n\n const dialogElement = quote && dialog ? (\n <PriceAdjustmentDialog<Q>\n open\n quoteId={quote.id}\n currency={quote.currencyCode}\n pricingRevision={revision}\n target={dialog.target}\n targetLabel={dialog.targetLabel}\n charge={dialog.charge}\n existing={dialog.existing}\n storedAdjustmentIds={list?.items.map((item) => item.id) ?? []}\n reasons={reasons}\n onClose={() => setDialog(null)}\n onSaved={(next) => onQuote(next)}\n onStale={() => { void reloadQuote() }}\n />\n ) : null\n\n return {\n ui,\n items: list?.items ?? [],\n total: list?.total ?? 0,\n errorsById,\n elements: <>{dialogElement}{ConfirmDialogElement}</>,\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|