@fiado/type-kit 3.657.0 → 3.659.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/downPaymentBase.test.ts +48 -0
- package/_test_/unit/retailOrg/dtos/RetailUser.test.ts +12 -0
- package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.d.ts +2 -0
- package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.js +7 -0
- package/bin/loanCredit/dtos/requests/QuoteLoanCreditRequest.d.ts +2 -0
- package/bin/loanCredit/dtos/requests/QuoteLoanCreditRequest.js +7 -0
- package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.d.ts +2 -0
- package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.js +7 -0
- package/bin/retailOrg/dtos/RetailUser.d.ts +2 -0
- package/bin/retailOrg/dtos/RetailUser.js +7 -0
- package/bin/retailWizard/dtos/WizardFinancingPreview.d.ts +1 -1
- package/bin/retailWizard/dtos/WizardSession.d.ts +2 -0
- package/package.json +1 -1
- package/src/loanCredit/dtos/requests/OriginateLoanCreditRequest.ts +7 -0
- package/src/loanCredit/dtos/requests/QuoteLoanCreditRequest.ts +7 -0
- package/src/loanOfferings/dtos/requests/SimulateCreditPlanRequest.ts +7 -0
- package/src/retailOrg/dtos/RetailUser.ts +7 -0
- package/src/retailWizard/dtos/WizardFinancingPreview.ts +1 -1
- package/src/retailWizard/dtos/WizardSession.ts +2 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate } from 'class-validator';
|
|
4
|
+
import { OriginateLoanCreditRequest } from '../../../src/loanCredit/dtos/requests/OriginateLoanCreditRequest';
|
|
5
|
+
import { QuoteLoanCreditRequest } from '../../../src/loanCredit/dtos/requests/QuoteLoanCreditRequest';
|
|
6
|
+
import { SimulateCreditPlanRequest } from '../../../src/loanOfferings/dtos/requests/SimulateCreditPlanRequest';
|
|
7
|
+
|
|
8
|
+
type DtoClass = new () => object;
|
|
9
|
+
|
|
10
|
+
const CASES: Array<[string, DtoClass, object]> = [
|
|
11
|
+
['SimulateCreditPlanRequest', SimulateCreditPlanRequest, { productPriceCents: 1210000, downPaymentPct: 0.25, termWeeks: 52 }],
|
|
12
|
+
[
|
|
13
|
+
'OriginateLoanCreditRequest',
|
|
14
|
+
OriginateLoanCreditRequest,
|
|
15
|
+
{
|
|
16
|
+
wizardSessionId: 'WS-1',
|
|
17
|
+
borrowerId: 'B-1',
|
|
18
|
+
planId: 'PLAN-1',
|
|
19
|
+
termWeeks: 52,
|
|
20
|
+
equipmentPriceCents: 1210000,
|
|
21
|
+
downPaymentCents: 300000,
|
|
22
|
+
storeId: 'ST-1',
|
|
23
|
+
},
|
|
24
|
+
],
|
|
25
|
+
['QuoteLoanCreditRequest', QuoteLoanCreditRequest, { termWeeks: 52 }],
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const parse = (dtoClass: DtoClass, body: object) => plainToInstance(dtoClass, body, { excludeExtraneousValues: true });
|
|
29
|
+
|
|
30
|
+
const invalidProperties = async (dtoClass: DtoClass, body: object): Promise<string[]> =>
|
|
31
|
+
(await validate(parse(dtoClass, body))).map((error) => error.property);
|
|
32
|
+
|
|
33
|
+
describe.each(CASES)('%s con downPaymentBaseCents', (_name, dtoClass, base) => {
|
|
34
|
+
it('sigue siendo válido sin el campo', async () => {
|
|
35
|
+
expect(await invalidProperties(dtoClass, base)).toEqual([]);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
it('acepta y conserva un entero positivo', async () => {
|
|
39
|
+
const body = { ...base, downPaymentBaseCents: 1000000 };
|
|
40
|
+
|
|
41
|
+
expect(await invalidProperties(dtoClass, body)).toEqual([]);
|
|
42
|
+
expect(parse(dtoClass, body)).toHaveProperty('downPaymentBaseCents', 1000000);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each([0, -1, 10.5, '1000000'])('rechaza %p', async (value) => {
|
|
46
|
+
expect(await invalidProperties(dtoClass, { ...base, downPaymentBaseCents: value })).toContain('downPaymentBaseCents');
|
|
47
|
+
});
|
|
48
|
+
});
|
|
@@ -107,6 +107,7 @@ describe('RetailUser — wallet PCF', () => {
|
|
|
107
107
|
pcfAccountId: null,
|
|
108
108
|
walletLinkMethod: null,
|
|
109
109
|
walletClabeLast4: null,
|
|
110
|
+
walletPhoneLast4: null,
|
|
110
111
|
walletLinkedAt: null,
|
|
111
112
|
walletLinkedBy: null,
|
|
112
113
|
};
|
|
@@ -124,6 +125,17 @@ describe('RetailUser — wallet PCF', () => {
|
|
|
124
125
|
expect(errors.some(e => e.property === 'walletClabeLast4')).toBe(true);
|
|
125
126
|
});
|
|
126
127
|
|
|
128
|
+
it('valida la wallet vinculada por teléfono con sus últimos 4', async () => {
|
|
129
|
+
const byPhone = { ...linked, walletLinkMethod: RetailUserWalletLinkMethodEnum.PHONE, walletClabeLast4: null, walletPhoneLast4: '9900' };
|
|
130
|
+
const errors = await validate(plainToInstance(RetailUser, byPhone));
|
|
131
|
+
expect(errors).toEqual([]);
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('falla si walletPhoneLast4 trae el teléfono completo', async () => {
|
|
135
|
+
const errors = await validate(plainToInstance(RetailUser, { ...linked, walletPhoneLast4: '5512345678' }));
|
|
136
|
+
expect(errors.some(e => e.property === 'walletPhoneLast4')).toBe(true);
|
|
137
|
+
});
|
|
138
|
+
|
|
127
139
|
it('falla si walletLinkedAt no es ISO 8601', async () => {
|
|
128
140
|
const errors = await validate(plainToInstance(RetailUser, { ...linked, walletLinkedAt: '20/10/2026' }));
|
|
129
141
|
expect(errors.some(e => e.property === 'walletLinkedAt')).toBe(true);
|
|
@@ -20,6 +20,8 @@ export declare class OriginateLoanCreditRequest {
|
|
|
20
20
|
/** Precio del equipo en centavos; el techo es técnico, no de negocio (L-027). */
|
|
21
21
|
equipmentPriceCents: number;
|
|
22
22
|
downPaymentCents: number;
|
|
23
|
+
/** Base sobre la que se calcula el enganche mínimo; sin él, se usa el precio. */
|
|
24
|
+
downPaymentBaseCents?: number;
|
|
23
25
|
/**
|
|
24
26
|
* Id de la evaluación SCI del paso 05 (snapshot inmutable en loan-scoring). Si viene, el
|
|
25
27
|
* motor LEE el snapshot (getSciEvaluation), valida la decisión (REJECTED → 422
|
|
@@ -69,6 +69,13 @@ __decorate([
|
|
|
69
69
|
(0, class_validator_1.Max)(LoanInputLimits_1.LOAN_AMOUNT_MAX_CENTS),
|
|
70
70
|
__metadata("design:type", Number)
|
|
71
71
|
], OriginateLoanCreditRequest.prototype, "downPaymentCents", void 0);
|
|
72
|
+
__decorate([
|
|
73
|
+
(0, class_transformer_1.Expose)(),
|
|
74
|
+
(0, class_validator_1.IsOptional)(),
|
|
75
|
+
(0, class_validator_1.IsInt)(),
|
|
76
|
+
(0, class_validator_1.Min)(1),
|
|
77
|
+
__metadata("design:type", Number)
|
|
78
|
+
], OriginateLoanCreditRequest.prototype, "downPaymentBaseCents", void 0);
|
|
72
79
|
__decorate([
|
|
73
80
|
(0, class_transformer_1.Expose)(),
|
|
74
81
|
(0, class_validator_1.IsOptional)(),
|
|
@@ -37,3 +37,10 @@ __decorate([
|
|
|
37
37
|
(0, class_validator_1.Min)(0),
|
|
38
38
|
__metadata("design:type", Number)
|
|
39
39
|
], QuoteLoanCreditRequest.prototype, "downPaymentCents", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
(0, class_transformer_1.Expose)(),
|
|
42
|
+
(0, class_validator_1.IsOptional)(),
|
|
43
|
+
(0, class_validator_1.IsInt)(),
|
|
44
|
+
(0, class_validator_1.Min)(1),
|
|
45
|
+
__metadata("design:type", Number)
|
|
46
|
+
], QuoteLoanCreditRequest.prototype, "downPaymentBaseCents", void 0);
|
|
@@ -12,6 +12,8 @@ import { ProductTierEnum } from '../../../retailCatalog/enums/ProductTierEnum';
|
|
|
12
12
|
export declare class SimulateCreditPlanRequest {
|
|
13
13
|
productPriceCents: number;
|
|
14
14
|
downPaymentPct: number;
|
|
15
|
+
/** Base sobre la que se calcula el enganche mínimo; sin él, se usa el precio. */
|
|
16
|
+
downPaymentBaseCents?: number;
|
|
15
17
|
termWeeks: number;
|
|
16
18
|
customerSciScore?: number;
|
|
17
19
|
/** Tienda donde se vende. Sin él no se resuelven las promociones acotadas por tienda. */
|
|
@@ -40,6 +40,13 @@ __decorate([
|
|
|
40
40
|
(0, class_validator_1.Max)(1),
|
|
41
41
|
__metadata("design:type", Number)
|
|
42
42
|
], SimulateCreditPlanRequest.prototype, "downPaymentPct", void 0);
|
|
43
|
+
__decorate([
|
|
44
|
+
(0, class_transformer_1.Expose)(),
|
|
45
|
+
(0, class_validator_1.IsOptional)(),
|
|
46
|
+
(0, class_validator_1.IsInt)(),
|
|
47
|
+
(0, class_validator_1.Min)(1),
|
|
48
|
+
__metadata("design:type", Number)
|
|
49
|
+
], SimulateCreditPlanRequest.prototype, "downPaymentBaseCents", void 0);
|
|
43
50
|
__decorate([
|
|
44
51
|
(0, class_transformer_1.Expose)(),
|
|
45
52
|
(0, class_validator_1.IsInt)(),
|
|
@@ -37,6 +37,8 @@ export declare class RetailUser {
|
|
|
37
37
|
walletLinkMethod?: RetailUserWalletLinkMethodEnum | null;
|
|
38
38
|
/** Últimos 4 dígitos de la CLABE; la CLABE completa no se guarda. */
|
|
39
39
|
walletClabeLast4?: string | null;
|
|
40
|
+
/** Últimos 4 dígitos del teléfono con que se buscó la wallet; el teléfono completo no se guarda. */
|
|
41
|
+
walletPhoneLast4?: string | null;
|
|
40
42
|
/** Cuándo se vinculó (ISO). */
|
|
41
43
|
walletLinkedAt?: string | null;
|
|
42
44
|
/** Quién la vinculó: el actorId del usuario del back office. */
|
|
@@ -115,6 +115,13 @@ __decorate([
|
|
|
115
115
|
(0, class_validator_1.Matches)(/^\d{4}$/, { message: 'walletClabeLast4 debe tener 4 dígitos' }),
|
|
116
116
|
__metadata("design:type", String)
|
|
117
117
|
], RetailUser.prototype, "walletClabeLast4", void 0);
|
|
118
|
+
__decorate([
|
|
119
|
+
(0, class_transformer_1.Expose)(),
|
|
120
|
+
(0, class_validator_1.IsOptional)(),
|
|
121
|
+
(0, class_validator_1.IsString)(),
|
|
122
|
+
(0, class_validator_1.Matches)(/^\d{4}$/, { message: 'walletPhoneLast4 debe tener 4 dígitos' }),
|
|
123
|
+
__metadata("design:type", String)
|
|
124
|
+
], RetailUser.prototype, "walletPhoneLast4", void 0);
|
|
118
125
|
__decorate([
|
|
119
126
|
(0, class_transformer_1.Expose)(),
|
|
120
127
|
(0, class_validator_1.IsOptional)(),
|
|
@@ -5,7 +5,7 @@ export interface WizardFinancingPreviewLine {
|
|
|
5
5
|
}
|
|
6
6
|
/** Reparto provisional de la sesión; se calcula al leer y no se persiste. */
|
|
7
7
|
export interface WizardFinancingPreview {
|
|
8
|
-
/** Tope de extras
|
|
8
|
+
/** Tope de extras: mínimo entre 30% del financiable de la ancla y lo que falte para el crédito máximo. */
|
|
9
9
|
capCents: number;
|
|
10
10
|
lines: WizardFinancingPreviewLine[];
|
|
11
11
|
}
|
|
@@ -129,6 +129,8 @@ export interface WizardSession {
|
|
|
129
129
|
financedLineId?: string | null;
|
|
130
130
|
/** Reparto provisional mientras haya líneas; se calcula al leer, no se persiste. */
|
|
131
131
|
financingPreview?: WizardFinancingPreview | null;
|
|
132
|
+
/** Financiable de la ancla (neto − enganche mínimo); `null` si las cotas no están resueltas. */
|
|
133
|
+
anchorFinanceableCents?: number | null;
|
|
132
134
|
/** Marca de la saga: todas las piezas rastreadas de las líneas vivas quedaron HELD. */
|
|
133
135
|
piecesHeldAt?: string | null;
|
|
134
136
|
/** Promo de venta que fijó Promociones; `null` = ninguna. */
|
package/package.json
CHANGED
|
@@ -62,6 +62,13 @@ export class OriginateLoanCreditRequest {
|
|
|
62
62
|
@Max(LOAN_AMOUNT_MAX_CENTS)
|
|
63
63
|
downPaymentCents!: number;
|
|
64
64
|
|
|
65
|
+
/** Base sobre la que se calcula el enganche mínimo; sin él, se usa el precio. */
|
|
66
|
+
@Expose()
|
|
67
|
+
@IsOptional()
|
|
68
|
+
@IsInt()
|
|
69
|
+
@Min(1)
|
|
70
|
+
downPaymentBaseCents?: number;
|
|
71
|
+
|
|
65
72
|
/**
|
|
66
73
|
* Id de la evaluación SCI del paso 05 (snapshot inmutable en loan-scoring). Si viene, el
|
|
67
74
|
* motor LEE el snapshot (getSciEvaluation), valida la decisión (REJECTED → 422
|
|
@@ -21,4 +21,11 @@ export class QuoteLoanCreditRequest {
|
|
|
21
21
|
@IsInt()
|
|
22
22
|
@Min(0)
|
|
23
23
|
downPaymentCents?: number;
|
|
24
|
+
|
|
25
|
+
/** Base sobre la que se calcula el enganche mínimo; sin él, se usa el precio. */
|
|
26
|
+
@Expose()
|
|
27
|
+
@IsOptional()
|
|
28
|
+
@IsInt()
|
|
29
|
+
@Min(1)
|
|
30
|
+
downPaymentBaseCents?: number;
|
|
24
31
|
}
|
|
@@ -26,6 +26,13 @@ export class SimulateCreditPlanRequest {
|
|
|
26
26
|
@Max(1)
|
|
27
27
|
downPaymentPct!: number;
|
|
28
28
|
|
|
29
|
+
/** Base sobre la que se calcula el enganche mínimo; sin él, se usa el precio. */
|
|
30
|
+
@Expose()
|
|
31
|
+
@IsOptional()
|
|
32
|
+
@IsInt()
|
|
33
|
+
@Min(1)
|
|
34
|
+
downPaymentBaseCents?: number;
|
|
35
|
+
|
|
29
36
|
@Expose()
|
|
30
37
|
@IsInt()
|
|
31
38
|
@Min(1)
|
|
@@ -100,6 +100,13 @@ export class RetailUser {
|
|
|
100
100
|
@Matches(/^\d{4}$/, { message: 'walletClabeLast4 debe tener 4 dígitos' })
|
|
101
101
|
walletClabeLast4?: string | null;
|
|
102
102
|
|
|
103
|
+
/** Últimos 4 dígitos del teléfono con que se buscó la wallet; el teléfono completo no se guarda. */
|
|
104
|
+
@Expose()
|
|
105
|
+
@IsOptional()
|
|
106
|
+
@IsString()
|
|
107
|
+
@Matches(/^\d{4}$/, { message: 'walletPhoneLast4 debe tener 4 dígitos' })
|
|
108
|
+
walletPhoneLast4?: string | null;
|
|
109
|
+
|
|
103
110
|
/** Cuándo se vinculó (ISO). */
|
|
104
111
|
@Expose()
|
|
105
112
|
@IsOptional()
|
|
@@ -6,7 +6,7 @@ export interface WizardFinancingPreviewLine {
|
|
|
6
6
|
|
|
7
7
|
/** Reparto provisional de la sesión; se calcula al leer y no se persiste. */
|
|
8
8
|
export interface WizardFinancingPreview {
|
|
9
|
-
/** Tope de extras
|
|
9
|
+
/** Tope de extras: mínimo entre 30% del financiable de la ancla y lo que falte para el crédito máximo. */
|
|
10
10
|
capCents: number;
|
|
11
11
|
lines: WizardFinancingPreviewLine[];
|
|
12
12
|
}
|
|
@@ -133,6 +133,8 @@ export interface WizardSession {
|
|
|
133
133
|
financedLineId?: string | null;
|
|
134
134
|
/** Reparto provisional mientras haya líneas; se calcula al leer, no se persiste. */
|
|
135
135
|
financingPreview?: WizardFinancingPreview | null;
|
|
136
|
+
/** Financiable de la ancla (neto − enganche mínimo); `null` si las cotas no están resueltas. */
|
|
137
|
+
anchorFinanceableCents?: number | null;
|
|
136
138
|
/** Marca de la saga: todas las piezas rastreadas de las líneas vivas quedaron HELD. */
|
|
137
139
|
piecesHeldAt?: string | null;
|
|
138
140
|
/** Promo de venta que fijó Promociones; `null` = ninguna. */
|