@fiado/type-kit 3.400.0 → 3.401.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/dtos/loanDeviceContract.test.ts +405 -0
- package/bin/loanCredit/dtos/LoanDeviceNotifyItem.d.ts +12 -0
- package/bin/loanCredit/dtos/LoanDeviceNotifyItem.js +54 -0
- package/bin/loanCredit/dtos/LoanDeviceReleaseItem.d.ts +8 -0
- package/bin/loanCredit/dtos/LoanDeviceReleaseItem.js +37 -0
- package/bin/loanCredit/dtos/LoanDeviceUnlockItem.d.ts +6 -0
- package/bin/loanCredit/dtos/LoanDeviceUnlockItem.js +31 -0
- package/bin/loanCredit/dtos/requests/LoanDeviceManualUnlockRequest.d.ts +2 -2
- package/bin/loanCredit/dtos/requests/LoanDeviceManualUnlockRequest.js +2 -2
- package/bin/loanCredit/dtos/requests/LoanDeviceNotifyRequest.d.ts +7 -0
- package/bin/loanCredit/dtos/requests/LoanDeviceNotifyRequest.js +37 -0
- package/bin/loanCredit/dtos/requests/LoanDeviceReleaseRequest.d.ts +7 -0
- package/bin/loanCredit/dtos/requests/LoanDeviceReleaseRequest.js +37 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceNotifyResponse.d.ts +5 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceNotifyResponse.js +7 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceNotifyResult.d.ts +8 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceNotifyResult.js +8 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceReleaseResponse.d.ts +5 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceReleaseResponse.js +7 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceReleaseResult.d.ts +8 -0
- package/bin/loanCredit/dtos/responses/LoanDeviceReleaseResult.js +8 -0
- package/bin/loanCredit/index.d.ts +12 -0
- package/bin/loanCredit/index.js +15 -1
- package/bin/loanCredit/validators/IsFutureIso8601DateTime.d.ts +18 -0
- package/bin/loanCredit/validators/IsFutureIso8601DateTime.js +41 -0
- package/package.json +1 -1
- package/src/loanCredit/dtos/LoanDeviceNotifyItem.ts +36 -0
- package/src/loanCredit/dtos/LoanDeviceReleaseItem.ts +22 -0
- package/src/loanCredit/dtos/LoanDeviceUnlockItem.ts +17 -0
- package/src/loanCredit/dtos/requests/LoanDeviceManualUnlockRequest.ts +4 -4
- package/src/loanCredit/dtos/requests/LoanDeviceNotifyRequest.ts +23 -0
- package/src/loanCredit/dtos/requests/LoanDeviceReleaseRequest.ts +23 -0
- package/src/loanCredit/dtos/responses/LoanDeviceNotifyResponse.ts +6 -0
- package/src/loanCredit/dtos/responses/LoanDeviceNotifyResult.ts +10 -0
- package/src/loanCredit/dtos/responses/LoanDeviceReleaseResponse.ts +6 -0
- package/src/loanCredit/dtos/responses/LoanDeviceReleaseResult.ts +10 -0
- package/src/loanCredit/index.ts +13 -0
- package/src/loanCredit/validators/IsFutureIso8601DateTime.ts +32 -0
|
@@ -7,7 +7,17 @@ import { LoanDeviceLastSeenRequest } from '../../../../src/loanCredit/dtos/reque
|
|
|
7
7
|
import { LoanDeviceStatusRequest } from '../../../../src/loanCredit/dtos/requests/LoanDeviceStatusRequest';
|
|
8
8
|
import { LoanDeviceManualLockRequest } from '../../../../src/loanCredit/dtos/requests/LoanDeviceManualLockRequest';
|
|
9
9
|
import { LoanDeviceManualUnlockRequest } from '../../../../src/loanCredit/dtos/requests/LoanDeviceManualUnlockRequest';
|
|
10
|
+
import { LoanDeviceNotifyRequest } from '../../../../src/loanCredit/dtos/requests/LoanDeviceNotifyRequest';
|
|
11
|
+
import { LoanDeviceReleaseRequest } from '../../../../src/loanCredit/dtos/requests/LoanDeviceReleaseRequest';
|
|
12
|
+
import { LoanDeviceNotifyResponse } from '../../../../src/loanCredit/dtos/responses/LoanDeviceNotifyResponse';
|
|
13
|
+
import { LoanDeviceNotifyResult } from '../../../../src/loanCredit/dtos/responses/LoanDeviceNotifyResult';
|
|
14
|
+
import { LoanDeviceReleaseResponse } from '../../../../src/loanCredit/dtos/responses/LoanDeviceReleaseResponse';
|
|
15
|
+
import { LoanDeviceReleaseResult } from '../../../../src/loanCredit/dtos/responses/LoanDeviceReleaseResult';
|
|
10
16
|
import { LoanDeviceLockReasonEnum } from '../../../../src/loanCredit/enums/LoanDeviceLockReasonEnum';
|
|
17
|
+
import { LoanDeviceMdmStatusEnum } from '../../../../src/loanCredit/enums/LoanDeviceMdmStatusEnum';
|
|
18
|
+
import { DeviceNotifyIntensityEnum } from '../../../../src/mdm/enums/DeviceNotifyIntensityEnum';
|
|
19
|
+
import { DeviceReleaseReasonEnum } from '../../../../src/mdm/enums/DeviceReleaseReasonEnum';
|
|
20
|
+
import { MdmOperationStatusEnum } from '../../../../src/mdm/enums/MdmOperationStatusEnum';
|
|
11
21
|
import { LOAN_DEVICE_MAX_BATCH } from '../../../../src/loanCredit/constants/LoanDeviceBatchLimits';
|
|
12
22
|
|
|
13
23
|
/** ULIDs reales del motor: así es como `LoanCreditService` genera los creditId. */
|
|
@@ -34,6 +44,9 @@ const creditIdAt = (index: number): string => {
|
|
|
34
44
|
const hasConstraint = (errors: ValidationError[], property: string, constraint: string): boolean =>
|
|
35
45
|
errors.some(e => e.property === property && Boolean(e.constraints?.[constraint]));
|
|
36
46
|
|
|
47
|
+
/** Instante ISO-8601 a N días de ahora; evita que el test caduque con el calendario. */
|
|
48
|
+
const isoInDays = (days: number): string => new Date(Date.now() + days * 86_400_000).toISOString();
|
|
49
|
+
|
|
37
50
|
const validEnrollItem = {
|
|
38
51
|
creditId: CREDIT_ID,
|
|
39
52
|
imei: '351756051523999',
|
|
@@ -230,6 +243,15 @@ describe('LoanDeviceManualUnlockRequest', () => {
|
|
|
230
243
|
expect(JSON.stringify(itemsError)).toContain('creditId');
|
|
231
244
|
});
|
|
232
245
|
|
|
246
|
+
it(`acepta exactamente ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
247
|
+
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH }, (_, i) => ({
|
|
248
|
+
creditId: creditIdAt(i),
|
|
249
|
+
}));
|
|
250
|
+
const dto = plainToInstance(LoanDeviceManualUnlockRequest, { ...validUnlock, items });
|
|
251
|
+
const errors = await validate(dto);
|
|
252
|
+
expect(errors).toEqual([]);
|
|
253
|
+
});
|
|
254
|
+
|
|
233
255
|
it(`falla si el lote supera ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
234
256
|
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH + 1 }, (_, i) => ({
|
|
235
257
|
creditId: creditIdAt(i),
|
|
@@ -294,3 +316,386 @@ describe.each([
|
|
|
294
316
|
expect(hasConstraint(errors, 'creditIds', 'arrayUnique')).toBe(true);
|
|
295
317
|
});
|
|
296
318
|
});
|
|
319
|
+
|
|
320
|
+
describe('LoanDeviceNotifyRequest', () => {
|
|
321
|
+
const validNotifyItem = {
|
|
322
|
+
creditId: CREDIT_ID,
|
|
323
|
+
intensity: DeviceNotifyIntensityEnum.BANNER,
|
|
324
|
+
notificationCode: 'PAYMENT_REMINDER_D1',
|
|
325
|
+
title: 'Tu pago está pendiente',
|
|
326
|
+
content: 'Ponte al corriente para seguir usando tu equipo.',
|
|
327
|
+
};
|
|
328
|
+
const validNotify = { items: [validNotifyItem], operationReference: 'notify-2026-09-02-0001' };
|
|
329
|
+
|
|
330
|
+
const notifyWithItems = (items: unknown[]) => ({ ...validNotify, items });
|
|
331
|
+
|
|
332
|
+
/** Cota de cada campo de texto libre, para probar el borde exacto y el borde + 1. */
|
|
333
|
+
const NOTIFY_MAX_LENGTHS: ReadonlyArray<readonly [string, number]> = [
|
|
334
|
+
['notificationCode', 64],
|
|
335
|
+
['title', 128],
|
|
336
|
+
['content', 1024],
|
|
337
|
+
];
|
|
338
|
+
|
|
339
|
+
it('valida happy path (0 errores)', async () => {
|
|
340
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, validNotify);
|
|
341
|
+
const errors = await validate(dto);
|
|
342
|
+
expect(errors).toEqual([]);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
it('valida con intensity FULLSCREEN', async () => {
|
|
346
|
+
const items = [{ ...validNotifyItem, intensity: DeviceNotifyIntensityEnum.FULLSCREEN }];
|
|
347
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
348
|
+
const errors = await validate(dto);
|
|
349
|
+
expect(errors).toEqual([]);
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
it('falla si intensity está fuera del enum', async () => {
|
|
353
|
+
const items = [{ ...validNotifyItem, intensity: 'POPUP' }];
|
|
354
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
355
|
+
const errors = await validate(dto);
|
|
356
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
357
|
+
expect(itemsError).toBeDefined();
|
|
358
|
+
expect(JSON.stringify(itemsError)).toContain('intensity');
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
it.each(['notificationCode', 'title', 'content'] as const)('falla si %s viene vacío', async field => {
|
|
362
|
+
const items = [{ ...validNotifyItem, [field]: '' }];
|
|
363
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
364
|
+
const errors = await validate(dto);
|
|
365
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
366
|
+
expect(itemsError).toBeDefined();
|
|
367
|
+
expect(JSON.stringify(itemsError)).toContain(field);
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
it.each(['notificationCode', 'title', 'content'] as const)('falla si %s no viene', async field => {
|
|
371
|
+
const { [field]: _omitted, ...rest } = validNotifyItem;
|
|
372
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems([rest]));
|
|
373
|
+
const errors = await validate(dto);
|
|
374
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
375
|
+
expect(itemsError).toBeDefined();
|
|
376
|
+
expect(JSON.stringify(itemsError)).toContain(field);
|
|
377
|
+
});
|
|
378
|
+
|
|
379
|
+
it.each(['notificationCode', 'title', 'content'] as const)('falla si %s no es string', async field => {
|
|
380
|
+
const items = [{ ...validNotifyItem, [field]: 42 }];
|
|
381
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
382
|
+
const errors = await validate(dto);
|
|
383
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
384
|
+
expect(itemsError).toBeDefined();
|
|
385
|
+
expect(JSON.stringify(itemsError)).toContain(field);
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
it('falla si intensity no viene', async () => {
|
|
389
|
+
const { intensity: _omitted, ...rest } = validNotifyItem;
|
|
390
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems([rest]));
|
|
391
|
+
const errors = await validate(dto);
|
|
392
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
393
|
+
expect(itemsError).toBeDefined();
|
|
394
|
+
expect(JSON.stringify(itemsError)).toContain('intensity');
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
it.each(NOTIFY_MAX_LENGTHS)('acepta %s en su largo máximo de %i', async (field, max) => {
|
|
398
|
+
const items = [{ ...validNotifyItem, [field]: 'x'.repeat(max) }];
|
|
399
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
400
|
+
const errors = await validate(dto);
|
|
401
|
+
expect(errors).toEqual([]);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it.each(NOTIFY_MAX_LENGTHS)('falla si %s supera su largo máximo de %i', async (field, max) => {
|
|
405
|
+
const items = [{ ...validNotifyItem, [field]: 'x'.repeat(max + 1) }];
|
|
406
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
407
|
+
const errors = await validate(dto);
|
|
408
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
409
|
+
expect(itemsError).toBeDefined();
|
|
410
|
+
expect(JSON.stringify(itemsError)).toContain('maxLength');
|
|
411
|
+
expect(JSON.stringify(itemsError)).toContain(field);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
it('falla si algún creditId no es un ULID', async () => {
|
|
415
|
+
const items = [{ ...validNotifyItem, creditId: A_UUID }];
|
|
416
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
417
|
+
const errors = await validate(dto);
|
|
418
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
419
|
+
expect(itemsError).toBeDefined();
|
|
420
|
+
expect(JSON.stringify(itemsError)).toContain('creditId');
|
|
421
|
+
});
|
|
422
|
+
|
|
423
|
+
it(`acepta exactamente ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
424
|
+
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH }, (_, i) => ({
|
|
425
|
+
...validNotifyItem,
|
|
426
|
+
creditId: creditIdAt(i),
|
|
427
|
+
}));
|
|
428
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
429
|
+
const errors = await validate(dto);
|
|
430
|
+
expect(errors).toEqual([]);
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
it(`falla si el lote supera ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
434
|
+
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH + 1 }, (_, i) => ({
|
|
435
|
+
...validNotifyItem,
|
|
436
|
+
creditId: creditIdAt(i),
|
|
437
|
+
}));
|
|
438
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems(items));
|
|
439
|
+
const errors = await validate(dto);
|
|
440
|
+
expect(hasConstraint(errors, 'items', 'arrayMaxSize')).toBe(true);
|
|
441
|
+
});
|
|
442
|
+
|
|
443
|
+
it('falla si dos ítems traen el mismo creditId', async () => {
|
|
444
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems([validNotifyItem, { ...validNotifyItem }]));
|
|
445
|
+
const errors = await validate(dto);
|
|
446
|
+
expect(hasConstraint(errors, 'items', 'arrayUnique')).toBe(true);
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('falla si items viene vacío', async () => {
|
|
450
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, notifyWithItems([]));
|
|
451
|
+
const errors = await validate(dto);
|
|
452
|
+
expect(hasConstraint(errors, 'items', 'arrayNotEmpty')).toBe(true);
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
it('falla si operationReference supera los 64 caracteres', async () => {
|
|
456
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, { ...validNotify, operationReference: 'x'.repeat(200) });
|
|
457
|
+
const errors = await validate(dto);
|
|
458
|
+
expect(hasConstraint(errors, 'operationReference', 'isLength')).toBe(true);
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('falla si operationReference viene vacío', async () => {
|
|
462
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, { ...validNotify, operationReference: '' });
|
|
463
|
+
const errors = await validate(dto);
|
|
464
|
+
expect(hasConstraint(errors, 'operationReference', 'isNotEmpty')).toBe(true);
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
it('falla si operationReference no viene', async () => {
|
|
468
|
+
const { operationReference: _omitted, ...rest } = validNotify;
|
|
469
|
+
const dto = plainToInstance(LoanDeviceNotifyRequest, rest);
|
|
470
|
+
const errors = await validate(dto);
|
|
471
|
+
expect(errors.some(e => e.property === 'operationReference')).toBe(true);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
describe('LoanDeviceReleaseRequest', () => {
|
|
476
|
+
const validReleaseItem = { creditId: CREDIT_ID, reason: DeviceReleaseReasonEnum.CREDIT_PAID_OFF };
|
|
477
|
+
const validRelease = { items: [validReleaseItem], operationReference: 'release-2026-09-02-0001' };
|
|
478
|
+
|
|
479
|
+
const releaseWithItems = (items: unknown[]) => ({ ...validRelease, items });
|
|
480
|
+
|
|
481
|
+
it('valida happy path sin comment (0 errores)', async () => {
|
|
482
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, validRelease);
|
|
483
|
+
const errors = await validate(dto);
|
|
484
|
+
expect(errors).toEqual([]);
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
it('valida con comment presente', async () => {
|
|
488
|
+
const items = [{ ...validReleaseItem, reason: DeviceReleaseReasonEnum.OTHER, comment: 'Equipo robado y repuesto.' }];
|
|
489
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
490
|
+
const errors = await validate(dto);
|
|
491
|
+
expect(errors).toEqual([]);
|
|
492
|
+
});
|
|
493
|
+
|
|
494
|
+
it('falla si comment no es string', async () => {
|
|
495
|
+
const items = [{ ...validReleaseItem, comment: 42 }];
|
|
496
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
497
|
+
const errors = await validate(dto);
|
|
498
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
499
|
+
expect(itemsError).toBeDefined();
|
|
500
|
+
expect(JSON.stringify(itemsError)).toContain('comment');
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it('acepta comment en su largo máximo de 500', async () => {
|
|
504
|
+
const items = [{ ...validReleaseItem, comment: 'x'.repeat(500) }];
|
|
505
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
506
|
+
const errors = await validate(dto);
|
|
507
|
+
expect(errors).toEqual([]);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it('falla si comment supera los 500 caracteres', async () => {
|
|
511
|
+
const items = [{ ...validReleaseItem, comment: 'x'.repeat(501) }];
|
|
512
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
513
|
+
const errors = await validate(dto);
|
|
514
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
515
|
+
expect(itemsError).toBeDefined();
|
|
516
|
+
expect(JSON.stringify(itemsError)).toContain('maxLength');
|
|
517
|
+
expect(JSON.stringify(itemsError)).toContain('comment');
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
it('falla si reason está fuera del enum', async () => {
|
|
521
|
+
const items = [{ creditId: CREDIT_ID, reason: 'PORQUE_SI' }];
|
|
522
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
523
|
+
const errors = await validate(dto);
|
|
524
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
525
|
+
expect(itemsError).toBeDefined();
|
|
526
|
+
expect(JSON.stringify(itemsError)).toContain('reason');
|
|
527
|
+
});
|
|
528
|
+
|
|
529
|
+
it('falla si reason no viene', async () => {
|
|
530
|
+
const items = [{ creditId: CREDIT_ID }];
|
|
531
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
532
|
+
const errors = await validate(dto);
|
|
533
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
534
|
+
expect(itemsError).toBeDefined();
|
|
535
|
+
expect(JSON.stringify(itemsError)).toContain('reason');
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it('falla si algún creditId no es un ULID', async () => {
|
|
539
|
+
const items = [{ ...validReleaseItem, creditId: A_UUID }];
|
|
540
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
541
|
+
const errors = await validate(dto);
|
|
542
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
543
|
+
expect(itemsError).toBeDefined();
|
|
544
|
+
expect(JSON.stringify(itemsError)).toContain('creditId');
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
it(`acepta exactamente ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
548
|
+
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH }, (_, i) => ({
|
|
549
|
+
...validReleaseItem,
|
|
550
|
+
creditId: creditIdAt(i),
|
|
551
|
+
}));
|
|
552
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
553
|
+
const errors = await validate(dto);
|
|
554
|
+
expect(errors).toEqual([]);
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
it(`falla si el lote supera ${LOAN_DEVICE_MAX_BATCH} ítems`, async () => {
|
|
558
|
+
const items = Array.from({ length: LOAN_DEVICE_MAX_BATCH + 1 }, (_, i) => ({
|
|
559
|
+
...validReleaseItem,
|
|
560
|
+
creditId: creditIdAt(i),
|
|
561
|
+
}));
|
|
562
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems(items));
|
|
563
|
+
const errors = await validate(dto);
|
|
564
|
+
expect(hasConstraint(errors, 'items', 'arrayMaxSize')).toBe(true);
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
it('falla si dos ítems traen el mismo creditId', async () => {
|
|
568
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems([validReleaseItem, { ...validReleaseItem }]));
|
|
569
|
+
const errors = await validate(dto);
|
|
570
|
+
expect(hasConstraint(errors, 'items', 'arrayUnique')).toBe(true);
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
it('falla si items viene vacío', async () => {
|
|
574
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, releaseWithItems([]));
|
|
575
|
+
const errors = await validate(dto);
|
|
576
|
+
expect(hasConstraint(errors, 'items', 'arrayNotEmpty')).toBe(true);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it('falla si operationReference supera los 64 caracteres', async () => {
|
|
580
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, { ...validRelease, operationReference: 'x'.repeat(200) });
|
|
581
|
+
const errors = await validate(dto);
|
|
582
|
+
expect(hasConstraint(errors, 'operationReference', 'isLength')).toBe(true);
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it('falla si operationReference viene vacío', async () => {
|
|
586
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, { ...validRelease, operationReference: '' });
|
|
587
|
+
const errors = await validate(dto);
|
|
588
|
+
expect(hasConstraint(errors, 'operationReference', 'isNotEmpty')).toBe(true);
|
|
589
|
+
});
|
|
590
|
+
|
|
591
|
+
it('falla si operationReference no viene', async () => {
|
|
592
|
+
const { operationReference: _omitted, ...rest } = validRelease;
|
|
593
|
+
const dto = plainToInstance(LoanDeviceReleaseRequest, rest);
|
|
594
|
+
const errors = await validate(dto);
|
|
595
|
+
expect(errors.some(e => e.property === 'operationReference')).toBe(true);
|
|
596
|
+
});
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
describe('LoanDeviceManualUnlockRequest — unlockUntil', () => {
|
|
600
|
+
const unlockWith = (item: Record<string, unknown>) => ({
|
|
601
|
+
items: [item],
|
|
602
|
+
operationReference: 'unlock-2026-09-02-0001',
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
/** Falla y el error apunta a `unlockUntil`. */
|
|
606
|
+
const expectUnlockUntilRejected = async (unlockUntil: unknown): Promise<void> => {
|
|
607
|
+
const dto = plainToInstance(LoanDeviceManualUnlockRequest, unlockWith({ creditId: CREDIT_ID, unlockUntil }));
|
|
608
|
+
const errors = await validate(dto);
|
|
609
|
+
const itemsError = errors.find(e => e.property === 'items');
|
|
610
|
+
expect(itemsError).toBeDefined();
|
|
611
|
+
expect(JSON.stringify(itemsError)).toContain('unlockUntil');
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
it('valida con unlockUntil datetime futuro', async () => {
|
|
615
|
+
const dto = plainToInstance(LoanDeviceManualUnlockRequest, unlockWith({
|
|
616
|
+
creditId: CREDIT_ID,
|
|
617
|
+
unlockUntil: isoInDays(30),
|
|
618
|
+
}));
|
|
619
|
+
const errors = await validate(dto);
|
|
620
|
+
expect(errors).toEqual([]);
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it('falla si unlockUntil no es ISO-8601', async () => {
|
|
624
|
+
await expectUnlockUntilRejected('30/09/2026');
|
|
625
|
+
});
|
|
626
|
+
|
|
627
|
+
it('falla si unlockUntil es una fecha pasada', async () => {
|
|
628
|
+
await expectUnlockUntilRejected(isoInDays(-1));
|
|
629
|
+
});
|
|
630
|
+
|
|
631
|
+
it('falla si unlockUntil es solo fecha, sin hora', async () => {
|
|
632
|
+
await expectUnlockUntilRejected('2099-09-30');
|
|
633
|
+
});
|
|
634
|
+
|
|
635
|
+
it('falla si unlockUntil no trae zona horaria', async () => {
|
|
636
|
+
await expectUnlockUntilRejected('2099-09-30T23:59:59');
|
|
637
|
+
});
|
|
638
|
+
|
|
639
|
+
it('falla si unlockUntil no es string', async () => {
|
|
640
|
+
await expectUnlockUntilRejected(42);
|
|
641
|
+
});
|
|
642
|
+
|
|
643
|
+
it('sigue aceptando ítems sin unlockUntil (el campo es opcional)', async () => {
|
|
644
|
+
const dto = plainToInstance(LoanDeviceManualUnlockRequest, unlockWith({ creditId: CREDIT_ID }));
|
|
645
|
+
const errors = await validate(dto);
|
|
646
|
+
expect(errors).toEqual([]);
|
|
647
|
+
});
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
describe('responses de notify y release', () => {
|
|
651
|
+
it('LoanDeviceNotifyResponse arma un resultado por ítem, con la referencia del batch', () => {
|
|
652
|
+
const response: LoanDeviceNotifyResponse = {
|
|
653
|
+
results: [
|
|
654
|
+
{
|
|
655
|
+
creditId: CREDIT_ID,
|
|
656
|
+
status: MdmOperationStatusEnum.SUCCESS,
|
|
657
|
+
operationReference: 'notify-2026-09-02-0001',
|
|
658
|
+
mdmStatus: LoanDeviceMdmStatusEnum.LOCKED,
|
|
659
|
+
notifiedAt: isoInDays(0),
|
|
660
|
+
},
|
|
661
|
+
],
|
|
662
|
+
};
|
|
663
|
+
expect(response.results[0].notifiedAt).toBeDefined();
|
|
664
|
+
expect(response.results[0].operationReference).toBe('notify-2026-09-02-0001');
|
|
665
|
+
});
|
|
666
|
+
|
|
667
|
+
it('LoanDeviceNotifyResult deja notifiedAt opcional: un ítem puede fallar sin instante', () => {
|
|
668
|
+
const failed: LoanDeviceNotifyResult = {
|
|
669
|
+
creditId: CREDIT_ID,
|
|
670
|
+
status: MdmOperationStatusEnum.ERROR,
|
|
671
|
+
operationReference: 'notify-2026-09-02-0002',
|
|
672
|
+
mdmStatus: LoanDeviceMdmStatusEnum.ACTIVE,
|
|
673
|
+
};
|
|
674
|
+
expect(failed.notifiedAt).toBeUndefined();
|
|
675
|
+
});
|
|
676
|
+
|
|
677
|
+
it('LoanDeviceReleaseResponse reporta el expediente ya liberado', () => {
|
|
678
|
+
const response: LoanDeviceReleaseResponse = {
|
|
679
|
+
results: [
|
|
680
|
+
{
|
|
681
|
+
creditId: CREDIT_ID,
|
|
682
|
+
status: MdmOperationStatusEnum.SUCCESS,
|
|
683
|
+
operationReference: 'release-2026-09-02-0001',
|
|
684
|
+
mdmStatus: LoanDeviceMdmStatusEnum.RELEASED,
|
|
685
|
+
releasedAt: isoInDays(0),
|
|
686
|
+
},
|
|
687
|
+
],
|
|
688
|
+
};
|
|
689
|
+
expect(response.results[0].mdmStatus).toBe(LoanDeviceMdmStatusEnum.RELEASED);
|
|
690
|
+
});
|
|
691
|
+
|
|
692
|
+
it('LoanDeviceReleaseResult deja releasedAt opcional: un ítem puede fallar sin instante', () => {
|
|
693
|
+
const failed: LoanDeviceReleaseResult = {
|
|
694
|
+
creditId: CREDIT_ID,
|
|
695
|
+
status: MdmOperationStatusEnum.ERROR,
|
|
696
|
+
operationReference: 'release-2026-09-02-0002',
|
|
697
|
+
mdmStatus: LoanDeviceMdmStatusEnum.ACTIVE,
|
|
698
|
+
};
|
|
699
|
+
expect(failed.releasedAt).toBeUndefined();
|
|
700
|
+
});
|
|
701
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { DeviceNotifyIntensityEnum } from '../../mdm/enums/DeviceNotifyIntensityEnum';
|
|
2
|
+
/**
|
|
3
|
+
* Ítem de notificación: a qué crédito se le avisa y con qué texto.
|
|
4
|
+
* Los tres campos de texto son obligatorios: Datacultr usa el código y Trustonic el texto crudo.
|
|
5
|
+
*/
|
|
6
|
+
export declare class LoanDeviceNotifyItem {
|
|
7
|
+
creditId: string;
|
|
8
|
+
intensity: DeviceNotifyIntensityEnum;
|
|
9
|
+
notificationCode: string;
|
|
10
|
+
title: string;
|
|
11
|
+
content: string;
|
|
12
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
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.LoanDeviceNotifyItem = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const DeviceNotifyIntensityEnum_1 = require("../../mdm/enums/DeviceNotifyIntensityEnum");
|
|
16
|
+
const CreditIdPattern_1 = require("../constants/CreditIdPattern");
|
|
17
|
+
/**
|
|
18
|
+
* Ítem de notificación: a qué crédito se le avisa y con qué texto.
|
|
19
|
+
* Los tres campos de texto son obligatorios: Datacultr usa el código y Trustonic el texto crudo.
|
|
20
|
+
*/
|
|
21
|
+
class LoanDeviceNotifyItem {
|
|
22
|
+
}
|
|
23
|
+
exports.LoanDeviceNotifyItem = LoanDeviceNotifyItem;
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, class_transformer_1.Expose)(),
|
|
26
|
+
(0, class_validator_1.Matches)(CreditIdPattern_1.CREDIT_ID_PATTERN, { message: CreditIdPattern_1.CREDIT_ID_MESSAGE }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], LoanDeviceNotifyItem.prototype, "creditId", void 0);
|
|
29
|
+
__decorate([
|
|
30
|
+
(0, class_transformer_1.Expose)(),
|
|
31
|
+
(0, class_validator_1.IsEnum)(DeviceNotifyIntensityEnum_1.DeviceNotifyIntensityEnum),
|
|
32
|
+
__metadata("design:type", String)
|
|
33
|
+
], LoanDeviceNotifyItem.prototype, "intensity", void 0);
|
|
34
|
+
__decorate([
|
|
35
|
+
(0, class_transformer_1.Expose)(),
|
|
36
|
+
(0, class_validator_1.IsString)(),
|
|
37
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
38
|
+
(0, class_validator_1.MaxLength)(64),
|
|
39
|
+
__metadata("design:type", String)
|
|
40
|
+
], LoanDeviceNotifyItem.prototype, "notificationCode", void 0);
|
|
41
|
+
__decorate([
|
|
42
|
+
(0, class_transformer_1.Expose)(),
|
|
43
|
+
(0, class_validator_1.IsString)(),
|
|
44
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
45
|
+
(0, class_validator_1.MaxLength)(128),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], LoanDeviceNotifyItem.prototype, "title", void 0);
|
|
48
|
+
__decorate([
|
|
49
|
+
(0, class_transformer_1.Expose)(),
|
|
50
|
+
(0, class_validator_1.IsString)(),
|
|
51
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
52
|
+
(0, class_validator_1.MaxLength)(1024),
|
|
53
|
+
__metadata("design:type", String)
|
|
54
|
+
], LoanDeviceNotifyItem.prototype, "content", void 0);
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { DeviceReleaseReasonEnum } from '../../mdm/enums/DeviceReleaseReasonEnum';
|
|
2
|
+
/** Ítem de liberación definitiva: qué crédito se libera del MDM y por qué. */
|
|
3
|
+
export declare class LoanDeviceReleaseItem {
|
|
4
|
+
creditId: string;
|
|
5
|
+
reason: DeviceReleaseReasonEnum;
|
|
6
|
+
/** Nota libre del operador; queda en el expediente del dispositivo. */
|
|
7
|
+
comment?: string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.LoanDeviceReleaseItem = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const DeviceReleaseReasonEnum_1 = require("../../mdm/enums/DeviceReleaseReasonEnum");
|
|
16
|
+
const CreditIdPattern_1 = require("../constants/CreditIdPattern");
|
|
17
|
+
/** Ítem de liberación definitiva: qué crédito se libera del MDM y por qué. */
|
|
18
|
+
class LoanDeviceReleaseItem {
|
|
19
|
+
}
|
|
20
|
+
exports.LoanDeviceReleaseItem = LoanDeviceReleaseItem;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_transformer_1.Expose)(),
|
|
23
|
+
(0, class_validator_1.Matches)(CreditIdPattern_1.CREDIT_ID_PATTERN, { message: CreditIdPattern_1.CREDIT_ID_MESSAGE }),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], LoanDeviceReleaseItem.prototype, "creditId", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, class_transformer_1.Expose)(),
|
|
28
|
+
(0, class_validator_1.IsEnum)(DeviceReleaseReasonEnum_1.DeviceReleaseReasonEnum),
|
|
29
|
+
__metadata("design:type", String)
|
|
30
|
+
], LoanDeviceReleaseItem.prototype, "reason", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_transformer_1.Expose)(),
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
(0, class_validator_1.IsOptional)(),
|
|
35
|
+
(0, class_validator_1.MaxLength)(500),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], LoanDeviceReleaseItem.prototype, "comment", void 0);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Ítem de desbloqueo: qué crédito se desbloquea y, opcionalmente, hasta cuándo. */
|
|
2
|
+
export declare class LoanDeviceUnlockItem {
|
|
3
|
+
creditId: string;
|
|
4
|
+
/** Datetime ISO-8601 futuro de hasta cuándo vale el desbloqueo; solo aplica en modo SCHEDULED. */
|
|
5
|
+
unlockUntil?: string;
|
|
6
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
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.LoanDeviceUnlockItem = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const CreditIdPattern_1 = require("../constants/CreditIdPattern");
|
|
16
|
+
const IsFutureIso8601DateTime_1 = require("../validators/IsFutureIso8601DateTime");
|
|
17
|
+
/** Ítem de desbloqueo: qué crédito se desbloquea y, opcionalmente, hasta cuándo. */
|
|
18
|
+
class LoanDeviceUnlockItem {
|
|
19
|
+
}
|
|
20
|
+
exports.LoanDeviceUnlockItem = LoanDeviceUnlockItem;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_transformer_1.Expose)(),
|
|
23
|
+
(0, class_validator_1.Matches)(CreditIdPattern_1.CREDIT_ID_PATTERN, { message: CreditIdPattern_1.CREDIT_ID_MESSAGE }),
|
|
24
|
+
__metadata("design:type", String)
|
|
25
|
+
], LoanDeviceUnlockItem.prototype, "creditId", void 0);
|
|
26
|
+
__decorate([
|
|
27
|
+
(0, class_transformer_1.Expose)(),
|
|
28
|
+
(0, class_validator_1.Validate)(IsFutureIso8601DateTime_1.IsFutureIso8601DateTime),
|
|
29
|
+
(0, class_validator_1.IsOptional)(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], LoanDeviceUnlockItem.prototype, "unlockUntil", void 0);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LoanDeviceUnlockItem } from '../LoanDeviceUnlockItem';
|
|
2
2
|
/** Body de `POST /private/devices/manual-unlock` (loan-credit-business). Desbloquea en lote. */
|
|
3
3
|
export declare class LoanDeviceManualUnlockRequest {
|
|
4
|
-
items:
|
|
4
|
+
items: LoanDeviceUnlockItem[];
|
|
5
5
|
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
6
6
|
operationReference: string;
|
|
7
7
|
}
|
|
@@ -12,7 +12,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
12
12
|
exports.LoanDeviceManualUnlockRequest = void 0;
|
|
13
13
|
const class_transformer_1 = require("class-transformer");
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
|
-
const
|
|
15
|
+
const LoanDeviceUnlockItem_1 = require("../LoanDeviceUnlockItem");
|
|
16
16
|
const LoanDeviceBatchLimits_1 = require("../../constants/LoanDeviceBatchLimits");
|
|
17
17
|
/** Body de `POST /private/devices/manual-unlock` (loan-credit-business). Desbloquea en lote. */
|
|
18
18
|
class LoanDeviceManualUnlockRequest {
|
|
@@ -25,7 +25,7 @@ __decorate([
|
|
|
25
25
|
(0, class_validator_1.ArrayMaxSize)(LoanDeviceBatchLimits_1.LOAN_DEVICE_MAX_BATCH),
|
|
26
26
|
(0, class_validator_1.ArrayUnique)((item) => item.creditId),
|
|
27
27
|
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
28
|
-
(0, class_transformer_1.Type)(() =>
|
|
28
|
+
(0, class_transformer_1.Type)(() => LoanDeviceUnlockItem_1.LoanDeviceUnlockItem),
|
|
29
29
|
__metadata("design:type", Array)
|
|
30
30
|
], LoanDeviceManualUnlockRequest.prototype, "items", void 0);
|
|
31
31
|
__decorate([
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LoanDeviceNotifyItem } from '../LoanDeviceNotifyItem';
|
|
2
|
+
/** Body de `POST /private/devices/notify` (loan-credit-business). Notifica en lote. */
|
|
3
|
+
export declare class LoanDeviceNotifyRequest {
|
|
4
|
+
items: LoanDeviceNotifyItem[];
|
|
5
|
+
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
6
|
+
operationReference: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.LoanDeviceNotifyRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const LoanDeviceNotifyItem_1 = require("../LoanDeviceNotifyItem");
|
|
16
|
+
const LoanDeviceBatchLimits_1 = require("../../constants/LoanDeviceBatchLimits");
|
|
17
|
+
/** Body de `POST /private/devices/notify` (loan-credit-business). Notifica en lote. */
|
|
18
|
+
class LoanDeviceNotifyRequest {
|
|
19
|
+
}
|
|
20
|
+
exports.LoanDeviceNotifyRequest = LoanDeviceNotifyRequest;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_transformer_1.Expose)(),
|
|
23
|
+
(0, class_validator_1.IsArray)(),
|
|
24
|
+
(0, class_validator_1.ArrayNotEmpty)(),
|
|
25
|
+
(0, class_validator_1.ArrayMaxSize)(LoanDeviceBatchLimits_1.LOAN_DEVICE_MAX_BATCH),
|
|
26
|
+
(0, class_validator_1.ArrayUnique)((item) => item.creditId),
|
|
27
|
+
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
28
|
+
(0, class_transformer_1.Type)(() => LoanDeviceNotifyItem_1.LoanDeviceNotifyItem),
|
|
29
|
+
__metadata("design:type", Array)
|
|
30
|
+
], LoanDeviceNotifyRequest.prototype, "items", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_transformer_1.Expose)(),
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
35
|
+
(0, class_validator_1.Length)(1, 64),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], LoanDeviceNotifyRequest.prototype, "operationReference", void 0);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { LoanDeviceReleaseItem } from '../LoanDeviceReleaseItem';
|
|
2
|
+
/** Body de `POST /private/devices/release` (loan-credit-business). Libera en lote. */
|
|
3
|
+
export declare class LoanDeviceReleaseRequest {
|
|
4
|
+
items: LoanDeviceReleaseItem[];
|
|
5
|
+
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
6
|
+
operationReference: string;
|
|
7
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
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.LoanDeviceReleaseRequest = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
const LoanDeviceReleaseItem_1 = require("../LoanDeviceReleaseItem");
|
|
16
|
+
const LoanDeviceBatchLimits_1 = require("../../constants/LoanDeviceBatchLimits");
|
|
17
|
+
/** Body de `POST /private/devices/release` (loan-credit-business). Libera en lote. */
|
|
18
|
+
class LoanDeviceReleaseRequest {
|
|
19
|
+
}
|
|
20
|
+
exports.LoanDeviceReleaseRequest = LoanDeviceReleaseRequest;
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, class_transformer_1.Expose)(),
|
|
23
|
+
(0, class_validator_1.IsArray)(),
|
|
24
|
+
(0, class_validator_1.ArrayNotEmpty)(),
|
|
25
|
+
(0, class_validator_1.ArrayMaxSize)(LoanDeviceBatchLimits_1.LOAN_DEVICE_MAX_BATCH),
|
|
26
|
+
(0, class_validator_1.ArrayUnique)((item) => item.creditId),
|
|
27
|
+
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
28
|
+
(0, class_transformer_1.Type)(() => LoanDeviceReleaseItem_1.LoanDeviceReleaseItem),
|
|
29
|
+
__metadata("design:type", Array)
|
|
30
|
+
], LoanDeviceReleaseRequest.prototype, "items", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
(0, class_transformer_1.Expose)(),
|
|
33
|
+
(0, class_validator_1.IsString)(),
|
|
34
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
35
|
+
(0, class_validator_1.Length)(1, 64),
|
|
36
|
+
__metadata("design:type", String)
|
|
37
|
+
], LoanDeviceReleaseRequest.prototype, "operationReference", void 0);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoanDeviceNotifyResponse = void 0;
|
|
4
|
+
/** Response de `POST /private/devices/notify`. Un resultado por ítem enviado. */
|
|
5
|
+
class LoanDeviceNotifyResponse {
|
|
6
|
+
}
|
|
7
|
+
exports.LoanDeviceNotifyResponse = LoanDeviceNotifyResponse;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LoanDeviceOperationResult } from '../LoanDeviceOperationResult';
|
|
2
|
+
import { LoanDeviceMdmStatusEnum } from '../../enums/LoanDeviceMdmStatusEnum';
|
|
3
|
+
/** Resultado por crédito de la notificación en lote. */
|
|
4
|
+
export declare class LoanDeviceNotifyResult extends LoanDeviceOperationResult {
|
|
5
|
+
/** Estado nuestro del expediente después de intentar la notificación. */
|
|
6
|
+
mdmStatus: LoanDeviceMdmStatusEnum;
|
|
7
|
+
notifiedAt?: string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoanDeviceNotifyResult = void 0;
|
|
4
|
+
const LoanDeviceOperationResult_1 = require("../LoanDeviceOperationResult");
|
|
5
|
+
/** Resultado por crédito de la notificación en lote. */
|
|
6
|
+
class LoanDeviceNotifyResult extends LoanDeviceOperationResult_1.LoanDeviceOperationResult {
|
|
7
|
+
}
|
|
8
|
+
exports.LoanDeviceNotifyResult = LoanDeviceNotifyResult;
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoanDeviceReleaseResponse = void 0;
|
|
4
|
+
/** Response de `POST /private/devices/release`. Un resultado por ítem enviado. */
|
|
5
|
+
class LoanDeviceReleaseResponse {
|
|
6
|
+
}
|
|
7
|
+
exports.LoanDeviceReleaseResponse = LoanDeviceReleaseResponse;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LoanDeviceOperationResult } from '../LoanDeviceOperationResult';
|
|
2
|
+
import { LoanDeviceMdmStatusEnum } from '../../enums/LoanDeviceMdmStatusEnum';
|
|
3
|
+
/** Resultado por crédito de la liberación definitiva en lote. */
|
|
4
|
+
export declare class LoanDeviceReleaseResult extends LoanDeviceOperationResult {
|
|
5
|
+
/** Estado nuestro del expediente después de intentar la liberación. */
|
|
6
|
+
mdmStatus: LoanDeviceMdmStatusEnum;
|
|
7
|
+
releasedAt?: string;
|
|
8
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LoanDeviceReleaseResult = void 0;
|
|
4
|
+
const LoanDeviceOperationResult_1 = require("../LoanDeviceOperationResult");
|
|
5
|
+
/** Resultado por crédito de la liberación definitiva en lote. */
|
|
6
|
+
class LoanDeviceReleaseResult extends LoanDeviceOperationResult_1.LoanDeviceOperationResult {
|
|
7
|
+
}
|
|
8
|
+
exports.LoanDeviceReleaseResult = LoanDeviceReleaseResult;
|
|
@@ -33,6 +33,8 @@ export { MdmDeviceStatusEnum } from '../mdm/enums/MdmDeviceStatusEnum';
|
|
|
33
33
|
export { MdmOperationTypeEnum } from '../mdm/enums/MdmOperationTypeEnum';
|
|
34
34
|
export { MdmProviderEnum } from '../mdm/enums/MdmProviderEnum';
|
|
35
35
|
export { DeviceEnrollmentModeEnum } from '../mdm/enums/DeviceEnrollmentModeEnum';
|
|
36
|
+
export { DeviceNotifyIntensityEnum } from '../mdm/enums/DeviceNotifyIntensityEnum';
|
|
37
|
+
export { DeviceReleaseReasonEnum } from '../mdm/enums/DeviceReleaseReasonEnum';
|
|
36
38
|
export { MdmError } from '../mdm/dtos/MdmError';
|
|
37
39
|
export { DeviceLockScreenMessage } from '../mdm/dtos/DeviceLockScreenMessage';
|
|
38
40
|
export { MdmLockModeEnum } from '../retailCatalog/enums/MdmLockModeEnum';
|
|
@@ -42,12 +44,17 @@ export * from './dtos/LoanDeviceOperationResult';
|
|
|
42
44
|
export * from './dtos/LoanDeviceCreditRefItem';
|
|
43
45
|
export * from './dtos/LoanDeviceEnrollItem';
|
|
44
46
|
export * from './dtos/LoanDeviceManualLockItem';
|
|
47
|
+
export * from './dtos/LoanDeviceUnlockItem';
|
|
48
|
+
export * from './dtos/LoanDeviceNotifyItem';
|
|
49
|
+
export * from './dtos/LoanDeviceReleaseItem';
|
|
45
50
|
export * from './dtos/requests/LoanDeviceEnrollRequest';
|
|
46
51
|
export * from './dtos/requests/LoanDeviceEnrollStatusRequest';
|
|
47
52
|
export * from './dtos/requests/LoanDeviceStatusRequest';
|
|
48
53
|
export * from './dtos/requests/LoanDeviceLastSeenRequest';
|
|
49
54
|
export * from './dtos/requests/LoanDeviceManualLockRequest';
|
|
50
55
|
export * from './dtos/requests/LoanDeviceManualUnlockRequest';
|
|
56
|
+
export * from './dtos/requests/LoanDeviceNotifyRequest';
|
|
57
|
+
export * from './dtos/requests/LoanDeviceReleaseRequest';
|
|
51
58
|
export * from './dtos/responses/LoanDeviceEnrollResult';
|
|
52
59
|
export * from './dtos/responses/LoanDeviceEnrollResponse';
|
|
53
60
|
export * from './dtos/responses/LoanDeviceEnrollStatusResult';
|
|
@@ -61,3 +68,8 @@ export * from './dtos/responses/LoanDeviceManualLockResponse';
|
|
|
61
68
|
export * from './dtos/responses/LoanDevicePinUnlockResult';
|
|
62
69
|
export * from './dtos/responses/LoanDeviceUnlockResult';
|
|
63
70
|
export * from './dtos/responses/LoanDeviceManualUnlockResponse';
|
|
71
|
+
export * from './dtos/responses/LoanDeviceNotifyResult';
|
|
72
|
+
export * from './dtos/responses/LoanDeviceNotifyResponse';
|
|
73
|
+
export * from './dtos/responses/LoanDeviceReleaseResult';
|
|
74
|
+
export * from './dtos/responses/LoanDeviceReleaseResponse';
|
|
75
|
+
export * from './validators/IsFutureIso8601DateTime';
|
package/bin/loanCredit/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.MdmLockModeEnum = exports.DeviceLockScreenMessage = exports.MdmError = exports.DeviceEnrollmentModeEnum = exports.MdmProviderEnum = exports.MdmOperationTypeEnum = exports.MdmDeviceStatusEnum = exports.MdmOperationStatusEnum = exports.ClientLevelEnum = void 0;
|
|
17
|
+
exports.MdmLockModeEnum = exports.DeviceLockScreenMessage = exports.MdmError = exports.DeviceReleaseReasonEnum = exports.DeviceNotifyIntensityEnum = exports.DeviceEnrollmentModeEnum = exports.MdmProviderEnum = exports.MdmOperationTypeEnum = exports.MdmDeviceStatusEnum = exports.MdmOperationStatusEnum = exports.ClientLevelEnum = void 0;
|
|
18
18
|
// Enums
|
|
19
19
|
__exportStar(require("./enums/LoanCreditStatusEnum"), exports);
|
|
20
20
|
__exportStar(require("./enums/LoanInstallmentStatusEnum"), exports);
|
|
@@ -63,6 +63,10 @@ var MdmProviderEnum_1 = require("../mdm/enums/MdmProviderEnum");
|
|
|
63
63
|
Object.defineProperty(exports, "MdmProviderEnum", { enumerable: true, get: function () { return MdmProviderEnum_1.MdmProviderEnum; } });
|
|
64
64
|
var DeviceEnrollmentModeEnum_1 = require("../mdm/enums/DeviceEnrollmentModeEnum");
|
|
65
65
|
Object.defineProperty(exports, "DeviceEnrollmentModeEnum", { enumerable: true, get: function () { return DeviceEnrollmentModeEnum_1.DeviceEnrollmentModeEnum; } });
|
|
66
|
+
var DeviceNotifyIntensityEnum_1 = require("../mdm/enums/DeviceNotifyIntensityEnum");
|
|
67
|
+
Object.defineProperty(exports, "DeviceNotifyIntensityEnum", { enumerable: true, get: function () { return DeviceNotifyIntensityEnum_1.DeviceNotifyIntensityEnum; } });
|
|
68
|
+
var DeviceReleaseReasonEnum_1 = require("../mdm/enums/DeviceReleaseReasonEnum");
|
|
69
|
+
Object.defineProperty(exports, "DeviceReleaseReasonEnum", { enumerable: true, get: function () { return DeviceReleaseReasonEnum_1.DeviceReleaseReasonEnum; } });
|
|
66
70
|
var MdmError_1 = require("../mdm/dtos/MdmError");
|
|
67
71
|
Object.defineProperty(exports, "MdmError", { enumerable: true, get: function () { return MdmError_1.MdmError; } });
|
|
68
72
|
var DeviceLockScreenMessage_1 = require("../mdm/dtos/DeviceLockScreenMessage");
|
|
@@ -75,12 +79,17 @@ __exportStar(require("./dtos/LoanDeviceOperationResult"), exports);
|
|
|
75
79
|
__exportStar(require("./dtos/LoanDeviceCreditRefItem"), exports);
|
|
76
80
|
__exportStar(require("./dtos/LoanDeviceEnrollItem"), exports);
|
|
77
81
|
__exportStar(require("./dtos/LoanDeviceManualLockItem"), exports);
|
|
82
|
+
__exportStar(require("./dtos/LoanDeviceUnlockItem"), exports);
|
|
83
|
+
__exportStar(require("./dtos/LoanDeviceNotifyItem"), exports);
|
|
84
|
+
__exportStar(require("./dtos/LoanDeviceReleaseItem"), exports);
|
|
78
85
|
__exportStar(require("./dtos/requests/LoanDeviceEnrollRequest"), exports);
|
|
79
86
|
__exportStar(require("./dtos/requests/LoanDeviceEnrollStatusRequest"), exports);
|
|
80
87
|
__exportStar(require("./dtos/requests/LoanDeviceStatusRequest"), exports);
|
|
81
88
|
__exportStar(require("./dtos/requests/LoanDeviceLastSeenRequest"), exports);
|
|
82
89
|
__exportStar(require("./dtos/requests/LoanDeviceManualLockRequest"), exports);
|
|
83
90
|
__exportStar(require("./dtos/requests/LoanDeviceManualUnlockRequest"), exports);
|
|
91
|
+
__exportStar(require("./dtos/requests/LoanDeviceNotifyRequest"), exports);
|
|
92
|
+
__exportStar(require("./dtos/requests/LoanDeviceReleaseRequest"), exports);
|
|
84
93
|
__exportStar(require("./dtos/responses/LoanDeviceEnrollResult"), exports);
|
|
85
94
|
__exportStar(require("./dtos/responses/LoanDeviceEnrollResponse"), exports);
|
|
86
95
|
__exportStar(require("./dtos/responses/LoanDeviceEnrollStatusResult"), exports);
|
|
@@ -94,3 +103,8 @@ __exportStar(require("./dtos/responses/LoanDeviceManualLockResponse"), exports);
|
|
|
94
103
|
__exportStar(require("./dtos/responses/LoanDevicePinUnlockResult"), exports);
|
|
95
104
|
__exportStar(require("./dtos/responses/LoanDeviceUnlockResult"), exports);
|
|
96
105
|
__exportStar(require("./dtos/responses/LoanDeviceManualUnlockResponse"), exports);
|
|
106
|
+
__exportStar(require("./dtos/responses/LoanDeviceNotifyResult"), exports);
|
|
107
|
+
__exportStar(require("./dtos/responses/LoanDeviceNotifyResponse"), exports);
|
|
108
|
+
__exportStar(require("./dtos/responses/LoanDeviceReleaseResult"), exports);
|
|
109
|
+
__exportStar(require("./dtos/responses/LoanDeviceReleaseResponse"), exports);
|
|
110
|
+
__exportStar(require("./validators/IsFutureIso8601DateTime"), exports);
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ValidationArguments, ValidatorConstraintInterface } from 'class-validator';
|
|
2
|
+
/**
|
|
3
|
+
* Valida que el valor sea un datetime ISO-8601 con hora y zona, y posterior al momento actual.
|
|
4
|
+
*
|
|
5
|
+
* Por qué no alcanza `@IsISO8601()`:
|
|
6
|
+
* - acepta el date-only (`"2026-09-30"`), que no fija un instante y el proveedor MDM necesita uno;
|
|
7
|
+
* - acepta fechas pasadas, y un corte "hasta ayer" es un no-op silencioso.
|
|
8
|
+
*
|
|
9
|
+
* Uso típico:
|
|
10
|
+
* ```ts
|
|
11
|
+
* @Validate(IsFutureIso8601DateTime)
|
|
12
|
+
* unlockUntil?: string;
|
|
13
|
+
* ```
|
|
14
|
+
*/
|
|
15
|
+
export declare class IsFutureIso8601DateTime implements ValidatorConstraintInterface {
|
|
16
|
+
validate(value: unknown, _args: ValidationArguments): boolean;
|
|
17
|
+
defaultMessage(args: ValidationArguments): string;
|
|
18
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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.IsFutureIso8601DateTime = void 0;
|
|
10
|
+
const class_validator_1 = require("class-validator");
|
|
11
|
+
/** Datetime ISO-8601 completo: fecha, hora y zona (`Z` u offset). Rechaza el date-only. */
|
|
12
|
+
const ISO_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
13
|
+
/**
|
|
14
|
+
* Valida que el valor sea un datetime ISO-8601 con hora y zona, y posterior al momento actual.
|
|
15
|
+
*
|
|
16
|
+
* Por qué no alcanza `@IsISO8601()`:
|
|
17
|
+
* - acepta el date-only (`"2026-09-30"`), que no fija un instante y el proveedor MDM necesita uno;
|
|
18
|
+
* - acepta fechas pasadas, y un corte "hasta ayer" es un no-op silencioso.
|
|
19
|
+
*
|
|
20
|
+
* Uso típico:
|
|
21
|
+
* ```ts
|
|
22
|
+
* @Validate(IsFutureIso8601DateTime)
|
|
23
|
+
* unlockUntil?: string;
|
|
24
|
+
* ```
|
|
25
|
+
*/
|
|
26
|
+
let IsFutureIso8601DateTime = class IsFutureIso8601DateTime {
|
|
27
|
+
validate(value, _args) {
|
|
28
|
+
if (typeof value !== 'string' || !ISO_DATETIME_PATTERN.test(value)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
const instant = Date.parse(value);
|
|
32
|
+
return !Number.isNaN(instant) && instant > Date.now();
|
|
33
|
+
}
|
|
34
|
+
defaultMessage(args) {
|
|
35
|
+
return `${args.property} debe ser un datetime ISO-8601 con hora y zona, y posterior al momento actual`;
|
|
36
|
+
}
|
|
37
|
+
};
|
|
38
|
+
exports.IsFutureIso8601DateTime = IsFutureIso8601DateTime;
|
|
39
|
+
exports.IsFutureIso8601DateTime = IsFutureIso8601DateTime = __decorate([
|
|
40
|
+
(0, class_validator_1.ValidatorConstraint)({ name: 'isFutureIso8601DateTime', async: false })
|
|
41
|
+
], IsFutureIso8601DateTime);
|
package/package.json
CHANGED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsEnum, IsNotEmpty, IsString, Matches, MaxLength } from 'class-validator';
|
|
3
|
+
import { DeviceNotifyIntensityEnum } from '../../mdm/enums/DeviceNotifyIntensityEnum';
|
|
4
|
+
import { CREDIT_ID_MESSAGE, CREDIT_ID_PATTERN } from '../constants/CreditIdPattern';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Ítem de notificación: a qué crédito se le avisa y con qué texto.
|
|
8
|
+
* Los tres campos de texto son obligatorios: Datacultr usa el código y Trustonic el texto crudo.
|
|
9
|
+
*/
|
|
10
|
+
export class LoanDeviceNotifyItem {
|
|
11
|
+
@Expose()
|
|
12
|
+
@Matches(CREDIT_ID_PATTERN, { message: CREDIT_ID_MESSAGE })
|
|
13
|
+
creditId: string;
|
|
14
|
+
|
|
15
|
+
@Expose()
|
|
16
|
+
@IsEnum(DeviceNotifyIntensityEnum)
|
|
17
|
+
intensity: DeviceNotifyIntensityEnum;
|
|
18
|
+
|
|
19
|
+
@Expose()
|
|
20
|
+
@IsString()
|
|
21
|
+
@IsNotEmpty()
|
|
22
|
+
@MaxLength(64)
|
|
23
|
+
notificationCode: string;
|
|
24
|
+
|
|
25
|
+
@Expose()
|
|
26
|
+
@IsString()
|
|
27
|
+
@IsNotEmpty()
|
|
28
|
+
@MaxLength(128)
|
|
29
|
+
title: string;
|
|
30
|
+
|
|
31
|
+
@Expose()
|
|
32
|
+
@IsString()
|
|
33
|
+
@IsNotEmpty()
|
|
34
|
+
@MaxLength(1024)
|
|
35
|
+
content: string;
|
|
36
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsEnum, IsOptional, IsString, Matches, MaxLength } from 'class-validator';
|
|
3
|
+
import { DeviceReleaseReasonEnum } from '../../mdm/enums/DeviceReleaseReasonEnum';
|
|
4
|
+
import { CREDIT_ID_MESSAGE, CREDIT_ID_PATTERN } from '../constants/CreditIdPattern';
|
|
5
|
+
|
|
6
|
+
/** Ítem de liberación definitiva: qué crédito se libera del MDM y por qué. */
|
|
7
|
+
export class LoanDeviceReleaseItem {
|
|
8
|
+
@Expose()
|
|
9
|
+
@Matches(CREDIT_ID_PATTERN, { message: CREDIT_ID_MESSAGE })
|
|
10
|
+
creditId: string;
|
|
11
|
+
|
|
12
|
+
@Expose()
|
|
13
|
+
@IsEnum(DeviceReleaseReasonEnum)
|
|
14
|
+
reason: DeviceReleaseReasonEnum;
|
|
15
|
+
|
|
16
|
+
/** Nota libre del operador; queda en el expediente del dispositivo. */
|
|
17
|
+
@Expose()
|
|
18
|
+
@IsString()
|
|
19
|
+
@IsOptional()
|
|
20
|
+
@MaxLength(500)
|
|
21
|
+
comment?: string;
|
|
22
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Expose } from 'class-transformer';
|
|
2
|
+
import { IsOptional, Matches, Validate } from 'class-validator';
|
|
3
|
+
import { CREDIT_ID_MESSAGE, CREDIT_ID_PATTERN } from '../constants/CreditIdPattern';
|
|
4
|
+
import { IsFutureIso8601DateTime } from '../validators/IsFutureIso8601DateTime';
|
|
5
|
+
|
|
6
|
+
/** Ítem de desbloqueo: qué crédito se desbloquea y, opcionalmente, hasta cuándo. */
|
|
7
|
+
export class LoanDeviceUnlockItem {
|
|
8
|
+
@Expose()
|
|
9
|
+
@Matches(CREDIT_ID_PATTERN, { message: CREDIT_ID_MESSAGE })
|
|
10
|
+
creditId: string;
|
|
11
|
+
|
|
12
|
+
/** Datetime ISO-8601 futuro de hasta cuándo vale el desbloqueo; solo aplica en modo SCHEDULED. */
|
|
13
|
+
@Expose()
|
|
14
|
+
@Validate(IsFutureIso8601DateTime)
|
|
15
|
+
@IsOptional()
|
|
16
|
+
unlockUntil?: string;
|
|
17
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Expose, Type } from 'class-transformer';
|
|
2
2
|
import { ArrayMaxSize, ArrayNotEmpty, ArrayUnique, IsArray, IsNotEmpty, IsString, Length, ValidateNested } from 'class-validator';
|
|
3
|
-
import {
|
|
3
|
+
import { LoanDeviceUnlockItem } from '../LoanDeviceUnlockItem';
|
|
4
4
|
import { LOAN_DEVICE_MAX_BATCH } from '../../constants/LoanDeviceBatchLimits';
|
|
5
5
|
|
|
6
6
|
/** Body de `POST /private/devices/manual-unlock` (loan-credit-business). Desbloquea en lote. */
|
|
@@ -9,10 +9,10 @@ export class LoanDeviceManualUnlockRequest {
|
|
|
9
9
|
@IsArray()
|
|
10
10
|
@ArrayNotEmpty()
|
|
11
11
|
@ArrayMaxSize(LOAN_DEVICE_MAX_BATCH)
|
|
12
|
-
@ArrayUnique((item:
|
|
12
|
+
@ArrayUnique((item: LoanDeviceUnlockItem) => item.creditId)
|
|
13
13
|
@ValidateNested({ each: true })
|
|
14
|
-
@Type(() =>
|
|
15
|
-
items:
|
|
14
|
+
@Type(() => LoanDeviceUnlockItem)
|
|
15
|
+
items: LoanDeviceUnlockItem[];
|
|
16
16
|
|
|
17
17
|
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
18
18
|
@Expose()
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { ArrayMaxSize, ArrayNotEmpty, ArrayUnique, IsArray, IsNotEmpty, IsString, Length, ValidateNested } from 'class-validator';
|
|
3
|
+
import { LoanDeviceNotifyItem } from '../LoanDeviceNotifyItem';
|
|
4
|
+
import { LOAN_DEVICE_MAX_BATCH } from '../../constants/LoanDeviceBatchLimits';
|
|
5
|
+
|
|
6
|
+
/** Body de `POST /private/devices/notify` (loan-credit-business). Notifica en lote. */
|
|
7
|
+
export class LoanDeviceNotifyRequest {
|
|
8
|
+
@Expose()
|
|
9
|
+
@IsArray()
|
|
10
|
+
@ArrayNotEmpty()
|
|
11
|
+
@ArrayMaxSize(LOAN_DEVICE_MAX_BATCH)
|
|
12
|
+
@ArrayUnique((item: LoanDeviceNotifyItem) => item.creditId)
|
|
13
|
+
@ValidateNested({ each: true })
|
|
14
|
+
@Type(() => LoanDeviceNotifyItem)
|
|
15
|
+
items: LoanDeviceNotifyItem[];
|
|
16
|
+
|
|
17
|
+
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
18
|
+
@Expose()
|
|
19
|
+
@IsString()
|
|
20
|
+
@IsNotEmpty()
|
|
21
|
+
@Length(1, 64)
|
|
22
|
+
operationReference: string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { Expose, Type } from 'class-transformer';
|
|
2
|
+
import { ArrayMaxSize, ArrayNotEmpty, ArrayUnique, IsArray, IsNotEmpty, IsString, Length, ValidateNested } from 'class-validator';
|
|
3
|
+
import { LoanDeviceReleaseItem } from '../LoanDeviceReleaseItem';
|
|
4
|
+
import { LOAN_DEVICE_MAX_BATCH } from '../../constants/LoanDeviceBatchLimits';
|
|
5
|
+
|
|
6
|
+
/** Body de `POST /private/devices/release` (loan-credit-business). Libera en lote. */
|
|
7
|
+
export class LoanDeviceReleaseRequest {
|
|
8
|
+
@Expose()
|
|
9
|
+
@IsArray()
|
|
10
|
+
@ArrayNotEmpty()
|
|
11
|
+
@ArrayMaxSize(LOAN_DEVICE_MAX_BATCH)
|
|
12
|
+
@ArrayUnique((item: LoanDeviceReleaseItem) => item.creditId)
|
|
13
|
+
@ValidateNested({ each: true })
|
|
14
|
+
@Type(() => LoanDeviceReleaseItem)
|
|
15
|
+
items: LoanDeviceReleaseItem[];
|
|
16
|
+
|
|
17
|
+
/** Referencia con la que el caller correlaciona este batch; vuelve en cada resultado. */
|
|
18
|
+
@Expose()
|
|
19
|
+
@IsString()
|
|
20
|
+
@IsNotEmpty()
|
|
21
|
+
@Length(1, 64)
|
|
22
|
+
operationReference: string;
|
|
23
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LoanDeviceOperationResult } from '../LoanDeviceOperationResult';
|
|
2
|
+
import { LoanDeviceMdmStatusEnum } from '../../enums/LoanDeviceMdmStatusEnum';
|
|
3
|
+
|
|
4
|
+
/** Resultado por crédito de la notificación en lote. */
|
|
5
|
+
export class LoanDeviceNotifyResult extends LoanDeviceOperationResult {
|
|
6
|
+
/** Estado nuestro del expediente después de intentar la notificación. */
|
|
7
|
+
mdmStatus: LoanDeviceMdmStatusEnum;
|
|
8
|
+
|
|
9
|
+
notifiedAt?: string;
|
|
10
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { LoanDeviceOperationResult } from '../LoanDeviceOperationResult';
|
|
2
|
+
import { LoanDeviceMdmStatusEnum } from '../../enums/LoanDeviceMdmStatusEnum';
|
|
3
|
+
|
|
4
|
+
/** Resultado por crédito de la liberación definitiva en lote. */
|
|
5
|
+
export class LoanDeviceReleaseResult extends LoanDeviceOperationResult {
|
|
6
|
+
/** Estado nuestro del expediente después de intentar la liberación. */
|
|
7
|
+
mdmStatus: LoanDeviceMdmStatusEnum;
|
|
8
|
+
|
|
9
|
+
releasedAt?: string;
|
|
10
|
+
}
|
package/src/loanCredit/index.ts
CHANGED
|
@@ -44,6 +44,8 @@ export { MdmDeviceStatusEnum } from '../mdm/enums/MdmDeviceStatusEnum';
|
|
|
44
44
|
export { MdmOperationTypeEnum } from '../mdm/enums/MdmOperationTypeEnum';
|
|
45
45
|
export { MdmProviderEnum } from '../mdm/enums/MdmProviderEnum';
|
|
46
46
|
export { DeviceEnrollmentModeEnum } from '../mdm/enums/DeviceEnrollmentModeEnum';
|
|
47
|
+
export { DeviceNotifyIntensityEnum } from '../mdm/enums/DeviceNotifyIntensityEnum';
|
|
48
|
+
export { DeviceReleaseReasonEnum } from '../mdm/enums/DeviceReleaseReasonEnum';
|
|
47
49
|
export { MdmError } from '../mdm/dtos/MdmError';
|
|
48
50
|
export { DeviceLockScreenMessage } from '../mdm/dtos/DeviceLockScreenMessage';
|
|
49
51
|
export { MdmLockModeEnum } from '../retailCatalog/enums/MdmLockModeEnum';
|
|
@@ -55,6 +57,9 @@ export * from './dtos/LoanDeviceOperationResult';
|
|
|
55
57
|
export * from './dtos/LoanDeviceCreditRefItem';
|
|
56
58
|
export * from './dtos/LoanDeviceEnrollItem';
|
|
57
59
|
export * from './dtos/LoanDeviceManualLockItem';
|
|
60
|
+
export * from './dtos/LoanDeviceUnlockItem';
|
|
61
|
+
export * from './dtos/LoanDeviceNotifyItem';
|
|
62
|
+
export * from './dtos/LoanDeviceReleaseItem';
|
|
58
63
|
|
|
59
64
|
export * from './dtos/requests/LoanDeviceEnrollRequest';
|
|
60
65
|
export * from './dtos/requests/LoanDeviceEnrollStatusRequest';
|
|
@@ -62,6 +67,8 @@ export * from './dtos/requests/LoanDeviceStatusRequest';
|
|
|
62
67
|
export * from './dtos/requests/LoanDeviceLastSeenRequest';
|
|
63
68
|
export * from './dtos/requests/LoanDeviceManualLockRequest';
|
|
64
69
|
export * from './dtos/requests/LoanDeviceManualUnlockRequest';
|
|
70
|
+
export * from './dtos/requests/LoanDeviceNotifyRequest';
|
|
71
|
+
export * from './dtos/requests/LoanDeviceReleaseRequest';
|
|
65
72
|
|
|
66
73
|
export * from './dtos/responses/LoanDeviceEnrollResult';
|
|
67
74
|
export * from './dtos/responses/LoanDeviceEnrollResponse';
|
|
@@ -76,3 +83,9 @@ export * from './dtos/responses/LoanDeviceManualLockResponse';
|
|
|
76
83
|
export * from './dtos/responses/LoanDevicePinUnlockResult';
|
|
77
84
|
export * from './dtos/responses/LoanDeviceUnlockResult';
|
|
78
85
|
export * from './dtos/responses/LoanDeviceManualUnlockResponse';
|
|
86
|
+
export * from './dtos/responses/LoanDeviceNotifyResult';
|
|
87
|
+
export * from './dtos/responses/LoanDeviceNotifyResponse';
|
|
88
|
+
export * from './dtos/responses/LoanDeviceReleaseResult';
|
|
89
|
+
export * from './dtos/responses/LoanDeviceReleaseResponse';
|
|
90
|
+
|
|
91
|
+
export * from './validators/IsFutureIso8601DateTime';
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { ValidatorConstraint, ValidationArguments, ValidatorConstraintInterface } from 'class-validator';
|
|
2
|
+
|
|
3
|
+
/** Datetime ISO-8601 completo: fecha, hora y zona (`Z` u offset). Rechaza el date-only. */
|
|
4
|
+
const ISO_DATETIME_PATTERN = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d{1,3})?)?(Z|[+-]\d{2}:\d{2})$/;
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Valida que el valor sea un datetime ISO-8601 con hora y zona, y posterior al momento actual.
|
|
8
|
+
*
|
|
9
|
+
* Por qué no alcanza `@IsISO8601()`:
|
|
10
|
+
* - acepta el date-only (`"2026-09-30"`), que no fija un instante y el proveedor MDM necesita uno;
|
|
11
|
+
* - acepta fechas pasadas, y un corte "hasta ayer" es un no-op silencioso.
|
|
12
|
+
*
|
|
13
|
+
* Uso típico:
|
|
14
|
+
* ```ts
|
|
15
|
+
* @Validate(IsFutureIso8601DateTime)
|
|
16
|
+
* unlockUntil?: string;
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
@ValidatorConstraint({ name: 'isFutureIso8601DateTime', async: false })
|
|
20
|
+
export class IsFutureIso8601DateTime implements ValidatorConstraintInterface {
|
|
21
|
+
validate(value: unknown, _args: ValidationArguments): boolean {
|
|
22
|
+
if (typeof value !== 'string' || !ISO_DATETIME_PATTERN.test(value)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const instant = Date.parse(value);
|
|
26
|
+
return !Number.isNaN(instant) && instant > Date.now();
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
defaultMessage(args: ValidationArguments): string {
|
|
30
|
+
return `${args.property} debe ser un datetime ISO-8601 con hora y zona, y posterior al momento actual`;
|
|
31
|
+
}
|
|
32
|
+
}
|