@dainamite/cpq 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +64 -0
- package/MIGRATION.md +74 -0
- package/dist/modules/cpq/acl.js +1 -0
- package/dist/modules/cpq/acl.js.map +2 -2
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
- package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
- package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
- package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
- package/dist/modules/cpq/api/orders/route.js +47 -2
- package/dist/modules/cpq/api/orders/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
- package/dist/modules/cpq/api/quotes/price/route.js +43 -3
- package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +3 -3
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +174 -184
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +140 -280
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +208 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
- package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
- package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
- package/dist/modules/cpq/ce.js +8 -0
- package/dist/modules/cpq/ce.js.map +2 -2
- package/dist/modules/cpq/cli.js +34 -1
- package/dist/modules/cpq/cli.js.map +2 -2
- package/dist/modules/cpq/commands/price-adjustments.js +107 -0
- package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
- package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
- package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
- package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
- package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
- package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
- package/dist/modules/cpq/components/StatusPath.js +5 -3
- package/dist/modules/cpq/components/StatusPath.js.map +2 -2
- package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
- package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
- package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
- package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
- package/dist/modules/cpq/data/entities.js +97 -0
- package/dist/modules/cpq/data/entities.js.map +2 -2
- package/dist/modules/cpq/data/price-adjustments.js +70 -0
- package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
- package/dist/modules/cpq/data/validators.js +4 -0
- package/dist/modules/cpq/data/validators.js.map +2 -2
- package/dist/modules/cpq/di.js +6 -0
- package/dist/modules/cpq/di.js.map +2 -2
- package/dist/modules/cpq/events.js +47 -0
- package/dist/modules/cpq/events.js.map +2 -2
- package/dist/modules/cpq/i18n/de.json +1220 -0
- package/dist/modules/cpq/i18n/en.json +1220 -0
- package/dist/modules/cpq/i18n/es.json +1220 -0
- package/dist/modules/cpq/i18n/pl.json +1220 -0
- package/dist/modules/cpq/index.js +1 -1
- package/dist/modules/cpq/index.js.map +2 -2
- package/dist/modules/cpq/lib/auditActor.js +10 -0
- package/dist/modules/cpq/lib/auditActor.js.map +7 -0
- package/dist/modules/cpq/lib/formatMoney.js +7 -0
- package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
- package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
- package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
- package/dist/modules/cpq/lib/quotePermissions.js +13 -0
- package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
- package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
- package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
- package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
- package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
- package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
- package/dist/modules/cpq/services/cpqOrderService.js +25 -28
- package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
- package/dist/modules/cpq/services/cpqPricingService.js +7 -0
- package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
- package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
- package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
- package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
- package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
- package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
- package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
- package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
- package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
- package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
- package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
- package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
- package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
- package/dist/modules/cpq/services/types.js.map +2 -2
- package/dist/modules/cpq/setup.js +6 -0
- package/dist/modules/cpq/setup.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
- package/dist/modules/cpq/widgets/injection-table.js +2 -2
- package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
- package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
- package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
- package/dist/modules/cpq/workflows/steps/index.js +12 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
- package/package.json +1 -1
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
- package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
- package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
- package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
- package/src/modules/cpq/acl.ts +1 -0
- package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
- package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
- package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
- package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
- package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
- package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
- package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
- package/src/modules/cpq/api/orders/route.ts +51 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
- package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
- package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
- package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
- package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
- package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
- package/src/modules/cpq/api/quotes/price/route.ts +28 -2
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +3 -3
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +164 -120
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +122 -308
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
- package/src/modules/cpq/ce.ts +8 -0
- package/src/modules/cpq/cli.ts +48 -1
- package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
- package/src/modules/cpq/commands/price-adjustments.ts +90 -0
- package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
- package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
- package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
- package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
- package/src/modules/cpq/components/StatusPath.tsx +7 -4
- package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
- package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
- package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
- package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
- package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
- package/src/modules/cpq/data/entities.ts +83 -0
- package/src/modules/cpq/data/price-adjustments.ts +67 -0
- package/src/modules/cpq/data/validators.ts +3 -0
- package/src/modules/cpq/di.ts +6 -0
- package/src/modules/cpq/events.ts +53 -2
- package/src/modules/cpq/i18n/de.json +1220 -0
- package/src/modules/cpq/i18n/en.json +1220 -0
- package/src/modules/cpq/i18n/es.json +1220 -0
- package/src/modules/cpq/i18n/pl.json +1220 -0
- package/src/modules/cpq/index.ts +1 -1
- package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
- package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
- package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
- package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
- package/src/modules/cpq/lib/auditActor.ts +13 -0
- package/src/modules/cpq/lib/formatMoney.ts +4 -0
- package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
- package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
- package/src/modules/cpq/lib/quotePermissions.ts +22 -0
- package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
- package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
- package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
- package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
- package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
- package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
- package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
- package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
- package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
- package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
- package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
- package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
- package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
- package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
- package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
- package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
- package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
- package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
- package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
- package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
- package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
- package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
- package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
- package/src/modules/cpq/services/cpqOrderService.ts +22 -28
- package/src/modules/cpq/services/cpqPricingService.ts +8 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
- package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
- package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
- package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
- package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
- package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
- package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
- package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
- package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
- package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
- package/src/modules/cpq/services/types.ts +34 -1
- package/src/modules/cpq/setup.ts +7 -1
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
- package/src/modules/cpq/widgets/injection-table.ts +8 -2
- package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
- package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
- package/src/modules/cpq/workflows/steps/index.ts +12 -0
- package/src/modules/cpq/workflows/types.ts +4 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import * as React from 'react'
|
|
3
|
-
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
3
|
+
import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
4
4
|
import { useRouter } from 'next/navigation'
|
|
5
5
|
import type { LegacyColumnDef as ColumnDef } from '@tanstack/react-table/legacy'
|
|
6
6
|
import { DataTable } from '@open-mercato/ui/backend/DataTable'
|
|
@@ -15,19 +15,21 @@ import {
|
|
|
15
15
|
} from '@open-mercato/ui/primitives/select'
|
|
16
16
|
import { useCpqListData } from '../../../components/CpqListView'
|
|
17
17
|
import { useCpqRowActions } from '../../../components/useCpqRowActions'
|
|
18
|
-
import { StatusBadge, formatCurrency } from './shared'
|
|
18
|
+
import { StatusBadge, buildInventoryStatusOptions, formatCurrency } from './shared'
|
|
19
19
|
|
|
20
20
|
// `all` is a sentinel value because Radix Select disallows empty strings
|
|
21
21
|
// as option values. We map it back to "no filter" before writing to the
|
|
22
22
|
// query.
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
23
|
+
function buildExpiringWindows(t: TranslateFn): { value: string; label: string }[] {
|
|
24
|
+
return [
|
|
25
|
+
{ value: 'all', label: t('cpq.inventory.subscriptions.expiring.all', 'All subscriptions') },
|
|
26
|
+
{ value: '7', label: t('cpq.inventory.subscriptions.expiring.in7Days', 'Expiring in 7 days') },
|
|
27
|
+
{ value: '14', label: t('cpq.inventory.subscriptions.expiring.in14Days', 'Expiring in 14 days') },
|
|
28
|
+
{ value: '30', label: t('cpq.inventory.subscriptions.expiring.in30Days', 'Expiring in 30 days') },
|
|
29
|
+
{ value: '60', label: t('cpq.inventory.subscriptions.expiring.in60Days', 'Expiring in 60 days') },
|
|
30
|
+
{ value: '90', label: t('cpq.inventory.subscriptions.expiring.in90Days', 'Expiring in 90 days') },
|
|
31
|
+
]
|
|
32
|
+
}
|
|
31
33
|
|
|
32
34
|
type Subscription = {
|
|
33
35
|
id: string
|
|
@@ -44,14 +46,7 @@ type Subscription = {
|
|
|
44
46
|
|
|
45
47
|
const PAGE_SIZE = 50
|
|
46
48
|
|
|
47
|
-
const
|
|
48
|
-
{ value: 'pending', label: 'Pending' },
|
|
49
|
-
{ value: 'active', label: 'Active' },
|
|
50
|
-
{ value: 'suspended', label: 'Suspended' },
|
|
51
|
-
{ value: 'terminated', label: 'Terminated' },
|
|
52
|
-
{ value: 'expired', label: 'Expired' },
|
|
53
|
-
{ value: 'cancelled', label: 'Cancelled' },
|
|
54
|
-
] as const
|
|
49
|
+
const SUBSCRIPTION_STATUSES = ['pending', 'active', 'suspended', 'terminated', 'expired', 'cancelled'] as const
|
|
55
50
|
|
|
56
51
|
function buildFilterParams(values: FilterValues, params: URLSearchParams) {
|
|
57
52
|
if (typeof values.status === 'string' && values.status) {
|
|
@@ -95,7 +90,7 @@ export function SubscriptionsTable() {
|
|
|
95
90
|
id: 'status',
|
|
96
91
|
label: t('cpq.inventory.filters.status', 'Status'),
|
|
97
92
|
type: 'select',
|
|
98
|
-
options:
|
|
93
|
+
options: buildInventoryStatusOptions(t, SUBSCRIPTION_STATUSES),
|
|
99
94
|
},
|
|
100
95
|
{
|
|
101
96
|
id: 'billingCycle',
|
|
@@ -108,11 +103,11 @@ export function SubscriptionsTable() {
|
|
|
108
103
|
type: 'select',
|
|
109
104
|
// Pick from common renewal-watch windows. Backend caps at 365.
|
|
110
105
|
options: [
|
|
111
|
-
{ value: '7', label: '7 days' },
|
|
112
|
-
{ value: '14', label: '14 days' },
|
|
113
|
-
{ value: '30', label: '30 days' },
|
|
114
|
-
{ value: '60', label: '60 days' },
|
|
115
|
-
{ value: '90', label: '90 days' },
|
|
106
|
+
{ value: '7', label: t('cpq.inventory.filters.days7', '7 days') },
|
|
107
|
+
{ value: '14', label: t('cpq.inventory.filters.days14', '14 days') },
|
|
108
|
+
{ value: '30', label: t('cpq.inventory.filters.days30', '30 days') },
|
|
109
|
+
{ value: '60', label: t('cpq.inventory.filters.days60', '60 days') },
|
|
110
|
+
{ value: '90', label: t('cpq.inventory.filters.days90', '90 days') },
|
|
116
111
|
],
|
|
117
112
|
},
|
|
118
113
|
],
|
|
@@ -190,13 +185,14 @@ export function SubscriptionsTable() {
|
|
|
190
185
|
// Render the picker inline with the DataTable's title-row actions
|
|
191
186
|
// (right next to Refresh / kebab menu) so it sits flush with the
|
|
192
187
|
// card chrome instead of floating outside.
|
|
188
|
+
const expiringWindows = React.useMemo(() => buildExpiringWindows(t), [t])
|
|
193
189
|
const expiringPicker = (
|
|
194
190
|
<Select value={currentExpiring} onValueChange={onExpiringChange}>
|
|
195
191
|
<SelectTrigger className="h-9 w-[200px]">
|
|
196
|
-
<SelectValue placeholder=
|
|
192
|
+
<SelectValue placeholder={t('cpq.inventory.subscriptions.expiring.all', 'All subscriptions')} />
|
|
197
193
|
</SelectTrigger>
|
|
198
194
|
<SelectContent>
|
|
199
|
-
{
|
|
195
|
+
{expiringWindows.map((w) => (
|
|
200
196
|
<SelectItem key={w.value} value={w.value}>
|
|
201
197
|
{w.label}
|
|
202
198
|
</SelectItem>
|
|
@@ -4,6 +4,6 @@ export const metadata = {
|
|
|
4
4
|
navHidden: true,
|
|
5
5
|
breadcrumb: [
|
|
6
6
|
{ label: 'Customer Inventory', labelKey: 'cpq.inventory.title', href: '/backend/cpq/inventory' },
|
|
7
|
-
{ label: 'Asset Detail' },
|
|
7
|
+
{ label: 'Asset Detail', labelKey: 'cpq.inventory.assets.detail.breadcrumb' },
|
|
8
8
|
],
|
|
9
9
|
} as const
|
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import * as React from 'react'
|
|
3
3
|
import { useRouter, useParams } from 'next/navigation'
|
|
4
|
+
import { useT } from '@open-mercato/shared/lib/i18n/context'
|
|
4
5
|
import { Alert } from '@open-mercato/ui/primitives/alert'
|
|
5
6
|
import { Button } from '@open-mercato/ui/primitives/button'
|
|
6
7
|
import { Tag } from '@open-mercato/ui/primitives/tag'
|
|
7
8
|
import { FormHeader } from '@open-mercato/ui/backend/forms'
|
|
8
9
|
import {
|
|
9
10
|
assetStatusMap,
|
|
10
|
-
formatStatusLabel,
|
|
11
11
|
type AssetStatus,
|
|
12
12
|
} from '../../../../../components/statusMaps'
|
|
13
13
|
import { StatusPath } from '../../../../../components/StatusPath'
|
|
14
|
+
import { buildInventoryStatusLabels, inventoryStatusLabel } from '../../shared'
|
|
14
15
|
|
|
15
16
|
type AssetDetail = {
|
|
16
17
|
id: string
|
|
@@ -48,19 +49,14 @@ const ALLOWED_TRANSITIONS: Record<string, string[]> = {
|
|
|
48
49
|
cancelled: [],
|
|
49
50
|
}
|
|
50
51
|
|
|
51
|
-
const
|
|
52
|
-
pending: 'Pending',
|
|
53
|
-
delivered: 'Delivered',
|
|
54
|
-
active: 'Active',
|
|
55
|
-
returned: 'Returned',
|
|
56
|
-
cancelled: 'Cancelled',
|
|
57
|
-
}
|
|
52
|
+
const ASSET_STATUSES = ['pending', 'delivered', 'active', 'returned', 'cancelled'] as const
|
|
58
53
|
|
|
59
54
|
function fmt(amount: number | string, currency: string): string {
|
|
60
55
|
return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(Number(amount))
|
|
61
56
|
}
|
|
62
57
|
|
|
63
58
|
export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
59
|
+
const t = useT()
|
|
64
60
|
const router = useRouter()
|
|
65
61
|
const urlParams = useParams()
|
|
66
62
|
const assetId = (props.params?.id ?? urlParams?.id) as string
|
|
@@ -89,7 +85,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
89
85
|
body: JSON.stringify({ id: asset.id, targetStatus }),
|
|
90
86
|
})
|
|
91
87
|
const data = await res.json()
|
|
92
|
-
if (!res.ok) throw new Error(data?.error ?? 'Status transition failed')
|
|
88
|
+
if (!res.ok) throw new Error(data?.error ?? t('cpq.inventory.assets.detail.error.transitionFailed', 'Status transition failed'))
|
|
93
89
|
setAsset((prev) => prev ? { ...prev, ...data } : prev)
|
|
94
90
|
} catch (err) {
|
|
95
91
|
setError((err as Error).message)
|
|
@@ -103,7 +99,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
103
99
|
async function load() {
|
|
104
100
|
try {
|
|
105
101
|
const res = await fetch(`/api/cpq/inventory/assets?id=${assetId}`)
|
|
106
|
-
if (!res.ok) throw new Error('Asset not found')
|
|
102
|
+
if (!res.ok) throw new Error(t('cpq.inventory.assets.detail.error.notFound', 'Asset not found'))
|
|
107
103
|
const data: AssetDetail = await res.json()
|
|
108
104
|
if (!cancelled) {
|
|
109
105
|
setAsset(data)
|
|
@@ -182,12 +178,12 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
182
178
|
}
|
|
183
179
|
load()
|
|
184
180
|
return () => { cancelled = true }
|
|
185
|
-
}, [assetId])
|
|
181
|
+
}, [assetId, t])
|
|
186
182
|
|
|
187
183
|
if (loading) {
|
|
188
184
|
return (
|
|
189
185
|
<div className="flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground">
|
|
190
|
-
<Spinner /> Loading…
|
|
186
|
+
<Spinner /> {t('cpq.inventory.assets.detail.loading', 'Loading…')}
|
|
191
187
|
</div>
|
|
192
188
|
)
|
|
193
189
|
}
|
|
@@ -195,15 +191,16 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
195
191
|
if (!asset) {
|
|
196
192
|
return (
|
|
197
193
|
<div className="space-y-4">
|
|
198
|
-
<button onClick={() => router.push('/backend/cpq/inventory')} className="text-sm text-muted-foreground hover:text-foreground">← Back</button>
|
|
194
|
+
<button onClick={() => router.push('/backend/cpq/inventory')} className="text-sm text-muted-foreground hover:text-foreground">← {t('cpq.common.back', 'Back')}</button>
|
|
199
195
|
{error && <ErrorBanner message={error} />}
|
|
200
|
-
<div className="rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground">Asset not found
|
|
196
|
+
<div className="rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground">{t('cpq.inventory.assets.detail.notFound', 'Asset not found.')}</div>
|
|
201
197
|
</div>
|
|
202
198
|
)
|
|
203
199
|
}
|
|
204
200
|
|
|
205
201
|
const currency = asset.currencyCode || 'USD'
|
|
206
202
|
const statusTransitions = ALLOWED_TRANSITIONS[asset.status] ?? []
|
|
203
|
+
const statusLabels = buildInventoryStatusLabels(t, ASSET_STATUSES)
|
|
207
204
|
|
|
208
205
|
return (
|
|
209
206
|
<div className="space-y-6">
|
|
@@ -214,13 +211,13 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
214
211
|
<FormHeader
|
|
215
212
|
mode="detail"
|
|
216
213
|
backHref="/backend/cpq/inventory"
|
|
217
|
-
backLabel=
|
|
218
|
-
entityTypeLabel=
|
|
214
|
+
backLabel={t('cpq.inventory.detail.backToInventory', 'Back to Customer Inventory')}
|
|
215
|
+
entityTypeLabel={t('cpq.inventory.assets.detail.entityType', 'Asset')}
|
|
219
216
|
title={asset.name}
|
|
220
217
|
statusBadge={
|
|
221
218
|
<div className="flex flex-wrap items-center gap-2 pt-1">
|
|
222
219
|
<Tag variant={assetStatusMap[asset.status as AssetStatus] ?? 'neutral'} dot>
|
|
223
|
-
{
|
|
220
|
+
{inventoryStatusLabel(t, asset.status)}
|
|
224
221
|
</Tag>
|
|
225
222
|
<Tag variant="brand" className="capitalize">{asset.assetType.replace(/_/g, ' ')}</Tag>
|
|
226
223
|
{transitioning && <Spinner />}
|
|
@@ -236,11 +233,11 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
236
233
|
path={['pending', 'delivered', 'active', 'returned']}
|
|
237
234
|
terminals={['cancelled']}
|
|
238
235
|
statusMap={assetStatusMap}
|
|
239
|
-
labels={
|
|
236
|
+
labels={statusLabels}
|
|
240
237
|
onTransition={transitionStatus}
|
|
241
238
|
allowedTransitions={statusTransitions}
|
|
242
239
|
disabled={transitioning}
|
|
243
|
-
ariaLabel=
|
|
240
|
+
ariaLabel={t('cpq.inventory.assets.detail.statusPath', 'Asset status path')}
|
|
244
241
|
/>
|
|
245
242
|
{transitioning && <Spinner />}
|
|
246
243
|
</div>
|
|
@@ -250,36 +247,36 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
250
247
|
{/* Detail Cards */}
|
|
251
248
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
|
|
252
249
|
<div className="rounded-lg border bg-card p-4 space-y-3">
|
|
253
|
-
<h3 className="text-sm font-medium">Asset Details</h3>
|
|
250
|
+
<h3 className="text-sm font-medium">{t('cpq.inventory.assets.detail.sections.details', 'Asset Details')}</h3>
|
|
254
251
|
<div className="grid grid-cols-2 gap-x-4 gap-y-2">
|
|
255
|
-
<Field label=
|
|
256
|
-
<Field label=
|
|
257
|
-
<Field label=
|
|
258
|
-
<Field label=
|
|
259
|
-
<Field label=
|
|
260
|
-
{asset.purchaseDate && <Field label=
|
|
261
|
-
{asset.deliveryDate && <Field label=
|
|
252
|
+
<Field label={t('cpq.inventory.assets.detail.fields.code', 'Code')} value={asset.code} mono />
|
|
253
|
+
<Field label={t('cpq.inventory.assets.detail.fields.status', 'Status')} value={asset.status} />
|
|
254
|
+
<Field label={t('cpq.inventory.assets.detail.fields.assetType', 'Asset Type')} value={asset.assetType.replace(/_/g, ' ')} />
|
|
255
|
+
<Field label={t('cpq.inventory.assets.detail.fields.currency', 'Currency')} value={currency} />
|
|
256
|
+
<Field label={t('cpq.inventory.assets.detail.fields.quantity', 'Quantity')} value={String(asset.quantity)} />
|
|
257
|
+
{asset.purchaseDate && <Field label={t('cpq.inventory.assets.detail.fields.purchaseDate', 'Purchase Date')} value={new Date(asset.purchaseDate).toLocaleDateString()} />}
|
|
258
|
+
{asset.deliveryDate && <Field label={t('cpq.inventory.assets.detail.fields.deliveryDate', 'Delivery Date')} value={new Date(asset.deliveryDate).toLocaleDateString()} />}
|
|
262
259
|
</div>
|
|
263
260
|
{asset.description && (
|
|
264
261
|
<div className="pt-1">
|
|
265
|
-
<p className="text-xs text-muted-foreground">Description</p>
|
|
262
|
+
<p className="text-xs text-muted-foreground">{t('cpq.inventory.assets.detail.fields.description', 'Description')}</p>
|
|
266
263
|
<p className="text-sm">{asset.description}</p>
|
|
267
264
|
</div>
|
|
268
265
|
)}
|
|
269
266
|
</div>
|
|
270
267
|
|
|
271
268
|
<div className="rounded-lg border bg-card p-4 space-y-3">
|
|
272
|
-
<h3 className="text-sm font-medium">Pricing & References</h3>
|
|
269
|
+
<h3 className="text-sm font-medium">{t('cpq.inventory.assets.detail.sections.pricing', 'Pricing & References')}</h3>
|
|
273
270
|
<div>
|
|
274
|
-
<p className="text-xs text-muted-foreground">Purchase Price</p>
|
|
271
|
+
<p className="text-xs text-muted-foreground">{t('cpq.inventory.assets.detail.fields.purchasePrice', 'Purchase Price')}</p>
|
|
275
272
|
<p className="text-lg font-semibold font-mono">{fmt(asset.purchasePrice, currency)}</p>
|
|
276
273
|
</div>
|
|
277
274
|
<div className="grid grid-cols-2 gap-x-4 gap-y-2 pt-1">
|
|
278
|
-
<Field label=
|
|
279
|
-
<Field label=
|
|
275
|
+
<Field label={t('cpq.inventory.assets.detail.fields.customer', 'Customer')} value={customerName ?? `${asset.customerId.slice(0, 12)}…`} />
|
|
276
|
+
<Field label={t('cpq.inventory.assets.detail.fields.created', 'Created')} value={new Date(asset.createdAt).toLocaleString()} />
|
|
280
277
|
{asset.subscriptionId && (
|
|
281
278
|
<RefField
|
|
282
|
-
label=
|
|
279
|
+
label={t('cpq.inventory.assets.detail.fields.linkedSubscription', 'Linked Subscription')}
|
|
283
280
|
name={subscriptionName ?? undefined}
|
|
284
281
|
fallback={asset.subscriptionId}
|
|
285
282
|
onClick={() => router.push(`/backend/cpq/inventory/subscriptions/${asset.subscriptionId}`)}
|
|
@@ -287,7 +284,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
287
284
|
)}
|
|
288
285
|
{asset.offeringId && (
|
|
289
286
|
<RefField
|
|
290
|
-
label=
|
|
287
|
+
label={t('cpq.inventory.assets.detail.fields.offering', 'Offering')}
|
|
291
288
|
name={offeringName ?? undefined}
|
|
292
289
|
fallback={asset.offeringId}
|
|
293
290
|
onClick={() => router.push(`/backend/cpq/offerings/${asset.offeringId}`)}
|
|
@@ -295,7 +292,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
295
292
|
)}
|
|
296
293
|
{asset.specId && (
|
|
297
294
|
<RefField
|
|
298
|
-
label=
|
|
295
|
+
label={t('cpq.inventory.assets.detail.fields.specification', 'Specification')}
|
|
299
296
|
name={specName ?? undefined}
|
|
300
297
|
fallback={asset.specId}
|
|
301
298
|
onClick={() => router.push(`/backend/cpq/specifications/${asset.specId}`)}
|
|
@@ -303,7 +300,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
303
300
|
)}
|
|
304
301
|
{asset.productId && (
|
|
305
302
|
<RefField
|
|
306
|
-
label=
|
|
303
|
+
label={t('cpq.inventory.assets.detail.fields.product', 'Product')}
|
|
307
304
|
name={productName ?? undefined}
|
|
308
305
|
fallback={asset.productId}
|
|
309
306
|
onClick={() => router.push(`/backend/catalog/products/${asset.productId}`)}
|
|
@@ -311,7 +308,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
311
308
|
)}
|
|
312
309
|
{asset.sourceQuoteId && (
|
|
313
310
|
<RefField
|
|
314
|
-
label=
|
|
311
|
+
label={t('cpq.inventory.assets.detail.fields.sourceQuote', 'Source Quote')}
|
|
315
312
|
name={sourceQuoteNumber ?? undefined}
|
|
316
313
|
fallback={asset.sourceQuoteId}
|
|
317
314
|
onClick={() => router.push(`/backend/cpq/quotes/${asset.sourceQuoteId}`)}
|
|
@@ -319,7 +316,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
319
316
|
)}
|
|
320
317
|
{asset.sourceOrderId && (
|
|
321
318
|
<RefField
|
|
322
|
-
label=
|
|
319
|
+
label={t('cpq.inventory.assets.detail.fields.sourceOrder', 'Source Order')}
|
|
323
320
|
name={sourceOrderNumber ?? undefined}
|
|
324
321
|
fallback={asset.sourceOrderId}
|
|
325
322
|
onClick={() => router.push(`/backend/cpq/orders/${asset.sourceOrderId}`)}
|
|
@@ -332,7 +329,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
332
329
|
{/* Configuration */}
|
|
333
330
|
{asset.configuration && Object.keys(asset.configuration).length > 0 && (
|
|
334
331
|
<div className="rounded-lg border bg-card p-4">
|
|
335
|
-
<h3 className="text-sm font-medium mb-2">Configuration</h3>
|
|
332
|
+
<h3 className="text-sm font-medium mb-2">{t('cpq.inventory.assets.detail.sections.configuration', 'Configuration')}</h3>
|
|
336
333
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1">
|
|
337
334
|
{Object.entries(asset.configuration).map(([key, value]) => (
|
|
338
335
|
<div key={key} className="text-xs">
|
|
@@ -347,15 +344,15 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
347
344
|
{/* Charges */}
|
|
348
345
|
{asset.charges && asset.charges.length > 0 && (
|
|
349
346
|
<div className="rounded-lg border bg-card p-4">
|
|
350
|
-
<h3 className="text-sm font-medium mb-2">Charges</h3>
|
|
347
|
+
<h3 className="text-sm font-medium mb-2">{t('cpq.inventory.assets.detail.sections.charges', 'Charges')}</h3>
|
|
351
348
|
<table className="w-full text-sm">
|
|
352
349
|
<thead>
|
|
353
350
|
<tr className="border-b bg-muted/50 text-muted-foreground">
|
|
354
|
-
<th className="text-left px-3 py-2 font-medium">Charge</th>
|
|
355
|
-
<th className="text-left px-3 py-2 font-medium">Type</th>
|
|
356
|
-
<th className="text-right px-3 py-2 font-medium">Unit Price</th>
|
|
357
|
-
<th className="text-right px-3 py-2 font-medium">Qty</th>
|
|
358
|
-
<th className="text-right px-3 py-2 font-medium">Total</th>
|
|
351
|
+
<th className="text-left px-3 py-2 font-medium">{t('cpq.inventory.assets.detail.charges.charge', 'Charge')}</th>
|
|
352
|
+
<th className="text-left px-3 py-2 font-medium">{t('cpq.inventory.assets.detail.charges.type', 'Type')}</th>
|
|
353
|
+
<th className="text-right px-3 py-2 font-medium">{t('cpq.inventory.assets.detail.charges.unitPrice', 'Unit Price')}</th>
|
|
354
|
+
<th className="text-right px-3 py-2 font-medium">{t('cpq.inventory.assets.detail.charges.qty', 'Qty')}</th>
|
|
355
|
+
<th className="text-right px-3 py-2 font-medium">{t('cpq.inventory.assets.detail.charges.total', 'Total')}</th>
|
|
359
356
|
</tr>
|
|
360
357
|
</thead>
|
|
361
358
|
<tbody>
|
|
@@ -376,7 +373,7 @@ export default function AssetDetailPage(props: { params?: { id?: string } }) {
|
|
|
376
373
|
{/* Metadata */}
|
|
377
374
|
{asset.metadata && Object.keys(asset.metadata).length > 0 && (
|
|
378
375
|
<div className="rounded-lg border bg-card p-4">
|
|
379
|
-
<h3 className="text-sm font-medium mb-2">Metadata</h3>
|
|
376
|
+
<h3 className="text-sm font-medium mb-2">{t('cpq.inventory.assets.detail.sections.metadata', 'Metadata')}</h3>
|
|
380
377
|
<div className="grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1">
|
|
381
378
|
{Object.entries(asset.metadata).map(([key, value]) => (
|
|
382
379
|
<div key={key} className="text-xs">
|
|
@@ -7,10 +7,10 @@ export const metadata = {
|
|
|
7
7
|
requireAuth: true,
|
|
8
8
|
requireFeatures: ['cpq.inventory.view'],
|
|
9
9
|
pageTitle: 'CPQ Subscriptions',
|
|
10
|
-
pageTitleKey: 'cpq.inventory.title',
|
|
10
|
+
pageTitleKey: 'cpq.inventory.nav.title',
|
|
11
11
|
pageGroup: 'CPQ',
|
|
12
12
|
pageGroupKey: 'customers~sales~cpq.nav.group',
|
|
13
|
-
pageOrder:
|
|
13
|
+
pageOrder: 3,
|
|
14
14
|
icon: inventoryIcon,
|
|
15
|
-
breadcrumb: [{ label: 'CPQ Subscriptions', labelKey: 'cpq.inventory.title' }],
|
|
15
|
+
breadcrumb: [{ label: 'CPQ Subscriptions', labelKey: 'cpq.inventory.nav.title' }],
|
|
16
16
|
} as const
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use client"
|
|
2
2
|
import * as React from 'react'
|
|
3
|
+
import { useT, type TranslateFn } from '@open-mercato/shared/lib/i18n/context'
|
|
3
4
|
import { Tag } from '@open-mercato/ui/primitives/tag'
|
|
4
5
|
import {
|
|
5
6
|
assetStatusMap,
|
|
@@ -9,6 +10,40 @@ import {
|
|
|
9
10
|
type SubscriptionStatus,
|
|
10
11
|
} from '../../../components/statusMaps'
|
|
11
12
|
|
|
13
|
+
/**
|
|
14
|
+
* Translated display label for a subscription / subscription-item / asset
|
|
15
|
+
* status. The enum value stays the key; unknown values fall back to the
|
|
16
|
+
* title-cased raw value so a renamed enum still renders.
|
|
17
|
+
*/
|
|
18
|
+
export function inventoryStatusLabel(t: TranslateFn, status: string): string {
|
|
19
|
+
switch (status) {
|
|
20
|
+
case 'pending': return t('cpq.inventory.status.pending', 'Pending')
|
|
21
|
+
case 'active': return t('cpq.inventory.status.active', 'Active')
|
|
22
|
+
case 'delivered': return t('cpq.inventory.status.delivered', 'Delivered')
|
|
23
|
+
case 'returned': return t('cpq.inventory.status.returned', 'Returned')
|
|
24
|
+
case 'suspended': return t('cpq.inventory.status.suspended', 'Suspended')
|
|
25
|
+
case 'terminated': return t('cpq.inventory.status.terminated', 'Terminated')
|
|
26
|
+
case 'expired': return t('cpq.inventory.status.expired', 'Expired')
|
|
27
|
+
case 'cancelled': return t('cpq.inventory.status.cancelled', 'Cancelled')
|
|
28
|
+
default: return formatStatusLabel(status)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `{ value: label }` map for the given statuses, translated at render time. */
|
|
33
|
+
export function buildInventoryStatusLabels(t: TranslateFn, statuses: readonly string[]): Record<string, string> {
|
|
34
|
+
const out: Record<string, string> = {}
|
|
35
|
+
for (const status of statuses) out[status] = inventoryStatusLabel(t, status)
|
|
36
|
+
return out
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Select options for the given statuses, translated at render time. */
|
|
40
|
+
export function buildInventoryStatusOptions(
|
|
41
|
+
t: TranslateFn,
|
|
42
|
+
statuses: readonly string[],
|
|
43
|
+
): { value: string; label: string }[] {
|
|
44
|
+
return statuses.map((value) => ({ value, label: inventoryStatusLabel(t, value) }))
|
|
45
|
+
}
|
|
46
|
+
|
|
12
47
|
/**
|
|
13
48
|
* Subscription / asset status pill. Looks up the OM Tag variant from the
|
|
14
49
|
* shared maps so palettes stay consistent with the rest of the CPQ surface.
|
|
@@ -16,13 +51,14 @@ import {
|
|
|
16
51
|
* renders without crashing.
|
|
17
52
|
*/
|
|
18
53
|
export function StatusBadge({ status }: { status: string }) {
|
|
54
|
+
const t = useT()
|
|
19
55
|
const variant =
|
|
20
56
|
subscriptionStatusMap[status as SubscriptionStatus] ??
|
|
21
57
|
assetStatusMap[status as AssetStatus] ??
|
|
22
58
|
'neutral'
|
|
23
59
|
return (
|
|
24
60
|
<Tag variant={variant} dot>
|
|
25
|
-
{
|
|
61
|
+
{inventoryStatusLabel(t, status)}
|
|
26
62
|
</Tag>
|
|
27
63
|
)
|
|
28
64
|
}
|
|
@@ -4,6 +4,6 @@ export const metadata = {
|
|
|
4
4
|
navHidden: true,
|
|
5
5
|
breadcrumb: [
|
|
6
6
|
{ label: 'Customer Inventory', labelKey: 'cpq.inventory.title', href: '/backend/cpq/inventory' },
|
|
7
|
-
{ label: 'Subscription Detail' },
|
|
7
|
+
{ label: 'Subscription Detail', labelKey: 'cpq.inventory.subscriptions.detail.breadcrumb' },
|
|
8
8
|
],
|
|
9
9
|
} as const
|