@fiado/type-kit 3.499.0 → 3.501.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/cognitoBackofficeConnector/dtos/AuthStartLogin.test.ts +76 -0
- package/_test_/unit/platformRbac/auth/dtos.test.ts +33 -0
- package/_test_/unit/platformRbac/enums/ChallengeNameEnum.test.ts +17 -0
- package/_test_/unit/retailPromotions/constants.test.ts +1 -1
- package/_test_/unit/retailPromotions/evaluatedSalePromotion.test.ts +2 -0
- package/_test_/unit/retailPromotions/resolveSalePromotionKind.test.ts +2 -0
- package/_test_/unit/retailPromotions/salePromotionConditions.test.ts +4 -0
- package/_test_/unit/retailPromotions/stackablePromotions.test.ts +238 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.d.ts +13 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.js +53 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.d.ts +22 -0
- package/bin/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.js +79 -0
- package/bin/cognitoBackofficeConnector/index.d.ts +2 -0
- package/bin/cognitoBackofficeConnector/index.js +2 -0
- package/bin/platformRbac/auth/DefineNextChallengeRequest.d.ts +1 -0
- package/bin/platformRbac/auth/DefineNextChallengeRequest.js +5 -0
- package/bin/platformRbac/enums/ChallengeNameEnum.d.ts +3 -1
- package/bin/platformRbac/enums/ChallengeNameEnum.js +4 -0
- package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +1 -0
- package/bin/retailPromotions/dtos/LinePromotionAmount.d.ts +6 -0
- package/bin/retailPromotions/dtos/LinePromotionAmount.js +36 -0
- package/bin/retailPromotions/dtos/LinePromotionDiscount.d.ts +4 -0
- package/bin/retailPromotions/dtos/SalePromotion.d.ts +2 -0
- package/bin/retailPromotions/dtos/SalePromotionRef.d.ts +5 -0
- package/bin/retailPromotions/dtos/SalePromotionRef.js +30 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.d.ts +2 -0
- package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.js +6 -0
- package/bin/retailPromotions/dtos/requests/HoldPromotionInput.d.ts +8 -0
- package/bin/retailPromotions/dtos/requests/HoldPromotionInput.js +46 -0
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.d.ts +9 -3
- package/bin/retailPromotions/dtos/requests/HoldSalePromotionRequest.js +11 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.d.ts +2 -0
- package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.js +6 -0
- package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +20 -0
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +3 -1
- package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +2 -0
- package/bin/retailPromotions/index.d.ts +4 -0
- package/bin/retailPromotions/index.js +5 -0
- package/bin/retailPromotions/validators/IsHoldModeExclusiveConstraint.d.ts +6 -0
- package/bin/retailPromotions/validators/IsHoldModeExclusiveConstraint.js +24 -0
- package/bin/retailWizard/dtos/Sale.d.ts +3 -0
- package/bin/retailWizard/dtos/WizardLinePromotionFields.d.ts +4 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +3 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.d.ts +5 -0
- package/bin/retailWizard/dtos/requests/ApplyLinePromotionRequest.js +16 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.d.ts +3 -0
- package/bin/retailWizard/dtos/requests/ApplySalePromotionRequest.js +9 -0
- package/bin/retailWizard/dtos/responses/PrivateWizardSessionResponse.d.ts +3 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +8 -0
- package/bin/retailWizard/events/RetailPromotionInboundMessage.js +22 -0
- package/bin/retailWizard/index.d.ts +2 -0
- package/bin/retailWizard/index.js +5 -0
- package/package.json +1 -1
- package/src/cognitoBackofficeConnector/dtos/AuthStartLoginRequest.ts +16 -0
- package/src/cognitoBackofficeConnector/dtos/AuthStartLoginResponse.ts +29 -0
- package/src/cognitoBackofficeConnector/index.ts +2 -0
- package/src/platformRbac/auth/DefineNextChallengeRequest.ts +7 -1
- package/src/platformRbac/enums/ChallengeNameEnum.ts +4 -0
- package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +1 -0
- package/src/retailPromotions/dtos/LinePromotionAmount.ts +20 -0
- package/src/retailPromotions/dtos/LinePromotionDiscount.ts +5 -0
- package/src/retailPromotions/dtos/SalePromotion.ts +2 -0
- package/src/retailPromotions/dtos/SalePromotionRef.ts +15 -0
- package/src/retailPromotions/dtos/requests/CreateSalePromotionRequest.ts +6 -0
- package/src/retailPromotions/dtos/requests/HoldPromotionInput.ts +30 -0
- package/src/retailPromotions/dtos/requests/HoldSalePromotionRequest.ts +28 -4
- package/src/retailPromotions/dtos/requests/UpdateSalePromotionRequest.ts +6 -0
- package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +21 -0
- package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +2 -0
- package/src/retailPromotions/index.ts +6 -0
- package/src/retailPromotions/validators/IsHoldModeExclusiveConstraint.ts +18 -0
- package/src/retailWizard/dtos/Sale.ts +3 -0
- package/src/retailWizard/dtos/WizardLinePromotionFields.ts +4 -0
- package/src/retailWizard/dtos/WizardSession.ts +3 -0
- package/src/retailWizard/dtos/requests/ApplyLinePromotionRequest.ts +18 -2
- package/src/retailWizard/dtos/requests/ApplySalePromotionRequest.ts +11 -2
- package/src/retailWizard/dtos/responses/PrivateWizardSessionResponse.ts +3 -0
- package/src/retailWizard/events/RetailPromotionInboundMessage.ts +22 -0
- package/src/retailWizard/index.ts +2 -0
package/package.json
CHANGED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsNotEmpty, IsString } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Request del login de un solo flujo (SRP dentro de CUSTOM_AUTH). El connector
|
|
6
|
+
* hace InitiateAuth(CUSTOM_AUTH, CHALLENGE_NAME=SRP_A) y responde PASSWORD_VERIFIER
|
|
7
|
+
* firmado en la misma operacion. La contraseña nunca viaja en claro a Cognito: SRP
|
|
8
|
+
* la convierte en prueba. El caller (rbac) no conoce el mecanismo.
|
|
9
|
+
*/
|
|
10
|
+
export class AuthStartLoginRequest {
|
|
11
|
+
@Expose() @IsString() @IsNotEmpty() userPoolId!: string;
|
|
12
|
+
@Expose() @IsString() @IsNotEmpty() region!: string;
|
|
13
|
+
@Expose() @IsString() @IsNotEmpty() clientId!: string;
|
|
14
|
+
@Expose() @IsString() @IsNotEmpty() username!: string;
|
|
15
|
+
@Expose() @IsString() @IsNotEmpty() password!: string;
|
|
16
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { IsEnum, IsNumber, IsObject, IsOptional, IsString, ValidateNested } from 'class-validator';
|
|
3
|
+
import { CognitoChallengeType } from '../enums/CognitoChallengeType';
|
|
4
|
+
|
|
5
|
+
export class AuthStartLoginTokens {
|
|
6
|
+
@Expose() @IsString() accessToken!: string;
|
|
7
|
+
@Expose() @IsString() idToken!: string;
|
|
8
|
+
@Expose() @IsString() refreshToken!: string;
|
|
9
|
+
@Expose() @IsNumber() expiresIn!: number;
|
|
10
|
+
@Expose() @IsString() tokenType!: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Respuesta de `authStartLogin`. Tras verificar el password via SRP
|
|
15
|
+
* (PASSWORD_VERIFIER), Cognito devuelve una de dos cosas: `tokens` si el flujo
|
|
16
|
+
* quedo completo (2FA apagado), o `challengeType` + `session` + `challengeParameters`
|
|
17
|
+
* si falta un paso (NEW_PASSWORD_REQUIRED, o el CUSTOM_CHALLENGE del 2do factor con
|
|
18
|
+
* el sub-challenge real en `challengeParameters.challengeName`). Todos los campos son
|
|
19
|
+
* opcionales: el caller (rbac) decide por que rama sigue.
|
|
20
|
+
*/
|
|
21
|
+
export class AuthStartLoginResponse {
|
|
22
|
+
@Expose() @IsOptional() @ValidateNested() @Type(() => AuthStartLoginTokens) tokens?: AuthStartLoginTokens;
|
|
23
|
+
|
|
24
|
+
@Expose() @IsOptional() @IsEnum(CognitoChallengeType) challengeType?: CognitoChallengeType;
|
|
25
|
+
|
|
26
|
+
@Expose() @IsOptional() @IsString() session?: string;
|
|
27
|
+
|
|
28
|
+
@Expose() @IsOptional() @IsObject() challengeParameters?: Record<string, string>;
|
|
29
|
+
}
|
|
@@ -21,6 +21,8 @@ export * from './dtos/UserDetailResponse';
|
|
|
21
21
|
export * from './dtos/AuthEventsRequest';
|
|
22
22
|
export * from './dtos/AuthEventResponse';
|
|
23
23
|
export * from './dtos/AuthTokensResponse';
|
|
24
|
+
export * from './dtos/AuthStartLoginRequest';
|
|
25
|
+
export * from './dtos/AuthStartLoginResponse';
|
|
24
26
|
export * from './dtos/InitiateAuthRequest';
|
|
25
27
|
export * from './dtos/InitiateAuthResponse';
|
|
26
28
|
export * from './dtos/VerifyPasswordRequest';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import 'reflect-metadata';
|
|
2
2
|
import { Type } from 'class-transformer';
|
|
3
|
-
import { IsArray, IsBoolean, IsEnum, IsString, ValidateNested } from 'class-validator';
|
|
3
|
+
import { IsArray, IsBoolean, IsEnum, IsOptional, IsString, ValidateNested } from 'class-validator';
|
|
4
4
|
import { ChallengeNameEnum } from '../enums/ChallengeNameEnum';
|
|
5
5
|
|
|
6
6
|
export class ChallengeResultEntry {
|
|
@@ -22,4 +22,10 @@ export class DefineNextChallengeRequest {
|
|
|
22
22
|
@ValidateNested({ each: true })
|
|
23
23
|
@Type(() => ChallengeResultEntry)
|
|
24
24
|
session!: ChallengeResultEntry[];
|
|
25
|
+
|
|
26
|
+
// cognito:user_status del evento de Define (C-06). El orquestador decide
|
|
27
|
+
// NEW_PASSWORD_REQUIRED por este valor, no por el fail-safe de getUser.
|
|
28
|
+
@IsOptional()
|
|
29
|
+
@IsString()
|
|
30
|
+
userStatus?: string;
|
|
25
31
|
}
|
|
@@ -5,4 +5,8 @@ export enum ChallengeNameEnum {
|
|
|
5
5
|
NEW_PASSWORD_REQUIRED = 'NEW_PASSWORD_REQUIRED',
|
|
6
6
|
RESET_PASSWORD = 'RESET_PASSWORD',
|
|
7
7
|
SMS_OTP = 'SMS_OTP',
|
|
8
|
+
// SRP dentro de CUSTOM_AUTH: entries nativos que Cognito deja en session[] y que
|
|
9
|
+
// el orquestador del rbac lee para decidir el siguiente paso del login de un solo flujo.
|
|
10
|
+
SRP_A = 'SRP_A',
|
|
11
|
+
PASSWORD_VERIFIER = 'PASSWORD_VERIFIER',
|
|
8
12
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsInt, IsNotEmpty, IsString, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/** Descuento que una promo le aplica a una línea; la suma de la lista + el código da el total. */
|
|
5
|
+
export class LinePromotionAmount {
|
|
6
|
+
@Expose()
|
|
7
|
+
@IsString()
|
|
8
|
+
@IsNotEmpty()
|
|
9
|
+
promotionId!: string;
|
|
10
|
+
|
|
11
|
+
@Expose()
|
|
12
|
+
@IsInt()
|
|
13
|
+
@Min(1)
|
|
14
|
+
promotionVersion!: number;
|
|
15
|
+
|
|
16
|
+
@Expose()
|
|
17
|
+
@IsInt()
|
|
18
|
+
@Min(0)
|
|
19
|
+
discountCents!: number;
|
|
20
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LinePromotionAmount } from './LinePromotionAmount';
|
|
2
|
+
|
|
1
3
|
/** Desglose por línea de la sesión: qué promo o código la tocó y cuánto bajó. */
|
|
2
4
|
export interface LinePromotionDiscount {
|
|
3
5
|
lineId: string;
|
|
@@ -7,4 +9,7 @@ export interface LinePromotionDiscount {
|
|
|
7
9
|
originalPriceCents: number;
|
|
8
10
|
discountCents: number;
|
|
9
11
|
finalPriceCents: number;
|
|
12
|
+
/** Descuento de cada promo en la línea; sin el campo, la de `promotionId` lleva todo menos el código. */
|
|
13
|
+
promotionDiscounts?: LinePromotionAmount[];
|
|
14
|
+
codeDiscountCents?: number;
|
|
10
15
|
}
|
|
@@ -22,6 +22,8 @@ export interface SalePromotion {
|
|
|
22
22
|
/** `null` = permanente. */
|
|
23
23
|
validUntil: string | null;
|
|
24
24
|
oncePerCustomer: boolean;
|
|
25
|
+
/** Si se puede sumar con otras promos en la misma línea. */
|
|
26
|
+
stackable: boolean;
|
|
25
27
|
status: SalePromotionStatusEnum;
|
|
26
28
|
/** Derivada; `null` cuando el estado no es `ACTIVE`. */
|
|
27
29
|
phase: SalePromotionPhaseEnum | null;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsInt, IsNotEmpty, IsString, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/** Promo de venta fijada en la sesión o en la venta, con su versión. */
|
|
5
|
+
export class SalePromotionRef {
|
|
6
|
+
@Expose()
|
|
7
|
+
@IsString()
|
|
8
|
+
@IsNotEmpty()
|
|
9
|
+
promotionId!: string;
|
|
10
|
+
|
|
11
|
+
@Expose()
|
|
12
|
+
@IsInt()
|
|
13
|
+
@Min(1)
|
|
14
|
+
promotionVersion!: number;
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsArray, IsInt, IsNotEmpty, IsOptional, IsString, Min } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/** Promo apartada en la lista nueva: a qué líneas aplica y qué regalos eligió el cliente. */
|
|
5
|
+
export class HoldPromotionInput {
|
|
6
|
+
@Expose()
|
|
7
|
+
@IsString()
|
|
8
|
+
@IsNotEmpty()
|
|
9
|
+
promotionId!: string;
|
|
10
|
+
|
|
11
|
+
@Expose()
|
|
12
|
+
@IsInt()
|
|
13
|
+
@Min(1)
|
|
14
|
+
promotionVersion!: number;
|
|
15
|
+
|
|
16
|
+
/** Líneas que toca la promo; ausente = las que resuelva el lambda. */
|
|
17
|
+
@Expose()
|
|
18
|
+
@IsOptional()
|
|
19
|
+
@IsArray()
|
|
20
|
+
@IsString({ each: true })
|
|
21
|
+
@IsNotEmpty({ each: true })
|
|
22
|
+
lineIds?: string[];
|
|
23
|
+
|
|
24
|
+
@Expose()
|
|
25
|
+
@IsOptional()
|
|
26
|
+
@IsArray()
|
|
27
|
+
@IsString({ each: true })
|
|
28
|
+
@IsNotEmpty({ each: true })
|
|
29
|
+
giftSkus?: string[];
|
|
30
|
+
}
|
|
@@ -1,6 +1,18 @@
|
|
|
1
1
|
import { Expose, Type } from 'class-transformer';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
IsArray,
|
|
4
|
+
IsInt,
|
|
5
|
+
IsNotEmpty,
|
|
6
|
+
IsObject,
|
|
7
|
+
IsOptional,
|
|
8
|
+
IsString,
|
|
9
|
+
Validate,
|
|
10
|
+
ValidateIf,
|
|
11
|
+
ValidateNested,
|
|
12
|
+
} from 'class-validator';
|
|
13
|
+
import { IsHoldModeExclusiveConstraint } from '../../validators/IsHoldModeExclusiveConstraint';
|
|
3
14
|
import { HoldGiftPromotionInput } from './HoldGiftPromotionInput';
|
|
15
|
+
import { HoldPromotionInput } from './HoldPromotionInput';
|
|
4
16
|
|
|
5
17
|
/**
|
|
6
18
|
* Body de publicHoldSalePromotion (PUT, `sessionId` en el path): hasta una promo de regalo, una de venta
|
|
@@ -9,7 +21,7 @@ import { HoldGiftPromotionInput } from './HoldGiftPromotionInput';
|
|
|
9
21
|
export class HoldSalePromotionRequest {
|
|
10
22
|
/**
|
|
11
23
|
* Promo de regalo del teléfono (su única palanca es el regalo).
|
|
12
|
-
*
|
|
24
|
+
* @deprecated Usa `promotions`; no se combina con él.
|
|
13
25
|
*/
|
|
14
26
|
@Expose()
|
|
15
27
|
@IsOptional()
|
|
@@ -20,7 +32,7 @@ export class HoldSalePromotionRequest {
|
|
|
20
32
|
|
|
21
33
|
/**
|
|
22
34
|
* Promo de venta; `null` = «Ninguna».
|
|
23
|
-
*
|
|
35
|
+
* @deprecated Usa `promotions`; no se combina con él.
|
|
24
36
|
*/
|
|
25
37
|
@Expose()
|
|
26
38
|
@IsOptional()
|
|
@@ -33,7 +45,10 @@ export class HoldSalePromotionRequest {
|
|
|
33
45
|
@IsInt()
|
|
34
46
|
promotionVersion?: number | null;
|
|
35
47
|
|
|
36
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Regalo incluido en la promo de venta, solo si esa promo además trae otras palancas.
|
|
50
|
+
* @deprecated Usa `giftSkus` en `promotions`.
|
|
51
|
+
*/
|
|
37
52
|
@Expose()
|
|
38
53
|
@IsOptional()
|
|
39
54
|
@IsArray()
|
|
@@ -45,4 +60,13 @@ export class HoldSalePromotionRequest {
|
|
|
45
60
|
@IsString()
|
|
46
61
|
@IsNotEmpty()
|
|
47
62
|
discountCode?: string | null;
|
|
63
|
+
|
|
64
|
+
/** Promos apartadas, cada una con sus líneas y regalos; excluyente con la forma vieja. */
|
|
65
|
+
@Expose()
|
|
66
|
+
@IsOptional()
|
|
67
|
+
@IsArray()
|
|
68
|
+
@ValidateNested({ each: true })
|
|
69
|
+
@Type(() => HoldPromotionInput)
|
|
70
|
+
@Validate(IsHoldModeExclusiveConstraint)
|
|
71
|
+
promotions?: HoldPromotionInput[] | null;
|
|
48
72
|
}
|
|
@@ -29,6 +29,10 @@ export interface StoreSalePromotionItem {
|
|
|
29
29
|
scope: SalePromotionScope;
|
|
30
30
|
/** SUREKEEP_ONLY = solo con crédito SureKeep; ANY_FINANCIER = también contado o derivada. */
|
|
31
31
|
appliesTo: PromotionAppliesToEnum;
|
|
32
|
+
/** Si se puede sumar con otras promos en la misma línea. */
|
|
33
|
+
stackable: boolean;
|
|
34
|
+
/** Ocupa el lugar de la línea ancla (teléfono o kit), aunque no la toque directo. */
|
|
35
|
+
anchorBound: boolean;
|
|
32
36
|
conditions?: SalePromotionConditions;
|
|
33
37
|
/** Si viene, todavía no se puede decidir para este cliente (ej. CUSTOMER_NOT_IDENTIFIED). */
|
|
34
38
|
blockedBy?: RetailPromotionsErrorCodeEnum | null;
|
|
@@ -69,6 +73,10 @@ export interface EvaluatedSalePromotion {
|
|
|
69
73
|
afterSale: AfterSaleBenefit[];
|
|
70
74
|
/** SUREKEEP_ONLY = solo con crédito SureKeep; ANY_FINANCIER = también contado o derivada. */
|
|
71
75
|
appliesTo: PromotionAppliesToEnum;
|
|
76
|
+
/** Si se puede sumar con otras promos en la misma línea. */
|
|
77
|
+
stackable: boolean;
|
|
78
|
+
/** Ocupa el lugar de la línea ancla (teléfono o kit), aunque no la toque directo. */
|
|
79
|
+
anchorBound: boolean;
|
|
72
80
|
conditions?: SalePromotionConditions;
|
|
73
81
|
/** Si viene, la promo se muestra pero no se puede elegir; dice por qué (ej. CUSTOMER_NOT_IDENTIFIED). */
|
|
74
82
|
blockedBy?: RetailPromotionsErrorCodeEnum | null;
|
|
@@ -95,6 +103,16 @@ export interface HeldGiftPromotion {
|
|
|
95
103
|
skus: string[];
|
|
96
104
|
}
|
|
97
105
|
|
|
106
|
+
/** Promo apartada en la lista nueva, con las líneas que toca y los regalos elegidos. */
|
|
107
|
+
export interface HeldPromotion {
|
|
108
|
+
promotionId: string;
|
|
109
|
+
promotionVersion: number;
|
|
110
|
+
kind: SalePromotionKindEnum;
|
|
111
|
+
stackable: boolean;
|
|
112
|
+
lineIds: string[];
|
|
113
|
+
giftSkus: string[];
|
|
114
|
+
}
|
|
115
|
+
|
|
98
116
|
/** Lo que quedó apartado y escrito en la sesión: la promo de regalo, la de venta y el código. */
|
|
99
117
|
export interface HoldSalePromotionResponse {
|
|
100
118
|
sessionId: string;
|
|
@@ -106,6 +124,8 @@ export interface HoldSalePromotionResponse {
|
|
|
106
124
|
saleGiftSkus: string[];
|
|
107
125
|
lines: LinePromotionDiscount[];
|
|
108
126
|
totalDiscountCents: number;
|
|
127
|
+
/** Todas las promos apartadas; los campos de arriba se conservan por compatibilidad. */
|
|
128
|
+
promotions: HeldPromotion[];
|
|
109
129
|
}
|
|
110
130
|
|
|
111
131
|
/** `released: false` = no había apartado; soltar es idempotente. */
|
|
@@ -142,6 +162,7 @@ export interface SaleAppliedPromotion {
|
|
|
142
162
|
version: number;
|
|
143
163
|
name: string;
|
|
144
164
|
kind: SalePromotionKindEnum;
|
|
165
|
+
stackable?: boolean;
|
|
145
166
|
}
|
|
146
167
|
|
|
147
168
|
/** Cupón y «lo que se lleva» de una venta cerrada (publicGetSaleCoupon). */
|
|
@@ -45,4 +45,6 @@ export enum RetailPromotionsErrorCodeEnum {
|
|
|
45
45
|
CATALOG_SKU_NOT_FOUND = 'CATALOG_SKU_NOT_FOUND',
|
|
46
46
|
/** Con el reparto apagado no se aplican palancas de precio ni código; los regalos sí. */
|
|
47
47
|
SPLIT_FINANCING_DISABLED = 'SPLIT_FINANCING_DISABLED',
|
|
48
|
+
/** La promo no acumulable choca con otra en la misma línea. */
|
|
49
|
+
PROMOTION_NOT_STACKABLE = 'PROMOTION_NOT_STACKABLE',
|
|
48
50
|
}
|
|
@@ -34,6 +34,11 @@ export * from './dtos/SalePromotion';
|
|
|
34
34
|
export * from './dtos/SalePromotionLogEntry';
|
|
35
35
|
export * from './dtos/DiscountCode';
|
|
36
36
|
export * from './dtos/LinePromotionDiscount';
|
|
37
|
+
export * from './dtos/LinePromotionAmount';
|
|
38
|
+
export * from './dtos/SalePromotionRef';
|
|
39
|
+
|
|
40
|
+
// Validadores
|
|
41
|
+
export * from './validators/IsHoldModeExclusiveConstraint';
|
|
37
42
|
|
|
38
43
|
// Request DTOs
|
|
39
44
|
export * from './dtos/requests/DiscountValueInput';
|
|
@@ -50,6 +55,7 @@ export * from './dtos/requests/ChangeDiscountCodeStatusRequest';
|
|
|
50
55
|
export * from './dtos/requests/ListStoreSalePromotionsQuery';
|
|
51
56
|
export * from './dtos/requests/EvaluateSalePromotionsRequest';
|
|
52
57
|
export * from './dtos/requests/HoldGiftPromotionInput';
|
|
58
|
+
export * from './dtos/requests/HoldPromotionInput';
|
|
53
59
|
export * from './dtos/requests/HoldSalePromotionRequest';
|
|
54
60
|
export * from './dtos/requests/GetSaleCouponQuery';
|
|
55
61
|
export * from './dtos/requests/SkuSalePromotionsQuery';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ValidatorConstraint,
|
|
3
|
+
type ValidationArguments,
|
|
4
|
+
type ValidatorConstraintInterface,
|
|
5
|
+
} from 'class-validator';
|
|
6
|
+
|
|
7
|
+
/** Rechaza mandar `promotions` junto con `gift`, `promotionId` o `saleGiftSkus` (forma vieja). */
|
|
8
|
+
@ValidatorConstraint({ name: 'isHoldModeExclusive', async: false })
|
|
9
|
+
export class IsHoldModeExclusiveConstraint implements ValidatorConstraintInterface {
|
|
10
|
+
validate(_value: unknown, args: ValidationArguments): boolean {
|
|
11
|
+
const o = args.object as { gift?: unknown; promotionId?: unknown; saleGiftSkus?: unknown };
|
|
12
|
+
return o.gift == null && o.promotionId == null && o.saleGiftSkus == null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
defaultMessage(): string {
|
|
16
|
+
return 'promotions no se combina con gift, promotionId ni saleGiftSkus';
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -5,6 +5,7 @@ import type { ExternalProviderEnum } from '../enums/ExternalProviderEnum';
|
|
|
5
5
|
import type { CentralSyncStatusEnum } from '../enums/CentralSyncStatusEnum';
|
|
6
6
|
import type { SalePaymentDetail } from './SalePaymentDetail';
|
|
7
7
|
import type { SaleLine } from './SaleLine';
|
|
8
|
+
import type { SalePromotionRef } from '../../retailPromotions/dtos/SalePromotionRef';
|
|
8
9
|
|
|
9
10
|
/**
|
|
10
11
|
* Venta (modelo-datos §3). pk `SALE#<saleId>` · sk `META` · 4 GSIs (`retailerId-soldAt-index`,
|
|
@@ -32,6 +33,8 @@ export interface Sale {
|
|
|
32
33
|
/** Promo de venta congelada al cerrar; `null` = ninguna. */
|
|
33
34
|
salePromotionId?: string | null;
|
|
34
35
|
salePromotionVersion?: number | null;
|
|
36
|
+
/** Todas las promos de venta congeladas al cerrar. */
|
|
37
|
+
salePromotions?: SalePromotionRef[] | null;
|
|
35
38
|
/** IMEI consumido del silo también en contado (local-first); null en EXTERNAL_REFERRAL (D15). */
|
|
36
39
|
imei: string | null;
|
|
37
40
|
/** Lo cobrado en caja al cerrar: enganche + lo no financiado; 0 en EXTERNAL_REFERRAL. Contrato de `SaleCompletedV1`. */
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LinePromotionAmount } from '../../retailPromotions/dtos/LinePromotionAmount';
|
|
1
2
|
import type { WizardLineRoleEnum } from '../enums/WizardLineRoleEnum';
|
|
2
3
|
|
|
3
4
|
/** Campos de Promociones en la línea de sesión y de venta; solo los escribe su endpoint M2M. */
|
|
@@ -13,4 +14,7 @@ export interface WizardLinePromotionFields {
|
|
|
13
14
|
discountCents?: number;
|
|
14
15
|
/** Precio antes del descuento; en un regalo, el valor de lista con la línea a $0. */
|
|
15
16
|
originalPriceCents?: number;
|
|
17
|
+
/** Descuento de cada promo en la línea; sin el campo, la de `promotionId` lleva todo menos el código. */
|
|
18
|
+
promotionDiscounts?: LinePromotionAmount[];
|
|
19
|
+
codeDiscountCents?: number;
|
|
16
20
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SalePromotionRef } from '../../retailPromotions/dtos/SalePromotionRef';
|
|
1
2
|
import type { WizardStateEnum } from '../enums/WizardStateEnum';
|
|
2
3
|
import type { WizardStepEnum } from '../enums/WizardStepEnum';
|
|
3
4
|
import type { FiadoPhoneStatusEnum } from '../enums/FiadoPhoneStatusEnum';
|
|
@@ -126,6 +127,8 @@ export interface WizardSession {
|
|
|
126
127
|
/** Promo de venta que fijó Promociones; `null` = ninguna. */
|
|
127
128
|
salePromotionId?: string | null;
|
|
128
129
|
salePromotionVersion?: number | null;
|
|
130
|
+
/** Todas las promos de venta fijadas; `salePromotionId` queda como la principal. */
|
|
131
|
+
salePromotions?: SalePromotionRef[] | null;
|
|
129
132
|
/** Enganche cobrado que queda por reembolsar tras cancelar. */
|
|
130
133
|
refundPendingCents?: number;
|
|
131
134
|
paymentMethod: PaymentMethodEnum | null;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsInt, IsNotEmpty, IsOptional, IsString, Min, ValidateIf } from 'class-validator';
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { IsArray, IsInt, IsNotEmpty, IsOptional, IsString, Min, ValidateIf, ValidateNested } from 'class-validator';
|
|
3
|
+
import { LinePromotionAmount } from '../../../retailPromotions/dtos/LinePromotionAmount';
|
|
3
4
|
|
|
4
5
|
const hasPromotion = (o: ApplyLinePromotionRequest): boolean =>
|
|
5
6
|
o.promotionId !== undefined || o.promotionVersion !== undefined;
|
|
@@ -41,4 +42,19 @@ export class ApplyLinePromotionRequest {
|
|
|
41
42
|
@IsInt()
|
|
42
43
|
@Min(0)
|
|
43
44
|
originalPriceCents?: number;
|
|
45
|
+
|
|
46
|
+
/** Descuento de cada promo en la línea; `promotionId` queda como la principal. */
|
|
47
|
+
@Expose()
|
|
48
|
+
@IsOptional()
|
|
49
|
+
@IsArray()
|
|
50
|
+
@ValidateNested({ each: true })
|
|
51
|
+
@Type(() => LinePromotionAmount)
|
|
52
|
+
promotionDiscounts?: LinePromotionAmount[];
|
|
53
|
+
|
|
54
|
+
/** Parte de `discountCents` que viene del código. */
|
|
55
|
+
@Expose()
|
|
56
|
+
@IsOptional()
|
|
57
|
+
@IsInt()
|
|
58
|
+
@Min(0)
|
|
59
|
+
codeDiscountCents?: number;
|
|
44
60
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsInt, IsNotEmpty, IsString, Min, ValidateIf } from 'class-validator';
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { IsArray, IsInt, IsNotEmpty, IsOptional, IsString, Min, ValidateIf, ValidateNested } from 'class-validator';
|
|
3
|
+
import { SalePromotionRef } from '../../../retailPromotions/dtos/SalePromotionRef';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* PUT /private/wizard/sessions/{id}/sale-promotion (privateApplySalePromotion, M2M de Promociones).
|
|
@@ -21,4 +22,12 @@ export class ApplySalePromotionRequest {
|
|
|
21
22
|
@IsInt()
|
|
22
23
|
@Min(1)
|
|
23
24
|
salePromotionVersion!: number | null;
|
|
25
|
+
|
|
26
|
+
/** Todas las promos de venta fijadas; `salePromotionId` queda como la principal. */
|
|
27
|
+
@Expose()
|
|
28
|
+
@IsOptional()
|
|
29
|
+
@IsArray()
|
|
30
|
+
@ValidateNested({ each: true })
|
|
31
|
+
@Type(() => SalePromotionRef)
|
|
32
|
+
salePromotions?: SalePromotionRef[] | null;
|
|
24
33
|
}
|
|
@@ -3,6 +3,7 @@ import type { WizardStateEnum } from '../../enums/WizardStateEnum';
|
|
|
3
3
|
import type { WizardStepEnum } from '../../enums/WizardStepEnum';
|
|
4
4
|
import type { WizardPayment } from '../WizardPayment';
|
|
5
5
|
import type { WizardReservedLine } from '../WizardReservedLine';
|
|
6
|
+
import type { SalePromotionRef } from '../../../retailPromotions/dtos/SalePromotionRef';
|
|
6
7
|
|
|
7
8
|
/** GET /private/wizard/sessions/{id} (privateGetWizardSession): lo que Promociones lee de la sesión. */
|
|
8
9
|
export interface PrivateWizardSessionResponse {
|
|
@@ -19,6 +20,8 @@ export interface PrivateWizardSessionResponse {
|
|
|
19
20
|
anchorLineId: string | null;
|
|
20
21
|
salePromotionId: string | null;
|
|
21
22
|
salePromotionVersion: number | null;
|
|
23
|
+
/** Todas las promos de venta fijadas; `salePromotionId` queda como la principal. */
|
|
24
|
+
salePromotions?: SalePromotionRef[] | null;
|
|
22
25
|
/** Líneas vivas con los campos de Promociones y financedCents. */
|
|
23
26
|
lines: WizardReservedLine[];
|
|
24
27
|
fiadoDirectoryId?: string | null;
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
Min,
|
|
13
13
|
ValidateNested,
|
|
14
14
|
} from 'class-validator';
|
|
15
|
+
import { LinePromotionAmount } from '../../retailPromotions/dtos/LinePromotionAmount';
|
|
16
|
+
import { SalePromotionRef } from '../../retailPromotions/dtos/SalePromotionRef';
|
|
15
17
|
import { RetailPromotionInboundMessageTypeEnum } from '../enums/RetailPromotionInboundMessageTypeEnum';
|
|
16
18
|
import { WizardLineRoleEnum } from '../enums/WizardLineRoleEnum';
|
|
17
19
|
|
|
@@ -59,6 +61,19 @@ export class RetailPromotionInboundLine {
|
|
|
59
61
|
@Min(0)
|
|
60
62
|
discountCents?: number;
|
|
61
63
|
|
|
64
|
+
/** Descuento de cada promo en la línea; `promotionId` queda como la principal. */
|
|
65
|
+
@IsOptional()
|
|
66
|
+
@IsArray()
|
|
67
|
+
@ValidateNested({ each: true })
|
|
68
|
+
@Type(() => LinePromotionAmount)
|
|
69
|
+
promotionDiscounts?: LinePromotionAmount[];
|
|
70
|
+
|
|
71
|
+
/** Parte de `discountCents` que viene del código. */
|
|
72
|
+
@IsOptional()
|
|
73
|
+
@IsInt()
|
|
74
|
+
@Min(0)
|
|
75
|
+
codeDiscountCents?: number;
|
|
76
|
+
|
|
62
77
|
/** Si la línea no lo trae, basePriceCents × qty. */
|
|
63
78
|
@IsInt()
|
|
64
79
|
@Min(0)
|
|
@@ -132,6 +147,13 @@ export class RetailPromotionInboundMessage {
|
|
|
132
147
|
@Min(1)
|
|
133
148
|
salePromotionVersion?: number | null;
|
|
134
149
|
|
|
150
|
+
/** Todas las promos de venta de la venta; `salePromotionId` queda como la principal. */
|
|
151
|
+
@IsOptional()
|
|
152
|
+
@IsArray()
|
|
153
|
+
@ValidateNested({ each: true })
|
|
154
|
+
@Type(() => SalePromotionRef)
|
|
155
|
+
salePromotions?: SalePromotionRef[] | null;
|
|
156
|
+
|
|
135
157
|
@IsArray()
|
|
136
158
|
@ArrayMinSize(1)
|
|
137
159
|
@ValidateNested({ each: true })
|
|
@@ -46,6 +46,8 @@ export * from './dtos/WizardBlocked';
|
|
|
46
46
|
export * from './dtos/WizardLineProduct';
|
|
47
47
|
export * from './dtos/WizardLineIdentifiers';
|
|
48
48
|
export * from './dtos/WizardLinePromotionFields';
|
|
49
|
+
export { LinePromotionAmount } from '../retailPromotions/dtos/LinePromotionAmount';
|
|
50
|
+
export { SalePromotionRef } from '../retailPromotions/dtos/SalePromotionRef';
|
|
49
51
|
export * from './dtos/WizardFinancingPreview';
|
|
50
52
|
export * from './dtos/WizardReservedLine';
|
|
51
53
|
export * from './dtos/WizardGiftLine';
|