@dainamite/cpq 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +70 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +236 -0
- package/README.md +108 -0
- package/dist/modules/cpq/acl.js +30 -0
- package/dist/modules/cpq/acl.js.map +7 -0
- package/dist/modules/cpq/api/_helpers/handleArcError.js +37 -0
- package/dist/modules/cpq/api/_helpers/handleArcError.js.map +7 -0
- package/dist/modules/cpq/api/attribute-options/route.js +47 -0
- package/dist/modules/cpq/api/attribute-options/route.js.map +7 -0
- package/dist/modules/cpq/api/bundle-slots/route.js +101 -0
- package/dist/modules/cpq/api/bundle-slots/route.js.map +7 -0
- package/dist/modules/cpq/api/bundle-tree/route.js +28 -0
- package/dist/modules/cpq/api/bundle-tree/route.js.map +7 -0
- package/dist/modules/cpq/api/context.js +29 -0
- package/dist/modules/cpq/api/context.js.map +7 -0
- package/dist/modules/cpq/api/inventory/assets/route.js +113 -0
- package/dist/modules/cpq/api/inventory/assets/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/assets/status/route.js +35 -0
- package/dist/modules/cpq/api/inventory/assets/status/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js +30 -0
- package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js +31 -0
- package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/subscription-items/route.js +99 -0
- package/dist/modules/cpq/api/inventory/subscription-items/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js +57 -0
- package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js +62 -0
- package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/subscriptions/route.js +116 -0
- package/dist/modules/cpq/api/inventory/subscriptions/route.js.map +7 -0
- package/dist/modules/cpq/api/inventory/subscriptions/status/route.js +35 -0
- package/dist/modules/cpq/api/inventory/subscriptions/status/route.js.map +7 -0
- package/dist/modules/cpq/api/offering-components/route.js +91 -0
- package/dist/modules/cpq/api/offering-components/route.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +39 -0
- package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js +28 -0
- package/dist/modules/cpq/api/orders/[orderId]/route.js.map +7 -0
- package/dist/modules/cpq/api/orders/[orderId]/status/route.js +38 -0
- package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +7 -0
- package/dist/modules/cpq/api/orders/route.js +71 -0
- package/dist/modules/cpq/api/orders/route.js.map +7 -0
- package/dist/modules/cpq/api/price-rules/route.js +180 -0
- package/dist/modules/cpq/api/price-rules/route.js.map +7 -0
- package/dist/modules/cpq/api/pricing-table-entries/route.js +142 -0
- package/dist/modules/cpq/api/pricing-table-entries/route.js.map +7 -0
- package/dist/modules/cpq/api/pricing-tables/route.js +179 -0
- package/dist/modules/cpq/api/pricing-tables/route.js.map +7 -0
- package/dist/modules/cpq/api/product-attributes/route.js +160 -0
- package/dist/modules/cpq/api/product-attributes/route.js.map +7 -0
- package/dist/modules/cpq/api/product-charges/route.js +166 -0
- package/dist/modules/cpq/api/product-charges/route.js.map +7 -0
- package/dist/modules/cpq/api/product-offerings/route.js +294 -0
- package/dist/modules/cpq/api/product-offerings/route.js.map +7 -0
- package/dist/modules/cpq/api/product-relationships/route.js +144 -0
- package/dist/modules/cpq/api/product-relationships/route.js.map +7 -0
- package/dist/modules/cpq/api/product-specifications/route.js +292 -0
- package/dist/modules/cpq/api/product-specifications/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js +71 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +29 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +54 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js +72 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +35 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/route.js +102 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +27 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js +79 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js +95 -0
- package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/attributes/route.js +52 -0
- package/dist/modules/cpq/api/quotes/attributes/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/from-subscription/route.js +72 -0
- package/dist/modules/cpq/api/quotes/from-subscription/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/price/route.js +69 -0
- package/dist/modules/cpq/api/quotes/price/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/route.js +97 -0
- package/dist/modules/cpq/api/quotes/route.js.map +7 -0
- package/dist/modules/cpq/api/quotes/validate-relationships/route.js +86 -0
- package/dist/modules/cpq/api/quotes/validate-relationships/route.js.map +7 -0
- package/dist/modules/cpq/api/resolveQuotingService.js +7 -0
- package/dist/modules/cpq/api/resolveQuotingService.js.map +7 -0
- package/dist/modules/cpq/api/wizards/resolve/route.js +38 -0
- package/dist/modules/cpq/api/wizards/resolve/route.js.map +7 -0
- package/dist/modules/cpq/api/wizards/route.js +111 -0
- package/dist/modules/cpq/api/wizards/route.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +136 -0
- package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +185 -0
- package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +343 -0
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +13 -0
- package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/page.js +35 -0
- package/dist/modules/cpq/backend/cpq/inventory/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/shared.js +20 -0
- package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +794 -0
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +13 -0
- package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +917 -0
- package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/offerings/page.js +405 -0
- package/dist/modules/cpq/backend/cpq/offerings/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/offerings/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/offerings/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +677 -0
- package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/orders/page.js +134 -0
- package/dist/modules/cpq/backend/cpq/orders/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +272 -0
- package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +407 -0
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +13 -0
- package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/price-rules/page.js +211 -0
- package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +275 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +112 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +492 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js +50 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +249 -0
- package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/page.js +87 -0
- package/dist/modules/cpq/backend/cpq/pricing/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/pricing/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/pricing/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +652 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +1289 -0
- package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/page.js +283 -0
- package/dist/modules/cpq/backend/cpq/quotes/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +214 -0
- package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +1399 -0
- package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/specifications/page.js +148 -0
- package/dist/modules/cpq/backend/cpq/specifications/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/specifications/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/specifications/page.meta.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +204 -0
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +191 -0
- package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/wizards/page.js +119 -0
- package/dist/modules/cpq/backend/cpq/wizards/page.js.map +7 -0
- package/dist/modules/cpq/backend/cpq/wizards/page.meta.js +18 -0
- package/dist/modules/cpq/backend/cpq/wizards/page.meta.js.map +7 -0
- package/dist/modules/cpq/ce.js +151 -0
- package/dist/modules/cpq/ce.js.map +7 -0
- package/dist/modules/cpq/cli.js +11 -0
- package/dist/modules/cpq/cli.js.map +7 -0
- package/dist/modules/cpq/components/CpqListView.js +265 -0
- package/dist/modules/cpq/components/CpqListView.js.map +7 -0
- package/dist/modules/cpq/components/NumberInput.js +95 -0
- package/dist/modules/cpq/components/NumberInput.js.map +7 -0
- package/dist/modules/cpq/components/StatusPath.js +60 -0
- package/dist/modules/cpq/components/StatusPath.js.map +7 -0
- package/dist/modules/cpq/components/statusMaps.js +66 -0
- package/dist/modules/cpq/components/statusMaps.js.map +7 -0
- package/dist/modules/cpq/components/useCpqRowActions.js +125 -0
- package/dist/modules/cpq/components/useCpqRowActions.js.map +7 -0
- package/dist/modules/cpq/data/entities.js +1556 -0
- package/dist/modules/cpq/data/entities.js.map +7 -0
- package/dist/modules/cpq/data/validators.js +575 -0
- package/dist/modules/cpq/data/validators.js.map +7 -0
- package/dist/modules/cpq/di.js +57 -0
- package/dist/modules/cpq/di.js.map +7 -0
- package/dist/modules/cpq/events.js +50 -0
- package/dist/modules/cpq/events.js.map +7 -0
- package/dist/modules/cpq/index.js +16 -0
- package/dist/modules/cpq/index.js.map +7 -0
- package/dist/modules/cpq/lib/seeds/api.js +32 -0
- package/dist/modules/cpq/lib/seeds/api.js.map +7 -0
- package/dist/modules/cpq/lib/seeds/tenant-provisioning.js +158 -0
- package/dist/modules/cpq/lib/seeds/tenant-provisioning.js.map +7 -0
- package/dist/modules/cpq/lib/seeds.js +6 -0
- package/dist/modules/cpq/lib/seeds.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js +40 -0
- package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js +54 -0
- package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js +15 -0
- package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js +62 -0
- package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js +56 -0
- package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js +26 -0
- package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js +37 -0
- package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js +35 -0
- package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js +21 -0
- package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js +46 -0
- package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js +118 -0
- package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js +63 -0
- package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js +28 -0
- package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js +36 -0
- package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js +14 -0
- package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js +14 -0
- package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js +58 -0
- package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js.map +7 -0
- package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js +29 -0
- package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js.map +7 -0
- package/dist/modules/cpq/services/cpqBundleService.js +447 -0
- package/dist/modules/cpq/services/cpqBundleService.js.map +7 -0
- package/dist/modules/cpq/services/cpqInventoryService.js +1371 -0
- package/dist/modules/cpq/services/cpqInventoryService.js.map +7 -0
- package/dist/modules/cpq/services/cpqOrderService.js +901 -0
- package/dist/modules/cpq/services/cpqOrderService.js.map +7 -0
- package/dist/modules/cpq/services/cpqPricingService.js +289 -0
- package/dist/modules/cpq/services/cpqPricingService.js.map +7 -0
- package/dist/modules/cpq/services/cpqProductService.js +204 -0
- package/dist/modules/cpq/services/cpqProductService.js.map +7 -0
- package/dist/modules/cpq/services/cpqQuotingService.js +1867 -0
- package/dist/modules/cpq/services/cpqQuotingService.js.map +7 -0
- package/dist/modules/cpq/services/cpqValidationService.js +234 -0
- package/dist/modules/cpq/services/cpqValidationService.js.map +7 -0
- package/dist/modules/cpq/services/cpqWizardService.js +137 -0
- package/dist/modules/cpq/services/cpqWizardService.js.map +7 -0
- package/dist/modules/cpq/services/types.js +114 -0
- package/dist/modules/cpq/services/types.js.map +7 -0
- package/dist/modules/cpq/setup.js +49 -0
- package/dist/modules/cpq/setup.js.map +7 -0
- package/dist/modules/cpq/subscribers/subscription-amended-log.js +18 -0
- package/dist/modules/cpq/subscribers/subscription-amended-log.js.map +7 -0
- package/dist/modules/cpq/subscribers/subscription-cancelled-log.js +19 -0
- package/dist/modules/cpq/subscribers/subscription-cancelled-log.js.map +7 -0
- package/dist/modules/cpq/subscribers/subscription-merged-log.js +20 -0
- package/dist/modules/cpq/subscribers/subscription-merged-log.js.map +7 -0
- package/dist/modules/cpq/subscribers/subscription-renewed-log.js +18 -0
- package/dist/modules/cpq/subscribers/subscription-renewed-log.js.map +7 -0
- package/dist/modules/cpq/subscribers/subscription-superseded-log.js +18 -0
- package/dist/modules/cpq/subscribers/subscription-superseded-log.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +192 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js +17 -0
- package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +79 -0
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js +17 -0
- package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +181 -0
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +7 -0
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js +17 -0
- package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js.map +7 -0
- package/dist/modules/cpq/widgets/injection-table.js +44 -0
- package/dist/modules/cpq/widgets/injection-table.js.map +7 -0
- package/dist/modules/cpq/workflows/WizardRunner.js +113 -0
- package/dist/modules/cpq/workflows/WizardRunner.js.map +7 -0
- package/dist/modules/cpq/workflows/WizardTrigger.js +116 -0
- package/dist/modules/cpq/workflows/WizardTrigger.js.map +7 -0
- package/dist/modules/cpq/workflows/engine.js +188 -0
- package/dist/modules/cpq/workflows/engine.js.map +7 -0
- package/dist/modules/cpq/workflows/registry.js +16 -0
- package/dist/modules/cpq/workflows/registry.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +119 -0
- package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +24 -0
- package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/ItemListStep.js +115 -0
- package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +327 -0
- package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +231 -0
- package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/ReviewStep.js +152 -0
- package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +7 -0
- package/dist/modules/cpq/workflows/steps/index.js +49 -0
- package/dist/modules/cpq/workflows/steps/index.js.map +7 -0
- package/dist/modules/cpq/workflows/types.js +1 -0
- package/dist/modules/cpq/workflows/types.js.map +7 -0
- package/package.json +150 -0
- package/src/modules/cpq/__integration__/TC-CPQ-001.spec.ts +177 -0
- package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +123 -0
- package/src/modules/cpq/__integration__/TC-CPQ-003.spec.ts +115 -0
- package/src/modules/cpq/__integration__/TC-CPQ-004.spec.ts +184 -0
- package/src/modules/cpq/__integration__/TC-CPQ-005.spec.ts +43 -0
- package/src/modules/cpq/__integration__/TC-CPQ-006.spec.ts +87 -0
- package/src/modules/cpq/__integration__/TC-CPQ-007.spec.ts +79 -0
- package/src/modules/cpq/__integration__/TC-CPQ-008.spec.ts +55 -0
- package/src/modules/cpq/__integration__/TC-CPQ-009.spec.ts +45 -0
- package/src/modules/cpq/__integration__/TC-CPQ-010.spec.ts +54 -0
- package/src/modules/cpq/__integration__/TC-CPQ-011.spec.ts +24 -0
- package/src/modules/cpq/__integration__/TC-CPQ-012.spec.ts +62 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-001.spec.ts +52 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-002.spec.ts +65 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-003.spec.ts +107 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-004.spec.ts +74 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-005.spec.ts +86 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-006.spec.ts +79 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-007.spec.ts +63 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-008.spec.ts +62 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-009.spec.ts +62 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-010.spec.ts +59 -0
- package/src/modules/cpq/__integration__/TC-CPQ-ARC-011.spec.ts +128 -0
- package/src/modules/cpq/__integration__/_helpers/cpqArcFixtures.ts +208 -0
- package/src/modules/cpq/__tests__/package-isolation.test.ts +232 -0
- package/src/modules/cpq/acl.ts +26 -0
- package/src/modules/cpq/api/_helpers/__tests__/handleArcError.test.ts +117 -0
- package/src/modules/cpq/api/_helpers/handleArcError.ts +81 -0
- package/src/modules/cpq/api/attribute-options/route.ts +55 -0
- package/src/modules/cpq/api/bundle-slots/route.ts +112 -0
- package/src/modules/cpq/api/bundle-tree/route.ts +31 -0
- package/src/modules/cpq/api/context.ts +39 -0
- package/src/modules/cpq/api/inventory/assets/route.ts +123 -0
- package/src/modules/cpq/api/inventory/assets/status/route.ts +34 -0
- package/src/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.ts +30 -0
- package/src/modules/cpq/api/inventory/customer/[customerId]/route.ts +31 -0
- package/src/modules/cpq/api/inventory/subscription-items/route.ts +107 -0
- package/src/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.ts +64 -0
- package/src/modules/cpq/api/inventory/subscriptions/expiring/route.ts +66 -0
- package/src/modules/cpq/api/inventory/subscriptions/route.ts +126 -0
- package/src/modules/cpq/api/inventory/subscriptions/status/route.ts +34 -0
- package/src/modules/cpq/api/offering-components/route.ts +103 -0
- package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +48 -0
- package/src/modules/cpq/api/orders/[orderId]/route.ts +28 -0
- package/src/modules/cpq/api/orders/[orderId]/status/route.ts +38 -0
- package/src/modules/cpq/api/orders/route.ts +77 -0
- package/src/modules/cpq/api/price-rules/route.ts +210 -0
- package/src/modules/cpq/api/pricing-table-entries/route.ts +159 -0
- package/src/modules/cpq/api/pricing-tables/route.ts +202 -0
- package/src/modules/cpq/api/product-attributes/route.ts +175 -0
- package/src/modules/cpq/api/product-charges/route.ts +181 -0
- package/src/modules/cpq/api/product-offerings/route.ts +329 -0
- package/src/modules/cpq/api/product-relationships/route.ts +156 -0
- package/src/modules/cpq/api/product-specifications/route.ts +325 -0
- package/src/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.ts +77 -0
- package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +28 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +55 -0
- package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +26 -0
- package/src/modules/cpq/api/quotes/[quoteId]/merge-meta/route.ts +78 -0
- package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +34 -0
- package/src/modules/cpq/api/quotes/[quoteId]/route.ts +109 -0
- package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +26 -0
- package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.ts +91 -0
- package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.ts +107 -0
- package/src/modules/cpq/api/quotes/attributes/route.ts +61 -0
- package/src/modules/cpq/api/quotes/from-subscription/route.ts +85 -0
- package/src/modules/cpq/api/quotes/price/route.ts +81 -0
- package/src/modules/cpq/api/quotes/route.ts +114 -0
- package/src/modules/cpq/api/quotes/validate-relationships/route.ts +100 -0
- package/src/modules/cpq/api/resolveQuotingService.ts +11 -0
- package/src/modules/cpq/api/wizards/resolve/route.ts +39 -0
- package/src/modules/cpq/api/wizards/route.ts +127 -0
- package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +166 -0
- package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +251 -0
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +9 -0
- package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +440 -0
- package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/inventory/page.tsx +49 -0
- package/src/modules/cpq/backend/cpq/inventory/shared.tsx +32 -0
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +9 -0
- package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +1085 -0
- package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +1165 -0
- package/src/modules/cpq/backend/cpq/offerings/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/offerings/page.tsx +468 -0
- package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +921 -0
- package/src/modules/cpq/backend/cpq/orders/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/orders/page.tsx +182 -0
- package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +310 -0
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +9 -0
- package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +472 -0
- package/src/modules/cpq/backend/cpq/price-rules/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/price-rules/page.tsx +253 -0
- package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +431 -0
- package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +122 -0
- package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +588 -0
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.ts +90 -0
- package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +338 -0
- package/src/modules/cpq/backend/cpq/pricing/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/pricing/page.tsx +111 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +737 -0
- package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +1721 -0
- package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/quotes/page.tsx +370 -0
- package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +300 -0
- package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +1722 -0
- package/src/modules/cpq/backend/cpq/specifications/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/specifications/page.tsx +191 -0
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +319 -0
- package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +234 -0
- package/src/modules/cpq/backend/cpq/wizards/page.meta.ts +16 -0
- package/src/modules/cpq/backend/cpq/wizards/page.tsx +141 -0
- package/src/modules/cpq/ce.ts +150 -0
- package/src/modules/cpq/cli.ts +10 -0
- package/src/modules/cpq/components/CpqListView.tsx +431 -0
- package/src/modules/cpq/components/NumberInput.tsx +166 -0
- package/src/modules/cpq/components/StatusPath.tsx +116 -0
- package/src/modules/cpq/components/statusMaps.ts +148 -0
- package/src/modules/cpq/components/useCpqRowActions.tsx +183 -0
- package/src/modules/cpq/data/__tests__/validators.arc.test.ts +271 -0
- package/src/modules/cpq/data/__tests__/validators.test.ts +782 -0
- package/src/modules/cpq/data/entities.ts +1467 -0
- package/src/modules/cpq/data/validators.ts +663 -0
- package/src/modules/cpq/di.ts +70 -0
- package/src/modules/cpq/events.ts +62 -0
- package/src/modules/cpq/index.ts +15 -0
- package/src/modules/cpq/lib/seeds/__tests__/api.test.ts +86 -0
- package/src/modules/cpq/lib/seeds/__tests__/tenant-provisioning.test.ts +349 -0
- package/src/modules/cpq/lib/seeds/api.ts +119 -0
- package/src/modules/cpq/lib/seeds/tenant-provisioning.ts +239 -0
- package/src/modules/cpq/lib/seeds.ts +18 -0
- package/src/modules/cpq/migrations/.snapshot-open-mercato.json +7473 -0
- package/src/modules/cpq/migrations/Migration20260220220000_cpq.ts +51 -0
- package/src/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.ts +70 -0
- package/src/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.ts +15 -0
- package/src/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.ts +73 -0
- package/src/modules/cpq/migrations/Migration20260402000000_cpq_workflows.ts +59 -0
- package/src/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.ts +30 -0
- package/src/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.ts +36 -0
- package/src/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.ts +33 -0
- package/src/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.ts +22 -0
- package/src/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.ts +56 -0
- package/src/modules/cpq/migrations/Migration20260410100000_cpq_inventory.ts +118 -0
- package/src/modules/cpq/migrations/Migration20260411000000_cpq_orders.ts +62 -0
- package/src/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.ts +33 -0
- package/src/modules/cpq/migrations/Migration20260504064621_cpq_arc.ts +68 -0
- package/src/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.ts +30 -0
- package/src/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.ts +27 -0
- package/src/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.ts +82 -0
- package/src/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.ts +52 -0
- package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +59 -0
- package/src/modules/cpq/services/__tests__/cpqBundleService.test.ts +661 -0
- package/src/modules/cpq/services/__tests__/cpqInventoryService.arc.test.ts +357 -0
- package/src/modules/cpq/services/__tests__/cpqInventoryService.test.ts +718 -0
- package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +603 -0
- package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +1012 -0
- package/src/modules/cpq/services/__tests__/cpqProductService.test.ts +603 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +143 -0
- package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +658 -0
- package/src/modules/cpq/services/__tests__/cpqValidationService.test.ts +811 -0
- package/src/modules/cpq/services/__tests__/cpqWizardService.test.ts +389 -0
- package/src/modules/cpq/services/__tests__/types.test.ts +359 -0
- package/src/modules/cpq/services/cpqBundleService.ts +671 -0
- package/src/modules/cpq/services/cpqInventoryService.ts +1929 -0
- package/src/modules/cpq/services/cpqOrderService.ts +1230 -0
- package/src/modules/cpq/services/cpqPricingService.ts +412 -0
- package/src/modules/cpq/services/cpqProductService.ts +311 -0
- package/src/modules/cpq/services/cpqQuotingService.ts +2638 -0
- package/src/modules/cpq/services/cpqValidationService.ts +323 -0
- package/src/modules/cpq/services/cpqWizardService.ts +204 -0
- package/src/modules/cpq/services/types.ts +303 -0
- package/src/modules/cpq/setup.ts +63 -0
- package/src/modules/cpq/subscribers/subscription-amended-log.ts +20 -0
- package/src/modules/cpq/subscribers/subscription-cancelled-log.ts +19 -0
- package/src/modules/cpq/subscribers/subscription-merged-log.ts +22 -0
- package/src/modules/cpq/subscribers/subscription-renewed-log.ts +18 -0
- package/src/modules/cpq/subscribers/subscription-superseded-log.ts +19 -0
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +339 -0
- package/src/modules/cpq/widgets/injection/customer-inventory/widget.ts +16 -0
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +96 -0
- package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.ts +16 -0
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +265 -0
- package/src/modules/cpq/widgets/injection/quote-configurator/widget.ts +16 -0
- package/src/modules/cpq/widgets/injection-table.ts +42 -0
- package/src/modules/cpq/workflows/WizardRunner.tsx +158 -0
- package/src/modules/cpq/workflows/WizardTrigger.tsx +142 -0
- package/src/modules/cpq/workflows/engine.tsx +278 -0
- package/src/modules/cpq/workflows/registry.ts +15 -0
- package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +136 -0
- package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +34 -0
- package/src/modules/cpq/workflows/steps/ItemListStep.tsx +146 -0
- package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +438 -0
- package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +287 -0
- package/src/modules/cpq/workflows/steps/ReviewStep.tsx +219 -0
- package/src/modules/cpq/workflows/steps/index.ts +51 -0
- package/src/modules/cpq/workflows/types.ts +102 -0
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/cpq/workflows/steps/ProductConfigureStep.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\nimport type { WizardStepProps } from '../types'\nimport { NumberInput } from '../../components/NumberInput'\n\ntype ConstrainedAttribute = {\n code: string\n name: string\n attributeType: string\n isRequired: boolean\n options: Array<{ value: string; label: string }> | null\n constraints: Record<string, unknown> | null\n defaultValue: unknown | null\n helpText: string | null\n}\n\ntype AttributeGroup = {\n label: string\n attributeCodes: string[]\n}\n\nfunction resolveOfferingId(config: Record<string, unknown>, stepData: Record<string, Record<string, unknown>>): string | null {\n // Check explicit source\n const source = config.offeringSource as string | undefined\n if (source && stepData[source]) {\n return (stepData[source].offeringId as string) ?? null\n }\n // Auto-detect: find first step data with offeringId\n for (const data of Object.values(stepData)) {\n if (data.offeringId) return data.offeringId as string\n }\n return null\n}\n\nexport function ProductConfigureStep({ config, onComplete, onBack, stepData, currentStepId, completedSteps, quoteId, customerId }: WizardStepProps) {\n const [attributes, setAttributes] = useState<ConstrainedAttribute[]>([])\n const [values, setValues] = useState<Record<string, unknown>>({})\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [resolvedOfferingId, setResolvedOfferingId] = useState<string | null>(null)\n\n const directOfferingId = resolveOfferingId(config, stepData)\n const offeringCode = config.offeringCode as string | undefined\n const attributeGroups = config.attributeGroups as AttributeGroup[] | undefined\n\n // Resolve offering by code if no direct offeringId\n useEffect(() => {\n if (directOfferingId) {\n setResolvedOfferingId(directOfferingId)\n return\n }\n if (!offeringCode) return\n async function resolve() {\n try {\n const res = await fetch(`/api/cpq/product-offerings?code=${encodeURIComponent(offeringCode!)}`)\n if (res.ok) {\n const data = await res.json()\n const items = data.items ?? data\n if (Array.isArray(items) && items.length > 0) {\n setResolvedOfferingId(items[0].id)\n }\n }\n } catch { /* ignore */ }\n }\n resolve()\n }, [directOfferingId, offeringCode])\n\n const offeringId = resolvedOfferingId\n const hiddenAttributes = (config.hiddenAttributes as string[]) ?? []\n const readonlyAttributes = (config.readonlyAttributes as string[]) ?? []\n\n // Restore previous values\n const previousData = stepData[currentStepId] as { configuration?: Record<string, unknown> } | undefined\n\n useEffect(() => {\n async function loadAttributes() {\n if (!offeringId) { setLoading(false); return }\n setLoading(true)\n try {\n const params = new URLSearchParams({ offeringId })\n if (Object.keys(values).length > 0) {\n params.set('configuration', JSON.stringify(values))\n }\n const res = await fetch(`/api/cpq/quotes/attributes?${params}`)\n if (res.ok) {\n const data = await res.json()\n const attrs = (data.attributes ?? []) as ConstrainedAttribute[]\n setAttributes(attrs.filter((a) => !hiddenAttributes.includes(a.code)))\n\n // Set defaults on first load\n if (Object.keys(values).length === 0) {\n const defaults: Record<string, unknown> = previousData?.configuration ?? {}\n for (const attr of attrs) {\n if (defaults[attr.code] === undefined && attr.defaultValue != null) {\n defaults[attr.code] = attr.defaultValue\n }\n }\n setValues(defaults)\n }\n }\n } finally {\n setLoading(false)\n }\n }\n loadAttributes()\n }, [offeringId]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleChange = useCallback((code: string, value: unknown) => {\n setValues((prev) => ({ ...prev, [code]: value }))\n }, [])\n\n // Resolve customerId from props or stepData\n const resolvedCustomerId = customerId ?? (() => {\n for (const data of Object.values(stepData)) {\n if (data.customerId) return data.customerId as string\n }\n return null\n })()\n\n const handleComplete = useCallback(async () => {\n setSaving(true)\n try {\n let activeQuoteId = quoteId\n\n // Auto-create a quote if none exists yet\n if (!activeQuoteId && resolvedCustomerId && offeringId) {\n const createRes = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: resolvedCustomerId }),\n })\n if (createRes.ok) {\n const quoteData = await createRes.json()\n activeQuoteId = quoteData.id\n }\n }\n\n if (activeQuoteId && offeringId) {\n // Check if there's already a line for this offering\n const existingLineId = (stepData[currentStepId] as { lineId?: string })?.lineId\n\n if (existingLineId) {\n await fetch(`/api/cpq/quotes/${activeQuoteId}/items/${existingLineId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ configuration: values }),\n })\n onComplete({ configuration: values, offeringId, lineId: existingLineId, quoteId: activeQuoteId })\n } else {\n const res = await fetch(`/api/cpq/quotes/${activeQuoteId}/items`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ offeringId, configuration: values }),\n })\n if (res.ok) {\n const data = await res.json()\n const line = data.lines?.find((l: { offeringId: string }) => l.offeringId === offeringId)\n onComplete({ configuration: values, offeringId, lineId: line?.lineId, quoteId: activeQuoteId })\n } else {\n onComplete({ configuration: values, offeringId, quoteId: activeQuoteId })\n }\n }\n } else {\n onComplete({ configuration: values, offeringId })\n }\n } finally {\n setSaving(false)\n }\n }, [values, quoteId, offeringId, stepData, currentStepId, onComplete, resolvedCustomerId])\n\n const renderAttribute = (attr: ConstrainedAttribute) => {\n const isReadonly = readonlyAttributes.includes(attr.code)\n const value = values[attr.code] ?? ''\n\n if (attr.attributeType === 'select' || attr.attributeType === 'enum') {\n return (\n <select\n value={String(value)}\n onChange={(e) => handleChange(attr.code, e.target.value)}\n disabled={isReadonly}\n className=\"w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50\"\n >\n <option value=\"\">Select...</option>\n {attr.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )\n }\n\n if (attr.attributeType === 'boolean') {\n return (\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => handleChange(attr.code, e.target.checked)}\n disabled={isReadonly}\n className=\"rounded border\"\n />\n {attr.name}\n </label>\n )\n }\n\n if (attr.attributeType === 'number') {\n return (\n <NumberInput\n value={value === '' || value == null ? null : Number(value)}\n onChange={(n) => handleChange(attr.code, n ?? '')}\n disabled={isReadonly}\n />\n )\n }\n\n return (\n <input\n type=\"text\"\n value={String(value)}\n onChange={(e) => handleChange(attr.code, e.target.value)}\n disabled={isReadonly}\n className=\"w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50\"\n />\n )\n }\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">Loading attributes...</div>\n }\n\n if (!offeringId) {\n return (\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n No offering selected. Please go back and select an offering.\n </div>\n )\n }\n\n const renderGroup = (label: string, attrs: ConstrainedAttribute[]) => (\n <div key={label} className=\"space-y-3\">\n <h4 className=\"text-sm font-medium border-b pb-2\">{label}</h4>\n <div className=\"space-y-3\">\n {attrs.map((attr) => (\n <div key={attr.code}>\n <label className=\"block text-sm font-medium mb-1\">\n {attr.name}\n {attr.isRequired && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n {renderAttribute(attr)}\n {attr.helpText && <p className=\"text-xs text-muted-foreground mt-1\">{attr.helpText}</p>}\n </div>\n ))}\n </div>\n </div>\n )\n\n return (\n <div className=\"space-y-4\">\n {attributeGroups?.length ? (\n attributeGroups.map((group) => {\n const groupAttrs = attributes.filter((a) => group.attributeCodes.includes(a.code))\n if (groupAttrs.length === 0) return null\n return renderGroup(group.label, groupAttrs)\n })\n ) : (\n renderGroup('Configuration', attributes)\n )}\n\n <div className=\"flex gap-2 justify-end pt-2\">\n {completedSteps.length > 0 && (\n <button type=\"button\" onClick={onBack} className=\"inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted\">\n Back\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleComplete}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {saving ? 'Saving...' : 'Next'}\n </button>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
|
+
import React, { useState, useEffect, useCallback } from "react";
|
|
4
|
+
function fmt(n, currency = "USD") {
|
|
5
|
+
return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(n);
|
|
6
|
+
}
|
|
7
|
+
function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId }) {
|
|
8
|
+
const [quote, setQuote] = useState(null);
|
|
9
|
+
const [loading, setLoading] = useState(true);
|
|
10
|
+
const [submitting, setSubmitting] = useState(false);
|
|
11
|
+
const showChargeBreakdown = config.showChargeBreakdown !== false;
|
|
12
|
+
const showAdjustments = config.showAdjustments !== false;
|
|
13
|
+
const submitAction = config.submitAction ?? "save";
|
|
14
|
+
const submitLabel = config.submitLabel ?? "Complete";
|
|
15
|
+
const loadQuote = useCallback(async () => {
|
|
16
|
+
if (!quoteId) {
|
|
17
|
+
setLoading(false);
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
setLoading(true);
|
|
21
|
+
try {
|
|
22
|
+
const res = await fetch(`/api/cpq/quotes/${quoteId}`);
|
|
23
|
+
if (res.ok) {
|
|
24
|
+
setQuote(await res.json());
|
|
25
|
+
}
|
|
26
|
+
} finally {
|
|
27
|
+
setLoading(false);
|
|
28
|
+
}
|
|
29
|
+
}, [quoteId]);
|
|
30
|
+
useEffect(() => {
|
|
31
|
+
loadQuote();
|
|
32
|
+
}, [loadQuote]);
|
|
33
|
+
const handleSubmit = useCallback(async () => {
|
|
34
|
+
setSubmitting(true);
|
|
35
|
+
try {
|
|
36
|
+
if (quoteId && submitAction !== "none") {
|
|
37
|
+
const targetStatus = submitAction === "submit_for_approval" ? "in_approval" : submitAction === "send_to_customer" ? "with_customer" : void 0;
|
|
38
|
+
if (targetStatus) {
|
|
39
|
+
await fetch(`/api/cpq/quotes/${quoteId}/status`, {
|
|
40
|
+
method: "POST",
|
|
41
|
+
headers: { "Content-Type": "application/json" },
|
|
42
|
+
body: JSON.stringify({ targetStatus })
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
onComplete({
|
|
47
|
+
quoteId,
|
|
48
|
+
action: submitAction,
|
|
49
|
+
nrcTotal: quote?.pricingSummary.nrcTotal ?? 0,
|
|
50
|
+
mrcTotal: quote?.pricingSummary.mrcTotal ?? 0
|
|
51
|
+
});
|
|
52
|
+
} finally {
|
|
53
|
+
setSubmitting(false);
|
|
54
|
+
}
|
|
55
|
+
}, [quoteId, submitAction, quote, onComplete]);
|
|
56
|
+
if (loading) {
|
|
57
|
+
return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Loading quote summary..." });
|
|
58
|
+
}
|
|
59
|
+
if (!quote) {
|
|
60
|
+
return /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No quote data available. Please go back and configure items." });
|
|
61
|
+
}
|
|
62
|
+
const { pricingSummary, lines } = quote;
|
|
63
|
+
return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
|
|
64
|
+
/* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-muted/30 p-4 space-y-3", children: [
|
|
65
|
+
/* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: "Quote Summary" }),
|
|
66
|
+
/* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
|
|
67
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
68
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "NRC Total" }),
|
|
69
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: fmt(pricingSummary.nrcTotal, pricingSummary.currencyCode) })
|
|
70
|
+
] }),
|
|
71
|
+
/* @__PURE__ */ jsxs("div", { children: [
|
|
72
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "MRC Total" }),
|
|
73
|
+
/* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: fmt(pricingSummary.mrcTotal, pricingSummary.currencyCode) })
|
|
74
|
+
] }),
|
|
75
|
+
pricingSummary.discountTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
76
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Discounts" }),
|
|
77
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
78
|
+
"-",
|
|
79
|
+
fmt(pricingSummary.discountTotal, pricingSummary.currencyCode)
|
|
80
|
+
] })
|
|
81
|
+
] }),
|
|
82
|
+
pricingSummary.surchargeTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
|
|
83
|
+
/* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Surcharges" }),
|
|
84
|
+
/* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
|
|
85
|
+
"+",
|
|
86
|
+
fmt(pricingSummary.surchargeTotal, pricingSummary.currencyCode)
|
|
87
|
+
] })
|
|
88
|
+
] })
|
|
89
|
+
] })
|
|
90
|
+
] }),
|
|
91
|
+
/* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
|
|
92
|
+
/* @__PURE__ */ jsxs("h4", { className: "text-sm font-medium", children: [
|
|
93
|
+
"Items (",
|
|
94
|
+
lines.length,
|
|
95
|
+
")"
|
|
96
|
+
] }),
|
|
97
|
+
/* @__PURE__ */ jsx("div", { className: "space-y-2", children: lines.map((line) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-3 space-y-2", children: [
|
|
98
|
+
/* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
|
|
99
|
+
/* @__PURE__ */ jsx("strong", { className: "text-sm", children: line.offeringName }),
|
|
100
|
+
/* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
|
|
101
|
+
fmt(line.nrcTotal, pricingSummary.currencyCode),
|
|
102
|
+
" NRC / ",
|
|
103
|
+
fmt(line.mrcTotal, pricingSummary.currencyCode),
|
|
104
|
+
" MRC"
|
|
105
|
+
] })
|
|
106
|
+
] }),
|
|
107
|
+
line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
|
|
108
|
+
"Quantity: ",
|
|
109
|
+
line.quantity
|
|
110
|
+
] }),
|
|
111
|
+
showChargeBreakdown && line.charges?.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
|
|
112
|
+
/* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/30", children: [
|
|
113
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium", children: "Charge" }),
|
|
114
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium", children: "Type" }),
|
|
115
|
+
/* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-right font-medium", children: "Price" })
|
|
116
|
+
] }) }),
|
|
117
|
+
/* @__PURE__ */ jsx("tbody", { children: line.charges.map((charge) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
|
|
118
|
+
/* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0", children: [
|
|
119
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-1.5", children: charge.chargeName }),
|
|
120
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 uppercase", children: charge.chargeType }),
|
|
121
|
+
/* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right", children: charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : `${fmt(charge.unitPrice, charge.currencyCode)}/unit` })
|
|
122
|
+
] }),
|
|
123
|
+
showAdjustments && charge.adjustments?.map((adj) => /* @__PURE__ */ jsxs("tr", { className: "text-[11px] text-muted-foreground", children: [
|
|
124
|
+
/* @__PURE__ */ jsx("td", { colSpan: 2, className: "px-3 py-0.5 pl-6", children: adj.ruleName }),
|
|
125
|
+
/* @__PURE__ */ jsxs("td", { className: "px-3 py-0.5 text-right", children: [
|
|
126
|
+
adj.delta < 0 ? "" : "+",
|
|
127
|
+
fmt(adj.delta, charge.currencyCode)
|
|
128
|
+
] })
|
|
129
|
+
] }, adj.ruleCode))
|
|
130
|
+
] }, charge.chargeCode)) })
|
|
131
|
+
] }) })
|
|
132
|
+
] }, line.lineId)) })
|
|
133
|
+
] }),
|
|
134
|
+
/* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end pt-2", children: [
|
|
135
|
+
completedSteps.length > 0 && /* @__PURE__ */ jsx("button", { type: "button", onClick: onBack, className: "inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted", children: "Back" }),
|
|
136
|
+
/* @__PURE__ */ jsx(
|
|
137
|
+
"button",
|
|
138
|
+
{
|
|
139
|
+
type: "button",
|
|
140
|
+
onClick: handleSubmit,
|
|
141
|
+
disabled: submitting,
|
|
142
|
+
className: "inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
|
|
143
|
+
children: submitting ? "Submitting..." : submitLabel
|
|
144
|
+
}
|
|
145
|
+
)
|
|
146
|
+
] })
|
|
147
|
+
] });
|
|
148
|
+
}
|
|
149
|
+
export {
|
|
150
|
+
ReviewStep
|
|
151
|
+
};
|
|
152
|
+
//# sourceMappingURL=ReviewStep.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/cpq/workflows/steps/ReviewStep.tsx"],
|
|
4
|
+
"sourcesContent": ["'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\nimport type { WizardStepProps } from '../types'\n\ntype ResolvedCharge = {\n chargeCode: string\n chargeName: string\n chargeType: string\n unitPrice: number\n totalPrice: number | null\n currencyCode: string\n adjustments?: Array<{\n ruleCode: string\n ruleName: string\n ruleType: string\n delta: number\n }> | null\n}\n\ntype QuoteLine = {\n lineId: string\n offeringName: string\n quantity: number\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n isConfigured: boolean\n}\n\ntype QuoteData = {\n id: string\n quoteNumber: string\n cpqStatus: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n discountTotal: number\n surchargeTotal: number\n currencyCode: string\n }\n lines: QuoteLine[]\n}\n\nfunction fmt(n: number, currency = 'USD') {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(n)\n}\n\nexport function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId }: WizardStepProps) {\n const [quote, setQuote] = useState<QuoteData | null>(null)\n const [loading, setLoading] = useState(true)\n const [submitting, setSubmitting] = useState(false)\n\n const showChargeBreakdown = config.showChargeBreakdown !== false\n const showAdjustments = config.showAdjustments !== false\n const submitAction = (config.submitAction as string) ?? 'save'\n const submitLabel = (config.submitLabel as string) ?? 'Complete'\n\n const loadQuote = useCallback(async () => {\n if (!quoteId) { setLoading(false); return }\n setLoading(true)\n try {\n const res = await fetch(`/api/cpq/quotes/${quoteId}`)\n if (res.ok) {\n setQuote(await res.json())\n }\n } finally {\n setLoading(false)\n }\n }, [quoteId])\n\n useEffect(() => { loadQuote() }, [loadQuote])\n\n const handleSubmit = useCallback(async () => {\n setSubmitting(true)\n try {\n if (quoteId && submitAction !== 'none') {\n const targetStatus =\n submitAction === 'submit_for_approval' ? 'in_approval'\n : submitAction === 'send_to_customer' ? 'with_customer'\n : undefined\n\n if (targetStatus) {\n await fetch(`/api/cpq/quotes/${quoteId}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus }),\n })\n }\n }\n\n onComplete({\n quoteId,\n action: submitAction,\n nrcTotal: quote?.pricingSummary.nrcTotal ?? 0,\n mrcTotal: quote?.pricingSummary.mrcTotal ?? 0,\n })\n } finally {\n setSubmitting(false)\n }\n }, [quoteId, submitAction, quote, onComplete])\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">Loading quote summary...</div>\n }\n\n if (!quote) {\n return (\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n No quote data available. Please go back and configure items.\n </div>\n )\n }\n\n const { pricingSummary, lines } = quote\n\n return (\n <div className=\"space-y-5\">\n {/* Quote summary */}\n <div className=\"rounded-lg border bg-muted/30 p-4 space-y-3\">\n <h4 className=\"text-sm font-medium\">Quote Summary</h4>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <span className=\"text-xs text-muted-foreground\">NRC Total</span>\n <p className=\"text-lg font-semibold\">{fmt(pricingSummary.nrcTotal, pricingSummary.currencyCode)}</p>\n </div>\n <div>\n <span className=\"text-xs text-muted-foreground\">MRC Total</span>\n <p className=\"text-lg font-semibold\">{fmt(pricingSummary.mrcTotal, pricingSummary.currencyCode)}</p>\n </div>\n {pricingSummary.discountTotal > 0 && (\n <div>\n <span className=\"text-xs text-muted-foreground\">Discounts</span>\n <p className=\"text-sm\">-{fmt(pricingSummary.discountTotal, pricingSummary.currencyCode)}</p>\n </div>\n )}\n {pricingSummary.surchargeTotal > 0 && (\n <div>\n <span className=\"text-xs text-muted-foreground\">Surcharges</span>\n <p className=\"text-sm\">+{fmt(pricingSummary.surchargeTotal, pricingSummary.currencyCode)}</p>\n </div>\n )}\n </div>\n </div>\n\n {/* Line items */}\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-medium\">Items ({lines.length})</h4>\n <div className=\"space-y-2\">\n {lines.map((line) => (\n <div key={line.lineId} className=\"rounded-lg border p-3 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <strong className=\"text-sm\">{line.offeringName}</strong>\n <span className=\"text-sm\">\n {fmt(line.nrcTotal, pricingSummary.currencyCode)} NRC / {fmt(line.mrcTotal, pricingSummary.currencyCode)} MRC\n </span>\n </div>\n {line.quantity > 1 && (\n <span className=\"text-xs text-muted-foreground\">Quantity: {line.quantity}</span>\n )}\n\n {showChargeBreakdown && line.charges?.length > 0 && (\n <div className=\"rounded border\">\n <table className=\"w-full text-xs\">\n <thead>\n <tr className=\"border-b bg-muted/30\">\n <th className=\"px-3 py-1.5 text-left font-medium\">Charge</th>\n <th className=\"px-3 py-1.5 text-left font-medium\">Type</th>\n <th className=\"px-3 py-1.5 text-right font-medium\">Price</th>\n </tr>\n </thead>\n <tbody>\n {line.charges.map((charge) => (\n <React.Fragment key={charge.chargeCode}>\n <tr className=\"border-b last:border-0\">\n <td className=\"px-3 py-1.5\">{charge.chargeName}</td>\n <td className=\"px-3 py-1.5 uppercase\">{charge.chargeType}</td>\n <td className=\"px-3 py-1.5 text-right\">\n {charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : `${fmt(charge.unitPrice, charge.currencyCode)}/unit`}\n </td>\n </tr>\n {showAdjustments && charge.adjustments?.map((adj) => (\n <tr key={adj.ruleCode} className=\"text-[11px] text-muted-foreground\">\n <td colSpan={2} className=\"px-3 py-0.5 pl-6\">{adj.ruleName}</td>\n <td className=\"px-3 py-0.5 text-right\">\n {adj.delta < 0 ? '' : '+'}{fmt(adj.delta, charge.currencyCode)}\n </td>\n </tr>\n ))}\n </React.Fragment>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div className=\"flex gap-2 justify-end pt-2\">\n {completedSteps.length > 0 && (\n <button type=\"button\" onClick={onBack} className=\"inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted\">\n Back\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {submitting ? 'Submitting...' : submitLabel}\n </button>\n </div>\n </div>\n )\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { registerStepType } from "../registry.js";
|
|
2
|
+
import { CustomerSelectStep } from "./CustomerSelectStep.js";
|
|
3
|
+
import { OfferingSelectStep } from "./OfferingSelectStep.js";
|
|
4
|
+
import { ProductConfigureStep } from "./ProductConfigureStep.js";
|
|
5
|
+
import { ItemListStep } from "./ItemListStep.js";
|
|
6
|
+
import { ReviewStep } from "./ReviewStep.js";
|
|
7
|
+
import { InventorySelectStep } from "./InventorySelectStep.js";
|
|
8
|
+
function registerBuiltInStepTypes() {
|
|
9
|
+
registerStepType({
|
|
10
|
+
type: "customer_select",
|
|
11
|
+
label: "Customer Selection",
|
|
12
|
+
description: "Search and select a customer for the quote.",
|
|
13
|
+
component: CustomerSelectStep
|
|
14
|
+
});
|
|
15
|
+
registerStepType({
|
|
16
|
+
type: "offering_select",
|
|
17
|
+
label: "Offering Selection",
|
|
18
|
+
description: "Browse and select a product offering from the catalog.",
|
|
19
|
+
component: OfferingSelectStep
|
|
20
|
+
});
|
|
21
|
+
registerStepType({
|
|
22
|
+
type: "product_configure",
|
|
23
|
+
label: "Product Configuration",
|
|
24
|
+
description: "Configure run-time attributes for the selected offering.",
|
|
25
|
+
component: ProductConfigureStep
|
|
26
|
+
});
|
|
27
|
+
registerStepType({
|
|
28
|
+
type: "item_list",
|
|
29
|
+
label: "Item List",
|
|
30
|
+
description: "View and manage child items on the quote.",
|
|
31
|
+
component: ItemListStep
|
|
32
|
+
});
|
|
33
|
+
registerStepType({
|
|
34
|
+
type: "review",
|
|
35
|
+
label: "Review & Submit",
|
|
36
|
+
description: "Review the quote summary and submit.",
|
|
37
|
+
component: ReviewStep
|
|
38
|
+
});
|
|
39
|
+
registerStepType({
|
|
40
|
+
type: "inventory_select",
|
|
41
|
+
label: "Inventory Selection",
|
|
42
|
+
description: "Select existing customer inventory items (stub \u2014 pending inventory domain).",
|
|
43
|
+
component: InventorySelectStep
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
export {
|
|
47
|
+
registerBuiltInStepTypes
|
|
48
|
+
};
|
|
49
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../../../../src/modules/cpq/workflows/steps/index.ts"],
|
|
4
|
+
"sourcesContent": ["import { registerStepType } from '../registry'\nimport { CustomerSelectStep } from './CustomerSelectStep'\nimport { OfferingSelectStep } from './OfferingSelectStep'\nimport { ProductConfigureStep } from './ProductConfigureStep'\nimport { ItemListStep } from './ItemListStep'\nimport { ReviewStep } from './ReviewStep'\nimport { InventorySelectStep } from './InventorySelectStep'\n\nexport function registerBuiltInStepTypes() {\n registerStepType({\n type: 'customer_select',\n label: 'Customer Selection',\n description: 'Search and select a customer for the quote.',\n component: CustomerSelectStep,\n })\n\n registerStepType({\n type: 'offering_select',\n label: 'Offering Selection',\n description: 'Browse and select a product offering from the catalog.',\n component: OfferingSelectStep,\n })\n\n registerStepType({\n type: 'product_configure',\n label: 'Product Configuration',\n description: 'Configure run-time attributes for the selected offering.',\n component: ProductConfigureStep,\n })\n\n registerStepType({\n type: 'item_list',\n label: 'Item List',\n description: 'View and manage child items on the quote.',\n component: ItemListStep,\n })\n\n registerStepType({\n type: 'review',\n label: 'Review & Submit',\n description: 'Review the quote summary and submit.',\n component: ReviewStep,\n })\n\n registerStepType({\n type: 'inventory_select',\n label: 'Inventory Selection',\n description: 'Select existing customer inventory items (stub \u2014 pending inventory domain).',\n component: InventorySelectStep,\n })\n}\n"],
|
|
5
|
+
"mappings": "AAAA,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAE7B,SAAS,2BAA2B;AACzC,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AACH;",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
//# sourceMappingURL=types.js.map
|
package/package.json
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dainamite/cpq",
|
|
3
|
+
"version": "0.2.2",
|
|
4
|
+
"description": "Dainamite CPQ — Configure, Price, Quote engine for Open Mercato (configurable products, multi-charge pricing tables, guided quoting, customer inventory, orders).",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/modules/cpq/index.js",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist",
|
|
9
|
+
"src",
|
|
10
|
+
"README.md",
|
|
11
|
+
"CHANGELOG.md",
|
|
12
|
+
"MIGRATION.md"
|
|
13
|
+
],
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "node build.mjs",
|
|
16
|
+
"watch": "node watch.mjs",
|
|
17
|
+
"typecheck": "tsc --noEmit"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"chokidar": "^4.0.3"
|
|
21
|
+
},
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./src/modules/cpq/index.ts",
|
|
25
|
+
"default": "./dist/modules/cpq/index.js"
|
|
26
|
+
},
|
|
27
|
+
"./modules/cpq": {
|
|
28
|
+
"types": "./src/modules/cpq/index.ts",
|
|
29
|
+
"default": "./dist/modules/cpq/index.js"
|
|
30
|
+
},
|
|
31
|
+
"./*.ts": {
|
|
32
|
+
"types": "./src/*.ts",
|
|
33
|
+
"default": "./dist/*.js"
|
|
34
|
+
},
|
|
35
|
+
"./*.tsx": {
|
|
36
|
+
"types": "./src/*.tsx",
|
|
37
|
+
"default": "./dist/*.js"
|
|
38
|
+
},
|
|
39
|
+
"./*.json": "./src/*.json",
|
|
40
|
+
"./*": {
|
|
41
|
+
"types": [
|
|
42
|
+
"./src/*.ts",
|
|
43
|
+
"./src/*.tsx"
|
|
44
|
+
],
|
|
45
|
+
"default": "./dist/*.js"
|
|
46
|
+
},
|
|
47
|
+
"./*/*.json": "./src/*/*.json",
|
|
48
|
+
"./*/*": {
|
|
49
|
+
"types": [
|
|
50
|
+
"./src/*/*.ts",
|
|
51
|
+
"./src/*/*.tsx"
|
|
52
|
+
],
|
|
53
|
+
"default": "./dist/*/*.js"
|
|
54
|
+
},
|
|
55
|
+
"./*/*/*.json": "./src/*/*/*.json",
|
|
56
|
+
"./*/*/*": {
|
|
57
|
+
"types": [
|
|
58
|
+
"./src/*/*/*.ts",
|
|
59
|
+
"./src/*/*/*.tsx"
|
|
60
|
+
],
|
|
61
|
+
"default": "./dist/*/*/*.js"
|
|
62
|
+
},
|
|
63
|
+
"./*/*/*/*.json": "./src/*/*/*/*.json",
|
|
64
|
+
"./*/*/*/*": {
|
|
65
|
+
"types": [
|
|
66
|
+
"./src/*/*/*/*.ts",
|
|
67
|
+
"./src/*/*/*/*.tsx"
|
|
68
|
+
],
|
|
69
|
+
"default": "./dist/*/*/*/*.js"
|
|
70
|
+
},
|
|
71
|
+
"./*/*/*/*/*.json": "./src/*/*/*/*/*.json",
|
|
72
|
+
"./*/*/*/*/*": {
|
|
73
|
+
"types": [
|
|
74
|
+
"./src/*/*/*/*/*.ts",
|
|
75
|
+
"./src/*/*/*/*/*.tsx"
|
|
76
|
+
],
|
|
77
|
+
"default": "./dist/*/*/*/*/*.js"
|
|
78
|
+
},
|
|
79
|
+
"./*/*/*/*/*/*.json": "./src/*/*/*/*/*/*.json",
|
|
80
|
+
"./*/*/*/*/*/*": {
|
|
81
|
+
"types": [
|
|
82
|
+
"./src/*/*/*/*/*/*.ts",
|
|
83
|
+
"./src/*/*/*/*/*/*.tsx"
|
|
84
|
+
],
|
|
85
|
+
"default": "./dist/*/*/*/*/*/*.js"
|
|
86
|
+
},
|
|
87
|
+
"./*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*.json",
|
|
88
|
+
"./*/*/*/*/*/*/*": {
|
|
89
|
+
"types": [
|
|
90
|
+
"./src/*/*/*/*/*/*/*.ts",
|
|
91
|
+
"./src/*/*/*/*/*/*/*.tsx"
|
|
92
|
+
],
|
|
93
|
+
"default": "./dist/*/*/*/*/*/*/*.js"
|
|
94
|
+
},
|
|
95
|
+
"./*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*.json",
|
|
96
|
+
"./*/*/*/*/*/*/*/*": {
|
|
97
|
+
"types": [
|
|
98
|
+
"./src/*/*/*/*/*/*/*/*.ts",
|
|
99
|
+
"./src/*/*/*/*/*/*/*/*.tsx"
|
|
100
|
+
],
|
|
101
|
+
"default": "./dist/*/*/*/*/*/*/*/*.js"
|
|
102
|
+
},
|
|
103
|
+
"./*/*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*/*.json",
|
|
104
|
+
"./*/*/*/*/*/*/*/*/*": {
|
|
105
|
+
"types": [
|
|
106
|
+
"./src/*/*/*/*/*/*/*/*/*.ts",
|
|
107
|
+
"./src/*/*/*/*/*/*/*/*/*.tsx"
|
|
108
|
+
],
|
|
109
|
+
"default": "./dist/*/*/*/*/*/*/*/*/*.js"
|
|
110
|
+
},
|
|
111
|
+
"./*/*/*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*/*/*.json",
|
|
112
|
+
"./*/*/*/*/*/*/*/*/*/*": {
|
|
113
|
+
"types": [
|
|
114
|
+
"./src/*/*/*/*/*/*/*/*/*/*.ts",
|
|
115
|
+
"./src/*/*/*/*/*/*/*/*/*/*.tsx"
|
|
116
|
+
],
|
|
117
|
+
"default": "./dist/*/*/*/*/*/*/*/*/*/*.js"
|
|
118
|
+
}
|
|
119
|
+
},
|
|
120
|
+
"peerDependencies": {
|
|
121
|
+
"@mikro-orm/core": "^7.0.0",
|
|
122
|
+
"@mikro-orm/decorators": "^7.0.0",
|
|
123
|
+
"@mikro-orm/postgresql": "^7.0.0",
|
|
124
|
+
"@open-mercato/core": "^0.6.0",
|
|
125
|
+
"@open-mercato/events": "^0.6.0",
|
|
126
|
+
"@open-mercato/shared": "^0.6.0",
|
|
127
|
+
"@open-mercato/ui": "^0.6.0",
|
|
128
|
+
"@tanstack/react-table": "^8.0.0 || ^9.0.0",
|
|
129
|
+
"awilix": "^12.0.0",
|
|
130
|
+
"bcryptjs": "^3.0.0",
|
|
131
|
+
"lucide-react": "^0.400.0",
|
|
132
|
+
"next": "^16.0.0",
|
|
133
|
+
"react": "^19.0.0",
|
|
134
|
+
"react-dom": "^19.0.0",
|
|
135
|
+
"xlsx": "^0.18.0",
|
|
136
|
+
"zod": "^4.0.0"
|
|
137
|
+
},
|
|
138
|
+
"publishConfig": {
|
|
139
|
+
"access": "public"
|
|
140
|
+
},
|
|
141
|
+
"repository": {
|
|
142
|
+
"type": "git",
|
|
143
|
+
"url": "https://github.com/dainamite-technologies/dainamite-core.git",
|
|
144
|
+
"directory": "packages/cpq"
|
|
145
|
+
},
|
|
146
|
+
"license": "MIT",
|
|
147
|
+
"engines": {
|
|
148
|
+
"node": ">=24.0.0"
|
|
149
|
+
}
|
|
150
|
+
}
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
import { test, expect, type APIRequestContext } from '@playwright/test'
|
|
2
|
+
import { getAuthToken, apiRequest } from '@open-mercato/core/helpers/integration/api'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TC-CPQ-001: Product specification CRUD via REST API
|
|
6
|
+
* Category: CPQ — API
|
|
7
|
+
* Priority: High
|
|
8
|
+
*
|
|
9
|
+
* Validates the foundation entity of the CPQ product catalogue.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
const uniq = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
|
|
13
|
+
|
|
14
|
+
async function deleteSpec(request: APIRequestContext, token: string, id: string): Promise<void> {
|
|
15
|
+
await apiRequest(request, 'DELETE', '/api/cpq/product-specifications', {
|
|
16
|
+
token,
|
|
17
|
+
data: { id },
|
|
18
|
+
})
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
test.describe('TC-CPQ-001: Product Specification CRUD (API)', () => {
|
|
22
|
+
let token: string
|
|
23
|
+
const createdIds: string[] = []
|
|
24
|
+
|
|
25
|
+
test.beforeAll(async ({ request }) => {
|
|
26
|
+
token = await getAuthToken(request, 'admin')
|
|
27
|
+
})
|
|
28
|
+
|
|
29
|
+
test.afterEach(async ({ request }) => {
|
|
30
|
+
while (createdIds.length > 0) {
|
|
31
|
+
const id = createdIds.pop()!
|
|
32
|
+
await deleteSpec(request, token, id)
|
|
33
|
+
}
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
test('creates a simple specification with defaults', async ({ request }) => {
|
|
37
|
+
const code = `SPEC-${uniq()}`
|
|
38
|
+
const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
39
|
+
token,
|
|
40
|
+
data: { code, name: 'Test Spec' },
|
|
41
|
+
})
|
|
42
|
+
expect(res.ok(), `POST status was ${res.status()}`).toBeTruthy()
|
|
43
|
+
|
|
44
|
+
const body = await res.json()
|
|
45
|
+
expect(body).toMatchObject({
|
|
46
|
+
code,
|
|
47
|
+
name: 'Test Spec',
|
|
48
|
+
specType: 'simple',
|
|
49
|
+
lifecycleStatus: 'draft',
|
|
50
|
+
version: 1,
|
|
51
|
+
isActive: true,
|
|
52
|
+
})
|
|
53
|
+
expect(body.id).toBeTruthy()
|
|
54
|
+
createdIds.push(body.id)
|
|
55
|
+
})
|
|
56
|
+
|
|
57
|
+
test('rejects creation with missing code', async ({ request }) => {
|
|
58
|
+
const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
59
|
+
token,
|
|
60
|
+
data: { name: 'No Code' },
|
|
61
|
+
})
|
|
62
|
+
expect(res.status()).toBe(400)
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
test('rejects creation with empty code', async ({ request }) => {
|
|
66
|
+
const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
67
|
+
token,
|
|
68
|
+
data: { code: '', name: 'Empty Code' },
|
|
69
|
+
})
|
|
70
|
+
expect(res.status()).toBe(400)
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('lists previously created specifications', async ({ request }) => {
|
|
74
|
+
const code = `SPEC-${uniq()}`
|
|
75
|
+
const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
76
|
+
token,
|
|
77
|
+
data: { code, name: 'Listable Spec' },
|
|
78
|
+
})
|
|
79
|
+
const createdBody = await created.json()
|
|
80
|
+
createdIds.push(createdBody.id)
|
|
81
|
+
|
|
82
|
+
const list = await apiRequest(request, 'GET', '/api/cpq/product-specifications?pageSize=100', {
|
|
83
|
+
token,
|
|
84
|
+
data: undefined,
|
|
85
|
+
})
|
|
86
|
+
expect(list.ok()).toBeTruthy()
|
|
87
|
+
const listBody = await list.json()
|
|
88
|
+
const found = (listBody.items as Array<{ id: string; code: string }>).find((s) => s.id === createdBody.id)
|
|
89
|
+
expect(found).toBeDefined()
|
|
90
|
+
expect(found?.code).toBe(code)
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
test('reads a specification detail by id (with related collections)', async ({ request }) => {
|
|
94
|
+
const code = `SPEC-${uniq()}`
|
|
95
|
+
const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
96
|
+
token,
|
|
97
|
+
data: { code, name: 'Detail Spec', specType: 'bundle' },
|
|
98
|
+
})
|
|
99
|
+
const createdBody = await created.json()
|
|
100
|
+
createdIds.push(createdBody.id)
|
|
101
|
+
|
|
102
|
+
const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${createdBody.id}`, {
|
|
103
|
+
token,
|
|
104
|
+
data: undefined,
|
|
105
|
+
})
|
|
106
|
+
expect(detail.ok()).toBeTruthy()
|
|
107
|
+
const detailBody = await detail.json()
|
|
108
|
+
expect(detailBody.id).toBe(createdBody.id)
|
|
109
|
+
expect(detailBody.specType).toBe('bundle')
|
|
110
|
+
expect(Array.isArray(detailBody.attributes)).toBe(true)
|
|
111
|
+
expect(Array.isArray(detailBody.offerings)).toBe(true)
|
|
112
|
+
expect(Array.isArray(detailBody.relationships)).toBe(true)
|
|
113
|
+
})
|
|
114
|
+
|
|
115
|
+
test('returns 404 for unknown id', async ({ request }) => {
|
|
116
|
+
const res = await apiRequest(
|
|
117
|
+
request,
|
|
118
|
+
'GET',
|
|
119
|
+
'/api/cpq/product-specifications?id=00000000-0000-4000-8000-000000000000',
|
|
120
|
+
{ token, data: undefined },
|
|
121
|
+
)
|
|
122
|
+
expect(res.status()).toBe(404)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('updates fields and bumps the persisted state', async ({ request }) => {
|
|
126
|
+
const code = `SPEC-${uniq()}`
|
|
127
|
+
const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
128
|
+
token,
|
|
129
|
+
data: { code, name: 'Original Name' },
|
|
130
|
+
})
|
|
131
|
+
const { id } = await created.json()
|
|
132
|
+
createdIds.push(id)
|
|
133
|
+
|
|
134
|
+
const upd = await apiRequest(request, 'PUT', '/api/cpq/product-specifications', {
|
|
135
|
+
token,
|
|
136
|
+
data: { id, name: 'Updated Name', lifecycleStatus: 'active' },
|
|
137
|
+
})
|
|
138
|
+
expect(upd.ok(), `PUT status was ${upd.status()}`).toBeTruthy()
|
|
139
|
+
|
|
140
|
+
const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${id}`, {
|
|
141
|
+
token,
|
|
142
|
+
data: undefined,
|
|
143
|
+
})
|
|
144
|
+
const body = await detail.json()
|
|
145
|
+
expect(body.name).toBe('Updated Name')
|
|
146
|
+
expect(body.lifecycleStatus).toBe('active')
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('soft-deletes a specification (subsequent detail returns 404)', async ({ request }) => {
|
|
150
|
+
const code = `SPEC-${uniq()}`
|
|
151
|
+
const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
|
|
152
|
+
token,
|
|
153
|
+
data: { code, name: 'To Delete' },
|
|
154
|
+
})
|
|
155
|
+
const { id } = await created.json()
|
|
156
|
+
|
|
157
|
+
const del = await apiRequest(request, 'DELETE', '/api/cpq/product-specifications', {
|
|
158
|
+
token,
|
|
159
|
+
data: { id },
|
|
160
|
+
})
|
|
161
|
+
expect(del.ok(), `DELETE status was ${del.status()}`).toBeTruthy()
|
|
162
|
+
|
|
163
|
+
const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${id}`, {
|
|
164
|
+
token,
|
|
165
|
+
data: undefined,
|
|
166
|
+
})
|
|
167
|
+
expect(detail.status()).toBe(404)
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
test('rejects unauthenticated requests', async ({ request }) => {
|
|
171
|
+
const res = await request.fetch(
|
|
172
|
+
`${process.env.BASE_URL || 'http://localhost:3000'}/api/cpq/product-specifications`,
|
|
173
|
+
{ method: 'GET' },
|
|
174
|
+
)
|
|
175
|
+
expect([401, 403]).toContain(res.status())
|
|
176
|
+
})
|
|
177
|
+
})
|