@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.
Files changed (504) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/LICENSE +21 -0
  3. package/MIGRATION.md +236 -0
  4. package/README.md +108 -0
  5. package/dist/modules/cpq/acl.js +30 -0
  6. package/dist/modules/cpq/acl.js.map +7 -0
  7. package/dist/modules/cpq/api/_helpers/handleArcError.js +37 -0
  8. package/dist/modules/cpq/api/_helpers/handleArcError.js.map +7 -0
  9. package/dist/modules/cpq/api/attribute-options/route.js +47 -0
  10. package/dist/modules/cpq/api/attribute-options/route.js.map +7 -0
  11. package/dist/modules/cpq/api/bundle-slots/route.js +101 -0
  12. package/dist/modules/cpq/api/bundle-slots/route.js.map +7 -0
  13. package/dist/modules/cpq/api/bundle-tree/route.js +28 -0
  14. package/dist/modules/cpq/api/bundle-tree/route.js.map +7 -0
  15. package/dist/modules/cpq/api/context.js +29 -0
  16. package/dist/modules/cpq/api/context.js.map +7 -0
  17. package/dist/modules/cpq/api/inventory/assets/route.js +113 -0
  18. package/dist/modules/cpq/api/inventory/assets/route.js.map +7 -0
  19. package/dist/modules/cpq/api/inventory/assets/status/route.js +35 -0
  20. package/dist/modules/cpq/api/inventory/assets/status/route.js.map +7 -0
  21. package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js +30 -0
  22. package/dist/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.js.map +7 -0
  23. package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js +31 -0
  24. package/dist/modules/cpq/api/inventory/customer/[customerId]/route.js.map +7 -0
  25. package/dist/modules/cpq/api/inventory/subscription-items/route.js +99 -0
  26. package/dist/modules/cpq/api/inventory/subscription-items/route.js.map +7 -0
  27. package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js +57 -0
  28. package/dist/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.js.map +7 -0
  29. package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js +62 -0
  30. package/dist/modules/cpq/api/inventory/subscriptions/expiring/route.js.map +7 -0
  31. package/dist/modules/cpq/api/inventory/subscriptions/route.js +116 -0
  32. package/dist/modules/cpq/api/inventory/subscriptions/route.js.map +7 -0
  33. package/dist/modules/cpq/api/inventory/subscriptions/status/route.js +35 -0
  34. package/dist/modules/cpq/api/inventory/subscriptions/status/route.js.map +7 -0
  35. package/dist/modules/cpq/api/offering-components/route.js +91 -0
  36. package/dist/modules/cpq/api/offering-components/route.js.map +7 -0
  37. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +39 -0
  38. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +7 -0
  39. package/dist/modules/cpq/api/orders/[orderId]/route.js +28 -0
  40. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +7 -0
  41. package/dist/modules/cpq/api/orders/[orderId]/status/route.js +38 -0
  42. package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +7 -0
  43. package/dist/modules/cpq/api/orders/route.js +71 -0
  44. package/dist/modules/cpq/api/orders/route.js.map +7 -0
  45. package/dist/modules/cpq/api/price-rules/route.js +180 -0
  46. package/dist/modules/cpq/api/price-rules/route.js.map +7 -0
  47. package/dist/modules/cpq/api/pricing-table-entries/route.js +142 -0
  48. package/dist/modules/cpq/api/pricing-table-entries/route.js.map +7 -0
  49. package/dist/modules/cpq/api/pricing-tables/route.js +179 -0
  50. package/dist/modules/cpq/api/pricing-tables/route.js.map +7 -0
  51. package/dist/modules/cpq/api/product-attributes/route.js +160 -0
  52. package/dist/modules/cpq/api/product-attributes/route.js.map +7 -0
  53. package/dist/modules/cpq/api/product-charges/route.js +166 -0
  54. package/dist/modules/cpq/api/product-charges/route.js.map +7 -0
  55. package/dist/modules/cpq/api/product-offerings/route.js +294 -0
  56. package/dist/modules/cpq/api/product-offerings/route.js.map +7 -0
  57. package/dist/modules/cpq/api/product-relationships/route.js +144 -0
  58. package/dist/modules/cpq/api/product-relationships/route.js.map +7 -0
  59. package/dist/modules/cpq/api/product-specifications/route.js +292 -0
  60. package/dist/modules/cpq/api/product-specifications/route.js.map +7 -0
  61. package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js +71 -0
  62. package/dist/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.js.map +7 -0
  63. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +29 -0
  64. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +7 -0
  65. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +54 -0
  66. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +7 -0
  67. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -0
  68. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +7 -0
  69. package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js +72 -0
  70. package/dist/modules/cpq/api/quotes/[quoteId]/merge-meta/route.js.map +7 -0
  71. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +35 -0
  72. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +7 -0
  73. package/dist/modules/cpq/api/quotes/[quoteId]/route.js +102 -0
  74. package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +7 -0
  75. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +27 -0
  76. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +7 -0
  77. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js +79 -0
  78. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.js.map +7 -0
  79. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js +95 -0
  80. package/dist/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.js.map +7 -0
  81. package/dist/modules/cpq/api/quotes/attributes/route.js +52 -0
  82. package/dist/modules/cpq/api/quotes/attributes/route.js.map +7 -0
  83. package/dist/modules/cpq/api/quotes/from-subscription/route.js +72 -0
  84. package/dist/modules/cpq/api/quotes/from-subscription/route.js.map +7 -0
  85. package/dist/modules/cpq/api/quotes/price/route.js +69 -0
  86. package/dist/modules/cpq/api/quotes/price/route.js.map +7 -0
  87. package/dist/modules/cpq/api/quotes/route.js +97 -0
  88. package/dist/modules/cpq/api/quotes/route.js.map +7 -0
  89. package/dist/modules/cpq/api/quotes/validate-relationships/route.js +86 -0
  90. package/dist/modules/cpq/api/quotes/validate-relationships/route.js.map +7 -0
  91. package/dist/modules/cpq/api/resolveQuotingService.js +7 -0
  92. package/dist/modules/cpq/api/resolveQuotingService.js.map +7 -0
  93. package/dist/modules/cpq/api/wizards/resolve/route.js +38 -0
  94. package/dist/modules/cpq/api/wizards/resolve/route.js.map +7 -0
  95. package/dist/modules/cpq/api/wizards/route.js +111 -0
  96. package/dist/modules/cpq/api/wizards/route.js.map +7 -0
  97. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +136 -0
  98. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +7 -0
  99. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +185 -0
  100. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +7 -0
  101. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +343 -0
  102. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +7 -0
  103. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +13 -0
  104. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +7 -0
  105. package/dist/modules/cpq/backend/cpq/inventory/page.js +35 -0
  106. package/dist/modules/cpq/backend/cpq/inventory/page.js.map +7 -0
  107. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +18 -0
  108. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +7 -0
  109. package/dist/modules/cpq/backend/cpq/inventory/shared.js +20 -0
  110. package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +7 -0
  111. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +794 -0
  112. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +7 -0
  113. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +13 -0
  114. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +7 -0
  115. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +917 -0
  116. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +7 -0
  117. package/dist/modules/cpq/backend/cpq/offerings/page.js +405 -0
  118. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +7 -0
  119. package/dist/modules/cpq/backend/cpq/offerings/page.meta.js +18 -0
  120. package/dist/modules/cpq/backend/cpq/offerings/page.meta.js.map +7 -0
  121. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +677 -0
  122. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +7 -0
  123. package/dist/modules/cpq/backend/cpq/orders/page.js +134 -0
  124. package/dist/modules/cpq/backend/cpq/orders/page.js.map +7 -0
  125. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +18 -0
  126. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +7 -0
  127. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +272 -0
  128. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +7 -0
  129. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +407 -0
  130. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +7 -0
  131. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +13 -0
  132. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +7 -0
  133. package/dist/modules/cpq/backend/cpq/price-rules/page.js +211 -0
  134. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +7 -0
  135. package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js +18 -0
  136. package/dist/modules/cpq/backend/cpq/price-rules/page.meta.js.map +7 -0
  137. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +275 -0
  138. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +7 -0
  139. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +112 -0
  140. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +7 -0
  141. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +492 -0
  142. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +7 -0
  143. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js +50 -0
  144. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.js.map +7 -0
  145. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +249 -0
  146. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +7 -0
  147. package/dist/modules/cpq/backend/cpq/pricing/page.js +87 -0
  148. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +7 -0
  149. package/dist/modules/cpq/backend/cpq/pricing/page.meta.js +18 -0
  150. package/dist/modules/cpq/backend/cpq/pricing/page.meta.js.map +7 -0
  151. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +652 -0
  152. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +7 -0
  153. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +1289 -0
  154. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +7 -0
  155. package/dist/modules/cpq/backend/cpq/quotes/page.js +283 -0
  156. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +7 -0
  157. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +18 -0
  158. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +7 -0
  159. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +214 -0
  160. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +7 -0
  161. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +1399 -0
  162. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +7 -0
  163. package/dist/modules/cpq/backend/cpq/specifications/page.js +148 -0
  164. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +7 -0
  165. package/dist/modules/cpq/backend/cpq/specifications/page.meta.js +18 -0
  166. package/dist/modules/cpq/backend/cpq/specifications/page.meta.js.map +7 -0
  167. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +204 -0
  168. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +7 -0
  169. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +191 -0
  170. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +7 -0
  171. package/dist/modules/cpq/backend/cpq/wizards/page.js +119 -0
  172. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +7 -0
  173. package/dist/modules/cpq/backend/cpq/wizards/page.meta.js +18 -0
  174. package/dist/modules/cpq/backend/cpq/wizards/page.meta.js.map +7 -0
  175. package/dist/modules/cpq/ce.js +151 -0
  176. package/dist/modules/cpq/ce.js.map +7 -0
  177. package/dist/modules/cpq/cli.js +11 -0
  178. package/dist/modules/cpq/cli.js.map +7 -0
  179. package/dist/modules/cpq/components/CpqListView.js +265 -0
  180. package/dist/modules/cpq/components/CpqListView.js.map +7 -0
  181. package/dist/modules/cpq/components/NumberInput.js +95 -0
  182. package/dist/modules/cpq/components/NumberInput.js.map +7 -0
  183. package/dist/modules/cpq/components/StatusPath.js +60 -0
  184. package/dist/modules/cpq/components/StatusPath.js.map +7 -0
  185. package/dist/modules/cpq/components/statusMaps.js +66 -0
  186. package/dist/modules/cpq/components/statusMaps.js.map +7 -0
  187. package/dist/modules/cpq/components/useCpqRowActions.js +125 -0
  188. package/dist/modules/cpq/components/useCpqRowActions.js.map +7 -0
  189. package/dist/modules/cpq/data/entities.js +1556 -0
  190. package/dist/modules/cpq/data/entities.js.map +7 -0
  191. package/dist/modules/cpq/data/validators.js +575 -0
  192. package/dist/modules/cpq/data/validators.js.map +7 -0
  193. package/dist/modules/cpq/di.js +57 -0
  194. package/dist/modules/cpq/di.js.map +7 -0
  195. package/dist/modules/cpq/events.js +50 -0
  196. package/dist/modules/cpq/events.js.map +7 -0
  197. package/dist/modules/cpq/index.js +16 -0
  198. package/dist/modules/cpq/index.js.map +7 -0
  199. package/dist/modules/cpq/lib/seeds/api.js +32 -0
  200. package/dist/modules/cpq/lib/seeds/api.js.map +7 -0
  201. package/dist/modules/cpq/lib/seeds/tenant-provisioning.js +158 -0
  202. package/dist/modules/cpq/lib/seeds/tenant-provisioning.js.map +7 -0
  203. package/dist/modules/cpq/lib/seeds.js +6 -0
  204. package/dist/modules/cpq/lib/seeds.js.map +7 -0
  205. package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js +40 -0
  206. package/dist/modules/cpq/migrations/Migration20260220220000_cpq.js.map +7 -0
  207. package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js +54 -0
  208. package/dist/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.js.map +7 -0
  209. package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js +15 -0
  210. package/dist/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.js.map +7 -0
  211. package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js +62 -0
  212. package/dist/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.js.map +7 -0
  213. package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js +56 -0
  214. package/dist/modules/cpq/migrations/Migration20260402000000_cpq_workflows.js.map +7 -0
  215. package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js +26 -0
  216. package/dist/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.js.map +7 -0
  217. package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js +37 -0
  218. package/dist/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.js.map +7 -0
  219. package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js +35 -0
  220. package/dist/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.js.map +7 -0
  221. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js +21 -0
  222. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.js.map +7 -0
  223. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js +46 -0
  224. package/dist/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.js.map +7 -0
  225. package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js +118 -0
  226. package/dist/modules/cpq/migrations/Migration20260410100000_cpq_inventory.js.map +7 -0
  227. package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js +63 -0
  228. package/dist/modules/cpq/migrations/Migration20260411000000_cpq_orders.js.map +7 -0
  229. package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js +28 -0
  230. package/dist/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.js.map +7 -0
  231. package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js +36 -0
  232. package/dist/modules/cpq/migrations/Migration20260504064621_cpq_arc.js.map +7 -0
  233. package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js +14 -0
  234. package/dist/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.js.map +7 -0
  235. package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js +14 -0
  236. package/dist/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.js.map +7 -0
  237. package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js +58 -0
  238. package/dist/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.js.map +7 -0
  239. package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js +29 -0
  240. package/dist/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.js.map +7 -0
  241. package/dist/modules/cpq/services/cpqBundleService.js +447 -0
  242. package/dist/modules/cpq/services/cpqBundleService.js.map +7 -0
  243. package/dist/modules/cpq/services/cpqInventoryService.js +1371 -0
  244. package/dist/modules/cpq/services/cpqInventoryService.js.map +7 -0
  245. package/dist/modules/cpq/services/cpqOrderService.js +901 -0
  246. package/dist/modules/cpq/services/cpqOrderService.js.map +7 -0
  247. package/dist/modules/cpq/services/cpqPricingService.js +289 -0
  248. package/dist/modules/cpq/services/cpqPricingService.js.map +7 -0
  249. package/dist/modules/cpq/services/cpqProductService.js +204 -0
  250. package/dist/modules/cpq/services/cpqProductService.js.map +7 -0
  251. package/dist/modules/cpq/services/cpqQuotingService.js +1867 -0
  252. package/dist/modules/cpq/services/cpqQuotingService.js.map +7 -0
  253. package/dist/modules/cpq/services/cpqValidationService.js +234 -0
  254. package/dist/modules/cpq/services/cpqValidationService.js.map +7 -0
  255. package/dist/modules/cpq/services/cpqWizardService.js +137 -0
  256. package/dist/modules/cpq/services/cpqWizardService.js.map +7 -0
  257. package/dist/modules/cpq/services/types.js +114 -0
  258. package/dist/modules/cpq/services/types.js.map +7 -0
  259. package/dist/modules/cpq/setup.js +49 -0
  260. package/dist/modules/cpq/setup.js.map +7 -0
  261. package/dist/modules/cpq/subscribers/subscription-amended-log.js +18 -0
  262. package/dist/modules/cpq/subscribers/subscription-amended-log.js.map +7 -0
  263. package/dist/modules/cpq/subscribers/subscription-cancelled-log.js +19 -0
  264. package/dist/modules/cpq/subscribers/subscription-cancelled-log.js.map +7 -0
  265. package/dist/modules/cpq/subscribers/subscription-merged-log.js +20 -0
  266. package/dist/modules/cpq/subscribers/subscription-merged-log.js.map +7 -0
  267. package/dist/modules/cpq/subscribers/subscription-renewed-log.js +18 -0
  268. package/dist/modules/cpq/subscribers/subscription-renewed-log.js.map +7 -0
  269. package/dist/modules/cpq/subscribers/subscription-superseded-log.js +18 -0
  270. package/dist/modules/cpq/subscribers/subscription-superseded-log.js.map +7 -0
  271. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +192 -0
  272. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +7 -0
  273. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js +17 -0
  274. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.js.map +7 -0
  275. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +79 -0
  276. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +7 -0
  277. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js +17 -0
  278. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.js.map +7 -0
  279. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +181 -0
  280. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +7 -0
  281. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js +17 -0
  282. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.js.map +7 -0
  283. package/dist/modules/cpq/widgets/injection-table.js +44 -0
  284. package/dist/modules/cpq/widgets/injection-table.js.map +7 -0
  285. package/dist/modules/cpq/workflows/WizardRunner.js +113 -0
  286. package/dist/modules/cpq/workflows/WizardRunner.js.map +7 -0
  287. package/dist/modules/cpq/workflows/WizardTrigger.js +116 -0
  288. package/dist/modules/cpq/workflows/WizardTrigger.js.map +7 -0
  289. package/dist/modules/cpq/workflows/engine.js +188 -0
  290. package/dist/modules/cpq/workflows/engine.js.map +7 -0
  291. package/dist/modules/cpq/workflows/registry.js +16 -0
  292. package/dist/modules/cpq/workflows/registry.js.map +7 -0
  293. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +119 -0
  294. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +7 -0
  295. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +24 -0
  296. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +7 -0
  297. package/dist/modules/cpq/workflows/steps/ItemListStep.js +115 -0
  298. package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +7 -0
  299. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +327 -0
  300. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +7 -0
  301. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +231 -0
  302. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +7 -0
  303. package/dist/modules/cpq/workflows/steps/ReviewStep.js +152 -0
  304. package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +7 -0
  305. package/dist/modules/cpq/workflows/steps/index.js +49 -0
  306. package/dist/modules/cpq/workflows/steps/index.js.map +7 -0
  307. package/dist/modules/cpq/workflows/types.js +1 -0
  308. package/dist/modules/cpq/workflows/types.js.map +7 -0
  309. package/package.json +150 -0
  310. package/src/modules/cpq/__integration__/TC-CPQ-001.spec.ts +177 -0
  311. package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +123 -0
  312. package/src/modules/cpq/__integration__/TC-CPQ-003.spec.ts +115 -0
  313. package/src/modules/cpq/__integration__/TC-CPQ-004.spec.ts +184 -0
  314. package/src/modules/cpq/__integration__/TC-CPQ-005.spec.ts +43 -0
  315. package/src/modules/cpq/__integration__/TC-CPQ-006.spec.ts +87 -0
  316. package/src/modules/cpq/__integration__/TC-CPQ-007.spec.ts +79 -0
  317. package/src/modules/cpq/__integration__/TC-CPQ-008.spec.ts +55 -0
  318. package/src/modules/cpq/__integration__/TC-CPQ-009.spec.ts +45 -0
  319. package/src/modules/cpq/__integration__/TC-CPQ-010.spec.ts +54 -0
  320. package/src/modules/cpq/__integration__/TC-CPQ-011.spec.ts +24 -0
  321. package/src/modules/cpq/__integration__/TC-CPQ-012.spec.ts +62 -0
  322. package/src/modules/cpq/__integration__/TC-CPQ-ARC-001.spec.ts +52 -0
  323. package/src/modules/cpq/__integration__/TC-CPQ-ARC-002.spec.ts +65 -0
  324. package/src/modules/cpq/__integration__/TC-CPQ-ARC-003.spec.ts +107 -0
  325. package/src/modules/cpq/__integration__/TC-CPQ-ARC-004.spec.ts +74 -0
  326. package/src/modules/cpq/__integration__/TC-CPQ-ARC-005.spec.ts +86 -0
  327. package/src/modules/cpq/__integration__/TC-CPQ-ARC-006.spec.ts +79 -0
  328. package/src/modules/cpq/__integration__/TC-CPQ-ARC-007.spec.ts +63 -0
  329. package/src/modules/cpq/__integration__/TC-CPQ-ARC-008.spec.ts +62 -0
  330. package/src/modules/cpq/__integration__/TC-CPQ-ARC-009.spec.ts +62 -0
  331. package/src/modules/cpq/__integration__/TC-CPQ-ARC-010.spec.ts +59 -0
  332. package/src/modules/cpq/__integration__/TC-CPQ-ARC-011.spec.ts +128 -0
  333. package/src/modules/cpq/__integration__/_helpers/cpqArcFixtures.ts +208 -0
  334. package/src/modules/cpq/__tests__/package-isolation.test.ts +232 -0
  335. package/src/modules/cpq/acl.ts +26 -0
  336. package/src/modules/cpq/api/_helpers/__tests__/handleArcError.test.ts +117 -0
  337. package/src/modules/cpq/api/_helpers/handleArcError.ts +81 -0
  338. package/src/modules/cpq/api/attribute-options/route.ts +55 -0
  339. package/src/modules/cpq/api/bundle-slots/route.ts +112 -0
  340. package/src/modules/cpq/api/bundle-tree/route.ts +31 -0
  341. package/src/modules/cpq/api/context.ts +39 -0
  342. package/src/modules/cpq/api/inventory/assets/route.ts +123 -0
  343. package/src/modules/cpq/api/inventory/assets/status/route.ts +34 -0
  344. package/src/modules/cpq/api/inventory/customer/[customerId]/parent-items/route.ts +30 -0
  345. package/src/modules/cpq/api/inventory/customer/[customerId]/route.ts +31 -0
  346. package/src/modules/cpq/api/inventory/subscription-items/route.ts +107 -0
  347. package/src/modules/cpq/api/inventory/subscriptions/[subscriptionId]/change-log/route.ts +64 -0
  348. package/src/modules/cpq/api/inventory/subscriptions/expiring/route.ts +66 -0
  349. package/src/modules/cpq/api/inventory/subscriptions/route.ts +126 -0
  350. package/src/modules/cpq/api/inventory/subscriptions/status/route.ts +34 -0
  351. package/src/modules/cpq/api/offering-components/route.ts +103 -0
  352. package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +48 -0
  353. package/src/modules/cpq/api/orders/[orderId]/route.ts +28 -0
  354. package/src/modules/cpq/api/orders/[orderId]/status/route.ts +38 -0
  355. package/src/modules/cpq/api/orders/route.ts +77 -0
  356. package/src/modules/cpq/api/price-rules/route.ts +210 -0
  357. package/src/modules/cpq/api/pricing-table-entries/route.ts +159 -0
  358. package/src/modules/cpq/api/pricing-tables/route.ts +202 -0
  359. package/src/modules/cpq/api/product-attributes/route.ts +175 -0
  360. package/src/modules/cpq/api/product-charges/route.ts +181 -0
  361. package/src/modules/cpq/api/product-offerings/route.ts +329 -0
  362. package/src/modules/cpq/api/product-relationships/route.ts +156 -0
  363. package/src/modules/cpq/api/product-specifications/route.ts +325 -0
  364. package/src/modules/cpq/api/quotes/[quoteId]/cancel-meta/route.ts +77 -0
  365. package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +28 -0
  366. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +55 -0
  367. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +26 -0
  368. package/src/modules/cpq/api/quotes/[quoteId]/merge-meta/route.ts +78 -0
  369. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +34 -0
  370. package/src/modules/cpq/api/quotes/[quoteId]/route.ts +109 -0
  371. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +26 -0
  372. package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/[targetId]/route.ts +91 -0
  373. package/src/modules/cpq/api/quotes/[quoteId]/target-subscriptions/route.ts +107 -0
  374. package/src/modules/cpq/api/quotes/attributes/route.ts +61 -0
  375. package/src/modules/cpq/api/quotes/from-subscription/route.ts +85 -0
  376. package/src/modules/cpq/api/quotes/price/route.ts +81 -0
  377. package/src/modules/cpq/api/quotes/route.ts +114 -0
  378. package/src/modules/cpq/api/quotes/validate-relationships/route.ts +100 -0
  379. package/src/modules/cpq/api/resolveQuotingService.ts +11 -0
  380. package/src/modules/cpq/api/wizards/resolve/route.ts +39 -0
  381. package/src/modules/cpq/api/wizards/route.ts +127 -0
  382. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +166 -0
  383. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +251 -0
  384. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +9 -0
  385. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +440 -0
  386. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +16 -0
  387. package/src/modules/cpq/backend/cpq/inventory/page.tsx +49 -0
  388. package/src/modules/cpq/backend/cpq/inventory/shared.tsx +32 -0
  389. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +9 -0
  390. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +1085 -0
  391. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +1165 -0
  392. package/src/modules/cpq/backend/cpq/offerings/page.meta.ts +16 -0
  393. package/src/modules/cpq/backend/cpq/offerings/page.tsx +468 -0
  394. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +921 -0
  395. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +16 -0
  396. package/src/modules/cpq/backend/cpq/orders/page.tsx +182 -0
  397. package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +310 -0
  398. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +9 -0
  399. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +472 -0
  400. package/src/modules/cpq/backend/cpq/price-rules/page.meta.ts +16 -0
  401. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +253 -0
  402. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +431 -0
  403. package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +122 -0
  404. package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +588 -0
  405. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxExport.ts +90 -0
  406. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +338 -0
  407. package/src/modules/cpq/backend/cpq/pricing/page.meta.ts +16 -0
  408. package/src/modules/cpq/backend/cpq/pricing/page.tsx +111 -0
  409. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +737 -0
  410. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +1721 -0
  411. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +16 -0
  412. package/src/modules/cpq/backend/cpq/quotes/page.tsx +370 -0
  413. package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +300 -0
  414. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +1722 -0
  415. package/src/modules/cpq/backend/cpq/specifications/page.meta.ts +16 -0
  416. package/src/modules/cpq/backend/cpq/specifications/page.tsx +191 -0
  417. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +319 -0
  418. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +234 -0
  419. package/src/modules/cpq/backend/cpq/wizards/page.meta.ts +16 -0
  420. package/src/modules/cpq/backend/cpq/wizards/page.tsx +141 -0
  421. package/src/modules/cpq/ce.ts +150 -0
  422. package/src/modules/cpq/cli.ts +10 -0
  423. package/src/modules/cpq/components/CpqListView.tsx +431 -0
  424. package/src/modules/cpq/components/NumberInput.tsx +166 -0
  425. package/src/modules/cpq/components/StatusPath.tsx +116 -0
  426. package/src/modules/cpq/components/statusMaps.ts +148 -0
  427. package/src/modules/cpq/components/useCpqRowActions.tsx +183 -0
  428. package/src/modules/cpq/data/__tests__/validators.arc.test.ts +271 -0
  429. package/src/modules/cpq/data/__tests__/validators.test.ts +782 -0
  430. package/src/modules/cpq/data/entities.ts +1467 -0
  431. package/src/modules/cpq/data/validators.ts +663 -0
  432. package/src/modules/cpq/di.ts +70 -0
  433. package/src/modules/cpq/events.ts +62 -0
  434. package/src/modules/cpq/index.ts +15 -0
  435. package/src/modules/cpq/lib/seeds/__tests__/api.test.ts +86 -0
  436. package/src/modules/cpq/lib/seeds/__tests__/tenant-provisioning.test.ts +349 -0
  437. package/src/modules/cpq/lib/seeds/api.ts +119 -0
  438. package/src/modules/cpq/lib/seeds/tenant-provisioning.ts +239 -0
  439. package/src/modules/cpq/lib/seeds.ts +18 -0
  440. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +7473 -0
  441. package/src/modules/cpq/migrations/Migration20260220220000_cpq.ts +51 -0
  442. package/src/modules/cpq/migrations/Migration20260330000000_cpq_product_domain.ts +70 -0
  443. package/src/modules/cpq/migrations/Migration20260331000000_cpq_charge_unique_per_offering.ts +15 -0
  444. package/src/modules/cpq/migrations/Migration20260401000000_cpq_quoting_v2.ts +73 -0
  445. package/src/modules/cpq/migrations/Migration20260402000000_cpq_workflows.ts +59 -0
  446. package/src/modules/cpq/migrations/Migration20260407000000_cpq_pricing_tables_v2.ts +30 -0
  447. package/src/modules/cpq/migrations/Migration20260407100000_cpq_price_rules.ts +36 -0
  448. package/src/modules/cpq/migrations/Migration20260408000000_cpq_price_rules.ts +33 -0
  449. package/src/modules/cpq/migrations/Migration20260410000000_cpq_drop_datacentres_inventory.ts +22 -0
  450. package/src/modules/cpq/migrations/Migration20260410000000_cpq_wizard_rename.ts +56 -0
  451. package/src/modules/cpq/migrations/Migration20260410100000_cpq_inventory.ts +118 -0
  452. package/src/modules/cpq/migrations/Migration20260411000000_cpq_orders.ts +62 -0
  453. package/src/modules/cpq/migrations/Migration20260412000000_cpq_bundled_offerings.ts +33 -0
  454. package/src/modules/cpq/migrations/Migration20260504064621_cpq_arc.ts +68 -0
  455. package/src/modules/cpq/migrations/Migration20260514000000_cpq_spec_merge_retired_deprecated.ts +30 -0
  456. package/src/modules/cpq/migrations/Migration20260514100000_cpq_offering_merge_retired_deprecated.ts +27 -0
  457. package/src/modules/cpq/migrations/Migration20260514110000_cpq_list_search_indexes.ts +82 -0
  458. package/src/modules/cpq/migrations/Migration20260515000000_cpq_inventory_subscriptions_search_indexes.ts +52 -0
  459. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +59 -0
  460. package/src/modules/cpq/services/__tests__/cpqBundleService.test.ts +661 -0
  461. package/src/modules/cpq/services/__tests__/cpqInventoryService.arc.test.ts +357 -0
  462. package/src/modules/cpq/services/__tests__/cpqInventoryService.test.ts +718 -0
  463. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +603 -0
  464. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +1012 -0
  465. package/src/modules/cpq/services/__tests__/cpqProductService.test.ts +603 -0
  466. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +143 -0
  467. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +658 -0
  468. package/src/modules/cpq/services/__tests__/cpqValidationService.test.ts +811 -0
  469. package/src/modules/cpq/services/__tests__/cpqWizardService.test.ts +389 -0
  470. package/src/modules/cpq/services/__tests__/types.test.ts +359 -0
  471. package/src/modules/cpq/services/cpqBundleService.ts +671 -0
  472. package/src/modules/cpq/services/cpqInventoryService.ts +1929 -0
  473. package/src/modules/cpq/services/cpqOrderService.ts +1230 -0
  474. package/src/modules/cpq/services/cpqPricingService.ts +412 -0
  475. package/src/modules/cpq/services/cpqProductService.ts +311 -0
  476. package/src/modules/cpq/services/cpqQuotingService.ts +2638 -0
  477. package/src/modules/cpq/services/cpqValidationService.ts +323 -0
  478. package/src/modules/cpq/services/cpqWizardService.ts +204 -0
  479. package/src/modules/cpq/services/types.ts +303 -0
  480. package/src/modules/cpq/setup.ts +63 -0
  481. package/src/modules/cpq/subscribers/subscription-amended-log.ts +20 -0
  482. package/src/modules/cpq/subscribers/subscription-cancelled-log.ts +19 -0
  483. package/src/modules/cpq/subscribers/subscription-merged-log.ts +22 -0
  484. package/src/modules/cpq/subscribers/subscription-renewed-log.ts +18 -0
  485. package/src/modules/cpq/subscribers/subscription-superseded-log.ts +19 -0
  486. package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +339 -0
  487. package/src/modules/cpq/widgets/injection/customer-inventory/widget.ts +16 -0
  488. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +96 -0
  489. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.ts +16 -0
  490. package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +265 -0
  491. package/src/modules/cpq/widgets/injection/quote-configurator/widget.ts +16 -0
  492. package/src/modules/cpq/widgets/injection-table.ts +42 -0
  493. package/src/modules/cpq/workflows/WizardRunner.tsx +158 -0
  494. package/src/modules/cpq/workflows/WizardTrigger.tsx +142 -0
  495. package/src/modules/cpq/workflows/engine.tsx +278 -0
  496. package/src/modules/cpq/workflows/registry.ts +15 -0
  497. package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +136 -0
  498. package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +34 -0
  499. package/src/modules/cpq/workflows/steps/ItemListStep.tsx +146 -0
  500. package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +438 -0
  501. package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +287 -0
  502. package/src/modules/cpq/workflows/steps/ReviewStep.tsx +219 -0
  503. package/src/modules/cpq/workflows/steps/index.ts +51 -0
  504. package/src/modules/cpq/workflows/types.ts +102 -0
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/cpq/workflows/steps/ProductConfigureStep.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\nimport type { WizardStepProps } from '../types'\nimport { NumberInput } from '../../components/NumberInput'\n\ntype ConstrainedAttribute = {\n code: string\n name: string\n attributeType: string\n isRequired: boolean\n options: Array<{ value: string; label: string }> | null\n constraints: Record<string, unknown> | null\n defaultValue: unknown | null\n helpText: string | null\n}\n\ntype AttributeGroup = {\n label: string\n attributeCodes: string[]\n}\n\nfunction resolveOfferingId(config: Record<string, unknown>, stepData: Record<string, Record<string, unknown>>): string | null {\n // Check explicit source\n const source = config.offeringSource as string | undefined\n if (source && stepData[source]) {\n return (stepData[source].offeringId as string) ?? null\n }\n // Auto-detect: find first step data with offeringId\n for (const data of Object.values(stepData)) {\n if (data.offeringId) return data.offeringId as string\n }\n return null\n}\n\nexport function ProductConfigureStep({ config, onComplete, onBack, stepData, currentStepId, completedSteps, quoteId, customerId }: WizardStepProps) {\n const [attributes, setAttributes] = useState<ConstrainedAttribute[]>([])\n const [values, setValues] = useState<Record<string, unknown>>({})\n const [loading, setLoading] = useState(true)\n const [saving, setSaving] = useState(false)\n const [resolvedOfferingId, setResolvedOfferingId] = useState<string | null>(null)\n\n const directOfferingId = resolveOfferingId(config, stepData)\n const offeringCode = config.offeringCode as string | undefined\n const attributeGroups = config.attributeGroups as AttributeGroup[] | undefined\n\n // Resolve offering by code if no direct offeringId\n useEffect(() => {\n if (directOfferingId) {\n setResolvedOfferingId(directOfferingId)\n return\n }\n if (!offeringCode) return\n async function resolve() {\n try {\n const res = await fetch(`/api/cpq/product-offerings?code=${encodeURIComponent(offeringCode!)}`)\n if (res.ok) {\n const data = await res.json()\n const items = data.items ?? data\n if (Array.isArray(items) && items.length > 0) {\n setResolvedOfferingId(items[0].id)\n }\n }\n } catch { /* ignore */ }\n }\n resolve()\n }, [directOfferingId, offeringCode])\n\n const offeringId = resolvedOfferingId\n const hiddenAttributes = (config.hiddenAttributes as string[]) ?? []\n const readonlyAttributes = (config.readonlyAttributes as string[]) ?? []\n\n // Restore previous values\n const previousData = stepData[currentStepId] as { configuration?: Record<string, unknown> } | undefined\n\n useEffect(() => {\n async function loadAttributes() {\n if (!offeringId) { setLoading(false); return }\n setLoading(true)\n try {\n const params = new URLSearchParams({ offeringId })\n if (Object.keys(values).length > 0) {\n params.set('configuration', JSON.stringify(values))\n }\n const res = await fetch(`/api/cpq/quotes/attributes?${params}`)\n if (res.ok) {\n const data = await res.json()\n const attrs = (data.attributes ?? []) as ConstrainedAttribute[]\n setAttributes(attrs.filter((a) => !hiddenAttributes.includes(a.code)))\n\n // Set defaults on first load\n if (Object.keys(values).length === 0) {\n const defaults: Record<string, unknown> = previousData?.configuration ?? {}\n for (const attr of attrs) {\n if (defaults[attr.code] === undefined && attr.defaultValue != null) {\n defaults[attr.code] = attr.defaultValue\n }\n }\n setValues(defaults)\n }\n }\n } finally {\n setLoading(false)\n }\n }\n loadAttributes()\n }, [offeringId]) // eslint-disable-line react-hooks/exhaustive-deps\n\n const handleChange = useCallback((code: string, value: unknown) => {\n setValues((prev) => ({ ...prev, [code]: value }))\n }, [])\n\n // Resolve customerId from props or stepData\n const resolvedCustomerId = customerId ?? (() => {\n for (const data of Object.values(stepData)) {\n if (data.customerId) return data.customerId as string\n }\n return null\n })()\n\n const handleComplete = useCallback(async () => {\n setSaving(true)\n try {\n let activeQuoteId = quoteId\n\n // Auto-create a quote if none exists yet\n if (!activeQuoteId && resolvedCustomerId && offeringId) {\n const createRes = await fetch('/api/cpq/quotes', {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ customerId: resolvedCustomerId }),\n })\n if (createRes.ok) {\n const quoteData = await createRes.json()\n activeQuoteId = quoteData.id\n }\n }\n\n if (activeQuoteId && offeringId) {\n // Check if there's already a line for this offering\n const existingLineId = (stepData[currentStepId] as { lineId?: string })?.lineId\n\n if (existingLineId) {\n await fetch(`/api/cpq/quotes/${activeQuoteId}/items/${existingLineId}`, {\n method: 'PUT',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ configuration: values }),\n })\n onComplete({ configuration: values, offeringId, lineId: existingLineId, quoteId: activeQuoteId })\n } else {\n const res = await fetch(`/api/cpq/quotes/${activeQuoteId}/items`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ offeringId, configuration: values }),\n })\n if (res.ok) {\n const data = await res.json()\n const line = data.lines?.find((l: { offeringId: string }) => l.offeringId === offeringId)\n onComplete({ configuration: values, offeringId, lineId: line?.lineId, quoteId: activeQuoteId })\n } else {\n onComplete({ configuration: values, offeringId, quoteId: activeQuoteId })\n }\n }\n } else {\n onComplete({ configuration: values, offeringId })\n }\n } finally {\n setSaving(false)\n }\n }, [values, quoteId, offeringId, stepData, currentStepId, onComplete, resolvedCustomerId])\n\n const renderAttribute = (attr: ConstrainedAttribute) => {\n const isReadonly = readonlyAttributes.includes(attr.code)\n const value = values[attr.code] ?? ''\n\n if (attr.attributeType === 'select' || attr.attributeType === 'enum') {\n return (\n <select\n value={String(value)}\n onChange={(e) => handleChange(attr.code, e.target.value)}\n disabled={isReadonly}\n className=\"w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50\"\n >\n <option value=\"\">Select...</option>\n {attr.options?.map((opt) => (\n <option key={opt.value} value={opt.value}>{opt.label}</option>\n ))}\n </select>\n )\n }\n\n if (attr.attributeType === 'boolean') {\n return (\n <label className=\"flex items-center gap-2 text-sm\">\n <input\n type=\"checkbox\"\n checked={Boolean(value)}\n onChange={(e) => handleChange(attr.code, e.target.checked)}\n disabled={isReadonly}\n className=\"rounded border\"\n />\n {attr.name}\n </label>\n )\n }\n\n if (attr.attributeType === 'number') {\n return (\n <NumberInput\n value={value === '' || value == null ? null : Number(value)}\n onChange={(n) => handleChange(attr.code, n ?? '')}\n disabled={isReadonly}\n />\n )\n }\n\n return (\n <input\n type=\"text\"\n value={String(value)}\n onChange={(e) => handleChange(attr.code, e.target.value)}\n disabled={isReadonly}\n className=\"w-full rounded-md border px-3 py-2 text-sm disabled:opacity-50\"\n />\n )\n }\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">Loading attributes...</div>\n }\n\n if (!offeringId) {\n return (\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n No offering selected. Please go back and select an offering.\n </div>\n )\n }\n\n const renderGroup = (label: string, attrs: ConstrainedAttribute[]) => (\n <div key={label} className=\"space-y-3\">\n <h4 className=\"text-sm font-medium border-b pb-2\">{label}</h4>\n <div className=\"space-y-3\">\n {attrs.map((attr) => (\n <div key={attr.code}>\n <label className=\"block text-sm font-medium mb-1\">\n {attr.name}\n {attr.isRequired && <span className=\"text-destructive ml-0.5\">*</span>}\n </label>\n {renderAttribute(attr)}\n {attr.helpText && <p className=\"text-xs text-muted-foreground mt-1\">{attr.helpText}</p>}\n </div>\n ))}\n </div>\n </div>\n )\n\n return (\n <div className=\"space-y-4\">\n {attributeGroups?.length ? (\n attributeGroups.map((group) => {\n const groupAttrs = attributes.filter((a) => group.attributeCodes.includes(a.code))\n if (groupAttrs.length === 0) return null\n return renderGroup(group.label, groupAttrs)\n })\n ) : (\n renderGroup('Configuration', attributes)\n )}\n\n <div className=\"flex gap-2 justify-end pt-2\">\n {completedSteps.length > 0 && (\n <button type=\"button\" onClick={onBack} className=\"inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted\">\n Back\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleComplete}\n disabled={saving}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {saving ? 'Saving...' : 'Next'}\n </button>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,152 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import React, { useState, useEffect, useCallback } from "react";
4
+ function fmt(n, currency = "USD") {
5
+ return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(n);
6
+ }
7
+ function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId }) {
8
+ const [quote, setQuote] = useState(null);
9
+ const [loading, setLoading] = useState(true);
10
+ const [submitting, setSubmitting] = useState(false);
11
+ const showChargeBreakdown = config.showChargeBreakdown !== false;
12
+ const showAdjustments = config.showAdjustments !== false;
13
+ const submitAction = config.submitAction ?? "save";
14
+ const submitLabel = config.submitLabel ?? "Complete";
15
+ const loadQuote = useCallback(async () => {
16
+ if (!quoteId) {
17
+ setLoading(false);
18
+ return;
19
+ }
20
+ setLoading(true);
21
+ try {
22
+ const res = await fetch(`/api/cpq/quotes/${quoteId}`);
23
+ if (res.ok) {
24
+ setQuote(await res.json());
25
+ }
26
+ } finally {
27
+ setLoading(false);
28
+ }
29
+ }, [quoteId]);
30
+ useEffect(() => {
31
+ loadQuote();
32
+ }, [loadQuote]);
33
+ const handleSubmit = useCallback(async () => {
34
+ setSubmitting(true);
35
+ try {
36
+ if (quoteId && submitAction !== "none") {
37
+ const targetStatus = submitAction === "submit_for_approval" ? "in_approval" : submitAction === "send_to_customer" ? "with_customer" : void 0;
38
+ if (targetStatus) {
39
+ await fetch(`/api/cpq/quotes/${quoteId}/status`, {
40
+ method: "POST",
41
+ headers: { "Content-Type": "application/json" },
42
+ body: JSON.stringify({ targetStatus })
43
+ });
44
+ }
45
+ }
46
+ onComplete({
47
+ quoteId,
48
+ action: submitAction,
49
+ nrcTotal: quote?.pricingSummary.nrcTotal ?? 0,
50
+ mrcTotal: quote?.pricingSummary.mrcTotal ?? 0
51
+ });
52
+ } finally {
53
+ setSubmitting(false);
54
+ }
55
+ }, [quoteId, submitAction, quote, onComplete]);
56
+ if (loading) {
57
+ return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: "Loading quote summary..." });
58
+ }
59
+ if (!quote) {
60
+ return /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "No quote data available. Please go back and configure items." });
61
+ }
62
+ const { pricingSummary, lines } = quote;
63
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-5", children: [
64
+ /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-muted/30 p-4 space-y-3", children: [
65
+ /* @__PURE__ */ jsx("h4", { className: "text-sm font-medium", children: "Quote Summary" }),
66
+ /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
67
+ /* @__PURE__ */ jsxs("div", { children: [
68
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "NRC Total" }),
69
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: fmt(pricingSummary.nrcTotal, pricingSummary.currencyCode) })
70
+ ] }),
71
+ /* @__PURE__ */ jsxs("div", { children: [
72
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "MRC Total" }),
73
+ /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold", children: fmt(pricingSummary.mrcTotal, pricingSummary.currencyCode) })
74
+ ] }),
75
+ pricingSummary.discountTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
76
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Discounts" }),
77
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
78
+ "-",
79
+ fmt(pricingSummary.discountTotal, pricingSummary.currencyCode)
80
+ ] })
81
+ ] }),
82
+ pricingSummary.surchargeTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
83
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: "Surcharges" }),
84
+ /* @__PURE__ */ jsxs("p", { className: "text-sm", children: [
85
+ "+",
86
+ fmt(pricingSummary.surchargeTotal, pricingSummary.currencyCode)
87
+ ] })
88
+ ] })
89
+ ] })
90
+ ] }),
91
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
92
+ /* @__PURE__ */ jsxs("h4", { className: "text-sm font-medium", children: [
93
+ "Items (",
94
+ lines.length,
95
+ ")"
96
+ ] }),
97
+ /* @__PURE__ */ jsx("div", { className: "space-y-2", children: lines.map((line) => /* @__PURE__ */ jsxs("div", { className: "rounded-lg border p-3 space-y-2", children: [
98
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
99
+ /* @__PURE__ */ jsx("strong", { className: "text-sm", children: line.offeringName }),
100
+ /* @__PURE__ */ jsxs("span", { className: "text-sm", children: [
101
+ fmt(line.nrcTotal, pricingSummary.currencyCode),
102
+ " NRC / ",
103
+ fmt(line.mrcTotal, pricingSummary.currencyCode),
104
+ " MRC"
105
+ ] })
106
+ ] }),
107
+ line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
108
+ "Quantity: ",
109
+ line.quantity
110
+ ] }),
111
+ showChargeBreakdown && line.charges?.length > 0 && /* @__PURE__ */ jsx("div", { className: "rounded border", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
112
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "border-b bg-muted/30", children: [
113
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium", children: "Charge" }),
114
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-left font-medium", children: "Type" }),
115
+ /* @__PURE__ */ jsx("th", { className: "px-3 py-1.5 text-right font-medium", children: "Price" })
116
+ ] }) }),
117
+ /* @__PURE__ */ jsx("tbody", { children: line.charges.map((charge) => /* @__PURE__ */ jsxs(React.Fragment, { children: [
118
+ /* @__PURE__ */ jsxs("tr", { className: "border-b last:border-0", children: [
119
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5", children: charge.chargeName }),
120
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 uppercase", children: charge.chargeType }),
121
+ /* @__PURE__ */ jsx("td", { className: "px-3 py-1.5 text-right", children: charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : `${fmt(charge.unitPrice, charge.currencyCode)}/unit` })
122
+ ] }),
123
+ showAdjustments && charge.adjustments?.map((adj) => /* @__PURE__ */ jsxs("tr", { className: "text-[11px] text-muted-foreground", children: [
124
+ /* @__PURE__ */ jsx("td", { colSpan: 2, className: "px-3 py-0.5 pl-6", children: adj.ruleName }),
125
+ /* @__PURE__ */ jsxs("td", { className: "px-3 py-0.5 text-right", children: [
126
+ adj.delta < 0 ? "" : "+",
127
+ fmt(adj.delta, charge.currencyCode)
128
+ ] })
129
+ ] }, adj.ruleCode))
130
+ ] }, charge.chargeCode)) })
131
+ ] }) })
132
+ ] }, line.lineId)) })
133
+ ] }),
134
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2 justify-end pt-2", children: [
135
+ completedSteps.length > 0 && /* @__PURE__ */ jsx("button", { type: "button", onClick: onBack, className: "inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted", children: "Back" }),
136
+ /* @__PURE__ */ jsx(
137
+ "button",
138
+ {
139
+ type: "button",
140
+ onClick: handleSubmit,
141
+ disabled: submitting,
142
+ className: "inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50",
143
+ children: submitting ? "Submitting..." : submitLabel
144
+ }
145
+ )
146
+ ] })
147
+ ] });
148
+ }
149
+ export {
150
+ ReviewStep
151
+ };
152
+ //# sourceMappingURL=ReviewStep.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/cpq/workflows/steps/ReviewStep.tsx"],
4
+ "sourcesContent": ["'use client'\n\nimport React, { useState, useEffect, useCallback } from 'react'\nimport type { WizardStepProps } from '../types'\n\ntype ResolvedCharge = {\n chargeCode: string\n chargeName: string\n chargeType: string\n unitPrice: number\n totalPrice: number | null\n currencyCode: string\n adjustments?: Array<{\n ruleCode: string\n ruleName: string\n ruleType: string\n delta: number\n }> | null\n}\n\ntype QuoteLine = {\n lineId: string\n offeringName: string\n quantity: number\n nrcTotal: number\n mrcTotal: number\n charges: ResolvedCharge[]\n isConfigured: boolean\n}\n\ntype QuoteData = {\n id: string\n quoteNumber: string\n cpqStatus: string\n currencyCode: string\n pricingSummary: {\n nrcTotal: number\n mrcTotal: number\n discountTotal: number\n surchargeTotal: number\n currencyCode: string\n }\n lines: QuoteLine[]\n}\n\nfunction fmt(n: number, currency = 'USD') {\n return new Intl.NumberFormat('en-US', { style: 'currency', currency }).format(n)\n}\n\nexport function ReviewStep({ config, onComplete, onBack, completedSteps, quoteId }: WizardStepProps) {\n const [quote, setQuote] = useState<QuoteData | null>(null)\n const [loading, setLoading] = useState(true)\n const [submitting, setSubmitting] = useState(false)\n\n const showChargeBreakdown = config.showChargeBreakdown !== false\n const showAdjustments = config.showAdjustments !== false\n const submitAction = (config.submitAction as string) ?? 'save'\n const submitLabel = (config.submitLabel as string) ?? 'Complete'\n\n const loadQuote = useCallback(async () => {\n if (!quoteId) { setLoading(false); return }\n setLoading(true)\n try {\n const res = await fetch(`/api/cpq/quotes/${quoteId}`)\n if (res.ok) {\n setQuote(await res.json())\n }\n } finally {\n setLoading(false)\n }\n }, [quoteId])\n\n useEffect(() => { loadQuote() }, [loadQuote])\n\n const handleSubmit = useCallback(async () => {\n setSubmitting(true)\n try {\n if (quoteId && submitAction !== 'none') {\n const targetStatus =\n submitAction === 'submit_for_approval' ? 'in_approval'\n : submitAction === 'send_to_customer' ? 'with_customer'\n : undefined\n\n if (targetStatus) {\n await fetch(`/api/cpq/quotes/${quoteId}/status`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ targetStatus }),\n })\n }\n }\n\n onComplete({\n quoteId,\n action: submitAction,\n nrcTotal: quote?.pricingSummary.nrcTotal ?? 0,\n mrcTotal: quote?.pricingSummary.mrcTotal ?? 0,\n })\n } finally {\n setSubmitting(false)\n }\n }, [quoteId, submitAction, quote, onComplete])\n\n if (loading) {\n return <div className=\"text-sm text-muted-foreground\">Loading quote summary...</div>\n }\n\n if (!quote) {\n return (\n <div className=\"rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground\">\n No quote data available. Please go back and configure items.\n </div>\n )\n }\n\n const { pricingSummary, lines } = quote\n\n return (\n <div className=\"space-y-5\">\n {/* Quote summary */}\n <div className=\"rounded-lg border bg-muted/30 p-4 space-y-3\">\n <h4 className=\"text-sm font-medium\">Quote Summary</h4>\n <div className=\"grid grid-cols-2 gap-4\">\n <div>\n <span className=\"text-xs text-muted-foreground\">NRC Total</span>\n <p className=\"text-lg font-semibold\">{fmt(pricingSummary.nrcTotal, pricingSummary.currencyCode)}</p>\n </div>\n <div>\n <span className=\"text-xs text-muted-foreground\">MRC Total</span>\n <p className=\"text-lg font-semibold\">{fmt(pricingSummary.mrcTotal, pricingSummary.currencyCode)}</p>\n </div>\n {pricingSummary.discountTotal > 0 && (\n <div>\n <span className=\"text-xs text-muted-foreground\">Discounts</span>\n <p className=\"text-sm\">-{fmt(pricingSummary.discountTotal, pricingSummary.currencyCode)}</p>\n </div>\n )}\n {pricingSummary.surchargeTotal > 0 && (\n <div>\n <span className=\"text-xs text-muted-foreground\">Surcharges</span>\n <p className=\"text-sm\">+{fmt(pricingSummary.surchargeTotal, pricingSummary.currencyCode)}</p>\n </div>\n )}\n </div>\n </div>\n\n {/* Line items */}\n <div className=\"space-y-3\">\n <h4 className=\"text-sm font-medium\">Items ({lines.length})</h4>\n <div className=\"space-y-2\">\n {lines.map((line) => (\n <div key={line.lineId} className=\"rounded-lg border p-3 space-y-2\">\n <div className=\"flex items-center justify-between\">\n <strong className=\"text-sm\">{line.offeringName}</strong>\n <span className=\"text-sm\">\n {fmt(line.nrcTotal, pricingSummary.currencyCode)} NRC / {fmt(line.mrcTotal, pricingSummary.currencyCode)} MRC\n </span>\n </div>\n {line.quantity > 1 && (\n <span className=\"text-xs text-muted-foreground\">Quantity: {line.quantity}</span>\n )}\n\n {showChargeBreakdown && line.charges?.length > 0 && (\n <div className=\"rounded border\">\n <table className=\"w-full text-xs\">\n <thead>\n <tr className=\"border-b bg-muted/30\">\n <th className=\"px-3 py-1.5 text-left font-medium\">Charge</th>\n <th className=\"px-3 py-1.5 text-left font-medium\">Type</th>\n <th className=\"px-3 py-1.5 text-right font-medium\">Price</th>\n </tr>\n </thead>\n <tbody>\n {line.charges.map((charge) => (\n <React.Fragment key={charge.chargeCode}>\n <tr className=\"border-b last:border-0\">\n <td className=\"px-3 py-1.5\">{charge.chargeName}</td>\n <td className=\"px-3 py-1.5 uppercase\">{charge.chargeType}</td>\n <td className=\"px-3 py-1.5 text-right\">\n {charge.totalPrice != null ? fmt(charge.totalPrice, charge.currencyCode) : `${fmt(charge.unitPrice, charge.currencyCode)}/unit`}\n </td>\n </tr>\n {showAdjustments && charge.adjustments?.map((adj) => (\n <tr key={adj.ruleCode} className=\"text-[11px] text-muted-foreground\">\n <td colSpan={2} className=\"px-3 py-0.5 pl-6\">{adj.ruleName}</td>\n <td className=\"px-3 py-0.5 text-right\">\n {adj.delta < 0 ? '' : '+'}{fmt(adj.delta, charge.currencyCode)}\n </td>\n </tr>\n ))}\n </React.Fragment>\n ))}\n </tbody>\n </table>\n </div>\n )}\n </div>\n ))}\n </div>\n </div>\n\n <div className=\"flex gap-2 justify-end pt-2\">\n {completedSteps.length > 0 && (\n <button type=\"button\" onClick={onBack} className=\"inline-flex items-center justify-center rounded-md border px-4 py-2 text-sm font-medium hover:bg-muted\">\n Back\n </button>\n )}\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={submitting}\n className=\"inline-flex items-center justify-center rounded-md bg-primary px-5 py-2 text-sm font-medium text-primary-foreground hover:bg-primary/90 disabled:opacity-50\"\n >\n {submitting ? 'Submitting...' : submitLabel}\n </button>\n </div>\n </div>\n )\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }
@@ -0,0 +1,49 @@
1
+ import { registerStepType } from "../registry.js";
2
+ import { CustomerSelectStep } from "./CustomerSelectStep.js";
3
+ import { OfferingSelectStep } from "./OfferingSelectStep.js";
4
+ import { ProductConfigureStep } from "./ProductConfigureStep.js";
5
+ import { ItemListStep } from "./ItemListStep.js";
6
+ import { ReviewStep } from "./ReviewStep.js";
7
+ import { InventorySelectStep } from "./InventorySelectStep.js";
8
+ function registerBuiltInStepTypes() {
9
+ registerStepType({
10
+ type: "customer_select",
11
+ label: "Customer Selection",
12
+ description: "Search and select a customer for the quote.",
13
+ component: CustomerSelectStep
14
+ });
15
+ registerStepType({
16
+ type: "offering_select",
17
+ label: "Offering Selection",
18
+ description: "Browse and select a product offering from the catalog.",
19
+ component: OfferingSelectStep
20
+ });
21
+ registerStepType({
22
+ type: "product_configure",
23
+ label: "Product Configuration",
24
+ description: "Configure run-time attributes for the selected offering.",
25
+ component: ProductConfigureStep
26
+ });
27
+ registerStepType({
28
+ type: "item_list",
29
+ label: "Item List",
30
+ description: "View and manage child items on the quote.",
31
+ component: ItemListStep
32
+ });
33
+ registerStepType({
34
+ type: "review",
35
+ label: "Review & Submit",
36
+ description: "Review the quote summary and submit.",
37
+ component: ReviewStep
38
+ });
39
+ registerStepType({
40
+ type: "inventory_select",
41
+ label: "Inventory Selection",
42
+ description: "Select existing customer inventory items (stub \u2014 pending inventory domain).",
43
+ component: InventorySelectStep
44
+ });
45
+ }
46
+ export {
47
+ registerBuiltInStepTypes
48
+ };
49
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../src/modules/cpq/workflows/steps/index.ts"],
4
+ "sourcesContent": ["import { registerStepType } from '../registry'\nimport { CustomerSelectStep } from './CustomerSelectStep'\nimport { OfferingSelectStep } from './OfferingSelectStep'\nimport { ProductConfigureStep } from './ProductConfigureStep'\nimport { ItemListStep } from './ItemListStep'\nimport { ReviewStep } from './ReviewStep'\nimport { InventorySelectStep } from './InventorySelectStep'\n\nexport function registerBuiltInStepTypes() {\n registerStepType({\n type: 'customer_select',\n label: 'Customer Selection',\n description: 'Search and select a customer for the quote.',\n component: CustomerSelectStep,\n })\n\n registerStepType({\n type: 'offering_select',\n label: 'Offering Selection',\n description: 'Browse and select a product offering from the catalog.',\n component: OfferingSelectStep,\n })\n\n registerStepType({\n type: 'product_configure',\n label: 'Product Configuration',\n description: 'Configure run-time attributes for the selected offering.',\n component: ProductConfigureStep,\n })\n\n registerStepType({\n type: 'item_list',\n label: 'Item List',\n description: 'View and manage child items on the quote.',\n component: ItemListStep,\n })\n\n registerStepType({\n type: 'review',\n label: 'Review & Submit',\n description: 'Review the quote summary and submit.',\n component: ReviewStep,\n })\n\n registerStepType({\n type: 'inventory_select',\n label: 'Inventory Selection',\n description: 'Select existing customer inventory items (stub \u2014 pending inventory domain).',\n component: InventorySelectStep,\n })\n}\n"],
5
+ "mappings": "AAAA,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,kBAAkB;AAC3B,SAAS,2BAA2B;AAE7B,SAAS,2BAA2B;AACzC,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AAED,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,OAAO;AAAA,IACP,aAAa;AAAA,IACb,WAAW;AAAA,EACb,CAAC;AACH;",
6
+ "names": []
7
+ }
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": [],
4
+ "sourcesContent": [],
5
+ "mappings": "",
6
+ "names": []
7
+ }
package/package.json ADDED
@@ -0,0 +1,150 @@
1
+ {
2
+ "name": "@dainamite/cpq",
3
+ "version": "0.2.2",
4
+ "description": "Dainamite CPQ — Configure, Price, Quote engine for Open Mercato (configurable products, multi-charge pricing tables, guided quoting, customer inventory, orders).",
5
+ "type": "module",
6
+ "main": "./dist/modules/cpq/index.js",
7
+ "files": [
8
+ "dist",
9
+ "src",
10
+ "README.md",
11
+ "CHANGELOG.md",
12
+ "MIGRATION.md"
13
+ ],
14
+ "scripts": {
15
+ "build": "node build.mjs",
16
+ "watch": "node watch.mjs",
17
+ "typecheck": "tsc --noEmit"
18
+ },
19
+ "devDependencies": {
20
+ "chokidar": "^4.0.3"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./src/modules/cpq/index.ts",
25
+ "default": "./dist/modules/cpq/index.js"
26
+ },
27
+ "./modules/cpq": {
28
+ "types": "./src/modules/cpq/index.ts",
29
+ "default": "./dist/modules/cpq/index.js"
30
+ },
31
+ "./*.ts": {
32
+ "types": "./src/*.ts",
33
+ "default": "./dist/*.js"
34
+ },
35
+ "./*.tsx": {
36
+ "types": "./src/*.tsx",
37
+ "default": "./dist/*.js"
38
+ },
39
+ "./*.json": "./src/*.json",
40
+ "./*": {
41
+ "types": [
42
+ "./src/*.ts",
43
+ "./src/*.tsx"
44
+ ],
45
+ "default": "./dist/*.js"
46
+ },
47
+ "./*/*.json": "./src/*/*.json",
48
+ "./*/*": {
49
+ "types": [
50
+ "./src/*/*.ts",
51
+ "./src/*/*.tsx"
52
+ ],
53
+ "default": "./dist/*/*.js"
54
+ },
55
+ "./*/*/*.json": "./src/*/*/*.json",
56
+ "./*/*/*": {
57
+ "types": [
58
+ "./src/*/*/*.ts",
59
+ "./src/*/*/*.tsx"
60
+ ],
61
+ "default": "./dist/*/*/*.js"
62
+ },
63
+ "./*/*/*/*.json": "./src/*/*/*/*.json",
64
+ "./*/*/*/*": {
65
+ "types": [
66
+ "./src/*/*/*/*.ts",
67
+ "./src/*/*/*/*.tsx"
68
+ ],
69
+ "default": "./dist/*/*/*/*.js"
70
+ },
71
+ "./*/*/*/*/*.json": "./src/*/*/*/*/*.json",
72
+ "./*/*/*/*/*": {
73
+ "types": [
74
+ "./src/*/*/*/*/*.ts",
75
+ "./src/*/*/*/*/*.tsx"
76
+ ],
77
+ "default": "./dist/*/*/*/*/*.js"
78
+ },
79
+ "./*/*/*/*/*/*.json": "./src/*/*/*/*/*/*.json",
80
+ "./*/*/*/*/*/*": {
81
+ "types": [
82
+ "./src/*/*/*/*/*/*.ts",
83
+ "./src/*/*/*/*/*/*.tsx"
84
+ ],
85
+ "default": "./dist/*/*/*/*/*/*.js"
86
+ },
87
+ "./*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*.json",
88
+ "./*/*/*/*/*/*/*": {
89
+ "types": [
90
+ "./src/*/*/*/*/*/*/*.ts",
91
+ "./src/*/*/*/*/*/*/*.tsx"
92
+ ],
93
+ "default": "./dist/*/*/*/*/*/*/*.js"
94
+ },
95
+ "./*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*.json",
96
+ "./*/*/*/*/*/*/*/*": {
97
+ "types": [
98
+ "./src/*/*/*/*/*/*/*/*.ts",
99
+ "./src/*/*/*/*/*/*/*/*.tsx"
100
+ ],
101
+ "default": "./dist/*/*/*/*/*/*/*/*.js"
102
+ },
103
+ "./*/*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*/*.json",
104
+ "./*/*/*/*/*/*/*/*/*": {
105
+ "types": [
106
+ "./src/*/*/*/*/*/*/*/*/*.ts",
107
+ "./src/*/*/*/*/*/*/*/*/*.tsx"
108
+ ],
109
+ "default": "./dist/*/*/*/*/*/*/*/*/*.js"
110
+ },
111
+ "./*/*/*/*/*/*/*/*/*/*.json": "./src/*/*/*/*/*/*/*/*/*/*.json",
112
+ "./*/*/*/*/*/*/*/*/*/*": {
113
+ "types": [
114
+ "./src/*/*/*/*/*/*/*/*/*/*.ts",
115
+ "./src/*/*/*/*/*/*/*/*/*/*.tsx"
116
+ ],
117
+ "default": "./dist/*/*/*/*/*/*/*/*/*/*.js"
118
+ }
119
+ },
120
+ "peerDependencies": {
121
+ "@mikro-orm/core": "^7.0.0",
122
+ "@mikro-orm/decorators": "^7.0.0",
123
+ "@mikro-orm/postgresql": "^7.0.0",
124
+ "@open-mercato/core": "^0.6.0",
125
+ "@open-mercato/events": "^0.6.0",
126
+ "@open-mercato/shared": "^0.6.0",
127
+ "@open-mercato/ui": "^0.6.0",
128
+ "@tanstack/react-table": "^8.0.0 || ^9.0.0",
129
+ "awilix": "^12.0.0",
130
+ "bcryptjs": "^3.0.0",
131
+ "lucide-react": "^0.400.0",
132
+ "next": "^16.0.0",
133
+ "react": "^19.0.0",
134
+ "react-dom": "^19.0.0",
135
+ "xlsx": "^0.18.0",
136
+ "zod": "^4.0.0"
137
+ },
138
+ "publishConfig": {
139
+ "access": "public"
140
+ },
141
+ "repository": {
142
+ "type": "git",
143
+ "url": "https://github.com/dainamite-technologies/dainamite-core.git",
144
+ "directory": "packages/cpq"
145
+ },
146
+ "license": "MIT",
147
+ "engines": {
148
+ "node": ">=24.0.0"
149
+ }
150
+ }
@@ -0,0 +1,177 @@
1
+ import { test, expect, type APIRequestContext } from '@playwright/test'
2
+ import { getAuthToken, apiRequest } from '@open-mercato/core/helpers/integration/api'
3
+
4
+ /**
5
+ * TC-CPQ-001: Product specification CRUD via REST API
6
+ * Category: CPQ — API
7
+ * Priority: High
8
+ *
9
+ * Validates the foundation entity of the CPQ product catalogue.
10
+ */
11
+
12
+ const uniq = () => Date.now().toString(36) + Math.random().toString(36).slice(2, 6)
13
+
14
+ async function deleteSpec(request: APIRequestContext, token: string, id: string): Promise<void> {
15
+ await apiRequest(request, 'DELETE', '/api/cpq/product-specifications', {
16
+ token,
17
+ data: { id },
18
+ })
19
+ }
20
+
21
+ test.describe('TC-CPQ-001: Product Specification CRUD (API)', () => {
22
+ let token: string
23
+ const createdIds: string[] = []
24
+
25
+ test.beforeAll(async ({ request }) => {
26
+ token = await getAuthToken(request, 'admin')
27
+ })
28
+
29
+ test.afterEach(async ({ request }) => {
30
+ while (createdIds.length > 0) {
31
+ const id = createdIds.pop()!
32
+ await deleteSpec(request, token, id)
33
+ }
34
+ })
35
+
36
+ test('creates a simple specification with defaults', async ({ request }) => {
37
+ const code = `SPEC-${uniq()}`
38
+ const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
39
+ token,
40
+ data: { code, name: 'Test Spec' },
41
+ })
42
+ expect(res.ok(), `POST status was ${res.status()}`).toBeTruthy()
43
+
44
+ const body = await res.json()
45
+ expect(body).toMatchObject({
46
+ code,
47
+ name: 'Test Spec',
48
+ specType: 'simple',
49
+ lifecycleStatus: 'draft',
50
+ version: 1,
51
+ isActive: true,
52
+ })
53
+ expect(body.id).toBeTruthy()
54
+ createdIds.push(body.id)
55
+ })
56
+
57
+ test('rejects creation with missing code', async ({ request }) => {
58
+ const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
59
+ token,
60
+ data: { name: 'No Code' },
61
+ })
62
+ expect(res.status()).toBe(400)
63
+ })
64
+
65
+ test('rejects creation with empty code', async ({ request }) => {
66
+ const res = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
67
+ token,
68
+ data: { code: '', name: 'Empty Code' },
69
+ })
70
+ expect(res.status()).toBe(400)
71
+ })
72
+
73
+ test('lists previously created specifications', async ({ request }) => {
74
+ const code = `SPEC-${uniq()}`
75
+ const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
76
+ token,
77
+ data: { code, name: 'Listable Spec' },
78
+ })
79
+ const createdBody = await created.json()
80
+ createdIds.push(createdBody.id)
81
+
82
+ const list = await apiRequest(request, 'GET', '/api/cpq/product-specifications?pageSize=100', {
83
+ token,
84
+ data: undefined,
85
+ })
86
+ expect(list.ok()).toBeTruthy()
87
+ const listBody = await list.json()
88
+ const found = (listBody.items as Array<{ id: string; code: string }>).find((s) => s.id === createdBody.id)
89
+ expect(found).toBeDefined()
90
+ expect(found?.code).toBe(code)
91
+ })
92
+
93
+ test('reads a specification detail by id (with related collections)', async ({ request }) => {
94
+ const code = `SPEC-${uniq()}`
95
+ const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
96
+ token,
97
+ data: { code, name: 'Detail Spec', specType: 'bundle' },
98
+ })
99
+ const createdBody = await created.json()
100
+ createdIds.push(createdBody.id)
101
+
102
+ const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${createdBody.id}`, {
103
+ token,
104
+ data: undefined,
105
+ })
106
+ expect(detail.ok()).toBeTruthy()
107
+ const detailBody = await detail.json()
108
+ expect(detailBody.id).toBe(createdBody.id)
109
+ expect(detailBody.specType).toBe('bundle')
110
+ expect(Array.isArray(detailBody.attributes)).toBe(true)
111
+ expect(Array.isArray(detailBody.offerings)).toBe(true)
112
+ expect(Array.isArray(detailBody.relationships)).toBe(true)
113
+ })
114
+
115
+ test('returns 404 for unknown id', async ({ request }) => {
116
+ const res = await apiRequest(
117
+ request,
118
+ 'GET',
119
+ '/api/cpq/product-specifications?id=00000000-0000-4000-8000-000000000000',
120
+ { token, data: undefined },
121
+ )
122
+ expect(res.status()).toBe(404)
123
+ })
124
+
125
+ test('updates fields and bumps the persisted state', async ({ request }) => {
126
+ const code = `SPEC-${uniq()}`
127
+ const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
128
+ token,
129
+ data: { code, name: 'Original Name' },
130
+ })
131
+ const { id } = await created.json()
132
+ createdIds.push(id)
133
+
134
+ const upd = await apiRequest(request, 'PUT', '/api/cpq/product-specifications', {
135
+ token,
136
+ data: { id, name: 'Updated Name', lifecycleStatus: 'active' },
137
+ })
138
+ expect(upd.ok(), `PUT status was ${upd.status()}`).toBeTruthy()
139
+
140
+ const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${id}`, {
141
+ token,
142
+ data: undefined,
143
+ })
144
+ const body = await detail.json()
145
+ expect(body.name).toBe('Updated Name')
146
+ expect(body.lifecycleStatus).toBe('active')
147
+ })
148
+
149
+ test('soft-deletes a specification (subsequent detail returns 404)', async ({ request }) => {
150
+ const code = `SPEC-${uniq()}`
151
+ const created = await apiRequest(request, 'POST', '/api/cpq/product-specifications', {
152
+ token,
153
+ data: { code, name: 'To Delete' },
154
+ })
155
+ const { id } = await created.json()
156
+
157
+ const del = await apiRequest(request, 'DELETE', '/api/cpq/product-specifications', {
158
+ token,
159
+ data: { id },
160
+ })
161
+ expect(del.ok(), `DELETE status was ${del.status()}`).toBeTruthy()
162
+
163
+ const detail = await apiRequest(request, 'GET', `/api/cpq/product-specifications?id=${id}`, {
164
+ token,
165
+ data: undefined,
166
+ })
167
+ expect(detail.status()).toBe(404)
168
+ })
169
+
170
+ test('rejects unauthenticated requests', async ({ request }) => {
171
+ const res = await request.fetch(
172
+ `${process.env.BASE_URL || 'http://localhost:3000'}/api/cpq/product-specifications`,
173
+ { method: 'GET' },
174
+ )
175
+ expect([401, 403]).toContain(res.status())
176
+ })
177
+ })