@dainamite/cpq 0.5.0 → 0.7.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 (368) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/MIGRATION.md +74 -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/quoteMutationGuard.js +23 -0
  8. package/dist/modules/cpq/api/_helpers/quoteMutationGuard.js.map +7 -0
  9. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js +19 -0
  10. package/dist/modules/cpq/api/_helpers/quoteRouteOpenApi.js.map +7 -0
  11. package/dist/modules/cpq/api/orders/[orderId]/route.js +17 -1
  12. package/dist/modules/cpq/api/orders/[orderId]/route.js.map +2 -2
  13. package/dist/modules/cpq/api/orders/route.js +47 -2
  14. package/dist/modules/cpq/api/orders/route.js.map +2 -2
  15. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js +54 -0
  16. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.js.map +7 -0
  17. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js +42 -2
  18. package/dist/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.js.map +2 -2
  19. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js +27 -2
  20. package/dist/modules/cpq/api/quotes/[quoteId]/items/route.js.map +2 -2
  21. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js +47 -0
  22. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.js.map +7 -0
  23. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js +30 -0
  24. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.js.map +7 -0
  25. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js +47 -0
  26. package/dist/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.js.map +7 -0
  27. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js +32 -2
  28. package/dist/modules/cpq/api/quotes/[quoteId]/recalculate/route.js.map +2 -2
  29. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js +33 -2
  30. package/dist/modules/cpq/api/quotes/[quoteId]/status/route.js.map +2 -2
  31. package/dist/modules/cpq/api/quotes/price/route.js +43 -3
  32. package/dist/modules/cpq/api/quotes/price/route.js.map +2 -2
  33. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js +3 -9
  34. package/dist/modules/cpq/backend/cpq/inventory/AssetsTable.js.map +2 -2
  35. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js +21 -25
  36. package/dist/modules/cpq/backend/cpq/inventory/SubscriptionsTable.js.map +2 -2
  37. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js +48 -47
  38. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.js.map +2 -2
  39. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js +1 -1
  40. package/dist/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.js.map +2 -2
  41. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js +3 -3
  42. package/dist/modules/cpq/backend/cpq/inventory/page.meta.js.map +2 -2
  43. package/dist/modules/cpq/backend/cpq/inventory/shared.js +37 -2
  44. package/dist/modules/cpq/backend/cpq/inventory/shared.js.map +2 -2
  45. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js +174 -184
  46. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.js.map +2 -2
  47. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js +1 -1
  48. package/dist/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.js.map +2 -2
  49. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js +168 -143
  50. package/dist/modules/cpq/backend/cpq/offerings/[id]/page.js.map +2 -2
  51. package/dist/modules/cpq/backend/cpq/offerings/page.js +36 -34
  52. package/dist/modules/cpq/backend/cpq/offerings/page.js.map +2 -2
  53. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js +140 -280
  54. package/dist/modules/cpq/backend/cpq/orders/[id]/page.js.map +2 -2
  55. package/dist/modules/cpq/backend/cpq/orders/page.js +6 -6
  56. package/dist/modules/cpq/backend/cpq/orders/page.js.map +2 -2
  57. package/dist/modules/cpq/backend/cpq/orders/page.meta.js +1 -1
  58. package/dist/modules/cpq/backend/cpq/orders/page.meta.js.map +1 -1
  59. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js +41 -36
  60. package/dist/modules/cpq/backend/cpq/price-rules/PriceRuleForm.js.map +2 -2
  61. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js +76 -72
  62. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.js.map +2 -2
  63. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js +1 -1
  64. package/dist/modules/cpq/backend/cpq/price-rules/[id]/page.meta.js.map +2 -2
  65. package/dist/modules/cpq/backend/cpq/price-rules/page.js +18 -15
  66. package/dist/modules/cpq/backend/cpq/price-rules/page.js.map +2 -2
  67. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js +23 -21
  68. package/dist/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.js.map +2 -2
  69. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js +22 -50
  70. package/dist/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.js.map +2 -2
  71. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js +74 -52
  72. package/dist/modules/cpq/backend/cpq/pricing/[id]/page.js.map +2 -2
  73. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js +20 -17
  74. package/dist/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.js.map +2 -2
  75. package/dist/modules/cpq/backend/cpq/pricing/page.js +1 -1
  76. package/dist/modules/cpq/backend/cpq/pricing/page.js.map +2 -2
  77. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js +115 -123
  78. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.js.map +2 -2
  79. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js +208 -0
  80. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.js.map +7 -0
  81. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js +79 -0
  82. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.js.map +7 -0
  83. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js +53 -0
  84. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.js.map +7 -0
  85. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js +107 -0
  86. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.js.map +7 -0
  87. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js +130 -0
  88. package/dist/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.js.map +7 -0
  89. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js +269 -311
  90. package/dist/modules/cpq/backend/cpq/quotes/[id]/page.js.map +3 -3
  91. package/dist/modules/cpq/backend/cpq/quotes/page.js +23 -19
  92. package/dist/modules/cpq/backend/cpq/quotes/page.js.map +2 -2
  93. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js +1 -1
  94. package/dist/modules/cpq/backend/cpq/quotes/page.meta.js.map +1 -1
  95. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js +19 -15
  96. package/dist/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.js.map +2 -2
  97. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js +171 -178
  98. package/dist/modules/cpq/backend/cpq/specifications/[id]/page.js.map +2 -2
  99. package/dist/modules/cpq/backend/cpq/specifications/page.js +2 -2
  100. package/dist/modules/cpq/backend/cpq/specifications/page.js.map +2 -2
  101. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js +60 -50
  102. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.js.map +2 -2
  103. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js +22 -18
  104. package/dist/modules/cpq/backend/cpq/wizards/[definitionCode]/page.js.map +2 -2
  105. package/dist/modules/cpq/backend/cpq/wizards/page.js +1 -1
  106. package/dist/modules/cpq/backend/cpq/wizards/page.js.map +2 -2
  107. package/dist/modules/cpq/ce.js +8 -0
  108. package/dist/modules/cpq/ce.js.map +2 -2
  109. package/dist/modules/cpq/cli.js +34 -1
  110. package/dist/modules/cpq/cli.js.map +2 -2
  111. package/dist/modules/cpq/commands/price-adjustments.js +107 -0
  112. package/dist/modules/cpq/commands/price-adjustments.js.map +7 -0
  113. package/dist/modules/cpq/components/ArcLineDiff.js +53 -0
  114. package/dist/modules/cpq/components/ArcLineDiff.js.map +7 -0
  115. package/dist/modules/cpq/components/ChargeBreakdownTable.js +148 -0
  116. package/dist/modules/cpq/components/ChargeBreakdownTable.js.map +7 -0
  117. package/dist/modules/cpq/components/PriceWaterfall.js +68 -0
  118. package/dist/modules/cpq/components/PriceWaterfall.js.map +7 -0
  119. package/dist/modules/cpq/components/PricingSummaryCard.js +112 -0
  120. package/dist/modules/cpq/components/PricingSummaryCard.js.map +7 -0
  121. package/dist/modules/cpq/components/StatusPath.js +5 -3
  122. package/dist/modules/cpq/components/StatusPath.js.map +2 -2
  123. package/dist/modules/cpq/components/useCpqRowActions.js +10 -22
  124. package/dist/modules/cpq/components/useCpqRowActions.js.map +2 -2
  125. package/dist/modules/cpq/components/useGrantedFeatures.js +27 -0
  126. package/dist/modules/cpq/components/useGrantedFeatures.js.map +7 -0
  127. package/dist/modules/cpq/data/entities.js +97 -0
  128. package/dist/modules/cpq/data/entities.js.map +2 -2
  129. package/dist/modules/cpq/data/price-adjustments.js +70 -0
  130. package/dist/modules/cpq/data/price-adjustments.js.map +7 -0
  131. package/dist/modules/cpq/data/validators.js +4 -0
  132. package/dist/modules/cpq/data/validators.js.map +2 -2
  133. package/dist/modules/cpq/di.js +6 -0
  134. package/dist/modules/cpq/di.js.map +2 -2
  135. package/dist/modules/cpq/events.js +47 -0
  136. package/dist/modules/cpq/events.js.map +2 -2
  137. package/dist/modules/cpq/i18n/de.json +1220 -0
  138. package/dist/modules/cpq/i18n/en.json +1220 -0
  139. package/dist/modules/cpq/i18n/es.json +1220 -0
  140. package/dist/modules/cpq/i18n/pl.json +1220 -0
  141. package/dist/modules/cpq/index.js +1 -1
  142. package/dist/modules/cpq/index.js.map +2 -2
  143. package/dist/modules/cpq/lib/auditActor.js +10 -0
  144. package/dist/modules/cpq/lib/auditActor.js.map +7 -0
  145. package/dist/modules/cpq/lib/formatMoney.js +7 -0
  146. package/dist/modules/cpq/lib/formatMoney.js.map +7 -0
  147. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js +60 -0
  148. package/dist/modules/cpq/lib/priceAdjustmentDefaults.js.map +7 -0
  149. package/dist/modules/cpq/lib/priceAdjustmentView.js +169 -0
  150. package/dist/modules/cpq/lib/priceAdjustmentView.js.map +7 -0
  151. package/dist/modules/cpq/lib/quotePermissions.js +13 -0
  152. package/dist/modules/cpq/lib/quotePermissions.js.map +7 -0
  153. package/dist/modules/cpq/lib/quoteSummaryView.js +28 -0
  154. package/dist/modules/cpq/lib/quoteSummaryView.js.map +7 -0
  155. package/dist/modules/cpq/migrations/Migration20261110213052.js +22 -0
  156. package/dist/modules/cpq/migrations/Migration20261110213052.js.map +7 -0
  157. package/dist/modules/cpq/services/arcLineRemoval.js +53 -0
  158. package/dist/modules/cpq/services/arcLineRemoval.js.map +7 -0
  159. package/dist/modules/cpq/services/arcPriceFreeze.js +79 -0
  160. package/dist/modules/cpq/services/arcPriceFreeze.js.map +7 -0
  161. package/dist/modules/cpq/services/cpqOrderService.js +25 -28
  162. package/dist/modules/cpq/services/cpqOrderService.js.map +2 -2
  163. package/dist/modules/cpq/services/cpqPricingService.js +7 -0
  164. package/dist/modules/cpq/services/cpqPricingService.js.map +2 -2
  165. package/dist/modules/cpq/services/cpqQuotingService.js +330 -109
  166. package/dist/modules/cpq/services/cpqQuotingService.js.map +2 -2
  167. package/dist/modules/cpq/services/orderPricingSummary.js +26 -0
  168. package/dist/modules/cpq/services/orderPricingSummary.js.map +7 -0
  169. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js +264 -0
  170. package/dist/modules/cpq/services/priceAdjustments/adjustmentService.js.map +7 -0
  171. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js +25 -0
  172. package/dist/modules/cpq/services/priceAdjustments/chargeWaterfall.js.map +7 -0
  173. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js +38 -0
  174. package/dist/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.js.map +7 -0
  175. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js +104 -0
  176. package/dist/modules/cpq/services/priceAdjustments/pricingSnapshot.js.map +7 -0
  177. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js +102 -0
  178. package/dist/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.js.map +7 -0
  179. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js +52 -0
  180. package/dist/modules/cpq/services/priceAdjustments/quotePricingEvents.js.map +7 -0
  181. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js +37 -0
  182. package/dist/modules/cpq/services/priceAdjustments/quotePricingWrite.js.map +7 -0
  183. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js +107 -0
  184. package/dist/modules/cpq/services/priceAdjustments/resolveManualAdjustments.js.map +7 -0
  185. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js +30 -0
  186. package/dist/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.js.map +7 -0
  187. package/dist/modules/cpq/services/types.js.map +2 -2
  188. package/dist/modules/cpq/setup.js +6 -0
  189. package/dist/modules/cpq/setup.js.map +2 -2
  190. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js +39 -31
  191. package/dist/modules/cpq/widgets/injection/customer-inventory/widget.client.js.map +2 -2
  192. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js +13 -5
  193. package/dist/modules/cpq/widgets/injection/order-cpq-tab/widget.client.js.map +2 -2
  194. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js +41 -21
  195. package/dist/modules/cpq/widgets/injection/quote-configurator/widget.client.js.map +2 -2
  196. package/dist/modules/cpq/widgets/injection-table.js +2 -2
  197. package/dist/modules/cpq/widgets/injection-table.js.map +2 -2
  198. package/dist/modules/cpq/workflows/WizardRunner.js +10 -8
  199. package/dist/modules/cpq/workflows/WizardRunner.js.map +2 -2
  200. package/dist/modules/cpq/workflows/WizardTrigger.js +15 -9
  201. package/dist/modules/cpq/workflows/WizardTrigger.js.map +2 -2
  202. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js +9 -7
  203. package/dist/modules/cpq/workflows/steps/CustomerSelectStep.js.map +2 -2
  204. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js +9 -4
  205. package/dist/modules/cpq/workflows/steps/InventorySelectStep.js.map +2 -2
  206. package/dist/modules/cpq/workflows/steps/ItemListStep.js +16 -19
  207. package/dist/modules/cpq/workflows/steps/ItemListStep.js.map +2 -2
  208. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js +19 -32
  209. package/dist/modules/cpq/workflows/steps/OfferingSelectStep.js.map +2 -2
  210. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js +8 -6
  211. package/dist/modules/cpq/workflows/steps/ProductConfigureStep.js.map +2 -2
  212. package/dist/modules/cpq/workflows/steps/ReviewStep.js +32 -30
  213. package/dist/modules/cpq/workflows/steps/ReviewStep.js.map +2 -2
  214. package/dist/modules/cpq/workflows/steps/index.js +12 -0
  215. package/dist/modules/cpq/workflows/steps/index.js.map +2 -2
  216. package/package.json +1 -1
  217. package/src/modules/cpq/__integration__/TC-CPQ-ARC-012.spec.ts +134 -0
  218. package/src/modules/cpq/__tests__/i18n/dictionaries.test.ts +62 -0
  219. package/src/modules/cpq/__tests__/i18n/hardcodedStrings.test.ts +35 -0
  220. package/src/modules/cpq/__tests__/i18n/sourceScan.ts +109 -0
  221. package/src/modules/cpq/acl.ts +1 -0
  222. package/src/modules/cpq/api/__tests__/adjustmentRoutes.test.ts +79 -0
  223. package/src/modules/cpq/api/__tests__/quoteWriteGuards.test.ts +100 -0
  224. package/src/modules/cpq/api/_helpers/__tests__/quoteMutationGuard.test.ts +48 -0
  225. package/src/modules/cpq/api/_helpers/adjustmentCommandRoute.ts +45 -0
  226. package/src/modules/cpq/api/_helpers/quoteMutationGuard.ts +38 -0
  227. package/src/modules/cpq/api/_helpers/quoteRouteOpenApi.ts +19 -0
  228. package/src/modules/cpq/api/orders/[orderId]/route.ts +18 -0
  229. package/src/modules/cpq/api/orders/route.ts +51 -1
  230. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/restore/route.ts +56 -0
  231. package/src/modules/cpq/api/quotes/[quoteId]/items/[lineId]/route.ts +48 -1
  232. package/src/modules/cpq/api/quotes/[quoteId]/items/route.ts +31 -1
  233. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/[adjustmentId]/route.ts +39 -0
  234. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/preview/route.ts +24 -0
  235. package/src/modules/cpq/api/quotes/[quoteId]/price-adjustments/route.ts +40 -0
  236. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/__tests__/route.test.ts +95 -0
  237. package/src/modules/cpq/api/quotes/[quoteId]/recalculate/route.ts +40 -1
  238. package/src/modules/cpq/api/quotes/[quoteId]/status/__tests__/route.test.ts +74 -0
  239. package/src/modules/cpq/api/quotes/[quoteId]/status/route.ts +37 -1
  240. package/src/modules/cpq/api/quotes/price/__tests__/route.test.ts +37 -0
  241. package/src/modules/cpq/api/quotes/price/route.ts +28 -2
  242. package/src/modules/cpq/backend/cpq/inventory/AssetsTable.tsx +3 -9
  243. package/src/modules/cpq/backend/cpq/inventory/SubscriptionsTable.tsx +22 -26
  244. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.meta.ts +1 -1
  245. package/src/modules/cpq/backend/cpq/inventory/assets/[id]/page.tsx +43 -46
  246. package/src/modules/cpq/backend/cpq/inventory/page.meta.ts +3 -3
  247. package/src/modules/cpq/backend/cpq/inventory/shared.tsx +37 -1
  248. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.meta.ts +1 -1
  249. package/src/modules/cpq/backend/cpq/inventory/subscriptions/[id]/page.tsx +164 -120
  250. package/src/modules/cpq/backend/cpq/offerings/[id]/page.tsx +169 -130
  251. package/src/modules/cpq/backend/cpq/offerings/page.tsx +34 -24
  252. package/src/modules/cpq/backend/cpq/orders/[id]/page.tsx +122 -308
  253. package/src/modules/cpq/backend/cpq/orders/page.meta.ts +1 -1
  254. package/src/modules/cpq/backend/cpq/orders/page.tsx +6 -6
  255. package/src/modules/cpq/backend/cpq/price-rules/PriceRuleForm.tsx +48 -34
  256. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.meta.ts +1 -1
  257. package/src/modules/cpq/backend/cpq/price-rules/[id]/page.tsx +76 -70
  258. package/src/modules/cpq/backend/cpq/price-rules/page.tsx +25 -16
  259. package/src/modules/cpq/backend/cpq/pricing/[id]/EntriesEditor.tsx +24 -18
  260. package/src/modules/cpq/backend/cpq/pricing/[id]/ImportPreview.tsx +29 -19
  261. package/src/modules/cpq/backend/cpq/pricing/[id]/page.tsx +95 -47
  262. package/src/modules/cpq/backend/cpq/pricing/[id]/xlsxImport.ts +23 -15
  263. package/src/modules/cpq/backend/cpq/pricing/page.tsx +1 -1
  264. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/ArcQuoteConfigurator.tsx +111 -88
  265. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentDialog.tsx +260 -0
  266. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/PriceAdjustmentsPanel.tsx +124 -0
  267. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/__tests__/PriceAdjustmentsPanel.test.tsx +76 -0
  268. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/arcLineRemoval.tsx +76 -0
  269. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/priceAdjustmentContext.tsx +133 -0
  270. package/src/modules/cpq/backend/cpq/quotes/[id]/_components/usePriceAdjustments.tsx +165 -0
  271. package/src/modules/cpq/backend/cpq/quotes/[id]/page.tsx +304 -348
  272. package/src/modules/cpq/backend/cpq/quotes/page.meta.ts +1 -1
  273. package/src/modules/cpq/backend/cpq/quotes/page.tsx +29 -21
  274. package/src/modules/cpq/backend/cpq/specifications/[id]/DependsOnRulesEditor.tsx +19 -15
  275. package/src/modules/cpq/backend/cpq/specifications/[id]/page.tsx +171 -171
  276. package/src/modules/cpq/backend/cpq/specifications/page.tsx +2 -2
  277. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/detail/page.tsx +51 -41
  278. package/src/modules/cpq/backend/cpq/wizards/[definitionCode]/page.tsx +17 -13
  279. package/src/modules/cpq/backend/cpq/wizards/page.tsx +1 -1
  280. package/src/modules/cpq/ce.ts +8 -0
  281. package/src/modules/cpq/cli.ts +48 -1
  282. package/src/modules/cpq/commands/__tests__/price-adjustments.test.ts +54 -0
  283. package/src/modules/cpq/commands/price-adjustments.ts +90 -0
  284. package/src/modules/cpq/components/ArcLineDiff.tsx +87 -0
  285. package/src/modules/cpq/components/ChargeBreakdownTable.tsx +203 -0
  286. package/src/modules/cpq/components/PriceWaterfall.tsx +96 -0
  287. package/src/modules/cpq/components/PricingSummaryCard.tsx +128 -0
  288. package/src/modules/cpq/components/StatusPath.tsx +7 -4
  289. package/src/modules/cpq/components/__tests__/PriceWaterfall.test.tsx +38 -0
  290. package/src/modules/cpq/components/useCpqRowActions.tsx +17 -25
  291. package/src/modules/cpq/components/useGrantedFeatures.ts +35 -0
  292. package/src/modules/cpq/data/__tests__/price-adjustment-migration.test.ts +11 -0
  293. package/src/modules/cpq/data/__tests__/price-adjustments.test.ts +26 -0
  294. package/src/modules/cpq/data/entities.ts +83 -0
  295. package/src/modules/cpq/data/price-adjustments.ts +67 -0
  296. package/src/modules/cpq/data/validators.ts +3 -0
  297. package/src/modules/cpq/di.ts +6 -0
  298. package/src/modules/cpq/events.ts +53 -2
  299. package/src/modules/cpq/i18n/de.json +1220 -0
  300. package/src/modules/cpq/i18n/en.json +1220 -0
  301. package/src/modules/cpq/i18n/es.json +1220 -0
  302. package/src/modules/cpq/i18n/pl.json +1220 -0
  303. package/src/modules/cpq/index.ts +1 -1
  304. package/src/modules/cpq/lib/__tests__/auditActor.test.ts +12 -0
  305. package/src/modules/cpq/lib/__tests__/priceAdjustmentDefaults.test.ts +37 -0
  306. package/src/modules/cpq/lib/__tests__/priceAdjustmentView.test.ts +152 -0
  307. package/src/modules/cpq/lib/__tests__/quotePermissions.test.ts +30 -0
  308. package/src/modules/cpq/lib/__tests__/quoteSummaryView.test.ts +56 -0
  309. package/src/modules/cpq/lib/auditActor.ts +13 -0
  310. package/src/modules/cpq/lib/formatMoney.ts +4 -0
  311. package/src/modules/cpq/lib/priceAdjustmentDefaults.ts +36 -0
  312. package/src/modules/cpq/lib/priceAdjustmentView.ts +259 -0
  313. package/src/modules/cpq/lib/quotePermissions.ts +22 -0
  314. package/src/modules/cpq/lib/quoteSummaryView.ts +51 -0
  315. package/src/modules/cpq/migrations/.snapshot-open-mercato.json +3298 -2423
  316. package/src/modules/cpq/migrations/Migration20261110213052.ts +22 -0
  317. package/src/modules/cpq/services/__tests__/_helpers/mockEm.ts +9 -2
  318. package/src/modules/cpq/services/__tests__/adjustmentService.test.ts +183 -0
  319. package/src/modules/cpq/services/__tests__/arcLineRemoval.test.ts +109 -0
  320. package/src/modules/cpq/services/__tests__/arcPriceFreeze.test.ts +141 -0
  321. package/src/modules/cpq/services/__tests__/chargeWaterfall.test.ts +28 -0
  322. package/src/modules/cpq/services/__tests__/cpqOrderService.test.ts +35 -2
  323. package/src/modules/cpq/services/__tests__/cpqPricingService.test.ts +57 -0
  324. package/src/modules/cpq/services/__tests__/cpqQuotingService.arc.test.ts +237 -0
  325. package/src/modules/cpq/services/__tests__/cpqQuotingService.test.ts +58 -3
  326. package/src/modules/cpq/services/__tests__/orderPricingSummary.test.ts +50 -0
  327. package/src/modules/cpq/services/__tests__/pricingSnapshot.test.ts +62 -0
  328. package/src/modules/cpq/services/__tests__/quoteAdjustmentLifecycle.test.ts +35 -0
  329. package/src/modules/cpq/services/__tests__/quoteAdjustmentPricing.test.ts +65 -0
  330. package/src/modules/cpq/services/__tests__/quoteCloneAdjustments.test.ts +45 -0
  331. package/src/modules/cpq/services/__tests__/quoteDetachTargetAdjustments.test.ts +54 -0
  332. package/src/modules/cpq/services/__tests__/quoteEventsService.test.ts +72 -0
  333. package/src/modules/cpq/services/__tests__/quotePricingEvents.test.ts +126 -0
  334. package/src/modules/cpq/services/__tests__/quotePricingIsolation.test.ts +52 -0
  335. package/src/modules/cpq/services/__tests__/quotePricingWrite.test.ts +74 -0
  336. package/src/modules/cpq/services/__tests__/resolveManualAdjustments.test.ts +90 -0
  337. package/src/modules/cpq/services/__tests__/storedAdjustmentFailure.test.ts +9 -0
  338. package/src/modules/cpq/services/arcLineRemoval.ts +87 -0
  339. package/src/modules/cpq/services/arcPriceFreeze.ts +165 -0
  340. package/src/modules/cpq/services/cpqOrderService.ts +22 -28
  341. package/src/modules/cpq/services/cpqPricingService.ts +8 -0
  342. package/src/modules/cpq/services/cpqQuotingService.ts +374 -125
  343. package/src/modules/cpq/services/orderPricingSummary.ts +47 -0
  344. package/src/modules/cpq/services/priceAdjustments/adjustmentService.ts +249 -0
  345. package/src/modules/cpq/services/priceAdjustments/chargeWaterfall.ts +28 -0
  346. package/src/modules/cpq/services/priceAdjustments/loadQuoteAdjustments.ts +25 -0
  347. package/src/modules/cpq/services/priceAdjustments/pricingSnapshot.ts +86 -0
  348. package/src/modules/cpq/services/priceAdjustments/quoteAdjustmentLifecycle.ts +68 -0
  349. package/src/modules/cpq/services/priceAdjustments/quotePricingEvents.ts +94 -0
  350. package/src/modules/cpq/services/priceAdjustments/quotePricingWrite.ts +51 -0
  351. package/src/modules/cpq/services/priceAdjustments/resolveManualAdjustments.ts +116 -0
  352. package/src/modules/cpq/services/priceAdjustments/storedAdjustmentFailure.ts +31 -0
  353. package/src/modules/cpq/services/types.ts +34 -1
  354. package/src/modules/cpq/setup.ts +7 -1
  355. package/src/modules/cpq/widgets/injection/customer-inventory/widget.client.tsx +38 -31
  356. package/src/modules/cpq/widgets/injection/order-cpq-tab/widget.client.tsx +7 -5
  357. package/src/modules/cpq/widgets/injection/quote-configurator/widget.client.tsx +36 -21
  358. package/src/modules/cpq/widgets/injection-table.ts +8 -2
  359. package/src/modules/cpq/workflows/WizardRunner.tsx +8 -5
  360. package/src/modules/cpq/workflows/WizardTrigger.tsx +15 -9
  361. package/src/modules/cpq/workflows/steps/CustomerSelectStep.tsx +11 -7
  362. package/src/modules/cpq/workflows/steps/InventorySelectStep.tsx +9 -5
  363. package/src/modules/cpq/workflows/steps/ItemListStep.tsx +18 -12
  364. package/src/modules/cpq/workflows/steps/OfferingSelectStep.tsx +20 -16
  365. package/src/modules/cpq/workflows/steps/ProductConfigureStep.tsx +8 -6
  366. package/src/modules/cpq/workflows/steps/ReviewStep.tsx +33 -18
  367. package/src/modules/cpq/workflows/steps/index.ts +12 -0
  368. package/src/modules/cpq/workflows/types.ts +4 -0
@@ -12,34 +12,44 @@ import {
12
12
  orderCpqStatusMap
13
13
  } from "../../../../components/statusMaps.js";
14
14
  import { StatusPath } from "../../../../components/StatusPath.js";
15
+ import { ChargeBreakdownTable } from "../../../../components/ChargeBreakdownTable.js";
16
+ import { PricingSummaryCard } from "../../../../components/PricingSummaryCard.js";
17
+ import { ArcLineDiff, LineActionBadge } from "../../../../components/ArcLineDiff.js";
18
+ import { arcRecurringDelta } from "../../../../lib/quoteSummaryView.js";
19
+ import { isArcCancelledLine } from "../../../../services/arcLineRemoval.js";
20
+ import { useT } from "@open-mercato/shared/lib/i18n/context";
21
+ function toChargeRow(c) {
22
+ return { ...c, chargeName: c.chargeName ?? "\u2014", chargeType: c.chargeType ?? "mrc", unitPrice: c.unitPrice ?? 0, totalPrice: c.totalPrice ?? null };
23
+ }
15
24
  const ARC_BADGE_VARIANTS = {
16
25
  amend: "brand",
17
26
  renew: "success",
18
27
  cancel: "error"
19
28
  };
20
- const RULE_TYPE_LABELS = {
21
- discount_percent: "% discount",
22
- discount_absolute: "fixed discount",
23
- surcharge_percent: "% surcharge",
24
- surcharge_absolute: "fixed surcharge",
25
- price_override: "override"
26
- };
27
- function fmtRuleValue(adj, currency) {
28
- if (adj.ruleType === "discount_percent" || adj.ruleType === "surcharge_percent") return `${adj.value}%`;
29
- if (currency) return fmt(adj.value, currency);
30
- return String(adj.value);
29
+ function quoteTypeLabel(t, quoteType) {
30
+ switch (quoteType) {
31
+ case "amend":
32
+ return t("cpq.quotes.type.amend", "amend");
33
+ case "renew":
34
+ return t("cpq.quotes.type.renew", "renew");
35
+ case "cancel":
36
+ return t("cpq.quotes.type.cancel", "cancel");
37
+ default:
38
+ return quoteType;
39
+ }
31
40
  }
32
41
  function fmt(amount, currency) {
33
42
  return new Intl.NumberFormat("en-US", { style: "currency", currency }).format(amount);
34
43
  }
35
- async function apiJson(url, init) {
44
+ async function apiJson(t, url, init) {
36
45
  const res = await fetch(url, init);
37
46
  const json = await res.json();
38
- if (!res.ok) throw new Error(json?.error ?? `Request failed (${res.status})`);
47
+ if (!res.ok) throw new Error(json?.error ?? t("cpq.common.requestFailed", "Request failed ({status})", { status: res.status }));
39
48
  return json;
40
49
  }
41
50
  function CpqOrderDetailPage(props) {
42
51
  const router = useRouter();
52
+ const t = useT();
43
53
  const urlParams = useParams();
44
54
  const cpqOrderId = props.params?.id ?? urlParams?.id;
45
55
  const [order, setOrder] = React.useState(null);
@@ -59,7 +69,7 @@ function CpqOrderDetailPage(props) {
59
69
  const targets = d?.items ?? [];
60
70
  const fetched = await Promise.all(
61
71
  targets.map(
62
- (t) => fetch(`/api/cpq/inventory/subscriptions?id=${t.subscriptionId}`).then((r) => r.ok ? r.json() : null).catch(() => null)
72
+ (target) => fetch(`/api/cpq/inventory/subscriptions?id=${target.subscriptionId}`).then((r) => r.ok ? r.json() : null).catch(() => null)
63
73
  )
64
74
  );
65
75
  return fetched.filter(Boolean);
@@ -80,7 +90,7 @@ function CpqOrderDetailPage(props) {
80
90
  let cancelled = false;
81
91
  async function load() {
82
92
  try {
83
- const result = await apiJson(`/api/cpq/orders/${cpqOrderId}`);
93
+ const result = await apiJson(t, `/api/cpq/orders/${cpqOrderId}`);
84
94
  if (!cancelled) {
85
95
  setOrder(result);
86
96
  fetch(`/api/customers/companies?id=${result.customerId}&pageSize=1`).then((r) => r.json()).then((data) => {
@@ -103,13 +113,13 @@ function CpqOrderDetailPage(props) {
103
113
  return () => {
104
114
  cancelled = true;
105
115
  };
106
- }, [cpqOrderId, fetchLinkedSubscriptions]);
116
+ }, [cpqOrderId, fetchLinkedSubscriptions, t]);
107
117
  const activateOrder = async () => {
108
118
  if (!order) return;
109
119
  try {
110
120
  setActivating(true);
111
121
  setError(null);
112
- const result = await apiJson(`/api/cpq/orders/${order.id}/activate`, {
122
+ const result = await apiJson(t, `/api/cpq/orders/${order.id}/activate`, {
113
123
  method: "POST",
114
124
  headers: { "Content-Type": "application/json" },
115
125
  body: JSON.stringify({})
@@ -126,7 +136,7 @@ function CpqOrderDetailPage(props) {
126
136
  if (!order) return;
127
137
  try {
128
138
  setError(null);
129
- const result = await apiJson(`/api/cpq/orders/${order.id}/status`, {
139
+ const result = await apiJson(t, `/api/cpq/orders/${order.id}/status`, {
130
140
  method: "POST",
131
141
  headers: { "Content-Type": "application/json" },
132
142
  body: JSON.stringify({ targetStatus: "cancelled" })
@@ -146,14 +156,15 @@ function CpqOrderDetailPage(props) {
146
156
  if (loading) {
147
157
  return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 py-12 justify-center text-sm text-muted-foreground", children: [
148
158
  /* @__PURE__ */ jsx(Spinner, {}),
149
- " Loading\u2026"
159
+ " ",
160
+ t("cpq.orders.detail.loading", "Loading\u2026")
150
161
  ] });
151
162
  }
152
163
  if (!order) {
153
164
  return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
154
- /* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/orders"), className: "text-sm text-muted-foreground hover:text-foreground", children: "\u2190 Back to Orders" }),
165
+ /* @__PURE__ */ jsx("button", { onClick: () => router.push("/backend/cpq/orders"), className: "text-sm text-muted-foreground hover:text-foreground", children: t("cpq.orders.detail.backToOrders", "\u2190 Back to Orders") }),
155
166
  error && /* @__PURE__ */ jsx(ErrorBanner, { message: error }),
156
- /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: "Order not found." })
167
+ /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card p-6 text-center text-sm text-muted-foreground", children: t("cpq.orders.detail.notFound", "Order not found.") })
157
168
  ] });
158
169
  }
159
170
  const canActivate = order.cpqStatus === "draft" || order.cpqStatus === "pending_activation";
@@ -170,12 +181,12 @@ function CpqOrderDetailPage(props) {
170
181
  {
171
182
  mode: "detail",
172
183
  backHref: "/backend/cpq/orders",
173
- backLabel: "Back to CPQ Orders",
174
- entityTypeLabel: "CPQ Order",
184
+ backLabel: t("cpq.orders.detail.backLabel", "Back to CPQ Orders"),
185
+ entityTypeLabel: t("cpq.orders.detail.entityType", "CPQ Order"),
175
186
  title: order.orderNumber || order.orderId.slice(0, 8),
176
187
  statusBadge: /* @__PURE__ */ jsxs("div", { className: "flex flex-wrap items-center gap-2 pt-1", children: [
177
188
  /* @__PURE__ */ jsx(Tag, { variant: orderCpqStatusMap[order.cpqStatus] ?? "neutral", dot: true, children: formatStatusLabel(order.cpqStatus) }),
178
- order.quoteType && order.quoteType !== "new" && /* @__PURE__ */ jsx(Tag, { variant: ARC_BADGE_VARIANTS[order.quoteType] ?? "neutral", className: "uppercase", children: order.quoteType })
189
+ order.quoteType && order.quoteType !== "new" && /* @__PURE__ */ jsx(Tag, { variant: ARC_BADGE_VARIANTS[order.quoteType] ?? "neutral", className: "uppercase", children: quoteTypeLabel(t, order.quoteType) })
179
190
  ] }),
180
191
  actionsContent: /* @__PURE__ */ jsxs(Fragment, { children: [
181
192
  /* @__PURE__ */ jsxs(
@@ -186,7 +197,7 @@ function CpqOrderDetailPage(props) {
186
197
  onClick: () => router.push(`/backend/sales/orders/${order.orderId}`),
187
198
  children: [
188
199
  /* @__PURE__ */ jsx(ExternalLink, { className: "h-3.5 w-3.5" }),
189
- "View Sales Order"
200
+ t("cpq.orders.detail.viewSalesOrder", "View Sales Order")
190
201
  ]
191
202
  }
192
203
  ),
@@ -196,16 +207,16 @@ function CpqOrderDetailPage(props) {
196
207
  type: "button",
197
208
  variant: "outline",
198
209
  onClick: () => router.push(`/backend/cpq/quotes/${order.sourceQuoteId}`),
199
- children: "Source Quote"
210
+ children: t("cpq.orders.detail.sourceQuote", "Source Quote")
200
211
  }
201
212
  ),
202
213
  canActivate && /* @__PURE__ */ jsxs(Button, { type: "button", onClick: activateOrder, disabled: activating, children: [
203
214
  activating ? /* @__PURE__ */ jsx(Spinner, {}) : /* @__PURE__ */ jsx(Play, { className: "h-4 w-4" }),
204
- "Activate"
215
+ t("cpq.orders.detail.activate", "Activate")
205
216
  ] }),
206
217
  canCancel && /* @__PURE__ */ jsxs(Button, { type: "button", variant: "destructive-outline", onClick: cancelOrder, children: [
207
218
  /* @__PURE__ */ jsx(X, { className: "h-4 w-4" }),
208
- "Cancel"
219
+ t("cpq.common.cancel", "Cancel")
209
220
  ] })
210
221
  ] })
211
222
  }
@@ -217,7 +228,7 @@ function CpqOrderDetailPage(props) {
217
228
  path: ["draft", "pending_activation", "active", "fulfilled"],
218
229
  terminals: ["cancelled"],
219
230
  statusMap: orderCpqStatusMap,
220
- ariaLabel: "Order status path"
231
+ ariaLabel: t("cpq.orders.detail.statusPath", "Order status path")
221
232
  }
222
233
  ) }),
223
234
  error && /* @__PURE__ */ jsx(ErrorBanner, { message: error, onDismiss: () => setError(null) }),
@@ -228,7 +239,7 @@ function CpqOrderDetailPage(props) {
228
239
  children: [
229
240
  /* @__PURE__ */ jsxs("div", { className: "flex-1 min-w-0 space-y-1", children: [
230
241
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 flex-wrap", children: [
231
- /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: "Linked Subscription" }),
242
+ /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wide text-muted-foreground", children: t("cpq.orders.detail.linkedSubscription", "Linked Subscription") }),
232
243
  /* @__PURE__ */ jsx(
233
244
  Tag,
234
245
  {
@@ -248,10 +259,7 @@ function CpqOrderDetailPage(props) {
248
259
  ")"
249
260
  ] })
250
261
  ] }),
251
- order.activatedAt && /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground", children: [
252
- "Activated on ",
253
- new Date(order.activatedAt).toLocaleString()
254
- ] })
262
+ order.activatedAt && /* @__PURE__ */ jsx("div", { className: "text-xs text-muted-foreground", children: t("cpq.orders.detail.activatedOn", "Activated on {date}", { date: new Date(order.activatedAt).toLocaleString() }) })
255
263
  ] }),
256
264
  /* @__PURE__ */ jsxs(
257
265
  Button,
@@ -261,7 +269,7 @@ function CpqOrderDetailPage(props) {
261
269
  onClick: () => router.push(`/backend/cpq/inventory/subscriptions/${sub.id}`),
262
270
  children: [
263
271
  /* @__PURE__ */ jsx(ArrowRight, { className: "h-3.5 w-3.5" }),
264
- "Open"
272
+ t("cpq.orders.actions.open", "Open")
265
273
  ]
266
274
  }
267
275
  )
@@ -270,30 +278,26 @@ function CpqOrderDetailPage(props) {
270
278
  sub.id
271
279
  )) }),
272
280
  /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-3", children: [
273
- /* @__PURE__ */ jsx(MetaCard, { label: "Customer", children: customerName ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: customerName }) : /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
281
+ /* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.customer", "Customer"), children: customerName ? /* @__PURE__ */ jsx("span", { className: "font-medium", children: customerName }) : /* @__PURE__ */ jsxs("span", { className: "font-mono text-xs text-muted-foreground", children: [
274
282
  order.customerId.slice(0, 8),
275
283
  "\u2026"
276
284
  ] }) }),
277
- /* @__PURE__ */ jsx(MetaCard, { label: "Created", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: new Date(order.createdAt).toLocaleDateString() }) }),
278
- /* @__PURE__ */ jsx(MetaCard, { label: "Currency", children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: currency }) })
279
- ] }),
280
- /* @__PURE__ */ jsxs("div", { className: "rounded-lg border bg-card p-4", children: [
281
- /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-3", children: "Pricing Summary" }),
282
- /* @__PURE__ */ jsxs("div", { className: "grid grid-cols-2 gap-4", children: [
283
- /* @__PURE__ */ jsxs("div", { children: [
284
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Non-Recurring (NRC)" }),
285
- /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(order.pricingSummary.nrcTotal, currency) })
286
- ] }),
287
- /* @__PURE__ */ jsxs("div", { children: [
288
- /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground", children: "Monthly Recurring (MRC)" }),
289
- /* @__PURE__ */ jsx("p", { className: "text-lg font-semibold font-mono", children: fmt(order.pricingSummary.mrcTotal, currency) })
290
- ] })
291
- ] })
285
+ /* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.created", "Created"), children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: new Date(order.createdAt).toLocaleDateString() }) }),
286
+ /* @__PURE__ */ jsx(MetaCard, { label: t("cpq.orders.detail.meta.currency", "Currency"), children: /* @__PURE__ */ jsx("span", { className: "font-medium", children: currency }) })
292
287
  ] }),
288
+ /* @__PURE__ */ jsx(
289
+ PricingSummaryCard,
290
+ {
291
+ summary: order.pricingSummary,
292
+ currency,
293
+ arcDelta: order.quoteType === "amend" || order.quoteType === "renew" ? arcRecurringDelta(order.lines.map((l) => ({ ...l, action: l.action }))) : null
294
+ }
295
+ ),
293
296
  /* @__PURE__ */ jsx(
294
297
  OrderLineTree,
295
298
  {
296
299
  lines: order.lines,
300
+ quoteType: order.quoteType,
297
301
  currency,
298
302
  expandedLines,
299
303
  pricingDetail,
@@ -311,12 +315,14 @@ function MetaCard({ label, children }) {
311
315
  }
312
316
  function OrderLineTree({
313
317
  lines,
318
+ quoteType,
314
319
  currency,
315
320
  expandedLines,
316
321
  pricingDetail,
317
322
  onToggleLine,
318
323
  onTogglePricingDetail
319
324
  }) {
325
+ const t = useT();
320
326
  const rootLines = lines.filter((l) => !l.parentLineId);
321
327
  const childrenByParent = /* @__PURE__ */ new Map();
322
328
  for (const line of lines) {
@@ -329,12 +335,8 @@ function OrderLineTree({
329
335
  const indexOf = /* @__PURE__ */ new Map();
330
336
  lines.forEach((l, i) => indexOf.set(l.lineId, i));
331
337
  return /* @__PURE__ */ jsxs("div", { children: [
332
- /* @__PURE__ */ jsxs("h3", { className: "text-sm font-medium mb-2 px-1", children: [
333
- "Line Items (",
334
- lines.length,
335
- ")"
336
- ] }),
337
- lines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: "No line items." }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: rootLines.map((line) => {
338
+ /* @__PURE__ */ jsx("h3", { className: "text-sm font-medium mb-2 px-1", children: t("cpq.orders.detail.lineItems", "Line Items ({count})", { count: lines.length }) }),
339
+ lines.length === 0 ? /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card py-8 text-center text-sm text-muted-foreground", children: t("cpq.orders.detail.noLineItems", "No line items.") }) : /* @__PURE__ */ jsx("div", { className: "rounded-lg border bg-card divide-y", children: rootLines.map((line) => {
338
340
  const isBundle = line.offeringType === "bundle";
339
341
  const children = childrenByParent.get(line.lineId) ?? [];
340
342
  const isExpanded = expandedLines.has(line.lineId);
@@ -345,6 +347,7 @@ function OrderLineTree({
345
347
  {
346
348
  line,
347
349
  lineIdx,
350
+ quoteType,
348
351
  currency,
349
352
  isExpanded,
350
353
  isBundle,
@@ -356,11 +359,7 @@ function OrderLineTree({
356
359
  }
357
360
  ),
358
361
  isBundle && isExpanded && children.length > 0 && /* @__PURE__ */ jsxs("div", { className: "border-t border-dashed border-brand-violet/30 bg-brand-violet/5", children: [
359
- /* @__PURE__ */ jsx("div", { className: "px-6 py-1.5", children: /* @__PURE__ */ jsxs("span", { className: "text-[10px] font-medium text-brand-violet uppercase tracking-wider", children: [
360
- "Bundle Components (",
361
- children.length,
362
- ")"
363
- ] }) }),
362
+ /* @__PURE__ */ jsx("div", { className: "px-6 py-1.5", children: /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium text-brand-violet uppercase tracking-wider", children: t("cpq.lines.bundleComponents", "Bundle Components ({count})", { count: children.length }) }) }),
364
363
  children.map((child) => {
365
364
  const childIdx = indexOf.get(child.lineId) ?? 0;
366
365
  return /* @__PURE__ */ jsx(
@@ -368,6 +367,7 @@ function OrderLineTree({
368
367
  {
369
368
  line: child,
370
369
  lineIdx: childIdx,
370
+ quoteType,
371
371
  currency,
372
372
  isExpanded: expandedLines.has(child.lineId),
373
373
  isBundle: false,
@@ -385,39 +385,59 @@ function OrderLineTree({
385
385
  }) })
386
386
  ] });
387
387
  }
388
- function OrderLineRow({ line, lineIdx, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }) {
388
+ function OrderLineRow({ line, lineIdx, quoteType, currency, isExpanded, isBundle, childCount, indent, pricingDetail, onToggle, onTogglePricingDetail }) {
389
+ const t = useT();
389
390
  const paddingLeft = indent > 0 ? `${indent * 2 + 1}rem` : void 0;
391
+ const isRemoved = isArcCancelledLine(line, quoteType);
390
392
  return /* @__PURE__ */ jsxs("div", { children: [
391
393
  /* @__PURE__ */ jsxs(
392
394
  "div",
393
395
  {
394
- className: "flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer",
396
+ className: `flex items-center gap-3 px-4 py-3 hover:bg-muted/30 transition-colors cursor-pointer ${isRemoved ? "bg-muted/20" : ""}`,
395
397
  style: paddingLeft ? { paddingLeft } : void 0,
396
398
  onClick: onToggle,
399
+ "data-testid": "cpq-order-line",
400
+ "data-removed": isRemoved ? "true" : void 0,
397
401
  children: [
398
402
  /* @__PURE__ */ jsx("button", { className: "flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:text-foreground", children: /* @__PURE__ */ jsx("svg", { className: `h-4 w-4 transition-transform ${isExpanded ? "rotate-90" : ""}`, fill: "none", viewBox: "0 0 24 24", strokeWidth: "2", stroke: "currentColor", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M8.25 4.5l7.5 7.5-7.5 7.5" }) }) }),
399
403
  /* @__PURE__ */ jsx("div", { className: "flex-1 min-w-0", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
400
404
  indent > 0 && /* @__PURE__ */ jsx("span", { className: "text-muted-foreground text-xs", children: "\u2514" }),
401
- isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: "bundle" }),
402
- /* @__PURE__ */ jsx("span", { className: "font-medium text-sm", children: line.offeringName }),
405
+ isBundle && /* @__PURE__ */ jsx(Tag, { variant: "brand", className: "px-1.5 text-xs", children: t("cpq.lines.bundle", "bundle") }),
406
+ /* @__PURE__ */ jsx("span", { className: `font-medium text-sm ${isRemoved ? "text-muted-foreground line-through" : ""}`, children: line.offeringName }),
403
407
  line.quantity > 1 && /* @__PURE__ */ jsxs("span", { className: "rounded bg-muted px-1.5 py-0.5 text-xs font-medium", children: [
404
408
  "\xD7",
405
409
  line.quantity
406
410
  ] }),
407
- /* @__PURE__ */ jsx(Tag, { variant: "info", className: "px-1.5 text-xs", children: line.action }),
408
- isBundle && /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground", children: [
409
- childCount,
410
- " component",
411
- childCount !== 1 ? "s" : ""
412
- ] })
411
+ /* @__PURE__ */ jsx(
412
+ LineActionBadge,
413
+ {
414
+ action: line.action,
415
+ label: isRemoved ? t("cpq.quotes.arc.lineRemoved", "remove") : void 0,
416
+ title: isRemoved ? t("cpq.quotes.arc.lineRemovedHint", "This item is cancelled on the subscription when the order is activated") : void 0
417
+ }
418
+ ),
419
+ isBundle && /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground", children: childCount !== 1 ? t("cpq.lines.componentCount.other", "{count} components", { count: childCount }) : t("cpq.lines.componentCount.one", "{count} component", { count: childCount }) })
413
420
  ] }) }),
414
421
  /* @__PURE__ */ jsxs("div", { className: "shrink-0 text-right text-xs space-y-0.5", children: [
422
+ isRemoved && line.arcSource && line.arcSource.mrcAmount > 0 && /* @__PURE__ */ jsxs("div", { children: [
423
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
424
+ t("cpq.lines.mrc", "MRC"),
425
+ " "
426
+ ] }),
427
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-muted-foreground line-through", children: fmt(line.arcSource.mrcAmount, currency) })
428
+ ] }),
415
429
  line.nrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
416
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "NRC " }),
430
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
431
+ t("cpq.lines.nrc", "NRC"),
432
+ " "
433
+ ] }),
417
434
  /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.nrcTotal, currency) })
418
435
  ] }),
419
436
  line.mrcTotal > 0 && /* @__PURE__ */ jsxs("div", { children: [
420
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: "MRC " }),
437
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
438
+ t("cpq.lines.mrc", "MRC"),
439
+ " "
440
+ ] }),
421
441
  /* @__PURE__ */ jsx("span", { className: "font-mono font-medium", children: fmt(line.mrcTotal, currency) })
422
442
  ] })
423
443
  ] })
@@ -425,7 +445,7 @@ function OrderLineRow({ line, lineIdx, currency, isExpanded, isBundle, childCoun
425
445
  }
426
446
  ),
427
447
  isExpanded && (() => {
428
- const internalKeys = /* @__PURE__ */ new Set(["_arcMirroredName", "_arcMirroredFromItemId", "offeringName"]);
448
+ const internalKeys = /* @__PURE__ */ new Set(["_arcMirroredName", "_arcMirroredFromItemId", "_arcPriceFrozen", "_arcRepriced", "offeringName"]);
429
449
  const visibleConfigEntries = Object.entries(line.configuration).filter(
430
450
  ([key]) => !internalKeys.has(key)
431
451
  );
@@ -435,174 +455,61 @@ function OrderLineRow({ line, lineIdx, currency, isExpanded, isBundle, childCoun
435
455
  const hasArcDiff = !!line.arcSource;
436
456
  const mirroredFromItemId = line.configuration?._arcMirroredFromItemId ?? null;
437
457
  const isEmpty = !hasConfig && !hasCharges && !hasTerm && !hasArcDiff;
438
- return /* @__PURE__ */ jsxs(
458
+ return /* @__PURE__ */ jsx(
439
459
  "div",
440
460
  {
441
461
  className: "border-t bg-muted/10 px-12 py-3 space-y-3",
442
462
  style: paddingLeft ? { paddingLeft: `calc(${paddingLeft} + 2rem)` } : void 0,
443
- children: [
444
- hasArcDiff && /* @__PURE__ */ jsx(ArcLineDiff, { line, currency }),
445
- isEmpty ? /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [
446
- /* @__PURE__ */ jsxs("div", { children: [
447
- "No detailed breakdown for this line.",
448
- mirroredFromItemId && " Mirrored from existing subscription item."
449
- ] }),
450
- (line.nrcTotal > 0 || line.mrcTotal > 0) && /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
451
- line.nrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
452
- "NRC ",
453
- /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.nrcTotal, currency) })
454
- ] }),
455
- line.mrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
456
- "MRC ",
457
- /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.mrcTotal, currency) })
458
- ] })
459
- ] })
460
- ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
461
- hasConfig && /* @__PURE__ */ jsxs("div", { children: [
462
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Configuration" }),
463
- /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: visibleConfigEntries.map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
464
- /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
465
- key,
466
- ": "
467
- ] }),
468
- /* @__PURE__ */ jsx("span", { className: "font-medium", children: String(value) })
469
- ] }, key)) })
470
- ] }),
471
- hasCharges && /* @__PURE__ */ jsxs("div", { children: [
472
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: "Charges" }),
473
- /* @__PURE__ */ jsx(
474
- ChargeBreakdown,
475
- {
476
- line,
477
- lineIdx,
478
- currency,
479
- pricingDetail,
480
- onTogglePricingDetail
481
- }
482
- )
463
+ children: isEmpty ? /* @__PURE__ */ jsxs("div", { className: "text-xs text-muted-foreground space-y-1", children: [
464
+ /* @__PURE__ */ jsxs("div", { children: [
465
+ t("cpq.orders.detail.noBreakdown", "No detailed breakdown for this line."),
466
+ mirroredFromItemId && " ",
467
+ mirroredFromItemId && t("cpq.orders.detail.mirroredItem", "Mirrored from existing subscription item.")
468
+ ] }),
469
+ (line.nrcTotal > 0 || line.mrcTotal > 0) && /* @__PURE__ */ jsxs("div", { className: "flex gap-4", children: [
470
+ line.nrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
471
+ t("cpq.lines.nrc", "NRC"),
472
+ " ",
473
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.nrcTotal, currency) })
483
474
  ] }),
484
- hasTerm && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-xs text-muted-foreground", children: [
485
- line.startDate && /* @__PURE__ */ jsxs("span", { children: [
486
- "Start: ",
487
- new Date(line.startDate).toLocaleDateString()
488
- ] }),
489
- line.termMonths && /* @__PURE__ */ jsxs("span", { children: [
490
- "Term: ",
491
- line.termMonths,
492
- "mo"
493
- ] }),
494
- line.endDate && /* @__PURE__ */ jsxs("span", { children: [
495
- "End: ",
496
- new Date(line.endDate).toLocaleDateString()
497
- ] })
475
+ line.mrcTotal > 0 && /* @__PURE__ */ jsxs("span", { children: [
476
+ t("cpq.lines.mrc", "MRC"),
477
+ " ",
478
+ /* @__PURE__ */ jsx("span", { className: "font-mono font-medium text-foreground", children: fmt(line.mrcTotal, currency) })
498
479
  ] })
499
480
  ] })
500
- ]
481
+ ] }) : /* @__PURE__ */ jsxs(Fragment, { children: [
482
+ hasConfig && /* @__PURE__ */ jsxs("div", { children: [
483
+ /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1", children: t("cpq.lines.configuration", "Configuration") }),
484
+ /* @__PURE__ */ jsx("div", { className: "grid grid-cols-2 sm:grid-cols-3 gap-x-4 gap-y-1", children: visibleConfigEntries.map(([key, value]) => /* @__PURE__ */ jsxs("div", { className: "text-xs", children: [
485
+ /* @__PURE__ */ jsxs("span", { className: "text-muted-foreground", children: [
486
+ key,
487
+ ": "
488
+ ] }),
489
+ /* @__PURE__ */ jsx("span", { className: "font-medium", children: String(value) })
490
+ ] }, key)) })
491
+ ] }),
492
+ hasCharges && /* @__PURE__ */ jsx(
493
+ ChargeBreakdownTable,
494
+ {
495
+ charges: line.charges.map(toChargeRow),
496
+ currency,
497
+ openDetailIndex: pricingDetail?.lineIdx === lineIdx ? pricingDetail.chargeIdx : null,
498
+ onToggleDetail: (chargeIdx) => onTogglePricingDetail(lineIdx, chargeIdx)
499
+ }
500
+ ),
501
+ hasTerm && /* @__PURE__ */ jsxs("div", { className: "flex gap-4 text-xs text-muted-foreground", children: [
502
+ line.startDate && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.start", "Start: {date}", { date: new Date(line.startDate).toLocaleDateString() }) }),
503
+ line.termMonths && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.months", "Term: {months}mo", { months: line.termMonths }) }),
504
+ line.endDate && /* @__PURE__ */ jsx("span", { children: t("cpq.lines.term.end", "End: {date}", { date: new Date(line.endDate).toLocaleDateString() }) })
505
+ ] }),
506
+ hasArcDiff && /* @__PURE__ */ jsx(ArcLineDiff, { line, currency })
507
+ ] })
501
508
  }
502
509
  );
503
510
  })()
504
511
  ] });
505
512
  }
506
- function ChargeBreakdown({ line, lineIdx, currency, pricingDetail, onTogglePricingDetail }) {
507
- return /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
508
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
509
- /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Charge" }),
510
- /* @__PURE__ */ jsx("th", { className: "text-left py-1 font-medium", children: "Type" }),
511
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Unit Price" }),
512
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Qty" }),
513
- /* @__PURE__ */ jsx("th", { className: "text-right py-1 font-medium", children: "Total" }),
514
- /* @__PURE__ */ jsx("th", { className: "w-8" })
515
- ] }) }),
516
- /* @__PURE__ */ jsx("tbody", { children: line.charges.map((c, i) => {
517
- const hasDetail = !!(c.breakdown?.tiers?.length || c.adjustments?.length);
518
- const isDetailOpen = pricingDetail?.lineIdx === lineIdx && pricingDetail?.chargeIdx === i;
519
- return /* @__PURE__ */ jsxs(React.Fragment, { children: [
520
- /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/50", children: [
521
- /* @__PURE__ */ jsx("td", { className: "py-1", children: c.chargeName ?? "\u2014" }),
522
- /* @__PURE__ */ jsx("td", { className: "py-1 uppercase", children: c.chargeType ?? "\u2014" }),
523
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.unitPrice != null ? fmt(c.unitPrice, currency) : "\u2014" }),
524
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.quantity ?? "\u2014" }),
525
- /* @__PURE__ */ jsx("td", { className: "py-1 text-right font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : c.note ?? "\u2014" }),
526
- /* @__PURE__ */ jsx("td", { className: "py-1 text-center", children: hasDetail && /* @__PURE__ */ jsx(
527
- "button",
528
- {
529
- onClick: () => onTogglePricingDetail(lineIdx, i),
530
- className: "inline-flex items-center justify-center rounded p-0.5 hover:bg-accent text-muted-foreground hover:text-foreground transition-colors",
531
- title: "Show pricing breakdown",
532
- children: /* @__PURE__ */ jsx("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", strokeWidth: 1.5, stroke: "currentColor", className: "w-3.5 h-3.5", children: /* @__PURE__ */ jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M11.25 11.25l.041-.02a.75.75 0 011.063.852l-.708 2.836a.75.75 0 001.063.853l.041-.021M21 12a9 9 0 11-18 0 9 9 0 0118 0zm-9-3.75h.008v.008H12V8.25z" }) })
533
- }
534
- ) })
535
- ] }),
536
- isDetailOpen && /* @__PURE__ */ jsx("tr", { children: /* @__PURE__ */ jsx("td", { colSpan: 6, className: "pb-3 pt-1", children: /* @__PURE__ */ jsxs("div", { className: "rounded border bg-background p-3 space-y-3 text-xs", children: [
537
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between", children: [
538
- /* @__PURE__ */ jsxs("span", { className: "font-semibold text-sm", children: [
539
- "Pricing Details \u2014 ",
540
- c.chargeName
541
- ] }),
542
- /* @__PURE__ */ jsx("span", { className: "text-muted-foreground", children: c.pricingMethod ?? "flat" })
543
- ] }),
544
- c.breakdown?.tiers && c.breakdown.tiers.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
545
- /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Tier Breakdown" }),
546
- /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
547
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
548
- /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Tier" }),
549
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Range" }),
550
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Price/Unit" }),
551
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Qty" }),
552
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Subtotal" })
553
- ] }) }),
554
- /* @__PURE__ */ jsxs("tbody", { children: [
555
- c.breakdown.tiers.map((t) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
556
- /* @__PURE__ */ jsx("td", { className: "py-0.5", children: t.tier }),
557
- /* @__PURE__ */ jsxs("td", { className: "py-0.5 text-right font-mono", children: [
558
- t.from,
559
- "\u2013",
560
- t.to
561
- ] }),
562
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.pricePerUnit, currency) }),
563
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: t.quantity }),
564
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(t.subtotal, currency) })
565
- ] }, t.tier)),
566
- /* @__PURE__ */ jsxs("tr", { className: "border-t font-medium", children: [
567
- /* @__PURE__ */ jsx("td", { colSpan: 4, className: "py-0.5 text-right", children: "Tiered Total" }),
568
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(c.breakdown.tiers.reduce((s, t) => s + t.subtotal, 0), currency) })
569
- ] })
570
- ] })
571
- ] })
572
- ] }),
573
- c.adjustments && c.adjustments.length > 0 && /* @__PURE__ */ jsxs("div", { children: [
574
- /* @__PURE__ */ jsx("div", { className: "font-medium text-muted-foreground mb-1", children: "Applied Price Rules" }),
575
- /* @__PURE__ */ jsxs("table", { className: "w-full", children: [
576
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
577
- /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Rule" }),
578
- /* @__PURE__ */ jsx("th", { className: "text-left py-0.5 font-medium", children: "Type" }),
579
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Value" }),
580
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Before" }),
581
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "After" }),
582
- /* @__PURE__ */ jsx("th", { className: "text-right py-0.5 font-medium", children: "Delta" })
583
- ] }) }),
584
- /* @__PURE__ */ jsx("tbody", { children: c.adjustments.map((adj, ai) => /* @__PURE__ */ jsxs("tr", { className: "border-t border-border/30", children: [
585
- /* @__PURE__ */ jsx("td", { className: "py-0.5", children: adj.ruleName }),
586
- /* @__PURE__ */ jsx("td", { className: "py-0.5", children: RULE_TYPE_LABELS[adj.ruleType] ?? adj.ruleType }),
587
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmtRuleValue(adj, currency) }),
588
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceBefore, currency) }),
589
- /* @__PURE__ */ jsx("td", { className: "py-0.5 text-right font-mono", children: fmt(adj.unitPriceAfter, currency) }),
590
- /* @__PURE__ */ jsxs("td", { className: `py-0.5 text-right font-mono ${adj.delta < 0 ? "text-destructive" : adj.delta > 0 ? "text-status-success-text" : ""}`, children: [
591
- adj.delta > 0 ? "+" : "",
592
- fmt(adj.delta, currency)
593
- ] })
594
- ] }, ai)) })
595
- ] })
596
- ] }),
597
- /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between pt-1 border-t text-sm font-semibold", children: [
598
- /* @__PURE__ */ jsx("span", { children: "Final Total" }),
599
- /* @__PURE__ */ jsx("span", { className: "font-mono", children: c.totalPrice != null ? fmt(c.totalPrice, currency) : "\u2014" })
600
- ] })
601
- ] }) }) })
602
- ] }, i);
603
- }) })
604
- ] });
605
- }
606
513
  function Spinner() {
607
514
  return /* @__PURE__ */ jsxs("svg", { className: "h-4 w-4 animate-spin", viewBox: "0 0 24 24", fill: "none", children: [
608
515
  /* @__PURE__ */ jsx("circle", { className: "opacity-25", cx: "12", cy: "12", r: "10", stroke: "currentColor", strokeWidth: "4" }),
@@ -610,6 +517,7 @@ function Spinner() {
610
517
  ] });
611
518
  }
612
519
  function ErrorBanner({ message, onDismiss }) {
520
+ const t = useT();
613
521
  return /* @__PURE__ */ jsxs(Alert, { variant: "destructive", className: "flex items-start gap-2", children: [
614
522
  /* @__PURE__ */ jsx("span", { className: "flex-1", children: message }),
615
523
  onDismiss && /* @__PURE__ */ jsx(
@@ -617,60 +525,12 @@ function ErrorBanner({ message, onDismiss }) {
617
525
  {
618
526
  onClick: onDismiss,
619
527
  className: "text-destructive/70 hover:text-destructive text-sm font-bold",
620
- "aria-label": "Dismiss",
528
+ "aria-label": t("cpq.common.dismiss", "Dismiss"),
621
529
  children: "\xD7"
622
530
  }
623
531
  )
624
532
  ] });
625
533
  }
626
- function ArcLineDiff({ line, currency }) {
627
- const src = line.arcSource;
628
- if (!src) return null;
629
- const isCancel = line.action === "cancel";
630
- const beforeMrc = src.mrcAmount;
631
- const beforeNrc = src.nrcAmount;
632
- const beforeQty = src.quantity;
633
- const afterMrc = isCancel ? 0 : line.mrcTotal;
634
- const afterNrc = isCancel ? 0 : line.nrcTotal;
635
- const afterQty = isCancel ? 0 : line.quantity;
636
- const mrcDelta = afterMrc - beforeMrc;
637
- const nrcDelta = afterNrc - beforeNrc;
638
- const qtyDelta = afterQty - beforeQty;
639
- return /* @__PURE__ */ jsxs("div", { children: [
640
- /* @__PURE__ */ jsx("p", { className: "text-xs font-medium text-muted-foreground mb-1.5", children: "Change Preview" }),
641
- /* @__PURE__ */ jsx("div", { className: "rounded border bg-background overflow-hidden", children: /* @__PURE__ */ jsxs("table", { className: "w-full text-xs", children: [
642
- /* @__PURE__ */ jsx("thead", { className: "bg-muted/40", children: /* @__PURE__ */ jsxs("tr", { className: "text-muted-foreground", children: [
643
- /* @__PURE__ */ jsx("th", { className: "text-left px-3 py-1.5 font-medium" }),
644
- /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "Before" }),
645
- /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "After" }),
646
- /* @__PURE__ */ jsx("th", { className: "text-right px-3 py-1.5 font-medium", children: "\u0394" })
647
- ] }) }),
648
- /* @__PURE__ */ jsxs("tbody", { children: [
649
- /* @__PURE__ */ jsx(ArcDiffRow, { label: "Quantity", before: beforeQty, after: afterQty, delta: qtyDelta, currency: null, dimAfter: isCancel }),
650
- /* @__PURE__ */ jsx(ArcDiffRow, { label: "MRC", before: beforeMrc, after: afterMrc, delta: mrcDelta, currency, dimAfter: isCancel }),
651
- /* @__PURE__ */ jsx(ArcDiffRow, { label: "NRC", before: beforeNrc, after: afterNrc, delta: nrcDelta, currency, dimAfter: isCancel })
652
- ] })
653
- ] }) }),
654
- isCancel && /* @__PURE__ */ jsx("p", { className: "text-xs text-destructive mt-1 italic", children: "Cancellation \u2014 item will be terminated on activation." })
655
- ] });
656
- }
657
- function ArcDiffRow({
658
- label,
659
- before,
660
- after,
661
- delta,
662
- currency,
663
- dimAfter
664
- }) {
665
- const fmtVal = (v) => currency ? fmt(v, currency) : String(v);
666
- const showDelta = delta !== 0;
667
- return /* @__PURE__ */ jsxs("tr", { className: "border-t border-dashed border-border/40", children: [
668
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-muted-foreground", children: label }),
669
- /* @__PURE__ */ jsx("td", { className: "px-3 py-1 text-right font-mono", children: fmtVal(before) }),
670
- /* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono ${dimAfter ? "text-muted-foreground line-through" : ""}`, children: fmtVal(after) }),
671
- /* @__PURE__ */ jsx("td", { className: `px-3 py-1 text-right font-mono font-medium ${showDelta ? delta > 0 ? "text-status-success-text" : "text-destructive" : "text-muted-foreground"}`, children: showDelta ? `${delta > 0 ? "+" : ""}${fmtVal(delta)}` : "\u2014" })
672
- ] });
673
- }
674
534
  export {
675
535
  CpqOrderDetailPage as default
676
536
  };