@fiado/type-kit 3.489.0 → 3.490.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/_test_/unit/loanOfferings/appliedPromotionBonus.test.ts +22 -0
- package/_test_/unit/retailCustomer/promotionBonusContract.test.ts +101 -0
- package/_test_/unit/retailWizard/creditPromotionBonusContract.test.ts +26 -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/retailWizard/dtos/WizardCreditPromotionBonus.d.ts +8 -0
- package/bin/retailWizard/dtos/WizardCreditPromotionBonus.js +2 -0
- package/bin/retailWizard/dtos/WizardSession.d.ts +3 -0
- package/bin/retailWizard/dtos/responses/QuoteResponse.d.ts +2 -0
- package/bin/retailWizard/index.d.ts +1 -0
- package/bin/retailWizard/index.js +1 -0
- package/package.json +1 -1
- package/src/loanOfferings/dtos/responses/SimulationResultResponse.ts +2 -0
- package/src/retailCustomer/dtos/requests/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/retailWizard/dtos/WizardCreditPromotionBonus.ts +9 -0
- package/src/retailWizard/dtos/WizardSession.ts +3 -0
- package/src/retailWizard/dtos/responses/QuoteResponse.ts +2 -0
- package/src/retailWizard/index.ts +1 -0
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { LoanOfferings } from '../../../src/index';
|
|
3
|
+
import type { AppliedPromotionResponse } from '../../../src/loanOfferings/index';
|
|
4
|
+
|
|
5
|
+
describe('AppliedPromotionResponse.pcfBonusCents', () => {
|
|
6
|
+
it('la ganadora del bono lo trae; las demás lo omiten', () => {
|
|
7
|
+
const winner: AppliedPromotionResponse = {
|
|
8
|
+
promotionId: 'p-1',
|
|
9
|
+
name: 'Bono',
|
|
10
|
+
version: 2,
|
|
11
|
+
appliedEffects: [LoanOfferings.PromotionEffectKindEnum.PCF_BONUS],
|
|
12
|
+
pcfBonusCents: 5_000,
|
|
13
|
+
};
|
|
14
|
+
const other: AppliedPromotionResponse = {
|
|
15
|
+
promotionId: 'p-0',
|
|
16
|
+
name: 'Tasa',
|
|
17
|
+
version: 1,
|
|
18
|
+
appliedEffects: [LoanOfferings.PromotionEffectKindEnum.TNA_OVERRIDE],
|
|
19
|
+
};
|
|
20
|
+
expect([winner.pcfBonusCents, other.pcfBonusCents]).toEqual([5_000, undefined]);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validateSync } from 'class-validator';
|
|
4
|
+
import { RetailCustomer } from '../../../src/index';
|
|
5
|
+
import { BonusTypeEnum, UpsertPromotionBonusRequest } from '../../../src/retailCustomer/index';
|
|
6
|
+
import type { PromotionBonusEntryResponse } from '../../../src/retailCustomer/index';
|
|
7
|
+
|
|
8
|
+
describe('BonusTypeEnum', () => {
|
|
9
|
+
it('distingue la bienvenida de las dos promociones', () => {
|
|
10
|
+
expect(Object.values(BonusTypeEnum)).toEqual(['WELCOME', 'CREDIT_PROMOTION', 'SALE_PROMOTION']);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
it('sale por el index raíz', () => {
|
|
14
|
+
expect(RetailCustomer.BonusTypeEnum.SALE_PROMOTION).toBe('SALE_PROMOTION');
|
|
15
|
+
});
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
/** Valida como retail-customer-business: con `@Expose` y `excludeExtraneousValues`. */
|
|
19
|
+
function errorsOf(payload: Record<string, unknown>): string[] {
|
|
20
|
+
const dto = plainToInstance(UpsertPromotionBonusRequest, payload, { excludeExtraneousValues: true });
|
|
21
|
+
return validateSync(dto).map((e) => e.property);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const PENDING_SALE = {
|
|
25
|
+
bonusType: 'SALE_PROMOTION',
|
|
26
|
+
promotionId: 'promo-1',
|
|
27
|
+
sourceId: 'sale-1',
|
|
28
|
+
storeId: 'st-1',
|
|
29
|
+
amountCents: 10_000,
|
|
30
|
+
currency: 'MXN',
|
|
31
|
+
status: 'PENDING',
|
|
32
|
+
idempotencyKey: 'sale-1-WALLET-1',
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
describe('UpsertPromotionBonusRequest', () => {
|
|
36
|
+
it('acepta el PENDING «sin cuenta todavía», sin directorio', () => {
|
|
37
|
+
expect(errorsOf(PENDING_SALE)).toEqual([]);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('ACCREDITED exige el directorio que recibió el depósito', () => {
|
|
41
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'ACCREDITED' })).toContain('fiadoDirectoryId');
|
|
42
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'ACCREDITED', fiadoDirectoryId: 'dir-1' })).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
it.each(['FAILED', 'UNVERIFIED'])('%s exige el motivo', (status) => {
|
|
46
|
+
expect(errorsOf({ ...PENDING_SALE, status })).toContain('failReason');
|
|
47
|
+
expect(errorsOf({ ...PENDING_SALE, status, failReason: 'VOID_EXPIRED' })).toEqual([]);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it('rechaza WELCOME: la bienvenida tiene su propio endpoint', () => {
|
|
51
|
+
expect(errorsOf({ ...PENDING_SALE, bonusType: 'WELCOME' })).toContain('bonusType');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it.each(['promotionId', 'sourceId', 'storeId', 'currency', 'idempotencyKey'])('rechaza %s vacío', (field) => {
|
|
55
|
+
expect(errorsOf({ ...PENDING_SALE, [field]: ' ' })).toContain(field);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it.each([0, -1, 1.5])('rechaza el monto %p', (amountCents) => {
|
|
59
|
+
expect(errorsOf({ ...PENDING_SALE, amountCents })).toContain('amountCents');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('rechaza un estado fuera del libro', () => {
|
|
63
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'SETTLED' })).toContain('status');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('rechaza un motivo de más de 256 caracteres', () => {
|
|
67
|
+
expect(errorsOf({ ...PENDING_SALE, status: 'FAILED', failReason: 'x'.repeat(257) })).toContain('failReason');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('sale por el index raíz', () => {
|
|
71
|
+
expect(new RetailCustomer.UpsertPromotionBonusRequest()).toBeInstanceOf(UpsertPromotionBonusRequest);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
describe('PromotionBonusEntryResponse', () => {
|
|
76
|
+
it('describe el asiento de promo con su fuente', () => {
|
|
77
|
+
const entry: PromotionBonusEntryResponse = {
|
|
78
|
+
bonusId: 'B-1',
|
|
79
|
+
bonusType: BonusTypeEnum.CREDIT_PROMOTION,
|
|
80
|
+
promotionId: 'promo-1',
|
|
81
|
+
sourceId: 'credit-1',
|
|
82
|
+
retailCustomerId: 'rc-1',
|
|
83
|
+
fiadoDirectoryId: null,
|
|
84
|
+
saleId: null,
|
|
85
|
+
creditId: 'credit-1',
|
|
86
|
+
storeId: 'st-1',
|
|
87
|
+
amountCents: 5_000,
|
|
88
|
+
currency: 'MXN',
|
|
89
|
+
status: RetailCustomer.WelcomeBonusStatusEnum.PENDING,
|
|
90
|
+
failReason: null,
|
|
91
|
+
processorTransactionId: null,
|
|
92
|
+
idempotencyKey: 'credit-1',
|
|
93
|
+
fundedBy: 'VENTASLUGA',
|
|
94
|
+
settlementStatus: RetailCustomer.WelcomeBonusSettlementStatusEnum.PENDING,
|
|
95
|
+
resolvedAt: null,
|
|
96
|
+
createdAt: '2026-10-02T00:00:00.000Z',
|
|
97
|
+
updatedAt: '2026-10-02T00:00:00.000Z',
|
|
98
|
+
};
|
|
99
|
+
expect(entry.sourceId).toBe('credit-1');
|
|
100
|
+
});
|
|
101
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { LoanOfferings } from '../../../src/index';
|
|
3
|
+
import type { QuoteResponse, WizardCreditPromotionBonus, WizardSession } from '../../../src/retailWizard/index';
|
|
4
|
+
|
|
5
|
+
describe('bono de crédito en el contrato del wizard', () => {
|
|
6
|
+
it('la sesión lleva el desenlace persistido del bono de crédito', () => {
|
|
7
|
+
const bonus: WizardCreditPromotionBonus = {
|
|
8
|
+
promotionId: 'promo-1',
|
|
9
|
+
amountCents: 5_000,
|
|
10
|
+
status: LoanOfferings.PromotionBonusStatusEnum.RESERVED,
|
|
11
|
+
};
|
|
12
|
+
const session: Pick<WizardSession, 'creditPromotionBonus'> = { creditPromotionBonus: bonus };
|
|
13
|
+
expect(session.creditPromotionBonus?.status).toBe('RESERVED');
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
it('la sesión sin bono resuelto lo omite o lo manda null', () => {
|
|
17
|
+
const absent: Pick<WizardSession, 'creditPromotionBonus'> = {};
|
|
18
|
+
const nulled: Pick<WizardSession, 'creditPromotionBonus'> = { creditPromotionBonus: null };
|
|
19
|
+
expect([absent.creditPromotionBonus, nulled.creditPromotionBonus]).toEqual([undefined, null]);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
it('la cotización puede traer el monto del bono que se cotizó', () => {
|
|
23
|
+
const quote: Pick<QuoteResponse, 'creditPromotionBonusCents'> = { creditPromotionBonusCents: 5_000 };
|
|
24
|
+
expect(quote.creditPromotionBonusCents).toBe(5_000);
|
|
25
|
+
});
|
|
26
|
+
});
|
|
@@ -27,6 +27,8 @@ export interface AppliedPromotionResponse {
|
|
|
27
27
|
version: number;
|
|
28
28
|
/** Palancas del plan que la promoción pisó. */
|
|
29
29
|
appliedEffects: PromotionEffectKindEnum[];
|
|
30
|
+
/** Monto del bono PCF; solo en la promoción que gana el bono de la cotización. */
|
|
31
|
+
pcfBonusCents?: number;
|
|
30
32
|
}
|
|
31
33
|
/**
|
|
32
34
|
* Una `CreditPlan` que el paso Base consideró para esta venta, haya ganado algún término o no.
|
|
@@ -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);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { PromotionBonusStatusEnum } from '../../loanOfferings/enums/PromotionBonusStatusEnum';
|
|
2
|
+
/** Desenlace del bono de la promo de crédito, persistido en la sesión del wizard. */
|
|
3
|
+
export interface WizardCreditPromotionBonus {
|
|
4
|
+
promotionId: string;
|
|
5
|
+
amountCents: number;
|
|
6
|
+
/** `RESERVED` = en proceso; los otros tres son terminales. */
|
|
7
|
+
status: PromotionBonusStatusEnum;
|
|
8
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { WizardProduct } from './WizardProduct';
|
|
|
13
13
|
import type { WizardReservedLine } from './WizardReservedLine';
|
|
14
14
|
import type { WizardResumeTarget } from './WizardResumeTarget';
|
|
15
15
|
import type { WizardWelcome } from './WizardWelcome';
|
|
16
|
+
import type { WizardCreditPromotionBonus } from './WizardCreditPromotionBonus';
|
|
16
17
|
/** Modalidad de captura del KYC (M1 §213-329). */
|
|
17
18
|
export type KycModality = 'CUSTOMER_LINK' | 'STORE_DEVICE' | 'MANUAL';
|
|
18
19
|
/** Estado de la apertura de cuenta async (D35) — espejo de `RetailCustomer.accountStatus`. */
|
|
@@ -129,6 +130,8 @@ export interface WizardSession {
|
|
|
129
130
|
folio: string | null;
|
|
130
131
|
payment: WizardPayment | null;
|
|
131
132
|
welcome: WizardWelcome | null;
|
|
133
|
+
/** Bono de la promo de crédito; ausente o `null` mientras no se haya reservado. */
|
|
134
|
+
creditPromotionBonus?: WizardCreditPromotionBonus | null;
|
|
132
135
|
/** Contexto de crédito (pista SOFOM, pasos 04-09): PUNTEROS al dominio loan-*. Null en contado/externa. */
|
|
133
136
|
loan: WizardLoan | null;
|
|
134
137
|
saleId: string | null;
|
|
@@ -28,6 +28,7 @@ export * from './dtos/WizardConsents';
|
|
|
28
28
|
export * from './dtos/WizardPayment';
|
|
29
29
|
export * from './dtos/WizardProduct';
|
|
30
30
|
export * from './dtos/WizardWelcome';
|
|
31
|
+
export * from './dtos/WizardCreditPromotionBonus';
|
|
31
32
|
export * from './dtos/SalePaymentDetail';
|
|
32
33
|
export * from './dtos/ProspectNewCustomer';
|
|
33
34
|
export * from './dtos/WizardBeneficiary';
|
|
@@ -47,6 +47,7 @@ __exportStar(require("./dtos/WizardConsents"), exports);
|
|
|
47
47
|
__exportStar(require("./dtos/WizardPayment"), exports);
|
|
48
48
|
__exportStar(require("./dtos/WizardProduct"), exports);
|
|
49
49
|
__exportStar(require("./dtos/WizardWelcome"), exports);
|
|
50
|
+
__exportStar(require("./dtos/WizardCreditPromotionBonus"), exports);
|
|
50
51
|
__exportStar(require("./dtos/SalePaymentDetail"), exports);
|
|
51
52
|
__exportStar(require("./dtos/ProspectNewCustomer"), exports);
|
|
52
53
|
__exportStar(require("./dtos/WizardBeneficiary"), exports);
|
package/package.json
CHANGED
|
@@ -29,6 +29,8 @@ export interface AppliedPromotionResponse {
|
|
|
29
29
|
version: number;
|
|
30
30
|
/** Palancas del plan que la promoción pisó. */
|
|
31
31
|
appliedEffects: PromotionEffectKindEnum[];
|
|
32
|
+
/** Monto del bono PCF; solo en la promoción que gana el bono de la cotización. */
|
|
33
|
+
pcfBonusCents?: number;
|
|
32
34
|
}
|
|
33
35
|
|
|
34
36
|
/**
|
|
@@ -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';
|
|
@@ -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';
|