@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.
- package/_test_/unit/common/IsStringValueRecord.test.ts +25 -0
- package/_test_/unit/platformRbac/dtos/tenantUserCustomFields.test.ts +55 -0
- package/_test_/unit/retailCatalog/catalogDebt.test.ts +123 -0
- package/_test_/unit/retailCatalog/catalogEnums.test.ts +2 -2
- package/_test_/unit/retailCatalog/ventaSaga.test.ts +152 -0
- package/_test_/unit/retailPromotions/computeBudgetUsedBps.test.ts +30 -0
- package/_test_/unit/retailPromotions/constants.test.ts +71 -0
- package/_test_/unit/retailPromotions/discountCodeAndWizardRequests.test.ts +84 -0
- package/_test_/unit/retailPromotions/salePromotionRequests.test.ts +151 -0
- package/_test_/unit/retailWizard/promotionM2mRequests.test.ts +189 -0
- package/_test_/unit/retailWizard/ventaSagaMessages.test.ts +163 -0
- package/_test_/unit/retailWizard/ventaSagaRequests.test.ts +59 -0
- package/_test_/unit/retailWizard/ventaSagaShapes.test.ts +99 -0
- package/bin/common/index.d.ts +1 -0
- package/bin/common/index.js +2 -0
- package/bin/common/validators/IsStringValueRecord.d.ts +5 -0
- package/bin/common/validators/IsStringValueRecord.js +25 -0
- package/bin/index.d.ts +1 -0
- package/bin/index.js +5 -2
- package/bin/messagesConnector/validators/IsStringValueRecord.d.ts +1 -5
- package/bin/messagesConnector/validators/IsStringValueRecord.js +3 -21
- package/bin/platformRbac/dtos/CreateTenantUserRequest.d.ts +4 -0
- package/bin/platformRbac/dtos/CreateTenantUserRequest.js +9 -0
- package/bin/platformRbac/dtos/UpdateTenantUserRequest.d.ts +5 -2
- package/bin/platformRbac/dtos/UpdateTenantUserRequest.js +10 -2
- package/bin/retailCatalog/dtos/Product.d.ts +6 -0
- package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.d.ts +9 -0
- package/bin/retailCatalog/dtos/requests/BackofficeCreateProductRequest.js +30 -0
- package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.d.ts +2 -0
- package/bin/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.js +6 -0
- package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.d.ts +9 -0
- package/bin/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.js +30 -0
- package/bin/retailCatalog/dtos/requests/HoldStockRequest.d.ts +12 -0
- package/bin/retailCatalog/dtos/requests/HoldStockRequest.js +47 -0
- package/bin/retailCatalog/dtos/requests/ReserveStockRequest.d.ts +3 -0
- package/bin/retailCatalog/dtos/requests/ReserveStockRequest.js +6 -0
- package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.d.ts +2 -0
- package/bin/retailCatalog/dtos/requests/SuggestPiecesQuery.js +6 -0
- package/bin/retailCatalog/dtos/responses/GetInventorySkuResponse.d.ts +3 -0
- package/bin/retailCatalog/dtos/responses/HoldStockResponse.d.ts +8 -0
- package/bin/retailCatalog/dtos/responses/HoldStockResponse.js +2 -0
- package/bin/retailCatalog/dtos/responses/StoreCatalogItem.d.ts +5 -1
- package/bin/retailCatalog/enums/InventoryInboundMessageTypeEnum.d.ts +5 -0
- package/bin/retailCatalog/enums/InventoryInboundMessageTypeEnum.js +9 -0
- package/bin/retailCatalog/enums/InventoryItemStatusEnum.d.ts +4 -2
- package/bin/retailCatalog/enums/InventoryItemStatusEnum.js +3 -1
- package/bin/retailCatalog/enums/ReservePurposeEnum.d.ts +4 -0
- package/bin/retailCatalog/enums/ReservePurposeEnum.js +8 -0
- package/bin/retailCatalog/events/InventoryInboundMessage.d.ts +4 -0
- package/bin/retailCatalog/events/InventoryInboundMessage.js +2 -0
- package/bin/retailCatalog/events/InventoryReturnMessage.d.ts +12 -0
- package/bin/retailCatalog/events/InventoryReturnMessage.js +42 -0
- package/bin/retailCatalog/events/InventorySaleAppliedMessage.d.ts +12 -0
- package/bin/retailCatalog/events/InventorySaleAppliedMessage.js +42 -0
- package/bin/retailCatalog/index.d.ts +7 -0
- package/bin/retailCatalog/index.js +8 -0
- package/bin/retailPromotions/constants/bonusPreMoney.d.ts +7 -0
- package/bin/retailPromotions/constants/bonusPreMoney.js +21 -0
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.d.ts +3 -0
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +23 -0
- package/bin/retailPromotions/constants/salePromotionTransitions.d.ts +7 -0
- package/bin/retailPromotions/constants/salePromotionTransitions.js +13 -0
- package/bin/retailPromotions/dtos/DiscountCode.d.ts +35 -0
- package/bin/retailPromotions/dtos/DiscountCode.js +2 -0
- package/bin/retailPromotions/dtos/DiscountValue.d.ts +7 -0
- package/bin/retailPromotions/dtos/DiscountValue.js +2 -0
- package/bin/retailPromotions/dtos/LinePromotionDiscount.d.ts +10 -0
- package/bin/retailPromotions/dtos/LinePromotionDiscount.js +2 -0
- package/bin/retailPromotions/dtos/SalePromotion.d.ts +35 -0
- package/bin/retailPromotions/dtos/SalePromotion.js +2 -0
- package/bin/retailPromotions/dtos/SalePromotionLevers.d.ts +58 -0
- package/bin/retailPromotions/dtos/SalePromotionLevers.js +2 -0
- package/bin/retailPromotions/dtos/SalePromotionLogEntry.d.ts +18 -0
- package/bin/retailPromotions/dtos/SalePromotionLogEntry.js +2 -0
- package/bin/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.d.ts +6 -0
- package/bin/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.js +30 -0
- package/bin/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.d.ts +6 -0
- package/bin/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.js +30 -0
- package/bin/retailPromotions/dtos/requests/CreateDiscountCodeRequest.d.ts +17 -0
- package/bin/retailPromotions/dtos/requests/CreateDiscountCodeRequest.js +78 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.d.ts +19 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.js +83 -0
- package/bin/retailPromotions/dtos/requests/DiscountValueInput.d.ts +8 -0
- package/bin/retailPromotions/dtos/requests/DiscountValueInput.js +36 -0
- package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.d.ts +5 -0
- package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.js +31 -0
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.d.ts +11 -0
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.js +48 -0
- package/bin/retailPromotions/dtos/requests/ListDiscountCodesQuery.d.ts +10 -0
- package/bin/retailPromotions/dtos/requests/ListDiscountCodesQuery.js +49 -0
- package/bin/retailPromotions/dtos/requests/ListSalePromotionLogQuery.d.ts +8 -0
- package/bin/retailPromotions/dtos/requests/ListSalePromotionLogQuery.js +46 -0
- package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.d.ts +7 -0
- package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.js +40 -0
- package/bin/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.d.ts +4 -0
- package/bin/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.js +24 -0
- package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.d.ts +40 -0
- package/bin/retailPromotions/dtos/requests/SalePromotionLeversInput.js +166 -0
- package/bin/retailPromotions/dtos/requests/SalePromotionScopeInput.d.ts +14 -0
- package/bin/retailPromotions/dtos/requests/SalePromotionScopeInput.js +65 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.d.ts +19 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.js +85 -0
- package/bin/retailPromotions/dtos/responses/DiscountCodeResponses.d.ts +7 -0
- package/bin/retailPromotions/dtos/responses/DiscountCodeResponses.js +2 -0
- package/bin/retailPromotions/dtos/responses/SalePromotionCrudResponses.d.ts +30 -0
- package/bin/retailPromotions/dtos/responses/SalePromotionCrudResponses.js +2 -0
- package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +102 -0
- package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.js +2 -0
- package/bin/retailPromotions/enums/CouponScopeEnum.d.ts +11 -0
- package/bin/retailPromotions/enums/CouponScopeEnum.js +15 -0
- package/bin/retailPromotions/enums/CouponStatusEnum.d.ts +11 -0
- package/bin/retailPromotions/enums/CouponStatusEnum.js +15 -0
- package/bin/retailPromotions/enums/DiscountCodeOriginEnum.d.ts +8 -0
- package/bin/retailPromotions/enums/DiscountCodeOriginEnum.js +12 -0
- package/bin/retailPromotions/enums/DiscountCodeStatusEnum.d.ts +11 -0
- package/bin/retailPromotions/enums/DiscountCodeStatusEnum.js +15 -0
- package/bin/retailPromotions/enums/DiscountCodeTransitionEnum.d.ts +7 -0
- package/bin/retailPromotions/enums/DiscountCodeTransitionEnum.js +11 -0
- package/bin/retailPromotions/enums/DiscountTypeEnum.d.ts +8 -0
- package/bin/retailPromotions/enums/DiscountTypeEnum.js +12 -0
- package/bin/retailPromotions/enums/EntitlementKindEnum.d.ts +9 -0
- package/bin/retailPromotions/enums/EntitlementKindEnum.js +13 -0
- package/bin/retailPromotions/enums/EntitlementStateEnum.d.ts +13 -0
- package/bin/retailPromotions/enums/EntitlementStateEnum.js +17 -0
- package/bin/retailPromotions/enums/GiftModeEnum.d.ts +8 -0
- package/bin/retailPromotions/enums/GiftModeEnum.js +12 -0
- package/bin/retailPromotions/enums/PromotionAppliesToEnum.d.ts +9 -0
- package/bin/retailPromotions/enums/PromotionAppliesToEnum.js +13 -0
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +23 -0
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +27 -0
- package/bin/retailPromotions/enums/SaleCouponAvailabilityEnum.d.ts +10 -0
- package/bin/retailPromotions/enums/SaleCouponAvailabilityEnum.js +14 -0
- package/bin/retailPromotions/enums/SalePromotionLeverEnum.d.ts +13 -0
- package/bin/retailPromotions/enums/SalePromotionLeverEnum.js +17 -0
- package/bin/retailPromotions/enums/SalePromotionLogEventEnum.d.ts +14 -0
- package/bin/retailPromotions/enums/SalePromotionLogEventEnum.js +18 -0
- package/bin/retailPromotions/enums/SalePromotionLogFormatEnum.d.ts +5 -0
- package/bin/retailPromotions/enums/SalePromotionLogFormatEnum.js +9 -0
- package/bin/retailPromotions/enums/SalePromotionPhaseEnum.d.ts +11 -0
- package/bin/retailPromotions/enums/SalePromotionPhaseEnum.js +15 -0
- package/bin/retailPromotions/enums/SalePromotionStatusEnum.d.ts +12 -0
- package/bin/retailPromotions/enums/SalePromotionStatusEnum.js +16 -0
- package/bin/retailPromotions/enums/SalePromotionTransitionEnum.d.ts +13 -0
- package/bin/retailPromotions/enums/SalePromotionTransitionEnum.js +17 -0
- package/bin/retailPromotions/helpers/computeBudgetUsedBps.d.ts +3 -0
- package/bin/retailPromotions/helpers/computeBudgetUsedBps.js +15 -0
- package/bin/retailPromotions/helpers/resolveSalePromotionTransition.d.ts +4 -0
- package/bin/retailPromotions/helpers/resolveSalePromotionTransition.js +9 -0
- package/bin/retailPromotions/index.d.ts +46 -0
- package/bin/retailPromotions/index.js +67 -0
- package/bin/retailWizard/dtos/Sale.d.ts +8 -8
- package/bin/retailWizard/dtos/SaleLine.d.ts +4 -1
- package/bin/retailWizard/dtos/WizardFinancingPreview.d.ts +11 -0
- package/bin/retailWizard/dtos/WizardFinancingPreview.js +2 -0
- package/bin/retailWizard/dtos/WizardGiftLine.d.ts +8 -0
- package/bin/retailWizard/dtos/WizardGiftLine.js +2 -0
- package/bin/retailWizard/dtos/WizardLinePromotionFields.d.ts +15 -0
- package/bin/retailWizard/dtos/WizardLinePromotionFields.js +2 -0
- package/bin/retailWizard/dtos/WizardReservedLine.d.ts +4 -1
- package/bin/retailWizard/dtos/WizardSession.d.ts +10 -0
- package/bin/retailWizard/dtos/requests/AddProductRequest.d.ts +2 -0
- package/bin/retailWizard/dtos/requests/AddProductRequest.js +7 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +15 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +58 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.d.ts +9 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.js +36 -0
- package/bin/retailWizard/dtos/requests/PinSessionPiecesRequest.d.ts +14 -0
- package/bin/retailWizard/dtos/requests/PinSessionPiecesRequest.js +60 -0
- package/bin/retailWizard/dtos/requests/QuoteRequest.d.ts +1 -1
- package/bin/retailWizard/dtos/requests/SetAnchorGiftsRequest.d.ts +14 -0
- package/bin/retailWizard/dtos/requests/SetAnchorGiftsRequest.js +56 -0
- package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.d.ts +29 -0
- package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.js +2 -0
- package/bin/retailWizard/dtos/responses/SetAnchorGiftsResponse.d.ts +5 -0
- package/bin/retailWizard/dtos/responses/SetAnchorGiftsResponse.js +2 -0
- package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.d.ts +5 -0
- package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.js +9 -0
- package/bin/retailWizard/enums/WizardLineRoleEnum.d.ts +4 -0
- package/bin/retailWizard/enums/WizardLineRoleEnum.js +8 -0
- package/bin/retailWizard/events/RetailCommissionInboundMessage.d.ts +19 -0
- package/bin/retailWizard/events/RetailCommissionInboundMessage.js +78 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +40 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.js +160 -0
- package/bin/retailWizard/events/SaleCompletedV1.d.ts +2 -3
- package/bin/retailWizard/index.d.ts +13 -0
- package/bin/retailWizard/index.js +14 -0
- package/package.json +1 -1
- package/src/common/index.ts +3 -0
- package/src/common/validators/IsStringValueRecord.ts +13 -0
- package/src/index.ts +3 -0
- package/src/messagesConnector/validators/IsStringValueRecord.ts +2 -12
- package/src/platformRbac/dtos/CreateTenantUserRequest.ts +8 -1
- package/src/platformRbac/dtos/UpdateTenantUserRequest.ts +9 -3
- package/src/retailCatalog/dtos/Product.ts +6 -0
- package/src/retailCatalog/dtos/requests/BackofficeCreateProductRequest.ts +30 -1
- package/src/retailCatalog/dtos/requests/BackofficeRegisterAdjustmentRequest.ts +6 -0
- package/src/retailCatalog/dtos/requests/BackofficeUpdateProductRequest.ts +30 -1
- package/src/retailCatalog/dtos/requests/HoldStockRequest.ts +28 -0
- package/src/retailCatalog/dtos/requests/ReserveStockRequest.ts +7 -1
- package/src/retailCatalog/dtos/requests/SuggestPiecesQuery.ts +7 -1
- package/src/retailCatalog/dtos/responses/GetInventorySkuResponse.ts +3 -0
- package/src/retailCatalog/dtos/responses/HoldStockResponse.ts +9 -0
- package/src/retailCatalog/dtos/responses/StoreCatalogItem.ts +6 -1
- package/src/retailCatalog/enums/InventoryInboundMessageTypeEnum.ts +5 -0
- package/src/retailCatalog/enums/InventoryItemStatusEnum.ts +3 -1
- package/src/retailCatalog/enums/ReservePurposeEnum.ts +4 -0
- package/src/retailCatalog/events/InventoryInboundMessage.ts +5 -0
- package/src/retailCatalog/events/InventoryReturnMessage.ts +26 -0
- package/src/retailCatalog/events/InventorySaleAppliedMessage.ts +26 -0
- package/src/retailCatalog/index.ts +9 -0
- package/src/retailPromotions/constants/bonusPreMoney.ts +19 -0
- package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +21 -0
- package/src/retailPromotions/constants/salePromotionTransitions.ts +11 -0
- package/src/retailPromotions/dtos/DiscountCode.ts +36 -0
- package/src/retailPromotions/dtos/DiscountValue.ts +8 -0
- package/src/retailPromotions/dtos/LinePromotionDiscount.ts +10 -0
- package/src/retailPromotions/dtos/SalePromotion.ts +36 -0
- package/src/retailPromotions/dtos/SalePromotionLevers.ts +67 -0
- package/src/retailPromotions/dtos/SalePromotionLogEntry.ts +19 -0
- package/src/retailPromotions/dtos/requests/ChangeDiscountCodeStatusRequest.ts +15 -0
- package/src/retailPromotions/dtos/requests/ChangeSalePromotionStatusRequest.ts +15 -0
- package/src/retailPromotions/dtos/requests/CreateDiscountCodeRequest.ts +71 -0
- package/src/retailPromotions/dtos/requests/CreateSalePromotionRequest.ts +73 -0
- package/src/retailPromotions/dtos/requests/DiscountValueInput.ts +21 -0
- package/src/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.ts +16 -0
- package/src/retailPromotions/dtos/requests/HoldSalePromotionRequest.ts +32 -0
- package/src/retailPromotions/dtos/requests/ListDiscountCodesQuery.ts +33 -0
- package/src/retailPromotions/dtos/requests/ListSalePromotionLogQuery.ts +29 -0
- package/src/retailPromotions/dtos/requests/ListSalePromotionsQuery.ts +24 -0
- package/src/retailPromotions/dtos/requests/ListStoreSalePromotionsQuery.ts +10 -0
- package/src/retailPromotions/dtos/requests/SalePromotionLeversInput.ts +136 -0
- package/src/retailPromotions/dtos/requests/SalePromotionScopeInput.ts +45 -0
- package/src/retailPromotions/dtos/requests/UpdateSalePromotionRequest.ts +74 -0
- package/src/retailPromotions/dtos/responses/DiscountCodeResponses.ts +9 -0
- package/src/retailPromotions/dtos/responses/SalePromotionCrudResponses.ts +35 -0
- package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +115 -0
- package/src/retailPromotions/enums/CouponScopeEnum.ts +11 -0
- package/src/retailPromotions/enums/CouponStatusEnum.ts +11 -0
- package/src/retailPromotions/enums/DiscountCodeOriginEnum.ts +8 -0
- package/src/retailPromotions/enums/DiscountCodeStatusEnum.ts +11 -0
- package/src/retailPromotions/enums/DiscountCodeTransitionEnum.ts +7 -0
- package/src/retailPromotions/enums/DiscountTypeEnum.ts +8 -0
- package/src/retailPromotions/enums/EntitlementKindEnum.ts +9 -0
- package/src/retailPromotions/enums/EntitlementStateEnum.ts +13 -0
- package/src/retailPromotions/enums/GiftModeEnum.ts +8 -0
- package/src/retailPromotions/enums/PromotionAppliesToEnum.ts +9 -0
- package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +23 -0
- package/src/retailPromotions/enums/SaleCouponAvailabilityEnum.ts +10 -0
- package/src/retailPromotions/enums/SalePromotionLeverEnum.ts +13 -0
- package/src/retailPromotions/enums/SalePromotionLogEventEnum.ts +14 -0
- package/src/retailPromotions/enums/SalePromotionLogFormatEnum.ts +5 -0
- package/src/retailPromotions/enums/SalePromotionPhaseEnum.ts +11 -0
- package/src/retailPromotions/enums/SalePromotionStatusEnum.ts +12 -0
- package/src/retailPromotions/enums/SalePromotionTransitionEnum.ts +13 -0
- package/src/retailPromotions/helpers/computeBudgetUsedBps.ts +15 -0
- package/src/retailPromotions/helpers/resolveSalePromotionTransition.ts +12 -0
- package/src/retailPromotions/index.ts +55 -0
- package/src/retailWizard/dtos/Sale.ts +8 -8
- package/src/retailWizard/dtos/SaleLine.ts +4 -1
- package/src/retailWizard/dtos/WizardFinancingPreview.ts +12 -0
- package/src/retailWizard/dtos/WizardGiftLine.ts +9 -0
- package/src/retailWizard/dtos/WizardLinePromotionFields.ts +16 -0
- package/src/retailWizard/dtos/WizardReservedLine.ts +4 -1
- package/src/retailWizard/dtos/WizardSession.ts +10 -0
- package/src/retailWizard/dtos/requests/AddProductRequest.ts +8 -1
- package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +45 -0
- package/src/retailWizard/dtos/requests/ApplySalePromotionRequest.ts +24 -0
- package/src/retailWizard/dtos/requests/PinSessionPiecesRequest.ts +40 -0
- package/src/retailWizard/dtos/requests/QuoteRequest.ts +1 -1
- package/src/retailWizard/dtos/requests/SetAnchorGiftsRequest.ts +40 -0
- package/src/retailWizard/dtos/responses/PrivateWizardSessionResponse.ts +30 -0
- package/src/retailWizard/dtos/responses/SetAnchorGiftsResponse.ts +6 -0
- package/src/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.ts +5 -0
- package/src/retailWizard/enums/WizardLineRoleEnum.ts +4 -0
- package/src/retailWizard/events/RetailCommissionInboundMessage.ts +54 -0
- package/src/retailWizard/events/RetailPromotionInboundMessage.ts +138 -0
- package/src/retailWizard/events/SaleCompletedV1.ts +2 -3
- 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
|
+
});
|
package/bin/common/index.d.ts
CHANGED
package/bin/common/index.js
CHANGED
|
@@ -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
|