@dainamite/cpq 0.4.0 → 0.6.0

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 (273) hide show
  1. package/CHANGELOG.md +70 -0
  2. package/MIGRATION.md +101 -0
  3. package/dist/modules/cpq/acl.js +1 -0
  4. package/dist/modules/cpq/acl.js.map +2 -2
  5. package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js +45 -0
  6. package/dist/modules/cpq/api/_helpers/adjustmentCommandRoute.js.map +7 -0
  7. package/dist/modules/cpq/api/_helpers/handleArcError.js +2 -2
  8. package/dist/modules/cpq/api/_helpers/handleArcError.js.map +2 -2
  9. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js +23 -0
  10. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
  11. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
  12. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
  13. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js +2 -2
  14. package/dist/modules/cpq/api/orders/[orderId]/activate/route.js.map +2 -2
  15. package/dist/modules/cpq/api/orders/[orderId]/route.js +19 -3
  16. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
  17. package/dist/modules/cpq/api/orders/[orderId]/status/route.js +2 -2
  18. package/dist/modules/cpq/api/orders/[orderId]/status/route.js.map +2 -2
  19. package/dist/modules/cpq/api/orders/route.js +49 -4
  20. package/dist/modules/cpq/api/orders/route.js.map +2 -2
  21. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js +2 -2
  22. package/dist/modules/cpq/api/quotes/[quoteId]/clone/route.js.map +2 -2
  23. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
  24. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
  25. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +45 -5
  26. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
  27. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
  28. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
  29. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
  30. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
  31. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
  32. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
  33. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
  34. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
  35. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +34 -4
  36. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
  37. package/dist/modules/cpq/api/quotes/[quoteId]/route.js +2 -2
  38. package/dist/modules/cpq/api/quotes/[quoteId]/route.js.map +2 -2
  39. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
  40. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
  41. package/dist/modules/cpq/api/quotes/price/route.js +43 -3
  42. package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
  43. package/dist/modules/cpq/api/quotes/route.js +2 -2
  44. package/dist/modules/cpq/api/quotes/route.js.map +2 -2
  45. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +1 -1
  46. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +1 -1
  47. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +1 -1
  48. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +1 -1
  49. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +22 -8
  50. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
  51. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +1 -1
  52. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +89 -232
  53. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
  54. package/dist/modules/cpq/backend/cpq/orders/page.js.map +1 -1
  55. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
  56. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
  57. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +1 -1
  58. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +1 -1
  59. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +205 -0
  60. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
  61. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
  62. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
  63. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
  64. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
  65. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
  66. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
  67. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
  68. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
  69. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +144 -221
  70. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
  71. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +1 -1
  72. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
  73. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
  74. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +1 -1
  75. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +1 -1
  76. package/dist/modules/cpq/ce.js +8 -0
  77. package/dist/modules/cpq/ce.js.map +2 -2
  78. package/dist/modules/cpq/cli.js +34 -1
  79. package/dist/modules/cpq/cli.js.map +2 -2
  80. package/dist/modules/cpq/commands/price-adjustments.js +107 -0
  81. package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
  82. package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
  83. package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
  84. package/dist/modules/cpq/components/ChargeBreakdownTable.js +154 -0
  85. package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
  86. package/dist/modules/cpq/components/CpqListView.js.map +2 -2
  87. package/dist/modules/cpq/components/PriceWaterfall.js +66 -0
  88. package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
  89. package/dist/modules/cpq/components/PricingSummaryCard.js +117 -0
  90. package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
  91. package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
  92. package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
  93. package/dist/modules/cpq/data/entities.js +97 -0
  94. package/dist/modules/cpq/data/entities.js.map +2 -2
  95. package/dist/modules/cpq/data/price-adjustments.js +70 -0
  96. package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
  97. package/dist/modules/cpq/data/validators.js +4 -0
  98. package/dist/modules/cpq/data/validators.js.map +2 -2
  99. package/dist/modules/cpq/di.js +6 -0
  100. package/dist/modules/cpq/di.js.map +2 -2
  101. package/dist/modules/cpq/events.js +47 -0
  102. package/dist/modules/cpq/events.js.map +2 -2
  103. package/dist/modules/cpq/index.js +1 -1
  104. package/dist/modules/cpq/index.js.map +2 -2
  105. package/dist/modules/cpq/lib/auditActor.js +10 -0
  106. package/dist/modules/cpq/lib/auditActor.js.map +7 -0
  107. package/dist/modules/cpq/lib/formatMoney.js +7 -0
  108. package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
  109. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
  110. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
  111. package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
  112. package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
  113. package/dist/modules/cpq/lib/quotePermissions.js +13 -0
  114. package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
  115. package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
  116. package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
  117. package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
  118. package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
  119. package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
  120. package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
  121. package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
  122. package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
  123. package/dist/modules/cpq/services/cpqOrderService.js +34 -30
  124. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  125. package/dist/modules/cpq/services/cpqPricingService.js +7 -0
  126. package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
  127. package/dist/modules/cpq/services/cpqQuotingService.js +339 -111
  128. package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
  129. package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
  130. package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
  131. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
  132. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
  133. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
  134. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
  135. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
  136. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
  137. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
  138. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
  139. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
  140. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
  141. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
  142. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
  143. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
  144. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
  145. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
  146. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
  147. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
  148. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
  149. package/dist/modules/cpq/services/types.js.map +2 -2
  150. package/dist/modules/cpq/setup.js +6 -0
  151. package/dist/modules/cpq/setup.js.map +2 -2
  152. package/package.json +9 -9
  153. package/src/modules/cpq/__integration__/TC-CPQ-002.spec.ts +8 -5
  154. package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
  155. package/src/modules/cpq/acl.ts +1 -0
  156. package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
  157. package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
  158. package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
  159. package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
  160. package/src/modules/cpq/api/_helpers/handleArcError.ts +12 -12
  161. package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
  162. package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
  163. package/src/modules/cpq/api/orders/[orderId]/activate/route.ts +2 -2
  164. package/src/modules/cpq/api/orders/[orderId]/route.ts +20 -2
  165. package/src/modules/cpq/api/orders/[orderId]/status/route.ts +2 -2
  166. package/src/modules/cpq/api/orders/route.ts +53 -3
  167. package/src/modules/cpq/api/quotes/[quoteId]/clone/route.ts +2 -2
  168. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
  169. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +51 -4
  170. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
  171. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
  172. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
  173. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
  174. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
  175. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +42 -3
  176. package/src/modules/cpq/api/quotes/[quoteId]/route.ts +2 -2
  177. package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
  178. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
  179. package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
  180. package/src/modules/cpq/api/quotes/price/route.ts +28 -2
  181. package/src/modules/cpq/api/quotes/route.ts +2 -2
  182. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +1 -1
  183. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +1 -1
  184. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +1 -1
  185. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +32 -10
  186. package/src/modules/cpq/backend/cpq/offerings/page.tsx +1 -1
  187. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +60 -269
  188. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
  189. package/src/modules/cpq/backend/cpq/orders/page.tsx +1 -1
  190. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +1 -1
  191. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  192. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
  193. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
  194. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
  195. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
  196. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
  197. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
  198. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +178 -266
  199. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
  200. package/src/modules/cpq/backend/cpq/quotes/page.tsx +1 -1
  201. package/src/modules/cpq/backend/cpq/specifications/page.tsx +1 -1
  202. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  203. package/src/modules/cpq/ce.ts +8 -0
  204. package/src/modules/cpq/cli.ts +48 -1
  205. package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
  206. package/src/modules/cpq/commands/price-adjustments.ts +90 -0
  207. package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
  208. package/src/modules/cpq/components/ChargeBreakdownTable.tsx +201 -0
  209. package/src/modules/cpq/components/CpqListView.tsx +2 -1
  210. package/src/modules/cpq/components/PriceWaterfall.tsx +94 -0
  211. package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
  212. package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
  213. package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
  214. package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
  215. package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
  216. package/src/modules/cpq/data/entities.ts +83 -0
  217. package/src/modules/cpq/data/price-adjustments.ts +67 -0
  218. package/src/modules/cpq/data/validators.ts +3 -0
  219. package/src/modules/cpq/di.ts +6 -0
  220. package/src/modules/cpq/events.ts +53 -2
  221. package/src/modules/cpq/index.ts +1 -1
  222. package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
  223. package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
  224. package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
  225. package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
  226. package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
  227. package/src/modules/cpq/lib/auditActor.ts +13 -0
  228. package/src/modules/cpq/lib/formatMoney.ts +4 -0
  229. package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
  230. package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
  231. package/src/modules/cpq/lib/quotePermissions.ts +22 -0
  232. package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
  233. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
  234. package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
  235. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
  236. package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
  237. package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
  238. package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
  239. package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
  240. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
  241. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
  242. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
  243. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
  244. package/src/modules/cpq/services/__tests__/errorBrands.test.ts +72 -0
  245. package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
  246. package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
  247. package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
  248. package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
  249. package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
  250. package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
  251. package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
  252. package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
  253. package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
  254. package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
  255. package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
  256. package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
  257. package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
  258. package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
  259. package/src/modules/cpq/services/cpqOrderService.ts +32 -28
  260. package/src/modules/cpq/services/cpqPricingService.ts +8 -0
  261. package/src/modules/cpq/services/cpqQuotingService.ts +388 -125
  262. package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
  263. package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
  264. package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
  265. package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
  266. package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
  267. package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
  268. package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
  269. package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
  270. package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
  271. package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
  272. package/src/modules/cpq/services/types.ts +34 -1
  273. package/src/modules/cpq/setup.ts +7 -1
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Frozen Amend mirror lines.
3
+ *
4
+ * An Amend quote mirrors every live subscription item as a `modify` line.
5
+ * Engine-priced mirror lines used to be re-priced at today's catalog price on
6
+ * every recalculation, and activation wrote that price back to the
7
+ * subscription item — so amending one item silently moved every other item's
8
+ * price. A mirror line on an Amend quote is therefore created **frozen**: it
9
+ * keeps the source item's stored charges until a pricing input of that line
10
+ * changes, at which point it is re-priced as before.
11
+ *
12
+ * The flag lives in the line configuration jsonb next to the existing
13
+ * `_arcMirrored*` markers, so no schema change is needed.
14
+ */
15
+
16
+ export const ARC_PRICE_FROZEN_KEY = '_arcPriceFrozen'
17
+
18
+ /**
19
+ * Set when the server unfreezes an Amend mirror line because a pricing input
20
+ * changed — drives the "re-priced at the current catalog price" notice (an
21
+ * explicit marker, so Amend quotes created before freezing existed don't
22
+ * show it).
23
+ */
24
+ export const ARC_REPRICED_KEY = '_arcRepriced'
25
+
26
+ /** Markers that describe quote-time pricing state and never leave the quote. */
27
+ const QUOTE_ONLY_ARC_KEYS = [ARC_PRICE_FROZEN_KEY, ARC_REPRICED_KEY] as const
28
+
29
+ const ARC_MARKER_PREFIX = '_arc'
30
+
31
+ export type ArcMirrorQuoteType = 'amend' | 'cancel' | 'renew'
32
+
33
+ export function isArcPriceFrozen(configuration: Record<string, unknown> | null | undefined): boolean {
34
+ return configuration?.[ARC_PRICE_FROZEN_KEY] === true
35
+ }
36
+
37
+ /**
38
+ * Whether recalculation must keep a line's stored charges. Only server-made
39
+ * mirror lines on Amend quotes qualify — the flag alone is never trusted
40
+ * (client input is stripped of `_arc*` keys, and a clone of an Amend quote
41
+ * is a new quote).
42
+ */
43
+ export function isArcMirrorLine(configuration: Record<string, unknown> | null | undefined, quoteType: string | null | undefined): boolean {
44
+ return (quoteType === 'amend' || quoteType === 'renew' || quoteType === 'cancel') && typeof configuration?._arcMirroredFromItemId === 'string'
45
+ }
46
+
47
+ export function isFrozenAmendMirrorLine(
48
+ configuration: Record<string, unknown> | null | undefined,
49
+ quoteType: string | null | undefined,
50
+ ): boolean {
51
+ return (
52
+ quoteType === 'amend' &&
53
+ typeof configuration?._arcMirroredFromItemId === 'string' &&
54
+ isArcPriceFrozen(configuration)
55
+ )
56
+ }
57
+
58
+ /**
59
+ * Configuration for a mirror line: the item's configuration plus the ARC
60
+ * markers. Only Amend lines are frozen — Renew re-prices by product decision
61
+ * and Cancel lines are not priced.
62
+ */
63
+ export function buildArcMirrorConfiguration(
64
+ itemConfiguration: Record<string, unknown> | null | undefined,
65
+ item: { id: string; name: string },
66
+ quoteType: ArcMirrorQuoteType,
67
+ ): Record<string, unknown> {
68
+ return {
69
+ // An item's configuration can carry markers from an earlier ARC quote
70
+ // (activation copies line configuration onto the item); never inherit them.
71
+ ...stripArcMarkers(itemConfiguration),
72
+ _arcMirroredFromItemId: item.id,
73
+ _arcMirroredName: item.name,
74
+ ...(quoteType === 'amend' ? { [ARC_PRICE_FROZEN_KEY]: true } : {}),
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Line configuration as it may leave the quote (order lines, subscription
80
+ * items, clones): quote-only pricing markers are dropped; the descriptive
81
+ * `_arcMirrored*` markers stay.
82
+ */
83
+ export function stripQuoteOnlyArcMarkers(
84
+ configuration: Record<string, unknown> | null | undefined,
85
+ ): Record<string, unknown> | null | undefined {
86
+ if (!configuration || !QUOTE_ONLY_ARC_KEYS.some((key) => key in configuration)) return configuration
87
+ const out: Record<string, unknown> = {}
88
+ for (const [key, value] of Object.entries(configuration)) {
89
+ if (!(QUOTE_ONLY_ARC_KEYS as readonly string[]).includes(key)) out[key] = value
90
+ }
91
+ return out
92
+ }
93
+
94
+ /**
95
+ * Configuration without any `_arc*` marker. ARC markers are server-owned:
96
+ * client-supplied configuration goes through this before it is stored.
97
+ */
98
+ export function stripArcMarkers(configuration: Record<string, unknown> | null | undefined): Record<string, unknown> {
99
+ const out: Record<string, unknown> = {}
100
+ for (const [key, value] of Object.entries(configuration ?? {})) {
101
+ if (!key.startsWith(ARC_MARKER_PREFIX)) out[key] = value
102
+ }
103
+ return out
104
+ }
105
+
106
+ function canonical(value: unknown): string {
107
+ if (Array.isArray(value)) return `[${value.map(canonical).join(',')}]`
108
+ if (value && typeof value === 'object') {
109
+ const entries = Object.keys(value as Record<string, unknown>)
110
+ .sort()
111
+ .map((key) => `${JSON.stringify(key)}:${canonical((value as Record<string, unknown>)[key])}`)
112
+ return `{${entries.join(',')}}`
113
+ }
114
+ return JSON.stringify(value ?? null)
115
+ }
116
+
117
+ function isoDay(value: Date | string | null | undefined): string | null {
118
+ if (!value) return null
119
+ if (value instanceof Date) return value.toISOString().slice(0, 10)
120
+ return String(value).slice(0, 10)
121
+ }
122
+
123
+ export type ArcLinePricingState = {
124
+ configuration: Record<string, unknown> | null | undefined
125
+ quantity: number
126
+ termMonths?: number | null
127
+ startDate?: Date | string | null
128
+ endDate?: Date | string | null
129
+ usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }> | null
130
+ }
131
+
132
+ export type ArcLinePricingUpdate = {
133
+ configuration: Record<string, unknown>
134
+ quantity?: number
135
+ termMonths?: number
136
+ startDate?: string
137
+ endDate?: string
138
+ usageEstimates?: Array<{ chargeCode: string; estimatedQuantity: number }>
139
+ }
140
+
141
+ /**
142
+ * Whether an update changes any input the pricing engine reads. The quote UI
143
+ * always resubmits the full configuration, so "a field was sent" is not a
144
+ * change — values are compared (ARC markers ignored, key order ignored).
145
+ * Fields the update omits keep their stored value.
146
+ */
147
+ export function arcPricingInputsChanged(before: ArcLinePricingState, update: ArcLinePricingUpdate): boolean {
148
+ if (canonical(stripArcMarkers(before.configuration)) !== canonical(stripArcMarkers(update.configuration))) {
149
+ return true
150
+ }
151
+ if (update.quantity !== undefined && update.quantity !== before.quantity) return true
152
+ if (update.termMonths !== undefined && update.termMonths !== (before.termMonths ?? undefined)) return true
153
+ if (update.startDate !== undefined && isoDay(update.startDate) !== isoDay(before.startDate)) return true
154
+ if (update.endDate !== undefined && isoDay(update.endDate) !== isoDay(before.endDate)) return true
155
+ if (update.usageEstimates !== undefined) {
156
+ const normalize = (list: Array<{ chargeCode: string; estimatedQuantity: number }> | null | undefined) =>
157
+ canonical(
158
+ [...(list ?? [])]
159
+ .map((e) => ({ chargeCode: e.chargeCode, estimatedQuantity: e.estimatedQuantity }))
160
+ .sort((a, b) => a.chargeCode.localeCompare(b.chargeCode)),
161
+ )
162
+ if (normalize(update.usageEstimates) !== normalize(before.usageEstimates)) return true
163
+ }
164
+ return false
165
+ }
@@ -1,3 +1,5 @@
1
+ import { storedAdjustmentFailure } from './priceAdjustments/storedAdjustmentFailure'
2
+ import { withQuotePricingWrite } from './priceAdjustments/quotePricingWrite'
1
3
  import type { EntityManager } from '@mikro-orm/postgresql'
2
4
  import { SalesOrder, SalesOrderLine } from '@open-mercato/core/modules/sales/data/entities'
3
5
  import type { SalesDocumentNumberGenerator } from '@open-mercato/core/modules/sales/services/salesDocumentNumberGenerator'
@@ -25,6 +27,8 @@ import {
25
27
  type TenantScope,
26
28
  } from './types'
27
29
  import { emitCpqEvent } from '../events'
30
+ import { stripQuoteOnlyArcMarkers } from './arcPriceFreeze'
31
+ import { summarizeOrderPricing, type OrderWaterfallSummary } from './orderPricingSummary'
28
32
 
29
33
  // ─── Types ──────────────────────────────────────────────────────
30
34
 
@@ -89,11 +93,12 @@ interface OrderResult {
89
93
  * order detail page to render the AMEND / RENEW / CANCEL badge. */
90
94
  quoteType: string
91
95
  currencyCode: string
96
+ /** NRC / MRC totals plus the source quote's list / discount / deal-value figures. */
92
97
  pricingSummary: {
93
98
  nrcTotal: number
94
99
  mrcTotal: number
95
100
  currencyCode: string
96
- }
101
+ } & OrderWaterfallSummary
97
102
  activatedAt: string | null
98
103
  createdAt: string
99
104
  lines: OrderLineResult[]
@@ -129,29 +134,12 @@ export class DefaultCpqOrderService {
129
134
  // ─── Convert Quote to Order ─────────────────────────────────
130
135
 
131
136
  async convertQuoteToOrder(quoteId: string, scope: TenantScope): Promise<OrderResult> {
132
- const em = this.em
133
-
134
- const cpqConfig = await em.findOne(CpqQuoteConfiguration, {
135
- id: quoteId,
136
- organizationId: scope.organizationId,
137
- tenantId: scope.tenantId,
138
- deletedAt: null,
139
- })
140
- if (!cpqConfig) {
141
- const byQuoteId = await em.findOne(CpqQuoteConfiguration, {
142
- quoteId,
143
- organizationId: scope.organizationId,
144
- tenantId: scope.tenantId,
145
- deletedAt: null,
146
- })
147
- if (!byQuoteId) throw new OrderError(404, 'Quote not found')
148
- return this.convertFromConfig(byQuoteId, scope)
149
- }
150
- return this.convertFromConfig(cpqConfig, scope)
137
+ return withQuotePricingWrite(this.em, quoteId, scope,
138
+ (em, quote) => this.convertFromConfig(quote, scope, em),
139
+ (status, message) => new OrderError(status, message))
151
140
  }
152
141
 
153
- private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope): Promise<OrderResult> {
154
- const em = this.em
142
+ private async convertFromConfig(cpqConfig: CpqQuoteConfiguration, scope: TenantScope, em: EntityManager): Promise<OrderResult> {
155
143
 
156
144
  const blockedStatuses = ['cancelled', 'rejected']
157
145
  if (blockedStatuses.includes(cpqConfig.cpqStatus)) {
@@ -186,6 +174,9 @@ export class DefaultCpqOrderService {
186
174
  throw new OrderError(422, 'Cannot convert a quote with no line items')
187
175
  }
188
176
 
177
+ const invalidAdjustment = storedAdjustmentFailure(cpqConfig, quoteLines)
178
+ if (invalidAdjustment) throw new OrderError(422, 'ADJUSTMENT_INVALID', invalidAdjustment)
179
+
189
180
  const { number: orderNumber } = await this.numberGenerator.generate({
190
181
  kind: 'order',
191
182
  organizationId: scope.organizationId,
@@ -309,7 +300,8 @@ export class DefaultCpqOrderService {
309
300
  startDate: ql.startDate,
310
301
  termMonths: ql.termMonths ?? null,
311
302
  endDate: ql.endDate,
312
- configuration: { ...ql.configuration, offeringName },
303
+ // Quote-only ARC pricing markers stay on the quote.
304
+ configuration: { ...stripQuoteOnlyArcMarkers(ql.configuration), offeringName },
313
305
  quantity: ql.quantity,
314
306
  charges: ql.charges ? [...ql.charges] : null,
315
307
  nrcTotal: ql.nrcTotal,
@@ -641,7 +633,7 @@ export class DefaultCpqOrderService {
641
633
  offeringId: line.offeringId ?? undefined,
642
634
  specId: line.specId ?? undefined,
643
635
  name: this.getLineName(line),
644
- configuration: line.configuration ?? {},
636
+ configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
645
637
  charges: (line.charges ?? []) as Array<Record<string, unknown>>,
646
638
  mrcAmount: Number(line.mrcTotal),
647
639
  nrcAmount: Number(line.nrcTotal),
@@ -711,7 +703,7 @@ export class DefaultCpqOrderService {
711
703
  purchasePrice: Number(line.nrcTotal),
712
704
  quantity: line.quantity,
713
705
  purchaseDate: new Date().toISOString().split('T')[0],
714
- configuration: line.configuration ?? {},
706
+ configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
715
707
  charges: (line.charges ?? []) as Array<Record<string, unknown>>,
716
708
  sourceOrderId: orderConfig.id,
717
709
  sourceQuoteLineId: line.sourceQuoteLineId ?? undefined,
@@ -976,7 +968,7 @@ export class DefaultCpqOrderService {
976
968
  action: lc.action,
977
969
  parentLineId: lc.parentLineId ?? null,
978
970
  quantity: lc.quantity,
979
- configuration: lc.configuration ?? {},
971
+ configuration: stripQuoteOnlyArcMarkers(lc.configuration) ?? {},
980
972
  startDate: lc.startDate?.toISOString() ?? null,
981
973
  termMonths: lc.termMonths ?? null,
982
974
  endDate: lc.endDate?.toISOString() ?? null,
@@ -1018,6 +1010,7 @@ export class DefaultCpqOrderService {
1018
1010
  nrcTotal: Number((pricing as Record<string, unknown>).nrcTotal ?? 0),
1019
1011
  mrcTotal: Number((pricing as Record<string, unknown>).mrcTotal ?? 0),
1020
1012
  currencyCode: orderConfig.currencyCode,
1013
+ ...summarizeOrderPricing(lines, quoteType, orderConfig.currencyCode),
1021
1014
  },
1022
1015
  activatedAt: orderConfig.activatedAt?.toISOString() ?? null,
1023
1016
  createdAt: orderConfig.createdAt.toISOString(),
@@ -1045,16 +1038,27 @@ function pickArcSourceFromConfig(
1045
1038
 
1046
1039
  // ─── Error class ──────────────────────────────────────────────────
1047
1040
 
1041
+ // Cross-realm brand — see QUOTING_ERROR_BRAND in cpqQuotingService.ts for why
1042
+ // route handlers must not rely on `instanceof`. Use `isOrderError`.
1043
+ const ORDER_ERROR_BRAND = Symbol.for('@dainamite/cpq/OrderError')
1044
+
1048
1045
  export class OrderError extends Error {
1046
+ readonly [ORDER_ERROR_BRAND] = true
1047
+
1049
1048
  constructor(
1050
1049
  public status: number,
1051
1050
  message: string,
1051
+ public details?: Record<string, unknown>,
1052
1052
  ) {
1053
1053
  super(message)
1054
1054
  this.name = 'OrderError'
1055
1055
  }
1056
1056
  }
1057
1057
 
1058
+ export function isOrderError(err: unknown): err is OrderError {
1059
+ return !!err && typeof err === 'object' && (err as Record<symbol, unknown>)[ORDER_ERROR_BRAND] === true
1060
+ }
1061
+
1058
1062
  // ─── ARC helpers (XD-250) ─────────────────────────────────────────
1059
1063
 
1060
1064
  /**
@@ -1072,7 +1076,7 @@ function buildArcLineChange(
1072
1076
  offeringId: line.offeringId ?? undefined,
1073
1077
  specId: line.specId ?? undefined,
1074
1078
  name: resolveLineName(line, specMap),
1075
- configuration: line.configuration ?? {},
1079
+ configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? {},
1076
1080
  charges: (line.charges ?? null) as Array<Record<string, unknown>> | undefined,
1077
1081
  mrcAmount: Number(line.mrcTotal),
1078
1082
  nrcAmount: Number(line.nrcTotal),
@@ -1099,7 +1103,7 @@ function buildArcLineChange(
1099
1103
  action,
1100
1104
  sourceSubscriptionItemId: line.sourceSubscriptionItemId ?? null,
1101
1105
  modifyPatch: {
1102
- configuration: line.configuration ?? undefined,
1106
+ configuration: stripQuoteOnlyArcMarkers(line.configuration) ?? undefined,
1103
1107
  charges: (line.charges ?? undefined) as Array<Record<string, unknown>> | undefined,
1104
1108
  mrcAmount: Number(line.mrcTotal),
1105
1109
  nrcAmount: Number(line.nrcTotal),
@@ -119,6 +119,8 @@ export class DefaultCpqPricingService {
119
119
  if (currencyCode) {
120
120
  resolved.currencyCode = currencyCode
121
121
  }
122
+ resolved.listUnitPrice = resolved.unitPrice
123
+ resolved.listTotalPrice = resolved.totalPrice
122
124
  resolved.adjustments = []
123
125
  results.push(resolved)
124
126
  }
@@ -133,6 +135,11 @@ export class DefaultCpqPricingService {
133
135
  this.applyPriceRules(results, rules, configuration)
134
136
  }
135
137
 
138
+ for (const resolved of results) {
139
+ resolved.standardUnitPrice = resolved.unitPrice
140
+ resolved.standardTotalPrice = resolved.totalPrice
141
+ }
142
+
136
143
  return results
137
144
  }
138
145
 
@@ -219,6 +226,7 @@ export class DefaultCpqPricingService {
219
226
  }
220
227
 
221
228
  const adjustment: RuleAdjustment = {
229
+ origin: 'rule',
222
230
  ruleCode: rule.code,
223
231
  ruleName: rule.name,
224
232
  ruleType: rule.ruleType,