@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,39 @@
1
+ import { createRequestContainer } from '@open-mercato/shared/lib/di/container'
2
+ import { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'
3
+ import { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'
4
+ import { resolveFeatureCheckContext } from '@open-mercato/core/modules/directory/utils/organizationScope'
5
+ import type { EntityManager } from '@mikro-orm/postgresql'
6
+ import type { AwilixContainer } from 'awilix'
7
+ import type { OrganizationScope } from '@open-mercato/core/modules/directory/utils/organizationScope'
8
+
9
+ export type CpqRouteContext = {
10
+ container: AwilixContainer
11
+ auth: Awaited<ReturnType<typeof getAuthFromRequest>>
12
+ em: EntityManager
13
+ organizationId: string
14
+ tenantId: string
15
+ organizationScope: OrganizationScope | null
16
+ translate: (key: string, fallback?: string) => string
17
+ }
18
+
19
+ export async function resolveCpqRouteContext(req: Request): Promise<CpqRouteContext> {
20
+ const container = await createRequestContainer()
21
+ const auth = await getAuthFromRequest(req)
22
+ const { translate } = await resolveTranslations()
23
+ if (!auth || !auth.tenantId) {
24
+ throw new Error('Unauthorized')
25
+ }
26
+ const { organizationId: resolvedOrgId, scope } = await resolveFeatureCheckContext({ container, auth, request: req })
27
+ const organizationId = resolvedOrgId ?? ''
28
+ const tenantId = scope?.tenantId ?? auth.tenantId ?? ''
29
+ const em = container.resolve('em') as EntityManager
30
+ return {
31
+ container,
32
+ auth,
33
+ em,
34
+ organizationId,
35
+ tenantId,
36
+ organizationScope: scope ?? null,
37
+ translate,
38
+ }
39
+ }
@@ -0,0 +1,123 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { resolveCpqRouteContext } from '../../context'
4
+ import {
5
+ cpqInventoryAssetCreateSchema,
6
+ cpqInventoryAssetUpdateSchema,
7
+ } from '../../../data/validators'
8
+
9
+ export const metadata = {
10
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.view'] },
11
+ POST: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
12
+ PUT: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
13
+ DELETE: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
14
+ }
15
+
16
+ export async function GET(req: Request) {
17
+ try {
18
+ const ctx = await resolveCpqRouteContext(req)
19
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
20
+
21
+ const url = new URL(req.url)
22
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
23
+ const service = ctx.container.resolve('cpqInventoryService') as any
24
+
25
+ const id = url.searchParams.get('id')
26
+ if (id) {
27
+ const result = await service.getAsset(id, scope)
28
+ return NextResponse.json(result)
29
+ }
30
+
31
+ const filters: Record<string, unknown> = {}
32
+ const customerId = url.searchParams.get('customerId')
33
+ if (customerId) filters.customerId = customerId
34
+ const status = url.searchParams.get('status')
35
+ if (status) filters.status = status
36
+ const assetType = url.searchParams.get('assetType')
37
+ if (assetType) filters.assetType = assetType
38
+ const productId = url.searchParams.get('productId')
39
+ if (productId) filters.productId = productId
40
+ const subscriptionId = url.searchParams.get('subscriptionId')
41
+ if (subscriptionId) filters.subscriptionId = subscriptionId
42
+ const subscriptionItemId = url.searchParams.get('subscriptionItemId')
43
+ if (subscriptionItemId) filters.subscriptionItemId = subscriptionItemId
44
+ const search = url.searchParams.get('search')
45
+ if (search) filters.search = search
46
+
47
+ const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'code', 'name', 'status', 'assetType', 'purchasePrice'] as const
48
+ const sortFieldParam = url.searchParams.get('sortField') ?? ''
49
+ if ((ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)) {
50
+ filters.sortField = sortFieldParam
51
+ }
52
+ filters.sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'
53
+
54
+ filters.page = Math.max(1, Number(url.searchParams.get('page') ?? '1'))
55
+ filters.pageSize = Math.min(100, Math.max(1, Number(url.searchParams.get('pageSize') ?? '50')))
56
+
57
+ const result = await service.listAssets(filters, scope)
58
+ return NextResponse.json(result)
59
+ } catch (err) {
60
+ console.error('[cpq/inventory/assets.GET]', err)
61
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
62
+ }
63
+ }
64
+
65
+ export async function POST(req: Request) {
66
+ try {
67
+ const ctx = await resolveCpqRouteContext(req)
68
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
69
+
70
+ const body = cpqInventoryAssetCreateSchema.parse(await req.json())
71
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
72
+ const service = ctx.container.resolve('cpqInventoryService') as any
73
+
74
+ const result = await service.createAsset(body, scope)
75
+ return NextResponse.json(result, { status: 201 })
76
+ } catch (err) {
77
+ if (err instanceof z.ZodError) {
78
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
79
+ }
80
+ console.error('[cpq/inventory/assets.POST]', err)
81
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
82
+ }
83
+ }
84
+
85
+ export async function PUT(req: Request) {
86
+ try {
87
+ const ctx = await resolveCpqRouteContext(req)
88
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
89
+
90
+ const body = cpqInventoryAssetUpdateSchema.parse(await req.json())
91
+ const { id, ...updates } = body
92
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
93
+ const service = ctx.container.resolve('cpqInventoryService') as any
94
+
95
+ const result = await service.updateAsset(id, updates, scope)
96
+ return NextResponse.json(result)
97
+ } catch (err) {
98
+ if (err instanceof z.ZodError) {
99
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
100
+ }
101
+ console.error('[cpq/inventory/assets.PUT]', err)
102
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
103
+ }
104
+ }
105
+
106
+ export async function DELETE(req: Request) {
107
+ try {
108
+ const ctx = await resolveCpqRouteContext(req)
109
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
110
+
111
+ const { id } = (await req.json()) as { id?: string }
112
+ if (!id) return NextResponse.json({ error: 'Missing id' }, { status: 400 })
113
+
114
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
115
+ const service = ctx.container.resolve('cpqInventoryService') as any
116
+
117
+ await service.deleteAsset(id, scope)
118
+ return NextResponse.json({ success: true })
119
+ } catch (err) {
120
+ console.error('[cpq/inventory/assets.DELETE]', err)
121
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
122
+ }
123
+ }
@@ -0,0 +1,34 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { resolveCpqRouteContext } from '../../../context'
4
+ import { cpqInventoryStatusTransitionSchema } from '../../../../data/validators'
5
+
6
+ export const metadata = {
7
+ POST: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
8
+ }
9
+
10
+ export async function POST(req: Request) {
11
+ try {
12
+ const ctx = await resolveCpqRouteContext(req)
13
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
14
+
15
+ const body = cpqInventoryStatusTransitionSchema.parse(await req.json())
16
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
17
+ const service = ctx.container.resolve('cpqInventoryService') as any
18
+
19
+ const result = await service.transitionAssetStatus(body.id, body.targetStatus, scope)
20
+ return NextResponse.json(result)
21
+ } catch (err) {
22
+ if (err instanceof z.ZodError) {
23
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
24
+ }
25
+ if (err && typeof err === 'object' && 'status' in err && (err as any).status === 409) {
26
+ return NextResponse.json(
27
+ { error: (err as any).error ?? 'Conflict' },
28
+ { status: 409 },
29
+ )
30
+ }
31
+ console.error('[cpq/inventory/assets/status.POST]', err)
32
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
33
+ }
34
+ }
@@ -0,0 +1,30 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { resolveCpqRouteContext } from '../../../../context'
3
+
4
+ export const metadata = {
5
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.view'] },
6
+ }
7
+
8
+ export async function GET(req: Request, { params }: { params: Promise<{ customerId: string }> }) {
9
+ try {
10
+ const ctx = await resolveCpqRouteContext(req)
11
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
12
+
13
+ const { customerId } = await params
14
+ const url = new URL(req.url)
15
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
16
+ const service = ctx.container.resolve('cpqInventoryService') as any
17
+
18
+ const filters: Record<string, unknown> = {}
19
+ const productId = url.searchParams.get('productId')
20
+ if (productId) filters.productId = productId
21
+ const offeringId = url.searchParams.get('offeringId')
22
+ if (offeringId) filters.offeringId = offeringId
23
+
24
+ const result = await service.getCustomerParentItems(customerId, filters, scope)
25
+ return NextResponse.json(result)
26
+ } catch (err) {
27
+ console.error('[cpq/inventory/customer/parent-items.GET]', err)
28
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
29
+ }
30
+ }
@@ -0,0 +1,31 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { resolveCpqRouteContext } from '../../../context'
3
+
4
+ export const metadata = {
5
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.view'] },
6
+ }
7
+
8
+ export async function GET(req: Request, { params }: { params: Promise<{ customerId: string }> }) {
9
+ try {
10
+ const ctx = await resolveCpqRouteContext(req)
11
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
12
+
13
+ const { customerId } = await params
14
+ const url = new URL(req.url)
15
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
16
+ const service = ctx.container.resolve('cpqInventoryService') as any
17
+
18
+ const filters: Record<string, unknown> = {}
19
+ const status = url.searchParams.get('status')
20
+ if (status) filters.status = status
21
+ const productId = url.searchParams.get('productId')
22
+ if (productId) filters.productId = productId
23
+ filters.includeTerminated = url.searchParams.get('includeTerminated') === 'true'
24
+
25
+ const result = await service.getCustomerInventory(customerId, filters, scope)
26
+ return NextResponse.json(result)
27
+ } catch (err) {
28
+ console.error('[cpq/inventory/customer.GET]', err)
29
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
30
+ }
31
+ }
@@ -0,0 +1,107 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { resolveCpqRouteContext } from '../../context'
4
+ import {
5
+ cpqInventorySubscriptionItemCreateSchema,
6
+ cpqInventorySubscriptionItemUpdateSchema,
7
+ } from '../../../data/validators'
8
+
9
+ export const metadata = {
10
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.view'] },
11
+ POST: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
12
+ PUT: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
13
+ DELETE: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
14
+ }
15
+
16
+ export async function GET(req: Request) {
17
+ try {
18
+ const ctx = await resolveCpqRouteContext(req)
19
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
20
+
21
+ const url = new URL(req.url)
22
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
23
+ const service = ctx.container.resolve('cpqInventoryService') as any
24
+
25
+ const subscriptionId = url.searchParams.get('subscriptionId')
26
+ const parentItemId = url.searchParams.get('parentItemId')
27
+ if (!subscriptionId && !parentItemId) {
28
+ return NextResponse.json(
29
+ { error: 'At least one of subscriptionId or parentItemId is required' },
30
+ { status: 400 },
31
+ )
32
+ }
33
+
34
+ const filters: Record<string, unknown> = {}
35
+ if (subscriptionId) filters.subscriptionId = subscriptionId
36
+ if (parentItemId) filters.parentItemId = parentItemId
37
+ const status = url.searchParams.get('status')
38
+ if (status) filters.status = status
39
+
40
+ const result = await service.listSubscriptionItems(filters, scope)
41
+ return NextResponse.json(result)
42
+ } catch (err) {
43
+ console.error('[cpq/inventory/subscription-items.GET]', err)
44
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
45
+ }
46
+ }
47
+
48
+ export async function POST(req: Request) {
49
+ try {
50
+ const ctx = await resolveCpqRouteContext(req)
51
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
52
+
53
+ const body = cpqInventorySubscriptionItemCreateSchema.parse(await req.json())
54
+ const { subscriptionId, ...input } = body
55
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
56
+ const service = ctx.container.resolve('cpqInventoryService') as any
57
+
58
+ const result = await service.addSubscriptionItem(subscriptionId, input, scope)
59
+ return NextResponse.json(result, { status: 201 })
60
+ } catch (err) {
61
+ if (err instanceof z.ZodError) {
62
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
63
+ }
64
+ console.error('[cpq/inventory/subscription-items.POST]', err)
65
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
66
+ }
67
+ }
68
+
69
+ export async function PUT(req: Request) {
70
+ try {
71
+ const ctx = await resolveCpqRouteContext(req)
72
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
73
+
74
+ const body = cpqInventorySubscriptionItemUpdateSchema.parse(await req.json())
75
+ const { id, ...updates } = body
76
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
77
+ const service = ctx.container.resolve('cpqInventoryService') as any
78
+
79
+ const result = await service.updateSubscriptionItem(id, updates, scope)
80
+ return NextResponse.json(result)
81
+ } catch (err) {
82
+ if (err instanceof z.ZodError) {
83
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
84
+ }
85
+ console.error('[cpq/inventory/subscription-items.PUT]', err)
86
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
87
+ }
88
+ }
89
+
90
+ export async function DELETE(req: Request) {
91
+ try {
92
+ const ctx = await resolveCpqRouteContext(req)
93
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
94
+
95
+ const { id } = (await req.json()) as { id?: string }
96
+ if (!id) return NextResponse.json({ error: 'Missing id' }, { status: 400 })
97
+
98
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
99
+ const service = ctx.container.resolve('cpqInventoryService') as any
100
+
101
+ await service.deleteSubscriptionItem(id, scope)
102
+ return NextResponse.json({ success: true })
103
+ } catch (err) {
104
+ console.error('[cpq/inventory/subscription-items.DELETE]', err)
105
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
106
+ }
107
+ }
@@ -0,0 +1,64 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { resolveCpqRouteContext } from '../../../../context'
3
+ import { cpqChangeLogQuerySchema } from '../../../../../data/validators'
4
+ import type { DefaultCpqInventoryService } from '../../../../../services/cpqInventoryService'
5
+ import { handleArcError } from '../../../../_helpers/handleArcError'
6
+
7
+ /**
8
+ * GET /api/cpq/inventory/subscriptions/[subscriptionId]/change-log
9
+ *
10
+ * Paginated CpqSubscriptionChangeLog timeline for a subscription
11
+ * (most recent first).
12
+ */
13
+ export const metadata = {
14
+ GET: { requireAuth: true, requireFeatures: ['cpq.arc.changelog.view'] },
15
+ }
16
+
17
+ export async function GET(
18
+ req: Request,
19
+ { params }: { params: Promise<{ subscriptionId: string }> },
20
+ ) {
21
+ try {
22
+ const ctx = await resolveCpqRouteContext(req)
23
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
24
+ const { subscriptionId } = await params
25
+ const url = new URL(req.url)
26
+ const query = cpqChangeLogQuerySchema.parse({
27
+ page: url.searchParams.get('page') ?? undefined,
28
+ pageSize: url.searchParams.get('pageSize') ?? undefined,
29
+ })
30
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
31
+ const service = ctx.container.resolve('cpqInventoryService') as DefaultCpqInventoryService
32
+ const result = await service.listChangeLog(
33
+ subscriptionId,
34
+ { page: query.page, pageSize: query.pageSize },
35
+ scope,
36
+ )
37
+ return NextResponse.json(result)
38
+ } catch (err) {
39
+ return handleArcError(err, 'cpq/inventory/subscriptions/[subscriptionId]/change-log.GET')
40
+ }
41
+ }
42
+
43
+ export const openApi = {
44
+ '/api/cpq/inventory/subscriptions/{subscriptionId}/change-log': {
45
+ parameters: [
46
+ {
47
+ name: 'subscriptionId',
48
+ in: 'path',
49
+ required: true,
50
+ schema: { type: 'string', format: 'uuid' },
51
+ },
52
+ { name: 'page', in: 'query', schema: { type: 'integer', minimum: 1, default: 1 } },
53
+ { name: 'pageSize', in: 'query', schema: { type: 'integer', minimum: 1, maximum: 100 } },
54
+ ],
55
+ get: {
56
+ tags: ['CPQ ARC'],
57
+ summary: 'List ARC change-log entries for a subscription',
58
+ responses: {
59
+ '200': { description: 'Paginated change log' },
60
+ '401': { description: 'Unauthorized' },
61
+ },
62
+ },
63
+ },
64
+ }
@@ -0,0 +1,66 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { resolveCpqRouteContext } from '../../../context'
3
+ import { cpqExpiringSubscriptionsQuerySchema } from '../../../../data/validators'
4
+ import type { DefaultCpqInventoryService } from '../../../../services/cpqInventoryService'
5
+ import { handleArcError } from '../../../_helpers/handleArcError'
6
+
7
+ /**
8
+ * GET /api/cpq/inventory/subscriptions/expiring
9
+ *
10
+ * List active subscriptions whose currentTermEnd falls within the next
11
+ * `withinDays` days. Drives the operator's renewal triage view.
12
+ */
13
+ export const metadata = {
14
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.expiring.view'] },
15
+ }
16
+
17
+ export async function GET(req: Request) {
18
+ try {
19
+ const ctx = await resolveCpqRouteContext(req)
20
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
21
+ const url = new URL(req.url)
22
+ const query = cpqExpiringSubscriptionsQuerySchema.parse({
23
+ withinDays: url.searchParams.get('withinDays') ?? undefined,
24
+ customerId: url.searchParams.get('customerId') ?? undefined,
25
+ billingCycle: url.searchParams.get('billingCycle') ?? undefined,
26
+ status: url.searchParams.get('status') ?? undefined,
27
+ page: url.searchParams.get('page') ?? undefined,
28
+ pageSize: url.searchParams.get('pageSize') ?? undefined,
29
+ })
30
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
31
+ const service = ctx.container.resolve('cpqInventoryService') as DefaultCpqInventoryService
32
+ const result = await service.findExpiringSubscriptions(query, scope)
33
+ return NextResponse.json(result)
34
+ } catch (err) {
35
+ return handleArcError(err, 'cpq/inventory/subscriptions/expiring.GET')
36
+ }
37
+ }
38
+
39
+ export const openApi = {
40
+ '/api/cpq/inventory/subscriptions/expiring': {
41
+ parameters: [
42
+ {
43
+ name: 'withinDays',
44
+ in: 'query',
45
+ schema: { type: 'integer', minimum: 1, maximum: 365, default: 30 },
46
+ },
47
+ { name: 'customerId', in: 'query', schema: { type: 'string', format: 'uuid' } },
48
+ {
49
+ name: 'billingCycle',
50
+ in: 'query',
51
+ schema: { type: 'string', enum: ['monthly', 'quarterly', 'annually'] },
52
+ },
53
+ { name: 'status', in: 'query', schema: { type: 'string', default: 'active' } },
54
+ { name: 'page', in: 'query', schema: { type: 'integer', minimum: 1, default: 1 } },
55
+ { name: 'pageSize', in: 'query', schema: { type: 'integer', minimum: 1, maximum: 100 } },
56
+ ],
57
+ get: {
58
+ tags: ['CPQ ARC'],
59
+ summary: 'List subscriptions expiring within N days',
60
+ responses: {
61
+ '200': { description: 'Paginated subscriptions' },
62
+ '401': { description: 'Unauthorized' },
63
+ },
64
+ },
65
+ },
66
+ }
@@ -0,0 +1,126 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { resolveCpqRouteContext } from '../../context'
4
+ import {
5
+ cpqInventorySubscriptionCreateSchema,
6
+ cpqInventorySubscriptionUpdateSchema,
7
+ } from '../../../data/validators'
8
+
9
+ export const metadata = {
10
+ GET: { requireAuth: true, requireFeatures: ['cpq.inventory.view'] },
11
+ POST: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
12
+ PUT: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
13
+ DELETE: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
14
+ }
15
+
16
+ export async function GET(req: Request) {
17
+ try {
18
+ const ctx = await resolveCpqRouteContext(req)
19
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
20
+
21
+ const url = new URL(req.url)
22
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
23
+ const service = ctx.container.resolve('cpqInventoryService') as any
24
+
25
+ const id = url.searchParams.get('id')
26
+ if (id) {
27
+ const result = await service.getSubscription(id, scope)
28
+ return NextResponse.json(result)
29
+ }
30
+
31
+ const filters: Record<string, unknown> = {}
32
+ const customerId = url.searchParams.get('customerId')
33
+ if (customerId) filters.customerId = customerId
34
+ const status = url.searchParams.get('status')
35
+ if (status) filters.status = status.split(',')
36
+ const productId = url.searchParams.get('productId')
37
+ if (productId) filters.productId = productId
38
+ const sourceOrderId = url.searchParams.get('sourceOrderId')
39
+ if (sourceOrderId) filters.sourceOrderId = sourceOrderId
40
+ const billingCycle = url.searchParams.get('billingCycle')
41
+ if (billingCycle) filters.billingCycle = billingCycle
42
+ const search = url.searchParams.get('search')
43
+ if (search) filters.search = search
44
+ const expiringWithinDays = url.searchParams.get('expiringWithinDays')
45
+ if (expiringWithinDays) {
46
+ const n = Number(expiringWithinDays)
47
+ if (Number.isFinite(n) && n > 0) filters.expiringWithinDays = Math.min(365, n)
48
+ }
49
+
50
+ const ALLOWED_SORT_FIELDS = ['createdAt', 'updatedAt', 'code', 'name', 'status', 'startDate', 'currentTermEnd'] as const
51
+ const sortFieldParam = url.searchParams.get('sortField') ?? ''
52
+ if ((ALLOWED_SORT_FIELDS as readonly string[]).includes(sortFieldParam)) {
53
+ filters.sortField = sortFieldParam
54
+ }
55
+ filters.sortDir = url.searchParams.get('sortDir') === 'asc' ? 'asc' : 'desc'
56
+
57
+ filters.page = Math.max(1, Number(url.searchParams.get('page') ?? '1'))
58
+ filters.pageSize = Math.min(100, Math.max(1, Number(url.searchParams.get('pageSize') ?? '50')))
59
+
60
+ const result = await service.listSubscriptions(filters, scope)
61
+ return NextResponse.json(result)
62
+ } catch (err) {
63
+ console.error('[cpq/inventory/subscriptions.GET]', err)
64
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
65
+ }
66
+ }
67
+
68
+ export async function POST(req: Request) {
69
+ try {
70
+ const ctx = await resolveCpqRouteContext(req)
71
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
72
+
73
+ const body = cpqInventorySubscriptionCreateSchema.parse(await req.json())
74
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
75
+ const service = ctx.container.resolve('cpqInventoryService') as any
76
+
77
+ const result = await service.createSubscription(body, scope)
78
+ return NextResponse.json(result, { status: 201 })
79
+ } catch (err) {
80
+ if (err instanceof z.ZodError) {
81
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
82
+ }
83
+ console.error('[cpq/inventory/subscriptions.POST]', err)
84
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
85
+ }
86
+ }
87
+
88
+ export async function PUT(req: Request) {
89
+ try {
90
+ const ctx = await resolveCpqRouteContext(req)
91
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
92
+
93
+ const body = cpqInventorySubscriptionUpdateSchema.parse(await req.json())
94
+ const { id, ...updates } = body
95
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
96
+ const service = ctx.container.resolve('cpqInventoryService') as any
97
+
98
+ const result = await service.updateSubscription(id, updates, scope)
99
+ return NextResponse.json(result)
100
+ } catch (err) {
101
+ if (err instanceof z.ZodError) {
102
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
103
+ }
104
+ console.error('[cpq/inventory/subscriptions.PUT]', err)
105
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
106
+ }
107
+ }
108
+
109
+ export async function DELETE(req: Request) {
110
+ try {
111
+ const ctx = await resolveCpqRouteContext(req)
112
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
113
+
114
+ const { id } = (await req.json()) as { id?: string }
115
+ if (!id) return NextResponse.json({ error: 'Missing id' }, { status: 400 })
116
+
117
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
118
+ const service = ctx.container.resolve('cpqInventoryService') as any
119
+
120
+ await service.deleteSubscription(id, scope)
121
+ return NextResponse.json({ success: true })
122
+ } catch (err) {
123
+ console.error('[cpq/inventory/subscriptions.DELETE]', err)
124
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
125
+ }
126
+ }
@@ -0,0 +1,34 @@
1
+ import { NextResponse } from 'next/server'
2
+ import { z } from 'zod'
3
+ import { resolveCpqRouteContext } from '../../../context'
4
+ import { cpqInventoryStatusTransitionSchema } from '../../../../data/validators'
5
+
6
+ export const metadata = {
7
+ POST: { requireAuth: true, requireFeatures: ['cpq.inventory.manage'] },
8
+ }
9
+
10
+ export async function POST(req: Request) {
11
+ try {
12
+ const ctx = await resolveCpqRouteContext(req)
13
+ if (!ctx.auth) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
14
+
15
+ const body = cpqInventoryStatusTransitionSchema.parse(await req.json())
16
+ const scope = { organizationId: ctx.organizationId, tenantId: ctx.tenantId }
17
+ const service = ctx.container.resolve('cpqInventoryService') as any
18
+
19
+ const result = await service.transitionSubscriptionStatus(body.id, body.targetStatus, scope)
20
+ return NextResponse.json(result)
21
+ } catch (err) {
22
+ if (err instanceof z.ZodError) {
23
+ return NextResponse.json({ error: 'Validation error', details: err.issues }, { status: 400 })
24
+ }
25
+ if (err && typeof err === 'object' && 'status' in err && (err as any).status === 409) {
26
+ return NextResponse.json(
27
+ { error: (err as any).error ?? 'Conflict' },
28
+ { status: 409 },
29
+ )
30
+ }
31
+ console.error('[cpq/inventory/subscriptions/status.POST]', err)
32
+ return NextResponse.json({ error: 'Internal error' }, { status: 500 })
33
+ }
34
+ }