@fiado/type-kit 3.527.0 → 3.529.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/emailVerification/dtos/EmailSetTrustedRequest.test.ts +16 -0
- package/_test_/unit/loanCredit/signLoanCreditRequest.test.ts +68 -0
- package/_test_/unit/retailWizard/emailOtpRequests.test.ts +53 -0
- package/_test_/unit/retailWizard/wizardSessionEmail.test.ts +22 -0
- package/bin/emailVerification/dtos/EmailSetTrustedRequest.d.ts +2 -0
- package/bin/emailVerification/dtos/EmailSetTrustedRequest.js +6 -0
- package/bin/loanCredit/dtos/LoanContractDetails.d.ts +42 -0
- package/bin/loanCredit/dtos/LoanContractDetails.js +187 -0
- package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.d.ts +7 -0
- package/bin/loanCredit/dtos/requests/SignLoanCreditRequest.js +8 -0
- package/bin/loanCredit/index.d.ts +1 -0
- package/bin/loanCredit/index.js +1 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +10 -0
- package/bin/retailWizard/dtos/requests/SendEmailOtpRequest.d.ts +6 -0
- package/bin/retailWizard/dtos/requests/SendEmailOtpRequest.js +29 -0
- package/bin/retailWizard/dtos/requests/VerifyEmailOtpRequest.d.ts +4 -0
- package/bin/retailWizard/dtos/requests/VerifyEmailOtpRequest.js +24 -0
- package/bin/retailWizard/enums/EmailStateEnum.d.ts +9 -0
- package/bin/retailWizard/enums/EmailStateEnum.js +13 -0
- package/bin/retailWizard/index.d.ts +3 -0
- package/bin/retailWizard/index.js +3 -0
- package/package.json +1 -1
- package/src/emailVerification/dtos/EmailSetTrustedRequest.ts +7 -1
- package/src/loanCredit/dtos/LoanContractDetails.ts +156 -0
- package/src/loanCredit/dtos/requests/SignLoanCreditRequest.ts +14 -2
- package/src/loanCredit/index.ts +1 -0
- package/src/retailWizard/dtos/WizardSession.ts +10 -0
- package/src/retailWizard/dtos/requests/SendEmailOtpRequest.ts +14 -0
- package/src/retailWizard/dtos/requests/VerifyEmailOtpRequest.ts +10 -0
- package/src/retailWizard/enums/EmailStateEnum.ts +9 -0
- package/src/retailWizard/index.ts +3 -0
|
@@ -50,4 +50,20 @@ describe('EmailSetTrustedRequest', () => {
|
|
|
50
50
|
const errors = await validate(dto);
|
|
51
51
|
expect(errors.some(e => e.property === 'type')).toBe(true);
|
|
52
52
|
});
|
|
53
|
+
|
|
54
|
+
it('acepta isPrincipal en true', async () => {
|
|
55
|
+
const dto = plainToInstance(EmailSetTrustedRequest, { ...valid, isPrincipal: true });
|
|
56
|
+
const errors = await validate(dto);
|
|
57
|
+
expect(errors).toEqual([]);
|
|
58
|
+
expect(dto.isPrincipal).toBe(true);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('falla si isPrincipal no es booleano', async () => {
|
|
62
|
+
const dto = plainToInstance(EmailSetTrustedRequest, {
|
|
63
|
+
...valid,
|
|
64
|
+
isPrincipal: 'si' as unknown as boolean,
|
|
65
|
+
});
|
|
66
|
+
const errors = await validate(dto);
|
|
67
|
+
expect(errors.some(e => e.property === 'isPrincipal')).toBe(true);
|
|
68
|
+
});
|
|
53
69
|
});
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { ValidationError, validate } from 'class-validator';
|
|
4
|
+
import { SignLoanCreditRequest } from '../../../src/loanCredit/index';
|
|
5
|
+
|
|
6
|
+
/** Rutas de las propiedades que fallan, con los anidados como `contractDetails.borrower.fullName`. */
|
|
7
|
+
const pathsOf = (errors: ValidationError[], prefix = ''): string[] =>
|
|
8
|
+
errors.flatMap((error) => {
|
|
9
|
+
const path = prefix ? `${prefix}.${error.property}` : error.property;
|
|
10
|
+
return error.children?.length ? pathsOf(error.children, path) : [path];
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const errorsOf = async (body: Record<string, unknown>): Promise<string[]> =>
|
|
14
|
+
pathsOf(await validate(plainToInstance(SignLoanCreditRequest, body))).sort();
|
|
15
|
+
|
|
16
|
+
const DETAILS = {
|
|
17
|
+
borrower: {
|
|
18
|
+
fullName: 'María Fernanda López Hernández',
|
|
19
|
+
curp: 'LOHM900215MMCPRR09',
|
|
20
|
+
address: 'Av. Ejemplo 123, Col. Centro, C.P. 50000, Toluca, Estado de México',
|
|
21
|
+
rfc: 'LOHM900215AB1',
|
|
22
|
+
occupation: 'Empleada',
|
|
23
|
+
monthlyIncome: '10,000 – 15,000',
|
|
24
|
+
},
|
|
25
|
+
device: { brand: 'Samsung', model: 'Galaxy A15', color: 'Negro', storage: '128 GB', imei: '356938035643809' },
|
|
26
|
+
provider: { legalName: 'Comercializadora de Ejemplo, S.A. de C.V.' },
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
describe('SignLoanCreditRequest', () => {
|
|
30
|
+
it('acepta el inicio de firma sin datos del contrato, como hasta ahora', async () => {
|
|
31
|
+
expect(await errorsOf({ contractData: { nombreAcreditado: 'María' } })).toEqual([]);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('acepta los datos del contrato completos', async () => {
|
|
35
|
+
expect(await errorsOf({ contractDetails: DETAILS })).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('el proveedor es opcional: el motor lo resuelve con la tienda', async () => {
|
|
39
|
+
const { provider, ...withoutProvider } = DETAILS;
|
|
40
|
+
expect(provider).toBeDefined();
|
|
41
|
+
expect(await errorsOf({ contractDetails: withoutProvider })).toEqual([]);
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
it('exige acreditado y equipo cuando vienen los datos del contrato', async () => {
|
|
45
|
+
expect(await errorsOf({ contractDetails: {} })).toEqual(['contractDetails.borrower', 'contractDetails.device']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('exige nombre, CURP y domicilio del acreditado, y marca y modelo del equipo', async () => {
|
|
49
|
+
const details = { borrower: { rfc: 'LOHM900215AB1' }, device: { color: 'Negro' } };
|
|
50
|
+
expect(await errorsOf({ contractDetails: details })).toEqual([
|
|
51
|
+
'contractDetails.borrower.address',
|
|
52
|
+
'contractDetails.borrower.curp',
|
|
53
|
+
'contractDetails.borrower.fullName',
|
|
54
|
+
'contractDetails.device.brand',
|
|
55
|
+
'contractDetails.device.model',
|
|
56
|
+
]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('rechaza un IMEI que no tiene 15 dígitos', async () => {
|
|
60
|
+
const details = { ...DETAILS, device: { ...DETAILS.device, imei: '35693803564380A' } };
|
|
61
|
+
expect(await errorsOf({ contractDetails: details })).toEqual(['contractDetails.device.imei']);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('rechaza un texto más largo que el máximo', async () => {
|
|
65
|
+
const details = { ...DETAILS, provider: { legalName: 'x'.repeat(301) } };
|
|
66
|
+
expect(await errorsOf({ contractDetails: details })).toEqual(['contractDetails.provider.legalName']);
|
|
67
|
+
});
|
|
68
|
+
});
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validateSync } from 'class-validator';
|
|
4
|
+
import { SendEmailOtpRequest } from '../../../src/retailWizard/dtos/requests/SendEmailOtpRequest';
|
|
5
|
+
import { VerifyEmailOtpRequest } from '../../../src/retailWizard/dtos/requests/VerifyEmailOtpRequest';
|
|
6
|
+
import { EmailStateEnum } from '../../../src/retailWizard/enums/EmailStateEnum';
|
|
7
|
+
import { EmailTypeEnum } from '../../../src/emailVerification/enums/EmailTypeEnum';
|
|
8
|
+
import * as RetailWizard from '../../../src/retailWizard';
|
|
9
|
+
|
|
10
|
+
function errorsOf<T extends object>(cls: new () => T, payload: Record<string, unknown>): string[] {
|
|
11
|
+
const dto = plainToInstance(cls, payload, { excludeExtraneousValues: true });
|
|
12
|
+
return validateSync(dto).map((e) => e.property);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
describe('SendEmailOtpRequest', () => {
|
|
16
|
+
it('acepta correo y tipo válidos', () => {
|
|
17
|
+
expect(errorsOf(SendEmailOtpRequest, { email: 'cliente@correo.com', type: EmailTypeEnum.PERSONAL })).toEqual([]);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('rechaza un correo mal formado', () => {
|
|
21
|
+
expect(errorsOf(SendEmailOtpRequest, { email: 'no-es-correo', type: EmailTypeEnum.PERSONAL })).toContain('email');
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('rechaza un tipo fuera del enum', () => {
|
|
25
|
+
expect(errorsOf(SendEmailOtpRequest, { email: 'cliente@correo.com', type: 'WORK' })).toContain('type');
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
it('exige el tipo', () => {
|
|
29
|
+
expect(errorsOf(SendEmailOtpRequest, { email: 'cliente@correo.com' })).toContain('type');
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('VerifyEmailOtpRequest', () => {
|
|
34
|
+
it('acepta un código de 4 dígitos', () => {
|
|
35
|
+
expect(errorsOf(VerifyEmailOtpRequest, { code: '1234' })).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('rechaza un código vacío', () => {
|
|
39
|
+
expect(errorsOf(VerifyEmailOtpRequest, { code: '' })).toContain('code');
|
|
40
|
+
});
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('EmailStateEnum', () => {
|
|
44
|
+
it('tiene los tres estados del correo de la sesión', () => {
|
|
45
|
+
expect(Object.values(EmailStateEnum)).toEqual(['EXISTING', 'CAPTURED', 'PENDING']);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('se exporta desde el índice de retailWizard junto con los requests', () => {
|
|
49
|
+
expect(RetailWizard.EmailStateEnum).toBe(EmailStateEnum);
|
|
50
|
+
expect(RetailWizard.SendEmailOtpRequest).toBe(SendEmailOtpRequest);
|
|
51
|
+
expect(RetailWizard.VerifyEmailOtpRequest).toBe(VerifyEmailOtpRequest);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { WizardSession } from '../../../src/retailWizard/dtos/WizardSession';
|
|
2
|
+
import { EmailStateEnum } from '../../../src/retailWizard/enums/EmailStateEnum';
|
|
3
|
+
import { EmailTypeEnum } from '../../../src/emailVerification/enums/EmailTypeEnum';
|
|
4
|
+
|
|
5
|
+
type SessionEmail = Pick<WizardSession, 'emailState' | 'emailType' | 'emailMasked' | 'email'>;
|
|
6
|
+
|
|
7
|
+
describe('WizardSession — correo del paso 01', () => {
|
|
8
|
+
it('declara los cuatro campos del correo', () => {
|
|
9
|
+
const captured: SessionEmail = {
|
|
10
|
+
emailState: EmailStateEnum.CAPTURED,
|
|
11
|
+
emailType: EmailTypeEnum.PERSONAL,
|
|
12
|
+
emailMasked: 'ju•••••@gmail.com',
|
|
13
|
+
email: 'juan@gmail.com',
|
|
14
|
+
};
|
|
15
|
+
expect(captured.emailState).toBe('CAPTURED');
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
it('los cuatro son opcionales: una sesión vieja no los trae', () => {
|
|
19
|
+
const legacy: SessionEmail = {};
|
|
20
|
+
expect(legacy).toEqual({});
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -35,3 +35,9 @@ __decorate([
|
|
|
35
35
|
(0, class_validator_1.IsEnum)(EmailTypeEnum_1.EmailTypeEnum),
|
|
36
36
|
__metadata("design:type", String)
|
|
37
37
|
], EmailSetTrustedRequest.prototype, "type", void 0);
|
|
38
|
+
__decorate([
|
|
39
|
+
(0, class_transformer_1.Expose)(),
|
|
40
|
+
(0, class_validator_1.IsOptional)(),
|
|
41
|
+
(0, class_validator_1.IsBoolean)(),
|
|
42
|
+
__metadata("design:type", Boolean)
|
|
43
|
+
], EmailSetTrustedRequest.prototype, "isPrincipal", void 0);
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/** Datos del acreditado tal como se imprimen en el contrato (Sección II.1). */
|
|
2
|
+
export declare class LoanContractBorrowerDetails {
|
|
3
|
+
fullName: string;
|
|
4
|
+
curp: string;
|
|
5
|
+
/** Domicilio completo en una línea. */
|
|
6
|
+
address: string;
|
|
7
|
+
rfc?: string;
|
|
8
|
+
birthDate?: string;
|
|
9
|
+
/** Identificación oficial con la que se validó al acreditado (p. ej. INE). */
|
|
10
|
+
identification?: string;
|
|
11
|
+
phone?: string;
|
|
12
|
+
email?: string;
|
|
13
|
+
occupation?: string;
|
|
14
|
+
/** Ingresos mensuales declarados, ya formateados (monto o rango). */
|
|
15
|
+
monthlyIncome?: string;
|
|
16
|
+
fundsOrigin?: string;
|
|
17
|
+
}
|
|
18
|
+
/** Equipo celular que financia el crédito (Sección II.2). */
|
|
19
|
+
export declare class LoanContractDeviceDetails {
|
|
20
|
+
brand: string;
|
|
21
|
+
model: string;
|
|
22
|
+
color?: string;
|
|
23
|
+
/** Capacidad de almacenamiento, ya formateada (p. ej. "128 GB"). */
|
|
24
|
+
storage?: string;
|
|
25
|
+
imei?: string;
|
|
26
|
+
serialNumber?: string;
|
|
27
|
+
}
|
|
28
|
+
/** Proveedor que vende el equipo y recibe el desembolso (Sección II.3). */
|
|
29
|
+
export declare class LoanContractProviderDetails {
|
|
30
|
+
legalName: string;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Datos del contrato que el motor de crédito no guarda: los aporta el wizard al iniciar la firma
|
|
34
|
+
* y se imprimen en las plantillas de pdf-render-business. Son PII transitoria: no se persisten ni
|
|
35
|
+
* se loggean.
|
|
36
|
+
*/
|
|
37
|
+
export declare class LoanContractDetails {
|
|
38
|
+
borrower: LoanContractBorrowerDetails;
|
|
39
|
+
device: LoanContractDeviceDetails;
|
|
40
|
+
/** Si falta, el motor lo resuelve con la tienda del crédito. */
|
|
41
|
+
provider?: LoanContractProviderDetails;
|
|
42
|
+
}
|
|
@@ -0,0 +1,187 @@
|
|
|
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.LoanContractDetails = exports.LoanContractProviderDetails = exports.LoanContractDeviceDetails = exports.LoanContractBorrowerDetails = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/** Largo máximo de un texto corto del contrato (RFC, ocupación, marca…). */
|
|
16
|
+
const SHORT_TEXT = 120;
|
|
17
|
+
/** Largo máximo de un texto largo del contrato (nombre, domicilio, razón social). */
|
|
18
|
+
const LONG_TEXT = 300;
|
|
19
|
+
/** IMEI: exactamente 15 dígitos. */
|
|
20
|
+
const IMEI_PATTERN = /^\d{15}$/;
|
|
21
|
+
/** Datos del acreditado tal como se imprimen en el contrato (Sección II.1). */
|
|
22
|
+
class LoanContractBorrowerDetails {
|
|
23
|
+
}
|
|
24
|
+
exports.LoanContractBorrowerDetails = LoanContractBorrowerDetails;
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, class_transformer_1.Expose)(),
|
|
27
|
+
(0, class_validator_1.IsString)(),
|
|
28
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
29
|
+
(0, class_validator_1.MaxLength)(LONG_TEXT),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], LoanContractBorrowerDetails.prototype, "fullName", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, class_transformer_1.Expose)(),
|
|
34
|
+
(0, class_validator_1.IsString)(),
|
|
35
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
36
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], LoanContractBorrowerDetails.prototype, "curp", void 0);
|
|
39
|
+
__decorate([
|
|
40
|
+
(0, class_transformer_1.Expose)(),
|
|
41
|
+
(0, class_validator_1.IsString)(),
|
|
42
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
43
|
+
(0, class_validator_1.MaxLength)(LONG_TEXT),
|
|
44
|
+
__metadata("design:type", String)
|
|
45
|
+
], LoanContractBorrowerDetails.prototype, "address", void 0);
|
|
46
|
+
__decorate([
|
|
47
|
+
(0, class_transformer_1.Expose)(),
|
|
48
|
+
(0, class_validator_1.IsOptional)(),
|
|
49
|
+
(0, class_validator_1.IsString)(),
|
|
50
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
51
|
+
__metadata("design:type", String)
|
|
52
|
+
], LoanContractBorrowerDetails.prototype, "rfc", void 0);
|
|
53
|
+
__decorate([
|
|
54
|
+
(0, class_transformer_1.Expose)(),
|
|
55
|
+
(0, class_validator_1.IsOptional)(),
|
|
56
|
+
(0, class_validator_1.IsString)(),
|
|
57
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
58
|
+
__metadata("design:type", String)
|
|
59
|
+
], LoanContractBorrowerDetails.prototype, "birthDate", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
(0, class_transformer_1.Expose)(),
|
|
62
|
+
(0, class_validator_1.IsOptional)(),
|
|
63
|
+
(0, class_validator_1.IsString)(),
|
|
64
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
65
|
+
__metadata("design:type", String)
|
|
66
|
+
], LoanContractBorrowerDetails.prototype, "identification", void 0);
|
|
67
|
+
__decorate([
|
|
68
|
+
(0, class_transformer_1.Expose)(),
|
|
69
|
+
(0, class_validator_1.IsOptional)(),
|
|
70
|
+
(0, class_validator_1.IsString)(),
|
|
71
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
72
|
+
__metadata("design:type", String)
|
|
73
|
+
], LoanContractBorrowerDetails.prototype, "phone", void 0);
|
|
74
|
+
__decorate([
|
|
75
|
+
(0, class_transformer_1.Expose)(),
|
|
76
|
+
(0, class_validator_1.IsOptional)(),
|
|
77
|
+
(0, class_validator_1.IsString)(),
|
|
78
|
+
(0, class_validator_1.MaxLength)(LONG_TEXT),
|
|
79
|
+
__metadata("design:type", String)
|
|
80
|
+
], LoanContractBorrowerDetails.prototype, "email", void 0);
|
|
81
|
+
__decorate([
|
|
82
|
+
(0, class_transformer_1.Expose)(),
|
|
83
|
+
(0, class_validator_1.IsOptional)(),
|
|
84
|
+
(0, class_validator_1.IsString)(),
|
|
85
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
86
|
+
__metadata("design:type", String)
|
|
87
|
+
], LoanContractBorrowerDetails.prototype, "occupation", void 0);
|
|
88
|
+
__decorate([
|
|
89
|
+
(0, class_transformer_1.Expose)(),
|
|
90
|
+
(0, class_validator_1.IsOptional)(),
|
|
91
|
+
(0, class_validator_1.IsString)(),
|
|
92
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
93
|
+
__metadata("design:type", String)
|
|
94
|
+
], LoanContractBorrowerDetails.prototype, "monthlyIncome", void 0);
|
|
95
|
+
__decorate([
|
|
96
|
+
(0, class_transformer_1.Expose)(),
|
|
97
|
+
(0, class_validator_1.IsOptional)(),
|
|
98
|
+
(0, class_validator_1.IsString)(),
|
|
99
|
+
(0, class_validator_1.MaxLength)(LONG_TEXT),
|
|
100
|
+
__metadata("design:type", String)
|
|
101
|
+
], LoanContractBorrowerDetails.prototype, "fundsOrigin", void 0);
|
|
102
|
+
/** Equipo celular que financia el crédito (Sección II.2). */
|
|
103
|
+
class LoanContractDeviceDetails {
|
|
104
|
+
}
|
|
105
|
+
exports.LoanContractDeviceDetails = LoanContractDeviceDetails;
|
|
106
|
+
__decorate([
|
|
107
|
+
(0, class_transformer_1.Expose)(),
|
|
108
|
+
(0, class_validator_1.IsString)(),
|
|
109
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
110
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
111
|
+
__metadata("design:type", String)
|
|
112
|
+
], LoanContractDeviceDetails.prototype, "brand", void 0);
|
|
113
|
+
__decorate([
|
|
114
|
+
(0, class_transformer_1.Expose)(),
|
|
115
|
+
(0, class_validator_1.IsString)(),
|
|
116
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
117
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
118
|
+
__metadata("design:type", String)
|
|
119
|
+
], LoanContractDeviceDetails.prototype, "model", void 0);
|
|
120
|
+
__decorate([
|
|
121
|
+
(0, class_transformer_1.Expose)(),
|
|
122
|
+
(0, class_validator_1.IsOptional)(),
|
|
123
|
+
(0, class_validator_1.IsString)(),
|
|
124
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
125
|
+
__metadata("design:type", String)
|
|
126
|
+
], LoanContractDeviceDetails.prototype, "color", void 0);
|
|
127
|
+
__decorate([
|
|
128
|
+
(0, class_transformer_1.Expose)(),
|
|
129
|
+
(0, class_validator_1.IsOptional)(),
|
|
130
|
+
(0, class_validator_1.IsString)(),
|
|
131
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
132
|
+
__metadata("design:type", String)
|
|
133
|
+
], LoanContractDeviceDetails.prototype, "storage", void 0);
|
|
134
|
+
__decorate([
|
|
135
|
+
(0, class_transformer_1.Expose)(),
|
|
136
|
+
(0, class_validator_1.IsOptional)(),
|
|
137
|
+
(0, class_validator_1.IsString)(),
|
|
138
|
+
(0, class_validator_1.Matches)(IMEI_PATTERN, { message: 'imei admite solo 15 dígitos' }),
|
|
139
|
+
__metadata("design:type", String)
|
|
140
|
+
], LoanContractDeviceDetails.prototype, "imei", void 0);
|
|
141
|
+
__decorate([
|
|
142
|
+
(0, class_transformer_1.Expose)(),
|
|
143
|
+
(0, class_validator_1.IsOptional)(),
|
|
144
|
+
(0, class_validator_1.IsString)(),
|
|
145
|
+
(0, class_validator_1.MaxLength)(SHORT_TEXT),
|
|
146
|
+
__metadata("design:type", String)
|
|
147
|
+
], LoanContractDeviceDetails.prototype, "serialNumber", void 0);
|
|
148
|
+
/** Proveedor que vende el equipo y recibe el desembolso (Sección II.3). */
|
|
149
|
+
class LoanContractProviderDetails {
|
|
150
|
+
}
|
|
151
|
+
exports.LoanContractProviderDetails = LoanContractProviderDetails;
|
|
152
|
+
__decorate([
|
|
153
|
+
(0, class_transformer_1.Expose)(),
|
|
154
|
+
(0, class_validator_1.IsString)(),
|
|
155
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
156
|
+
(0, class_validator_1.MaxLength)(LONG_TEXT),
|
|
157
|
+
__metadata("design:type", String)
|
|
158
|
+
], LoanContractProviderDetails.prototype, "legalName", void 0);
|
|
159
|
+
/**
|
|
160
|
+
* Datos del contrato que el motor de crédito no guarda: los aporta el wizard al iniciar la firma
|
|
161
|
+
* y se imprimen en las plantillas de pdf-render-business. Son PII transitoria: no se persisten ni
|
|
162
|
+
* se loggean.
|
|
163
|
+
*/
|
|
164
|
+
class LoanContractDetails {
|
|
165
|
+
}
|
|
166
|
+
exports.LoanContractDetails = LoanContractDetails;
|
|
167
|
+
__decorate([
|
|
168
|
+
(0, class_transformer_1.Expose)(),
|
|
169
|
+
(0, class_validator_1.IsDefined)(),
|
|
170
|
+
(0, class_validator_1.ValidateNested)(),
|
|
171
|
+
(0, class_transformer_1.Type)(() => LoanContractBorrowerDetails),
|
|
172
|
+
__metadata("design:type", LoanContractBorrowerDetails)
|
|
173
|
+
], LoanContractDetails.prototype, "borrower", void 0);
|
|
174
|
+
__decorate([
|
|
175
|
+
(0, class_transformer_1.Expose)(),
|
|
176
|
+
(0, class_validator_1.IsDefined)(),
|
|
177
|
+
(0, class_validator_1.ValidateNested)(),
|
|
178
|
+
(0, class_transformer_1.Type)(() => LoanContractDeviceDetails),
|
|
179
|
+
__metadata("design:type", LoanContractDeviceDetails)
|
|
180
|
+
], LoanContractDetails.prototype, "device", void 0);
|
|
181
|
+
__decorate([
|
|
182
|
+
(0, class_transformer_1.Expose)(),
|
|
183
|
+
(0, class_validator_1.IsOptional)(),
|
|
184
|
+
(0, class_validator_1.ValidateNested)(),
|
|
185
|
+
(0, class_transformer_1.Type)(() => LoanContractProviderDetails),
|
|
186
|
+
__metadata("design:type", LoanContractProviderDetails)
|
|
187
|
+
], LoanContractDetails.prototype, "provider", void 0);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { LoanContractDetails } from '../LoanContractDetails';
|
|
1
2
|
/**
|
|
2
3
|
* Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
|
|
3
4
|
* M2 §4.4). Endpoint de DOS tiempos sobre el mismo contrato:
|
|
@@ -27,6 +28,12 @@ export declare class SignLoanCreditRequest {
|
|
|
27
28
|
* pagos, tasa) y las pasa a document-generator. NO se persisten ni loggean (silo PII-zero).
|
|
28
29
|
*/
|
|
29
30
|
contractData?: Record<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* SOLO en el inicio de firma (tiempo 1): datos del acreditado, del equipo y del proveedor que
|
|
33
|
+
* imprimen las plantillas de pdf-render-business. Los aporta el WIZARD; mismas reglas de PII
|
|
34
|
+
* que `contractData`.
|
|
35
|
+
*/
|
|
36
|
+
contractDetails?: LoanContractDetails;
|
|
30
37
|
/**
|
|
31
38
|
* SOLO en el inicio de firma (tiempo 1) con el OTP REAL: el teléfono destino del SMS (E.164).
|
|
32
39
|
* PII TRANSITORIA: el motor la pasa a messages-business y NO la persiste ni loggea (silo
|
|
@@ -12,6 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.SignLoanCreditRequest = void 0;
|
|
13
13
|
const class_transformer_1 = require("class-transformer");
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
|
+
const LoanContractDetails_1 = require("../LoanContractDetails");
|
|
15
16
|
/**
|
|
16
17
|
* Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
|
|
17
18
|
* M2 §4.4). Endpoint de DOS tiempos sobre el mismo contrato:
|
|
@@ -47,6 +48,13 @@ __decorate([
|
|
|
47
48
|
(0, class_validator_1.IsObject)(),
|
|
48
49
|
__metadata("design:type", Object)
|
|
49
50
|
], SignLoanCreditRequest.prototype, "contractData", void 0);
|
|
51
|
+
__decorate([
|
|
52
|
+
(0, class_transformer_1.Expose)(),
|
|
53
|
+
(0, class_validator_1.IsOptional)(),
|
|
54
|
+
(0, class_validator_1.ValidateNested)(),
|
|
55
|
+
(0, class_transformer_1.Type)(() => LoanContractDetails_1.LoanContractDetails),
|
|
56
|
+
__metadata("design:type", LoanContractDetails_1.LoanContractDetails)
|
|
57
|
+
], SignLoanCreditRequest.prototype, "contractDetails", void 0);
|
|
50
58
|
__decorate([
|
|
51
59
|
(0, class_transformer_1.Expose)(),
|
|
52
60
|
(0, class_validator_1.IsOptional)(),
|
|
@@ -8,6 +8,7 @@ export * from './dtos/LoanCredit';
|
|
|
8
8
|
export * from './dtos/LoanInstallment';
|
|
9
9
|
export * from './dtos/InstallmentPaidEvent';
|
|
10
10
|
export * from './dtos/LoanContract';
|
|
11
|
+
export * from './dtos/LoanContractDetails';
|
|
11
12
|
export * from './dtos/ActivationChecklist';
|
|
12
13
|
export * from './dtos/AppliedPromotionSnapshot';
|
|
13
14
|
export * from './dtos/AppliedStorePolicySnapshot';
|
package/bin/loanCredit/index.js
CHANGED
|
@@ -29,6 +29,7 @@ __exportStar(require("./dtos/LoanCredit"), exports);
|
|
|
29
29
|
__exportStar(require("./dtos/LoanInstallment"), exports);
|
|
30
30
|
__exportStar(require("./dtos/InstallmentPaidEvent"), exports);
|
|
31
31
|
__exportStar(require("./dtos/LoanContract"), exports);
|
|
32
|
+
__exportStar(require("./dtos/LoanContractDetails"), exports);
|
|
32
33
|
__exportStar(require("./dtos/ActivationChecklist"), exports);
|
|
33
34
|
__exportStar(require("./dtos/AppliedPromotionSnapshot"), exports);
|
|
34
35
|
__exportStar(require("./dtos/AppliedStorePolicySnapshot"), exports);
|
|
@@ -15,6 +15,8 @@ import type { WizardReservedLine } from './WizardReservedLine';
|
|
|
15
15
|
import type { WizardResumeTarget } from './WizardResumeTarget';
|
|
16
16
|
import type { WizardWelcome } from './WizardWelcome';
|
|
17
17
|
import type { WizardCreditPromotionBonus } from './WizardCreditPromotionBonus';
|
|
18
|
+
import type { EmailStateEnum } from '../enums/EmailStateEnum';
|
|
19
|
+
import type { EmailTypeEnum } from '../../emailVerification/enums/EmailTypeEnum';
|
|
18
20
|
/** Modalidad de captura del KYC (M1 §213-329). */
|
|
19
21
|
export type KycModality = 'CUSTOMER_LINK' | 'STORE_DEVICE' | 'MANUAL';
|
|
20
22
|
/** Estado de la apertura de cuenta async (D35) — espejo de `RetailCustomer.accountStatus`. */
|
|
@@ -64,6 +66,14 @@ export interface WizardSession {
|
|
|
64
66
|
*/
|
|
65
67
|
resumeTarget?: WizardResumeTarget | null;
|
|
66
68
|
consents: WizardConsents | null;
|
|
69
|
+
/** Estado del correo del cliente en el paso 01. Ausente en sesiones anteriores al correo. */
|
|
70
|
+
emailState?: EmailStateEnum;
|
|
71
|
+
/** Tipo del correo: personal o corporativo. */
|
|
72
|
+
emailType?: EmailTypeEnum;
|
|
73
|
+
/** Correo enmascarado (`ju•••••@gmail.com`); viaja siempre que la sesión tenga correo. */
|
|
74
|
+
emailMasked?: string;
|
|
75
|
+
/** Correo completo: solo con `emailState` CAPTURED, porque lo tecleó el vendedor. */
|
|
76
|
+
email?: string;
|
|
67
77
|
/** Espejo del estado KYC (fuente = customer). */
|
|
68
78
|
kycStatus: string | null;
|
|
69
79
|
kycModality: KycModality | null;
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { EmailTypeEnum } from '../../../emailVerification/enums/EmailTypeEnum';
|
|
2
|
+
/** POST /wizard/sessions/:id/email/otp/send — manda el código al correo que capturó el vendedor. */
|
|
3
|
+
export declare class SendEmailOtpRequest {
|
|
4
|
+
email: string;
|
|
5
|
+
type: EmailTypeEnum;
|
|
6
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
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.SendEmailOtpRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const EmailTypeEnum_1 = require("../../../emailVerification/enums/EmailTypeEnum");
|
|
16
|
+
/** POST /wizard/sessions/:id/email/otp/send — manda el código al correo que capturó el vendedor. */
|
|
17
|
+
class SendEmailOtpRequest {
|
|
18
|
+
}
|
|
19
|
+
exports.SendEmailOtpRequest = SendEmailOtpRequest;
|
|
20
|
+
__decorate([
|
|
21
|
+
(0, class_transformer_1.Expose)(),
|
|
22
|
+
(0, class_validator_1.IsEmail)(),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], SendEmailOtpRequest.prototype, "email", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, class_transformer_1.Expose)(),
|
|
27
|
+
(0, class_validator_1.IsEnum)(EmailTypeEnum_1.EmailTypeEnum),
|
|
28
|
+
__metadata("design:type", String)
|
|
29
|
+
], SendEmailOtpRequest.prototype, "type", void 0);
|
|
@@ -0,0 +1,24 @@
|
|
|
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.VerifyEmailOtpRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
/** POST /wizard/sessions/:id/email/otp/verify — verifica el código que llegó al correo. */
|
|
16
|
+
class VerifyEmailOtpRequest {
|
|
17
|
+
}
|
|
18
|
+
exports.VerifyEmailOtpRequest = VerifyEmailOtpRequest;
|
|
19
|
+
__decorate([
|
|
20
|
+
(0, class_transformer_1.Expose)(),
|
|
21
|
+
(0, class_validator_1.IsString)(),
|
|
22
|
+
(0, class_validator_1.Length)(4, 8),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], VerifyEmailOtpRequest.prototype, "code", void 0);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correo de la sesión del wizard: EXISTING = ya verificado en contacts; CAPTURED = lo tecleó el
|
|
3
|
+
* vendedor y se verificó con OTP; PENDING = falta capturarlo o verificarlo.
|
|
4
|
+
*/
|
|
5
|
+
export declare enum EmailStateEnum {
|
|
6
|
+
EXISTING = "EXISTING",
|
|
7
|
+
CAPTURED = "CAPTURED",
|
|
8
|
+
PENDING = "PENDING"
|
|
9
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EmailStateEnum = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* Correo de la sesión del wizard: EXISTING = ya verificado en contacts; CAPTURED = lo tecleó el
|
|
6
|
+
* vendedor y se verificó con OTP; PENDING = falta capturarlo o verificarlo.
|
|
7
|
+
*/
|
|
8
|
+
var EmailStateEnum;
|
|
9
|
+
(function (EmailStateEnum) {
|
|
10
|
+
EmailStateEnum["EXISTING"] = "EXISTING";
|
|
11
|
+
EmailStateEnum["CAPTURED"] = "CAPTURED";
|
|
12
|
+
EmailStateEnum["PENDING"] = "PENDING";
|
|
13
|
+
})(EmailStateEnum || (exports.EmailStateEnum = EmailStateEnum = {}));
|
|
@@ -26,6 +26,7 @@ export * from './enums/BlockedCategoryEnum';
|
|
|
26
26
|
export * from './enums/WizardLineStatusEnum';
|
|
27
27
|
export * from './enums/LinePaymentTypeEnum';
|
|
28
28
|
export * from './enums/WizardLineRoleEnum';
|
|
29
|
+
export * from './enums/EmailStateEnum';
|
|
29
30
|
export * from './dtos/WizardConsents';
|
|
30
31
|
export * from './dtos/WizardPayment';
|
|
31
32
|
export * from './dtos/WizardProduct';
|
|
@@ -90,6 +91,8 @@ export * from './dtos/requests/SignatureSignRequest';
|
|
|
90
91
|
export * from './dtos/requests/RegisterDeviceRequest';
|
|
91
92
|
export * from './dtos/requests/CompleteSaleRequest';
|
|
92
93
|
export * from './dtos/requests/SuggestIdentifiersQuery';
|
|
94
|
+
export * from './dtos/requests/SendEmailOtpRequest';
|
|
95
|
+
export * from './dtos/requests/VerifyEmailOtpRequest';
|
|
93
96
|
export * from './dtos/responses/KycPrepareResponse';
|
|
94
97
|
export * from './dtos/responses/ProofOfAddressPrepareResponse';
|
|
95
98
|
export * from './dtos/responses/SessionFinanciersResponse';
|
|
@@ -45,6 +45,7 @@ __exportStar(require("./enums/BlockedCategoryEnum"), exports);
|
|
|
45
45
|
__exportStar(require("./enums/WizardLineStatusEnum"), exports);
|
|
46
46
|
__exportStar(require("./enums/LinePaymentTypeEnum"), exports);
|
|
47
47
|
__exportStar(require("./enums/WizardLineRoleEnum"), exports);
|
|
48
|
+
__exportStar(require("./enums/EmailStateEnum"), exports);
|
|
48
49
|
// DTOs anidados
|
|
49
50
|
__exportStar(require("./dtos/WizardConsents"), exports);
|
|
50
51
|
__exportStar(require("./dtos/WizardPayment"), exports);
|
|
@@ -115,6 +116,8 @@ __exportStar(require("./dtos/requests/SignatureSignRequest"), exports);
|
|
|
115
116
|
__exportStar(require("./dtos/requests/RegisterDeviceRequest"), exports);
|
|
116
117
|
__exportStar(require("./dtos/requests/CompleteSaleRequest"), exports);
|
|
117
118
|
__exportStar(require("./dtos/requests/SuggestIdentifiersQuery"), exports);
|
|
119
|
+
__exportStar(require("./dtos/requests/SendEmailOtpRequest"), exports);
|
|
120
|
+
__exportStar(require("./dtos/requests/VerifyEmailOtpRequest"), exports);
|
|
118
121
|
// Response DTOs (output tipado por endpoint)
|
|
119
122
|
__exportStar(require("./dtos/responses/KycPrepareResponse"), exports);
|
|
120
123
|
__exportStar(require("./dtos/responses/ProofOfAddressPrepareResponse"), exports);
|
package/package.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsEmail, IsEnum, IsString } from 'class-validator';
|
|
2
|
+
import { IsBoolean, IsEmail, IsEnum, IsOptional, IsString } from 'class-validator';
|
|
3
3
|
import { EmailTypeEnum } from '../enums/EmailTypeEnum';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -18,4 +18,10 @@ export class EmailSetTrustedRequest {
|
|
|
18
18
|
@Expose()
|
|
19
19
|
@IsEnum(EmailTypeEnum)
|
|
20
20
|
type!: EmailTypeEnum;
|
|
21
|
+
|
|
22
|
+
/** En true, el correo queda como principal del directorio. Sin el campo, no cambia el principal. */
|
|
23
|
+
@Expose()
|
|
24
|
+
@IsOptional()
|
|
25
|
+
@IsBoolean()
|
|
26
|
+
isPrincipal?: boolean;
|
|
21
27
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { IsDefined, IsNotEmpty, IsOptional, IsString, Matches, MaxLength, ValidateNested } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/** Largo máximo de un texto corto del contrato (RFC, ocupación, marca…). */
|
|
5
|
+
const SHORT_TEXT = 120;
|
|
6
|
+
/** Largo máximo de un texto largo del contrato (nombre, domicilio, razón social). */
|
|
7
|
+
const LONG_TEXT = 300;
|
|
8
|
+
/** IMEI: exactamente 15 dígitos. */
|
|
9
|
+
const IMEI_PATTERN = /^\d{15}$/;
|
|
10
|
+
|
|
11
|
+
/** Datos del acreditado tal como se imprimen en el contrato (Sección II.1). */
|
|
12
|
+
export class LoanContractBorrowerDetails {
|
|
13
|
+
@Expose()
|
|
14
|
+
@IsString()
|
|
15
|
+
@IsNotEmpty()
|
|
16
|
+
@MaxLength(LONG_TEXT)
|
|
17
|
+
fullName: string;
|
|
18
|
+
|
|
19
|
+
@Expose()
|
|
20
|
+
@IsString()
|
|
21
|
+
@IsNotEmpty()
|
|
22
|
+
@MaxLength(SHORT_TEXT)
|
|
23
|
+
curp: string;
|
|
24
|
+
|
|
25
|
+
/** Domicilio completo en una línea. */
|
|
26
|
+
@Expose()
|
|
27
|
+
@IsString()
|
|
28
|
+
@IsNotEmpty()
|
|
29
|
+
@MaxLength(LONG_TEXT)
|
|
30
|
+
address: string;
|
|
31
|
+
|
|
32
|
+
@Expose()
|
|
33
|
+
@IsOptional()
|
|
34
|
+
@IsString()
|
|
35
|
+
@MaxLength(SHORT_TEXT)
|
|
36
|
+
rfc?: string;
|
|
37
|
+
|
|
38
|
+
@Expose()
|
|
39
|
+
@IsOptional()
|
|
40
|
+
@IsString()
|
|
41
|
+
@MaxLength(SHORT_TEXT)
|
|
42
|
+
birthDate?: string;
|
|
43
|
+
|
|
44
|
+
/** Identificación oficial con la que se validó al acreditado (p. ej. INE). */
|
|
45
|
+
@Expose()
|
|
46
|
+
@IsOptional()
|
|
47
|
+
@IsString()
|
|
48
|
+
@MaxLength(SHORT_TEXT)
|
|
49
|
+
identification?: string;
|
|
50
|
+
|
|
51
|
+
@Expose()
|
|
52
|
+
@IsOptional()
|
|
53
|
+
@IsString()
|
|
54
|
+
@MaxLength(SHORT_TEXT)
|
|
55
|
+
phone?: string;
|
|
56
|
+
|
|
57
|
+
@Expose()
|
|
58
|
+
@IsOptional()
|
|
59
|
+
@IsString()
|
|
60
|
+
@MaxLength(LONG_TEXT)
|
|
61
|
+
email?: string;
|
|
62
|
+
|
|
63
|
+
@Expose()
|
|
64
|
+
@IsOptional()
|
|
65
|
+
@IsString()
|
|
66
|
+
@MaxLength(SHORT_TEXT)
|
|
67
|
+
occupation?: string;
|
|
68
|
+
|
|
69
|
+
/** Ingresos mensuales declarados, ya formateados (monto o rango). */
|
|
70
|
+
@Expose()
|
|
71
|
+
@IsOptional()
|
|
72
|
+
@IsString()
|
|
73
|
+
@MaxLength(SHORT_TEXT)
|
|
74
|
+
monthlyIncome?: string;
|
|
75
|
+
|
|
76
|
+
@Expose()
|
|
77
|
+
@IsOptional()
|
|
78
|
+
@IsString()
|
|
79
|
+
@MaxLength(LONG_TEXT)
|
|
80
|
+
fundsOrigin?: string;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** Equipo celular que financia el crédito (Sección II.2). */
|
|
84
|
+
export class LoanContractDeviceDetails {
|
|
85
|
+
@Expose()
|
|
86
|
+
@IsString()
|
|
87
|
+
@IsNotEmpty()
|
|
88
|
+
@MaxLength(SHORT_TEXT)
|
|
89
|
+
brand: string;
|
|
90
|
+
|
|
91
|
+
@Expose()
|
|
92
|
+
@IsString()
|
|
93
|
+
@IsNotEmpty()
|
|
94
|
+
@MaxLength(SHORT_TEXT)
|
|
95
|
+
model: string;
|
|
96
|
+
|
|
97
|
+
@Expose()
|
|
98
|
+
@IsOptional()
|
|
99
|
+
@IsString()
|
|
100
|
+
@MaxLength(SHORT_TEXT)
|
|
101
|
+
color?: string;
|
|
102
|
+
|
|
103
|
+
/** Capacidad de almacenamiento, ya formateada (p. ej. "128 GB"). */
|
|
104
|
+
@Expose()
|
|
105
|
+
@IsOptional()
|
|
106
|
+
@IsString()
|
|
107
|
+
@MaxLength(SHORT_TEXT)
|
|
108
|
+
storage?: string;
|
|
109
|
+
|
|
110
|
+
@Expose()
|
|
111
|
+
@IsOptional()
|
|
112
|
+
@IsString()
|
|
113
|
+
@Matches(IMEI_PATTERN, { message: 'imei admite solo 15 dígitos' })
|
|
114
|
+
imei?: string;
|
|
115
|
+
|
|
116
|
+
@Expose()
|
|
117
|
+
@IsOptional()
|
|
118
|
+
@IsString()
|
|
119
|
+
@MaxLength(SHORT_TEXT)
|
|
120
|
+
serialNumber?: string;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/** Proveedor que vende el equipo y recibe el desembolso (Sección II.3). */
|
|
124
|
+
export class LoanContractProviderDetails {
|
|
125
|
+
@Expose()
|
|
126
|
+
@IsString()
|
|
127
|
+
@IsNotEmpty()
|
|
128
|
+
@MaxLength(LONG_TEXT)
|
|
129
|
+
legalName: string;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Datos del contrato que el motor de crédito no guarda: los aporta el wizard al iniciar la firma
|
|
134
|
+
* y se imprimen en las plantillas de pdf-render-business. Son PII transitoria: no se persisten ni
|
|
135
|
+
* se loggean.
|
|
136
|
+
*/
|
|
137
|
+
export class LoanContractDetails {
|
|
138
|
+
@Expose()
|
|
139
|
+
@IsDefined()
|
|
140
|
+
@ValidateNested()
|
|
141
|
+
@Type(() => LoanContractBorrowerDetails)
|
|
142
|
+
borrower: LoanContractBorrowerDetails;
|
|
143
|
+
|
|
144
|
+
@Expose()
|
|
145
|
+
@IsDefined()
|
|
146
|
+
@ValidateNested()
|
|
147
|
+
@Type(() => LoanContractDeviceDetails)
|
|
148
|
+
device: LoanContractDeviceDetails;
|
|
149
|
+
|
|
150
|
+
/** Si falta, el motor lo resuelve con la tienda del crédito. */
|
|
151
|
+
@Expose()
|
|
152
|
+
@IsOptional()
|
|
153
|
+
@ValidateNested()
|
|
154
|
+
@Type(() => LoanContractProviderDetails)
|
|
155
|
+
provider?: LoanContractProviderDetails;
|
|
156
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsObject, IsOptional, IsString, Length } from 'class-validator';
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { IsObject, IsOptional, IsString, Length, ValidateNested } from 'class-validator';
|
|
3
|
+
import { LoanContractDetails } from '../LoanContractDetails';
|
|
3
4
|
|
|
4
5
|
/**
|
|
5
6
|
* Body de `POST /private/credits/:creditId/sign` (loan-credit-business — wizard paso 08, firma FES
|
|
@@ -43,6 +44,17 @@ export class SignLoanCreditRequest {
|
|
|
43
44
|
@IsObject()
|
|
44
45
|
contractData?: Record<string, string>;
|
|
45
46
|
|
|
47
|
+
/**
|
|
48
|
+
* SOLO en el inicio de firma (tiempo 1): datos del acreditado, del equipo y del proveedor que
|
|
49
|
+
* imprimen las plantillas de pdf-render-business. Los aporta el WIZARD; mismas reglas de PII
|
|
50
|
+
* que `contractData`.
|
|
51
|
+
*/
|
|
52
|
+
@Expose()
|
|
53
|
+
@IsOptional()
|
|
54
|
+
@ValidateNested()
|
|
55
|
+
@Type(() => LoanContractDetails)
|
|
56
|
+
contractDetails?: LoanContractDetails;
|
|
57
|
+
|
|
46
58
|
/**
|
|
47
59
|
* SOLO en el inicio de firma (tiempo 1) con el OTP REAL: el teléfono destino del SMS (E.164).
|
|
48
60
|
* PII TRANSITORIA: el motor la pasa a messages-business y NO la persiste ni loggea (silo
|
package/src/loanCredit/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './dtos/LoanCredit';
|
|
|
12
12
|
export * from './dtos/LoanInstallment';
|
|
13
13
|
export * from './dtos/InstallmentPaidEvent';
|
|
14
14
|
export * from './dtos/LoanContract';
|
|
15
|
+
export * from './dtos/LoanContractDetails';
|
|
15
16
|
export * from './dtos/ActivationChecklist';
|
|
16
17
|
export * from './dtos/AppliedPromotionSnapshot';
|
|
17
18
|
export * from './dtos/AppliedStorePolicySnapshot';
|
|
@@ -15,6 +15,8 @@ import type { WizardReservedLine } from './WizardReservedLine';
|
|
|
15
15
|
import type { WizardResumeTarget } from './WizardResumeTarget';
|
|
16
16
|
import type { WizardWelcome } from './WizardWelcome';
|
|
17
17
|
import type { WizardCreditPromotionBonus } from './WizardCreditPromotionBonus';
|
|
18
|
+
import type { EmailStateEnum } from '../enums/EmailStateEnum';
|
|
19
|
+
import type { EmailTypeEnum } from '../../emailVerification/enums/EmailTypeEnum';
|
|
18
20
|
|
|
19
21
|
/** Modalidad de captura del KYC (M1 §213-329). */
|
|
20
22
|
export type KycModality = 'CUSTOMER_LINK' | 'STORE_DEVICE' | 'MANUAL';
|
|
@@ -68,6 +70,14 @@ export interface WizardSession {
|
|
|
68
70
|
*/
|
|
69
71
|
resumeTarget?: WizardResumeTarget | null;
|
|
70
72
|
consents: WizardConsents | null;
|
|
73
|
+
/** Estado del correo del cliente en el paso 01. Ausente en sesiones anteriores al correo. */
|
|
74
|
+
emailState?: EmailStateEnum;
|
|
75
|
+
/** Tipo del correo: personal o corporativo. */
|
|
76
|
+
emailType?: EmailTypeEnum;
|
|
77
|
+
/** Correo enmascarado (`ju•••••@gmail.com`); viaja siempre que la sesión tenga correo. */
|
|
78
|
+
emailMasked?: string;
|
|
79
|
+
/** Correo completo: solo con `emailState` CAPTURED, porque lo tecleó el vendedor. */
|
|
80
|
+
email?: string;
|
|
71
81
|
/** Espejo del estado KYC (fuente = customer). */
|
|
72
82
|
kycStatus: string | null;
|
|
73
83
|
kycModality: KycModality | null;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsEmail, IsEnum } from 'class-validator';
|
|
3
|
+
import { EmailTypeEnum } from '../../../emailVerification/enums/EmailTypeEnum';
|
|
4
|
+
|
|
5
|
+
/** POST /wizard/sessions/:id/email/otp/send — manda el código al correo que capturó el vendedor. */
|
|
6
|
+
export class SendEmailOtpRequest {
|
|
7
|
+
@Expose()
|
|
8
|
+
@IsEmail()
|
|
9
|
+
email!: string;
|
|
10
|
+
|
|
11
|
+
@Expose()
|
|
12
|
+
@IsEnum(EmailTypeEnum)
|
|
13
|
+
type!: EmailTypeEnum;
|
|
14
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsString, Length } from 'class-validator';
|
|
3
|
+
|
|
4
|
+
/** POST /wizard/sessions/:id/email/otp/verify — verifica el código que llegó al correo. */
|
|
5
|
+
export class VerifyEmailOtpRequest {
|
|
6
|
+
@Expose()
|
|
7
|
+
@IsString()
|
|
8
|
+
@Length(4, 8)
|
|
9
|
+
code!: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Correo de la sesión del wizard: EXISTING = ya verificado en contacts; CAPTURED = lo tecleó el
|
|
3
|
+
* vendedor y se verificó con OTP; PENDING = falta capturarlo o verificarlo.
|
|
4
|
+
*/
|
|
5
|
+
export enum EmailStateEnum {
|
|
6
|
+
EXISTING = 'EXISTING',
|
|
7
|
+
CAPTURED = 'CAPTURED',
|
|
8
|
+
PENDING = 'PENDING',
|
|
9
|
+
}
|
|
@@ -29,6 +29,7 @@ export * from './enums/BlockedCategoryEnum';
|
|
|
29
29
|
export * from './enums/WizardLineStatusEnum';
|
|
30
30
|
export * from './enums/LinePaymentTypeEnum';
|
|
31
31
|
export * from './enums/WizardLineRoleEnum';
|
|
32
|
+
export * from './enums/EmailStateEnum';
|
|
32
33
|
|
|
33
34
|
// DTOs anidados
|
|
34
35
|
export * from './dtos/WizardConsents';
|
|
@@ -101,6 +102,8 @@ export * from './dtos/requests/SignatureSignRequest';
|
|
|
101
102
|
export * from './dtos/requests/RegisterDeviceRequest';
|
|
102
103
|
export * from './dtos/requests/CompleteSaleRequest';
|
|
103
104
|
export * from './dtos/requests/SuggestIdentifiersQuery';
|
|
105
|
+
export * from './dtos/requests/SendEmailOtpRequest';
|
|
106
|
+
export * from './dtos/requests/VerifyEmailOtpRequest';
|
|
104
107
|
|
|
105
108
|
// Response DTOs (output tipado por endpoint)
|
|
106
109
|
export * from './dtos/responses/KycPrepareResponse';
|