@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,658 @@
1
+ import { DefaultCpqQuotingService, QuotingError } from '../cpqQuotingService'
2
+ import type { TenantScope } from '../types'
3
+
4
+ interface MockEm {
5
+ findOne: jest.MockedFunction<(entity: unknown, where: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown>>
6
+ find: jest.MockedFunction<
7
+ (entity: unknown, where: Record<string, unknown>, options?: Record<string, unknown>) => Promise<unknown[]>
8
+ >
9
+ create: jest.MockedFunction<(entity: unknown, data: Record<string, unknown>) => unknown>
10
+ persist: jest.MockedFunction<(entity: unknown) => unknown>
11
+ assign: jest.MockedFunction<(entity: unknown, updates: Record<string, unknown>) => unknown>
12
+ flush: jest.MockedFunction<() => Promise<void>>
13
+ fork: jest.MockedFunction<() => MockEm>
14
+ }
15
+
16
+ interface MockPricingService {
17
+ resolveProductCharges: jest.MockedFunction<(input: unknown) => Promise<unknown[]>>
18
+ }
19
+
20
+ interface MockValidationService {
21
+ validateOfferingConfiguration: jest.MockedFunction<(input: unknown) => Promise<{ valid: boolean; errors: unknown[] }>>
22
+ validateRelationships: jest.MockedFunction<(input: unknown) => Promise<unknown[]>>
23
+ }
24
+
25
+ interface MockProductService {
26
+ getOfferingWithDetails: jest.MockedFunction<(input: unknown) => Promise<unknown>>
27
+ }
28
+
29
+ interface MockNumberGenerator {
30
+ generate: jest.MockedFunction<(input: unknown) => Promise<{ number: string }>>
31
+ }
32
+
33
+ function createMockEm(): MockEm {
34
+ const em: MockEm = {
35
+ findOne: jest.fn(),
36
+ find: jest.fn(),
37
+ create: jest.fn().mockImplementation((_e, data: Record<string, unknown>) => ({ ...data, id: data.id ?? 'gen-id' })),
38
+ persist: jest.fn(),
39
+ assign: jest.fn().mockImplementation((entity: Record<string, unknown>, updates: Record<string, unknown>) => {
40
+ Object.assign(entity, updates)
41
+ return entity
42
+ }),
43
+ flush: jest.fn().mockResolvedValue(undefined),
44
+ fork: jest.fn(),
45
+ }
46
+ em.fork.mockReturnValue(em)
47
+ return em
48
+ }
49
+
50
+ function makeService() {
51
+ const em = createMockEm()
52
+ const pricingService: MockPricingService = {
53
+ resolveProductCharges: jest.fn().mockResolvedValue([]),
54
+ }
55
+ const validationService: MockValidationService = {
56
+ validateOfferingConfiguration: jest.fn().mockResolvedValue({ valid: true, errors: [] }),
57
+ validateRelationships: jest.fn().mockResolvedValue([]),
58
+ }
59
+ const productService: MockProductService = {
60
+ getOfferingWithDetails: jest.fn(),
61
+ }
62
+ const numberGenerator: MockNumberGenerator = {
63
+ generate: jest.fn().mockResolvedValue({ number: 'Q-0001' }),
64
+ }
65
+ const service = new DefaultCpqQuotingService({
66
+ em: em as unknown as never,
67
+ cpqPricingService: pricingService as unknown as never,
68
+ cpqValidationService: validationService as unknown as never,
69
+ cpqProductService: productService as unknown as never,
70
+ salesDocumentNumberGenerator: numberGenerator as unknown as never,
71
+ })
72
+ return { em, pricingService, validationService, productService, numberGenerator, service }
73
+ }
74
+
75
+ const SCOPE: TenantScope = {
76
+ tenantId: '00000000-0000-0000-0000-000000000001',
77
+ organizationId: '00000000-0000-0000-0000-000000000002',
78
+ }
79
+
80
+ describe('QuotingError', () => {
81
+ it('exposes status, message, and optional cpqStatus', () => {
82
+ const err = new QuotingError(409, 'Bad transition', 'incomplete')
83
+ expect(err.status).toBe(409)
84
+ expect(err.message).toBe('Bad transition')
85
+ expect(err.cpqStatus).toBe('incomplete')
86
+ expect(err.name).toBe('QuotingError')
87
+ expect(err).toBeInstanceOf(Error)
88
+ })
89
+
90
+ it('cpqStatus is optional', () => {
91
+ const err = new QuotingError(404, 'Not found')
92
+ expect(err.cpqStatus).toBeUndefined()
93
+ })
94
+ })
95
+
96
+ describe('DefaultCpqQuotingService.createQuote — guards', () => {
97
+ it('throws 404 when customer is not found', async () => {
98
+ const { em, service } = makeService()
99
+ em.findOne.mockResolvedValueOnce(null) // customer lookup
100
+
101
+ await expect(
102
+ service.createQuote(
103
+ { customerId: '11111111-1111-4111-8111-111111111111' },
104
+ SCOPE,
105
+ ),
106
+ ).rejects.toMatchObject({ status: 404, message: 'Customer not found' })
107
+ })
108
+
109
+ it('throws 404 when input.quoteId points to a non-existent SalesQuote', async () => {
110
+ const { em, service } = makeService()
111
+ em.findOne.mockResolvedValueOnce({ id: 'cust', kind: 'company' }) // customer
112
+ em.findOne.mockResolvedValueOnce(null) // SalesQuote not found
113
+
114
+ await expect(
115
+ service.createQuote(
116
+ { customerId: 'cust', quoteId: 'missing-sales-quote' },
117
+ SCOPE,
118
+ ),
119
+ ).rejects.toMatchObject({ status: 404, message: 'Sales quote not found' })
120
+ })
121
+
122
+ it('returns the existing CPQ config without recreating when one already exists for the SalesQuote', async () => {
123
+ const { em, service } = makeService()
124
+ const existingSalesQuote = { id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' }
125
+ const existingCpqConfig = {
126
+ id: 'cpq-1',
127
+ quoteId: 'sq-1',
128
+ customerId: 'cust',
129
+ cpqStatus: 'ready',
130
+ version: 1,
131
+ currencyCode: 'USD',
132
+ validationResult: { valid: true, errors: [] },
133
+ pricingSummary: {
134
+ nrcTotal: 0,
135
+ mrcTotal: 0,
136
+ usageCharges: [],
137
+ usageTotalEstimated: null,
138
+ discountTotal: 0,
139
+ surchargeTotal: 0,
140
+ currencyCode: 'USD',
141
+ },
142
+ parentQuoteId: null,
143
+ }
144
+
145
+ em.findOne
146
+ .mockResolvedValueOnce({ id: 'cust', kind: 'company' }) // customer
147
+ .mockResolvedValueOnce(existingSalesQuote) // SalesQuote
148
+ .mockResolvedValueOnce(existingCpqConfig) // existing CpqQuoteConfiguration
149
+ em.find.mockResolvedValueOnce([]) // line configs
150
+
151
+ // Pass currencyCode to short-circuit getBaseCurrencyCode (which would otherwise
152
+ // consume an em.findOne mock by querying the Currency entity).
153
+ const result = await service.createQuote(
154
+ { customerId: 'cust', quoteId: 'sq-1', currencyCode: 'USD' },
155
+ SCOPE,
156
+ )
157
+
158
+ expect(result.id).toBe('cpq-1')
159
+ expect(result.quoteId).toBe('sq-1')
160
+ expect(result.cpqStatus).toBe('ready')
161
+ expect(em.create).not.toHaveBeenCalled()
162
+ expect(em.flush).not.toHaveBeenCalled()
163
+ })
164
+ })
165
+
166
+ describe('DefaultCpqQuotingService.transitionStatus — state machine guard', () => {
167
+ // Per operator-override decision, "any → any" is allowed except for
168
+ // self-transitions and ARC-specific guard violations.
169
+ it('persists previously-illegal new → approved jump (operator override)', async () => {
170
+ const { em, service } = makeService()
171
+ const cpqConfig = {
172
+ id: 'cpq-1',
173
+ quoteId: 'sq-1',
174
+ cpqStatus: 'new',
175
+ customerId: 'cust',
176
+ currencyCode: 'USD',
177
+ version: 1,
178
+ parentQuoteId: null,
179
+ validationResult: { valid: true, errors: [] },
180
+ pricingSummary: {
181
+ nrcTotal: 0,
182
+ mrcTotal: 0,
183
+ usageCharges: [],
184
+ usageTotalEstimated: null,
185
+ discountTotal: 0,
186
+ surchargeTotal: 0,
187
+ currencyCode: 'USD',
188
+ },
189
+ }
190
+ em.findOne
191
+ .mockResolvedValueOnce(cpqConfig)
192
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
193
+ em.find.mockResolvedValueOnce([]) // line configs
194
+
195
+ await service.transitionStatus('cpq-1', 'approved', SCOPE)
196
+ expect(cpqConfig.cpqStatus).toBe('approved')
197
+ })
198
+
199
+ it('persists previously-illegal accepted → with_customer jump (operator override)', async () => {
200
+ const { em, service } = makeService()
201
+ const cpqConfig = {
202
+ id: 'cpq-1',
203
+ quoteId: 'sq-1',
204
+ cpqStatus: 'accepted',
205
+ customerId: 'cust',
206
+ currencyCode: 'USD',
207
+ version: 1,
208
+ parentQuoteId: null,
209
+ validationResult: { valid: true, errors: [] },
210
+ pricingSummary: {
211
+ nrcTotal: 0,
212
+ mrcTotal: 0,
213
+ usageCharges: [],
214
+ usageTotalEstimated: null,
215
+ discountTotal: 0,
216
+ surchargeTotal: 0,
217
+ currencyCode: 'USD',
218
+ },
219
+ }
220
+ em.findOne
221
+ .mockResolvedValueOnce(cpqConfig)
222
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD' })
223
+ em.find.mockResolvedValueOnce([]) // line configs
224
+
225
+ await service.transitionStatus('cpq-1', 'with_customer', SCOPE)
226
+ expect(cpqConfig.cpqStatus).toBe('with_customer')
227
+ })
228
+
229
+ it('still rejects self-transition (new → new)', async () => {
230
+ const { em, service } = makeService()
231
+ em.findOne
232
+ .mockResolvedValueOnce({ id: 'cpq-1', quoteId: 'sq-1', cpqStatus: 'new' })
233
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
234
+
235
+ await expect(service.transitionStatus('cpq-1', 'new', SCOPE)).rejects.toMatchObject({
236
+ status: 409,
237
+ message: "Cannot transition from 'new' to 'new'",
238
+ })
239
+ })
240
+
241
+ it('persists ready → in_approval', async () => {
242
+ const { em, service } = makeService()
243
+ const cpqConfig = {
244
+ id: 'cpq-1',
245
+ quoteId: 'sq-1',
246
+ cpqStatus: 'ready',
247
+ customerId: 'cust',
248
+ currencyCode: 'USD',
249
+ version: 1,
250
+ parentQuoteId: null,
251
+ validationResult: { valid: true, errors: [] },
252
+ pricingSummary: {
253
+ nrcTotal: 0,
254
+ mrcTotal: 0,
255
+ usageCharges: [],
256
+ usageTotalEstimated: null,
257
+ discountTotal: 0,
258
+ surchargeTotal: 0,
259
+ currencyCode: 'USD',
260
+ },
261
+ }
262
+ const salesQuote = { id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' }
263
+ em.findOne.mockResolvedValueOnce(cpqConfig).mockResolvedValueOnce(salesQuote)
264
+ em.find.mockResolvedValueOnce([]) // line configs
265
+
266
+ const result = await service.transitionStatus('cpq-1', 'in_approval', SCOPE)
267
+
268
+ expect(cpqConfig.cpqStatus).toBe('in_approval')
269
+ expect(em.flush).toHaveBeenCalled()
270
+ expect(result.cpqStatus).toBe('in_approval')
271
+ })
272
+
273
+ it('persists ready → pre_approved (auto-approval bypass)', async () => {
274
+ const { em, service } = makeService()
275
+ const cpqConfig = {
276
+ id: 'cpq-1',
277
+ quoteId: 'sq-1',
278
+ cpqStatus: 'ready',
279
+ customerId: 'cust',
280
+ currencyCode: 'USD',
281
+ version: 1,
282
+ parentQuoteId: null,
283
+ validationResult: { valid: true, errors: [] },
284
+ pricingSummary: {
285
+ nrcTotal: 0,
286
+ mrcTotal: 0,
287
+ usageCharges: [],
288
+ usageTotalEstimated: null,
289
+ discountTotal: 0,
290
+ surchargeTotal: 0,
291
+ currencyCode: 'USD',
292
+ },
293
+ }
294
+ em.findOne.mockResolvedValueOnce(cpqConfig).mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
295
+ em.find.mockResolvedValueOnce([])
296
+
297
+ await service.transitionStatus('cpq-1', 'pre_approved', SCOPE)
298
+ expect(cpqConfig.cpqStatus).toBe('pre_approved')
299
+ })
300
+
301
+ it('persists in_approval → approved', async () => {
302
+ const { em, service } = makeService()
303
+ const cpqConfig = {
304
+ id: 'cpq-1',
305
+ quoteId: 'sq-1',
306
+ cpqStatus: 'in_approval',
307
+ customerId: 'cust',
308
+ currencyCode: 'USD',
309
+ version: 1,
310
+ parentQuoteId: null,
311
+ validationResult: { valid: true, errors: [] },
312
+ pricingSummary: {
313
+ nrcTotal: 0,
314
+ mrcTotal: 0,
315
+ usageCharges: [],
316
+ usageTotalEstimated: null,
317
+ discountTotal: 0,
318
+ surchargeTotal: 0,
319
+ currencyCode: 'USD',
320
+ },
321
+ }
322
+ em.findOne.mockResolvedValueOnce(cpqConfig).mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
323
+ em.find.mockResolvedValueOnce([])
324
+
325
+ await service.transitionStatus('cpq-1', 'approved', SCOPE)
326
+ expect(cpqConfig.cpqStatus).toBe('approved')
327
+ })
328
+
329
+ it('persists with_customer → accepted (terminal)', async () => {
330
+ const { em, service } = makeService()
331
+ const cpqConfig = {
332
+ id: 'cpq-1',
333
+ quoteId: 'sq-1',
334
+ cpqStatus: 'with_customer',
335
+ customerId: 'cust',
336
+ currencyCode: 'USD',
337
+ version: 1,
338
+ parentQuoteId: null,
339
+ validationResult: { valid: true, errors: [] },
340
+ pricingSummary: {
341
+ nrcTotal: 0,
342
+ mrcTotal: 0,
343
+ usageCharges: [],
344
+ usageTotalEstimated: null,
345
+ discountTotal: 0,
346
+ surchargeTotal: 0,
347
+ currencyCode: 'USD',
348
+ },
349
+ }
350
+ em.findOne.mockResolvedValueOnce(cpqConfig).mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
351
+ em.find.mockResolvedValueOnce([])
352
+
353
+ await service.transitionStatus('cpq-1', 'accepted', SCOPE)
354
+ expect(cpqConfig.cpqStatus).toBe('accepted')
355
+ })
356
+
357
+ it('persists incomplete → cancelled', async () => {
358
+ const { em, service } = makeService()
359
+ const cpqConfig = {
360
+ id: 'cpq-1',
361
+ quoteId: 'sq-1',
362
+ cpqStatus: 'incomplete',
363
+ customerId: 'cust',
364
+ currencyCode: 'USD',
365
+ version: 1,
366
+ parentQuoteId: null,
367
+ validationResult: { valid: false, errors: [] },
368
+ pricingSummary: {
369
+ nrcTotal: 0,
370
+ mrcTotal: 0,
371
+ usageCharges: [],
372
+ usageTotalEstimated: null,
373
+ discountTotal: 0,
374
+ surchargeTotal: 0,
375
+ currencyCode: 'USD',
376
+ },
377
+ }
378
+ em.findOne.mockResolvedValueOnce(cpqConfig).mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-001' })
379
+ em.find.mockResolvedValueOnce([])
380
+
381
+ await service.transitionStatus('cpq-1', 'cancelled', SCOPE)
382
+ expect(cpqConfig.cpqStatus).toBe('cancelled')
383
+ })
384
+ })
385
+
386
+ describe('DefaultCpqQuotingService.getQuote', () => {
387
+ it('throws 404 when CPQ config is not found (lookup by id then by quoteId)', async () => {
388
+ const { em, service } = makeService()
389
+ em.findOne.mockResolvedValueOnce(null).mockResolvedValueOnce(null)
390
+
391
+ await expect(service.getQuote('missing', SCOPE)).rejects.toMatchObject({
392
+ status: 404,
393
+ })
394
+ })
395
+
396
+ it('throws 404 when SalesQuote is missing (orphan CPQ config)', async () => {
397
+ const { em, service } = makeService()
398
+ em.findOne
399
+ .mockResolvedValueOnce({
400
+ id: 'cpq-1',
401
+ quoteId: 'sq-orphan',
402
+ cpqStatus: 'ready',
403
+ customerId: 'cust',
404
+ currencyCode: 'USD',
405
+ version: 1,
406
+ parentQuoteId: null,
407
+ validationResult: null,
408
+ pricingSummary: null,
409
+ })
410
+ .mockResolvedValueOnce(null) // SalesQuote missing
411
+
412
+ await expect(service.getQuote('cpq-1', SCOPE)).rejects.toMatchObject({
413
+ status: 404,
414
+ })
415
+ })
416
+
417
+ it('falls back to lookup by quoteId when id miss', async () => {
418
+ const { em, service } = makeService()
419
+ em.findOne
420
+ .mockResolvedValueOnce(null) // id miss
421
+ .mockResolvedValueOnce({
422
+ id: 'cpq-1',
423
+ quoteId: 'sq-1',
424
+ cpqStatus: 'ready',
425
+ customerId: 'cust',
426
+ currencyCode: 'USD',
427
+ version: 1,
428
+ parentQuoteId: null,
429
+ validationResult: { valid: true, errors: [] },
430
+ pricingSummary: {
431
+ nrcTotal: 0,
432
+ mrcTotal: 0,
433
+ usageCharges: [],
434
+ usageTotalEstimated: null,
435
+ discountTotal: 0,
436
+ surchargeTotal: 0,
437
+ currencyCode: 'USD',
438
+ },
439
+ })
440
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-0001' })
441
+ em.find.mockResolvedValueOnce([])
442
+
443
+ const result = await service.getQuote('sq-1', SCOPE)
444
+ expect(result.id).toBe('cpq-1')
445
+ expect(result.quoteNumber).toBe('Q-0001')
446
+
447
+ // Verify second findOne called with quoteId filter
448
+ const [, secondWhere] = em.findOne.mock.calls[1]
449
+ expect(secondWhere).toMatchObject({ quoteId: 'sq-1' })
450
+ })
451
+
452
+ it('returns a QuoteResult with persisted status, customer, currency and metadata', async () => {
453
+ const { em, service } = makeService()
454
+ em.findOne
455
+ .mockResolvedValueOnce({
456
+ id: 'cpq-1',
457
+ quoteId: 'sq-1',
458
+ cpqStatus: 'with_customer',
459
+ customerId: 'cust-1',
460
+ currencyCode: 'EUR',
461
+ version: 2,
462
+ parentQuoteId: 'cpq-original',
463
+ validationResult: { valid: true, errors: [] },
464
+ pricingSummary: null, // intentionally null — see assertion below
465
+ })
466
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'EUR', quoteNumber: 'Q-9999' })
467
+ em.find.mockResolvedValueOnce([]) // no lines
468
+
469
+ const result = await service.getQuote('cpq-1', SCOPE)
470
+
471
+ expect(result).toMatchObject({
472
+ id: 'cpq-1',
473
+ quoteId: 'sq-1',
474
+ quoteNumber: 'Q-9999',
475
+ cpqStatus: 'with_customer',
476
+ customerId: 'cust-1',
477
+ currencyCode: 'EUR',
478
+ version: 2,
479
+ parentQuoteId: 'cpq-original',
480
+ lines: [],
481
+ })
482
+ })
483
+
484
+ it('NOTE: getQuote always recomputes pricingSummary from lineConfigs — persisted summary on the entity is ignored', async () => {
485
+ // This documents (and pins) that buildQuoteResult does NOT preserve the entity-level pricingSummary.
486
+ // With no lines, the result is always zero — even when 1500/200 was persisted.
487
+ const { em, service } = makeService()
488
+ em.findOne
489
+ .mockResolvedValueOnce({
490
+ id: 'cpq-1',
491
+ quoteId: 'sq-1',
492
+ cpqStatus: 'with_customer',
493
+ customerId: 'cust-1',
494
+ currencyCode: 'EUR',
495
+ version: 2,
496
+ parentQuoteId: null,
497
+ validationResult: { valid: true, errors: [] },
498
+ pricingSummary: { nrcTotal: 1500, mrcTotal: 200 },
499
+ })
500
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'EUR', quoteNumber: 'Q-9999' })
501
+ em.find.mockResolvedValueOnce([])
502
+
503
+ const result = await service.getQuote('cpq-1', SCOPE)
504
+ expect(result.pricingSummary.nrcTotal).toBe(0)
505
+ expect(result.pricingSummary.mrcTotal).toBe(0)
506
+ })
507
+ })
508
+
509
+ describe('DefaultCpqQuotingService.removeQuoteItem', () => {
510
+ it('throws 409 when quote is in a terminal status (accepted)', async () => {
511
+ const { em, service } = makeService()
512
+ em.findOne
513
+ .mockResolvedValueOnce({
514
+ id: 'cpq-1',
515
+ quoteId: 'sq-1',
516
+ cpqStatus: 'accepted',
517
+ })
518
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD' })
519
+
520
+ await expect(service.removeQuoteItem('cpq-1', 'line-1', SCOPE)).rejects.toMatchObject({
521
+ status: 409,
522
+ message: "Cannot modify quote in 'accepted' status",
523
+ })
524
+ })
525
+
526
+ it('throws 409 when quote is rejected (terminal)', async () => {
527
+ const { em, service } = makeService()
528
+ em.findOne
529
+ .mockResolvedValueOnce({ id: 'cpq-1', quoteId: 'sq-1', cpqStatus: 'rejected' })
530
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD' })
531
+
532
+ await expect(service.removeQuoteItem('cpq-1', 'line-1', SCOPE)).rejects.toMatchObject({
533
+ status: 409,
534
+ })
535
+ })
536
+
537
+ it('throws 409 when quote is cancelled (terminal)', async () => {
538
+ const { em, service } = makeService()
539
+ em.findOne
540
+ .mockResolvedValueOnce({ id: 'cpq-1', quoteId: 'sq-1', cpqStatus: 'cancelled' })
541
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD' })
542
+
543
+ await expect(service.removeQuoteItem('cpq-1', 'line-1', SCOPE)).rejects.toMatchObject({
544
+ status: 409,
545
+ })
546
+ })
547
+
548
+ it('throws 404 when the line is not found', async () => {
549
+ const { em, service } = makeService()
550
+ em.findOne
551
+ .mockResolvedValueOnce({ id: 'cpq-1', quoteId: 'sq-1', cpqStatus: 'ready' })
552
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD' })
553
+ .mockResolvedValueOnce(null) // line lookup miss
554
+
555
+ await expect(service.removeQuoteItem('cpq-1', 'missing-line', SCOPE)).rejects.toMatchObject({
556
+ status: 404,
557
+ message: 'Quote line not found',
558
+ })
559
+ })
560
+ })
561
+
562
+ describe('DefaultCpqQuotingService.recalculate', () => {
563
+ it('does NOT fork the EM when save=false (read-only path uses primary EM)', async () => {
564
+ const { em, service } = makeService()
565
+ em.findOne
566
+ .mockResolvedValueOnce({
567
+ id: 'cpq-1',
568
+ quoteId: 'sq-1',
569
+ cpqStatus: 'new',
570
+ customerId: 'cust',
571
+ currencyCode: 'USD',
572
+ version: 1,
573
+ parentQuoteId: null,
574
+ validationResult: null,
575
+ pricingSummary: null,
576
+ quoteContext: null,
577
+ })
578
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-0001' })
579
+ em.find.mockResolvedValueOnce([]) // line configs (recalculateInternal)
580
+
581
+ await service.recalculate('cpq-1', { save: false }, SCOPE)
582
+
583
+ expect(em.fork).not.toHaveBeenCalled()
584
+ expect(em.flush).not.toHaveBeenCalled()
585
+ })
586
+
587
+ it('forks the EM when save=true (write path uses isolated UoW)', async () => {
588
+ const { em, service } = makeService()
589
+ em.findOne
590
+ .mockResolvedValueOnce({
591
+ id: 'cpq-1',
592
+ quoteId: 'sq-1',
593
+ cpqStatus: 'new',
594
+ customerId: 'cust',
595
+ currencyCode: 'USD',
596
+ version: 1,
597
+ parentQuoteId: null,
598
+ validationResult: null,
599
+ pricingSummary: null,
600
+ quoteContext: null,
601
+ })
602
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-0001' })
603
+ em.find.mockResolvedValueOnce([]) // no lines
604
+
605
+ await service.recalculate('cpq-1', { save: true }, SCOPE)
606
+
607
+ expect(em.fork).toHaveBeenCalledTimes(1)
608
+ })
609
+
610
+ it('marks status "new" when no lines exist (auto-managed status)', async () => {
611
+ const { em, service } = makeService()
612
+ const cpqConfig = {
613
+ id: 'cpq-1',
614
+ quoteId: 'sq-1',
615
+ cpqStatus: 'incomplete', // auto-managed → should become 'new'
616
+ customerId: 'cust',
617
+ currencyCode: 'USD',
618
+ version: 1,
619
+ parentQuoteId: null,
620
+ validationResult: null,
621
+ pricingSummary: null,
622
+ quoteContext: null,
623
+ }
624
+ em.findOne
625
+ .mockResolvedValueOnce(cpqConfig)
626
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-0001' })
627
+ em.find.mockResolvedValueOnce([])
628
+
629
+ const result = await service.recalculate('cpq-1', { save: true }, SCOPE)
630
+
631
+ expect(cpqConfig.cpqStatus).toBe('new')
632
+ expect(result.cpqStatus).toBe('new')
633
+ })
634
+
635
+ it('does NOT auto-update status when current is non-auto-managed (e.g. with_customer)', async () => {
636
+ const { em, service } = makeService()
637
+ const cpqConfig = {
638
+ id: 'cpq-1',
639
+ quoteId: 'sq-1',
640
+ cpqStatus: 'with_customer',
641
+ customerId: 'cust',
642
+ currencyCode: 'USD',
643
+ version: 1,
644
+ parentQuoteId: null,
645
+ validationResult: null,
646
+ pricingSummary: null,
647
+ quoteContext: null,
648
+ }
649
+ em.findOne
650
+ .mockResolvedValueOnce(cpqConfig)
651
+ .mockResolvedValueOnce({ id: 'sq-1', currencyCode: 'USD', quoteNumber: 'Q-0001' })
652
+ em.find.mockResolvedValueOnce([])
653
+
654
+ await service.recalculate('cpq-1', { save: true }, SCOPE)
655
+
656
+ expect(cpqConfig.cpqStatus).toBe('with_customer') // unchanged
657
+ })
658
+ })