@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,79 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedActiveSubscription,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
deleteSubscription,
|
|
8
|
+
deleteCustomer,
|
|
9
|
+
hideFloatingOverlays,
|
|
10
|
+
} from './_helpers/cpqArcFixtures'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TC-CPQ-ARC-006 — Concurrent ARC quote rejection + redirect-on-confirm (UI) (XD-250)
|
|
14
|
+
*
|
|
15
|
+
* Operator opens Amend on subscription S → quote Q1 created. While Q1 is
|
|
16
|
+
* still in non-terminal status, the operator returns to S and clicks "Amend"
|
|
17
|
+
* again. The second attempt is rejected server-side (409); the UI surfaces
|
|
18
|
+
* the conflict via a window.confirm() carrying the 409 message and offers
|
|
19
|
+
* to navigate to the existing quote. Spec § UI Integration "Existing-quote
|
|
20
|
+
* redirect": clicking OK navigates to the conflicting Q1.
|
|
21
|
+
*/
|
|
22
|
+
test.describe('TC-CPQ-ARC-006 — Concurrent ARC rejection + redirect (UI)', () => {
|
|
23
|
+
test('second Amend on a subscription already on a non-terminal ARC quote prompts and redirects to it', async ({
|
|
24
|
+
page,
|
|
25
|
+
request,
|
|
26
|
+
}) => {
|
|
27
|
+
const token = await getAuthToken(request, 'admin')
|
|
28
|
+
const customerId = await seedCustomer(request, token)
|
|
29
|
+
const sub = await seedActiveSubscription(request, token, { customerId, name: 'Conflict Sub' })
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await login(page, 'admin')
|
|
33
|
+
|
|
34
|
+
// 1st Amend — succeeds. Capture the resulting quote URL so we can
|
|
35
|
+
// assert the redirect lands on the same one. We must wait for the URL
|
|
36
|
+
// to actually change to /backend/cpq/quotes/<...> before snapshotting
|
|
37
|
+
// it — body-text assertions can pass on the source subscription page
|
|
38
|
+
// (which also contains sub.code) before router.push() completes.
|
|
39
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
40
|
+
waitUntil: 'domcontentloaded',
|
|
41
|
+
})
|
|
42
|
+
await hideFloatingOverlays(page)
|
|
43
|
+
await page.getByRole('button', { name: /^Amend$/ }).click()
|
|
44
|
+
await page.waitForURL(/\/backend\/cpq\/quotes\/[0-9a-f-]+$/, { timeout: 15_000 })
|
|
45
|
+
await expect(page.locator('body')).toContainText(sub.code, { timeout: 10_000 })
|
|
46
|
+
const firstQuoteUrl = new URL(page.url())
|
|
47
|
+
const firstQuoteId = firstQuoteUrl.pathname.split('/').pop()
|
|
48
|
+
expect(firstQuoteId).toBeTruthy()
|
|
49
|
+
|
|
50
|
+
// 2nd Amend on the same sub — should trigger window.confirm() with
|
|
51
|
+
// the conflict message + an offer to open the existing quote.
|
|
52
|
+
// Accept the dialog → page should navigate back to the first quote.
|
|
53
|
+
let confirmMessage: string | null = null
|
|
54
|
+
page.once('dialog', async (dialog) => {
|
|
55
|
+
confirmMessage = dialog.message()
|
|
56
|
+
// Click OK → component should router.push(/backend/cpq/quotes/<existingId>).
|
|
57
|
+
await dialog.accept()
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
61
|
+
waitUntil: 'domcontentloaded',
|
|
62
|
+
})
|
|
63
|
+
await hideFloatingOverlays(page)
|
|
64
|
+
await page.getByRole('button', { name: /^Amend$/ }).click()
|
|
65
|
+
|
|
66
|
+
// Wait for the confirm to have surfaced.
|
|
67
|
+
await expect.poll(() => confirmMessage, { timeout: 10_000 }).not.toBeNull()
|
|
68
|
+
expect(confirmMessage).toMatch(/in progress|already|existing/i)
|
|
69
|
+
// And the message should include an "Open the existing quote" prompt.
|
|
70
|
+
expect(confirmMessage).toMatch(/Open the existing quote/i)
|
|
71
|
+
|
|
72
|
+
// After accepting the confirm, the page should land on the first quote.
|
|
73
|
+
await expect.poll(() => page.url(), { timeout: 10_000 }).toContain(firstQuoteId!)
|
|
74
|
+
} finally {
|
|
75
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
76
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
77
|
+
}
|
|
78
|
+
})
|
|
79
|
+
})
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedActiveSubscription,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
daysFromNow,
|
|
8
|
+
deleteSubscription,
|
|
9
|
+
deleteCustomer,
|
|
10
|
+
hideFloatingOverlays,
|
|
11
|
+
} from './_helpers/cpqArcFixtures'
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* TC-CPQ-ARC-007 — Renew from subscription detail (UI) (XD-250 scenario 3)
|
|
15
|
+
*
|
|
16
|
+
* The standalone /expiring page was retired (commit fa5b899) — the Renew
|
|
17
|
+
* action moved onto the subscription detail page header. This test now
|
|
18
|
+
* navigates straight to the expiring subscription's detail and clicks
|
|
19
|
+
* Renew there. The "expiring" angle is preserved by seeding with
|
|
20
|
+
* currentTermEnd inside the 30d window so the sub would have appeared on
|
|
21
|
+
* the deprecated view; the renewal flow is identical regardless.
|
|
22
|
+
*/
|
|
23
|
+
test.describe('TC-CPQ-ARC-007 — Renew from subscription detail (UI)', () => {
|
|
24
|
+
test('Renew on an expiring sub creates a RENEW quote with the source as the only target', async ({
|
|
25
|
+
page,
|
|
26
|
+
request,
|
|
27
|
+
}) => {
|
|
28
|
+
const token = await getAuthToken(request, 'admin')
|
|
29
|
+
const customerId = await seedCustomer(request, token)
|
|
30
|
+
const sub = await seedActiveSubscription(request, token, {
|
|
31
|
+
customerId,
|
|
32
|
+
name: 'Expiring Soon — for renew',
|
|
33
|
+
currentTermEnd: daysFromNow(20),
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await login(page, 'admin')
|
|
38
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
39
|
+
waitUntil: 'domcontentloaded',
|
|
40
|
+
})
|
|
41
|
+
await hideFloatingOverlays(page)
|
|
42
|
+
|
|
43
|
+
// Subscription detail loads — its name is visible in the header.
|
|
44
|
+
await expect(page.locator('body')).toContainText(sub.name, { timeout: 15_000 })
|
|
45
|
+
|
|
46
|
+
// Click the Renew button in the FormHeader actions row.
|
|
47
|
+
await page.getByRole('button', { name: /^Renew$/ }).first().click()
|
|
48
|
+
|
|
49
|
+
// After click, startArcQuote('renew') POSTs from-subscription then
|
|
50
|
+
// router.push()es to the new quote. Wait for URL transition.
|
|
51
|
+
await page.waitForURL(/\/backend\/cpq\/quotes\/[0-9a-f-]+$/, { timeout: 15_000 })
|
|
52
|
+
await expect(page.locator('body')).toContainText(sub.code, { timeout: 10_000 })
|
|
53
|
+
// ARC banner shows "RENEW • 1 target: <code>" — DOM stores lowercase
|
|
54
|
+
// 'renew' under a CSS uppercase rule, so `getByText` regex must use the
|
|
55
|
+
// case-insensitive flag.
|
|
56
|
+
await expect(page.getByText(/1 target/i).first()).toBeVisible({ timeout: 5_000 })
|
|
57
|
+
await expect(page.getByText(/renew/i).first()).toBeVisible({ timeout: 5_000 })
|
|
58
|
+
} finally {
|
|
59
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
60
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
61
|
+
}
|
|
62
|
+
})
|
|
63
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedSubscriptionInStatus,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
deleteSubscription,
|
|
8
|
+
deleteCustomer,
|
|
9
|
+
hideFloatingOverlays,
|
|
10
|
+
} from './_helpers/cpqArcFixtures'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TC-CPQ-ARC-008 — ARC on suspended subscription (UI) (XD-250 scenario 6, DD-ARC)
|
|
14
|
+
*
|
|
15
|
+
* Spec § DD-ARC: amend / standalone-renew / cancel work on suspended subs
|
|
16
|
+
* without first reactivating. The action row in the subscription detail
|
|
17
|
+
* header MUST render for status ∈ {active, suspended} — only terminal
|
|
18
|
+
* statuses hide it. This test seeds a suspended sub, asserts the buttons
|
|
19
|
+
* are rendered, opens Amend, and verifies the resulting quote attaches the
|
|
20
|
+
* suspended sub as a target.
|
|
21
|
+
*/
|
|
22
|
+
test.describe('TC-CPQ-ARC-008 — ARC on suspended subscription (UI)', () => {
|
|
23
|
+
test('suspended sub still shows ARC buttons; Amend works and quote attaches it', async ({
|
|
24
|
+
page,
|
|
25
|
+
request,
|
|
26
|
+
}) => {
|
|
27
|
+
const token = await getAuthToken(request, 'admin')
|
|
28
|
+
const customerId = await seedCustomer(request, token)
|
|
29
|
+
const sub = await seedSubscriptionInStatus(
|
|
30
|
+
request,
|
|
31
|
+
token,
|
|
32
|
+
{ customerId, name: 'Suspended sub for ARC' },
|
|
33
|
+
'suspended',
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
await login(page, 'admin')
|
|
38
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
39
|
+
waitUntil: 'domcontentloaded',
|
|
40
|
+
})
|
|
41
|
+
await hideFloatingOverlays(page)
|
|
42
|
+
|
|
43
|
+
// Status badge confirms suspended; all three ARC buttons must render.
|
|
44
|
+
await expect(page.locator('body')).toContainText(/suspended/i, { timeout: 15_000 })
|
|
45
|
+
const amendBtn = page.getByRole('button', { name: /^Amend$/ })
|
|
46
|
+
const renewBtn = page.getByRole('button', { name: /^Renew$/ })
|
|
47
|
+
const cancelBtn = page.getByRole('button', { name: /^Cancel$/ })
|
|
48
|
+
await expect(amendBtn).toBeVisible()
|
|
49
|
+
await expect(renewBtn).toBeVisible()
|
|
50
|
+
await expect(cancelBtn).toBeVisible()
|
|
51
|
+
|
|
52
|
+
// Click Amend → fresh quote with the suspended sub as target.
|
|
53
|
+
await amendBtn.click()
|
|
54
|
+
await page.waitForURL(/\/backend\/cpq\/quotes\/[0-9a-f-]+$/, { timeout: 15_000 })
|
|
55
|
+
await expect(page.locator('body')).toContainText(sub.code, { timeout: 10_000 })
|
|
56
|
+
await expect(page.getByText(/1 target/i).first()).toBeVisible({ timeout: 5_000 })
|
|
57
|
+
} finally {
|
|
58
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
59
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
60
|
+
}
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedSubscriptionInStatus,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
deleteSubscription,
|
|
8
|
+
deleteCustomer,
|
|
9
|
+
hideFloatingOverlays,
|
|
10
|
+
} from './_helpers/cpqArcFixtures'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TC-CPQ-ARC-009 — Terminal-status subscriptions hide ARC buttons (UI) (XD-250 scenario 7)
|
|
14
|
+
*
|
|
15
|
+
* Spec § UI Integration: the action row only renders when status ∈
|
|
16
|
+
* {active, suspended}. Terminal statuses (`expired`, `terminated`,
|
|
17
|
+
* `superseded`) MUST NOT show Amend / Renew / Cancel buttons. This test
|
|
18
|
+
* verifies the gate for both `terminated` and `expired`.
|
|
19
|
+
*/
|
|
20
|
+
test.describe('TC-CPQ-ARC-009 — Terminal-status subs hide ARC buttons (UI)', () => {
|
|
21
|
+
for (const status of ['terminated', 'expired'] as const) {
|
|
22
|
+
test(`${status} sub renders status badge but no ARC action buttons`, async ({
|
|
23
|
+
page,
|
|
24
|
+
request,
|
|
25
|
+
}) => {
|
|
26
|
+
const token = await getAuthToken(request, 'admin')
|
|
27
|
+
const customerId = await seedCustomer(request, token)
|
|
28
|
+
const sub = await seedSubscriptionInStatus(
|
|
29
|
+
request,
|
|
30
|
+
token,
|
|
31
|
+
{ customerId, name: `${status} sub for ARC gate` },
|
|
32
|
+
status,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
await login(page, 'admin')
|
|
37
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
38
|
+
waitUntil: 'domcontentloaded',
|
|
39
|
+
})
|
|
40
|
+
await hideFloatingOverlays(page)
|
|
41
|
+
|
|
42
|
+
await expect(page.locator('body')).toContainText(new RegExp(status, 'i'), {
|
|
43
|
+
timeout: 15_000,
|
|
44
|
+
})
|
|
45
|
+
|
|
46
|
+
// ARC buttons must not be present.
|
|
47
|
+
await expect(page.getByRole('button', { name: /^Amend$/ })).toHaveCount(0)
|
|
48
|
+
await expect(page.getByRole('button', { name: /^Renew$/ })).toHaveCount(0)
|
|
49
|
+
// Note: there is a "Cancel" item-detail button elsewhere, but the
|
|
50
|
+
// header-level Cancel ARC button is what we're guarding against.
|
|
51
|
+
// Anchor by parent role — the action row sits inside the page
|
|
52
|
+
// header, while item-row Cancels live inside item cards.
|
|
53
|
+
const headerCancelButtons = page.locator('header, [role="banner"]')
|
|
54
|
+
.getByRole('button', { name: /^Cancel$/ })
|
|
55
|
+
await expect(headerCancelButtons).toHaveCount(0)
|
|
56
|
+
} finally {
|
|
57
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
58
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
59
|
+
}
|
|
60
|
+
})
|
|
61
|
+
}
|
|
62
|
+
})
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedActiveSubscription,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
deleteSubscription,
|
|
8
|
+
deleteCustomer,
|
|
9
|
+
hideFloatingOverlays,
|
|
10
|
+
} from './_helpers/cpqArcFixtures'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TC-CPQ-ARC-010 — Change History "Created" seed entry (UI) (XD-250 scenario 10)
|
|
14
|
+
*
|
|
15
|
+
* Commit fa5b899 made `createSubscription` write a `created` row into
|
|
16
|
+
* `CpqSubscriptionChangeLog` so the history panel is never empty —
|
|
17
|
+
* operators always see where the subscription came from before any
|
|
18
|
+
* ARC operation. This test verifies:
|
|
19
|
+
* - Change History renders with one row (the "Created" seed)
|
|
20
|
+
* - the row is tagged "Created" rather than "Amended" / "Renewed" / etc.
|
|
21
|
+
*/
|
|
22
|
+
test.describe('TC-CPQ-ARC-010 — Change History seeded with Created (UI)', () => {
|
|
23
|
+
test('fresh subscription renders Change History (1) with a "Created" entry', async ({
|
|
24
|
+
page,
|
|
25
|
+
request,
|
|
26
|
+
}) => {
|
|
27
|
+
const token = await getAuthToken(request, 'admin')
|
|
28
|
+
const customerId = await seedCustomer(request, token)
|
|
29
|
+
const sub = await seedActiveSubscription(request, token, {
|
|
30
|
+
customerId,
|
|
31
|
+
name: 'Fresh sub — seeded Created entry',
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
await login(page, 'admin')
|
|
36
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
37
|
+
waitUntil: 'domcontentloaded',
|
|
38
|
+
})
|
|
39
|
+
await hideFloatingOverlays(page)
|
|
40
|
+
|
|
41
|
+
const heading = page.getByRole('heading', { name: /Change History/i })
|
|
42
|
+
await expect(heading).toBeVisible({ timeout: 15_000 })
|
|
43
|
+
// Subscription was just created → exactly one log row.
|
|
44
|
+
await expect(heading).toContainText('(1)')
|
|
45
|
+
// Scope the "Created" lookup to the Change History container so
|
|
46
|
+
// the assertion can't accidentally match the "Created" Field
|
|
47
|
+
// label on the Pricing & References meta card above.
|
|
48
|
+
const historySection = page
|
|
49
|
+
.locator('div', { has: heading })
|
|
50
|
+
.first()
|
|
51
|
+
await expect(historySection.getByText(/^Created$/).first()).toBeVisible({
|
|
52
|
+
timeout: 5_000,
|
|
53
|
+
})
|
|
54
|
+
} finally {
|
|
55
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
56
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
57
|
+
}
|
|
58
|
+
})
|
|
59
|
+
})
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
import { getAuthToken, apiRequest } from '@open-mercato/core/helpers/integration/api'
|
|
4
|
+
import {
|
|
5
|
+
seedActiveSubscription,
|
|
6
|
+
seedCustomer,
|
|
7
|
+
deleteSubscription,
|
|
8
|
+
deleteCustomer,
|
|
9
|
+
hideFloatingOverlays,
|
|
10
|
+
} from './_helpers/cpqArcFixtures'
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* TC-CPQ-ARC-011 — Full ARC lifecycle: amend → approval → convert → activate (UI + API hybrid)
|
|
14
|
+
* (XD-250 scenario 11)
|
|
15
|
+
*
|
|
16
|
+
* The longest manual scenario: an amend quote is driven through the full
|
|
17
|
+
* approval ladder, converted to an order, and activated. Activation runs
|
|
18
|
+
* `cpqInventoryService.applyAmendment()` which writes a `CpqSubscriptionChangeLog`
|
|
19
|
+
* row + emits `cpq.subscription.amended`. We assert UI at the entry points
|
|
20
|
+
* (Amend button on the sub, Activate button on the order — renamed in
|
|
21
|
+
* commit 720db2b) and at the exit (Change History (2) on the source sub:
|
|
22
|
+
* one seeded `Created` row + one `Amended` row from the activation) and
|
|
23
|
+
* drive the approval lifecycle / convert via API to keep the test stable.
|
|
24
|
+
*/
|
|
25
|
+
test.describe('TC-CPQ-ARC-011 — Full ARC lifecycle (UI + API hybrid)', () => {
|
|
26
|
+
test('amend → drive through statuses → convert → activate → Change History (Created + Amended)', async ({
|
|
27
|
+
page,
|
|
28
|
+
request,
|
|
29
|
+
}) => {
|
|
30
|
+
const token = await getAuthToken(request, 'admin')
|
|
31
|
+
const customerId = await seedCustomer(request, token)
|
|
32
|
+
const sub = await seedActiveSubscription(request, token, {
|
|
33
|
+
customerId,
|
|
34
|
+
name: 'Lifecycle target',
|
|
35
|
+
items: [{ name: 'Premium Tier', mrcAmount: 250 }],
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
await login(page, 'admin')
|
|
40
|
+
|
|
41
|
+
// 1. UI — click Amend on the source sub. Captures the new quote id.
|
|
42
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
43
|
+
waitUntil: 'domcontentloaded',
|
|
44
|
+
})
|
|
45
|
+
await hideFloatingOverlays(page)
|
|
46
|
+
await page.getByRole('button', { name: /^Amend$/ }).click()
|
|
47
|
+
await page.waitForURL(/\/backend\/cpq\/quotes\/[0-9a-f-]+$/, { timeout: 15_000 })
|
|
48
|
+
const quoteId = page.url().split('/').pop()!
|
|
49
|
+
expect(quoteId).toMatch(/^[0-9a-f-]+$/)
|
|
50
|
+
|
|
51
|
+
// 2. API — drive quote through approval ladder. The API path was
|
|
52
|
+
// exercised manually for each transition; we just need an `accepted`
|
|
53
|
+
// quote so Convert to Order unlocks.
|
|
54
|
+
const transitions = [
|
|
55
|
+
'ready',
|
|
56
|
+
'in_approval',
|
|
57
|
+
'approved',
|
|
58
|
+
'with_customer',
|
|
59
|
+
'accepted',
|
|
60
|
+
]
|
|
61
|
+
for (const target of transitions) {
|
|
62
|
+
const res = await apiRequest(
|
|
63
|
+
request,
|
|
64
|
+
'POST',
|
|
65
|
+
`/api/cpq/quotes/${quoteId}/status`,
|
|
66
|
+
{ token, data: { targetStatus: target } },
|
|
67
|
+
)
|
|
68
|
+
expect(
|
|
69
|
+
res.ok(),
|
|
70
|
+
`transition to ${target} returned ${res.status()}: ${await res.text()}`,
|
|
71
|
+
).toBeTruthy()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 3. API — convert quote to order. POST /api/cpq/orders with quoteId.
|
|
75
|
+
const convertRes = await apiRequest(request, 'POST', '/api/cpq/orders', {
|
|
76
|
+
token,
|
|
77
|
+
data: { quoteId },
|
|
78
|
+
})
|
|
79
|
+
expect(
|
|
80
|
+
convertRes.ok(),
|
|
81
|
+
`convert returned ${convertRes.status()}: ${await convertRes.text()}`,
|
|
82
|
+
).toBeTruthy()
|
|
83
|
+
const order = (await convertRes.json()) as { id: string }
|
|
84
|
+
expect(order.id).toBeTruthy()
|
|
85
|
+
|
|
86
|
+
// 4. UI — open the order detail and click Activate Order. This is the
|
|
87
|
+
// critical mutation point: the API endpoint creates ChangeLog rows /
|
|
88
|
+
// updates the subscription, but the manual instructs operators to
|
|
89
|
+
// press the green button so we cover the same path here.
|
|
90
|
+
await page.goto(`/backend/cpq/orders/${order.id}`, {
|
|
91
|
+
waitUntil: 'domcontentloaded',
|
|
92
|
+
})
|
|
93
|
+
await hideFloatingOverlays(page)
|
|
94
|
+
// Button renamed to "Activate" in commit 720db2b (entity context is
|
|
95
|
+
// already in the FormHeader eyebrow, so the verb stands alone).
|
|
96
|
+
const activateBtn = page.getByRole('button', { name: /^Activate$/ })
|
|
97
|
+
await expect(activateBtn).toBeVisible({ timeout: 15_000 })
|
|
98
|
+
await activateBtn.click()
|
|
99
|
+
|
|
100
|
+
// Wait for activation to complete — order status flips to 'active' and
|
|
101
|
+
// the green success banner appears.
|
|
102
|
+
await expect(page.locator('body')).toContainText(/active/i, { timeout: 30_000 })
|
|
103
|
+
|
|
104
|
+
// 5. UI — return to the source sub. Expect Change History (2):
|
|
105
|
+
// - the seeded `Created` row (commit fa5b899)
|
|
106
|
+
// - the `Amended` row written by activation
|
|
107
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
108
|
+
waitUntil: 'domcontentloaded',
|
|
109
|
+
})
|
|
110
|
+
await hideFloatingOverlays(page)
|
|
111
|
+
const heading = page.getByRole('heading', { name: /Change History/i })
|
|
112
|
+
await expect(heading).toBeVisible({ timeout: 15_000 })
|
|
113
|
+
// Poll the count text — the change-log fetch is async and can race
|
|
114
|
+
// with the heading's first paint.
|
|
115
|
+
await expect.poll(async () => (await heading.textContent()) ?? '', {
|
|
116
|
+
timeout: 15_000,
|
|
117
|
+
}).toMatch(/\(2\)/)
|
|
118
|
+
// Scope tag lookups to the Change History container so the
|
|
119
|
+
// "Created" tag isn't confused with the meta-card "Created" label.
|
|
120
|
+
const historySection = page.locator('div', { has: heading }).first()
|
|
121
|
+
await expect(historySection.getByText(/^Amended$/).first()).toBeVisible({ timeout: 5_000 })
|
|
122
|
+
await expect(historySection.getByText(/^Created$/).first()).toBeVisible({ timeout: 5_000 })
|
|
123
|
+
} finally {
|
|
124
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
125
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
126
|
+
}
|
|
127
|
+
})
|
|
128
|
+
})
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { expect, type APIRequestContext, type Page } from '@playwright/test'
|
|
2
|
+
import { apiRequest } from '@open-mercato/core/helpers/integration/api'
|
|
3
|
+
import { createCompanyFixture } from '@open-mercato/core/helpers/integration/crmFixtures'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* XD-250 ARC integration test fixtures.
|
|
7
|
+
*
|
|
8
|
+
* Test setup helpers (subscription / customer creation) go through the
|
|
9
|
+
* CPQ inventory + customers REST APIs because building a real subscription
|
|
10
|
+
* end-to-end via the new-sale UI flow per test would be enormous. The
|
|
11
|
+
* actual ARC behaviour under test still drives the UI (drawer, status
|
|
12
|
+
* transitions, change history) — only the *prerequisite state* is API.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
export const uniq = (): string =>
|
|
16
|
+
Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
|
|
17
|
+
|
|
18
|
+
export interface SeededSubscription {
|
|
19
|
+
id: string
|
|
20
|
+
customerId: string
|
|
21
|
+
code: string
|
|
22
|
+
name: string
|
|
23
|
+
currencyCode: string
|
|
24
|
+
billingCycle: string
|
|
25
|
+
mrcAmount: string
|
|
26
|
+
currentTermEnd: string | null
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface SeedSubscriptionInput {
|
|
30
|
+
customerId: string
|
|
31
|
+
name?: string
|
|
32
|
+
code?: string
|
|
33
|
+
billingCycle?: 'monthly' | 'quarterly' | 'annually'
|
|
34
|
+
currencyCode?: string
|
|
35
|
+
mrcAmount?: number
|
|
36
|
+
/** ISO date — sets currentTermEnd via PUT after creation. */
|
|
37
|
+
currentTermEnd?: string
|
|
38
|
+
termMonths?: number
|
|
39
|
+
/** Pre-seed at least one item; subscription schema requires items.min(1). */
|
|
40
|
+
items?: Array<{
|
|
41
|
+
name: string
|
|
42
|
+
mrcAmount?: number
|
|
43
|
+
nrcAmount?: number
|
|
44
|
+
}>
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Creates a customer (company) and returns its id.
|
|
49
|
+
*/
|
|
50
|
+
export async function seedCustomer(
|
|
51
|
+
request: APIRequestContext,
|
|
52
|
+
token: string,
|
|
53
|
+
displayName?: string,
|
|
54
|
+
): Promise<string> {
|
|
55
|
+
return createCompanyFixture(request, token, displayName ?? `ARC-Test-Co-${uniq()}`)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Creates a subscription, transitions it to active, optionally sets the
|
|
60
|
+
* current term end (used by Expiring view test). Returns enough metadata
|
|
61
|
+
* for the test to navigate by id and assert by code.
|
|
62
|
+
*/
|
|
63
|
+
export async function seedActiveSubscription(
|
|
64
|
+
request: APIRequestContext,
|
|
65
|
+
token: string,
|
|
66
|
+
input: SeedSubscriptionInput,
|
|
67
|
+
): Promise<SeededSubscription> {
|
|
68
|
+
const code = input.code ?? `SUB-${uniq().toUpperCase()}`
|
|
69
|
+
const name = input.name ?? `ARC Test Subscription ${uniq()}`
|
|
70
|
+
const items = input.items ?? [{ name: 'Default Item', mrcAmount: input.mrcAmount ?? 100 }]
|
|
71
|
+
|
|
72
|
+
const createRes = await apiRequest(request, 'POST', '/api/cpq/inventory/subscriptions', {
|
|
73
|
+
token,
|
|
74
|
+
data: {
|
|
75
|
+
customerId: input.customerId,
|
|
76
|
+
code,
|
|
77
|
+
name,
|
|
78
|
+
billingCycle: input.billingCycle ?? 'monthly',
|
|
79
|
+
currencyCode: input.currencyCode ?? 'USD',
|
|
80
|
+
termMonths: input.termMonths ?? 12,
|
|
81
|
+
items,
|
|
82
|
+
},
|
|
83
|
+
})
|
|
84
|
+
expect(
|
|
85
|
+
createRes.ok(),
|
|
86
|
+
`subscription POST returned ${createRes.status()}: ${await createRes.text()}`,
|
|
87
|
+
).toBeTruthy()
|
|
88
|
+
const created = (await createRes.json()) as { id: string; code: string; name: string; currencyCode: string; billingCycle: string; mrcAmount: string }
|
|
89
|
+
|
|
90
|
+
// Pending → active.
|
|
91
|
+
const transitionRes = await apiRequest(
|
|
92
|
+
request,
|
|
93
|
+
'POST',
|
|
94
|
+
'/api/cpq/inventory/subscriptions/status',
|
|
95
|
+
{
|
|
96
|
+
token,
|
|
97
|
+
data: { id: created.id, targetStatus: 'active' },
|
|
98
|
+
},
|
|
99
|
+
)
|
|
100
|
+
expect(
|
|
101
|
+
transitionRes.ok(),
|
|
102
|
+
`status transition returned ${transitionRes.status()}: ${await transitionRes.text()}`,
|
|
103
|
+
).toBeTruthy()
|
|
104
|
+
|
|
105
|
+
// Optional: stamp currentTermEnd via PUT for the Expiring view test.
|
|
106
|
+
if (input.currentTermEnd) {
|
|
107
|
+
const updateRes = await apiRequest(request, 'PUT', '/api/cpq/inventory/subscriptions', {
|
|
108
|
+
token,
|
|
109
|
+
data: { id: created.id, currentTermEnd: input.currentTermEnd },
|
|
110
|
+
})
|
|
111
|
+
expect(
|
|
112
|
+
updateRes.ok(),
|
|
113
|
+
`subscription PUT returned ${updateRes.status()}: ${await updateRes.text()}`,
|
|
114
|
+
).toBeTruthy()
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
return {
|
|
118
|
+
id: created.id,
|
|
119
|
+
customerId: input.customerId,
|
|
120
|
+
code: created.code,
|
|
121
|
+
name: created.name,
|
|
122
|
+
currencyCode: created.currencyCode,
|
|
123
|
+
billingCycle: created.billingCycle,
|
|
124
|
+
mrcAmount: created.mrcAmount,
|
|
125
|
+
currentTermEnd: input.currentTermEnd ?? null,
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Creates a subscription, takes it to `active`, then transitions to the
|
|
131
|
+
* given non-active status (`suspended` / `terminated` / `expired`). Used by
|
|
132
|
+
* the ARC suspended-target test (scenario 6) and the terminal-status
|
|
133
|
+
* negative test (scenario 7) where ARC buttons must not render.
|
|
134
|
+
*/
|
|
135
|
+
export async function seedSubscriptionInStatus(
|
|
136
|
+
request: APIRequestContext,
|
|
137
|
+
token: string,
|
|
138
|
+
input: SeedSubscriptionInput,
|
|
139
|
+
status: 'suspended' | 'terminated' | 'expired',
|
|
140
|
+
): Promise<SeededSubscription> {
|
|
141
|
+
const sub = await seedActiveSubscription(request, token, input)
|
|
142
|
+
const res = await apiRequest(
|
|
143
|
+
request,
|
|
144
|
+
'POST',
|
|
145
|
+
'/api/cpq/inventory/subscriptions/status',
|
|
146
|
+
{ token, data: { id: sub.id, targetStatus: status } },
|
|
147
|
+
)
|
|
148
|
+
expect(
|
|
149
|
+
res.ok(),
|
|
150
|
+
`transition to ${status} returned ${res.status()}: ${await res.text()}`,
|
|
151
|
+
).toBeTruthy()
|
|
152
|
+
return sub
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Cleanup: soft-delete created subscriptions, swallow errors so teardown
|
|
157
|
+
* never aborts subsequent cleanup.
|
|
158
|
+
*/
|
|
159
|
+
export async function deleteSubscription(
|
|
160
|
+
request: APIRequestContext,
|
|
161
|
+
token: string,
|
|
162
|
+
id: string,
|
|
163
|
+
): Promise<void> {
|
|
164
|
+
await apiRequest(request, 'DELETE', '/api/cpq/inventory/subscriptions', {
|
|
165
|
+
token,
|
|
166
|
+
data: { id },
|
|
167
|
+
}).catch(() => undefined)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export async function deleteCustomer(
|
|
171
|
+
request: APIRequestContext,
|
|
172
|
+
token: string,
|
|
173
|
+
id: string,
|
|
174
|
+
): Promise<void> {
|
|
175
|
+
await apiRequest(request, 'DELETE', '/api/customers/companies', {
|
|
176
|
+
token,
|
|
177
|
+
data: { id },
|
|
178
|
+
}).catch(() => undefined)
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Compute an ISO date (YYYY-MM-DD) `daysFromNow` days into the future. Used
|
|
183
|
+
* by the Expiring view test to build subscriptions with predictable
|
|
184
|
+
* term-end dates. Always returns the UTC calendar date — runner timezone
|
|
185
|
+
* doesn't affect the ±1 day boundary that the 30/90-day window asserts on.
|
|
186
|
+
* Negative values are accepted (returns a past date).
|
|
187
|
+
*/
|
|
188
|
+
export function daysFromNow(days: number): string {
|
|
189
|
+
return new Date(Date.now() + days * 24 * 60 * 60 * 1000).toISOString().split('T')[0]
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Hide globally floating widgets (Feedback FAB, Next.js dev toolbar) so they
|
|
194
|
+
* don't intercept clicks on drawer / page buttons. Safe to call multiple times.
|
|
195
|
+
*/
|
|
196
|
+
export async function hideFloatingOverlays(page: Page): Promise<void> {
|
|
197
|
+
await page
|
|
198
|
+
.addStyleTag({
|
|
199
|
+
content: `
|
|
200
|
+
button[aria-label="Open feedback form"],
|
|
201
|
+
nextjs-portal,
|
|
202
|
+
[data-nextjs-toast-wrapper] {
|
|
203
|
+
display: none !important;
|
|
204
|
+
}
|
|
205
|
+
`,
|
|
206
|
+
})
|
|
207
|
+
.catch(() => undefined)
|
|
208
|
+
}
|