@fiado/type-kit 3.488.0 → 3.490.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/loanOfferings/appliedPromotionBonus.test.ts +22 -0
- package/_test_/unit/retailCustomer/promotionBonusContract.test.ts +101 -0
- package/_test_/unit/retailCustomer/upsertCustomerFromKycRequest.test.ts +51 -0
- package/_test_/unit/retailWizard/creditPromotionBonusContract.test.ts +26 -0
- package/bin/loanOfferings/dtos/responses/SimulationResultResponse.d.ts +2 -0
- package/bin/retailCustomer/dtos/requests/UpsertCustomerFromKycRequest.d.ts +5 -0
- package/bin/retailCustomer/dtos/requests/UpsertCustomerFromKycRequest.js +13 -0
- package/bin/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.d.ts +27 -0
- package/bin/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.js +139 -0
- package/bin/retailCustomer/dtos/responses/PromotionBonusEntryResponse.d.ts +27 -0
- package/bin/retailCustomer/dtos/responses/PromotionBonusEntryResponse.js +2 -0
- package/bin/retailCustomer/enums/BonusTypeEnum.d.ts +6 -0
- package/bin/retailCustomer/enums/BonusTypeEnum.js +10 -0
- package/bin/retailCustomer/enums/KycMethodEnum.d.ts +5 -0
- package/bin/retailCustomer/enums/KycMethodEnum.js +9 -0
- package/bin/retailCustomer/index.d.ts +4 -0
- package/bin/retailCustomer/index.js +4 -0
- package/bin/retailWizard/dtos/WizardCreditPromotionBonus.d.ts +8 -0
- package/bin/retailWizard/dtos/WizardCreditPromotionBonus.js +2 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +3 -0
- package/bin/retailWizard/dtos/responses/QuoteResponse.d.ts +2 -0
- package/bin/retailWizard/index.d.ts +1 -0
- package/bin/retailWizard/index.js +1 -0
- package/package.json +1 -1
- package/src/loanOfferings/dtos/responses/SimulationResultResponse.ts +2 -0
- package/src/retailCustomer/dtos/requests/UpsertCustomerFromKycRequest.ts +13 -0
- package/src/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.ts +121 -0
- package/src/retailCustomer/dtos/responses/PromotionBonusEntryResponse.ts +28 -0
- package/src/retailCustomer/enums/BonusTypeEnum.ts +6 -0
- package/src/retailCustomer/enums/KycMethodEnum.ts +5 -0
- package/src/retailCustomer/index.ts +4 -0
- package/src/retailWizard/dtos/WizardCreditPromotionBonus.ts +9 -0
- package/src/retailWizard/dtos/WizardSession.ts +3 -0
- package/src/retailWizard/dtos/responses/QuoteResponse.ts +2 -0
- package/src/retailWizard/index.ts +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { LoanOfferings } from '../../../src/index';
|
|
3
|
+
import type { AppliedPromotionResponse } from '../../../src/loanOfferings/index';
|
|
4
|
+
|
|
5
|
+
describe('AppliedPromotionResponse.pcfBonusCents', () => {
|
|
6
|
+
it('la ganadora del bono lo trae; las demás lo omiten', () => {
|
|
7
|
+
const winner: AppliedPromotionResponse = {
|
|
8
|
+
promotionId: 'p-1',
|
|
9
|
+
name: 'Bono',
|
|
10
|
+
version: 2,
|
|
11
|
+
appliedEffects: [LoanOfferings.PromotionEffectKindEnum.PCF_BONUS],
|
|
12
|
+
pcfBonusCents: 5_000,
|
|
13
|
+
};
|
|
14
|
+
const other: AppliedPromotionResponse = {
|
|
15
|
+
promotionId: 'p-0',
|
|
16
|
+
name: 'Tasa',
|
|
17
|
+
version: 1,
|
|
18
|
+
appliedEffects: [LoanOfferings.PromotionEffectKindEnum.TNA_OVERRIDE],
|
|
19
|
+
};
|
|
20
|
+
expect([winner.pcfBonusCents, other.pcfBonusCents]).toEqual([5_000, undefined]);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validateSync } from 'class-validator';
|
|
4
|
+
import { RetailCustomer } from '../../../src/index';
|
|
5
|
+
import { BonusTypeEnum, UpsertPromotionBonusRequest } from '../../../src/retailCustomer/index';
|
|
6
|
+
import type { PromotionBonusEntryResponse } from '../../../src/retailCustomer/index';
|
|
7
|
+
|
|
8
|
+
describe('BonusTypeEnum', () => {
|
|
9
|
+
it('distingue la bienvenida de las dos promociones', () => {
|
|
10
|
+
expect(Object.values(BonusTypeEnum)).toEqual(['WELCOME', 'CREDIT_PROMOTION', 'SALE_PROMOTION']);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('sale por el index raíz', () => {
|
|
14
|
+
expect(RetailCustomer.BonusTypeEnum.SALE_PROMOTION).toBe('SALE_PROMOTION');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/** Valida como retail-customer-business: con `@Expose` y `excludeExtraneousValues`. */
|
|
19
|
+
function errorsOf(payload: Record<string, unknown>): string[] {
|
|
20
|
+
const dto = plainToInstance(UpsertPromotionBonusRequest, payload, { excludeExtraneousValues: true });
|
|
21
|
+
return validateSync(dto).map((e) => e.property);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PENDING_SALE = {
|
|
25
|
+
bonusType: 'SALE_PROMOTION',
|
|
26
|
+
promotionId: 'promo-1',
|
|
27
|
+
sourceId: 'sale-1',
|
|
28
|
+
storeId: 'st-1',
|
|
29
|
+
amountCents: 10_000,
|
|
30
|
+
currency: 'MXN',
|
|
31
|
+
status: 'PENDING',
|
|
32
|
+
idempotencyKey: 'sale-1-WALLET-1',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('UpsertPromotionBonusRequest', () => {
|
|
36
|
+
it('acepta el PENDING «sin cuenta todavía», sin directorio', () => {
|
|
37
|
+
expect(errorsOf(PENDING_SALE)).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('ACCREDITED exige el directorio que recibió el depósito', () => {
|
|
41
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'ACCREDITED' })).toContain('fiadoDirectoryId');
|
|
42
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'ACCREDITED', fiadoDirectoryId: 'dir-1' })).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each(['FAILED', 'UNVERIFIED'])('%s exige el motivo', (status) => {
|
|
46
|
+
expect(errorsOf({ ...PENDING_SALE, status })).toContain('failReason');
|
|
47
|
+
expect(errorsOf({ ...PENDING_SALE, status, failReason: 'VOID_EXPIRED' })).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('rechaza WELCOME: la bienvenida tiene su propio endpoint', () => {
|
|
51
|
+
expect(errorsOf({ ...PENDING_SALE, bonusType: 'WELCOME' })).toContain('bonusType');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it.each(['promotionId', 'sourceId', 'storeId', 'currency', 'idempotencyKey'])('rechaza %s vacío', (field) => {
|
|
55
|
+
expect(errorsOf({ ...PENDING_SALE, [field]: ' ' })).toContain(field);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it.each([0, -1, 1.5])('rechaza el monto %p', (amountCents) => {
|
|
59
|
+
expect(errorsOf({ ...PENDING_SALE, amountCents })).toContain('amountCents');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('rechaza un estado fuera del libro', () => {
|
|
63
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'SETTLED' })).toContain('status');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('rechaza un motivo de más de 256 caracteres', () => {
|
|
67
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'FAILED', failReason: 'x'.repeat(257) })).toContain('failReason');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('sale por el index raíz', () => {
|
|
71
|
+
expect(new RetailCustomer.UpsertPromotionBonusRequest()).toBeInstanceOf(UpsertPromotionBonusRequest);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('PromotionBonusEntryResponse', () => {
|
|
76
|
+
it('describe el asiento de promo con su fuente', () => {
|
|
77
|
+
const entry: PromotionBonusEntryResponse = {
|
|
78
|
+
bonusId: 'B-1',
|
|
79
|
+
bonusType: BonusTypeEnum.CREDIT_PROMOTION,
|
|
80
|
+
promotionId: 'promo-1',
|
|
81
|
+
sourceId: 'credit-1',
|
|
82
|
+
retailCustomerId: 'rc-1',
|
|
83
|
+
fiadoDirectoryId: null,
|
|
84
|
+
saleId: null,
|
|
85
|
+
creditId: 'credit-1',
|
|
86
|
+
storeId: 'st-1',
|
|
87
|
+
amountCents: 5_000,
|
|
88
|
+
currency: 'MXN',
|
|
89
|
+
status: RetailCustomer.WelcomeBonusStatusEnum.PENDING,
|
|
90
|
+
failReason: null,
|
|
91
|
+
processorTransactionId: null,
|
|
92
|
+
idempotencyKey: 'credit-1',
|
|
93
|
+
fundedBy: 'VENTASLUGA',
|
|
94
|
+
settlementStatus: RetailCustomer.WelcomeBonusSettlementStatusEnum.PENDING,
|
|
95
|
+
resolvedAt: null,
|
|
96
|
+
createdAt: '2026-10-02T00:00:00.000Z',
|
|
97
|
+
updatedAt: '2026-10-02T00:00:00.000Z',
|
|
98
|
+
};
|
|
99
|
+
expect(entry.sourceId).toBe('credit-1');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validateSync } from 'class-validator';
|
|
4
|
+
import { UpsertCustomerFromKycRequest } from '../../../src/retailCustomer/dtos/requests/UpsertCustomerFromKycRequest';
|
|
5
|
+
import { KycMethodEnum } from '../../../src/retailCustomer/enums/KycMethodEnum';
|
|
6
|
+
import { KycModalityEnum } from '../../../src/retailCustomer/enums/KycModalityEnum';
|
|
7
|
+
import { RetailKycStatusEnum } from '../../../src/retailCustomer/enums/RetailKycStatusEnum';
|
|
8
|
+
|
|
9
|
+
/** Valida como lo hace `retail-customer-business`: con `@Expose` y `excludeExtraneousValues`. */
|
|
10
|
+
function toDto(payload: Record<string, unknown>): UpsertCustomerFromKycRequest {
|
|
11
|
+
return plainToInstance(UpsertCustomerFromKycRequest, payload, { excludeExtraneousValues: true });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const base = {
|
|
15
|
+
originStoreId: 'store-1',
|
|
16
|
+
names: 'Ana',
|
|
17
|
+
paternalLastName: 'Pérez',
|
|
18
|
+
phone: '5512345678',
|
|
19
|
+
curp: 'PEGA900101MDFRRN09',
|
|
20
|
+
fiadoDirectoryId: 'dir-1',
|
|
21
|
+
fiadoPeopleId: 'people-1',
|
|
22
|
+
kycStatus: Object.values(RetailKycStatusEnum)[0],
|
|
23
|
+
kycModality: KycModalityEnum.STORE_DEVICE,
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
describe('UpsertCustomerFromKycRequest — método de KYC', () => {
|
|
27
|
+
it('KycMethodEnum expone FULL_KYC y FACEMATCH', () => {
|
|
28
|
+
expect(Object.values(KycMethodEnum)).toEqual(['FULL_KYC', 'FACEMATCH']);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('acepta el payload sin kycMethod ni biometricVerificationId', () => {
|
|
32
|
+
expect(validateSync(toDto(base)).map((e) => e.property)).toEqual([]);
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
it('conserva kycMethod y biometricVerificationId del alta por facematch', () => {
|
|
36
|
+
const dto = toDto({ ...base, kycMethod: KycMethodEnum.FACEMATCH, biometricVerificationId: 'bv-1' });
|
|
37
|
+
expect(validateSync(dto)).toEqual([]);
|
|
38
|
+
expect(dto.kycMethod).toBe(KycMethodEnum.FACEMATCH);
|
|
39
|
+
expect(dto.biometricVerificationId).toBe('bv-1');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('rechaza un kycMethod fuera del catálogo', () => {
|
|
43
|
+
expect(validateSync(toDto({ ...base, kycMethod: 'SELFIE' })).map((e) => e.property)).toEqual(['kycMethod']);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('rechaza un biometricVerificationId que no es string', () => {
|
|
47
|
+
expect(validateSync(toDto({ ...base, biometricVerificationId: 42 })).map((e) => e.property)).toEqual([
|
|
48
|
+
'biometricVerificationId',
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { LoanOfferings } from '../../../src/index';
|
|
3
|
+
import type { QuoteResponse, WizardCreditPromotionBonus, WizardSession } from '../../../src/retailWizard/index';
|
|
4
|
+
|
|
5
|
+
describe('bono de crédito en el contrato del wizard', () => {
|
|
6
|
+
it('la sesión lleva el desenlace persistido del bono de crédito', () => {
|
|
7
|
+
const bonus: WizardCreditPromotionBonus = {
|
|
8
|
+
promotionId: 'promo-1',
|
|
9
|
+
amountCents: 5_000,
|
|
10
|
+
status: LoanOfferings.PromotionBonusStatusEnum.RESERVED,
|
|
11
|
+
};
|
|
12
|
+
const session: Pick<WizardSession, 'creditPromotionBonus'> = { creditPromotionBonus: bonus };
|
|
13
|
+
expect(session.creditPromotionBonus?.status).toBe('RESERVED');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('la sesión sin bono resuelto lo omite o lo manda null', () => {
|
|
17
|
+
const absent: Pick<WizardSession, 'creditPromotionBonus'> = {};
|
|
18
|
+
const nulled: Pick<WizardSession, 'creditPromotionBonus'> = { creditPromotionBonus: null };
|
|
19
|
+
expect([absent.creditPromotionBonus, nulled.creditPromotionBonus]).toEqual([undefined, null]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('la cotización puede traer el monto del bono que se cotizó', () => {
|
|
23
|
+
const quote: Pick<QuoteResponse, 'creditPromotionBonusCents'> = { creditPromotionBonusCents: 5_000 };
|
|
24
|
+
expect(quote.creditPromotionBonusCents).toBe(5_000);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -27,6 +27,8 @@ export interface AppliedPromotionResponse {
|
|
|
27
27
|
version: number;
|
|
28
28
|
/** Palancas del plan que la promoción pisó. */
|
|
29
29
|
appliedEffects: PromotionEffectKindEnum[];
|
|
30
|
+
/** Monto del bono PCF; solo en la promoción que gana el bono de la cotización. */
|
|
31
|
+
pcfBonusCents?: number;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* Una `CreditPlan` que el paso Base consideró para esta venta, haya ganado algún término o no.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { RetailAddress } from '../RetailAddress';
|
|
2
|
+
import { KycMethodEnum } from '../../enums/KycMethodEnum';
|
|
2
3
|
import { KycModalityEnum } from '../../enums/KycModalityEnum';
|
|
3
4
|
import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
|
|
4
5
|
/**
|
|
@@ -47,6 +48,10 @@ export declare class UpsertCustomerFromKycRequest {
|
|
|
47
48
|
hasActiveMxAccount?: boolean;
|
|
48
49
|
kycStatus: RetailKycStatusEnum;
|
|
49
50
|
kycModality: KycModalityEnum;
|
|
51
|
+
/** Método con el que se verificó la identidad. Ausente = no pisa el valor guardado. */
|
|
52
|
+
kycMethod?: KycMethodEnum;
|
|
53
|
+
/** Verificación biométrica de `biometrics-business` que respaldó el alta por facematch. */
|
|
54
|
+
biometricVerificationId?: string;
|
|
50
55
|
/**
|
|
51
56
|
* Domicilio del INE (paso 02e), ya normalizado contra `places-business`. Se persiste embebido en la
|
|
52
57
|
* MISMA TX del upsert.
|
|
@@ -14,6 +14,7 @@ const class_transformer_1 = require("class-transformer");
|
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
15
|
const regex_1 = require("../../../helpers/constans/regex");
|
|
16
16
|
const RetailAddress_1 = require("../RetailAddress");
|
|
17
|
+
const KycMethodEnum_1 = require("../../enums/KycMethodEnum");
|
|
17
18
|
const KycModalityEnum_1 = require("../../enums/KycModalityEnum");
|
|
18
19
|
const RetailKycStatusEnum_1 = require("../../enums/RetailKycStatusEnum");
|
|
19
20
|
/**
|
|
@@ -141,6 +142,18 @@ __decorate([
|
|
|
141
142
|
(0, class_validator_1.IsEnum)(KycModalityEnum_1.KycModalityEnum),
|
|
142
143
|
__metadata("design:type", String)
|
|
143
144
|
], UpsertCustomerFromKycRequest.prototype, "kycModality", void 0);
|
|
145
|
+
__decorate([
|
|
146
|
+
(0, class_transformer_1.Expose)(),
|
|
147
|
+
(0, class_validator_1.IsOptional)(),
|
|
148
|
+
(0, class_validator_1.IsEnum)(KycMethodEnum_1.KycMethodEnum),
|
|
149
|
+
__metadata("design:type", String)
|
|
150
|
+
], UpsertCustomerFromKycRequest.prototype, "kycMethod", void 0);
|
|
151
|
+
__decorate([
|
|
152
|
+
(0, class_transformer_1.Expose)(),
|
|
153
|
+
(0, class_validator_1.IsOptional)(),
|
|
154
|
+
(0, class_validator_1.IsString)(),
|
|
155
|
+
__metadata("design:type", String)
|
|
156
|
+
], UpsertCustomerFromKycRequest.prototype, "biometricVerificationId", void 0);
|
|
144
157
|
__decorate([
|
|
145
158
|
(0, class_transformer_1.Expose)(),
|
|
146
159
|
(0, class_validator_1.IsOptional)(),
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BonusTypeEnum } from '../../enums/BonusTypeEnum';
|
|
2
|
+
import { WelcomeBonusStatusEnum } from '../../enums/WelcomeBonusStatusEnum';
|
|
3
|
+
/**
|
|
4
|
+
* Asiento de un bono de promoción en el libro de bonos:
|
|
5
|
+
* `PUT /private/customers/:id/promotion-bonuses`. El libro es espejo del sistema de la promo.
|
|
6
|
+
*/
|
|
7
|
+
export declare class UpsertPromotionBonusRequest {
|
|
8
|
+
bonusType: BonusTypeEnum;
|
|
9
|
+
promotionId: string;
|
|
10
|
+
/** `creditId` en la promo de crédito, `saleId` en la de venta. */
|
|
11
|
+
sourceId: string;
|
|
12
|
+
/** Tienda de la venta: es la partición del asiento. */
|
|
13
|
+
storeId: string;
|
|
14
|
+
/** Obligatorio con `ACCREDITED`; el `PENDING` «sin cuenta todavía» no lo tiene. */
|
|
15
|
+
fiadoDirectoryId?: string;
|
|
16
|
+
amountCents: number;
|
|
17
|
+
currency: string;
|
|
18
|
+
status: WelcomeBonusStatusEnum;
|
|
19
|
+
idempotencyKey: string;
|
|
20
|
+
processorTransactionId?: string;
|
|
21
|
+
/** Resumen legible, sin el payload crudo del processor (el libro va sin PII). */
|
|
22
|
+
failReason?: string;
|
|
23
|
+
saleId?: string;
|
|
24
|
+
folio?: string;
|
|
25
|
+
wizardSessionId?: string;
|
|
26
|
+
sellerRetailUserId?: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
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
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.UpsertPromotionBonusRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const Trim_1 = require("../../../common/decorators/Trim");
|
|
16
|
+
const BonusTypeEnum_1 = require("../../enums/BonusTypeEnum");
|
|
17
|
+
const WelcomeBonusStatusEnum_1 = require("../../enums/WelcomeBonusStatusEnum");
|
|
18
|
+
/** Tope del motivo libre que el caller escribe en el asiento. */
|
|
19
|
+
const FREE_TEXT_MAX_LENGTH = 256;
|
|
20
|
+
const PROMOTION_BONUS_TYPES = [BonusTypeEnum_1.BonusTypeEnum.CREDIT_PROMOTION, BonusTypeEnum_1.BonusTypeEnum.SALE_PROMOTION];
|
|
21
|
+
const STATUSES_REQUIRING_REASON = [
|
|
22
|
+
WelcomeBonusStatusEnum_1.WelcomeBonusStatusEnum.FAILED,
|
|
23
|
+
WelcomeBonusStatusEnum_1.WelcomeBonusStatusEnum.UNVERIFIED,
|
|
24
|
+
];
|
|
25
|
+
/**
|
|
26
|
+
* Asiento de un bono de promoción en el libro de bonos:
|
|
27
|
+
* `PUT /private/customers/:id/promotion-bonuses`. El libro es espejo del sistema de la promo.
|
|
28
|
+
*/
|
|
29
|
+
class UpsertPromotionBonusRequest {
|
|
30
|
+
}
|
|
31
|
+
exports.UpsertPromotionBonusRequest = UpsertPromotionBonusRequest;
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, class_transformer_1.Expose)(),
|
|
34
|
+
(0, class_validator_1.IsIn)(PROMOTION_BONUS_TYPES),
|
|
35
|
+
__metadata("design:type", String)
|
|
36
|
+
], UpsertPromotionBonusRequest.prototype, "bonusType", void 0);
|
|
37
|
+
__decorate([
|
|
38
|
+
(0, class_transformer_1.Expose)(),
|
|
39
|
+
(0, Trim_1.Trim)(),
|
|
40
|
+
(0, class_validator_1.IsString)(),
|
|
41
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
42
|
+
__metadata("design:type", String)
|
|
43
|
+
], UpsertPromotionBonusRequest.prototype, "promotionId", void 0);
|
|
44
|
+
__decorate([
|
|
45
|
+
(0, class_transformer_1.Expose)(),
|
|
46
|
+
(0, Trim_1.Trim)(),
|
|
47
|
+
(0, class_validator_1.IsString)(),
|
|
48
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
49
|
+
__metadata("design:type", String)
|
|
50
|
+
], UpsertPromotionBonusRequest.prototype, "sourceId", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, class_transformer_1.Expose)(),
|
|
53
|
+
(0, Trim_1.Trim)(),
|
|
54
|
+
(0, class_validator_1.IsString)(),
|
|
55
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
56
|
+
__metadata("design:type", String)
|
|
57
|
+
], UpsertPromotionBonusRequest.prototype, "storeId", void 0);
|
|
58
|
+
__decorate([
|
|
59
|
+
(0, class_transformer_1.Expose)(),
|
|
60
|
+
(0, class_validator_1.ValidateIf)((o) => o.status === WelcomeBonusStatusEnum_1.WelcomeBonusStatusEnum.ACCREDITED || o.fiadoDirectoryId !== undefined),
|
|
61
|
+
(0, Trim_1.Trim)(),
|
|
62
|
+
(0, class_validator_1.IsString)(),
|
|
63
|
+
(0, class_validator_1.IsNotEmpty)({ message: 'fiadoDirectoryId es obligatorio cuando status es ACCREDITED' }),
|
|
64
|
+
__metadata("design:type", String)
|
|
65
|
+
], UpsertPromotionBonusRequest.prototype, "fiadoDirectoryId", void 0);
|
|
66
|
+
__decorate([
|
|
67
|
+
(0, class_transformer_1.Expose)(),
|
|
68
|
+
(0, class_validator_1.IsInt)(),
|
|
69
|
+
(0, class_validator_1.IsPositive)(),
|
|
70
|
+
__metadata("design:type", Number)
|
|
71
|
+
], UpsertPromotionBonusRequest.prototype, "amountCents", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, class_transformer_1.Expose)(),
|
|
74
|
+
(0, Trim_1.Trim)(),
|
|
75
|
+
(0, class_validator_1.IsString)(),
|
|
76
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
77
|
+
__metadata("design:type", String)
|
|
78
|
+
], UpsertPromotionBonusRequest.prototype, "currency", void 0);
|
|
79
|
+
__decorate([
|
|
80
|
+
(0, class_transformer_1.Expose)(),
|
|
81
|
+
(0, class_validator_1.IsIn)(Object.values(WelcomeBonusStatusEnum_1.WelcomeBonusStatusEnum)),
|
|
82
|
+
__metadata("design:type", String)
|
|
83
|
+
], UpsertPromotionBonusRequest.prototype, "status", void 0);
|
|
84
|
+
__decorate([
|
|
85
|
+
(0, class_transformer_1.Expose)(),
|
|
86
|
+
(0, Trim_1.Trim)(),
|
|
87
|
+
(0, class_validator_1.IsString)(),
|
|
88
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
89
|
+
__metadata("design:type", String)
|
|
90
|
+
], UpsertPromotionBonusRequest.prototype, "idempotencyKey", void 0);
|
|
91
|
+
__decorate([
|
|
92
|
+
(0, class_transformer_1.Expose)(),
|
|
93
|
+
(0, class_validator_1.IsOptional)(),
|
|
94
|
+
(0, Trim_1.Trim)(),
|
|
95
|
+
(0, class_validator_1.IsString)(),
|
|
96
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
97
|
+
__metadata("design:type", String)
|
|
98
|
+
], UpsertPromotionBonusRequest.prototype, "processorTransactionId", void 0);
|
|
99
|
+
__decorate([
|
|
100
|
+
(0, class_transformer_1.Expose)(),
|
|
101
|
+
(0, class_validator_1.ValidateIf)((o) => STATUSES_REQUIRING_REASON.includes(o.status)),
|
|
102
|
+
(0, Trim_1.Trim)(),
|
|
103
|
+
(0, class_validator_1.IsString)(),
|
|
104
|
+
(0, class_validator_1.IsNotEmpty)({ message: 'failReason es obligatorio cuando status es FAILED o UNVERIFIED' }),
|
|
105
|
+
(0, class_validator_1.MaxLength)(FREE_TEXT_MAX_LENGTH),
|
|
106
|
+
__metadata("design:type", String)
|
|
107
|
+
], UpsertPromotionBonusRequest.prototype, "failReason", void 0);
|
|
108
|
+
__decorate([
|
|
109
|
+
(0, class_transformer_1.Expose)(),
|
|
110
|
+
(0, class_validator_1.IsOptional)(),
|
|
111
|
+
(0, Trim_1.Trim)(),
|
|
112
|
+
(0, class_validator_1.IsString)(),
|
|
113
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
114
|
+
__metadata("design:type", String)
|
|
115
|
+
], UpsertPromotionBonusRequest.prototype, "saleId", void 0);
|
|
116
|
+
__decorate([
|
|
117
|
+
(0, class_transformer_1.Expose)(),
|
|
118
|
+
(0, class_validator_1.IsOptional)(),
|
|
119
|
+
(0, Trim_1.Trim)(),
|
|
120
|
+
(0, class_validator_1.IsString)(),
|
|
121
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
122
|
+
__metadata("design:type", String)
|
|
123
|
+
], UpsertPromotionBonusRequest.prototype, "folio", void 0);
|
|
124
|
+
__decorate([
|
|
125
|
+
(0, class_transformer_1.Expose)(),
|
|
126
|
+
(0, class_validator_1.IsOptional)(),
|
|
127
|
+
(0, Trim_1.Trim)(),
|
|
128
|
+
(0, class_validator_1.IsString)(),
|
|
129
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
130
|
+
__metadata("design:type", String)
|
|
131
|
+
], UpsertPromotionBonusRequest.prototype, "wizardSessionId", void 0);
|
|
132
|
+
__decorate([
|
|
133
|
+
(0, class_transformer_1.Expose)(),
|
|
134
|
+
(0, class_validator_1.IsOptional)(),
|
|
135
|
+
(0, Trim_1.Trim)(),
|
|
136
|
+
(0, class_validator_1.IsString)(),
|
|
137
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
138
|
+
__metadata("design:type", String)
|
|
139
|
+
], UpsertPromotionBonusRequest.prototype, "sellerRetailUserId", void 0);
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { BonusTypeEnum } from '../../enums/BonusTypeEnum';
|
|
2
|
+
import { WelcomeBonusSettlementStatusEnum } from '../../enums/WelcomeBonusSettlementStatusEnum';
|
|
3
|
+
import { WelcomeBonusStatusEnum } from '../../enums/WelcomeBonusStatusEnum';
|
|
4
|
+
/** Asiento de un bono de promoción en el libro de bonos. Sin PII. */
|
|
5
|
+
export interface PromotionBonusEntryResponse {
|
|
6
|
+
bonusId: string;
|
|
7
|
+
bonusType: BonusTypeEnum;
|
|
8
|
+
promotionId: string;
|
|
9
|
+
/** `creditId` en la promo de crédito, `saleId` en la de venta. */
|
|
10
|
+
sourceId: string;
|
|
11
|
+
retailCustomerId: string;
|
|
12
|
+
fiadoDirectoryId: string | null;
|
|
13
|
+
saleId: string | null;
|
|
14
|
+
creditId: string | null;
|
|
15
|
+
storeId: string;
|
|
16
|
+
amountCents: number | null;
|
|
17
|
+
currency: string;
|
|
18
|
+
status: WelcomeBonusStatusEnum;
|
|
19
|
+
failReason: string | null;
|
|
20
|
+
processorTransactionId: string | null;
|
|
21
|
+
idempotencyKey: string | null;
|
|
22
|
+
fundedBy: string;
|
|
23
|
+
settlementStatus: WelcomeBonusSettlementStatusEnum;
|
|
24
|
+
resolvedAt: string | null;
|
|
25
|
+
createdAt: string;
|
|
26
|
+
updatedAt: string;
|
|
27
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.BonusTypeEnum = void 0;
|
|
4
|
+
/** Tipo de bono del libro de bonos. Un asiento sin tipo se lee como `WELCOME`. */
|
|
5
|
+
var BonusTypeEnum;
|
|
6
|
+
(function (BonusTypeEnum) {
|
|
7
|
+
BonusTypeEnum["WELCOME"] = "WELCOME";
|
|
8
|
+
BonusTypeEnum["CREDIT_PROMOTION"] = "CREDIT_PROMOTION";
|
|
9
|
+
BonusTypeEnum["SALE_PROMOTION"] = "SALE_PROMOTION";
|
|
10
|
+
})(BonusTypeEnum || (exports.BonusTypeEnum = BonusTypeEnum = {}));
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.KycMethodEnum = void 0;
|
|
4
|
+
/** Cómo se verificó la identidad del cliente: KYC completo o solo selfie contra la cuenta existente. */
|
|
5
|
+
var KycMethodEnum;
|
|
6
|
+
(function (KycMethodEnum) {
|
|
7
|
+
KycMethodEnum["FULL_KYC"] = "FULL_KYC";
|
|
8
|
+
KycMethodEnum["FACEMATCH"] = "FACEMATCH";
|
|
9
|
+
})(KycMethodEnum || (exports.KycMethodEnum = KycMethodEnum = {}));
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
export * from './enums/AccountStatusEnum';
|
|
2
2
|
export * from './enums/RetailKycStatusEnum';
|
|
3
3
|
export * from './enums/KycModalityEnum';
|
|
4
|
+
export * from './enums/KycMethodEnum';
|
|
4
5
|
export * from './enums/KycReviewResultEnum';
|
|
5
6
|
export * from './enums/RetailCustomerStatusEnum';
|
|
6
7
|
export * from './enums/WelcomeBonusStatusEnum';
|
|
7
8
|
export * from './enums/WelcomeBonusAmountSourceEnum';
|
|
8
9
|
export * from './enums/WelcomeBonusSettlementStatusEnum';
|
|
10
|
+
export * from './enums/BonusTypeEnum';
|
|
9
11
|
export * from './dtos/KycReview';
|
|
10
12
|
export * from './dtos/RetailAddress';
|
|
11
13
|
export * from './dtos/RetailCustomer';
|
|
@@ -16,10 +18,12 @@ export * from './dtos/requests/AccountResultRequest';
|
|
|
16
18
|
export * from './dtos/requests/KycReviewRequest';
|
|
17
19
|
export * from './dtos/requests/WelcomeAccreditationRequest';
|
|
18
20
|
export * from './dtos/requests/WelcomeAccreditationResultRequest';
|
|
21
|
+
export * from './dtos/requests/UpsertPromotionBonusRequest';
|
|
19
22
|
export * from './dtos/responses/CustomerResponse';
|
|
20
23
|
export * from './dtos/responses/Customer360Response';
|
|
21
24
|
export * from './dtos/responses/WelcomeBonusResponse';
|
|
22
25
|
export * from './dtos/responses/WelcomeAccreditationResponse';
|
|
26
|
+
export * from './dtos/responses/PromotionBonusEntryResponse';
|
|
23
27
|
export * from './events/CustomerCreatedV1';
|
|
24
28
|
export * from './events/CustomerUpdatedV1';
|
|
25
29
|
export * from './events/CustomerKycVerifiedV1';
|
|
@@ -18,11 +18,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
__exportStar(require("./enums/AccountStatusEnum"), exports);
|
|
19
19
|
__exportStar(require("./enums/RetailKycStatusEnum"), exports);
|
|
20
20
|
__exportStar(require("./enums/KycModalityEnum"), exports);
|
|
21
|
+
__exportStar(require("./enums/KycMethodEnum"), exports);
|
|
21
22
|
__exportStar(require("./enums/KycReviewResultEnum"), exports);
|
|
22
23
|
__exportStar(require("./enums/RetailCustomerStatusEnum"), exports);
|
|
23
24
|
__exportStar(require("./enums/WelcomeBonusStatusEnum"), exports);
|
|
24
25
|
__exportStar(require("./enums/WelcomeBonusAmountSourceEnum"), exports);
|
|
25
26
|
__exportStar(require("./enums/WelcomeBonusSettlementStatusEnum"), exports);
|
|
27
|
+
__exportStar(require("./enums/BonusTypeEnum"), exports);
|
|
26
28
|
// Entidades (planas, con class-validator + @Expose)
|
|
27
29
|
__exportStar(require("./dtos/KycReview"), exports);
|
|
28
30
|
__exportStar(require("./dtos/RetailAddress"), exports);
|
|
@@ -35,11 +37,13 @@ __exportStar(require("./dtos/requests/AccountResultRequest"), exports);
|
|
|
35
37
|
__exportStar(require("./dtos/requests/KycReviewRequest"), exports);
|
|
36
38
|
__exportStar(require("./dtos/requests/WelcomeAccreditationRequest"), exports);
|
|
37
39
|
__exportStar(require("./dtos/requests/WelcomeAccreditationResultRequest"), exports);
|
|
40
|
+
__exportStar(require("./dtos/requests/UpsertPromotionBonusRequest"), exports);
|
|
38
41
|
// Response DTOs
|
|
39
42
|
__exportStar(require("./dtos/responses/CustomerResponse"), exports);
|
|
40
43
|
__exportStar(require("./dtos/responses/Customer360Response"), exports);
|
|
41
44
|
__exportStar(require("./dtos/responses/WelcomeBonusResponse"), exports);
|
|
42
45
|
__exportStar(require("./dtos/responses/WelcomeAccreditationResponse"), exports);
|
|
46
|
+
__exportStar(require("./dtos/responses/PromotionBonusEntryResponse"), exports);
|
|
43
47
|
// Contratos de eventos (PascalCase + sufijo V1)
|
|
44
48
|
__exportStar(require("./events/CustomerCreatedV1"), exports);
|
|
45
49
|
__exportStar(require("./events/CustomerUpdatedV1"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PromotionBonusStatusEnum } from '../../loanOfferings/enums/PromotionBonusStatusEnum';
|
|
2
|
+
/** Desenlace del bono de la promo de crédito, persistido en la sesión del wizard. */
|
|
3
|
+
export interface WizardCreditPromotionBonus {
|
|
4
|
+
promotionId: string;
|
|
5
|
+
amountCents: number;
|
|
6
|
+
/** `RESERVED` = en proceso; los otros tres son terminales. */
|
|
7
|
+
status: PromotionBonusStatusEnum;
|
|
8
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { WizardProduct } from './WizardProduct';
|
|
|
13
13
|
import type { WizardReservedLine } from './WizardReservedLine';
|
|
14
14
|
import type { WizardResumeTarget } from './WizardResumeTarget';
|
|
15
15
|
import type { WizardWelcome } from './WizardWelcome';
|
|
16
|
+
import type { WizardCreditPromotionBonus } from './WizardCreditPromotionBonus';
|
|
16
17
|
/** Modalidad de captura del KYC (M1 §213-329). */
|
|
17
18
|
export type KycModality = 'CUSTOMER_LINK' | 'STORE_DEVICE' | 'MANUAL';
|
|
18
19
|
/** Estado de la apertura de cuenta async (D35) — espejo de `RetailCustomer.accountStatus`. */
|
|
@@ -129,6 +130,8 @@ export interface WizardSession {
|
|
|
129
130
|
folio: string | null;
|
|
130
131
|
payment: WizardPayment | null;
|
|
131
132
|
welcome: WizardWelcome | null;
|
|
133
|
+
/** Bono de la promo de crédito; ausente o `null` mientras no se haya reservado. */
|
|
134
|
+
creditPromotionBonus?: WizardCreditPromotionBonus | null;
|
|
132
135
|
/** Contexto de crédito (pista SOFOM, pasos 04-09): PUNTEROS al dominio loan-*. Null en contado/externa. */
|
|
133
136
|
loan: WizardLoan | null;
|
|
134
137
|
saleId: string | null;
|
|
@@ -28,6 +28,7 @@ export * from './dtos/WizardConsents';
|
|
|
28
28
|
export * from './dtos/WizardPayment';
|
|
29
29
|
export * from './dtos/WizardProduct';
|
|
30
30
|
export * from './dtos/WizardWelcome';
|
|
31
|
+
export * from './dtos/WizardCreditPromotionBonus';
|
|
31
32
|
export * from './dtos/SalePaymentDetail';
|
|
32
33
|
export * from './dtos/ProspectNewCustomer';
|
|
33
34
|
export * from './dtos/WizardBeneficiary';
|
|
@@ -47,6 +47,7 @@ __exportStar(require("./dtos/WizardConsents"), exports);
|
|
|
47
47
|
__exportStar(require("./dtos/WizardPayment"), exports);
|
|
48
48
|
__exportStar(require("./dtos/WizardProduct"), exports);
|
|
49
49
|
__exportStar(require("./dtos/WizardWelcome"), exports);
|
|
50
|
+
__exportStar(require("./dtos/WizardCreditPromotionBonus"), exports);
|
|
50
51
|
__exportStar(require("./dtos/SalePaymentDetail"), exports);
|
|
51
52
|
__exportStar(require("./dtos/ProspectNewCustomer"), exports);
|
|
52
53
|
__exportStar(require("./dtos/WizardBeneficiary"), exports);
|
package/package.json
CHANGED
|
@@ -29,6 +29,8 @@ export interface AppliedPromotionResponse {
|
|
|
29
29
|
version: number;
|
|
30
30
|
/** Palancas del plan que la promoción pisó. */
|
|
31
31
|
appliedEffects: PromotionEffectKindEnum[];
|
|
32
|
+
/** Monto del bono PCF; solo en la promoción que gana el bono de la cotización. */
|
|
33
|
+
pcfBonusCents?: number;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
} from 'class-validator';
|
|
13
13
|
import { regexCurp, regexIso8601Date, regexPhoneMx } from '../../../helpers/constans/regex';
|
|
14
14
|
import { RetailAddress } from '../RetailAddress';
|
|
15
|
+
import { KycMethodEnum } from '../../enums/KycMethodEnum';
|
|
15
16
|
import { KycModalityEnum } from '../../enums/KycModalityEnum';
|
|
16
17
|
import { RetailKycStatusEnum } from '../../enums/RetailKycStatusEnum';
|
|
17
18
|
|
|
@@ -140,6 +141,18 @@ export class UpsertCustomerFromKycRequest {
|
|
|
140
141
|
@IsEnum(KycModalityEnum)
|
|
141
142
|
kycModality!: KycModalityEnum;
|
|
142
143
|
|
|
144
|
+
/** Método con el que se verificó la identidad. Ausente = no pisa el valor guardado. */
|
|
145
|
+
@Expose()
|
|
146
|
+
@IsOptional()
|
|
147
|
+
@IsEnum(KycMethodEnum)
|
|
148
|
+
kycMethod?: KycMethodEnum;
|
|
149
|
+
|
|
150
|
+
/** Verificación biométrica de `biometrics-business` que respaldó el alta por facematch. */
|
|
151
|
+
@Expose()
|
|
152
|
+
@IsOptional()
|
|
153
|
+
@IsString()
|
|
154
|
+
biometricVerificationId?: string;
|
|
155
|
+
|
|
143
156
|
/**
|
|
144
157
|
* Domicilio del INE (paso 02e), ya normalizado contra `places-business`. Se persiste embebido en la
|
|
145
158
|
* MISMA TX del upsert.
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsIn, IsInt, IsNotEmpty, IsOptional, IsPositive, IsString, MaxLength, ValidateIf } from 'class-validator';
|
|
3
|
+
import { Trim } from '../../../common/decorators/Trim';
|
|
4
|
+
import { BonusTypeEnum } from '../../enums/BonusTypeEnum';
|
|
5
|
+
import { WelcomeBonusStatusEnum } from '../../enums/WelcomeBonusStatusEnum';
|
|
6
|
+
|
|
7
|
+
/** Tope del motivo libre que el caller escribe en el asiento. */
|
|
8
|
+
const FREE_TEXT_MAX_LENGTH = 256;
|
|
9
|
+
|
|
10
|
+
const PROMOTION_BONUS_TYPES = [BonusTypeEnum.CREDIT_PROMOTION, BonusTypeEnum.SALE_PROMOTION];
|
|
11
|
+
|
|
12
|
+
const STATUSES_REQUIRING_REASON: WelcomeBonusStatusEnum[] = [
|
|
13
|
+
WelcomeBonusStatusEnum.FAILED,
|
|
14
|
+
WelcomeBonusStatusEnum.UNVERIFIED,
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Asiento de un bono de promoción en el libro de bonos:
|
|
19
|
+
* `PUT /private/customers/:id/promotion-bonuses`. El libro es espejo del sistema de la promo.
|
|
20
|
+
*/
|
|
21
|
+
export class UpsertPromotionBonusRequest {
|
|
22
|
+
@Expose()
|
|
23
|
+
@IsIn(PROMOTION_BONUS_TYPES)
|
|
24
|
+
bonusType!: BonusTypeEnum;
|
|
25
|
+
|
|
26
|
+
@Expose()
|
|
27
|
+
@Trim()
|
|
28
|
+
@IsString()
|
|
29
|
+
@IsNotEmpty()
|
|
30
|
+
promotionId!: string;
|
|
31
|
+
|
|
32
|
+
/** `creditId` en la promo de crédito, `saleId` en la de venta. */
|
|
33
|
+
@Expose()
|
|
34
|
+
@Trim()
|
|
35
|
+
@IsString()
|
|
36
|
+
@IsNotEmpty()
|
|
37
|
+
sourceId!: string;
|
|
38
|
+
|
|
39
|
+
/** Tienda de la venta: es la partición del asiento. */
|
|
40
|
+
@Expose()
|
|
41
|
+
@Trim()
|
|
42
|
+
@IsString()
|
|
43
|
+
@IsNotEmpty()
|
|
44
|
+
storeId!: string;
|
|
45
|
+
|
|
46
|
+
/** Obligatorio con `ACCREDITED`; el `PENDING` «sin cuenta todavía» no lo tiene. */
|
|
47
|
+
@Expose()
|
|
48
|
+
@ValidateIf(
|
|
49
|
+
(o: UpsertPromotionBonusRequest) =>
|
|
50
|
+
o.status === WelcomeBonusStatusEnum.ACCREDITED || o.fiadoDirectoryId !== undefined,
|
|
51
|
+
)
|
|
52
|
+
@Trim()
|
|
53
|
+
@IsString()
|
|
54
|
+
@IsNotEmpty({ message: 'fiadoDirectoryId es obligatorio cuando status es ACCREDITED' })
|
|
55
|
+
fiadoDirectoryId?: string;
|
|
56
|
+
|
|
57
|
+
@Expose()
|
|
58
|
+
@IsInt()
|
|
59
|
+
@IsPositive()
|
|
60
|
+
amountCents!: number;
|
|
61
|
+
|
|
62
|
+
@Expose()
|
|
63
|
+
@Trim()
|
|
64
|
+
@IsString()
|
|
65
|
+
@IsNotEmpty()
|
|
66
|
+
currency!: string;
|
|
67
|
+
|
|
68
|
+
@Expose()
|
|
69
|
+
@IsIn(Object.values(WelcomeBonusStatusEnum))
|
|
70
|
+
status!: WelcomeBonusStatusEnum;
|
|
71
|
+
|
|
72
|
+
@Expose()
|
|
73
|
+
@Trim()
|
|
74
|
+
@IsString()
|
|
75
|
+
@IsNotEmpty()
|
|
76
|
+
idempotencyKey!: string;
|
|
77
|
+
|
|
78
|
+
@Expose()
|
|
79
|
+
@IsOptional()
|
|
80
|
+
@Trim()
|
|
81
|
+
@IsString()
|
|
82
|
+
@IsNotEmpty()
|
|
83
|
+
processorTransactionId?: string;
|
|
84
|
+
|
|
85
|
+
/** Resumen legible, sin el payload crudo del processor (el libro va sin PII). */
|
|
86
|
+
@Expose()
|
|
87
|
+
@ValidateIf((o: UpsertPromotionBonusRequest) => STATUSES_REQUIRING_REASON.includes(o.status))
|
|
88
|
+
@Trim()
|
|
89
|
+
@IsString()
|
|
90
|
+
@IsNotEmpty({ message: 'failReason es obligatorio cuando status es FAILED o UNVERIFIED' })
|
|
91
|
+
@MaxLength(FREE_TEXT_MAX_LENGTH)
|
|
92
|
+
failReason?: string;
|
|
93
|
+
|
|
94
|
+
@Expose()
|
|
95
|
+
@IsOptional()
|
|
96
|
+
@Trim()
|
|
97
|
+
@IsString()
|
|
98
|
+
@IsNotEmpty()
|
|
99
|
+
saleId?: string;
|
|
100
|
+
|
|
101
|
+
@Expose()
|
|
102
|
+
@IsOptional()
|
|
103
|
+
@Trim()
|
|
104
|
+
@IsString()
|
|
105
|
+
@IsNotEmpty()
|
|
106
|
+
folio?: string;
|
|
107
|
+
|
|
108
|
+
@Expose()
|
|
109
|
+
@IsOptional()
|
|
110
|
+
@Trim()
|
|
111
|
+
@IsString()
|
|
112
|
+
@IsNotEmpty()
|
|
113
|
+
wizardSessionId?: string;
|
|
114
|
+
|
|
115
|
+
@Expose()
|
|
116
|
+
@IsOptional()
|
|
117
|
+
@Trim()
|
|
118
|
+
@IsString()
|
|
119
|
+
@IsNotEmpty()
|
|
120
|
+
sellerRetailUserId?: string;
|
|
121
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { BonusTypeEnum } from '../../enums/BonusTypeEnum';
|
|
2
|
+
import { WelcomeBonusSettlementStatusEnum } from '../../enums/WelcomeBonusSettlementStatusEnum';
|
|
3
|
+
import { WelcomeBonusStatusEnum } from '../../enums/WelcomeBonusStatusEnum';
|
|
4
|
+
|
|
5
|
+
/** Asiento de un bono de promoción en el libro de bonos. Sin PII. */
|
|
6
|
+
export interface PromotionBonusEntryResponse {
|
|
7
|
+
bonusId: string;
|
|
8
|
+
bonusType: BonusTypeEnum;
|
|
9
|
+
promotionId: string;
|
|
10
|
+
/** `creditId` en la promo de crédito, `saleId` en la de venta. */
|
|
11
|
+
sourceId: string;
|
|
12
|
+
retailCustomerId: string;
|
|
13
|
+
fiadoDirectoryId: string | null;
|
|
14
|
+
saleId: string | null;
|
|
15
|
+
creditId: string | null;
|
|
16
|
+
storeId: string;
|
|
17
|
+
amountCents: number | null;
|
|
18
|
+
currency: string;
|
|
19
|
+
status: WelcomeBonusStatusEnum;
|
|
20
|
+
failReason: string | null;
|
|
21
|
+
processorTransactionId: string | null;
|
|
22
|
+
idempotencyKey: string | null;
|
|
23
|
+
fundedBy: string;
|
|
24
|
+
settlementStatus: WelcomeBonusSettlementStatusEnum;
|
|
25
|
+
resolvedAt: string | null;
|
|
26
|
+
createdAt: string;
|
|
27
|
+
updatedAt: string;
|
|
28
|
+
}
|
|
@@ -2,11 +2,13 @@
|
|
|
2
2
|
export * from './enums/AccountStatusEnum';
|
|
3
3
|
export * from './enums/RetailKycStatusEnum';
|
|
4
4
|
export * from './enums/KycModalityEnum';
|
|
5
|
+
export * from './enums/KycMethodEnum';
|
|
5
6
|
export * from './enums/KycReviewResultEnum';
|
|
6
7
|
export * from './enums/RetailCustomerStatusEnum';
|
|
7
8
|
export * from './enums/WelcomeBonusStatusEnum';
|
|
8
9
|
export * from './enums/WelcomeBonusAmountSourceEnum';
|
|
9
10
|
export * from './enums/WelcomeBonusSettlementStatusEnum';
|
|
11
|
+
export * from './enums/BonusTypeEnum';
|
|
10
12
|
|
|
11
13
|
// Entidades (planas, con class-validator + @Expose)
|
|
12
14
|
export * from './dtos/KycReview';
|
|
@@ -21,12 +23,14 @@ export * from './dtos/requests/AccountResultRequest';
|
|
|
21
23
|
export * from './dtos/requests/KycReviewRequest';
|
|
22
24
|
export * from './dtos/requests/WelcomeAccreditationRequest';
|
|
23
25
|
export * from './dtos/requests/WelcomeAccreditationResultRequest';
|
|
26
|
+
export * from './dtos/requests/UpsertPromotionBonusRequest';
|
|
24
27
|
|
|
25
28
|
// Response DTOs
|
|
26
29
|
export * from './dtos/responses/CustomerResponse';
|
|
27
30
|
export * from './dtos/responses/Customer360Response';
|
|
28
31
|
export * from './dtos/responses/WelcomeBonusResponse';
|
|
29
32
|
export * from './dtos/responses/WelcomeAccreditationResponse';
|
|
33
|
+
export * from './dtos/responses/PromotionBonusEntryResponse';
|
|
30
34
|
|
|
31
35
|
// Contratos de eventos (PascalCase + sufijo V1)
|
|
32
36
|
export * from './events/CustomerCreatedV1';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { PromotionBonusStatusEnum } from '../../loanOfferings/enums/PromotionBonusStatusEnum';
|
|
2
|
+
|
|
3
|
+
/** Desenlace del bono de la promo de crédito, persistido en la sesión del wizard. */
|
|
4
|
+
export interface WizardCreditPromotionBonus {
|
|
5
|
+
promotionId: string;
|
|
6
|
+
amountCents: number;
|
|
7
|
+
/** `RESERVED` = en proceso; los otros tres son terminales. */
|
|
8
|
+
status: PromotionBonusStatusEnum;
|
|
9
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { WizardProduct } from './WizardProduct';
|
|
|
13
13
|
import type { WizardReservedLine } from './WizardReservedLine';
|
|
14
14
|
import type { WizardResumeTarget } from './WizardResumeTarget';
|
|
15
15
|
import type { WizardWelcome } from './WizardWelcome';
|
|
16
|
+
import type { WizardCreditPromotionBonus } from './WizardCreditPromotionBonus';
|
|
16
17
|
|
|
17
18
|
/** Modalidad de captura del KYC (M1 §213-329). */
|
|
18
19
|
export type KycModality = 'CUSTOMER_LINK' | 'STORE_DEVICE' | 'MANUAL';
|
|
@@ -133,6 +134,8 @@ export interface WizardSession {
|
|
|
133
134
|
folio: string | null;
|
|
134
135
|
payment: WizardPayment | null;
|
|
135
136
|
welcome: WizardWelcome | null;
|
|
137
|
+
/** Bono de la promo de crédito; ausente o `null` mientras no se haya reservado. */
|
|
138
|
+
creditPromotionBonus?: WizardCreditPromotionBonus | null;
|
|
136
139
|
/** Contexto de crédito (pista SOFOM, pasos 04-09): PUNTEROS al dominio loan-*. Null en contado/externa. */
|
|
137
140
|
loan: WizardLoan | null;
|
|
138
141
|
saleId: string | null;
|
|
@@ -33,6 +33,7 @@ export * from './dtos/WizardConsents';
|
|
|
33
33
|
export * from './dtos/WizardPayment';
|
|
34
34
|
export * from './dtos/WizardProduct';
|
|
35
35
|
export * from './dtos/WizardWelcome';
|
|
36
|
+
export * from './dtos/WizardCreditPromotionBonus';
|
|
36
37
|
export * from './dtos/SalePaymentDetail';
|
|
37
38
|
export * from './dtos/ProspectNewCustomer';
|
|
38
39
|
export * from './dtos/WizardBeneficiary';
|