@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,55 @@
|
|
|
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 { createCompanyFixture } from '@open-mercato/core/helpers/integration/crmFixtures'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* TC-CPQ-008: Quotes list + open detail (UI)
|
|
8
|
+
* Category: CPQ — UI
|
|
9
|
+
* Priority: High
|
|
10
|
+
*
|
|
11
|
+
* Creates a quote via API, then verifies it shows up on the list page and
|
|
12
|
+
* navigating to the detail URL renders without error.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const uniq = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
|
|
16
|
+
|
|
17
|
+
test.describe('TC-CPQ-008: Quotes list (UI)', () => {
|
|
18
|
+
test('admin opens the quotes list and sees the heading + table headers', async ({ page }) => {
|
|
19
|
+
await login(page, 'admin')
|
|
20
|
+
await page.goto('/backend/cpq/quotes', { waitUntil: 'domcontentloaded' })
|
|
21
|
+
|
|
22
|
+
// Title now lives inside DataTable as <h2> (CpqListView refactor).
|
|
23
|
+
await expect(page.getByRole('heading', { name: /CPQ Quotes/i }).first()).toBeVisible({ timeout: 15_000 })
|
|
24
|
+
await expect(page.getByRole('button', { name: /New Quote/i })).toBeVisible({ timeout: 15_000 })
|
|
25
|
+
|
|
26
|
+
// Table renders OR empty state appears. NOTE: cross-org admin scopes were observed during
|
|
27
|
+
// initial development — admins may see different quote rows depending on the active org context,
|
|
28
|
+
// so we don't pin to a specific row. UI list-load smoke is the goal here.
|
|
29
|
+
// Column was renamed "Quote ID" → "Quote Number" when the list started
|
|
30
|
+
// resolving SalesQuote.quoteNumber server-side (commit 209ca7a).
|
|
31
|
+
// Use getByText instead of role=columnheader because tanstack-table
|
|
32
|
+
// wraps headers in a sort-button container, and the accessibility
|
|
33
|
+
// name on the <th> often doesn't surface the plain header text.
|
|
34
|
+
const tableHeader = page.getByText(/Quote Number/i)
|
|
35
|
+
const emptyState = page.getByText(/no quote|empty|create one/i)
|
|
36
|
+
await expect(tableHeader.or(emptyState).first()).toBeVisible({ timeout: 15_000 })
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
test('quote detail page renders for a known quote id', async ({ page, request }) => {
|
|
40
|
+
const token = await getAuthToken(request, 'admin')
|
|
41
|
+
const customerId = await createCompanyFixture(request, token, `Acme ${uniq()}`)
|
|
42
|
+
|
|
43
|
+
const quoteRes = await apiRequest(request, 'POST', '/api/cpq/quotes', {
|
|
44
|
+
token,
|
|
45
|
+
data: { customerId, currencyCode: 'USD' },
|
|
46
|
+
})
|
|
47
|
+
const quote = (await quoteRes.json()) as { id: string }
|
|
48
|
+
|
|
49
|
+
await login(page, 'admin')
|
|
50
|
+
await page.goto(`/backend/cpq/quotes/${quote.id}`, { waitUntil: 'domcontentloaded' })
|
|
51
|
+
|
|
52
|
+
// Stays on the detail URL (no auth bounce / error redirect).
|
|
53
|
+
expect(page.url()).toContain(`/backend/cpq/quotes/${quote.id}`)
|
|
54
|
+
})
|
|
55
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
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
|
+
|
|
5
|
+
/**
|
|
6
|
+
* TC-CPQ-009: Pricing tables list (UI)
|
|
7
|
+
* Category: CPQ — UI
|
|
8
|
+
* Priority: Medium
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const uniq = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
|
|
12
|
+
|
|
13
|
+
test.describe('TC-CPQ-009: Pricing tables list (UI)', () => {
|
|
14
|
+
test('renders heading + freshly created table appears in the list', async ({ page, request }) => {
|
|
15
|
+
const token = await getAuthToken(request, 'admin')
|
|
16
|
+
|
|
17
|
+
const code = `PT-${uniq()}`
|
|
18
|
+
const created = await apiRequest(request, 'POST', '/api/cpq/pricing-tables', {
|
|
19
|
+
token,
|
|
20
|
+
data: {
|
|
21
|
+
code,
|
|
22
|
+
name: `UI Pricing Table ${code}`,
|
|
23
|
+
dimensions: [{ key: 'dc', label: 'Datacenter' }],
|
|
24
|
+
priceColumns: [{ key: 'list', label: 'List' }],
|
|
25
|
+
currencyCodeList: ['USD'],
|
|
26
|
+
},
|
|
27
|
+
})
|
|
28
|
+
expect(created.ok(), `pricing table POST returned ${created.status()}`).toBeTruthy()
|
|
29
|
+
const table = (await created.json()) as { id: string }
|
|
30
|
+
|
|
31
|
+
try {
|
|
32
|
+
await login(page, 'admin')
|
|
33
|
+
await page.goto('/backend/cpq/pricing', { waitUntil: 'domcontentloaded' })
|
|
34
|
+
|
|
35
|
+
// Title now lives inside DataTable as <h2> (CpqListView refactor).
|
|
36
|
+
await expect(page.getByRole('heading', { name: /Pricing Tables/i }).first()).toBeVisible({ timeout: 15_000 })
|
|
37
|
+
await expect(page.locator('body')).toContainText(code, { timeout: 15_000 })
|
|
38
|
+
} finally {
|
|
39
|
+
await apiRequest(request, 'DELETE', '/api/cpq/pricing-tables', {
|
|
40
|
+
token,
|
|
41
|
+
data: { id: table.id },
|
|
42
|
+
}).catch(() => undefined)
|
|
43
|
+
}
|
|
44
|
+
})
|
|
45
|
+
})
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TC-CPQ-010: Inventory section navigation (UI)
|
|
6
|
+
* Category: CPQ — UI
|
|
7
|
+
* Priority: Medium
|
|
8
|
+
*
|
|
9
|
+
* Inventory page exposes Subscriptions / Assets tabs. Verifies both
|
|
10
|
+
* are reachable and the heading is consistent.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
test.describe('TC-CPQ-010: Inventory tabs (UI)', () => {
|
|
14
|
+
test('admin lands on inventory page and can switch tabs', async ({ page }) => {
|
|
15
|
+
await login(page, 'admin')
|
|
16
|
+
await page.goto('/backend/cpq/inventory', { waitUntil: 'domcontentloaded' })
|
|
17
|
+
|
|
18
|
+
// Page-level h1 ("Customer Inventory") still exists; tab tables
|
|
19
|
+
// render their own <h2>. Either heading proves the page loaded.
|
|
20
|
+
await expect(
|
|
21
|
+
page.getByRole('heading', { name: /Customer Inventory|CPQ Subscriptions|Subscriptions/i }).first(),
|
|
22
|
+
).toBeVisible({ timeout: 10_000 })
|
|
23
|
+
|
|
24
|
+
// Tabs (defined in code as { subscriptions, assets })
|
|
25
|
+
const subsTab = page.getByRole('button', { name: /Subscriptions/i }).first()
|
|
26
|
+
const assetsTab = page.getByRole('button', { name: /^Assets$/i }).first()
|
|
27
|
+
|
|
28
|
+
if (await subsTab.isVisible().catch(() => false)) {
|
|
29
|
+
await subsTab.click()
|
|
30
|
+
await expect(page.locator('body')).toContainText(/Subscriptions|No inventory/i, {
|
|
31
|
+
timeout: 5_000,
|
|
32
|
+
})
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (await assetsTab.isVisible().catch(() => false)) {
|
|
36
|
+
await assetsTab.click()
|
|
37
|
+
await expect(page.locator('body')).toContainText(/Assets|No inventory/i, {
|
|
38
|
+
timeout: 5_000,
|
|
39
|
+
})
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
test('subscription detail route does not 500 for a non-existent id (graceful 404 / empty)', async ({ page }) => {
|
|
44
|
+
await login(page, 'admin')
|
|
45
|
+
const fakeId = '00000000-0000-4000-8000-000000000000'
|
|
46
|
+
const res = await page.goto(`/backend/cpq/inventory/subscriptions/${fakeId}`, {
|
|
47
|
+
waitUntil: 'domcontentloaded',
|
|
48
|
+
})
|
|
49
|
+
// Either renders an empty/404-ish state OR the dev page returns 404 — both are acceptable.
|
|
50
|
+
if (res) {
|
|
51
|
+
expect([200, 404]).toContain(res.status())
|
|
52
|
+
}
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TC-CPQ-011: Orders list page (UI)
|
|
6
|
+
* Category: CPQ — UI
|
|
7
|
+
* Priority: Medium
|
|
8
|
+
*
|
|
9
|
+
* Smoke test for the orders list — empty state must not crash, headings are
|
|
10
|
+
* present, and the page loads under admin auth.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
test.describe('TC-CPQ-011: Orders list page (UI)', () => {
|
|
14
|
+
test('admin can open the orders list', async ({ page }) => {
|
|
15
|
+
await login(page, 'admin')
|
|
16
|
+
await page.goto('/backend/cpq/orders', { waitUntil: 'domcontentloaded' })
|
|
17
|
+
|
|
18
|
+
// Title now lives inside DataTable as <h2> (CpqListView refactor).
|
|
19
|
+
await expect(page.getByRole('heading', { name: /CPQ Orders/i }).first()).toBeVisible({ timeout: 15_000 })
|
|
20
|
+
|
|
21
|
+
// Page rendered some content — table or empty-state message
|
|
22
|
+
await expect(page.locator('body')).not.toBeEmpty()
|
|
23
|
+
})
|
|
24
|
+
})
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { test, expect } from '@playwright/test'
|
|
2
|
+
import { login } from '@open-mercato/core/helpers/integration/auth'
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TC-CPQ-012 — Inline "Expiring within N days" picker smoke (UI)
|
|
6
|
+
*
|
|
7
|
+
* Commit `0992f60` / `071568d` replaced the retired `/expiring` page with
|
|
8
|
+
* an inline picker on the main subscriptions list, mounted in the
|
|
9
|
+
* DataTable title row. This test is a focused smoke check that the
|
|
10
|
+
* picker is present, defaults to "All subscriptions", and can be opened
|
|
11
|
+
* to reveal the canonical preset windows (7 / 14 / 30 / 60 / 90 days).
|
|
12
|
+
*
|
|
13
|
+
* Heavier data-level coverage (seeded subs, before/after window swap)
|
|
14
|
+
* lives in TC-CPQ-ARC-005 — this spec stays small so it catches
|
|
15
|
+
* "picker disappeared" regressions in seconds.
|
|
16
|
+
*/
|
|
17
|
+
// Radix Select.Trigger renders a `<button role="combobox">` whose
|
|
18
|
+
// accessible name comes from the SelectValue children. Looking it up by
|
|
19
|
+
// `getByRole('combobox', { name: /All subscriptions/ })` has been
|
|
20
|
+
// brittle in CI (Radix can render SelectValue before the matching
|
|
21
|
+
// SelectItem mounts in its Portal, leaving the trigger temporarily
|
|
22
|
+
// nameless). The picker is the only combobox visible on the page until
|
|
23
|
+
// the Filters overlay is opened, so `.locator('[role="combobox"]')`
|
|
24
|
+
// is unambiguous *and* doesn't depend on accessibility-name timing.
|
|
25
|
+
const pickerLocator = '[role="combobox"]'
|
|
26
|
+
|
|
27
|
+
test.describe('TC-CPQ-012: Inline expiring picker (UI)', () => {
|
|
28
|
+
test('picker renders in title row with the 5 preset windows', async ({ page }) => {
|
|
29
|
+
await login(page, 'admin')
|
|
30
|
+
await page.goto('/backend/cpq/inventory', { waitUntil: 'domcontentloaded' })
|
|
31
|
+
|
|
32
|
+
// Wait for the inline picker. Visible text content is "All
|
|
33
|
+
// subscriptions" (default value) once Radix mounts.
|
|
34
|
+
const picker = page.locator(pickerLocator).first()
|
|
35
|
+
await expect(picker).toBeVisible({ timeout: 15_000 })
|
|
36
|
+
await expect(picker).toContainText(/All subscriptions/i, { timeout: 10_000 })
|
|
37
|
+
|
|
38
|
+
// Open the dropdown and verify the 5 canonical preset windows are
|
|
39
|
+
// listed in the same order the operator's renewal-watch UX expects.
|
|
40
|
+
await picker.click()
|
|
41
|
+
for (const days of [7, 14, 30, 60, 90]) {
|
|
42
|
+
await expect(
|
|
43
|
+
page.getByRole('option', { name: new RegExp(`Expiring in ${days} days`, 'i') }),
|
|
44
|
+
).toBeVisible({ timeout: 5_000 })
|
|
45
|
+
}
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
test('selecting a window updates the picker label without leaving the page', async ({ page }) => {
|
|
49
|
+
await login(page, 'admin')
|
|
50
|
+
await page.goto('/backend/cpq/inventory', { waitUntil: 'domcontentloaded' })
|
|
51
|
+
|
|
52
|
+
const trigger = page.locator(pickerLocator).first()
|
|
53
|
+
await expect(trigger).toBeVisible({ timeout: 15_000 })
|
|
54
|
+
await trigger.click()
|
|
55
|
+
await page.getByRole('option', { name: /Expiring in 30 days/i }).click()
|
|
56
|
+
|
|
57
|
+
// Same URL — the picker writes into the in-page filter state, not a
|
|
58
|
+
// route param. Picker now reflects the chosen window.
|
|
59
|
+
expect(page.url()).toContain('/backend/cpq/inventory')
|
|
60
|
+
await expect(trigger).toContainText(/Expiring in 30 days/i, { timeout: 5_000 })
|
|
61
|
+
})
|
|
62
|
+
})
|
|
@@ -0,0 +1,52 @@
|
|
|
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-001 — Amend flow via UI (XD-250)
|
|
14
|
+
*
|
|
15
|
+
* Operator opens an active subscription, clicks "Amend", drawer attaches the
|
|
16
|
+
* source sub as the (sole) target, submits. The quote detail page renders
|
|
17
|
+
* the ARC summary banner with the attached target.
|
|
18
|
+
*/
|
|
19
|
+
test.describe('TC-CPQ-ARC-001 — Amend flow (UI)', () => {
|
|
20
|
+
test('opens Amend drawer from subscription detail and the new quote shows the target in the ARC banner', async ({
|
|
21
|
+
page,
|
|
22
|
+
request,
|
|
23
|
+
}) => {
|
|
24
|
+
const token = await getAuthToken(request, 'admin')
|
|
25
|
+
const customerId = await seedCustomer(request, token)
|
|
26
|
+
const sub = await seedActiveSubscription(request, token, { customerId })
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await login(page, 'admin')
|
|
30
|
+
|
|
31
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
32
|
+
waitUntil: 'domcontentloaded',
|
|
33
|
+
})
|
|
34
|
+
await hideFloatingOverlays(page)
|
|
35
|
+
|
|
36
|
+
// Header buttons render only for active/suspended subs.
|
|
37
|
+
const amendBtn = page.getByRole('button', { name: /^Amend$/ })
|
|
38
|
+
await expect(amendBtn).toBeVisible({ timeout: 15_000 })
|
|
39
|
+
|
|
40
|
+
await amendBtn.click()
|
|
41
|
+
|
|
42
|
+
// After click, startArcQuote() POSTs from-subscription then router.push()es
|
|
43
|
+
// to /backend/cpq/quotes/[newId]. Poll for an element specific to the
|
|
44
|
+
// quote detail with the ARC banner — survives Turbopack cold-compile.
|
|
45
|
+
await expect(page.locator('body')).toContainText(sub.code, { timeout: 15_000 })
|
|
46
|
+
await expect(page.getByText(/1 target/i).first()).toBeVisible({ timeout: 5_000 })
|
|
47
|
+
} finally {
|
|
48
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
49
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
50
|
+
}
|
|
51
|
+
})
|
|
52
|
+
})
|
|
@@ -0,0 +1,65 @@
|
|
|
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-002 — Multi-target Amend (UI) (XD-250)
|
|
14
|
+
*
|
|
15
|
+
* Operator starts an Amend quote on subscription A, opens the "Modify
|
|
16
|
+
* subscription" drawer, attaches subscription B as a 2nd target. The ARC
|
|
17
|
+
* banner then shows "2 targets".
|
|
18
|
+
*/
|
|
19
|
+
test.describe('TC-CPQ-ARC-002 — Multi-target Amend (UI)', () => {
|
|
20
|
+
test('attaching a 2nd target via the drawer shows "2 targets" in the ARC banner', async ({
|
|
21
|
+
page,
|
|
22
|
+
request,
|
|
23
|
+
}) => {
|
|
24
|
+
const token = await getAuthToken(request, 'admin')
|
|
25
|
+
const customerId = await seedCustomer(request, token)
|
|
26
|
+
const subA = await seedActiveSubscription(request, token, { customerId, name: 'Sub A' })
|
|
27
|
+
const subB = await seedActiveSubscription(request, token, { customerId, name: 'Sub B' })
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
await login(page, 'admin')
|
|
31
|
+
|
|
32
|
+
// Open Amend on sub A — quote pre-filled with A as the target.
|
|
33
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${subA.id}`, {
|
|
34
|
+
waitUntil: 'domcontentloaded',
|
|
35
|
+
})
|
|
36
|
+
await hideFloatingOverlays(page)
|
|
37
|
+
await page.getByRole('button', { name: /^Amend$/ }).click()
|
|
38
|
+
await expect(page.locator('body')).toContainText(subA.code, { timeout: 15_000 })
|
|
39
|
+
await hideFloatingOverlays(page)
|
|
40
|
+
|
|
41
|
+
// Open the configurator drawer.
|
|
42
|
+
await page.getByRole('button', { name: /Modify subscription/i }).click()
|
|
43
|
+
await hideFloatingOverlays(page)
|
|
44
|
+
const drawer = page.getByRole('dialog')
|
|
45
|
+
|
|
46
|
+
// Drawer opens at "pick-targets" because quote_type is already 'amend'.
|
|
47
|
+
// Pre-existing target (subA) is checked; subB row is unchecked. Tick subB.
|
|
48
|
+
const subBLabel = drawer.locator('label', { hasText: subB.code }).first()
|
|
49
|
+
await expect(subBLabel).toBeVisible({ timeout: 10_000 })
|
|
50
|
+
await subBLabel.locator('input[type="checkbox"]').check()
|
|
51
|
+
|
|
52
|
+
// Drawer step machine: Next → review → Submit.
|
|
53
|
+
await drawer.getByRole('button', { name: /^Next$/ }).click()
|
|
54
|
+
await drawer.getByRole('button', { name: /^Submit$/ }).click()
|
|
55
|
+
|
|
56
|
+
// After submit, drawer reaches 'done'. Close returns to host quote page.
|
|
57
|
+
await drawer.getByRole('button', { name: /^Close$/ }).click()
|
|
58
|
+
await expect(page.getByText(/2 targets/i)).toBeVisible({ timeout: 10_000 })
|
|
59
|
+
} finally {
|
|
60
|
+
await deleteSubscription(request, token, subA.id).catch(() => undefined)
|
|
61
|
+
await deleteSubscription(request, token, subB.id).catch(() => undefined)
|
|
62
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
})
|
|
@@ -0,0 +1,107 @@
|
|
|
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-003 — Merge renewal (UI) (XD-250)
|
|
14
|
+
*
|
|
15
|
+
* Operator starts a Renew quote on subscription A (becomes the first target,
|
|
16
|
+
* standalone). They open the drawer, attach 2 more subs (B + C). The drawer
|
|
17
|
+
* automatically flips to merge mode. They fill in the new contract term,
|
|
18
|
+
* submit. Banner shows "Merging into a new contract" + 3 targets.
|
|
19
|
+
*/
|
|
20
|
+
test.describe('TC-CPQ-ARC-003 — Merge renewal (UI)', () => {
|
|
21
|
+
test('attaching ≥2 renew targets enters merge mode and the banner shows the merge marker', async ({
|
|
22
|
+
page,
|
|
23
|
+
request,
|
|
24
|
+
}) => {
|
|
25
|
+
const token = await getAuthToken(request, 'admin')
|
|
26
|
+
const customerId = await seedCustomer(request, token)
|
|
27
|
+
const subA = await seedActiveSubscription(request, token, {
|
|
28
|
+
customerId,
|
|
29
|
+
name: 'Sub Alpha',
|
|
30
|
+
billingCycle: 'monthly',
|
|
31
|
+
})
|
|
32
|
+
const subB = await seedActiveSubscription(request, token, {
|
|
33
|
+
customerId,
|
|
34
|
+
name: 'Sub Bravo',
|
|
35
|
+
billingCycle: 'monthly',
|
|
36
|
+
})
|
|
37
|
+
const subC = await seedActiveSubscription(request, token, {
|
|
38
|
+
customerId,
|
|
39
|
+
name: 'Sub Charlie',
|
|
40
|
+
billingCycle: 'monthly',
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
await login(page, 'admin')
|
|
45
|
+
|
|
46
|
+
// Start a Renew quote on subA.
|
|
47
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${subA.id}`, {
|
|
48
|
+
waitUntil: 'domcontentloaded',
|
|
49
|
+
})
|
|
50
|
+
await hideFloatingOverlays(page)
|
|
51
|
+
await page.getByRole('button', { name: /^Renew$/ }).click()
|
|
52
|
+
await expect(page.locator('body')).toContainText(subA.code, { timeout: 15_000 })
|
|
53
|
+
await hideFloatingOverlays(page)
|
|
54
|
+
|
|
55
|
+
// Open drawer, attach subB + subC. The drawer auto-flips to merge mode
|
|
56
|
+
// when 2+ renew targets are picked.
|
|
57
|
+
await page.getByRole('button', { name: /Modify subscription/i }).click()
|
|
58
|
+
await hideFloatingOverlays(page)
|
|
59
|
+
const drawer = page.getByRole('dialog')
|
|
60
|
+
|
|
61
|
+
const subBLabel = drawer.locator('label', { hasText: subB.code }).first()
|
|
62
|
+
await expect(subBLabel).toBeVisible({ timeout: 10_000 })
|
|
63
|
+
await subBLabel.locator('input[type="checkbox"]').check()
|
|
64
|
+
|
|
65
|
+
const subCLabel = drawer.locator('label', { hasText: subC.code }).first()
|
|
66
|
+
await subCLabel.locator('input[type="checkbox"]').check()
|
|
67
|
+
|
|
68
|
+
// Merge banner inside drawer pick-targets step.
|
|
69
|
+
await expect(
|
|
70
|
+
drawer.getByText(/Merging \d+ contracts into a new one/i),
|
|
71
|
+
).toBeVisible({ timeout: 5_000 })
|
|
72
|
+
|
|
73
|
+
// Next → config-renew (merge form).
|
|
74
|
+
await drawer.getByRole('button', { name: /^Next$/ }).click()
|
|
75
|
+
|
|
76
|
+
// Fill quote-level merge term.
|
|
77
|
+
const startDate = new Date(Date.now() + 30 * 24 * 60 * 60 * 1000)
|
|
78
|
+
.toISOString()
|
|
79
|
+
.split('T')[0]
|
|
80
|
+
const endDate = new Date(Date.now() + 395 * 24 * 60 * 60 * 1000)
|
|
81
|
+
.toISOString()
|
|
82
|
+
.split('T')[0]
|
|
83
|
+
|
|
84
|
+
// Drawer renders 3 date/number inputs labelled Start / End / Months for
|
|
85
|
+
// the merge form. Scope to drawer to avoid any other date inputs on the
|
|
86
|
+
// host page.
|
|
87
|
+
const dateInputs = drawer.locator('input[type="date"]')
|
|
88
|
+
await dateInputs.nth(0).fill(startDate)
|
|
89
|
+
await dateInputs.nth(1).fill(endDate)
|
|
90
|
+
|
|
91
|
+
await drawer.getByRole('button', { name: /^Next$/ }).click()
|
|
92
|
+
await drawer.getByRole('button', { name: /^Submit$/ }).click()
|
|
93
|
+
await drawer.getByRole('button', { name: /^Close$/ }).click()
|
|
94
|
+
|
|
95
|
+
// Host page banner now reflects merge mode + 3 targets.
|
|
96
|
+
await expect(page.getByText(/3 targets/i)).toBeVisible({ timeout: 10_000 })
|
|
97
|
+
await expect(page.getByText(/Merging into a new contract/i)).toBeVisible({
|
|
98
|
+
timeout: 5_000,
|
|
99
|
+
})
|
|
100
|
+
} finally {
|
|
101
|
+
await deleteSubscription(request, token, subA.id).catch(() => undefined)
|
|
102
|
+
await deleteSubscription(request, token, subB.id).catch(() => undefined)
|
|
103
|
+
await deleteSubscription(request, token, subC.id).catch(() => undefined)
|
|
104
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
105
|
+
}
|
|
106
|
+
})
|
|
107
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
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-004 — Cancel quote with reason + ETF (UI) (XD-250)
|
|
14
|
+
*
|
|
15
|
+
* Operator opens an active subscription, clicks "Cancel", drawer pre-fills
|
|
16
|
+
* with the source sub as the cancel target. They open the configurator drawer
|
|
17
|
+
* to stamp reason + ETF, submit. The ARC banner shows type=cancel.
|
|
18
|
+
*/
|
|
19
|
+
test.describe('TC-CPQ-ARC-004 — Cancel quote (UI)', () => {
|
|
20
|
+
test('Cancel button creates a cancel quote, drawer stamps reason + ETF, banner shows CANCEL', async ({
|
|
21
|
+
page,
|
|
22
|
+
request,
|
|
23
|
+
}) => {
|
|
24
|
+
const token = await getAuthToken(request, 'admin')
|
|
25
|
+
const customerId = await seedCustomer(request, token)
|
|
26
|
+
const sub = await seedActiveSubscription(request, token, { customerId, name: 'To Cancel' })
|
|
27
|
+
|
|
28
|
+
try {
|
|
29
|
+
await login(page, 'admin')
|
|
30
|
+
|
|
31
|
+
await page.goto(`/backend/cpq/inventory/subscriptions/${sub.id}`, {
|
|
32
|
+
waitUntil: 'domcontentloaded',
|
|
33
|
+
})
|
|
34
|
+
await hideFloatingOverlays(page)
|
|
35
|
+
await page.getByRole('button', { name: /^Cancel$/ }).click()
|
|
36
|
+
await expect(page.locator('body')).toContainText(sub.code, { timeout: 15_000 })
|
|
37
|
+
await hideFloatingOverlays(page)
|
|
38
|
+
|
|
39
|
+
// The new quote has quote_type='cancel'. Banner should show CANCEL.
|
|
40
|
+
await expect(page.getByText('CANCEL', { exact: false }).first()).toBeVisible({
|
|
41
|
+
timeout: 10_000,
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
// Open drawer to stamp cancel-meta.
|
|
45
|
+
await page.getByRole('button', { name: /Modify subscription/i }).click()
|
|
46
|
+
await hideFloatingOverlays(page)
|
|
47
|
+
|
|
48
|
+
// initialType is 'cancel', so drawer skips pick-action and starts at
|
|
49
|
+
// pick-targets. Sub is already attached. Next → config-cancel.
|
|
50
|
+
const drawer = page.getByRole('dialog')
|
|
51
|
+
await drawer.getByRole('button', { name: /^Next$/ }).click()
|
|
52
|
+
|
|
53
|
+
// Reason code dropdown — scope to drawer to avoid the global org combobox.
|
|
54
|
+
await drawer.locator('select').first().selectOption('non-payment')
|
|
55
|
+
|
|
56
|
+
// Cancel-meta inputs in drawer order: reasonText, etfAmount, etfCurrency.
|
|
57
|
+
// We only need to set the ETF amount.
|
|
58
|
+
const drawerTextInputs = drawer.locator('input[type="text"]')
|
|
59
|
+
await drawerTextInputs.nth(1).fill('500.00')
|
|
60
|
+
|
|
61
|
+
await drawer.getByRole('button', { name: /^Next$/ }).click()
|
|
62
|
+
await drawer.getByRole('button', { name: /^Submit$/ }).click()
|
|
63
|
+
await drawer.getByRole('button', { name: /^Close$/ }).click()
|
|
64
|
+
|
|
65
|
+
// After submit, the banner still shows CANCEL.
|
|
66
|
+
await expect(page.getByText('CANCEL', { exact: false }).first()).toBeVisible({
|
|
67
|
+
timeout: 10_000,
|
|
68
|
+
})
|
|
69
|
+
} finally {
|
|
70
|
+
await deleteSubscription(request, token, sub.id).catch(() => undefined)
|
|
71
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
})
|
|
@@ -0,0 +1,86 @@
|
|
|
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-005 — Expiring Subscriptions filter (UI) (XD-250)
|
|
15
|
+
*
|
|
16
|
+
* The standalone `/expiring` page was retired (commit fa5b899) in favour
|
|
17
|
+
* of an `expiringWithinDays` filter + saved-perspective workflow on the
|
|
18
|
+
* main subscriptions list. This test now drives the inline expiring
|
|
19
|
+
* picker that sits in the title row of the Subscriptions tab.
|
|
20
|
+
*
|
|
21
|
+
* Seeds 3 subscriptions with currentTermEnd at +5d / +20d / +60d.
|
|
22
|
+
* Picking "Expiring in 30 days" shows the first two; switching to "90
|
|
23
|
+
* days" reveals the third.
|
|
24
|
+
*/
|
|
25
|
+
test.describe('TC-CPQ-ARC-005 — Expiring view filter (UI)', () => {
|
|
26
|
+
test('30-day window includes only subs with currentTermEnd within the window', async ({
|
|
27
|
+
page,
|
|
28
|
+
request,
|
|
29
|
+
}) => {
|
|
30
|
+
const token = await getAuthToken(request, 'admin')
|
|
31
|
+
const customerId = await seedCustomer(request, token)
|
|
32
|
+
const subSoon = await seedActiveSubscription(request, token, {
|
|
33
|
+
customerId,
|
|
34
|
+
name: 'Expiring Soon',
|
|
35
|
+
currentTermEnd: daysFromNow(5),
|
|
36
|
+
})
|
|
37
|
+
const subSoonish = await seedActiveSubscription(request, token, {
|
|
38
|
+
customerId,
|
|
39
|
+
name: 'Expiring Soonish',
|
|
40
|
+
currentTermEnd: daysFromNow(20),
|
|
41
|
+
})
|
|
42
|
+
const subLater = await seedActiveSubscription(request, token, {
|
|
43
|
+
customerId,
|
|
44
|
+
name: 'Expiring Later',
|
|
45
|
+
currentTermEnd: daysFromNow(60),
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
try {
|
|
49
|
+
await login(page, 'admin')
|
|
50
|
+
|
|
51
|
+
// Land on the Subscriptions tab of the main inventory page.
|
|
52
|
+
await page.goto('/backend/cpq/inventory', { waitUntil: 'domcontentloaded' })
|
|
53
|
+
await hideFloatingOverlays(page)
|
|
54
|
+
|
|
55
|
+
// All three seeded subs initially visible (no window filter yet).
|
|
56
|
+
const body = page.locator('body')
|
|
57
|
+
await expect(body).toContainText(subSoon.code, { timeout: 15_000 })
|
|
58
|
+
|
|
59
|
+
// The inline picker is a Radix Select.Trigger with role=combobox.
|
|
60
|
+
// Accessibility-name matching is brittle (SelectValue can render
|
|
61
|
+
// before its item mounts), so we target the trigger element
|
|
62
|
+
// directly and rely on its visible text content for assertions.
|
|
63
|
+
const picker = page.locator('[role="combobox"]').first()
|
|
64
|
+
await expect(picker).toBeVisible({ timeout: 15_000 })
|
|
65
|
+
|
|
66
|
+
// Open and pick "30 days".
|
|
67
|
+
await picker.click()
|
|
68
|
+
await page.getByRole('option', { name: /Expiring in 30 days/i }).click()
|
|
69
|
+
|
|
70
|
+
// 30d window → Soon (5d) + Soonish (20d) visible, Later (60d) NOT.
|
|
71
|
+
await expect(body).toContainText(subSoon.code, { timeout: 15_000 })
|
|
72
|
+
await expect(body).toContainText(subSoonish.code, { timeout: 5_000 })
|
|
73
|
+
await expect(body).not.toContainText(subLater.code)
|
|
74
|
+
|
|
75
|
+
// Switch to 90 days → all three appear.
|
|
76
|
+
await picker.click()
|
|
77
|
+
await page.getByRole('option', { name: /Expiring in 90 days/i }).click()
|
|
78
|
+
await expect(body).toContainText(subLater.code, { timeout: 10_000 })
|
|
79
|
+
} finally {
|
|
80
|
+
await deleteSubscription(request, token, subSoon.id).catch(() => undefined)
|
|
81
|
+
await deleteSubscription(request, token, subSoonish.id).catch(() => undefined)
|
|
82
|
+
await deleteSubscription(request, token, subLater.id).catch(() => undefined)
|
|
83
|
+
await deleteCustomer(request, token, customerId).catch(() => undefined)
|
|
84
|
+
}
|
|
85
|
+
})
|
|
86
|
+
})
|