@fiado/type-kit 3.638.0 → 3.640.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/transferNote/transferNoteConfigAndPrivate.test.ts +53 -0
- package/_test_/unit/transferNote/transferNoteEventsAndFeeds.test.ts +32 -0
- package/_test_/unit/transferNote/transferNoteRequests.test.ts +20 -1
- package/bin/retailCommission/dtos/CommissionAccrual.d.ts +2 -0
- package/bin/retailWizard/dtos/Sale.d.ts +4 -0
- package/bin/retailWizard/events/RetailCommissionInboundMessage.d.ts +5 -0
- package/bin/retailWizard/events/RetailCommissionInboundMessage.js +5 -0
- package/bin/transferNote/constants/transferNoteErrorHttpStatus.js +4 -0
- package/bin/transferNote/dtos/TransferNote.d.ts +10 -0
- package/bin/transferNote/dtos/TransferNoteConfigRecords.d.ts +14 -0
- package/bin/transferNote/dtos/requests/TransferNoteActionRequests.d.ts +3 -1
- package/bin/transferNote/dtos/requests/TransferNoteActionRequests.js +8 -2
- package/bin/transferNote/dtos/requests/TransferNoteConfigRequests.d.ts +11 -1
- package/bin/transferNote/dtos/requests/TransferNoteConfigRequests.js +20 -2
- package/bin/transferNote/dtos/responses/TransferNoteConfigResponses.d.ts +1 -0
- package/bin/transferNote/enums/TransferNoteConfigChangeDecisionEnum.d.ts +5 -0
- package/bin/transferNote/enums/TransferNoteConfigChangeDecisionEnum.js +9 -0
- package/bin/transferNote/enums/TransferNoteErrorCodeEnum.d.ts +9 -1
- package/bin/transferNote/enums/TransferNoteErrorCodeEnum.js +8 -0
- package/bin/transferNote/events/TransferNoteLifecycleEventDetails.d.ts +38 -0
- package/bin/transferNote/events/TransferNoteLifecycleEventDetails.js +28 -0
- package/bin/transferNote/events/TransferNoteReconciledEventDetail.d.ts +6 -1
- package/bin/transferNote/events/TransferNoteReconciledEventDetail.js +5 -0
- package/bin/transferNote/index.d.ts +3 -0
- package/bin/transferNote/index.js +3 -0
- package/bin/transferNote/validators/IsTransferNoteClabeConstraint.d.ts +8 -0
- package/bin/transferNote/validators/IsTransferNoteClabeConstraint.js +34 -0
- package/package.json +1 -1
- package/src/retailCommission/dtos/CommissionAccrual.ts +2 -0
- package/src/retailWizard/dtos/Sale.ts +4 -0
- package/src/retailWizard/events/RetailCommissionInboundMessage.ts +8 -0
- package/src/transferNote/constants/transferNoteErrorHttpStatus.ts +4 -0
- package/src/transferNote/dtos/TransferNote.ts +11 -0
- package/src/transferNote/dtos/TransferNoteConfigRecords.ts +15 -0
- package/src/transferNote/dtos/requests/TransferNoteActionRequests.ts +8 -2
- package/src/transferNote/dtos/requests/TransferNoteConfigRequests.ts +17 -1
- package/src/transferNote/dtos/responses/TransferNoteConfigResponses.ts +2 -0
- package/src/transferNote/enums/TransferNoteConfigChangeDecisionEnum.ts +5 -0
- package/src/transferNote/enums/TransferNoteErrorCodeEnum.ts +8 -0
- package/src/transferNote/events/TransferNoteLifecycleEventDetails.ts +51 -0
- package/src/transferNote/events/TransferNoteReconciledEventDetail.ts +5 -0
- package/src/transferNote/index.ts +3 -0
- package/src/transferNote/validators/IsTransferNoteClabeConstraint.ts +24 -0
|
@@ -113,8 +113,12 @@ describe('espejo privado', () => {
|
|
|
113
113
|
trackingKey: 'K1',
|
|
114
114
|
paidAt: '2026-10-29T15:00:00.000Z',
|
|
115
115
|
amountCents: 100,
|
|
116
|
+
paidToClabe: '002010077777777771',
|
|
116
117
|
}),
|
|
117
118
|
).toEqual([]);
|
|
119
|
+
expect(
|
|
120
|
+
errorsOf(PrivateCaptureTransferNoteSpeiRequest, { ...caller, trackingKey: 'K1', paidAt: '2026-10-29T15:00:00.000Z', amountCents: 100 }),
|
|
121
|
+
).toContain('paidToClabe');
|
|
118
122
|
});
|
|
119
123
|
|
|
120
124
|
it('las lecturas privadas también exigen author y actorId', () => {
|
|
@@ -215,3 +219,52 @@ describe('catálogos', () => {
|
|
|
215
219
|
expect(TRANSFER_NOTE_ERROR_HTTP_STATUS[TransferNoteErrorCodeEnum.TRANSFER_NOTE_ITEM_NOT_FOUND]).toBe(404);
|
|
216
220
|
});
|
|
217
221
|
});
|
|
222
|
+
|
|
223
|
+
describe('cambio de configuración con doble control', () => {
|
|
224
|
+
it('la decisión acepta APPROVE o REJECT y nada más', () => {
|
|
225
|
+
const { DecideTransferNoteConfigChangeRequest, TransferNoteConfigChangeDecisionEnum } = TransferNote;
|
|
226
|
+
const proposedAt = '2026-11-04T18:00:00.000Z';
|
|
227
|
+
expect(errorsOf(DecideTransferNoteConfigChangeRequest, { decision: TransferNoteConfigChangeDecisionEnum.APPROVE, proposedAt })).toEqual([]);
|
|
228
|
+
expect(errorsOf(DecideTransferNoteConfigChangeRequest, { decision: 'MAYBE', proposedAt })).toContain('decision');
|
|
229
|
+
expect(errorsOf(DecideTransferNoteConfigChangeRequest, { decision: TransferNoteConfigChangeDecisionEnum.APPROVE })).toContain('proposedAt');
|
|
230
|
+
expect(errorsOf(DecideTransferNoteConfigChangeRequest, { decision: TransferNoteConfigChangeDecisionEnum.REJECT, proposedAt: 'ayer' })).toContain('proposedAt');
|
|
231
|
+
});
|
|
232
|
+
|
|
233
|
+
it('los codes nuevos tienen su status', () => {
|
|
234
|
+
expect(TRANSFER_NOTE_ERROR_HTTP_STATUS[TransferNoteErrorCodeEnum.SAME_ACTOR_NOT_ALLOWED]).toBe(422);
|
|
235
|
+
expect(TRANSFER_NOTE_ERROR_HTTP_STATUS[TransferNoteErrorCodeEnum.CONFIG_CHANGE_NOT_PENDING]).toBe(409);
|
|
236
|
+
expect(TRANSFER_NOTE_ERROR_HTTP_STATUS[TransferNoteErrorCodeEnum.NOTE_DOCUMENT_NOT_READY]).toBe(409);
|
|
237
|
+
expect(TRANSFER_NOTE_ERROR_HTTP_STATUS[TransferNoteErrorCodeEnum.PARTY_ROUTING_CONFLICT]).toBe(409);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('el registro expone el cambio propuesto aparte del vigente', () => {
|
|
241
|
+
const record: TransferNote.TransferNoteConfigRecord = {
|
|
242
|
+
recordKey: 'PARTY#luga',
|
|
243
|
+
recordType: TransferNoteConfigRecordTypeEnum.PARTY,
|
|
244
|
+
party: { partyId: 'luga', partyType: TransferNotePartyTypeEnum.RETAILER, displayName: 'Luga', retailerId: 'r-1', clabe: '002010077777777771' },
|
|
245
|
+
pendingChange: {
|
|
246
|
+
party: { partyId: 'luga', partyType: TransferNotePartyTypeEnum.RETAILER, displayName: 'Luga', retailerId: 'r-1', clabe: '012180001234567897' },
|
|
247
|
+
proposedBy: 'ana@sk.mx',
|
|
248
|
+
proposedAt: '2026-11-04T10:00:00.000Z',
|
|
249
|
+
},
|
|
250
|
+
updatedAt: '2026-11-04T10:00:00.000Z',
|
|
251
|
+
updatedBy: 'ana@sk.mx',
|
|
252
|
+
};
|
|
253
|
+
expect(record.party?.clabe).toBe('002010077777777771');
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
it('un cambio de pagador, receptor o signo de un concepto también queda propuesto', () => {
|
|
257
|
+
const pendingChange: TransferNote.TransferNoteConfigPendingChange = {
|
|
258
|
+
concept: {
|
|
259
|
+
concept: TransferNoteConceptEnum.DEVICE_PAYMENT,
|
|
260
|
+
payerPartyType: TransferNotePartyTypeEnum.SOFOM,
|
|
261
|
+
payeePartyType: TransferNotePartyTypeEnum.RETAILER,
|
|
262
|
+
sign: TransferNoteConceptSignEnum.NEGATIVE,
|
|
263
|
+
activeFrom: '2026-11-04T10:00:00.000Z',
|
|
264
|
+
},
|
|
265
|
+
proposedBy: 'ana@sk.mx',
|
|
266
|
+
proposedAt: '2026-11-04T10:00:00.000Z',
|
|
267
|
+
};
|
|
268
|
+
expect(pendingChange.concept?.sign).toBe(TransferNoteConceptSignEnum.NEGATIVE);
|
|
269
|
+
});
|
|
270
|
+
});
|
|
@@ -36,6 +36,38 @@ describe('eventos de transferNote', () => {
|
|
|
36
36
|
});
|
|
37
37
|
});
|
|
38
38
|
|
|
39
|
+
describe('los 7 eventos de la nota', () => {
|
|
40
|
+
it('cada detailType vive bajo transferNote.*', () => {
|
|
41
|
+
expect(Object.values(TransferNote.TransferNoteEventDetailTypeEnum).sort()).toEqual([
|
|
42
|
+
'transferNote.TransferNoteCancelled',
|
|
43
|
+
'transferNote.TransferNoteDisputeResolved',
|
|
44
|
+
'transferNote.TransferNoteDisputed',
|
|
45
|
+
'transferNote.TransferNoteIssued',
|
|
46
|
+
'transferNote.TransferNoteReconciled',
|
|
47
|
+
'transferNote.TransferNoteSpeiCaptured',
|
|
48
|
+
'transferNote.TransferNoteSpeiReverted',
|
|
49
|
+
]);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
it('la captura lleva la CLABE pagada y solo ids y montos', () => {
|
|
53
|
+
const detail: TransferNote.TransferNoteSpeiCapturedEventDetail = {
|
|
54
|
+
eventId: 'n-1#SPEI_CAPTURED#1762250400000',
|
|
55
|
+
tenantId: 'surekeep',
|
|
56
|
+
noteId: 'n-1',
|
|
57
|
+
pairKey: 'fsa#sk#-',
|
|
58
|
+
isoWeek: '2026-W44',
|
|
59
|
+
netAmountCents: 125000,
|
|
60
|
+
currency: CurrencyId.MXN,
|
|
61
|
+
trackingKey: 'BNET01002610290000001',
|
|
62
|
+
paidAt: '2026-11-04T10:00:00.000Z',
|
|
63
|
+
paidAmountCents: 125000,
|
|
64
|
+
paidToClabe: '002010077777777771',
|
|
65
|
+
capturedAt: '2026-11-04T10:00:00.000Z',
|
|
66
|
+
};
|
|
67
|
+
expect(detail.paidToClabe).toHaveLength(18);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
|
|
39
71
|
describe('registros de feed de notas con la forma base', () => {
|
|
40
72
|
it('un item de nota se lee con el cliente genérico y su reversa va en refs', () => {
|
|
41
73
|
const reversal: TransferNote.TransferNoteItemFeedRecord = {
|
|
@@ -72,7 +72,7 @@ describe('PreviewTransferNoteRequest', () => {
|
|
|
72
72
|
});
|
|
73
73
|
|
|
74
74
|
describe('captura y reversa de SPEI', () => {
|
|
75
|
-
const spei = { trackingKey: 'BNET01002610290000001', paidAt: '2026-10-29T15:00:00.000Z', amountCents: 125000 };
|
|
75
|
+
const spei = { trackingKey: 'BNET01002610290000001', paidAt: '2026-10-29T15:00:00.000Z', amountCents: 125000, paidToClabe: '002010077777777771' };
|
|
76
76
|
|
|
77
77
|
it('acepta clave, fecha ISO y monto entero positivo', () => {
|
|
78
78
|
expect(errorsOf(CaptureTransferNoteSpeiRequest, spei)).toEqual([]);
|
|
@@ -90,6 +90,25 @@ describe('captura y reversa de SPEI', () => {
|
|
|
90
90
|
expect(errorsOf(RevertTransferNoteSpeiRequest, { reason: ' ' })).toContain('reason');
|
|
91
91
|
expect(errorsOf(CancelTransferNoteRequest, {})).toContain('reason');
|
|
92
92
|
});
|
|
93
|
+
|
|
94
|
+
it('la cancelación pide razón de 20+ como emitir u objetar', () => {
|
|
95
|
+
expect(errorsOf(CancelTransferNoteRequest, { reason: 'corta' })).toContain('reason');
|
|
96
|
+
expect(errorsOf(CancelTransferNoteRequest, { reason: longReason })).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('paidToClabe es obligatoria en toda captura', () => {
|
|
100
|
+
const { paidToClabe: _omit, ...withoutClabe } = spei;
|
|
101
|
+
expect(errorsOf(CaptureTransferNoteSpeiRequest, withoutClabe)).toContain('paidToClabe');
|
|
102
|
+
expect(errorsOf(CaptureTransferNoteSpeiRequest, { ...spei, paidToClabe: null })).toContain('paidToClabe');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('paidToClabe exige dígito verificador válido', () => {
|
|
106
|
+
expect(errorsOf(CaptureTransferNoteSpeiRequest, { ...spei, paidToClabe: '002010077777777771' })).toEqual([]);
|
|
107
|
+
expect(errorsOf(CaptureTransferNoteSpeiRequest, { ...spei, paidToClabe: '002010077777777772' })).toContain('paidToClabe');
|
|
108
|
+
expect(errorsOf(CaptureTransferNoteSpeiRequest, { ...spei, paidToClabe: '12345' })).toContain('paidToClabe');
|
|
109
|
+
expect(TransferNote.isValidTransferNoteClabe('002010077777777771')).toBe(true);
|
|
110
|
+
expect(TransferNote.isValidTransferNoteClabe(null)).toBe(false);
|
|
111
|
+
});
|
|
93
112
|
});
|
|
94
113
|
|
|
95
114
|
describe('objeción', () => {
|
|
@@ -85,6 +85,8 @@ export interface CommissionSaleInput {
|
|
|
85
85
|
/** `CASH` | `CREDIT`, para `PERCENT_BY_PAYMENT`. */
|
|
86
86
|
paymentType: string;
|
|
87
87
|
soldAt: string;
|
|
88
|
+
/** Cuándo se ganó la comisión: ubica la venta en su corte. Los devengos viejos no lo traen o lo traen en `null` (usar `soldAt`). */
|
|
89
|
+
earnedAt?: string | null;
|
|
88
90
|
/** Las líneas de la venta. Las ventas que se calculan con reglas siempre las traen. */
|
|
89
91
|
lines?: CommissionSaleLine[];
|
|
90
92
|
}
|
|
@@ -97,6 +97,10 @@ export interface Sale {
|
|
|
97
97
|
cancelledAt: string | null;
|
|
98
98
|
/** Reason OBLIGATORIO en cancelación (AuditTrail) — flujo C. */
|
|
99
99
|
cancelReason: string | null;
|
|
100
|
+
/** La comisión espera a que el cliente active su app: contraseña, contrato y número secreto. Ausente si no espera. */
|
|
101
|
+
commissionAwaitingApp?: boolean;
|
|
102
|
+
/** ISO; cuándo se ganó la comisión, si el cliente activó su app después de cerrar. Ausente: al cerrar o en espera. */
|
|
103
|
+
commissionEarnedAt?: string;
|
|
100
104
|
/** Si la venta se puede cancelar hoy y, si no, por qué; solo lo trae el detalle del backoffice. */
|
|
101
105
|
cancellation?: SaleCancellation;
|
|
102
106
|
/**
|
|
@@ -46,6 +46,11 @@ export declare class RetailCommissionInboundMessage {
|
|
|
46
46
|
soldAt: string;
|
|
47
47
|
/** soldAt en la venta; cancelledAt en la cancelación. */
|
|
48
48
|
occurredAt: string;
|
|
49
|
+
/**
|
|
50
|
+
* Cuándo se ganó la comisión, si el cliente activó su app después de cerrar la venta. Ubica la venta en
|
|
51
|
+
* su corte. Ausente: se ganó al cerrar (`soldAt`).
|
|
52
|
+
*/
|
|
53
|
+
earnedAt?: string;
|
|
49
54
|
/** Motivo de la cancelación; puede venir vacío. */
|
|
50
55
|
cancelReason?: string;
|
|
51
56
|
lines: RetailCommissionInboundLine[];
|
|
@@ -153,6 +153,11 @@ __decorate([
|
|
|
153
153
|
(0, class_validator_1.IsISO8601)(),
|
|
154
154
|
__metadata("design:type", String)
|
|
155
155
|
], RetailCommissionInboundMessage.prototype, "occurredAt", void 0);
|
|
156
|
+
__decorate([
|
|
157
|
+
(0, class_validator_1.IsOptional)(),
|
|
158
|
+
(0, class_validator_1.IsISO8601)(),
|
|
159
|
+
__metadata("design:type", String)
|
|
160
|
+
], RetailCommissionInboundMessage.prototype, "earnedAt", void 0);
|
|
156
161
|
__decorate([
|
|
157
162
|
(0, class_validator_1.IsOptional)(),
|
|
158
163
|
(0, class_validator_1.IsString)(),
|
|
@@ -34,4 +34,8 @@ exports.TRANSFER_NOTE_ERROR_HTTP_STATUS = {
|
|
|
34
34
|
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.CUTOFF_DISPATCH_FAILED]: 502,
|
|
35
35
|
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.CONCURRENT_WRITE_CONFLICT]: 409,
|
|
36
36
|
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.AUDIT_QUEUE_PUBLISH_FAILED]: 502,
|
|
37
|
+
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.SAME_ACTOR_NOT_ALLOWED]: 422,
|
|
38
|
+
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.CONFIG_CHANGE_NOT_PENDING]: 409,
|
|
39
|
+
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.NOTE_DOCUMENT_NOT_READY]: 409,
|
|
40
|
+
[TransferNoteErrorCodeEnum_1.TransferNoteErrorCodeEnum.PARTY_ROUTING_CONFLICT]: 409,
|
|
37
41
|
};
|
|
@@ -25,6 +25,8 @@ export interface TransferNoteSpeiPayment {
|
|
|
25
25
|
capturedBy: string;
|
|
26
26
|
capturedAt: string;
|
|
27
27
|
receiptUploaded: boolean;
|
|
28
|
+
/** CLABE a la que se mandó el SPEI, si se capturó. */
|
|
29
|
+
paidToClabe?: string;
|
|
28
30
|
}
|
|
29
31
|
export interface TransferNoteConfirmation {
|
|
30
32
|
type: TransferNoteConfirmationTypeEnum;
|
|
@@ -40,6 +42,12 @@ export interface TransferNoteDispute {
|
|
|
40
42
|
/** Estado al que vuelve la nota si se rechaza la objeción. */
|
|
41
43
|
previousStatus: TransferNoteStatusEnum;
|
|
42
44
|
}
|
|
45
|
+
/** Cancelación de la nota: quién, cuándo y por qué. */
|
|
46
|
+
export interface TransferNoteCancellation {
|
|
47
|
+
reason: string;
|
|
48
|
+
cancelledBy: string;
|
|
49
|
+
cancelledAt: string;
|
|
50
|
+
}
|
|
43
51
|
/** Fila de los listados de notas. */
|
|
44
52
|
export interface TransferNoteSummary {
|
|
45
53
|
noteId: string;
|
|
@@ -67,6 +75,8 @@ export interface TransferNote extends TransferNoteSummary {
|
|
|
67
75
|
issueReason?: string;
|
|
68
76
|
speiPayment?: TransferNoteSpeiPayment;
|
|
69
77
|
dispute?: TransferNoteDispute;
|
|
78
|
+
/** Solo en notas canceladas después de emitirse. */
|
|
79
|
+
cancellation?: TransferNoteCancellation;
|
|
70
80
|
/** Nota cancelada de la que nace esta, o la regenerada que la reemplaza. */
|
|
71
81
|
regeneratedFromNoteId?: string;
|
|
72
82
|
regeneratedNoteId?: string;
|
|
@@ -37,6 +37,18 @@ export interface TransferNotePairSettings {
|
|
|
37
37
|
/** Se llena al 4.º corte seguido sin emitir. */
|
|
38
38
|
discrepancyOpenedAt?: string;
|
|
39
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Cambio que altera a quién se le paga y espera la aprobación de otra persona: CLABE, tipo o tienda de una parte,
|
|
42
|
+
* pagador/receptor de un par, o tipos de pagador/receptor o signo de un concepto.
|
|
43
|
+
* Mientras no se apruebe rige el valor vigente del registro.
|
|
44
|
+
*/
|
|
45
|
+
export interface TransferNoteConfigPendingChange {
|
|
46
|
+
party?: TransferNotePartySettings;
|
|
47
|
+
concept?: TransferNoteConceptSettings;
|
|
48
|
+
pair?: TransferNotePairSettings;
|
|
49
|
+
proposedBy: string;
|
|
50
|
+
proposedAt: string;
|
|
51
|
+
}
|
|
40
52
|
/** Un registro de configuración; trae solo el sub-shape de su `recordType`. */
|
|
41
53
|
export interface TransferNoteConfigRecord {
|
|
42
54
|
recordKey: string;
|
|
@@ -45,6 +57,8 @@ export interface TransferNoteConfigRecord {
|
|
|
45
57
|
party?: TransferNotePartySettings;
|
|
46
58
|
concept?: TransferNoteConceptSettings;
|
|
47
59
|
pair?: TransferNotePairSettings;
|
|
60
|
+
/** Cambio propuesto que todavía no rige. */
|
|
61
|
+
pendingChange?: TransferNoteConfigPendingChange;
|
|
48
62
|
updatedAt: string;
|
|
49
63
|
updatedBy: string;
|
|
50
64
|
}
|
|
@@ -17,12 +17,14 @@ export declare class CaptureTransferNoteSpeiRequest {
|
|
|
17
17
|
trackingKey: string;
|
|
18
18
|
paidAt: string;
|
|
19
19
|
amountCents: number;
|
|
20
|
+
/** CLABE a la que se mandó el SPEI, obligatoria; viaja en el feed de liquidación para compararla con la registrada del receptor. */
|
|
21
|
+
paidToClabe: string;
|
|
20
22
|
}
|
|
21
23
|
/** Body de publicRevertTransferNoteSpei. */
|
|
22
24
|
export declare class RevertTransferNoteSpeiRequest {
|
|
23
25
|
reason: string;
|
|
24
26
|
}
|
|
25
|
-
/** Body de publicCancelTransferNote. */
|
|
27
|
+
/** Body de publicCancelTransferNote; la cancelación es irreversible y exige la misma razón larga que emitir u objetar. */
|
|
26
28
|
export declare class CancelTransferNoteRequest {
|
|
27
29
|
reason: string;
|
|
28
30
|
}
|
|
@@ -13,6 +13,7 @@ exports.ResolveTransferNoteDisputeRequest = exports.CancelTransferNoteRequest =
|
|
|
13
13
|
const class_transformer_1 = require("class-transformer");
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
15
|
const TransferNoteDisputeDecisionEnum_1 = require("../../enums/TransferNoteDisputeDecisionEnum");
|
|
16
|
+
const IsTransferNoteClabeConstraint_1 = require("../../validators/IsTransferNoteClabeConstraint");
|
|
16
17
|
const IsTransferNoteSelectionConstraint_1 = require("../../validators/IsTransferNoteSelectionConstraint");
|
|
17
18
|
const transferNoteRequestDecorators_1 = require("./transferNoteRequestDecorators");
|
|
18
19
|
/** Body de publicPreviewTransferNote: items de un par por selección o por semana. */
|
|
@@ -73,6 +74,11 @@ __decorate([
|
|
|
73
74
|
(0, class_validator_1.IsPositive)(),
|
|
74
75
|
__metadata("design:type", Number)
|
|
75
76
|
], CaptureTransferNoteSpeiRequest.prototype, "amountCents", void 0);
|
|
77
|
+
__decorate([
|
|
78
|
+
(0, class_transformer_1.Expose)(),
|
|
79
|
+
(0, class_validator_1.Validate)(IsTransferNoteClabeConstraint_1.IsTransferNoteClabeConstraint),
|
|
80
|
+
__metadata("design:type", String)
|
|
81
|
+
], CaptureTransferNoteSpeiRequest.prototype, "paidToClabe", void 0);
|
|
76
82
|
/** Body de publicRevertTransferNoteSpei. */
|
|
77
83
|
class RevertTransferNoteSpeiRequest {
|
|
78
84
|
}
|
|
@@ -81,12 +87,12 @@ __decorate([
|
|
|
81
87
|
(0, transferNoteRequestDecorators_1.RequiredReason)(),
|
|
82
88
|
__metadata("design:type", String)
|
|
83
89
|
], RevertTransferNoteSpeiRequest.prototype, "reason", void 0);
|
|
84
|
-
/** Body de publicCancelTransferNote. */
|
|
90
|
+
/** Body de publicCancelTransferNote; la cancelación es irreversible y exige la misma razón larga que emitir u objetar. */
|
|
85
91
|
class CancelTransferNoteRequest {
|
|
86
92
|
}
|
|
87
93
|
exports.CancelTransferNoteRequest = CancelTransferNoteRequest;
|
|
88
94
|
__decorate([
|
|
89
|
-
(0, transferNoteRequestDecorators_1.
|
|
95
|
+
(0, transferNoteRequestDecorators_1.LongReason)(),
|
|
90
96
|
__metadata("design:type", String)
|
|
91
97
|
], CancelTransferNoteRequest.prototype, "reason", void 0);
|
|
92
98
|
/** Body de publicResolveTransferNoteDispute. */
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CurrencyId } from '../../../currency/enums/CurrencyId';
|
|
2
2
|
import { TransferNoteConceptSignEnum } from '../../enums/TransferNoteConceptSignEnum';
|
|
3
|
+
import { TransferNoteConfigChangeDecisionEnum } from '../../enums/TransferNoteConfigChangeDecisionEnum';
|
|
3
4
|
import { TransferNoteConfigRecordTypeEnum as RecordType } from '../../enums/TransferNoteConfigRecordTypeEnum';
|
|
4
5
|
import { TransferNotePartyTypeEnum } from '../../enums/TransferNotePartyTypeEnum';
|
|
5
6
|
export declare class TransferNoteTenantSettingsInput {
|
|
@@ -28,7 +29,10 @@ export declare class TransferNotePairSettingsInput {
|
|
|
28
29
|
payeePartyId: string;
|
|
29
30
|
activeFrom: string;
|
|
30
31
|
}
|
|
31
|
-
/**
|
|
32
|
+
/**
|
|
33
|
+
* Body de publicUpdateTransferNoteConfig; da de alta el registro si no existe.
|
|
34
|
+
* Un cambio de CLABE o de pagador/receptor de un par queda propuesto hasta que otra persona lo apruebe.
|
|
35
|
+
*/
|
|
32
36
|
export declare class UpdateTransferNoteConfigRequest {
|
|
33
37
|
recordType: RecordType;
|
|
34
38
|
tenant?: TransferNoteTenantSettingsInput;
|
|
@@ -36,3 +40,9 @@ export declare class UpdateTransferNoteConfigRequest {
|
|
|
36
40
|
concept?: TransferNoteConceptSettingsInput;
|
|
37
41
|
pair?: TransferNotePairSettingsInput;
|
|
38
42
|
}
|
|
43
|
+
/** Body de publicDecideTransferNoteConfigChange: aprueba o descarta el cambio propuesto; quien lo propuso no puede aprobarlo. */
|
|
44
|
+
export declare class DecideTransferNoteConfigChangeRequest {
|
|
45
|
+
decision: TransferNoteConfigChangeDecisionEnum;
|
|
46
|
+
/** `pendingChange.proposedAt` que vio quien decide; si la propuesta se reemplazó después, la decisión se rechaza. */
|
|
47
|
+
proposedAt: string;
|
|
48
|
+
}
|
|
@@ -9,12 +9,13 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.UpdateTransferNoteConfigRequest = exports.TransferNotePairSettingsInput = exports.TransferNoteConceptSettingsInput = exports.TransferNotePartySettingsInput = exports.TransferNoteTenantSettingsInput = void 0;
|
|
12
|
+
exports.DecideTransferNoteConfigChangeRequest = exports.UpdateTransferNoteConfigRequest = exports.TransferNotePairSettingsInput = exports.TransferNoteConceptSettingsInput = exports.TransferNotePartySettingsInput = exports.TransferNoteTenantSettingsInput = void 0;
|
|
13
13
|
const class_transformer_1 = require("class-transformer");
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
15
|
const CurrencyId_1 = require("../../../currency/enums/CurrencyId");
|
|
16
16
|
const transferNoteValidation_1 = require("../../constants/transferNoteValidation");
|
|
17
17
|
const TransferNoteConceptSignEnum_1 = require("../../enums/TransferNoteConceptSignEnum");
|
|
18
|
+
const TransferNoteConfigChangeDecisionEnum_1 = require("../../enums/TransferNoteConfigChangeDecisionEnum");
|
|
18
19
|
const TransferNoteConfigRecordTypeEnum_1 = require("../../enums/TransferNoteConfigRecordTypeEnum");
|
|
19
20
|
const TransferNotePartyTypeEnum_1 = require("../../enums/TransferNotePartyTypeEnum");
|
|
20
21
|
const transferNoteRequestDecorators_1 = require("./transferNoteRequestDecorators");
|
|
@@ -109,7 +110,10 @@ __decorate([
|
|
|
109
110
|
(0, class_validator_1.IsISO8601)(),
|
|
110
111
|
__metadata("design:type", String)
|
|
111
112
|
], TransferNotePairSettingsInput.prototype, "activeFrom", void 0);
|
|
112
|
-
/**
|
|
113
|
+
/**
|
|
114
|
+
* Body de publicUpdateTransferNoteConfig; da de alta el registro si no existe.
|
|
115
|
+
* Un cambio de CLABE o de pagador/receptor de un par queda propuesto hasta que otra persona lo apruebe.
|
|
116
|
+
*/
|
|
113
117
|
class UpdateTransferNoteConfigRequest {
|
|
114
118
|
}
|
|
115
119
|
exports.UpdateTransferNoteConfigRequest = UpdateTransferNoteConfigRequest;
|
|
@@ -150,3 +154,17 @@ __decorate([
|
|
|
150
154
|
(0, class_transformer_1.Type)(() => TransferNotePairSettingsInput),
|
|
151
155
|
__metadata("design:type", TransferNotePairSettingsInput)
|
|
152
156
|
], UpdateTransferNoteConfigRequest.prototype, "pair", void 0);
|
|
157
|
+
/** Body de publicDecideTransferNoteConfigChange: aprueba o descarta el cambio propuesto; quien lo propuso no puede aprobarlo. */
|
|
158
|
+
class DecideTransferNoteConfigChangeRequest {
|
|
159
|
+
}
|
|
160
|
+
exports.DecideTransferNoteConfigChangeRequest = DecideTransferNoteConfigChangeRequest;
|
|
161
|
+
__decorate([
|
|
162
|
+
(0, class_transformer_1.Expose)(),
|
|
163
|
+
(0, class_validator_1.IsEnum)(TransferNoteConfigChangeDecisionEnum_1.TransferNoteConfigChangeDecisionEnum),
|
|
164
|
+
__metadata("design:type", String)
|
|
165
|
+
], DecideTransferNoteConfigChangeRequest.prototype, "decision", void 0);
|
|
166
|
+
__decorate([
|
|
167
|
+
(0, class_transformer_1.Expose)(),
|
|
168
|
+
(0, class_validator_1.IsISO8601)(),
|
|
169
|
+
__metadata("design:type", String)
|
|
170
|
+
], DecideTransferNoteConfigChangeRequest.prototype, "proposedAt", void 0);
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransferNoteConfigChangeDecisionEnum = void 0;
|
|
4
|
+
/** APPROVE aplica el cambio propuesto de CLABE o de par; REJECT lo descarta y sigue el valor vigente. */
|
|
5
|
+
var TransferNoteConfigChangeDecisionEnum;
|
|
6
|
+
(function (TransferNoteConfigChangeDecisionEnum) {
|
|
7
|
+
TransferNoteConfigChangeDecisionEnum["APPROVE"] = "APPROVE";
|
|
8
|
+
TransferNoteConfigChangeDecisionEnum["REJECT"] = "REJECT";
|
|
9
|
+
})(TransferNoteConfigChangeDecisionEnum || (exports.TransferNoteConfigChangeDecisionEnum = TransferNoteConfigChangeDecisionEnum = {}));
|
|
@@ -38,5 +38,13 @@ export declare enum TransferNoteErrorCodeEnum {
|
|
|
38
38
|
CUTOFF_DISPATCH_FAILED = "CUTOFF_DISPATCH_FAILED",
|
|
39
39
|
/** Otra escritura cambió el registro entre la lectura y el guardado. */
|
|
40
40
|
CONCURRENT_WRITE_CONFLICT = "CONCURRENT_WRITE_CONFLICT",
|
|
41
|
-
AUDIT_QUEUE_PUBLISH_FAILED = "AUDIT_QUEUE_PUBLISH_FAILED"
|
|
41
|
+
AUDIT_QUEUE_PUBLISH_FAILED = "AUDIT_QUEUE_PUBLISH_FAILED",
|
|
42
|
+
/** La misma persona no puede hacer las dos mitades de un control (capturar y confirmar, proponer y aprobar). */
|
|
43
|
+
SAME_ACTOR_NOT_ALLOWED = "SAME_ACTOR_NOT_ALLOWED",
|
|
44
|
+
/** El registro de configuración no tiene un cambio propuesto que decidir. */
|
|
45
|
+
CONFIG_CHANGE_NOT_PENDING = "CONFIG_CHANGE_NOT_PENDING",
|
|
46
|
+
/** El documento de la nota todavía no tiene todos sus items; se reintenta en unos segundos. */
|
|
47
|
+
NOTE_DOCUMENT_NOT_READY = "NOTE_DOCUMENT_NOT_READY",
|
|
48
|
+
/** La parte nueva repite la tienda de otra parte o un tipo que solo puede tener una parte en el tenant. */
|
|
49
|
+
PARTY_ROUTING_CONFLICT = "PARTY_ROUTING_CONFLICT"
|
|
42
50
|
}
|
|
@@ -43,4 +43,12 @@ var TransferNoteErrorCodeEnum;
|
|
|
43
43
|
/** Otra escritura cambió el registro entre la lectura y el guardado. */
|
|
44
44
|
TransferNoteErrorCodeEnum["CONCURRENT_WRITE_CONFLICT"] = "CONCURRENT_WRITE_CONFLICT";
|
|
45
45
|
TransferNoteErrorCodeEnum["AUDIT_QUEUE_PUBLISH_FAILED"] = "AUDIT_QUEUE_PUBLISH_FAILED";
|
|
46
|
+
/** La misma persona no puede hacer las dos mitades de un control (capturar y confirmar, proponer y aprobar). */
|
|
47
|
+
TransferNoteErrorCodeEnum["SAME_ACTOR_NOT_ALLOWED"] = "SAME_ACTOR_NOT_ALLOWED";
|
|
48
|
+
/** El registro de configuración no tiene un cambio propuesto que decidir. */
|
|
49
|
+
TransferNoteErrorCodeEnum["CONFIG_CHANGE_NOT_PENDING"] = "CONFIG_CHANGE_NOT_PENDING";
|
|
50
|
+
/** El documento de la nota todavía no tiene todos sus items; se reintenta en unos segundos. */
|
|
51
|
+
TransferNoteErrorCodeEnum["NOTE_DOCUMENT_NOT_READY"] = "NOTE_DOCUMENT_NOT_READY";
|
|
52
|
+
/** La parte nueva repite la tienda de otra parte o un tipo que solo puede tener una parte en el tenant. */
|
|
53
|
+
TransferNoteErrorCodeEnum["PARTY_ROUTING_CONFLICT"] = "PARTY_ROUTING_CONFLICT";
|
|
46
54
|
})(TransferNoteErrorCodeEnum || (exports.TransferNoteErrorCodeEnum = TransferNoteErrorCodeEnum = {}));
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { CurrencyId } from '../../currency/enums/CurrencyId';
|
|
2
|
+
import { TransferNoteDisputeCategoryEnum } from '../enums/TransferNoteDisputeCategoryEnum';
|
|
3
|
+
import { TransferNoteDisputeDecisionEnum } from '../enums/TransferNoteDisputeDecisionEnum';
|
|
4
|
+
export declare class TransferNoteLifecycleEventBase {
|
|
5
|
+
/** Fijo por ocurrencia (`{noteId}#{evento}#{instante}`): un reenvío llega con el mismo id. */
|
|
6
|
+
eventId: string;
|
|
7
|
+
tenantId: string;
|
|
8
|
+
noteId: string;
|
|
9
|
+
pairKey: string;
|
|
10
|
+
isoWeek: string;
|
|
11
|
+
netAmountCents: number;
|
|
12
|
+
currency: CurrencyId;
|
|
13
|
+
}
|
|
14
|
+
export declare class TransferNoteSpeiCapturedEventDetail extends TransferNoteLifecycleEventBase {
|
|
15
|
+
trackingKey: string;
|
|
16
|
+
/** ISO 8601 del pago declarado. */
|
|
17
|
+
paidAt: string;
|
|
18
|
+
paidAmountCents: number;
|
|
19
|
+
/** CLABE a la que se mandó el SPEI, si se capturó. */
|
|
20
|
+
paidToClabe?: string;
|
|
21
|
+
capturedAt: string;
|
|
22
|
+
}
|
|
23
|
+
export declare class TransferNoteSpeiRevertedEventDetail extends TransferNoteLifecycleEventBase {
|
|
24
|
+
/** Clave de rastreo del SPEI anulado. */
|
|
25
|
+
trackingKey: string;
|
|
26
|
+
revertedAt: string;
|
|
27
|
+
}
|
|
28
|
+
export declare class TransferNoteCancelledEventDetail extends TransferNoteLifecycleEventBase {
|
|
29
|
+
cancelledAt: string;
|
|
30
|
+
}
|
|
31
|
+
export declare class TransferNoteDisputedEventDetail extends TransferNoteLifecycleEventBase {
|
|
32
|
+
category: TransferNoteDisputeCategoryEnum;
|
|
33
|
+
disputedAt: string;
|
|
34
|
+
}
|
|
35
|
+
export declare class TransferNoteDisputeResolvedEventDetail extends TransferNoteLifecycleEventBase {
|
|
36
|
+
decision: TransferNoteDisputeDecisionEnum;
|
|
37
|
+
resolvedAt: string;
|
|
38
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TransferNoteDisputeResolvedEventDetail = exports.TransferNoteDisputedEventDetail = exports.TransferNoteCancelledEventDetail = exports.TransferNoteSpeiRevertedEventDetail = exports.TransferNoteSpeiCapturedEventDetail = exports.TransferNoteLifecycleEventBase = void 0;
|
|
4
|
+
// Campos que comparten los eventos del ciclo de vida de la nota; solo ids y montos, sin razones en texto libre.
|
|
5
|
+
class TransferNoteLifecycleEventBase {
|
|
6
|
+
}
|
|
7
|
+
exports.TransferNoteLifecycleEventBase = TransferNoteLifecycleEventBase;
|
|
8
|
+
// `detail` de TransferNoteSpeiCaptured: el pagador capturó el SPEI y la nota pasó a SENT.
|
|
9
|
+
class TransferNoteSpeiCapturedEventDetail extends TransferNoteLifecycleEventBase {
|
|
10
|
+
}
|
|
11
|
+
exports.TransferNoteSpeiCapturedEventDetail = TransferNoteSpeiCapturedEventDetail;
|
|
12
|
+
// `detail` de TransferNoteSpeiReverted: el SPEI se anuló y la nota volvió a ISSUED sin confirmaciones.
|
|
13
|
+
class TransferNoteSpeiRevertedEventDetail extends TransferNoteLifecycleEventBase {
|
|
14
|
+
}
|
|
15
|
+
exports.TransferNoteSpeiRevertedEventDetail = TransferNoteSpeiRevertedEventDetail;
|
|
16
|
+
// `detail` de TransferNoteCancelled: la nota se canceló y sus items volvieron al pool.
|
|
17
|
+
class TransferNoteCancelledEventDetail extends TransferNoteLifecycleEventBase {
|
|
18
|
+
}
|
|
19
|
+
exports.TransferNoteCancelledEventDetail = TransferNoteCancelledEventDetail;
|
|
20
|
+
// `detail` de TransferNoteDisputed: el retailer objetó la nota.
|
|
21
|
+
class TransferNoteDisputedEventDetail extends TransferNoteLifecycleEventBase {
|
|
22
|
+
}
|
|
23
|
+
exports.TransferNoteDisputedEventDetail = TransferNoteDisputedEventDetail;
|
|
24
|
+
// `detail` de TransferNoteDisputeResolved: quien controla la nota aceptó o rechazó la objeción. La nota que
|
|
25
|
+
// reemplaza a una aceptada sale después en su propio TransferNoteIssued.
|
|
26
|
+
class TransferNoteDisputeResolvedEventDetail extends TransferNoteLifecycleEventBase {
|
|
27
|
+
}
|
|
28
|
+
exports.TransferNoteDisputeResolvedEventDetail = TransferNoteDisputeResolvedEventDetail;
|
|
@@ -2,7 +2,12 @@ import { CurrencyId } from '../../currency/enums/CurrencyId';
|
|
|
2
2
|
export declare const TRANSFER_NOTE_EVENTS_SOURCE = "fiado.transferNote";
|
|
3
3
|
export declare enum TransferNoteEventDetailTypeEnum {
|
|
4
4
|
TRANSFER_NOTE_RECONCILED = "transferNote.TransferNoteReconciled",
|
|
5
|
-
TRANSFER_NOTE_ISSUED = "transferNote.TransferNoteIssued"
|
|
5
|
+
TRANSFER_NOTE_ISSUED = "transferNote.TransferNoteIssued",
|
|
6
|
+
TRANSFER_NOTE_SPEI_CAPTURED = "transferNote.TransferNoteSpeiCaptured",
|
|
7
|
+
TRANSFER_NOTE_SPEI_REVERTED = "transferNote.TransferNoteSpeiReverted",
|
|
8
|
+
TRANSFER_NOTE_CANCELLED = "transferNote.TransferNoteCancelled",
|
|
9
|
+
TRANSFER_NOTE_DISPUTED = "transferNote.TransferNoteDisputed",
|
|
10
|
+
TRANSFER_NOTE_DISPUTE_RESOLVED = "transferNote.TransferNoteDisputeResolved"
|
|
6
11
|
}
|
|
7
12
|
export declare class TransferNoteReconciledEventDetail {
|
|
8
13
|
/** Fijo por nota: un reenvío llega con el mismo id y el consumidor lo descarta. */
|
|
@@ -7,6 +7,11 @@ var TransferNoteEventDetailTypeEnum;
|
|
|
7
7
|
(function (TransferNoteEventDetailTypeEnum) {
|
|
8
8
|
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_RECONCILED"] = "transferNote.TransferNoteReconciled";
|
|
9
9
|
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_ISSUED"] = "transferNote.TransferNoteIssued";
|
|
10
|
+
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_SPEI_CAPTURED"] = "transferNote.TransferNoteSpeiCaptured";
|
|
11
|
+
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_SPEI_REVERTED"] = "transferNote.TransferNoteSpeiReverted";
|
|
12
|
+
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_CANCELLED"] = "transferNote.TransferNoteCancelled";
|
|
13
|
+
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_DISPUTED"] = "transferNote.TransferNoteDisputed";
|
|
14
|
+
TransferNoteEventDetailTypeEnum["TRANSFER_NOTE_DISPUTE_RESOLVED"] = "transferNote.TransferNoteDisputeResolved";
|
|
10
15
|
})(TransferNoteEventDetailTypeEnum || (exports.TransferNoteEventDetailTypeEnum = TransferNoteEventDetailTypeEnum = {}));
|
|
11
16
|
// `detail` de TransferNoteReconciled; el motor recalcula con él el agregado de la semana del par.
|
|
12
17
|
class TransferNoteReconciledEventDetail {
|
|
@@ -12,10 +12,12 @@ export * from './enums/TransferNoteConfirmationSourceEnum';
|
|
|
12
12
|
export * from './enums/TransferNoteHistoryActionEnum';
|
|
13
13
|
export * from './enums/TransferNoteFeedConceptEnum';
|
|
14
14
|
export * from './enums/TransferNoteErrorCodeEnum';
|
|
15
|
+
export * from './enums/TransferNoteConfigChangeDecisionEnum';
|
|
15
16
|
export * from './constants/transferNoteErrorHttpStatus';
|
|
16
17
|
export * from './constants/transferNoteValidation';
|
|
17
18
|
export * from './constants/transferNoteDefaultFees';
|
|
18
19
|
export * from './validators/IsTransferNoteSelectionConstraint';
|
|
20
|
+
export * from './validators/IsTransferNoteClabeConstraint';
|
|
19
21
|
export * from './dtos/TransferNote';
|
|
20
22
|
export * from './dtos/TransferNoteItem';
|
|
21
23
|
export * from './dtos/TransferNoteHistoryEntry';
|
|
@@ -33,3 +35,4 @@ export * from './dtos/responses/TransferNoteConfigResponses';
|
|
|
33
35
|
export * from './dtos/responses/PrivateTransferNoteResponses';
|
|
34
36
|
export * from './events/TransferNoteReconciledEventDetail';
|
|
35
37
|
export * from './events/TransferNoteIssuedEventDetail';
|
|
38
|
+
export * from './events/TransferNoteLifecycleEventDetails';
|
|
@@ -29,11 +29,13 @@ __exportStar(require("./enums/TransferNoteConfirmationSourceEnum"), exports);
|
|
|
29
29
|
__exportStar(require("./enums/TransferNoteHistoryActionEnum"), exports);
|
|
30
30
|
__exportStar(require("./enums/TransferNoteFeedConceptEnum"), exports);
|
|
31
31
|
__exportStar(require("./enums/TransferNoteErrorCodeEnum"), exports);
|
|
32
|
+
__exportStar(require("./enums/TransferNoteConfigChangeDecisionEnum"), exports);
|
|
32
33
|
// Constantes y validadores
|
|
33
34
|
__exportStar(require("./constants/transferNoteErrorHttpStatus"), exports);
|
|
34
35
|
__exportStar(require("./constants/transferNoteValidation"), exports);
|
|
35
36
|
__exportStar(require("./constants/transferNoteDefaultFees"), exports);
|
|
36
37
|
__exportStar(require("./validators/IsTransferNoteSelectionConstraint"), exports);
|
|
38
|
+
__exportStar(require("./validators/IsTransferNoteClabeConstraint"), exports);
|
|
37
39
|
// Entidades del dominio
|
|
38
40
|
__exportStar(require("./dtos/TransferNote"), exports);
|
|
39
41
|
__exportStar(require("./dtos/TransferNoteItem"), exports);
|
|
@@ -55,3 +57,4 @@ __exportStar(require("./dtos/responses/PrivateTransferNoteResponses"), exports);
|
|
|
55
57
|
// Eventos
|
|
56
58
|
__exportStar(require("./events/TransferNoteReconciledEventDetail"), exports);
|
|
57
59
|
__exportStar(require("./events/TransferNoteIssuedEventDetail"), exports);
|
|
60
|
+
__exportStar(require("./events/TransferNoteLifecycleEventDetails"), exports);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { type ValidatorConstraintInterface } from 'class-validator';
|
|
2
|
+
/** CLABE de 18 dígitos cuyo último dígito es el verificador correcto de los 17 anteriores. */
|
|
3
|
+
export declare function isValidTransferNoteClabe(clabe: unknown): boolean;
|
|
4
|
+
/** CLABE con formato y dígito verificador válidos. */
|
|
5
|
+
export declare class IsTransferNoteClabeConstraint implements ValidatorConstraintInterface {
|
|
6
|
+
validate(value: unknown): boolean;
|
|
7
|
+
defaultMessage(): string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.IsTransferNoteClabeConstraint = void 0;
|
|
10
|
+
exports.isValidTransferNoteClabe = isValidTransferNoteClabe;
|
|
11
|
+
const class_validator_1 = require("class-validator");
|
|
12
|
+
const transferNoteValidation_1 = require("../constants/transferNoteValidation");
|
|
13
|
+
/** Pesos 3, 7, 1 del dígito verificador de la CLABE (Banxico). */
|
|
14
|
+
const CLABE_CHECK_WEIGHTS = [3, 7, 1];
|
|
15
|
+
/** CLABE de 18 dígitos cuyo último dígito es el verificador correcto de los 17 anteriores. */
|
|
16
|
+
function isValidTransferNoteClabe(clabe) {
|
|
17
|
+
if (typeof clabe !== 'string' || !transferNoteValidation_1.TRANSFER_NOTE_CLABE_PATTERN.test(clabe))
|
|
18
|
+
return false;
|
|
19
|
+
const sum = [...clabe.slice(0, 17)].reduce((acc, digit, index) => acc + ((Number(digit) * CLABE_CHECK_WEIGHTS[index % 3]) % 10), 0);
|
|
20
|
+
return (10 - (sum % 10)) % 10 === Number(clabe[17]);
|
|
21
|
+
}
|
|
22
|
+
/** CLABE con formato y dígito verificador válidos. */
|
|
23
|
+
let IsTransferNoteClabeConstraint = class IsTransferNoteClabeConstraint {
|
|
24
|
+
validate(value) {
|
|
25
|
+
return isValidTransferNoteClabe(value);
|
|
26
|
+
}
|
|
27
|
+
defaultMessage() {
|
|
28
|
+
return 'CLABE de 18 dígitos con dígito verificador válido';
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
exports.IsTransferNoteClabeConstraint = IsTransferNoteClabeConstraint;
|
|
32
|
+
exports.IsTransferNoteClabeConstraint = IsTransferNoteClabeConstraint = __decorate([
|
|
33
|
+
(0, class_validator_1.ValidatorConstraint)({ name: 'isTransferNoteClabe', async: false })
|
|
34
|
+
], IsTransferNoteClabeConstraint);
|
package/package.json
CHANGED
|
@@ -88,6 +88,8 @@ export interface CommissionSaleInput {
|
|
|
88
88
|
/** `CASH` | `CREDIT`, para `PERCENT_BY_PAYMENT`. */
|
|
89
89
|
paymentType: string;
|
|
90
90
|
soldAt: string;
|
|
91
|
+
/** Cuándo se ganó la comisión: ubica la venta en su corte. Los devengos viejos no lo traen o lo traen en `null` (usar `soldAt`). */
|
|
92
|
+
earnedAt?: string | null;
|
|
91
93
|
/** Las líneas de la venta. Las ventas que se calculan con reglas siempre las traen. */
|
|
92
94
|
lines?: CommissionSaleLine[];
|
|
93
95
|
}
|
|
@@ -98,6 +98,10 @@ export interface Sale {
|
|
|
98
98
|
cancelledAt: string | null;
|
|
99
99
|
/** Reason OBLIGATORIO en cancelación (AuditTrail) — flujo C. */
|
|
100
100
|
cancelReason: string | null;
|
|
101
|
+
/** La comisión espera a que el cliente active su app: contraseña, contrato y número secreto. Ausente si no espera. */
|
|
102
|
+
commissionAwaitingApp?: boolean;
|
|
103
|
+
/** ISO; cuándo se ganó la comisión, si el cliente activó su app después de cerrar. Ausente: al cerrar o en espera. */
|
|
104
|
+
commissionEarnedAt?: string;
|
|
101
105
|
/** Si la venta se puede cancelar hoy y, si no, por qué; solo lo trae el detalle del backoffice. */
|
|
102
106
|
cancellation?: SaleCancellation;
|
|
103
107
|
/**
|
|
@@ -131,6 +131,14 @@ export class RetailCommissionInboundMessage {
|
|
|
131
131
|
@IsISO8601()
|
|
132
132
|
occurredAt!: string;
|
|
133
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Cuándo se ganó la comisión, si el cliente activó su app después de cerrar la venta. Ubica la venta en
|
|
136
|
+
* su corte. Ausente: se ganó al cerrar (`soldAt`).
|
|
137
|
+
*/
|
|
138
|
+
@IsOptional()
|
|
139
|
+
@IsISO8601()
|
|
140
|
+
earnedAt?: string;
|
|
141
|
+
|
|
134
142
|
/** Motivo de la cancelación; puede venir vacío. */
|
|
135
143
|
@IsOptional()
|
|
136
144
|
@IsString()
|
|
@@ -32,4 +32,8 @@ export const TRANSFER_NOTE_ERROR_HTTP_STATUS: Readonly<Record<C, 400 | 404 | 409
|
|
|
32
32
|
[C.CUTOFF_DISPATCH_FAILED]: 502,
|
|
33
33
|
[C.CONCURRENT_WRITE_CONFLICT]: 409,
|
|
34
34
|
[C.AUDIT_QUEUE_PUBLISH_FAILED]: 502,
|
|
35
|
+
[C.SAME_ACTOR_NOT_ALLOWED]: 422,
|
|
36
|
+
[C.CONFIG_CHANGE_NOT_PENDING]: 409,
|
|
37
|
+
[C.NOTE_DOCUMENT_NOT_READY]: 409,
|
|
38
|
+
[C.PARTY_ROUTING_CONFLICT]: 409,
|
|
35
39
|
};
|
|
@@ -28,6 +28,8 @@ export interface TransferNoteSpeiPayment {
|
|
|
28
28
|
capturedBy: string;
|
|
29
29
|
capturedAt: string;
|
|
30
30
|
receiptUploaded: boolean;
|
|
31
|
+
/** CLABE a la que se mandó el SPEI, si se capturó. */
|
|
32
|
+
paidToClabe?: string;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
export interface TransferNoteConfirmation {
|
|
@@ -46,6 +48,13 @@ export interface TransferNoteDispute {
|
|
|
46
48
|
previousStatus: TransferNoteStatusEnum;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
/** Cancelación de la nota: quién, cuándo y por qué. */
|
|
52
|
+
export interface TransferNoteCancellation {
|
|
53
|
+
reason: string;
|
|
54
|
+
cancelledBy: string;
|
|
55
|
+
cancelledAt: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
49
58
|
/** Fila de los listados de notas. */
|
|
50
59
|
export interface TransferNoteSummary {
|
|
51
60
|
noteId: string;
|
|
@@ -74,6 +83,8 @@ export interface TransferNote extends TransferNoteSummary {
|
|
|
74
83
|
issueReason?: string;
|
|
75
84
|
speiPayment?: TransferNoteSpeiPayment;
|
|
76
85
|
dispute?: TransferNoteDispute;
|
|
86
|
+
/** Solo en notas canceladas después de emitirse. */
|
|
87
|
+
cancellation?: TransferNoteCancellation;
|
|
77
88
|
/** Nota cancelada de la que nace esta, o la regenerada que la reemplaza. */
|
|
78
89
|
regeneratedFromNoteId?: string;
|
|
79
90
|
regeneratedNoteId?: string;
|
|
@@ -42,6 +42,19 @@ export interface TransferNotePairSettings {
|
|
|
42
42
|
discrepancyOpenedAt?: string;
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Cambio que altera a quién se le paga y espera la aprobación de otra persona: CLABE, tipo o tienda de una parte,
|
|
47
|
+
* pagador/receptor de un par, o tipos de pagador/receptor o signo de un concepto.
|
|
48
|
+
* Mientras no se apruebe rige el valor vigente del registro.
|
|
49
|
+
*/
|
|
50
|
+
export interface TransferNoteConfigPendingChange {
|
|
51
|
+
party?: TransferNotePartySettings;
|
|
52
|
+
concept?: TransferNoteConceptSettings;
|
|
53
|
+
pair?: TransferNotePairSettings;
|
|
54
|
+
proposedBy: string;
|
|
55
|
+
proposedAt: string;
|
|
56
|
+
}
|
|
57
|
+
|
|
45
58
|
/** Un registro de configuración; trae solo el sub-shape de su `recordType`. */
|
|
46
59
|
export interface TransferNoteConfigRecord {
|
|
47
60
|
recordKey: string;
|
|
@@ -50,6 +63,8 @@ export interface TransferNoteConfigRecord {
|
|
|
50
63
|
party?: TransferNotePartySettings;
|
|
51
64
|
concept?: TransferNoteConceptSettings;
|
|
52
65
|
pair?: TransferNotePairSettings;
|
|
66
|
+
/** Cambio propuesto que todavía no rige. */
|
|
67
|
+
pendingChange?: TransferNoteConfigPendingChange;
|
|
53
68
|
updatedAt: string;
|
|
54
69
|
updatedBy: string;
|
|
55
70
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Expose, Type } from 'class-transformer';
|
|
2
2
|
import { IsEnum, IsInt, IsISO8601, IsPositive, Validate } from 'class-validator';
|
|
3
3
|
import { TransferNoteDisputeDecisionEnum } from '../../enums/TransferNoteDisputeDecisionEnum';
|
|
4
|
+
import { IsTransferNoteClabeConstraint } from '../../validators/IsTransferNoteClabeConstraint';
|
|
4
5
|
import { IsTransferNoteSelectionConstraint } from '../../validators/IsTransferNoteSelectionConstraint';
|
|
5
6
|
import { LongReason, OptionalIsoWeek, RequiredId, RequiredReason } from './transferNoteRequestDecorators';
|
|
6
7
|
|
|
@@ -47,6 +48,11 @@ export class CaptureTransferNoteSpeiRequest {
|
|
|
47
48
|
@IsInt()
|
|
48
49
|
@IsPositive()
|
|
49
50
|
amountCents!: number;
|
|
51
|
+
|
|
52
|
+
/** CLABE a la que se mandó el SPEI, obligatoria; viaja en el feed de liquidación para compararla con la registrada del receptor. */
|
|
53
|
+
@Expose()
|
|
54
|
+
@Validate(IsTransferNoteClabeConstraint)
|
|
55
|
+
paidToClabe!: string;
|
|
50
56
|
}
|
|
51
57
|
|
|
52
58
|
/** Body de publicRevertTransferNoteSpei. */
|
|
@@ -55,9 +61,9 @@ export class RevertTransferNoteSpeiRequest {
|
|
|
55
61
|
reason!: string;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
/** Body de publicCancelTransferNote. */
|
|
64
|
+
/** Body de publicCancelTransferNote; la cancelación es irreversible y exige la misma razón larga que emitir u objetar. */
|
|
59
65
|
export class CancelTransferNoteRequest {
|
|
60
|
-
@
|
|
66
|
+
@LongReason()
|
|
61
67
|
reason!: string;
|
|
62
68
|
}
|
|
63
69
|
|
|
@@ -3,6 +3,7 @@ import { IsDefined, IsEmail, IsEnum, IsInt, IsISO8601, IsOptional, Matches, Min,
|
|
|
3
3
|
import { CurrencyId } from '../../../currency/enums/CurrencyId';
|
|
4
4
|
import { TRANSFER_NOTE_CLABE_PATTERN } from '../../constants/transferNoteValidation';
|
|
5
5
|
import { TransferNoteConceptSignEnum } from '../../enums/TransferNoteConceptSignEnum';
|
|
6
|
+
import { TransferNoteConfigChangeDecisionEnum } from '../../enums/TransferNoteConfigChangeDecisionEnum';
|
|
6
7
|
import { TransferNoteConfigRecordTypeEnum as RecordType } from '../../enums/TransferNoteConfigRecordTypeEnum';
|
|
7
8
|
import { TransferNotePartyTypeEnum } from '../../enums/TransferNotePartyTypeEnum';
|
|
8
9
|
import { RequiredId } from './transferNoteRequestDecorators';
|
|
@@ -79,7 +80,10 @@ export class TransferNotePairSettingsInput {
|
|
|
79
80
|
activeFrom!: string;
|
|
80
81
|
}
|
|
81
82
|
|
|
82
|
-
/**
|
|
83
|
+
/**
|
|
84
|
+
* Body de publicUpdateTransferNoteConfig; da de alta el registro si no existe.
|
|
85
|
+
* Un cambio de CLABE o de pagador/receptor de un par queda propuesto hasta que otra persona lo apruebe.
|
|
86
|
+
*/
|
|
83
87
|
export class UpdateTransferNoteConfigRequest {
|
|
84
88
|
@Expose()
|
|
85
89
|
@IsEnum(RecordType)
|
|
@@ -113,3 +117,15 @@ export class UpdateTransferNoteConfigRequest {
|
|
|
113
117
|
@Type(() => TransferNotePairSettingsInput)
|
|
114
118
|
pair?: TransferNotePairSettingsInput;
|
|
115
119
|
}
|
|
120
|
+
|
|
121
|
+
/** Body de publicDecideTransferNoteConfigChange: aprueba o descarta el cambio propuesto; quien lo propuso no puede aprobarlo. */
|
|
122
|
+
export class DecideTransferNoteConfigChangeRequest {
|
|
123
|
+
@Expose()
|
|
124
|
+
@IsEnum(TransferNoteConfigChangeDecisionEnum)
|
|
125
|
+
decision!: TransferNoteConfigChangeDecisionEnum;
|
|
126
|
+
|
|
127
|
+
/** `pendingChange.proposedAt` que vio quien decide; si la propuesta se reemplazó después, la decisión se rechaza. */
|
|
128
|
+
@Expose()
|
|
129
|
+
@IsISO8601()
|
|
130
|
+
proposedAt!: string;
|
|
131
|
+
}
|
|
@@ -41,4 +41,12 @@ export enum TransferNoteErrorCodeEnum {
|
|
|
41
41
|
/** Otra escritura cambió el registro entre la lectura y el guardado. */
|
|
42
42
|
CONCURRENT_WRITE_CONFLICT = 'CONCURRENT_WRITE_CONFLICT',
|
|
43
43
|
AUDIT_QUEUE_PUBLISH_FAILED = 'AUDIT_QUEUE_PUBLISH_FAILED',
|
|
44
|
+
/** La misma persona no puede hacer las dos mitades de un control (capturar y confirmar, proponer y aprobar). */
|
|
45
|
+
SAME_ACTOR_NOT_ALLOWED = 'SAME_ACTOR_NOT_ALLOWED',
|
|
46
|
+
/** El registro de configuración no tiene un cambio propuesto que decidir. */
|
|
47
|
+
CONFIG_CHANGE_NOT_PENDING = 'CONFIG_CHANGE_NOT_PENDING',
|
|
48
|
+
/** El documento de la nota todavía no tiene todos sus items; se reintenta en unos segundos. */
|
|
49
|
+
NOTE_DOCUMENT_NOT_READY = 'NOTE_DOCUMENT_NOT_READY',
|
|
50
|
+
/** La parte nueva repite la tienda de otra parte o un tipo que solo puede tener una parte en el tenant. */
|
|
51
|
+
PARTY_ROUTING_CONFLICT = 'PARTY_ROUTING_CONFLICT',
|
|
44
52
|
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { CurrencyId } from '../../currency/enums/CurrencyId';
|
|
2
|
+
import { TransferNoteDisputeCategoryEnum } from '../enums/TransferNoteDisputeCategoryEnum';
|
|
3
|
+
import { TransferNoteDisputeDecisionEnum } from '../enums/TransferNoteDisputeDecisionEnum';
|
|
4
|
+
|
|
5
|
+
// Campos que comparten los eventos del ciclo de vida de la nota; solo ids y montos, sin razones en texto libre.
|
|
6
|
+
export class TransferNoteLifecycleEventBase {
|
|
7
|
+
/** Fijo por ocurrencia (`{noteId}#{evento}#{instante}`): un reenvío llega con el mismo id. */
|
|
8
|
+
eventId!: string;
|
|
9
|
+
tenantId!: string;
|
|
10
|
+
noteId!: string;
|
|
11
|
+
pairKey!: string;
|
|
12
|
+
isoWeek!: string;
|
|
13
|
+
netAmountCents!: number;
|
|
14
|
+
currency!: CurrencyId;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// `detail` de TransferNoteSpeiCaptured: el pagador capturó el SPEI y la nota pasó a SENT.
|
|
18
|
+
export class TransferNoteSpeiCapturedEventDetail extends TransferNoteLifecycleEventBase {
|
|
19
|
+
trackingKey!: string;
|
|
20
|
+
/** ISO 8601 del pago declarado. */
|
|
21
|
+
paidAt!: string;
|
|
22
|
+
paidAmountCents!: number;
|
|
23
|
+
/** CLABE a la que se mandó el SPEI, si se capturó. */
|
|
24
|
+
paidToClabe?: string;
|
|
25
|
+
capturedAt!: string;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// `detail` de TransferNoteSpeiReverted: el SPEI se anuló y la nota volvió a ISSUED sin confirmaciones.
|
|
29
|
+
export class TransferNoteSpeiRevertedEventDetail extends TransferNoteLifecycleEventBase {
|
|
30
|
+
/** Clave de rastreo del SPEI anulado. */
|
|
31
|
+
trackingKey!: string;
|
|
32
|
+
revertedAt!: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// `detail` de TransferNoteCancelled: la nota se canceló y sus items volvieron al pool.
|
|
36
|
+
export class TransferNoteCancelledEventDetail extends TransferNoteLifecycleEventBase {
|
|
37
|
+
cancelledAt!: string;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// `detail` de TransferNoteDisputed: el retailer objetó la nota.
|
|
41
|
+
export class TransferNoteDisputedEventDetail extends TransferNoteLifecycleEventBase {
|
|
42
|
+
category!: TransferNoteDisputeCategoryEnum;
|
|
43
|
+
disputedAt!: string;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// `detail` de TransferNoteDisputeResolved: quien controla la nota aceptó o rechazó la objeción. La nota que
|
|
47
|
+
// reemplaza a una aceptada sale después en su propio TransferNoteIssued.
|
|
48
|
+
export class TransferNoteDisputeResolvedEventDetail extends TransferNoteLifecycleEventBase {
|
|
49
|
+
decision!: TransferNoteDisputeDecisionEnum;
|
|
50
|
+
resolvedAt!: string;
|
|
51
|
+
}
|
|
@@ -6,6 +6,11 @@ export const TRANSFER_NOTE_EVENTS_SOURCE = 'fiado.transferNote';
|
|
|
6
6
|
export enum TransferNoteEventDetailTypeEnum {
|
|
7
7
|
TRANSFER_NOTE_RECONCILED = 'transferNote.TransferNoteReconciled',
|
|
8
8
|
TRANSFER_NOTE_ISSUED = 'transferNote.TransferNoteIssued',
|
|
9
|
+
TRANSFER_NOTE_SPEI_CAPTURED = 'transferNote.TransferNoteSpeiCaptured',
|
|
10
|
+
TRANSFER_NOTE_SPEI_REVERTED = 'transferNote.TransferNoteSpeiReverted',
|
|
11
|
+
TRANSFER_NOTE_CANCELLED = 'transferNote.TransferNoteCancelled',
|
|
12
|
+
TRANSFER_NOTE_DISPUTED = 'transferNote.TransferNoteDisputed',
|
|
13
|
+
TRANSFER_NOTE_DISPUTE_RESOLVED = 'transferNote.TransferNoteDisputeResolved',
|
|
9
14
|
}
|
|
10
15
|
|
|
11
16
|
// `detail` de TransferNoteReconciled; el motor recalcula con él el agregado de la semana del par.
|
|
@@ -13,12 +13,14 @@ export * from './enums/TransferNoteConfirmationSourceEnum';
|
|
|
13
13
|
export * from './enums/TransferNoteHistoryActionEnum';
|
|
14
14
|
export * from './enums/TransferNoteFeedConceptEnum';
|
|
15
15
|
export * from './enums/TransferNoteErrorCodeEnum';
|
|
16
|
+
export * from './enums/TransferNoteConfigChangeDecisionEnum';
|
|
16
17
|
|
|
17
18
|
// Constantes y validadores
|
|
18
19
|
export * from './constants/transferNoteErrorHttpStatus';
|
|
19
20
|
export * from './constants/transferNoteValidation';
|
|
20
21
|
export * from './constants/transferNoteDefaultFees';
|
|
21
22
|
export * from './validators/IsTransferNoteSelectionConstraint';
|
|
23
|
+
export * from './validators/IsTransferNoteClabeConstraint';
|
|
22
24
|
|
|
23
25
|
// Entidades del dominio
|
|
24
26
|
export * from './dtos/TransferNote';
|
|
@@ -44,3 +46,4 @@ export * from './dtos/responses/PrivateTransferNoteResponses';
|
|
|
44
46
|
// Eventos
|
|
45
47
|
export * from './events/TransferNoteReconciledEventDetail';
|
|
46
48
|
export * from './events/TransferNoteIssuedEventDetail';
|
|
49
|
+
export * from './events/TransferNoteLifecycleEventDetails';
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { ValidatorConstraint, type ValidatorConstraintInterface } from 'class-validator';
|
|
2
|
+
import { TRANSFER_NOTE_CLABE_PATTERN } from '../constants/transferNoteValidation';
|
|
3
|
+
|
|
4
|
+
/** Pesos 3, 7, 1 del dígito verificador de la CLABE (Banxico). */
|
|
5
|
+
const CLABE_CHECK_WEIGHTS = [3, 7, 1] as const;
|
|
6
|
+
|
|
7
|
+
/** CLABE de 18 dígitos cuyo último dígito es el verificador correcto de los 17 anteriores. */
|
|
8
|
+
export function isValidTransferNoteClabe(clabe: unknown): boolean {
|
|
9
|
+
if (typeof clabe !== 'string' || !TRANSFER_NOTE_CLABE_PATTERN.test(clabe)) return false;
|
|
10
|
+
const sum = [...clabe.slice(0, 17)].reduce((acc, digit, index) => acc + ((Number(digit) * CLABE_CHECK_WEIGHTS[index % 3]) % 10), 0);
|
|
11
|
+
return (10 - (sum % 10)) % 10 === Number(clabe[17]);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** CLABE con formato y dígito verificador válidos. */
|
|
15
|
+
@ValidatorConstraint({ name: 'isTransferNoteClabe', async: false })
|
|
16
|
+
export class IsTransferNoteClabeConstraint implements ValidatorConstraintInterface {
|
|
17
|
+
validate(value: unknown): boolean {
|
|
18
|
+
return isValidTransferNoteClabe(value);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
defaultMessage(): string {
|
|
22
|
+
return 'CLABE de 18 dígitos con dígito verificador válido';
|
|
23
|
+
}
|
|
24
|
+
}
|