@fiado/type-kit 3.489.0 → 3.491.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/loanCredit/customerPaymentDtos.test.ts +2 -0
- package/_test_/unit/loanCredit/dtos/createCustomerPaymentRequest.test.ts +18 -4
- package/_test_/unit/loanOfferings/appliedPromotionBonus.test.ts +22 -0
- package/_test_/unit/retailCustomer/promotionBonusContract.test.ts +101 -0
- package/_test_/unit/retailLoan/paymentContract.test.ts +3 -1
- package/_test_/unit/retailWizard/creditPromotionBonusContract.test.ts +26 -0
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.d.ts +3 -1
- package/bin/loanCredit/dtos/requests/CreateCustomerPaymentRequest.js +7 -0
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentOptions.d.ts +21 -2
- package/bin/loanCredit/dtos/responses/LoanCustomerPaymentResult.d.ts +2 -0
- package/bin/loanCredit/enums/LoanCustomerPaymentTypeEnum.d.ts +5 -1
- package/bin/loanCredit/enums/LoanCustomerPaymentTypeEnum.js +4 -0
- package/bin/loanOfferings/dtos/responses/SimulationResultResponse.d.ts +2 -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/index.d.ts +3 -0
- package/bin/retailCustomer/index.js +3 -0
- package/bin/retailLoan/dtos/CreateMyPaymentRequest.d.ts +3 -1
- package/bin/retailLoan/dtos/CreateMyPaymentRequest.js +7 -0
- package/bin/retailLoan/dtos/CreateMyPaymentResponse.d.ts +2 -0
- package/bin/retailLoan/dtos/MyPaymentOptions.d.ts +21 -1
- package/bin/retailLoan/enums/MyPaymentTypeEnum.d.ts +3 -2
- package/bin/retailLoan/enums/MyPaymentTypeEnum.js +2 -1
- 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/loanCredit/dtos/requests/CreateCustomerPaymentRequest.ts +9 -2
- package/src/loanCredit/dtos/responses/LoanCustomerPaymentOptions.ts +26 -3
- package/src/loanCredit/dtos/responses/LoanCustomerPaymentResult.ts +2 -0
- package/src/loanCredit/enums/LoanCustomerPaymentTypeEnum.ts +4 -0
- package/src/loanOfferings/dtos/responses/SimulationResultResponse.ts +2 -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/index.ts +3 -0
- package/src/retailLoan/dtos/CreateMyPaymentRequest.ts +9 -2
- package/src/retailLoan/dtos/CreateMyPaymentResponse.ts +2 -0
- package/src/retailLoan/dtos/MyPaymentOptions.ts +26 -2
- package/src/retailLoan/enums/MyPaymentTypeEnum.ts +2 -1
- 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
|
@@ -22,6 +22,7 @@ describe('loanCredit — opciones y resultado del pago del cliente', () => {
|
|
|
22
22
|
dueDate: '2026-10-01',
|
|
23
23
|
},
|
|
24
24
|
advance: { available: false, unavailableReason: LoanPaymentOptionUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
25
|
+
prepayment: { available: false, unavailableReason: LoanPaymentOptionUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
25
26
|
settlement: {
|
|
26
27
|
available: true,
|
|
27
28
|
amountCents: 912345,
|
|
@@ -51,6 +52,7 @@ describe('loanCredit — opciones y resultado del pago del cliente', () => {
|
|
|
51
52
|
pendingCharge: { kind: LoanAdHocChargeKindEnum.ADVANCE, amountCents: 105000 },
|
|
52
53
|
nextInstallment: blocked,
|
|
53
54
|
advance: blocked,
|
|
55
|
+
prepayment: blocked,
|
|
54
56
|
settlement: blocked,
|
|
55
57
|
};
|
|
56
58
|
|
|
@@ -24,7 +24,7 @@ const VALID = {
|
|
|
24
24
|
};
|
|
25
25
|
|
|
26
26
|
describe.each([
|
|
27
|
-
['LoanCustomerPaymentTypeEnum', LoanCustomerPaymentTypeEnum, ['NEXT_INSTALLMENT', 'SETTLEMENT']],
|
|
27
|
+
['LoanCustomerPaymentTypeEnum', LoanCustomerPaymentTypeEnum, ['NEXT_INSTALLMENT', 'SETTLEMENT', 'ADVANCE', 'PREPAYMENT']],
|
|
28
28
|
['LoanNextInstallmentModeEnum', LoanNextInstallmentModeEnum, ['ADVANCE', 'COLLECT_DUE']],
|
|
29
29
|
[
|
|
30
30
|
'LoanPaymentOptionUnavailableReasonEnum',
|
|
@@ -44,7 +44,11 @@ describe.each([
|
|
|
44
44
|
});
|
|
45
45
|
|
|
46
46
|
describe('CreateCustomerPaymentRequest', () => {
|
|
47
|
-
it.each([
|
|
47
|
+
it.each([
|
|
48
|
+
[LoanCustomerPaymentTypeEnum.NEXT_INSTALLMENT],
|
|
49
|
+
[LoanCustomerPaymentTypeEnum.SETTLEMENT],
|
|
50
|
+
[LoanCustomerPaymentTypeEnum.PREPAYMENT],
|
|
51
|
+
])(
|
|
48
52
|
'acepta el tipo %s',
|
|
49
53
|
async type => {
|
|
50
54
|
expect(await errorProperties({ ...VALID, type })).toEqual([]);
|
|
@@ -57,8 +61,18 @@ describe('CreateCustomerPaymentRequest', () => {
|
|
|
57
61
|
);
|
|
58
62
|
});
|
|
59
63
|
|
|
60
|
-
it
|
|
61
|
-
expect(await errorProperties({ ...VALID, type })).toEqual([
|
|
64
|
+
it('acepta varias cuotas con su número de cuotas', async () => {
|
|
65
|
+
expect(await errorProperties({ ...VALID, type: LoanCustomerPaymentTypeEnum.ADVANCE, installmentCount: 3 })).toEqual([]);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it.each([[1], [0], [2.5], ['3']])('rechaza el número de cuotas %p', async installmentCount => {
|
|
69
|
+
expect(await errorProperties({ ...VALID, type: LoanCustomerPaymentTypeEnum.ADVANCE, installmentCount })).toEqual([
|
|
70
|
+
'installmentCount',
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
it('rechaza un tipo desconocido', async () => {
|
|
75
|
+
expect(await errorProperties({ ...VALID, type: 'PARTIAL' })).toEqual(['type']);
|
|
62
76
|
});
|
|
63
77
|
|
|
64
78
|
it.each([[0], [-100], [10.5], ['912345']])('rechaza el monto %p', async expectedAmountCents => {
|
|
@@ -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
|
+
});
|
|
@@ -22,7 +22,7 @@ const errorProperties = async (body: object): Promise<string[]> => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
describe.each([
|
|
25
|
-
['MyPaymentTypeEnum', MyPaymentTypeEnum, ['NEXT_INSTALLMENT', 'ADVANCE', 'SETTLEMENT']],
|
|
25
|
+
['MyPaymentTypeEnum', MyPaymentTypeEnum, ['NEXT_INSTALLMENT', 'ADVANCE', 'SETTLEMENT', 'PREPAYMENT']],
|
|
26
26
|
['MyNextInstallmentModeEnum', MyNextInstallmentModeEnum, ['ADVANCE', 'COLLECT_DUE']],
|
|
27
27
|
[
|
|
28
28
|
'MyPaymentUnavailableReasonEnum',
|
|
@@ -85,6 +85,7 @@ describe('retailLoan — opciones y respuesta del pago', () => {
|
|
|
85
85
|
dueDate: '2026-10-01',
|
|
86
86
|
},
|
|
87
87
|
advance: { available: false, unavailableReason: MyPaymentUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
88
|
+
prepayment: { available: false, unavailableReason: MyPaymentUnavailableReasonEnum.NOT_AVAILABLE_YET },
|
|
88
89
|
settlement: {
|
|
89
90
|
available: true,
|
|
90
91
|
amount: 9123.45,
|
|
@@ -104,6 +105,7 @@ describe('retailLoan — opciones y respuesta del pago', () => {
|
|
|
104
105
|
pendingPayment: { type: MyPaymentTypeEnum.NEXT_INSTALLMENT, amount: 1050 },
|
|
105
106
|
nextInstallment: blocked,
|
|
106
107
|
advance: blocked,
|
|
108
|
+
prepayment: blocked,
|
|
107
109
|
settlement: blocked,
|
|
108
110
|
};
|
|
109
111
|
|
|
@@ -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
|
+
});
|
|
@@ -2,8 +2,10 @@ import { LoanCustomerPaymentTypeEnum } from '../../enums/LoanCustomerPaymentType
|
|
|
2
2
|
/** Body de `POST /private/customers/:directoryId/credits/:creditId/payments` (loan-credit-business). */
|
|
3
3
|
export declare class CreateCustomerPaymentRequest {
|
|
4
4
|
type: LoanCustomerPaymentTypeEnum;
|
|
5
|
-
/** Monto que vio el cliente; si ya no coincide, responde 409 CUSTOMER_PAYMENT_AMOUNT_CHANGED. */
|
|
5
|
+
/** Monto que vio el cliente (en PREPAYMENT, el que eligió); si ya no coincide, responde 409 CUSTOMER_PAYMENT_AMOUNT_CHANGED. */
|
|
6
6
|
expectedAmountCents: number;
|
|
7
7
|
/** Llave del reintento: la misma llave con el mismo cuerpo devuelve la misma respuesta. */
|
|
8
8
|
idempotencyKey: string;
|
|
9
|
+
/** Solo ADVANCE: cuántas cuotas adelanta. */
|
|
10
|
+
installmentCount?: number;
|
|
9
11
|
}
|
|
@@ -33,3 +33,10 @@ __decorate([
|
|
|
33
33
|
(0, class_validator_1.IsUUID)(),
|
|
34
34
|
__metadata("design:type", String)
|
|
35
35
|
], CreateCustomerPaymentRequest.prototype, "idempotencyKey", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
(0, class_transformer_1.Expose)(),
|
|
38
|
+
(0, class_validator_1.IsOptional)(),
|
|
39
|
+
(0, class_validator_1.IsInt)(),
|
|
40
|
+
(0, class_validator_1.Min)(2),
|
|
41
|
+
__metadata("design:type", Number)
|
|
42
|
+
], CreateCustomerPaymentRequest.prototype, "installmentCount", void 0);
|
|
@@ -8,6 +8,13 @@ export interface LoanCustomerPaymentOption {
|
|
|
8
8
|
unavailableReason?: LoanPaymentOptionUnavailableReasonEnum;
|
|
9
9
|
amountCents?: number;
|
|
10
10
|
}
|
|
11
|
+
/** Una cuota que se puede adelantar. */
|
|
12
|
+
export interface LoanCustomerAdvanceInstallment {
|
|
13
|
+
installmentNumber: number;
|
|
14
|
+
/** `YYYY-MM-DD`. */
|
|
15
|
+
dueDate: string;
|
|
16
|
+
amountCents: number;
|
|
17
|
+
}
|
|
11
18
|
/** Response de `GET /private/customers/:directoryId/credits/:creditId/payment-options`. Montos en cents. */
|
|
12
19
|
export interface LoanCustomerPaymentOptions {
|
|
13
20
|
creditId: string;
|
|
@@ -15,6 +22,8 @@ export interface LoanCustomerPaymentOptions {
|
|
|
15
22
|
pendingCharge?: {
|
|
16
23
|
kind: LoanAdHocChargeKindEnum;
|
|
17
24
|
amountCents: number;
|
|
25
|
+
/** Solo en un adelanto: las cuotas que cubre. */
|
|
26
|
+
installmentNumbers?: number[];
|
|
18
27
|
};
|
|
19
28
|
nextInstallment: LoanCustomerPaymentOption & {
|
|
20
29
|
mode?: LoanNextInstallmentModeEnum;
|
|
@@ -22,8 +31,18 @@ export interface LoanCustomerPaymentOptions {
|
|
|
22
31
|
/** `YYYY-MM-DD`. */
|
|
23
32
|
dueDate?: string;
|
|
24
33
|
};
|
|
25
|
-
/** Varias cuotas
|
|
26
|
-
advance: LoanCustomerPaymentOption
|
|
34
|
+
/** Varias cuotas. `installments` trae las `maxInstallments` que se pueden adelantar, en orden. */
|
|
35
|
+
advance: LoanCustomerPaymentOption & {
|
|
36
|
+
minInstallments?: number;
|
|
37
|
+
maxInstallments?: number;
|
|
38
|
+
installments?: LoanCustomerAdvanceInstallment[];
|
|
39
|
+
};
|
|
40
|
+
/** Abono a capital: monto libre entre `minAmountCents` y `maxAmountCents`. */
|
|
41
|
+
prepayment: LoanCustomerPaymentOption & {
|
|
42
|
+
minAmountCents?: number;
|
|
43
|
+
maxAmountCents?: number;
|
|
44
|
+
outstandingCapitalCents?: number;
|
|
45
|
+
};
|
|
27
46
|
settlement: LoanCustomerPaymentOption & {
|
|
28
47
|
/** La cotización de hoy; su `settlementAmountCents` es el `amountCents`. */
|
|
29
48
|
quote?: LoanSettlementQuote;
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
/** Pago que el cliente puede pedir desde la app. */
|
|
2
2
|
export declare enum LoanCustomerPaymentTypeEnum {
|
|
3
3
|
NEXT_INSTALLMENT = "NEXT_INSTALLMENT",
|
|
4
|
-
SETTLEMENT = "SETTLEMENT"
|
|
4
|
+
SETTLEMENT = "SETTLEMENT",
|
|
5
|
+
/** Varias cuotas: las siguientes N tal como están, sin cambiar la tabla. */
|
|
6
|
+
ADVANCE = "ADVANCE",
|
|
7
|
+
/** Abono a capital: va completo a capital y baja las cuotas siguientes. */
|
|
8
|
+
PREPAYMENT = "PREPAYMENT"
|
|
5
9
|
}
|
|
@@ -6,4 +6,8 @@ var LoanCustomerPaymentTypeEnum;
|
|
|
6
6
|
(function (LoanCustomerPaymentTypeEnum) {
|
|
7
7
|
LoanCustomerPaymentTypeEnum["NEXT_INSTALLMENT"] = "NEXT_INSTALLMENT";
|
|
8
8
|
LoanCustomerPaymentTypeEnum["SETTLEMENT"] = "SETTLEMENT";
|
|
9
|
+
/** Varias cuotas: las siguientes N tal como están, sin cambiar la tabla. */
|
|
10
|
+
LoanCustomerPaymentTypeEnum["ADVANCE"] = "ADVANCE";
|
|
11
|
+
/** Abono a capital: va completo a capital y baja las cuotas siguientes. */
|
|
12
|
+
LoanCustomerPaymentTypeEnum["PREPAYMENT"] = "PREPAYMENT";
|
|
9
13
|
})(LoanCustomerPaymentTypeEnum || (exports.LoanCustomerPaymentTypeEnum = LoanCustomerPaymentTypeEnum = {}));
|
|
@@ -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.
|
|
@@ -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 = {}));
|
|
@@ -7,6 +7,7 @@ export * from './enums/RetailCustomerStatusEnum';
|
|
|
7
7
|
export * from './enums/WelcomeBonusStatusEnum';
|
|
8
8
|
export * from './enums/WelcomeBonusAmountSourceEnum';
|
|
9
9
|
export * from './enums/WelcomeBonusSettlementStatusEnum';
|
|
10
|
+
export * from './enums/BonusTypeEnum';
|
|
10
11
|
export * from './dtos/KycReview';
|
|
11
12
|
export * from './dtos/RetailAddress';
|
|
12
13
|
export * from './dtos/RetailCustomer';
|
|
@@ -17,10 +18,12 @@ export * from './dtos/requests/AccountResultRequest';
|
|
|
17
18
|
export * from './dtos/requests/KycReviewRequest';
|
|
18
19
|
export * from './dtos/requests/WelcomeAccreditationRequest';
|
|
19
20
|
export * from './dtos/requests/WelcomeAccreditationResultRequest';
|
|
21
|
+
export * from './dtos/requests/UpsertPromotionBonusRequest';
|
|
20
22
|
export * from './dtos/responses/CustomerResponse';
|
|
21
23
|
export * from './dtos/responses/Customer360Response';
|
|
22
24
|
export * from './dtos/responses/WelcomeBonusResponse';
|
|
23
25
|
export * from './dtos/responses/WelcomeAccreditationResponse';
|
|
26
|
+
export * from './dtos/responses/PromotionBonusEntryResponse';
|
|
24
27
|
export * from './events/CustomerCreatedV1';
|
|
25
28
|
export * from './events/CustomerUpdatedV1';
|
|
26
29
|
export * from './events/CustomerKycVerifiedV1';
|
|
@@ -24,6 +24,7 @@ __exportStar(require("./enums/RetailCustomerStatusEnum"), exports);
|
|
|
24
24
|
__exportStar(require("./enums/WelcomeBonusStatusEnum"), exports);
|
|
25
25
|
__exportStar(require("./enums/WelcomeBonusAmountSourceEnum"), exports);
|
|
26
26
|
__exportStar(require("./enums/WelcomeBonusSettlementStatusEnum"), exports);
|
|
27
|
+
__exportStar(require("./enums/BonusTypeEnum"), exports);
|
|
27
28
|
// Entidades (planas, con class-validator + @Expose)
|
|
28
29
|
__exportStar(require("./dtos/KycReview"), exports);
|
|
29
30
|
__exportStar(require("./dtos/RetailAddress"), exports);
|
|
@@ -36,11 +37,13 @@ __exportStar(require("./dtos/requests/AccountResultRequest"), exports);
|
|
|
36
37
|
__exportStar(require("./dtos/requests/KycReviewRequest"), exports);
|
|
37
38
|
__exportStar(require("./dtos/requests/WelcomeAccreditationRequest"), exports);
|
|
38
39
|
__exportStar(require("./dtos/requests/WelcomeAccreditationResultRequest"), exports);
|
|
40
|
+
__exportStar(require("./dtos/requests/UpsertPromotionBonusRequest"), exports);
|
|
39
41
|
// Response DTOs
|
|
40
42
|
__exportStar(require("./dtos/responses/CustomerResponse"), exports);
|
|
41
43
|
__exportStar(require("./dtos/responses/Customer360Response"), exports);
|
|
42
44
|
__exportStar(require("./dtos/responses/WelcomeBonusResponse"), exports);
|
|
43
45
|
__exportStar(require("./dtos/responses/WelcomeAccreditationResponse"), exports);
|
|
46
|
+
__exportStar(require("./dtos/responses/PromotionBonusEntryResponse"), exports);
|
|
44
47
|
// Contratos de eventos (PascalCase + sufijo V1)
|
|
45
48
|
__exportStar(require("./events/CustomerCreatedV1"), exports);
|
|
46
49
|
__exportStar(require("./events/CustomerUpdatedV1"), exports);
|
|
@@ -2,6 +2,8 @@ import { MyPaymentTypeEnum } from '../enums/MyPaymentTypeEnum';
|
|
|
2
2
|
/** Body de `POST /benefits/{benefitId}/credits/{creditId}/payments`. La llave va en el header `Idempotency-Key`. */
|
|
3
3
|
export declare class CreateMyPaymentRequest {
|
|
4
4
|
type: MyPaymentTypeEnum;
|
|
5
|
-
/** Monto en pesos que vio el cliente; si ya no coincide, responde PAYMENT_AMOUNT_CHANGED. */
|
|
5
|
+
/** Monto en pesos que vio el cliente (en PREPAYMENT, el que eligió); si ya no coincide, responde PAYMENT_AMOUNT_CHANGED. */
|
|
6
6
|
expectedAmount: number;
|
|
7
|
+
/** Solo ADVANCE: cuántas cuotas adelanta. */
|
|
8
|
+
installmentCount?: number;
|
|
7
9
|
}
|
|
@@ -28,3 +28,10 @@ __decorate([
|
|
|
28
28
|
(0, class_validator_1.IsPositive)(),
|
|
29
29
|
__metadata("design:type", Number)
|
|
30
30
|
], CreateMyPaymentRequest.prototype, "expectedAmount", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_transformer_1.Expose)(),
|
|
33
|
+
(0, class_validator_1.IsOptional)(),
|
|
34
|
+
(0, class_validator_1.IsInt)(),
|
|
35
|
+
(0, class_validator_1.Min)(2),
|
|
36
|
+
__metadata("design:type", Number)
|
|
37
|
+
], CreateMyPaymentRequest.prototype, "installmentCount", void 0);
|
|
@@ -7,12 +7,21 @@ export interface MyPaymentOption {
|
|
|
7
7
|
unavailableReason?: MyPaymentUnavailableReasonEnum;
|
|
8
8
|
amount?: number;
|
|
9
9
|
}
|
|
10
|
+
/** Una cuota que se puede adelantar. `amount` en pesos. */
|
|
11
|
+
export interface MyAdvanceInstallment {
|
|
12
|
+
installmentNumber: number;
|
|
13
|
+
/** `YYYY-MM-DD`. */
|
|
14
|
+
dueDate: string;
|
|
15
|
+
amount: number;
|
|
16
|
+
}
|
|
10
17
|
/** Opciones de pago de un crédito (`GET /benefits/{benefitId}/credits/{creditId}/payment-options`). Montos en pesos. */
|
|
11
18
|
export interface MyPaymentOptions {
|
|
12
19
|
/** Pago en curso; mientras exista, ninguna opción está disponible. */
|
|
13
20
|
pendingPayment?: {
|
|
14
21
|
type: MyPaymentTypeEnum;
|
|
15
22
|
amount: number;
|
|
23
|
+
/** Solo en varias cuotas: las cuotas que cubre. */
|
|
24
|
+
installmentNumbers?: number[];
|
|
16
25
|
};
|
|
17
26
|
nextInstallment: MyPaymentOption & {
|
|
18
27
|
mode?: MyNextInstallmentModeEnum;
|
|
@@ -20,7 +29,18 @@ export interface MyPaymentOptions {
|
|
|
20
29
|
/** `YYYY-MM-DD`. */
|
|
21
30
|
dueDate?: string;
|
|
22
31
|
};
|
|
23
|
-
|
|
32
|
+
/** Varias cuotas. `installments` trae las `maxInstallments` que se pueden adelantar, en orden. */
|
|
33
|
+
advance: MyPaymentOption & {
|
|
34
|
+
minInstallments?: number;
|
|
35
|
+
maxInstallments?: number;
|
|
36
|
+
installments?: MyAdvanceInstallment[];
|
|
37
|
+
};
|
|
38
|
+
/** Abono a capital: monto libre entre `minAmount` y `maxAmount`. */
|
|
39
|
+
prepayment: MyPaymentOption & {
|
|
40
|
+
minAmount?: number;
|
|
41
|
+
maxAmount?: number;
|
|
42
|
+
outstandingCapital?: number;
|
|
43
|
+
};
|
|
24
44
|
settlement: MyPaymentOption & {
|
|
25
45
|
breakdown?: {
|
|
26
46
|
overdue: number;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas. */
|
|
1
|
+
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas; `PREPAYMENT` = abono a capital. */
|
|
2
2
|
export declare enum MyPaymentTypeEnum {
|
|
3
3
|
NEXT_INSTALLMENT = "NEXT_INSTALLMENT",
|
|
4
4
|
ADVANCE = "ADVANCE",
|
|
5
|
-
SETTLEMENT = "SETTLEMENT"
|
|
5
|
+
SETTLEMENT = "SETTLEMENT",
|
|
6
|
+
PREPAYMENT = "PREPAYMENT"
|
|
6
7
|
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.MyPaymentTypeEnum = void 0;
|
|
4
|
-
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas. */
|
|
4
|
+
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas; `PREPAYMENT` = abono a capital. */
|
|
5
5
|
var MyPaymentTypeEnum;
|
|
6
6
|
(function (MyPaymentTypeEnum) {
|
|
7
7
|
MyPaymentTypeEnum["NEXT_INSTALLMENT"] = "NEXT_INSTALLMENT";
|
|
8
8
|
MyPaymentTypeEnum["ADVANCE"] = "ADVANCE";
|
|
9
9
|
MyPaymentTypeEnum["SETTLEMENT"] = "SETTLEMENT";
|
|
10
|
+
MyPaymentTypeEnum["PREPAYMENT"] = "PREPAYMENT";
|
|
10
11
|
})(MyPaymentTypeEnum || (exports.MyPaymentTypeEnum = MyPaymentTypeEnum = {}));
|
|
@@ -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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsEnum, IsInt, IsUUID, Min } from 'class-validator';
|
|
2
|
+
import { IsEnum, IsInt, IsOptional, IsUUID, Min } from 'class-validator';
|
|
3
3
|
import { LoanCustomerPaymentTypeEnum } from '../../enums/LoanCustomerPaymentTypeEnum';
|
|
4
4
|
|
|
5
5
|
/** Body de `POST /private/customers/:directoryId/credits/:creditId/payments` (loan-credit-business). */
|
|
@@ -8,7 +8,7 @@ export class CreateCustomerPaymentRequest {
|
|
|
8
8
|
@IsEnum(LoanCustomerPaymentTypeEnum)
|
|
9
9
|
type!: LoanCustomerPaymentTypeEnum;
|
|
10
10
|
|
|
11
|
-
/** Monto que vio el cliente; si ya no coincide, responde 409 CUSTOMER_PAYMENT_AMOUNT_CHANGED. */
|
|
11
|
+
/** Monto que vio el cliente (en PREPAYMENT, el que eligió); si ya no coincide, responde 409 CUSTOMER_PAYMENT_AMOUNT_CHANGED. */
|
|
12
12
|
@Expose()
|
|
13
13
|
@IsInt()
|
|
14
14
|
@Min(1)
|
|
@@ -18,4 +18,11 @@ export class CreateCustomerPaymentRequest {
|
|
|
18
18
|
@Expose()
|
|
19
19
|
@IsUUID()
|
|
20
20
|
idempotencyKey!: string;
|
|
21
|
+
|
|
22
|
+
/** Solo ADVANCE: cuántas cuotas adelanta. */
|
|
23
|
+
@Expose()
|
|
24
|
+
@IsOptional()
|
|
25
|
+
@IsInt()
|
|
26
|
+
@Min(2)
|
|
27
|
+
installmentCount?: number;
|
|
21
28
|
}
|
|
@@ -10,19 +10,42 @@ export interface LoanCustomerPaymentOption {
|
|
|
10
10
|
amountCents?: number;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** Una cuota que se puede adelantar. */
|
|
14
|
+
export interface LoanCustomerAdvanceInstallment {
|
|
15
|
+
installmentNumber: number;
|
|
16
|
+
/** `YYYY-MM-DD`. */
|
|
17
|
+
dueDate: string;
|
|
18
|
+
amountCents: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
13
21
|
/** Response de `GET /private/customers/:directoryId/credits/:creditId/payment-options`. Montos en cents. */
|
|
14
22
|
export interface LoanCustomerPaymentOptions {
|
|
15
23
|
creditId: string;
|
|
16
24
|
/** Cargo ad hoc en curso; mientras exista, ninguna opción está disponible. */
|
|
17
|
-
pendingCharge?: {
|
|
25
|
+
pendingCharge?: {
|
|
26
|
+
kind: LoanAdHocChargeKindEnum;
|
|
27
|
+
amountCents: number;
|
|
28
|
+
/** Solo en un adelanto: las cuotas que cubre. */
|
|
29
|
+
installmentNumbers?: number[];
|
|
30
|
+
};
|
|
18
31
|
nextInstallment: LoanCustomerPaymentOption & {
|
|
19
32
|
mode?: LoanNextInstallmentModeEnum;
|
|
20
33
|
installmentNumber?: number;
|
|
21
34
|
/** `YYYY-MM-DD`. */
|
|
22
35
|
dueDate?: string;
|
|
23
36
|
};
|
|
24
|
-
/** Varias cuotas
|
|
25
|
-
advance: LoanCustomerPaymentOption
|
|
37
|
+
/** Varias cuotas. `installments` trae las `maxInstallments` que se pueden adelantar, en orden. */
|
|
38
|
+
advance: LoanCustomerPaymentOption & {
|
|
39
|
+
minInstallments?: number;
|
|
40
|
+
maxInstallments?: number;
|
|
41
|
+
installments?: LoanCustomerAdvanceInstallment[];
|
|
42
|
+
};
|
|
43
|
+
/** Abono a capital: monto libre entre `minAmountCents` y `maxAmountCents`. */
|
|
44
|
+
prepayment: LoanCustomerPaymentOption & {
|
|
45
|
+
minAmountCents?: number;
|
|
46
|
+
maxAmountCents?: number;
|
|
47
|
+
outstandingCapitalCents?: number;
|
|
48
|
+
};
|
|
26
49
|
settlement: LoanCustomerPaymentOption & {
|
|
27
50
|
/** La cotización de hoy; su `settlementAmountCents` es el `amountCents`. */
|
|
28
51
|
quote?: LoanSettlementQuote;
|
|
@@ -9,4 +9,6 @@ export interface LoanCustomerPaymentResult {
|
|
|
9
9
|
amountCents: number;
|
|
10
10
|
/** Respuesta síncrona del motor; ausente si no contestó a tiempo. */
|
|
11
11
|
collection?: { status: CollectionResultStatus; amountCollectedCents: number };
|
|
12
|
+
/** Solo ADVANCE: cuántas cuotas cubre. */
|
|
13
|
+
installmentCount?: number;
|
|
12
14
|
}
|
|
@@ -2,4 +2,8 @@
|
|
|
2
2
|
export enum LoanCustomerPaymentTypeEnum {
|
|
3
3
|
NEXT_INSTALLMENT = 'NEXT_INSTALLMENT',
|
|
4
4
|
SETTLEMENT = 'SETTLEMENT',
|
|
5
|
+
/** Varias cuotas: las siguientes N tal como están, sin cambiar la tabla. */
|
|
6
|
+
ADVANCE = 'ADVANCE',
|
|
7
|
+
/** Abono a capital: va completo a capital y baja las cuotas siguientes. */
|
|
8
|
+
PREPAYMENT = 'PREPAYMENT',
|
|
5
9
|
}
|
|
@@ -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
|
/**
|
|
@@ -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
|
+
}
|
|
@@ -8,6 +8,7 @@ export * from './enums/RetailCustomerStatusEnum';
|
|
|
8
8
|
export * from './enums/WelcomeBonusStatusEnum';
|
|
9
9
|
export * from './enums/WelcomeBonusAmountSourceEnum';
|
|
10
10
|
export * from './enums/WelcomeBonusSettlementStatusEnum';
|
|
11
|
+
export * from './enums/BonusTypeEnum';
|
|
11
12
|
|
|
12
13
|
// Entidades (planas, con class-validator + @Expose)
|
|
13
14
|
export * from './dtos/KycReview';
|
|
@@ -22,12 +23,14 @@ export * from './dtos/requests/AccountResultRequest';
|
|
|
22
23
|
export * from './dtos/requests/KycReviewRequest';
|
|
23
24
|
export * from './dtos/requests/WelcomeAccreditationRequest';
|
|
24
25
|
export * from './dtos/requests/WelcomeAccreditationResultRequest';
|
|
26
|
+
export * from './dtos/requests/UpsertPromotionBonusRequest';
|
|
25
27
|
|
|
26
28
|
// Response DTOs
|
|
27
29
|
export * from './dtos/responses/CustomerResponse';
|
|
28
30
|
export * from './dtos/responses/Customer360Response';
|
|
29
31
|
export * from './dtos/responses/WelcomeBonusResponse';
|
|
30
32
|
export * from './dtos/responses/WelcomeAccreditationResponse';
|
|
33
|
+
export * from './dtos/responses/PromotionBonusEntryResponse';
|
|
31
34
|
|
|
32
35
|
// Contratos de eventos (PascalCase + sufijo V1)
|
|
33
36
|
export * from './events/CustomerCreatedV1';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Expose } from 'class-transformer';
|
|
2
|
-
import { IsEnum, IsNumber, IsPositive } from 'class-validator';
|
|
2
|
+
import { IsEnum, IsInt, IsNumber, IsOptional, IsPositive, Min } from 'class-validator';
|
|
3
3
|
import { MyPaymentTypeEnum } from '../enums/MyPaymentTypeEnum';
|
|
4
4
|
|
|
5
5
|
/** Body de `POST /benefits/{benefitId}/credits/{creditId}/payments`. La llave va en el header `Idempotency-Key`. */
|
|
@@ -8,9 +8,16 @@ export class CreateMyPaymentRequest {
|
|
|
8
8
|
@IsEnum(MyPaymentTypeEnum)
|
|
9
9
|
type!: MyPaymentTypeEnum;
|
|
10
10
|
|
|
11
|
-
/** Monto en pesos que vio el cliente; si ya no coincide, responde PAYMENT_AMOUNT_CHANGED. */
|
|
11
|
+
/** Monto en pesos que vio el cliente (en PREPAYMENT, el que eligió); si ya no coincide, responde PAYMENT_AMOUNT_CHANGED. */
|
|
12
12
|
@Expose()
|
|
13
13
|
@IsNumber({ maxDecimalPlaces: 2 })
|
|
14
14
|
@IsPositive()
|
|
15
15
|
expectedAmount!: number;
|
|
16
|
+
|
|
17
|
+
/** Solo ADVANCE: cuántas cuotas adelanta. */
|
|
18
|
+
@Expose()
|
|
19
|
+
@IsOptional()
|
|
20
|
+
@IsInt()
|
|
21
|
+
@Min(2)
|
|
22
|
+
installmentCount?: number;
|
|
16
23
|
}
|
|
@@ -9,17 +9,41 @@ export interface MyPaymentOption {
|
|
|
9
9
|
amount?: number;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
+
/** Una cuota que se puede adelantar. `amount` en pesos. */
|
|
13
|
+
export interface MyAdvanceInstallment {
|
|
14
|
+
installmentNumber: number;
|
|
15
|
+
/** `YYYY-MM-DD`. */
|
|
16
|
+
dueDate: string;
|
|
17
|
+
amount: number;
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
/** Opciones de pago de un crédito (`GET /benefits/{benefitId}/credits/{creditId}/payment-options`). Montos en pesos. */
|
|
13
21
|
export interface MyPaymentOptions {
|
|
14
22
|
/** Pago en curso; mientras exista, ninguna opción está disponible. */
|
|
15
|
-
pendingPayment?: {
|
|
23
|
+
pendingPayment?: {
|
|
24
|
+
type: MyPaymentTypeEnum;
|
|
25
|
+
amount: number;
|
|
26
|
+
/** Solo en varias cuotas: las cuotas que cubre. */
|
|
27
|
+
installmentNumbers?: number[];
|
|
28
|
+
};
|
|
16
29
|
nextInstallment: MyPaymentOption & {
|
|
17
30
|
mode?: MyNextInstallmentModeEnum;
|
|
18
31
|
installmentNumber?: number;
|
|
19
32
|
/** `YYYY-MM-DD`. */
|
|
20
33
|
dueDate?: string;
|
|
21
34
|
};
|
|
22
|
-
|
|
35
|
+
/** Varias cuotas. `installments` trae las `maxInstallments` que se pueden adelantar, en orden. */
|
|
36
|
+
advance: MyPaymentOption & {
|
|
37
|
+
minInstallments?: number;
|
|
38
|
+
maxInstallments?: number;
|
|
39
|
+
installments?: MyAdvanceInstallment[];
|
|
40
|
+
};
|
|
41
|
+
/** Abono a capital: monto libre entre `minAmount` y `maxAmount`. */
|
|
42
|
+
prepayment: MyPaymentOption & {
|
|
43
|
+
minAmount?: number;
|
|
44
|
+
maxAmount?: number;
|
|
45
|
+
outstandingCapital?: number;
|
|
46
|
+
};
|
|
23
47
|
settlement: MyPaymentOption & {
|
|
24
48
|
breakdown?: { overdue: number; capital: number; interest: number; iva: number };
|
|
25
49
|
savings?: number;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas. */
|
|
1
|
+
/** Pago que el cliente pide desde la app. `ADVANCE` = varias cuotas; `PREPAYMENT` = abono a capital. */
|
|
2
2
|
export enum MyPaymentTypeEnum {
|
|
3
3
|
NEXT_INSTALLMENT = 'NEXT_INSTALLMENT',
|
|
4
4
|
ADVANCE = 'ADVANCE',
|
|
5
5
|
SETTLEMENT = 'SETTLEMENT',
|
|
6
|
+
PREPAYMENT = 'PREPAYMENT',
|
|
6
7
|
}
|
|
@@ -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';
|