@fiado/type-kit 3.607.0 → 3.609.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 (52) hide show
  1. package/_test_/unit/loanCredit/dtos/originateLoanCreditRequest.test.ts +9 -0
  2. package/_test_/unit/loanOfferings/promotionCategoryTierScope.test.ts +89 -0
  3. package/_test_/unit/retailIntegration/eventPayloadBounds.test.ts +1 -1
  4. package/_test_/unit/retailIntegration/eventPayloads.test.ts +53 -6
  5. package/_test_/unit/retailIntegration/retailSystemEnums.test.ts +2 -0
  6. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.d.ts +6 -0
  7. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.js +14 -0
  8. package/bin/loanOfferings/constants/promotionSkuBatch.d.ts +2 -0
  9. package/bin/loanOfferings/constants/promotionSkuBatch.js +5 -0
  10. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.d.ts +6 -0
  11. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.js +16 -0
  12. package/bin/loanOfferings/dtos/requests/ListPromotedSkusRequest.d.ts +11 -0
  13. package/bin/loanOfferings/dtos/requests/ListPromotedSkusRequest.js +47 -0
  14. package/bin/loanOfferings/dtos/requests/ListSkuPromotionsRequest.d.ts +11 -0
  15. package/bin/loanOfferings/dtos/requests/ListSkuPromotionsRequest.js +47 -0
  16. package/bin/loanOfferings/dtos/requests/PromotionSkuInput.d.ts +8 -0
  17. package/bin/loanOfferings/dtos/requests/PromotionSkuInput.js +37 -0
  18. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.d.ts +6 -0
  19. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.js +14 -0
  20. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.d.ts +6 -0
  21. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.js +16 -0
  22. package/bin/loanOfferings/dtos/responses/PromotionResponse.d.ts +6 -0
  23. package/bin/loanOfferings/index.d.ts +4 -0
  24. package/bin/loanOfferings/index.js +5 -0
  25. package/bin/retailIntegration/dtos/ProductEventPayload.d.ts +3 -1
  26. package/bin/retailIntegration/dtos/ProductEventPayload.js +8 -2
  27. package/bin/retailIntegration/enums/RetailMdmModeEnum.d.ts +5 -0
  28. package/bin/retailIntegration/enums/RetailMdmModeEnum.js +9 -0
  29. package/bin/retailIntegration/enums/index.d.ts +1 -0
  30. package/bin/retailIntegration/enums/index.js +1 -0
  31. package/bin/retailIntegration/index.d.ts +1 -0
  32. package/bin/retailIntegration/index.js +1 -0
  33. package/bin/retailIntegration/models/RetailSystemProduct.d.ts +4 -1
  34. package/bin/retailIntegration/validators/productValidators.d.ts +13 -0
  35. package/bin/retailIntegration/validators/productValidators.js +49 -0
  36. package/package.json +1 -1
  37. package/src/loanCredit/dtos/requests/OriginateLoanCreditRequest.ts +14 -0
  38. package/src/loanOfferings/constants/promotionSkuBatch.ts +2 -0
  39. package/src/loanOfferings/dtos/requests/CreatePromotionRequest.ts +16 -0
  40. package/src/loanOfferings/dtos/requests/ListPromotedSkusRequest.ts +32 -0
  41. package/src/loanOfferings/dtos/requests/ListSkuPromotionsRequest.ts +32 -0
  42. package/src/loanOfferings/dtos/requests/PromotionSkuInput.ts +21 -0
  43. package/src/loanOfferings/dtos/requests/SimulateCreditPlanRequest.ts +16 -1
  44. package/src/loanOfferings/dtos/requests/UpdatePromotionRequest.ts +16 -0
  45. package/src/loanOfferings/dtos/responses/PromotionResponse.ts +6 -0
  46. package/src/loanOfferings/index.ts +6 -0
  47. package/src/retailIntegration/dtos/ProductEventPayload.ts +6 -2
  48. package/src/retailIntegration/enums/RetailMdmModeEnum.ts +5 -0
  49. package/src/retailIntegration/enums/index.ts +1 -0
  50. package/src/retailIntegration/index.ts +1 -0
  51. package/src/retailIntegration/models/RetailSystemProduct.ts +4 -0
  52. package/src/retailIntegration/validators/productValidators.ts +35 -0
@@ -33,6 +33,8 @@ __exportStar(require("./enums/SegmentStatusEnum"), exports);
33
33
  __exportStar(require("./enums/SegmentColorSlotEnum"), exports);
34
34
  __exportStar(require("./enums/SegmentFilterIdEnum"), exports);
35
35
  __exportStar(require("./enums/PrepaymentMinimumModeEnum"), exports);
36
+ // Constantes
37
+ __exportStar(require("./constants/promotionSkuBatch"), exports);
36
38
  // Entity DTOs
37
39
  __exportStar(require("./dtos/CreditPlan"), exports);
38
40
  __exportStar(require("./dtos/PrepaymentMinimum"), exports);
@@ -64,6 +66,9 @@ __exportStar(require("./dtos/requests/ChangeSegmentStatusRequest"), exports);
64
66
  __exportStar(require("./dtos/requests/ResolveCustomerSegmentsRequest"), exports);
65
67
  __exportStar(require("./dtos/requests/ComputeAmortizationScheduleRequest"), exports);
66
68
  __exportStar(require("./dtos/requests/ListSegmentCatalogQuery"), exports);
69
+ __exportStar(require("./dtos/requests/PromotionSkuInput"), exports);
70
+ __exportStar(require("./dtos/requests/ListPromotedSkusRequest"), exports);
71
+ __exportStar(require("./dtos/requests/ListSkuPromotionsRequest"), exports);
67
72
  // Response DTOs
68
73
  __exportStar(require("./dtos/responses/CreditPlanResponse"), exports);
69
74
  __exportStar(require("./dtos/responses/FinancierResponse"), exports);
@@ -1,4 +1,4 @@
1
- import type { RetailCurrency, RetailFinancier, RetailMdmProvider, RetailProductCategory, RetailProductStatus, RetailProductTier, RetailSystemProduct, RetailSystemProductPrice, RetailTrackingMode } from '../models/RetailSystemProduct';
1
+ import type { RetailCurrency, RetailFinancier, RetailMdmMode, RetailMdmProvider, RetailProductCategory, RetailProductStatus, RetailProductTier, RetailSystemProduct, RetailSystemProductPrice, RetailTrackingMode } from '../models/RetailSystemProduct';
2
2
  /** Precio del producto en decimales string. */
3
3
  export declare class ProductEventPricePayload implements RetailSystemProductPrice {
4
4
  basePrice: string;
@@ -23,5 +23,7 @@ export declare class ProductEventPayload implements RetailSystemProduct {
23
23
  /** Opcional en el evento: ausente o `null` se vuelve la lista de todas las financieras. */
24
24
  eligibleFinanciers: RetailFinancier[];
25
25
  mdmProvider: RetailMdmProvider | null;
26
+ /** Opcional en el evento: ausente se vuelve `null`. */
27
+ mdmMode: RetailMdmMode | null;
26
28
  status: RetailProductStatus;
27
29
  }
@@ -16,6 +16,7 @@ const retailIntegrationLimits_1 = require("../constants/retailIntegrationLimits"
16
16
  const enums_1 = require("../enums");
17
17
  const IsIsoDateTimeWithZone_1 = require("../validators/IsIsoDateTimeWithZone");
18
18
  const payloadValidators_1 = require("../validators/payloadValidators");
19
+ const productValidators_1 = require("../validators/productValidators");
19
20
  /** Precio del producto en decimales string. */
20
21
  class ProductEventPricePayload {
21
22
  }
@@ -118,10 +119,15 @@ __decorate([
118
119
  __metadata("design:type", Array)
119
120
  ], ProductEventPayload.prototype, "eligibleFinanciers", void 0);
120
121
  __decorate([
121
- (0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
122
- (0, class_validator_1.IsIn)(Object.values(enums_1.RetailMdmProviderEnum)),
122
+ (0, class_validator_1.Validate)(productValidators_1.MdmProviderMatchesTracking),
123
123
  __metadata("design:type", String)
124
124
  ], ProductEventPayload.prototype, "mdmProvider", void 0);
125
+ __decorate([
126
+ (0, class_transformer_1.Expose)(),
127
+ (0, class_transformer_1.Transform)(({ value }) => value ?? null),
128
+ (0, class_validator_1.Validate)(productValidators_1.MdmModeMatchesProvider),
129
+ __metadata("design:type", String)
130
+ ], ProductEventPayload.prototype, "mdmMode", void 0);
125
131
  __decorate([
126
132
  (0, class_validator_1.IsIn)(Object.values(enums_1.RetailProductStatusEnum)),
127
133
  __metadata("design:type", String)
@@ -0,0 +1,5 @@
1
+ /** Modo del candado MDM del equipo; mismos valores que `MdmLockModeEnum` de retailCatalog. */
2
+ export declare enum RetailMdmModeEnum {
3
+ SCHEDULED = "SCHEDULED",
4
+ ONDEMAND = "ONDEMAND"
5
+ }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RetailMdmModeEnum = void 0;
4
+ /** Modo del candado MDM del equipo; mismos valores que `MdmLockModeEnum` de retailCatalog. */
5
+ var RetailMdmModeEnum;
6
+ (function (RetailMdmModeEnum) {
7
+ RetailMdmModeEnum["SCHEDULED"] = "SCHEDULED";
8
+ RetailMdmModeEnum["ONDEMAND"] = "ONDEMAND";
9
+ })(RetailMdmModeEnum || (exports.RetailMdmModeEnum = RetailMdmModeEnum = {}));
@@ -10,6 +10,7 @@ export * from './RetailProductTierEnum';
10
10
  export * from './RetailCurrencyEnum';
11
11
  export * from './RetailFinancierEnum';
12
12
  export * from './RetailMdmProviderEnum';
13
+ export * from './RetailMdmModeEnum';
13
14
  export * from './RetailProductStatusEnum';
14
15
  export * from './RetailPieceStatusEnum';
15
16
  export * from './RetailPieceReasonEnum';
@@ -26,6 +26,7 @@ __exportStar(require("./RetailProductTierEnum"), exports);
26
26
  __exportStar(require("./RetailCurrencyEnum"), exports);
27
27
  __exportStar(require("./RetailFinancierEnum"), exports);
28
28
  __exportStar(require("./RetailMdmProviderEnum"), exports);
29
+ __exportStar(require("./RetailMdmModeEnum"), exports);
29
30
  __exportStar(require("./RetailProductStatusEnum"), exports);
30
31
  __exportStar(require("./RetailPieceStatusEnum"), exports);
31
32
  __exportStar(require("./RetailPieceReasonEnum"), exports);
@@ -5,6 +5,7 @@ export * from './validators/IsIsoDateTimeWithZone';
5
5
  export * from './validators/IsMovementsRangeValid';
6
6
  export * from './validators/IsCalendarDate';
7
7
  export * from './validators/saleValidators';
8
+ export * from './validators/productValidators';
8
9
  export * from './dtos/RetailEventEnvelope';
9
10
  export * from './dtos/ReceiveStoreEventRequest';
10
11
  export * from './dtos/ReceiveRetailUserEventRequest';
@@ -21,6 +21,7 @@ __exportStar(require("./validators/IsIsoDateTimeWithZone"), exports);
21
21
  __exportStar(require("./validators/IsMovementsRangeValid"), exports);
22
22
  __exportStar(require("./validators/IsCalendarDate"), exports);
23
23
  __exportStar(require("./validators/saleValidators"), exports);
24
+ __exportStar(require("./validators/productValidators"), exports);
24
25
  __exportStar(require("./dtos/RetailEventEnvelope"), exports);
25
26
  __exportStar(require("./dtos/ReceiveStoreEventRequest"), exports);
26
27
  __exportStar(require("./dtos/ReceiveRetailUserEventRequest"), exports);
@@ -1,10 +1,11 @@
1
- import { RetailCurrencyEnum, RetailFinancierEnum, RetailMdmProviderEnum, RetailProductCategoryEnum, RetailProductStatusEnum, RetailProductTierEnum, RetailTrackingModeEnum } from '../enums';
1
+ import { RetailCurrencyEnum, RetailFinancierEnum, RetailMdmModeEnum, RetailMdmProviderEnum, RetailProductCategoryEnum, RetailProductStatusEnum, RetailProductTierEnum, RetailTrackingModeEnum } from '../enums';
2
2
  export type RetailProductCategory = `${RetailProductCategoryEnum}`;
3
3
  export type RetailTrackingMode = `${RetailTrackingModeEnum}`;
4
4
  export type RetailProductTier = `${RetailProductTierEnum}`;
5
5
  export type RetailCurrency = `${RetailCurrencyEnum}`;
6
6
  export type RetailFinancier = `${RetailFinancierEnum}`;
7
7
  export type RetailMdmProvider = `${RetailMdmProviderEnum}`;
8
+ export type RetailMdmMode = `${RetailMdmModeEnum}`;
8
9
  export type RetailProductStatus = `${RetailProductStatusEnum}`;
9
10
  /** Precios como decimal en string con dos decimales. */
10
11
  export interface RetailSystemProductPrice {
@@ -28,5 +29,7 @@ export interface RetailSystemProduct {
28
29
  price: RetailSystemProductPrice;
29
30
  eligibleFinanciers: RetailFinancier[];
30
31
  mdmProvider: RetailMdmProvider | null;
32
+ /** Modo del candado; obligatorio cuando hay `mdmProvider`. */
33
+ mdmMode?: RetailMdmMode | null;
31
34
  status: RetailProductStatus;
32
35
  }
@@ -0,0 +1,13 @@
1
+ import { ValidationArguments, ValidatorConstraintInterface } from 'class-validator';
2
+ /** Un producto rastreado por IMEI lleva MDM. */
3
+ export declare function requiresMdm(trackingMode: unknown): boolean;
4
+ /** `mdmProvider`: obligatorio con `trackingMode` IMEI y `null` en los demás productos. */
5
+ export declare class MdmProviderMatchesTracking implements ValidatorConstraintInterface {
6
+ validate(value: unknown, args: ValidationArguments): boolean;
7
+ defaultMessage(): string;
8
+ }
9
+ /** `mdmMode`: obligatorio con `mdmProvider` y `null` sin él; MOTOSAFE solo opera en ONDEMAND. */
10
+ export declare class MdmModeMatchesProvider implements ValidatorConstraintInterface {
11
+ validate(value: unknown, args: ValidationArguments): boolean;
12
+ defaultMessage(): string;
13
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.MdmModeMatchesProvider = exports.MdmProviderMatchesTracking = void 0;
10
+ exports.requiresMdm = requiresMdm;
11
+ const class_validator_1 = require("class-validator");
12
+ const enums_1 = require("../enums");
13
+ /** Un producto rastreado por IMEI lleva MDM. */
14
+ function requiresMdm(trackingMode) {
15
+ return trackingMode === enums_1.RetailTrackingModeEnum.IMEI;
16
+ }
17
+ /** `mdmProvider`: obligatorio con `trackingMode` IMEI y `null` en los demás productos. */
18
+ let MdmProviderMatchesTracking = class MdmProviderMatchesTracking {
19
+ validate(value, args) {
20
+ if (!requiresMdm(args.object.trackingMode))
21
+ return value === null;
22
+ return Object.values(enums_1.RetailMdmProviderEnum).includes(value);
23
+ }
24
+ defaultMessage() {
25
+ return 'mdmProvider es obligatorio con trackingMode IMEI y null en los demás productos';
26
+ }
27
+ };
28
+ exports.MdmProviderMatchesTracking = MdmProviderMatchesTracking;
29
+ exports.MdmProviderMatchesTracking = MdmProviderMatchesTracking = __decorate([
30
+ (0, class_validator_1.ValidatorConstraint)({ name: 'mdmProviderMatchesTracking', async: false })
31
+ ], MdmProviderMatchesTracking);
32
+ /** `mdmMode`: obligatorio con `mdmProvider` y `null` sin él; MOTOSAFE solo opera en ONDEMAND. */
33
+ let MdmModeMatchesProvider = class MdmModeMatchesProvider {
34
+ validate(value, args) {
35
+ const provider = args.object.mdmProvider;
36
+ if (provider === null || provider === undefined)
37
+ return value === null;
38
+ if (provider === enums_1.RetailMdmProviderEnum.MOTOSAFE)
39
+ return value === enums_1.RetailMdmModeEnum.ONDEMAND;
40
+ return Object.values(enums_1.RetailMdmModeEnum).includes(value);
41
+ }
42
+ defaultMessage() {
43
+ return 'mdmMode es obligatorio con mdmProvider (MOTOSAFE solo ONDEMAND) y null sin él';
44
+ }
45
+ };
46
+ exports.MdmModeMatchesProvider = MdmModeMatchesProvider;
47
+ exports.MdmModeMatchesProvider = MdmModeMatchesProvider = __decorate([
48
+ (0, class_validator_1.ValidatorConstraint)({ name: 'mdmModeMatchesProvider', async: false })
49
+ ], MdmModeMatchesProvider);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fiado/type-kit",
3
- "version": "3.607.0",
3
+ "version": "3.609.0",
4
4
  "description": "",
5
5
  "main": "bin/index.js",
6
6
  "types": "bin/index.d.ts",
@@ -8,6 +8,8 @@ import {
8
8
  Max,
9
9
  Min,
10
10
  } from 'class-validator';
11
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
12
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
11
13
  import { ClientLevelEnum } from '../../../loanScoring/enums/ClientLevelEnum';
12
14
 
13
15
  /**
@@ -106,4 +108,16 @@ export class OriginateLoanCreditRequest {
106
108
  @IsOptional()
107
109
  @IsString()
108
110
  productSku?: string;
111
+
112
+ /** Tipo del equipo: resuelve promociones acotadas por tipo. */
113
+ @Expose()
114
+ @IsOptional()
115
+ @IsEnum(ProductCategoryEnum)
116
+ productCategory?: ProductCategoryEnum;
117
+
118
+ /** Gama del equipo: resuelve promociones acotadas por gama. */
119
+ @Expose()
120
+ @IsOptional()
121
+ @IsEnum(ProductTierEnum)
122
+ productTier?: ProductTierEnum;
109
123
  }
@@ -0,0 +1,2 @@
1
+ /** Tope de SKUs por llamada a los lotes de promociones por SKU; el caller parte en bloques. */
2
+ export const PROMOTION_SKU_BATCH_MAX_ITEMS = 100;
@@ -10,6 +10,8 @@ import {
10
10
  ValidateNested,
11
11
  } from 'class-validator';
12
12
  import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
13
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
14
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
13
15
  import { PromotionEffectInput } from './PromotionEffectInput';
14
16
 
15
17
  /**
@@ -49,6 +51,20 @@ export class CreatePromotionRequest {
49
51
  @IsString({ each: true })
50
52
  productSkus?: string[] | null;
51
53
 
54
+ /** Tipos de producto alcanzados; ausente o `null` = todos los tipos. */
55
+ @Expose()
56
+ @IsOptional()
57
+ @IsArray()
58
+ @IsEnum(ProductCategoryEnum, { each: true })
59
+ productCategories?: ProductCategoryEnum[] | null;
60
+
61
+ /** Gamas alcanzadas; ausente o `null` = todas las gamas. */
62
+ @Expose()
63
+ @IsOptional()
64
+ @IsArray()
65
+ @IsEnum(ProductTierEnum, { each: true })
66
+ tiers?: ProductTierEnum[] | null;
67
+
52
68
  /** Tiendas alcanzadas. `null` o ausente = todas. */
53
69
  @Expose()
54
70
  @IsOptional()
@@ -0,0 +1,32 @@
1
+ import { Expose, Type } from 'class-transformer';
2
+ import { ArrayMaxSize, IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator';
3
+ import { PROMOTION_SKU_BATCH_MAX_ITEMS } from '../../constants/promotionSkuBatch';
4
+ import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
5
+ import { PromotionSkuInput } from './PromotionSkuInput';
6
+
7
+ /** Body de `POST /private/promotions/promoted-skus` (M2M): SKUs del lote con promoción viva para el badge; hasta 100 SKUs. */
8
+ export class ListPromotedSkusRequest {
9
+ @Expose()
10
+ @IsString()
11
+ @IsNotEmpty()
12
+ storeId!: string;
13
+
14
+ /** Nivel del cliente si loan-scoring ya corrió. */
15
+ @Expose()
16
+ @IsOptional()
17
+ @IsEnum(CreditPlanLevelEnum)
18
+ level?: CreditPlanLevelEnum;
19
+
20
+ /** Segmento del cliente si ya se resolvió. */
21
+ @Expose()
22
+ @IsOptional()
23
+ @IsString()
24
+ segmentId?: string;
25
+
26
+ @Expose()
27
+ @IsArray()
28
+ @ArrayMaxSize(PROMOTION_SKU_BATCH_MAX_ITEMS)
29
+ @ValidateNested({ each: true })
30
+ @Type(() => PromotionSkuInput)
31
+ items!: PromotionSkuInput[];
32
+ }
@@ -0,0 +1,32 @@
1
+ import { Expose, Type } from 'class-transformer';
2
+ import { ArrayMaxSize, IsArray, IsEnum, IsNotEmpty, IsOptional, IsString, ValidateNested } from 'class-validator';
3
+ import { PROMOTION_SKU_BATCH_MAX_ITEMS } from '../../constants/promotionSkuBatch';
4
+ import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
5
+ import { PromotionSkuInput } from './PromotionSkuInput';
6
+
7
+ /** Body de `POST /private/promotions/by-sku` (M2M): promociones vivas de cada SKU del lote; hasta 100 SKUs. */
8
+ export class ListSkuPromotionsRequest {
9
+ @Expose()
10
+ @IsString()
11
+ @IsNotEmpty()
12
+ storeId!: string;
13
+
14
+ /** Nivel del cliente si loan-scoring ya corrió. */
15
+ @Expose()
16
+ @IsOptional()
17
+ @IsEnum(CreditPlanLevelEnum)
18
+ level?: CreditPlanLevelEnum;
19
+
20
+ /** Segmento del cliente si ya se resolvió. */
21
+ @Expose()
22
+ @IsOptional()
23
+ @IsString()
24
+ segmentId?: string;
25
+
26
+ @Expose()
27
+ @IsArray()
28
+ @ArrayMaxSize(PROMOTION_SKU_BATCH_MAX_ITEMS)
29
+ @ValidateNested({ each: true })
30
+ @Type(() => PromotionSkuInput)
31
+ items!: PromotionSkuInput[];
32
+ }
@@ -0,0 +1,21 @@
1
+ import { Expose } from 'class-transformer';
2
+ import { IsEnum, IsOptional, IsString } from 'class-validator';
3
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
4
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
5
+
6
+ /** Un SKU con su tipo y gama; sin ellos no cumple un alcance por tipo o por gama. */
7
+ export class PromotionSkuInput {
8
+ @Expose()
9
+ @IsString()
10
+ sku!: string;
11
+
12
+ @Expose()
13
+ @IsOptional()
14
+ @IsEnum(ProductCategoryEnum)
15
+ category?: ProductCategoryEnum;
16
+
17
+ @Expose()
18
+ @IsOptional()
19
+ @IsEnum(ProductTierEnum)
20
+ tier?: ProductTierEnum;
21
+ }
@@ -1,5 +1,8 @@
1
1
  import { Expose } from 'class-transformer';
2
- import { IsArray, IsInt, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator';
2
+ import { IsArray, IsEnum, IsInt, IsNumber, IsOptional, IsString, Max, Min } from 'class-validator';
3
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
4
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
5
+
3
6
 
4
7
  /**
5
8
  * Body de POST /private/credit-plans/:planId/simulate y query de GET /credit-plans/:planId/simulator.
@@ -46,6 +49,18 @@ export class SimulateCreditPlanRequest {
46
49
  @IsString()
47
50
  productSku?: string;
48
51
 
52
+ /** Tipo del equipo. Sin él no se resuelven las promociones acotadas por tipo. */
53
+ @Expose()
54
+ @IsOptional()
55
+ @IsEnum(ProductCategoryEnum)
56
+ productCategory?: ProductCategoryEnum;
57
+
58
+ /** Gama del equipo. Sin ella no se resuelven las promociones acotadas por gama. */
59
+ @Expose()
60
+ @IsOptional()
61
+ @IsEnum(ProductTierEnum)
62
+ productTier?: ProductTierEnum;
63
+
49
64
  /**
50
65
  * Segmentos del cliente, los que devolvió `POST /private/segments/resolve`. Un arreglo vacío
51
66
  * declara que el cliente no está en ninguno; ausente declara que el caller no lo consultó.
@@ -10,6 +10,8 @@ import {
10
10
  ValidateNested,
11
11
  } from 'class-validator';
12
12
  import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
13
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
14
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
13
15
  import { PromotionEffectInput } from './PromotionEffectInput';
14
16
 
15
17
  /**
@@ -52,6 +54,20 @@ export class UpdatePromotionRequest {
52
54
  @IsString({ each: true })
53
55
  productSkus?: string[] | null;
54
56
 
57
+ /** Tipos de producto alcanzados; ausente o `null` = todos los tipos. */
58
+ @Expose()
59
+ @IsOptional()
60
+ @IsArray()
61
+ @IsEnum(ProductCategoryEnum, { each: true })
62
+ productCategories?: ProductCategoryEnum[] | null;
63
+
64
+ /** Gamas alcanzadas; ausente o `null` = todas las gamas. */
65
+ @Expose()
66
+ @IsOptional()
67
+ @IsArray()
68
+ @IsEnum(ProductTierEnum, { each: true })
69
+ tiers?: ProductTierEnum[] | null;
70
+
55
71
  /** Tiendas alcanzadas. `null` o ausente = todas. */
56
72
  @Expose()
57
73
  @IsOptional()
@@ -1,3 +1,5 @@
1
+ import { ProductCategoryEnum } from '../../../retailCatalog/enums/ProductCategoryEnum';
2
+ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
1
3
  import { CreditPlanLevelEnum } from '../../enums/CreditPlanLevelEnum';
2
4
  import { PromotionEffectKindEnum } from '../../enums/PromotionEffectKindEnum';
3
5
  import { PromotionPhaseEnum } from '../../enums/PromotionPhaseEnum';
@@ -22,6 +24,10 @@ export interface PromotionResponse {
22
24
  targetLevels: CreditPlanLevelEnum[];
23
25
  /** `null` = alcanza a todos los productos. */
24
26
  productSkus: string[] | null;
27
+ /** `null` = alcanza a todos los tipos de producto. */
28
+ productCategories: ProductCategoryEnum[] | null;
29
+ /** `null` = alcanza a todas las gamas. */
30
+ tiers: ProductTierEnum[] | null;
25
31
  /** `null` = alcanza a todas las tiendas. Dimensión «Tienda / canal» del alcance. */
26
32
  storeIds: string[] | null;
27
33
  validFrom: string | null;
@@ -18,6 +18,9 @@ export * from './enums/SegmentColorSlotEnum';
18
18
  export * from './enums/SegmentFilterIdEnum';
19
19
  export * from './enums/PrepaymentMinimumModeEnum';
20
20
 
21
+ // Constantes
22
+ export * from './constants/promotionSkuBatch';
23
+
21
24
  // Entity DTOs
22
25
  export * from './dtos/CreditPlan';
23
26
  export * from './dtos/PrepaymentMinimum';
@@ -50,6 +53,9 @@ export * from './dtos/requests/ChangeSegmentStatusRequest';
50
53
  export * from './dtos/requests/ResolveCustomerSegmentsRequest';
51
54
  export * from './dtos/requests/ComputeAmortizationScheduleRequest';
52
55
  export * from './dtos/requests/ListSegmentCatalogQuery';
56
+ export * from './dtos/requests/PromotionSkuInput';
57
+ export * from './dtos/requests/ListPromotedSkusRequest';
58
+ export * from './dtos/requests/ListSkuPromotionsRequest';
53
59
 
54
60
  // Response DTOs
55
61
  export * from './dtos/responses/CreditPlanResponse';
@@ -11,7 +11,6 @@ import {
11
11
  import {
12
12
  RetailCurrencyEnum,
13
13
  RetailFinancierEnum,
14
- RetailMdmProviderEnum,
15
14
  RetailProductCategoryEnum,
16
15
  RetailProductStatusEnum,
17
16
  RetailProductTierEnum,
@@ -20,6 +19,7 @@ import {
20
19
  import type {
21
20
  RetailCurrency,
22
21
  RetailFinancier,
22
+ RetailMdmMode,
23
23
  RetailMdmProvider,
24
24
  RetailProductCategory,
25
25
  RetailProductStatus,
@@ -30,6 +30,7 @@ import type {
30
30
  } from '../models/RetailSystemProduct';
31
31
  import { IsIsoDateTimeWithZone } from '../validators/IsIsoDateTimeWithZone';
32
32
  import { isNullOrValidated } from '../validators/payloadValidators';
33
+ import { MdmModeMatchesProvider, MdmProviderMatchesTracking } from '../validators/productValidators';
33
34
 
34
35
  /** Precio del producto en decimales string. */
35
36
  export class ProductEventPricePayload implements RetailSystemProductPrice {
@@ -58,6 +59,9 @@ export class ProductEventPayload implements RetailSystemProduct {
58
59
  @Expose() @Transform(({ value }) => value ?? Object.values(RetailFinancierEnum))
59
60
  @IsOptional() @IsArray() @ArrayNotEmpty() @ArrayUnique() @ArrayMaxSize(RETAIL_ELIGIBLE_FINANCIERS_MAX) @IsIn(Object.values(RetailFinancierEnum), { each: true })
60
61
  eligibleFinanciers!: RetailFinancier[];
61
- @ValidateIf(isNullOrValidated) @IsIn(Object.values(RetailMdmProviderEnum)) mdmProvider!: RetailMdmProvider | null;
62
+ @Validate(MdmProviderMatchesTracking) mdmProvider!: RetailMdmProvider | null;
63
+ /** Opcional en el evento: ausente se vuelve `null`. */
64
+ @Expose() @Transform(({ value }) => value ?? null)
65
+ @Validate(MdmModeMatchesProvider) mdmMode!: RetailMdmMode | null;
62
66
  @IsIn(Object.values(RetailProductStatusEnum)) status!: RetailProductStatus;
63
67
  }
@@ -0,0 +1,5 @@
1
+ /** Modo del candado MDM del equipo; mismos valores que `MdmLockModeEnum` de retailCatalog. */
2
+ export enum RetailMdmModeEnum {
3
+ SCHEDULED = 'SCHEDULED',
4
+ ONDEMAND = 'ONDEMAND',
5
+ }
@@ -10,6 +10,7 @@ export * from './RetailProductTierEnum';
10
10
  export * from './RetailCurrencyEnum';
11
11
  export * from './RetailFinancierEnum';
12
12
  export * from './RetailMdmProviderEnum';
13
+ export * from './RetailMdmModeEnum';
13
14
  export * from './RetailProductStatusEnum';
14
15
  export * from './RetailPieceStatusEnum';
15
16
  export * from './RetailPieceReasonEnum';
@@ -5,6 +5,7 @@ export * from './validators/IsIsoDateTimeWithZone';
5
5
  export * from './validators/IsMovementsRangeValid';
6
6
  export * from './validators/IsCalendarDate';
7
7
  export * from './validators/saleValidators';
8
+ export * from './validators/productValidators';
8
9
  export * from './dtos/RetailEventEnvelope';
9
10
  export * from './dtos/ReceiveStoreEventRequest';
10
11
  export * from './dtos/ReceiveRetailUserEventRequest';
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  RetailCurrencyEnum,
3
3
  RetailFinancierEnum,
4
+ RetailMdmModeEnum,
4
5
  RetailMdmProviderEnum,
5
6
  RetailProductCategoryEnum,
6
7
  RetailProductStatusEnum,
@@ -14,6 +15,7 @@ export type RetailProductTier = `${RetailProductTierEnum}`;
14
15
  export type RetailCurrency = `${RetailCurrencyEnum}`;
15
16
  export type RetailFinancier = `${RetailFinancierEnum}`;
16
17
  export type RetailMdmProvider = `${RetailMdmProviderEnum}`;
18
+ export type RetailMdmMode = `${RetailMdmModeEnum}`;
17
19
  export type RetailProductStatus = `${RetailProductStatusEnum}`;
18
20
 
19
21
  /** Precios como decimal en string con dos decimales. */
@@ -39,5 +41,7 @@ export interface RetailSystemProduct {
39
41
  price: RetailSystemProductPrice;
40
42
  eligibleFinanciers: RetailFinancier[];
41
43
  mdmProvider: RetailMdmProvider | null;
44
+ /** Modo del candado; obligatorio cuando hay `mdmProvider`. */
45
+ mdmMode?: RetailMdmMode | null;
42
46
  status: RetailProductStatus;
43
47
  }
@@ -0,0 +1,35 @@
1
+ import { ValidationArguments, ValidatorConstraint, ValidatorConstraintInterface } from 'class-validator';
2
+ import { RetailMdmModeEnum, RetailMdmProviderEnum, RetailTrackingModeEnum } from '../enums';
3
+
4
+ /** Un producto rastreado por IMEI lleva MDM. */
5
+ export function requiresMdm(trackingMode: unknown): boolean {
6
+ return trackingMode === RetailTrackingModeEnum.IMEI;
7
+ }
8
+
9
+ /** `mdmProvider`: obligatorio con `trackingMode` IMEI y `null` en los demás productos. */
10
+ @ValidatorConstraint({ name: 'mdmProviderMatchesTracking', async: false })
11
+ export class MdmProviderMatchesTracking implements ValidatorConstraintInterface {
12
+ validate(value: unknown, args: ValidationArguments): boolean {
13
+ if (!requiresMdm((args.object as { trackingMode?: unknown }).trackingMode)) return value === null;
14
+ return (Object.values(RetailMdmProviderEnum) as unknown[]).includes(value);
15
+ }
16
+
17
+ defaultMessage(): string {
18
+ return 'mdmProvider es obligatorio con trackingMode IMEI y null en los demás productos';
19
+ }
20
+ }
21
+
22
+ /** `mdmMode`: obligatorio con `mdmProvider` y `null` sin él; MOTOSAFE solo opera en ONDEMAND. */
23
+ @ValidatorConstraint({ name: 'mdmModeMatchesProvider', async: false })
24
+ export class MdmModeMatchesProvider implements ValidatorConstraintInterface {
25
+ validate(value: unknown, args: ValidationArguments): boolean {
26
+ const provider = (args.object as { mdmProvider?: unknown }).mdmProvider;
27
+ if (provider === null || provider === undefined) return value === null;
28
+ if (provider === RetailMdmProviderEnum.MOTOSAFE) return value === RetailMdmModeEnum.ONDEMAND;
29
+ return (Object.values(RetailMdmModeEnum) as unknown[]).includes(value);
30
+ }
31
+
32
+ defaultMessage(): string {
33
+ return 'mdmMode es obligatorio con mdmProvider (MOTOSAFE solo ONDEMAND) y null sin él';
34
+ }
35
+ }