@fiado/type-kit 3.471.0 → 3.473.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 (278) hide show
  1. package/_test_/unit/common/IsStringValueRecord.test.ts +25 -0
  2. package/_test_/unit/platformRbac/dtos/tenantUserCustomFields.test.ts +55 -0
  3. package/_test_/unit/retailCatalog/catalogDebt.test.ts +123 -0
  4. package/_test_/unit/retailCatalog/catalogEnums.test.ts +2 -2
  5. package/_test_/unit/retailCatalog/ventaSaga.test.ts +152 -0
  6. package/_test_/unit/retailPromotions/computeBudgetUsedBps.test.ts +30 -0
  7. package/_test_/unit/retailPromotions/constants.test.ts +71 -0
  8. package/_test_/unit/retailPromotions/discountCodeAndWizardRequests.test.ts +84 -0
  9. package/_test_/unit/retailPromotions/salePromotionRequests.test.ts +151 -0
  10. package/_test_/unit/retailWizard/promotionM2mRequests.test.ts +189 -0
  11. package/_test_/unit/retailWizard/ventaSagaMessages.test.ts +163 -0
  12. package/_test_/unit/retailWizard/ventaSagaRequests.test.ts +59 -0
  13. package/_test_/unit/retailWizard/ventaSagaShapes.test.ts +99 -0
  14. package/bin/common/index.d.ts +1 -0
  15. package/bin/common/index.js +2 -0
  16. package/bin/common/validators/IsStringValueRecord.d.ts +5 -0
  17. package/bin/common/validators/IsStringValueRecord.js +25 -0
  18. package/bin/index.d.ts +1 -0
  19. package/bin/index.js +5 -2
  20. package/bin/messagesConnector/validators/IsStringValueRecord.d.ts +1 -5
  21. package/bin/messagesConnector/validators/IsStringValueRecord.js +3 -21
  22. package/bin/platformRbac/dtos/CreateTenantUserRequest.d.ts +4 -0
  23. package/bin/platformRbac/dtos/CreateTenantUserRequest.js +9 -0
  24. package/bin/platformRbac/dtos/UpdateTenantUserRequest.d.ts +5 -2
  25. package/bin/platformRbac/dtos/UpdateTenantUserRequest.js +10 -2
  26. package/bin/retailCatalog/dtos/Product.d.ts +6 -0
  27. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.d.ts +9 -0
  28. package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.js +30 -0
  29. package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.d.ts +2 -0
  30. package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.js +6 -0
  31. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.d.ts +9 -0
  32. package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.js +30 -0
  33. package/bin/retailCatalog/dtos/requests/HoldStockRequest.d.ts +12 -0
  34. package/bin/retailCatalog/dtos/requests/HoldStockRequest.js +47 -0
  35. package/bin/retailCatalog/dtos/requests/ReserveStockRequest.d.ts +3 -0
  36. package/bin/retailCatalog/dtos/requests/ReserveStockRequest.js +6 -0
  37. package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.d.ts +2 -0
  38. package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.js +6 -0
  39. package/bin/retailCatalog/dtos/responses/GetInventorySkuResponse.d.ts +3 -0
  40. package/bin/retailCatalog/dtos/responses/HoldStockResponse.d.ts +8 -0
  41. package/bin/retailCatalog/dtos/responses/HoldStockResponse.js +2 -0
  42. package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +5 -1
  43. package/bin/retailCatalog/enums/InventoryInboundMessageTypeEnum.d.ts +5 -0
  44. package/bin/retailCatalog/enums/InventoryInboundMessageTypeEnum.js +9 -0
  45. package/bin/retailCatalog/enums/InventoryItemStatusEnum.d.ts +4 -2
  46. package/bin/retailCatalog/enums/InventoryItemStatusEnum.js +3 -1
  47. package/bin/retailCatalog/enums/ReservePurposeEnum.d.ts +4 -0
  48. package/bin/retailCatalog/enums/ReservePurposeEnum.js +8 -0
  49. package/bin/retailCatalog/events/InventoryInboundMessage.d.ts +4 -0
  50. package/bin/retailCatalog/events/InventoryInboundMessage.js +2 -0
  51. package/bin/retailCatalog/events/InventoryReturnMessage.d.ts +12 -0
  52. package/bin/retailCatalog/events/InventoryReturnMessage.js +42 -0
  53. package/bin/retailCatalog/events/InventorySaleAppliedMessage.d.ts +12 -0
  54. package/bin/retailCatalog/events/InventorySaleAppliedMessage.js +42 -0
  55. package/bin/retailCatalog/index.d.ts +7 -0
  56. package/bin/retailCatalog/index.js +8 -0
  57. package/bin/retailPromotions/constants/bonusPreMoney.d.ts +7 -0
  58. package/bin/retailPromotions/constants/bonusPreMoney.js +21 -0
  59. package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.d.ts +3 -0
  60. package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +23 -0
  61. package/bin/retailPromotions/constants/salePromotionTransitions.d.ts +7 -0
  62. package/bin/retailPromotions/constants/salePromotionTransitions.js +13 -0
  63. package/bin/retailPromotions/dtos/DiscountCode.d.ts +35 -0
  64. package/bin/retailPromotions/dtos/DiscountCode.js +2 -0
  65. package/bin/retailPromotions/dtos/DiscountValue.d.ts +7 -0
  66. package/bin/retailPromotions/dtos/DiscountValue.js +2 -0
  67. package/bin/retailPromotions/dtos/LinePromotionDiscount.d.ts +10 -0
  68. package/bin/retailPromotions/dtos/LinePromotionDiscount.js +2 -0
  69. package/bin/retailPromotions/dtos/SalePromotion.d.ts +35 -0
  70. package/bin/retailPromotions/dtos/SalePromotion.js +2 -0
  71. package/bin/retailPromotions/dtos/SalePromotionLevers.d.ts +58 -0
  72. package/bin/retailPromotions/dtos/SalePromotionLevers.js +2 -0
  73. package/bin/retailPromotions/dtos/SalePromotionLogEntry.d.ts +18 -0
  74. package/bin/retailPromotions/dtos/SalePromotionLogEntry.js +2 -0
  75. package/bin/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.d.ts +6 -0
  76. package/bin/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.js +30 -0
  77. package/bin/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.d.ts +6 -0
  78. package/bin/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.js +30 -0
  79. package/bin/retailPromotions/dtos/requests/CreateDiscountCodeRequest.d.ts +17 -0
  80. package/bin/retailPromotions/dtos/requests/CreateDiscountCodeRequest.js +78 -0
  81. package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.d.ts +19 -0
  82. package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.js +83 -0
  83. package/bin/retailPromotions/dtos/requests/DiscountValueInput.d.ts +8 -0
  84. package/bin/retailPromotions/dtos/requests/DiscountValueInput.js +36 -0
  85. package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.d.ts +5 -0
  86. package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.js +31 -0
  87. package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.d.ts +11 -0
  88. package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.js +48 -0
  89. package/bin/retailPromotions/dtos/requests/ListDiscountCodesQuery.d.ts +10 -0
  90. package/bin/retailPromotions/dtos/requests/ListDiscountCodesQuery.js +49 -0
  91. package/bin/retailPromotions/dtos/requests/ListSalePromotionLogQuery.d.ts +8 -0
  92. package/bin/retailPromotions/dtos/requests/ListSalePromotionLogQuery.js +46 -0
  93. package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.d.ts +7 -0
  94. package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.js +40 -0
  95. package/bin/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.d.ts +4 -0
  96. package/bin/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.js +24 -0
  97. package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.d.ts +40 -0
  98. package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.js +166 -0
  99. package/bin/retailPromotions/dtos/requests/SalePromotionScopeInput.d.ts +14 -0
  100. package/bin/retailPromotions/dtos/requests/SalePromotionScopeInput.js +65 -0
  101. package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.d.ts +19 -0
  102. package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.js +85 -0
  103. package/bin/retailPromotions/dtos/responses/DiscountCodeResponses.d.ts +7 -0
  104. package/bin/retailPromotions/dtos/responses/DiscountCodeResponses.js +2 -0
  105. package/bin/retailPromotions/dtos/responses/SalePromotionCrudResponses.d.ts +30 -0
  106. package/bin/retailPromotions/dtos/responses/SalePromotionCrudResponses.js +2 -0
  107. package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +102 -0
  108. package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.js +2 -0
  109. package/bin/retailPromotions/enums/CouponScopeEnum.d.ts +11 -0
  110. package/bin/retailPromotions/enums/CouponScopeEnum.js +15 -0
  111. package/bin/retailPromotions/enums/CouponStatusEnum.d.ts +11 -0
  112. package/bin/retailPromotions/enums/CouponStatusEnum.js +15 -0
  113. package/bin/retailPromotions/enums/DiscountCodeOriginEnum.d.ts +8 -0
  114. package/bin/retailPromotions/enums/DiscountCodeOriginEnum.js +12 -0
  115. package/bin/retailPromotions/enums/DiscountCodeStatusEnum.d.ts +11 -0
  116. package/bin/retailPromotions/enums/DiscountCodeStatusEnum.js +15 -0
  117. package/bin/retailPromotions/enums/DiscountCodeTransitionEnum.d.ts +7 -0
  118. package/bin/retailPromotions/enums/DiscountCodeTransitionEnum.js +11 -0
  119. package/bin/retailPromotions/enums/DiscountTypeEnum.d.ts +8 -0
  120. package/bin/retailPromotions/enums/DiscountTypeEnum.js +12 -0
  121. package/bin/retailPromotions/enums/EntitlementKindEnum.d.ts +9 -0
  122. package/bin/retailPromotions/enums/EntitlementKindEnum.js +13 -0
  123. package/bin/retailPromotions/enums/EntitlementStateEnum.d.ts +13 -0
  124. package/bin/retailPromotions/enums/EntitlementStateEnum.js +17 -0
  125. package/bin/retailPromotions/enums/GiftModeEnum.d.ts +8 -0
  126. package/bin/retailPromotions/enums/GiftModeEnum.js +12 -0
  127. package/bin/retailPromotions/enums/PromotionAppliesToEnum.d.ts +9 -0
  128. package/bin/retailPromotions/enums/PromotionAppliesToEnum.js +13 -0
  129. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +23 -0
  130. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +27 -0
  131. package/bin/retailPromotions/enums/SaleCouponAvailabilityEnum.d.ts +10 -0
  132. package/bin/retailPromotions/enums/SaleCouponAvailabilityEnum.js +14 -0
  133. package/bin/retailPromotions/enums/SalePromotionLeverEnum.d.ts +13 -0
  134. package/bin/retailPromotions/enums/SalePromotionLeverEnum.js +17 -0
  135. package/bin/retailPromotions/enums/SalePromotionLogEventEnum.d.ts +14 -0
  136. package/bin/retailPromotions/enums/SalePromotionLogEventEnum.js +18 -0
  137. package/bin/retailPromotions/enums/SalePromotionLogFormatEnum.d.ts +5 -0
  138. package/bin/retailPromotions/enums/SalePromotionLogFormatEnum.js +9 -0
  139. package/bin/retailPromotions/enums/SalePromotionPhaseEnum.d.ts +11 -0
  140. package/bin/retailPromotions/enums/SalePromotionPhaseEnum.js +15 -0
  141. package/bin/retailPromotions/enums/SalePromotionStatusEnum.d.ts +12 -0
  142. package/bin/retailPromotions/enums/SalePromotionStatusEnum.js +16 -0
  143. package/bin/retailPromotions/enums/SalePromotionTransitionEnum.d.ts +13 -0
  144. package/bin/retailPromotions/enums/SalePromotionTransitionEnum.js +17 -0
  145. package/bin/retailPromotions/helpers/computeBudgetUsedBps.d.ts +3 -0
  146. package/bin/retailPromotions/helpers/computeBudgetUsedBps.js +15 -0
  147. package/bin/retailPromotions/helpers/resolveSalePromotionTransition.d.ts +4 -0
  148. package/bin/retailPromotions/helpers/resolveSalePromotionTransition.js +9 -0
  149. package/bin/retailPromotions/index.d.ts +46 -0
  150. package/bin/retailPromotions/index.js +67 -0
  151. package/bin/retailWizard/dtos/Sale.d.ts +8 -8
  152. package/bin/retailWizard/dtos/SaleLine.d.ts +4 -1
  153. package/bin/retailWizard/dtos/WizardFinancingPreview.d.ts +11 -0
  154. package/bin/retailWizard/dtos/WizardFinancingPreview.js +2 -0
  155. package/bin/retailWizard/dtos/WizardGiftLine.d.ts +8 -0
  156. package/bin/retailWizard/dtos/WizardGiftLine.js +2 -0
  157. package/bin/retailWizard/dtos/WizardLinePromotionFields.d.ts +15 -0
  158. package/bin/retailWizard/dtos/WizardLinePromotionFields.js +2 -0
  159. package/bin/retailWizard/dtos/WizardReservedLine.d.ts +4 -1
  160. package/bin/retailWizard/dtos/WizardSession.d.ts +10 -0
  161. package/bin/retailWizard/dtos/requests/AddProductRequest.d.ts +2 -0
  162. package/bin/retailWizard/dtos/requests/AddProductRequest.js +7 -0
  163. package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +15 -0
  164. package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +58 -0
  165. package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.d.ts +9 -0
  166. package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.js +36 -0
  167. package/bin/retailWizard/dtos/requests/PinSessionPiecesRequest.d.ts +14 -0
  168. package/bin/retailWizard/dtos/requests/PinSessionPiecesRequest.js +60 -0
  169. package/bin/retailWizard/dtos/requests/QuoteRequest.d.ts +1 -1
  170. package/bin/retailWizard/dtos/requests/SetAnchorGiftsRequest.d.ts +14 -0
  171. package/bin/retailWizard/dtos/requests/SetAnchorGiftsRequest.js +56 -0
  172. package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.d.ts +29 -0
  173. package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.js +2 -0
  174. package/bin/retailWizard/dtos/responses/SetAnchorGiftsResponse.d.ts +5 -0
  175. package/bin/retailWizard/dtos/responses/SetAnchorGiftsResponse.js +2 -0
  176. package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.d.ts +5 -0
  177. package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.js +9 -0
  178. package/bin/retailWizard/enums/WizardLineRoleEnum.d.ts +4 -0
  179. package/bin/retailWizard/enums/WizardLineRoleEnum.js +8 -0
  180. package/bin/retailWizard/events/RetailCommissionInboundMessage.d.ts +19 -0
  181. package/bin/retailWizard/events/RetailCommissionInboundMessage.js +78 -0
  182. package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +40 -0
  183. package/bin/retailWizard/events/RetailPromotionInboundMessage.js +160 -0
  184. package/bin/retailWizard/events/SaleCompletedV1.d.ts +2 -3
  185. package/bin/retailWizard/index.d.ts +13 -0
  186. package/bin/retailWizard/index.js +14 -0
  187. package/package.json +1 -1
  188. package/src/common/index.ts +3 -0
  189. package/src/common/validators/IsStringValueRecord.ts +13 -0
  190. package/src/index.ts +3 -0
  191. package/src/messagesConnector/validators/IsStringValueRecord.ts +2 -12
  192. package/src/platformRbac/dtos/CreateTenantUserRequest.ts +8 -1
  193. package/src/platformRbac/dtos/UpdateTenantUserRequest.ts +9 -3
  194. package/src/retailCatalog/dtos/Product.ts +6 -0
  195. package/src/retailCatalog/dtos/requests/BackofficeCreateProductRequest.ts +30 -1
  196. package/src/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.ts +6 -0
  197. package/src/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.ts +30 -1
  198. package/src/retailCatalog/dtos/requests/HoldStockRequest.ts +28 -0
  199. package/src/retailCatalog/dtos/requests/ReserveStockRequest.ts +7 -1
  200. package/src/retailCatalog/dtos/requests/SuggestPiecesQuery.ts +7 -1
  201. package/src/retailCatalog/dtos/responses/GetInventorySkuResponse.ts +3 -0
  202. package/src/retailCatalog/dtos/responses/HoldStockResponse.ts +9 -0
  203. package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +6 -1
  204. package/src/retailCatalog/enums/InventoryInboundMessageTypeEnum.ts +5 -0
  205. package/src/retailCatalog/enums/InventoryItemStatusEnum.ts +3 -1
  206. package/src/retailCatalog/enums/ReservePurposeEnum.ts +4 -0
  207. package/src/retailCatalog/events/InventoryInboundMessage.ts +5 -0
  208. package/src/retailCatalog/events/InventoryReturnMessage.ts +26 -0
  209. package/src/retailCatalog/events/InventorySaleAppliedMessage.ts +26 -0
  210. package/src/retailCatalog/index.ts +9 -0
  211. package/src/retailPromotions/constants/bonusPreMoney.ts +19 -0
  212. package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +21 -0
  213. package/src/retailPromotions/constants/salePromotionTransitions.ts +11 -0
  214. package/src/retailPromotions/dtos/DiscountCode.ts +36 -0
  215. package/src/retailPromotions/dtos/DiscountValue.ts +8 -0
  216. package/src/retailPromotions/dtos/LinePromotionDiscount.ts +10 -0
  217. package/src/retailPromotions/dtos/SalePromotion.ts +36 -0
  218. package/src/retailPromotions/dtos/SalePromotionLevers.ts +67 -0
  219. package/src/retailPromotions/dtos/SalePromotionLogEntry.ts +19 -0
  220. package/src/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.ts +15 -0
  221. package/src/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.ts +15 -0
  222. package/src/retailPromotions/dtos/requests/CreateDiscountCodeRequest.ts +71 -0
  223. package/src/retailPromotions/dtos/requests/CreateSalePromotionRequest.ts +73 -0
  224. package/src/retailPromotions/dtos/requests/DiscountValueInput.ts +21 -0
  225. package/src/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.ts +16 -0
  226. package/src/retailPromotions/dtos/requests/HoldSalePromotionRequest.ts +32 -0
  227. package/src/retailPromotions/dtos/requests/ListDiscountCodesQuery.ts +33 -0
  228. package/src/retailPromotions/dtos/requests/ListSalePromotionLogQuery.ts +29 -0
  229. package/src/retailPromotions/dtos/requests/ListSalePromotionsQuery.ts +24 -0
  230. package/src/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.ts +10 -0
  231. package/src/retailPromotions/dtos/requests/SalePromotionLeversInput.ts +136 -0
  232. package/src/retailPromotions/dtos/requests/SalePromotionScopeInput.ts +45 -0
  233. package/src/retailPromotions/dtos/requests/UpdateSalePromotionRequest.ts +74 -0
  234. package/src/retailPromotions/dtos/responses/DiscountCodeResponses.ts +9 -0
  235. package/src/retailPromotions/dtos/responses/SalePromotionCrudResponses.ts +35 -0
  236. package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +115 -0
  237. package/src/retailPromotions/enums/CouponScopeEnum.ts +11 -0
  238. package/src/retailPromotions/enums/CouponStatusEnum.ts +11 -0
  239. package/src/retailPromotions/enums/DiscountCodeOriginEnum.ts +8 -0
  240. package/src/retailPromotions/enums/DiscountCodeStatusEnum.ts +11 -0
  241. package/src/retailPromotions/enums/DiscountCodeTransitionEnum.ts +7 -0
  242. package/src/retailPromotions/enums/DiscountTypeEnum.ts +8 -0
  243. package/src/retailPromotions/enums/EntitlementKindEnum.ts +9 -0
  244. package/src/retailPromotions/enums/EntitlementStateEnum.ts +13 -0
  245. package/src/retailPromotions/enums/GiftModeEnum.ts +8 -0
  246. package/src/retailPromotions/enums/PromotionAppliesToEnum.ts +9 -0
  247. package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +23 -0
  248. package/src/retailPromotions/enums/SaleCouponAvailabilityEnum.ts +10 -0
  249. package/src/retailPromotions/enums/SalePromotionLeverEnum.ts +13 -0
  250. package/src/retailPromotions/enums/SalePromotionLogEventEnum.ts +14 -0
  251. package/src/retailPromotions/enums/SalePromotionLogFormatEnum.ts +5 -0
  252. package/src/retailPromotions/enums/SalePromotionPhaseEnum.ts +11 -0
  253. package/src/retailPromotions/enums/SalePromotionStatusEnum.ts +12 -0
  254. package/src/retailPromotions/enums/SalePromotionTransitionEnum.ts +13 -0
  255. package/src/retailPromotions/helpers/computeBudgetUsedBps.ts +15 -0
  256. package/src/retailPromotions/helpers/resolveSalePromotionTransition.ts +12 -0
  257. package/src/retailPromotions/index.ts +55 -0
  258. package/src/retailWizard/dtos/Sale.ts +8 -8
  259. package/src/retailWizard/dtos/SaleLine.ts +4 -1
  260. package/src/retailWizard/dtos/WizardFinancingPreview.ts +12 -0
  261. package/src/retailWizard/dtos/WizardGiftLine.ts +9 -0
  262. package/src/retailWizard/dtos/WizardLinePromotionFields.ts +16 -0
  263. package/src/retailWizard/dtos/WizardReservedLine.ts +4 -1
  264. package/src/retailWizard/dtos/WizardSession.ts +10 -0
  265. package/src/retailWizard/dtos/requests/AddProductRequest.ts +8 -1
  266. package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +45 -0
  267. package/src/retailWizard/dtos/requests/ApplySalePromotionRequest.ts +24 -0
  268. package/src/retailWizard/dtos/requests/PinSessionPiecesRequest.ts +40 -0
  269. package/src/retailWizard/dtos/requests/QuoteRequest.ts +1 -1
  270. package/src/retailWizard/dtos/requests/SetAnchorGiftsRequest.ts +40 -0
  271. package/src/retailWizard/dtos/responses/PrivateWizardSessionResponse.ts +30 -0
  272. package/src/retailWizard/dtos/responses/SetAnchorGiftsResponse.ts +6 -0
  273. package/src/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.ts +5 -0
  274. package/src/retailWizard/enums/WizardLineRoleEnum.ts +4 -0
  275. package/src/retailWizard/events/RetailCommissionInboundMessage.ts +54 -0
  276. package/src/retailWizard/events/RetailPromotionInboundMessage.ts +138 -0
  277. package/src/retailWizard/events/SaleCompletedV1.ts +2 -3
  278. package/src/retailWizard/index.ts +15 -0
@@ -0,0 +1,151 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import {
5
+ ChangeSalePromotionStatusRequest,
6
+ CreateSalePromotionRequest,
7
+ DiscountTypeEnum,
8
+ ListSalePromotionLogQuery,
9
+ ListSalePromotionsQuery,
10
+ PromotionAppliesToEnum,
11
+ SalePromotionTransitionEnum,
12
+ UpdateSalePromotionRequest,
13
+ } from '../../../src/retailPromotions/index';
14
+
15
+ type Dto = new () => object;
16
+
17
+ /** Rutas de propiedad con error, incluidas las anidadas (`levers.gift.skus`). */
18
+ const errorPaths = async (dtoClass: Dto, raw: Record<string, unknown>): Promise<string[]> => {
19
+ const instance = plainToInstance(dtoClass, raw);
20
+ const errors = await validate(instance as object);
21
+ const walk = (list: typeof errors, prefix: string): string[] =>
22
+ list.flatMap((e) => {
23
+ const path = prefix ? `${prefix}.${e.property}` : e.property;
24
+ return e.children && e.children.length > 0 ? walk(e.children, path) : [path];
25
+ });
26
+ return walk(errors, '');
27
+ };
28
+
29
+ const base = {
30
+ name: 'Regalo de audífonos',
31
+ customerText: 'Llévate unos audífonos gratis',
32
+ levers: { deviceDiscount: { type: 'AMOUNT', amountCents: 50000 } },
33
+ };
34
+
35
+ describe('CreateSalePromotionRequest', () => {
36
+ it('valida el alta mínima y deja appliesTo ausente para que el lambda ponga el default', async () => {
37
+ const dto = plainToInstance(CreateSalePromotionRequest, base);
38
+ expect(await validate(dto as object)).toHaveLength(0);
39
+ expect(dto.appliesTo).toBeUndefined();
40
+ expect(dto.levers.deviceDiscount?.type).toBe(DiscountTypeEnum.AMOUNT);
41
+ });
42
+
43
+ it('exige nombre no vacío y palancas', async () => {
44
+ const paths = await errorPaths(CreateSalePromotionRequest, { customerText: 'x', name: '' });
45
+ expect(paths).toEqual(expect.arrayContaining(['name', 'levers']));
46
+ });
47
+
48
+ it('rechaza porcentaje en puntos base con decimales', async () => {
49
+ const paths = await errorPaths(CreateSalePromotionRequest, {
50
+ ...base,
51
+ levers: { deviceDiscount: { type: 'PERCENTAGE', percentBps: 12.5 } },
52
+ });
53
+ expect(paths).toContain('levers.deviceDiscount.percentBps');
54
+ });
55
+
56
+ it('pide el campo que corresponde al tipo de descuento', async () => {
57
+ const amount = await errorPaths(CreateSalePromotionRequest, {
58
+ ...base,
59
+ levers: { deviceDiscount: { type: 'AMOUNT', percentBps: 1000 } },
60
+ });
61
+ expect(amount).toContain('levers.deviceDiscount.amountCents');
62
+ const percent = await errorPaths(CreateSalePromotionRequest, {
63
+ ...base,
64
+ levers: { deviceDiscount: { type: 'PERCENTAGE', percentBps: 1000 } },
65
+ });
66
+ expect(percent).toHaveLength(0);
67
+ });
68
+
69
+ it('regalo por lista exige SKUs; por gama exige categoría y precio máximo', async () => {
70
+ const list = await errorPaths(CreateSalePromotionRequest, {
71
+ ...base,
72
+ levers: { gift: { mode: 'LIST', chooseCount: 1 } },
73
+ });
74
+ expect(list).toContain('levers.gift.skus');
75
+ const range = await errorPaths(CreateSalePromotionRequest, {
76
+ ...base,
77
+ levers: { gift: { mode: 'RANGE', chooseCount: 1 } },
78
+ });
79
+ expect(range).toEqual(expect.arrayContaining(['levers.gift.category', 'levers.gift.maxPriceCents']));
80
+ const ok = await errorPaths(CreateSalePromotionRequest, {
81
+ ...base,
82
+ levers: { gift: { mode: 'RANGE', category: 'AUDIO', maxPriceCents: 30000, chooseCount: 1 } },
83
+ });
84
+ expect(ok).toHaveLength(0);
85
+ });
86
+
87
+ it('valida las 7 palancas juntas', async () => {
88
+ const paths = await errorPaths(CreateSalePromotionRequest, {
89
+ ...base,
90
+ levers: {
91
+ deviceDiscount: { type: 'PERCENTAGE', percentBps: 1000 },
92
+ accessoryDiscount: { percentBps: 2000, maxAmountCents: null },
93
+ gift: { mode: 'LIST', skus: ['SKU-1'], chooseCount: 1 },
94
+ coupon: { type: 'AMOUNT', amountCents: 20000, validityDays: 30, scope: 'CHAIN' },
95
+ airtime: { rechargeCount: 3, rechargeAmountCents: 10000 },
96
+ extendedWarranty: { extraMonths: 6, costCents: 45000 },
97
+ walletBonus: { amountCents: 15000 },
98
+ },
99
+ appliesTo: 'ANY_FINANCIER',
100
+ scope: { tiers: ['HIGH'], productCategories: ['PHONE'], skus: null, storeIds: ['S1'] },
101
+ segmentation: { segmentIds: ['SEG_GOLD'], requiresActiveSurekeepCredit: true },
102
+ budgetMaxCents: null,
103
+ validFrom: '2026-10-01',
104
+ validUntil: null,
105
+ oncePerCustomer: true,
106
+ });
107
+ expect(paths).toHaveLength(0);
108
+ });
109
+
110
+ it('rechaza cupón sin alcance válido, appliesTo desconocido y fecha no ISO', async () => {
111
+ const paths = await errorPaths(CreateSalePromotionRequest, {
112
+ ...base,
113
+ levers: { coupon: { type: 'AMOUNT', amountCents: 100, validityDays: 30, scope: 'WORLD' } },
114
+ appliesTo: 'ANY',
115
+ validUntil: 'el viernes',
116
+ scope: { tiers: ['GIGANTE'] },
117
+ });
118
+ expect(paths).toEqual(
119
+ expect.arrayContaining(['levers.coupon.scope', 'appliesTo', 'validUntil', 'scope.tiers']),
120
+ );
121
+ });
122
+ });
123
+
124
+ describe('UpdateSalePromotionRequest', () => {
125
+ it('el parche vacío es válido y un campo mal tipado no', async () => {
126
+ expect(await errorPaths(UpdateSalePromotionRequest, {})).toHaveLength(0);
127
+ expect(await errorPaths(UpdateSalePromotionRequest, { appliesTo: PromotionAppliesToEnum.SUREKEEP_ONLY })).toHaveLength(0);
128
+ expect(await errorPaths(UpdateSalePromotionRequest, { budgetMaxCents: 10.5 })).toContain('budgetMaxCents');
129
+ });
130
+ });
131
+
132
+ describe('ChangeSalePromotionStatusRequest', () => {
133
+ it('acepta una transición del enum y rechaza un estado', async () => {
134
+ expect(await errorPaths(ChangeSalePromotionStatusRequest, { transition: SalePromotionTransitionEnum.PAUSE, reason: 'fin de stock' })).toHaveLength(0);
135
+ expect(await errorPaths(ChangeSalePromotionStatusRequest, { transition: 'ACTIVE' })).toContain('transition');
136
+ });
137
+ });
138
+
139
+ describe('queries paginadas', () => {
140
+ it('convierte limit del query string y lo acota', async () => {
141
+ const dto = plainToInstance(ListSalePromotionsQuery, { limit: '25', status: 'ARCHIVED' });
142
+ expect(await validate(dto as object)).toHaveLength(0);
143
+ expect(dto.limit).toBe(25);
144
+ expect(await errorPaths(ListSalePromotionsQuery, { limit: '500' })).toContain('limit');
145
+ });
146
+
147
+ it('el log acepta formato CSV y rechaza otro', async () => {
148
+ expect(await errorPaths(ListSalePromotionLogQuery, { format: 'CSV', promotionId: 'p1' })).toHaveLength(0);
149
+ expect(await errorPaths(ListSalePromotionLogQuery, { format: 'XLSX' })).toContain('format');
150
+ });
151
+ });
@@ -0,0 +1,189 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import {
5
+ ApplyLinePromotionRequest,
6
+ ApplySalePromotionRequest,
7
+ LinePaymentTypeEnum,
8
+ PaymentMethodEnum,
9
+ SalePaymentMethodEnum,
10
+ WizardLineRoleEnum,
11
+ WizardLineStatusEnum,
12
+ WizardStateEnum,
13
+ WizardStepEnum,
14
+ SetAnchorGiftsRequest,
15
+ type SetAnchorGiftsResponse,
16
+ type WizardGiftLine,
17
+ type PrivateWizardSessionResponse,
18
+ type WizardReservedLine,
19
+ } from '../../../src/retailWizard';
20
+
21
+ function paths(errors: ValidationError[], prefix = ''): string[] {
22
+ return errors.flatMap((e) => {
23
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
24
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
25
+ });
26
+ }
27
+
28
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
29
+ return paths(validateSync(plainToInstance(cls, payload) as object));
30
+ }
31
+
32
+ describe('ApplyLinePromotionRequest', () => {
33
+ const ok = {
34
+ promotionId: 'promo-1',
35
+ promotionVersion: 2,
36
+ discountCode: 'VERANO',
37
+ discountCents: 50000,
38
+ originalPriceCents: 349900,
39
+ };
40
+
41
+ it('acepta los campos de promo de la línea', () => expect(errorsOf(ApplyLinePromotionRequest, ok)).toEqual([]));
42
+ it('acepta un cuerpo vacío (la línea queda sin promo)', () =>
43
+ expect(errorsOf(ApplyLinePromotionRequest, {})).toEqual([]));
44
+ it('acepta solo el código con descuento 0 (pendiente de revalidar)', () =>
45
+ expect(errorsOf(ApplyLinePromotionRequest, { discountCode: 'VERANO', discountCents: 0 })).toEqual([]));
46
+ it('exige la versión si viene la promo', () =>
47
+ expect(errorsOf(ApplyLinePromotionRequest, { promotionId: 'promo-1' })).toContain('promotionVersion'));
48
+ it('exige la promo si viene la versión', () =>
49
+ expect(errorsOf(ApplyLinePromotionRequest, { promotionVersion: 1 })).toContain('promotionId'));
50
+ it('rechaza la versión 0', () =>
51
+ expect(errorsOf(ApplyLinePromotionRequest, { ...ok, promotionVersion: 0 })).toContain('promotionVersion'));
52
+ it('acepta la versión 1', () =>
53
+ expect(errorsOf(ApplyLinePromotionRequest, { ...ok, promotionVersion: 1 })).toEqual([]));
54
+ it('rechaza promotionId vacío', () =>
55
+ expect(errorsOf(ApplyLinePromotionRequest, { ...ok, promotionId: '' })).toContain('promotionId'));
56
+ it('rechaza discountCode vacío', () =>
57
+ expect(errorsOf(ApplyLinePromotionRequest, { ...ok, discountCode: '' })).toContain('discountCode'));
58
+ it('rechaza montos negativos o no enteros', () =>
59
+ expect(errorsOf(ApplyLinePromotionRequest, { ...ok, discountCents: -1, originalPriceCents: 1.5 })).toEqual(
60
+ expect.arrayContaining(['discountCents', 'originalPriceCents']),
61
+ ));
62
+ });
63
+
64
+ describe('SetAnchorGiftsRequest', () => {
65
+ const ok = { promotionId: 'promo-1', promotionVersion: 1, items: [{ sku: 'CASE-1' }] };
66
+
67
+ it('acepta un regalo con su promo', () => expect(errorsOf(SetAnchorGiftsRequest, ok)).toEqual([]));
68
+ it('acepta dos regalos', () =>
69
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, items: [{ sku: 'CASE-1' }, { sku: 'MICA-1' }] })).toEqual([]));
70
+ it('acepta items vacío con promo y versión en null (suelta todos)', () =>
71
+ expect(errorsOf(SetAnchorGiftsRequest, { promotionId: null, promotionVersion: null, items: [] })).toEqual([]));
72
+ it('exige promotionId si hay regalos', () =>
73
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, promotionId: null })).toContain('promotionId'));
74
+ it('exige promotionVersion si hay regalos', () =>
75
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, promotionVersion: null })).toContain('promotionVersion'));
76
+ it('rechaza la versión 0', () =>
77
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, promotionVersion: 0 })).toContain('promotionVersion'));
78
+ it('rechaza promotionId vacío', () =>
79
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, promotionId: '' })).toContain('promotionId'));
80
+ it('rechaza sku vacío', () =>
81
+ expect(errorsOf(SetAnchorGiftsRequest, { ...ok, items: [{ sku: '' }] })).toContain('items.0.sku'));
82
+ it('exige items como arreglo', () =>
83
+ expect(errorsOf(SetAnchorGiftsRequest, { promotionId: null, promotionVersion: null })).toContain('items'));
84
+ });
85
+
86
+ describe('ApplySalePromotionRequest', () => {
87
+ it('acepta la promo de venta', () =>
88
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: 'sp-1', salePromotionVersion: 1 })).toEqual([]));
89
+ it('acepta «Ninguna» con ambos en null', () =>
90
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: null, salePromotionVersion: null })).toEqual([]));
91
+ it('exige salePromotionId explícito (null o valor)', () =>
92
+ expect(errorsOf(ApplySalePromotionRequest, {})).toContain('salePromotionId'));
93
+ it('exige la versión si viene la promo', () =>
94
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: 'sp-1', salePromotionVersion: null })).toContain(
95
+ 'salePromotionVersion',
96
+ ));
97
+ it('rechaza versión sin promo', () =>
98
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: null, salePromotionVersion: 3 })).toContain(
99
+ 'salePromotionId',
100
+ ));
101
+ it('rechaza la versión 0', () =>
102
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: 'sp-1', salePromotionVersion: 0 })).toContain(
103
+ 'salePromotionVersion',
104
+ ));
105
+ it('rechaza salePromotionId vacío', () =>
106
+ expect(errorsOf(ApplySalePromotionRequest, { salePromotionId: '', salePromotionVersion: 1 })).toContain(
107
+ 'salePromotionId',
108
+ ));
109
+ });
110
+
111
+ describe('respuestas de los M2M de Promociones', () => {
112
+ const gift: WizardGiftLine = {
113
+ lineId: 'L2',
114
+ sku: 'CASE-1',
115
+ qty: 1,
116
+ status: WizardLineStatusEnum.PENDING,
117
+ reservationId: 'r-2',
118
+ expiresAt: '2026-09-28T20:30:00.000Z',
119
+ basePriceCents: 0,
120
+ listPriceCents: 35000,
121
+ priceSk: null,
122
+ eligibleFinanciers: [],
123
+ financeable: false,
124
+ paymentType: LinePaymentTypeEnum.CASH,
125
+ sellItemCost: null,
126
+ role: WizardLineRoleEnum.GIFT,
127
+ linkedLineId: 'L1',
128
+ promotionId: 'promo-1',
129
+ promotionVersion: 1,
130
+ originalPriceCents: 35000,
131
+ };
132
+
133
+ it('setAnchorGifts devuelve las líneas GIFT del ancla', () => {
134
+ const res: SetAnchorGiftsResponse = { lines: [gift, { ...gift, lineId: 'L3', sku: 'MICA-1' }] };
135
+ expect(res.lines.map((l) => l.linkedLineId)).toEqual(['L1', 'L1']);
136
+ });
137
+
138
+ it('una línea GIFT exige role GIFT, ancla y precio original', () => {
139
+ const { linkedLineId: _l, ...sinAncla } = gift;
140
+ const { originalPriceCents: _o, ...sinPrecio } = gift;
141
+ const { role: _r, ...sinRole } = gift;
142
+ // @ts-expect-error linkedLineId es obligatorio en un GIFT
143
+ const a: WizardGiftLine = sinAncla;
144
+ // @ts-expect-error originalPriceCents es obligatorio en un GIFT
145
+ const b: WizardGiftLine = sinPrecio;
146
+ // @ts-expect-error role GIFT es obligatorio
147
+ const c: WizardGiftLine = sinRole;
148
+ const line: WizardReservedLine = gift;
149
+ expect([a, b, c, line]).toHaveLength(4);
150
+ });
151
+
152
+ it('la lectura privada trae líneas, ancla, crédito, paso y promo de venta', () => {
153
+ const view: PrivateWizardSessionResponse = {
154
+ sessionId: 'ses-1',
155
+ retailerId: 'ret',
156
+ storeId: 'st',
157
+ retailCustomerId: null,
158
+ state: WizardStateEnum.ACTIVE,
159
+ currentStep: WizardStepEnum.S06,
160
+ creditId: null,
161
+ anchorLineId: 'L1',
162
+ salePromotionId: null,
163
+ salePromotionVersion: null,
164
+ lines: [{ ...gift, financedCents: 0 }],
165
+ };
166
+ expect(view.lines[0].financedCents).toBe(0);
167
+ });
168
+
169
+ it('la lectura privada expone directorio, folio, cobro de contado y forma de pago', () => {
170
+ const view: PrivateWizardSessionResponse = {
171
+ sessionId: 'ses-1',
172
+ retailerId: 'ret',
173
+ storeId: 'st',
174
+ retailCustomerId: 'rc-1',
175
+ state: WizardStateEnum.ACTIVE,
176
+ currentStep: WizardStepEnum.S06,
177
+ creditId: null,
178
+ anchorLineId: 'L1',
179
+ salePromotionId: null,
180
+ salePromotionVersion: null,
181
+ lines: [],
182
+ fiadoDirectoryId: 'dir-1',
183
+ folio: 'F-001',
184
+ paymentMethod: PaymentMethodEnum.CASH_SALE,
185
+ payment: { method: SalePaymentMethodEnum.CASH, amountCents: 349900, paidAt: '2026-09-28T20:00:00.000Z' },
186
+ };
187
+ expect(view.payment?.paidAt).toBe('2026-09-28T20:00:00.000Z');
188
+ });
189
+ });
@@ -0,0 +1,163 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import {
5
+ RetailCommissionInboundMessage,
6
+ RetailPromotionInboundMessage,
7
+ RetailPromotionInboundMessageTypeEnum,
8
+ WizardLineRoleEnum,
9
+ } from '../../../src/retailWizard';
10
+ import { ItemTypeEnum } from '../../../src/retailCatalog';
11
+
12
+ function paths(errors: ValidationError[], prefix = ''): string[] {
13
+ return errors.flatMap((e) => {
14
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
15
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
16
+ });
17
+ }
18
+
19
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
20
+ return paths(validateSync(plainToInstance(cls, payload) as object));
21
+ }
22
+
23
+ describe('RetailPromotionInboundMessageTypeEnum', () => {
24
+ it('distingue confirmación y cancelación', () => {
25
+ expect(Object.values(RetailPromotionInboundMessageTypeEnum)).toEqual(['SALE_CONFIRMED', 'SALE_CANCELLED']);
26
+ });
27
+ });
28
+
29
+ describe('RetailPromotionInboundMessage', () => {
30
+ const anchor = {
31
+ lineId: 'L1',
32
+ sku: 'PHONE-1',
33
+ qty: 1,
34
+ promotionId: 'promo-1',
35
+ promotionVersion: 2,
36
+ discountCode: 'VERANO',
37
+ discountCents: 50000,
38
+ originalPriceCents: 349900,
39
+ identifiers: ['356938035643809'],
40
+ };
41
+ const gift = {
42
+ lineId: 'L2',
43
+ sku: 'CASE-1',
44
+ qty: 1,
45
+ role: WizardLineRoleEnum.GIFT,
46
+ linkedLineId: 'L1',
47
+ originalPriceCents: 35000,
48
+ identifiers: [],
49
+ };
50
+ const ok = {
51
+ type: RetailPromotionInboundMessageTypeEnum.SALE_CONFIRMED,
52
+ tenantId: 't',
53
+ retailerId: 'ret',
54
+ storeId: 'st',
55
+ saleId: 'sale-1',
56
+ sessionId: 'ses-1',
57
+ customerId: 'cus-1',
58
+ soldAt: '2026-09-28T20:00:00.000Z',
59
+ occurredAt: '2026-09-28T20:00:00.000Z',
60
+ lines: [anchor, gift],
61
+ };
62
+
63
+ it('acepta una confirmación de contado', () => expect(errorsOf(RetailPromotionInboundMessage, ok)).toEqual([]));
64
+
65
+ it('acepta crédito, directorio y promo de venta', () =>
66
+ expect(
67
+ errorsOf(RetailPromotionInboundMessage, {
68
+ ...ok,
69
+ creditId: 'cr-1',
70
+ fiadoDirectoryId: 'dir-1',
71
+ salePromotionId: 'sp-1',
72
+ salePromotionVersion: 1,
73
+ }),
74
+ ).toEqual([]));
75
+
76
+ it('acepta una cancelación con returned por línea', () =>
77
+ expect(
78
+ errorsOf(RetailPromotionInboundMessage, {
79
+ ...ok,
80
+ type: RetailPromotionInboundMessageTypeEnum.SALE_CANCELLED,
81
+ lines: [{ ...anchor, returned: true }],
82
+ }),
83
+ ).toEqual([]));
84
+
85
+ it('exige la identidad de la venta', () => {
86
+ const { tenantId: _t, saleId: _s, sessionId: _se, customerId: _c, occurredAt: _o, ...rest } = ok;
87
+ expect(errorsOf(RetailPromotionInboundMessage, rest)).toEqual(
88
+ expect.arrayContaining(['tenantId', 'saleId', 'sessionId', 'customerId', 'occurredAt']),
89
+ );
90
+ });
91
+
92
+ it('rechaza un type fuera del enum', () =>
93
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, type: 'SALE_APPLIED' })).toContain('type'));
94
+
95
+ it('exige originalPriceCents e identifiers por línea', () => {
96
+ const { originalPriceCents: _o, identifiers: _i, ...line } = anchor;
97
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, lines: [line] })).toEqual(
98
+ expect.arrayContaining(['lines.0.originalPriceCents', 'lines.0.identifiers']),
99
+ );
100
+ });
101
+
102
+ it('rechaza un role fuera del enum', () =>
103
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, lines: [{ ...gift, role: 'ANCHOR' }] })).toContain(
104
+ 'lines.0.role',
105
+ ));
106
+
107
+ it('rechaza la versión 0 de la promo de línea y acepta la 1', () => {
108
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, lines: [{ ...anchor, promotionVersion: 0 }] })).toContain(
109
+ 'lines.0.promotionVersion',
110
+ );
111
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, lines: [{ ...anchor, promotionVersion: 1 }] })).toEqual([]);
112
+ });
113
+
114
+ it('rechaza la versión 0 de la promo de venta y acepta la 1', () => {
115
+ const sale = { ...ok, salePromotionId: 'sp-1' };
116
+ expect(errorsOf(RetailPromotionInboundMessage, { ...sale, salePromotionVersion: 0 })).toContain('salePromotionVersion');
117
+ expect(errorsOf(RetailPromotionInboundMessage, { ...sale, salePromotionVersion: 1 })).toEqual([]);
118
+ });
119
+
120
+ it.each(['promotionId', 'discountCode', 'linkedLineId'])('rechaza %s vacío en la línea', (field) =>
121
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, lines: [{ ...anchor, [field]: '' }] })).toContain(
122
+ `lines.0.${field}`,
123
+ ));
124
+
125
+ it('rechaza salePromotionId vacío y acepta null', () => {
126
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, salePromotionId: '' })).toContain('salePromotionId');
127
+ expect(errorsOf(RetailPromotionInboundMessage, { ...ok, salePromotionId: null })).toEqual([]);
128
+ });
129
+
130
+ it('no lleva paymentMethod: el crédito se reconoce por creditId', () => {
131
+ const msg = plainToInstance(RetailPromotionInboundMessage, ok);
132
+ expect('paymentMethod' in msg).toBe(false);
133
+ });
134
+ });
135
+
136
+ describe('RetailCommissionInboundMessage', () => {
137
+ const ok = {
138
+ tenantId: 't',
139
+ retailerId: 'ret',
140
+ storeId: 'st',
141
+ saleId: 'sale-1',
142
+ sessionId: 'ses-1',
143
+ soldAt: '2026-09-28T20:00:00.000Z',
144
+ lines: [
145
+ { sku: 'PHONE-1', qty: 1, netCents: 299900, itemType: ItemTypeEnum.PRODUCT },
146
+ { sku: 'SVC-1', qty: 1, netCents: 9900, itemType: ItemTypeEnum.SERVICE },
147
+ ],
148
+ };
149
+
150
+ it('acepta la venta con sus líneas', () => expect(errorsOf(RetailCommissionInboundMessage, ok)).toEqual([]));
151
+
152
+ it('exige la identidad de la venta', () => {
153
+ const { saleId: _s, soldAt: _so, ...rest } = ok;
154
+ expect(errorsOf(RetailCommissionInboundMessage, rest)).toEqual(expect.arrayContaining(['saleId', 'soldAt']));
155
+ });
156
+
157
+ it('rechaza sin líneas', () => expect(errorsOf(RetailCommissionInboundMessage, { ...ok, lines: [] })).toContain('lines'));
158
+
159
+ it('valida cada línea', () =>
160
+ expect(
161
+ errorsOf(RetailCommissionInboundMessage, { ...ok, lines: [{ sku: 'X', qty: 0, netCents: -1, itemType: 'KIT' }] }),
162
+ ).toEqual(expect.arrayContaining(['lines.0.qty', 'lines.0.netCents', 'lines.0.itemType'])));
163
+ });
@@ -0,0 +1,59 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import { AddProductRequest, PinSessionPiecesRequest, QuoteRequest } from '../../../src/retailWizard';
5
+
6
+ function paths(errors: ValidationError[], prefix = ''): string[] {
7
+ return errors.flatMap((e) => {
8
+ const p = prefix ? `${prefix}.${e.property}` : e.property;
9
+ return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
10
+ });
11
+ }
12
+
13
+ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
14
+ return paths(validateSync(plainToInstance(cls, payload) as object));
15
+ }
16
+
17
+ describe('AddProductRequest.replacesLineId', () => {
18
+ it('es opcional', () => expect(errorsOf(AddProductRequest, { sku: 'KIT-1' })).toEqual([]));
19
+ it('acepta la línea a reemplazar', () =>
20
+ expect(errorsOf(AddProductRequest, { sku: 'KIT-1', replacesLineId: 'L1' })).toEqual([]));
21
+ it('rechaza vacío', () =>
22
+ expect(errorsOf(AddProductRequest, { sku: 'KIT-1', replacesLineId: '' })).toContain('replacesLineId'));
23
+ });
24
+
25
+ describe('QuoteRequest.financedLineId', () => {
26
+ it('sigue siendo opcional', () => expect(errorsOf(QuoteRequest, { downPaymentCents: 0, termWeeks: 26 })).toEqual([]));
27
+ it('se sigue aceptando del front viejo', () =>
28
+ expect(errorsOf(QuoteRequest, { downPaymentCents: 0, termWeeks: 26, financedLineId: 'L1' })).toEqual([]));
29
+ });
30
+
31
+ describe('PinSessionPiecesRequest', () => {
32
+ const ok = {
33
+ lines: [
34
+ {
35
+ lineId: 'L1',
36
+ components: [
37
+ { sku: 'PHONE-1', identifiers: ['356938035643809'] },
38
+ { sku: 'SIM-1', identifiers: ['8952140'] },
39
+ ],
40
+ },
41
+ ],
42
+ };
43
+ it('acepta líneas con sus componentes', () => expect(errorsOf(PinSessionPiecesRequest, ok)).toEqual([]));
44
+ it('rechaza sin líneas', () => expect(errorsOf(PinSessionPiecesRequest, { lines: [] })).toContain('lines'));
45
+ it('exige lineId', () =>
46
+ expect(errorsOf(PinSessionPiecesRequest, { lines: [{ components: ok.lines[0].components }] })).toContain(
47
+ 'lines.0.lineId',
48
+ ));
49
+ it('rechaza una línea sin componentes', () =>
50
+ expect(errorsOf(PinSessionPiecesRequest, { lines: [{ lineId: 'L1', components: [] }] })).toContain(
51
+ 'lines.0.components',
52
+ ));
53
+ it('valida sku e identificadores de cada componente', () => {
54
+ const bad = { lines: [{ lineId: 'L1', components: [{ identifiers: [3] }] }] };
55
+ expect(errorsOf(PinSessionPiecesRequest, bad)).toEqual(
56
+ expect.arrayContaining(['lines.0.components.0.sku', 'lines.0.components.0.identifiers']),
57
+ );
58
+ });
59
+ });
@@ -0,0 +1,99 @@
1
+ import 'reflect-metadata';
2
+ import {
3
+ LinePaymentTypeEnum,
4
+ WizardLineRoleEnum,
5
+ WizardLineStatusEnum,
6
+ type Sale,
7
+ type SaleLine,
8
+ type WizardFinancingPreview,
9
+ type WizardLinePromotionFields,
10
+ type WizardReservedLine,
11
+ type WizardSession,
12
+ } from '../../../src/retailWizard';
13
+
14
+ const promoFields: WizardLinePromotionFields = {
15
+ role: WizardLineRoleEnum.GIFT,
16
+ promotionId: 'promo-1',
17
+ promotionVersion: 3,
18
+ linkedLineId: 'L1',
19
+ discountCode: 'VERANO',
20
+ discountCents: 50000,
21
+ originalPriceCents: 35000,
22
+ };
23
+
24
+ describe('WizardLineRoleEnum', () => {
25
+ it('solo trae GIFT; sin role es una línea normal', () => {
26
+ expect(Object.values(WizardLineRoleEnum)).toEqual(['GIFT']);
27
+ });
28
+ });
29
+
30
+ describe('línea de sesión con reparto y campos de Promociones', () => {
31
+ it('acepta financedCents y los 7 campos opcionales', () => {
32
+ const line: WizardReservedLine = {
33
+ lineId: 'L2',
34
+ sku: 'CASE-1',
35
+ qty: 1,
36
+ status: WizardLineStatusEnum.RESERVED,
37
+ reservationId: 'r-2',
38
+ expiresAt: '2026-09-28T20:30:00.000Z',
39
+ basePriceCents: 0,
40
+ listPriceCents: 35000,
41
+ priceSk: 'p',
42
+ eligibleFinanciers: [],
43
+ financeable: false,
44
+ paymentType: LinePaymentTypeEnum.CASH,
45
+ sellItemCost: 1,
46
+ financedCents: 0,
47
+ ...promoFields,
48
+ };
49
+ expect(line.role).toBe('GIFT');
50
+ expect(line.financedCents).toBe(0);
51
+ });
52
+ });
53
+
54
+ describe('línea de venta con reparto y campos de Promociones', () => {
55
+ it('acepta financedCents y los 7 campos opcionales', () => {
56
+ const line: SaleLine = {
57
+ lineId: 'L1',
58
+ sku: 'PHONE-1',
59
+ qty: 1,
60
+ basePriceCents: 349900,
61
+ listPriceCents: 349900,
62
+ priceSk: 'p',
63
+ paymentType: LinePaymentTypeEnum.CREDIT,
64
+ components: [{ sku: 'PHONE-1', qty: 1, identifiers: ['356938035643809'] }],
65
+ financedCents: 299900,
66
+ ...promoFields,
67
+ role: undefined,
68
+ };
69
+ expect(line.discountCents).toBe(50000);
70
+ });
71
+ });
72
+
73
+ describe('sesión de la saga', () => {
74
+ it('acepta piecesHeldAt, financingPreview, promo de venta y refundPendingCents', () => {
75
+ const preview: WizardFinancingPreview = { capCents: 89970, lines: [{ lineId: 'L2', financedCents: 35000 }] };
76
+ const session: Pick<
77
+ WizardSession,
78
+ 'piecesHeldAt' | 'financingPreview' | 'salePromotionId' | 'salePromotionVersion' | 'refundPendingCents'
79
+ > = {
80
+ piecesHeldAt: '2026-09-28T20:00:00.000Z',
81
+ financingPreview: preview,
82
+ salePromotionId: null,
83
+ salePromotionVersion: null,
84
+ refundPendingCents: 150000,
85
+ };
86
+ expect(session.financingPreview?.lines[0].financedCents).toBe(35000);
87
+ });
88
+ });
89
+
90
+ describe('venta de la saga', () => {
91
+ it('acepta promo de venta e inventoryViaQueue', () => {
92
+ const sale: Pick<Sale, 'salePromotionId' | 'salePromotionVersion' | 'inventoryViaQueue'> = {
93
+ salePromotionId: 'sp-1',
94
+ salePromotionVersion: 2,
95
+ inventoryViaQueue: true,
96
+ };
97
+ expect(sale.inventoryViaQueue).toBe(true);
98
+ });
99
+ });
@@ -1,4 +1,5 @@
1
1
  export * from './decorators/Trim';
2
+ export * from './validators/IsStringValueRecord';
2
3
  export * from './dtos/ProcessValidationResultDto';
3
4
  export * from './enums/ProcessValidationEnum';
4
5
  export * from './enums/ProcessResultEnum';
@@ -16,6 +16,8 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  //decorators
18
18
  __exportStar(require("./decorators/Trim"), exports);
19
+ //validators
20
+ __exportStar(require("./validators/IsStringValueRecord"), exports);
19
21
  //dtos
20
22
  __exportStar(require("./dtos/ProcessValidationResultDto"), exports);
21
23
  //Enums