@fiado/type-kit 3.584.0 → 3.586.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.
Files changed (84) hide show
  1. package/_test_/unit/collection/disburseRequest.test.ts +10 -0
  2. package/_test_/unit/collection/disbursementType.test.ts +7 -2
  3. package/_test_/unit/platformRbac/dtos/UpdateTenantUserEmailRequest.test.ts +24 -0
  4. package/_test_/unit/platformRbac/enums/__snapshots__/permissionBits.test.ts.snap +2 -1
  5. package/_test_/unit/platformRbac/enums/apiUserPermission.test.ts +3 -2
  6. package/_test_/unit/platformRbac/enums/retailIntegrationPermission.test.ts +13 -0
  7. package/_test_/unit/productCatalog/rewardEnums.test.ts +19 -0
  8. package/_test_/unit/retailIntegration/reconciliationContracts.test.ts +3 -3
  9. package/_test_/unit/retailIntegration/retailMovementsContracts.test.ts +109 -0
  10. package/_test_/unit/retailIntegration/retailSystemResponses.test.ts +120 -0
  11. package/_test_/unit/rewards/enums.test.ts +1 -1
  12. package/_test_/unit/rewards/waitingLimit.test.ts +43 -0
  13. package/bin/collection/enums/DisbursementType.d.ts +3 -1
  14. package/bin/collection/enums/DisbursementType.js +2 -0
  15. package/bin/platformRbac/dtos/CreateTenantUserRequest.d.ts +2 -1
  16. package/bin/platformRbac/dtos/CreateTenantUserRequest.js +2 -1
  17. package/bin/platformRbac/dtos/UpdateTenantUserEmailRequest.d.ts +5 -0
  18. package/bin/platformRbac/dtos/UpdateTenantUserEmailRequest.js +30 -0
  19. package/bin/platformRbac/enums/Permission.d.ts +2 -1
  20. package/bin/platformRbac/enums/Permission.js +3 -0
  21. package/bin/platformRbac/index.d.ts +1 -0
  22. package/bin/platformRbac/index.js +1 -0
  23. package/bin/productCatalog/enums/ProductSubtypeEnum.d.ts +1 -0
  24. package/bin/productCatalog/enums/ProductSubtypeEnum.js +3 -0
  25. package/bin/productCatalog/enums/ProductTypeEnum.d.ts +3 -1
  26. package/bin/productCatalog/enums/ProductTypeEnum.js +2 -0
  27. package/bin/retailIntegration/constants/retailIntegrationLimits.d.ts +11 -0
  28. package/bin/retailIntegration/constants/retailIntegrationLimits.js +12 -1
  29. package/bin/retailIntegration/dtos/CheckRetailInventoryRequest.d.ts +4 -1
  30. package/bin/retailIntegration/dtos/CheckRetailInventoryRequest.js +4 -1
  31. package/bin/retailIntegration/dtos/GetRetailInventoryRequest.d.ts +3 -3
  32. package/bin/retailIntegration/dtos/GetRetailInventoryRequest.js +3 -3
  33. package/bin/retailIntegration/dtos/ListRetailInventoryMovementsRequest.d.ts +6 -0
  34. package/bin/retailIntegration/dtos/ListRetailInventoryMovementsRequest.js +29 -0
  35. package/bin/retailIntegration/dtos/ReconciliationPageMessage.d.ts +6 -0
  36. package/bin/retailIntegration/dtos/ReconciliationPageMessage.js +20 -0
  37. package/bin/retailIntegration/dtos/RetailOutboundResponses.d.ts +10 -2
  38. package/bin/retailIntegration/dtos/RetailSystemResponses.d.ts +41 -0
  39. package/bin/retailIntegration/dtos/RetailSystemResponses.js +136 -0
  40. package/bin/retailIntegration/enums/ReconciliationEntityEnum.d.ts +2 -1
  41. package/bin/retailIntegration/enums/ReconciliationEntityEnum.js +1 -0
  42. package/bin/retailIntegration/enums/ReconciliationTriggerEnum.d.ts +4 -2
  43. package/bin/retailIntegration/enums/ReconciliationTriggerEnum.js +3 -1
  44. package/bin/retailIntegration/index.d.ts +3 -0
  45. package/bin/retailIntegration/index.js +3 -0
  46. package/bin/retailIntegration/models/RetailPageMeta.d.ts +3 -1
  47. package/bin/retailIntegration/models/RetailSystemInventory.d.ts +9 -1
  48. package/bin/retailIntegration/validators/IsMovementsRangeValid.d.ts +6 -0
  49. package/bin/retailIntegration/validators/IsMovementsRangeValid.js +28 -0
  50. package/bin/rewards/dtos/backoffice/BackofficeGrantItemResponse.d.ts +9 -0
  51. package/bin/rewards/enums/GrantItemStatusEnum.d.ts +2 -0
  52. package/bin/rewards/enums/GrantItemStatusEnum.js +2 -0
  53. package/bin/rewards/enums/LimitWaitReasonEnum.d.ts +7 -0
  54. package/bin/rewards/enums/LimitWaitReasonEnum.js +11 -0
  55. package/bin/rewards/enums/RewardItemCodeEnum.d.ts +9 -0
  56. package/bin/rewards/enums/RewardItemCodeEnum.js +13 -0
  57. package/bin/rewards/index.d.ts +2 -0
  58. package/bin/rewards/index.js +2 -0
  59. package/package.json +1 -1
  60. package/src/collection/enums/DisbursementType.ts +2 -0
  61. package/src/platformRbac/dtos/CreateTenantUserRequest.ts +2 -1
  62. package/src/platformRbac/dtos/UpdateTenantUserEmailRequest.ts +12 -0
  63. package/src/platformRbac/enums/Permission.ts +3 -0
  64. package/src/platformRbac/index.ts +1 -0
  65. package/src/productCatalog/enums/ProductSubtypeEnum.ts +4 -0
  66. package/src/productCatalog/enums/ProductTypeEnum.ts +3 -1
  67. package/src/retailIntegration/constants/retailIntegrationLimits.ts +16 -0
  68. package/src/retailIntegration/dtos/CheckRetailInventoryRequest.ts +4 -1
  69. package/src/retailIntegration/dtos/GetRetailInventoryRequest.ts +3 -3
  70. package/src/retailIntegration/dtos/ListRetailInventoryMovementsRequest.ts +14 -0
  71. package/src/retailIntegration/dtos/ReconciliationPageMessage.ts +21 -1
  72. package/src/retailIntegration/dtos/RetailOutboundResponses.ts +11 -2
  73. package/src/retailIntegration/dtos/RetailSystemResponses.ts +75 -0
  74. package/src/retailIntegration/enums/ReconciliationEntityEnum.ts +1 -0
  75. package/src/retailIntegration/enums/ReconciliationTriggerEnum.ts +3 -1
  76. package/src/retailIntegration/index.ts +3 -0
  77. package/src/retailIntegration/models/RetailPageMeta.ts +3 -1
  78. package/src/retailIntegration/models/RetailSystemInventory.ts +10 -1
  79. package/src/retailIntegration/validators/IsMovementsRangeValid.ts +17 -0
  80. package/src/rewards/dtos/backoffice/BackofficeGrantItemResponse.ts +9 -0
  81. package/src/rewards/enums/GrantItemStatusEnum.ts +2 -0
  82. package/src/rewards/enums/LimitWaitReasonEnum.ts +7 -0
  83. package/src/rewards/enums/RewardItemCodeEnum.ts +9 -0
  84. package/src/rewards/index.ts +2 -0
@@ -26,6 +26,16 @@ describe('DisburseRequest', () => {
26
26
  expect(await invalidProperties(VALID)).toEqual([]);
27
27
  });
28
28
 
29
+ it('acepta disbursementType MEX_REWARD', async () => {
30
+ expect(await invalidProperties({ ...VALID, disbursementType: 'MEX_REWARD' })).toEqual([]);
31
+ });
32
+
33
+ it('acepta una idempotencyKey de 80 caracteres (sin límite de largo)', async () => {
34
+ const key = ('RW-' + 'a'.repeat(32) + '-' + 'k'.repeat(28) + '-12').padEnd(80, 'x');
35
+ expect(key).toHaveLength(80);
36
+ expect(await invalidProperties({ ...VALID, disbursementType: 'MEX_REWARD', idempotencyKey: key })).toEqual([]);
37
+ });
38
+
29
39
  it('reference es opcional', async () => {
30
40
  const { reference: _omit, ...withoutReference } = VALID;
31
41
  expect(await invalidProperties(withoutReference)).toEqual([]);
@@ -3,8 +3,12 @@ import { Collection } from '../../../src/index';
3
3
  import { DisbursementType } from '../../../src/collection/index';
4
4
 
5
5
  describe('DisbursementType', () => {
6
- it('trae los productos de bono y comisión de vendedor', () => {
7
- expect(Object.values(DisbursementType)).toEqual(['MEX_BONUS', 'MEX_SELLER_COMMISSION']);
6
+ it('trae los productos de bono, comisión de vendedor y recompensa', () => {
7
+ expect(Object.values(DisbursementType)).toEqual(['MEX_BONUS', 'MEX_SELLER_COMMISSION', 'MEX_REWARD']);
8
+ });
9
+
10
+ it('MEX_REWARD vale MEX_REWARD', () => {
11
+ expect(DisbursementType.MEX_REWARD).toBe('MEX_REWARD');
8
12
  });
9
13
 
10
14
  it('cada clave es igual a su valor', () => {
@@ -16,5 +20,6 @@ describe('DisbursementType', () => {
16
20
  it('sale por el index raíz', () => {
17
21
  expect(Collection.DisbursementType.MEX_BONUS).toBe('MEX_BONUS');
18
22
  expect(Collection.DisbursementType.MEX_SELLER_COMMISSION).toBe('MEX_SELLER_COMMISSION');
23
+ expect(Collection.DisbursementType.MEX_REWARD).toBe('MEX_REWARD');
19
24
  });
20
25
  });
@@ -0,0 +1,24 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validateSync } from 'class-validator';
4
+ import { UpdateTenantUserEmailRequest } from '../../../../src/platformRbac';
5
+
6
+ function propsWithErrors(payload: object): string[] {
7
+ return validateSync(plainToInstance(UpdateTenantUserEmailRequest, payload)).map((e) => e.property);
8
+ }
9
+
10
+ describe('UpdateTenantUserEmailRequest', () => {
11
+ it('normaliza el correo a minúsculas sin espacios', () => {
12
+ const dto = plainToInstance(UpdateTenantUserEmailRequest, { email: ' Ana.Perez@Example.COM ', actedBy: 'retail-org-business' });
13
+ expect(dto.email).toBe('ana.perez@example.com');
14
+ expect(validateSync(dto)).toEqual([]);
15
+ });
16
+
17
+ it.each([undefined, '', 'no-es-correo', 42])('rechaza email=%p', (email) => {
18
+ expect(propsWithErrors({ email, actedBy: 'retail-org-business' })).toEqual(['email']);
19
+ });
20
+
21
+ it.each([undefined, '', 7])('rechaza actedBy=%p', (actedBy) => {
22
+ expect(propsWithErrors({ email: 'ana@example.com', actedBy })).toEqual(['actedBy']);
23
+ });
24
+ });
@@ -1,6 +1,6 @@
1
1
  // Jest Snapshot v1, https://jestjs.io/docs/snapshot-testing
2
2
 
3
- exports[`PERMISSION_BIT_ORDER PERMS_VERSION es número estable 1`] = `2686516275`;
3
+ exports[`PERMISSION_BIT_ORDER PERMS_VERSION es número estable 1`] = `1071810597`;
4
4
 
5
5
  exports[`PERMISSION_BIT_ORDER append-only: snapshot del ORDEN COMPLETO (rompe ante cualquier reorden/inserción) 1`] = `
6
6
  [
@@ -96,5 +96,6 @@ exports[`PERMISSION_BIT_ORDER append-only: snapshot del ORDEN COMPLETO (rompe an
96
96
  "platform.integration.key.revoke",
97
97
  "tenant.integration.diagnostic",
98
98
  "tenant.api_user.manage",
99
+ "platform.retail_integration.activate",
99
100
  ]
100
101
  `;
@@ -5,7 +5,8 @@ describe('Permiso de administración de usuarios de API', () => {
5
5
  expect(Permission.TENANT_API_USER_MANAGE).toBe('tenant.api_user.manage');
6
6
  });
7
7
 
8
- it('va al FINAL del orden de bits (append-only)', () => {
9
- expect(PERMISSION_BIT_ORDER[PERMISSION_BIT_ORDER.length - 1]).toBe(Permission.TENANT_API_USER_MANAGE);
8
+ it('conserva su bit después de las integraciones M2M (append-only)', () => {
9
+ const index = PERMISSION_BIT_ORDER.indexOf(Permission.TENANT_API_USER_MANAGE);
10
+ expect(PERMISSION_BIT_ORDER[index - 1]).toBe(Permission.TENANT_INTEGRATION_DIAGNOSTIC);
10
11
  });
11
12
  });
@@ -0,0 +1,13 @@
1
+ import { Permission, PERMISSION_BIT_ORDER } from '../../../../src/platformRbac/enums/Permission';
2
+
3
+ describe('Permiso de activación de la integración retail', () => {
4
+ it('existe con la llave platform.retail_integration.activate (scope PLATFORM)', () => {
5
+ expect(Permission.PLATFORM_RETAIL_INTEGRATION_ACTIVATE).toBe('platform.retail_integration.activate');
6
+ });
7
+
8
+ it('va al FINAL del orden de bits, justo después de tenant.api_user.manage', () => {
9
+ const last = PERMISSION_BIT_ORDER.length - 1;
10
+ expect(PERMISSION_BIT_ORDER[last]).toBe(Permission.PLATFORM_RETAIL_INTEGRATION_ACTIVATE);
11
+ expect(PERMISSION_BIT_ORDER[last - 1]).toBe(Permission.TENANT_API_USER_MANAGE);
12
+ });
13
+ });
@@ -0,0 +1,19 @@
1
+ import { ProductSubtypeEnum } from '../../../src/productCatalog/enums/ProductSubtypeEnum';
2
+ import { ProductTypeEnum } from '../../../src/productCatalog/enums/ProductTypeEnum';
3
+
4
+ describe('enums de recompensas del catálogo de productos', () => {
5
+ it('ProductSubtypeEnum.REWARD_MX vale "REWARD_MX"', () => {
6
+ expect(ProductSubtypeEnum.REWARD_MX).toBe('REWARD_MX');
7
+ });
8
+
9
+ it('ProductTypeEnum.REWARDS vale "REWARDS"', () => {
10
+ expect(ProductTypeEnum.REWARDS).toBe('REWARDS');
11
+ });
12
+
13
+ it('ningún otro miembro de ProductSubtypeEnum vale "REWARD_MX"', () => {
14
+ const colisiones = Object.entries(ProductSubtypeEnum).filter(
15
+ ([key, value]) => key !== 'REWARD_MX' && value === 'REWARD_MX',
16
+ );
17
+ expect(colisiones).toEqual([]);
18
+ });
19
+ });
@@ -20,8 +20,8 @@ function propsWithErrors<T extends object>(cls: ClassConstructor<T>, payload: ob
20
20
  }
21
21
 
22
22
  describe('enums de la revisión', () => {
23
- it('ReconciliationEntityEnum trae las cuatro entidades', () => {
24
- expect(Object.values(ReconciliationEntityEnum)).toEqual(['STORES', 'USERS', 'CATALOG', 'INVENTORY']);
23
+ it('ReconciliationEntityEnum trae las cinco entidades', () => {
24
+ expect(Object.values(ReconciliationEntityEnum)).toEqual(['STORES', 'USERS', 'CATALOG', 'INVENTORY', 'MOVEMENTS']);
25
25
  });
26
26
 
27
27
  it('ReconciliationRunStatusEnum trae los cuatro estados de la corrida', () => {
@@ -29,7 +29,7 @@ describe('enums de la revisión', () => {
29
29
  });
30
30
 
31
31
  it('ReconciliationTriggerEnum distingue el reloj del botón', () => {
32
- expect(Object.values(ReconciliationTriggerEnum)).toEqual(['SCHEDULED', 'MANUAL', 'ACTIVATION']);
32
+ expect(Object.values(ReconciliationTriggerEnum)).toEqual(['SCHEDULED', 'MANUAL', 'ACTIVATION', 'DISCREPANCY', 'DAILY']);
33
33
  });
34
34
 
35
35
  it('InactiveReasonEnum distingue la baja de la revisión de la del evento', () => {
@@ -0,0 +1,109 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync } from 'class-validator';
4
+ import {
5
+ GetRetailInventoryRequest,
6
+ type GetRetailInventoryResponse,
7
+ ListRetailInventoryMovementsRequest,
8
+ type ListRetailInventoryMovementsResponse,
9
+ ReconciliationEntityEnum,
10
+ ReconciliationPageMessage,
11
+ ReconciliationTriggerEnum,
12
+ } from '../../../src/retailIntegration';
13
+
14
+ function propsWithErrors<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
15
+ return validateSync(plainToInstance(cls, payload)).map((e) => e.property);
16
+ }
17
+
18
+ const tenant = { tenantId: 'surekeep', retailerId: 'ret-1' };
19
+
20
+ describe('ListRetailInventoryMovementsRequest', () => {
21
+ const valid = { ...tenant, from: '2026-08-17T19:55:00Z', to: '2026-08-17T21:00:00Z' };
22
+
23
+ it('acepta el rango con y sin paginación', () => {
24
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, valid)).toEqual([]);
25
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, { ...valid, limit: '200', nextToken: 'abc', runId: 'run-1' })).toEqual([]);
26
+ });
27
+
28
+ it('acepta un rango de 7 días exactos', () => {
29
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, { ...tenant, from: '2026-08-10T00:00:00Z', to: '2026-08-17T00:00:00Z' })).toEqual([]);
30
+ });
31
+
32
+ it.each([
33
+ ['to igual a from', '2026-08-17T20:00:00Z', '2026-08-17T20:00:00Z'],
34
+ ['to antes de from', '2026-08-17T20:00:00Z', '2026-08-17T19:00:00Z'],
35
+ ['más de 7 días', '2026-08-10T00:00:00Z', '2026-08-17T00:00:01Z'],
36
+ ])('rechaza %s', (_label, from, to) => {
37
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, { ...tenant, from, to })).toEqual(['to']);
38
+ });
39
+
40
+ it.each(['from', 'to'])('rechaza sin %s o con fecha sin zona', (field) => {
41
+ const payload: Record<string, unknown> = { ...valid };
42
+ delete payload[field];
43
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, payload)).toContain(field);
44
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, { ...valid, [field]: '2026-08-17T20:00:00' })).toContain(field);
45
+ });
46
+
47
+ it.each(['0', '201'])('rechaza limit=%p', (limit) => {
48
+ expect(propsWithErrors(ListRetailInventoryMovementsRequest, { ...valid, limit })).toEqual(['limit']);
49
+ });
50
+
51
+ it('la respuesta trae items y meta de página', () => {
52
+ const response: ListRetailInventoryMovementsResponse = {
53
+ items: [
54
+ {
55
+ eventId: 'evt-1',
56
+ eventType: 'InventoryMovementV1',
57
+ occurredAt: '2026-08-17T20:32:07Z',
58
+ payload: { movementType: 'SALE', storeId: '12', targetStoreId: null, pieces: [], stock: [] },
59
+ },
60
+ ],
61
+ meta: { count: 1, nextToken: null },
62
+ };
63
+ expect(response.meta.nextToken).toBeNull();
64
+ });
65
+ });
66
+
67
+ describe('GetRetailInventoryRequest paginado', () => {
68
+ it('acepta limit y nextToken opcionales', () => {
69
+ expect(propsWithErrors(GetRetailInventoryRequest, { ...tenant, storeId: '12', limit: '50', nextToken: 'abc' })).toEqual([]);
70
+ expect(propsWithErrors(GetRetailInventoryRequest, { ...tenant, storeId: '12', limit: '0' })).toEqual(['limit']);
71
+ });
72
+
73
+ it('la respuesta admite meta.count y meta.nextToken', () => {
74
+ const response: GetRetailInventoryResponse = {
75
+ pieces: [],
76
+ stock: [],
77
+ meta: { storeId: '12', generatedAt: '2026-08-17T15:00:00Z', count: 0, nextToken: null },
78
+ };
79
+ expect(response.meta.count).toBe(0);
80
+ });
81
+ });
82
+
83
+ describe('enums de revisión para movimientos', () => {
84
+ it('ReconciliationEntityEnum suma MOVEMENTS', () => {
85
+ expect(ReconciliationEntityEnum.MOVEMENTS).toBe('MOVEMENTS');
86
+ });
87
+
88
+ it('ReconciliationTriggerEnum suma DISCREPANCY y DAILY', () => {
89
+ expect(ReconciliationTriggerEnum.DISCREPANCY).toBe('DISCREPANCY');
90
+ expect(ReconciliationTriggerEnum.DAILY).toBe('DAILY');
91
+ });
92
+ });
93
+
94
+ describe('ReconciliationPageMessage con cursor y rango', () => {
95
+ const base = { ...tenant, runId: 'run-1', pageIndex: 0 };
96
+
97
+ it('acepta nextToken en inventario y from/to en movimientos', () => {
98
+ expect(propsWithErrors(ReconciliationPageMessage, { ...base, entity: 'INVENTORY', storeId: '12', nextToken: 'abc' })).toEqual([]);
99
+ expect(
100
+ propsWithErrors(ReconciliationPageMessage, { ...base, entity: 'MOVEMENTS', from: '2026-08-17T19:55:00Z', to: '2026-08-17T21:00:00Z' }),
101
+ ).toEqual([]);
102
+ });
103
+
104
+ it('rechaza nextToken vacío y fechas sin zona', () => {
105
+ expect(propsWithErrors(ReconciliationPageMessage, { ...base, entity: 'INVENTORY', nextToken: '' })).toEqual(['nextToken']);
106
+ expect(propsWithErrors(ReconciliationPageMessage, { ...base, entity: 'MOVEMENTS', from: '2026-08-17' })).toEqual(['from']);
107
+ expect(propsWithErrors(ReconciliationPageMessage, { ...base, entity: 'MOVEMENTS', to: 'ayer' })).toEqual(['to']);
108
+ });
109
+ });
@@ -0,0 +1,120 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance, type ClassConstructor } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import {
5
+ RetailSystemErrorBody,
6
+ RetailSystemInventoryResponse,
7
+ RetailSystemMovementsResponse,
8
+ RetailSystemPageMeta,
9
+ RetailSystemPageResponse,
10
+ RetailSystemSaleResponse,
11
+ } from '../../../src/retailIntegration';
12
+
13
+ function errorPaths(errors: ValidationError[], prefix = ''): string[] {
14
+ return errors.flatMap((error) => {
15
+ const path = prefix ? `${prefix}.${error.property}` : error.property;
16
+ return error.children?.length ? errorPaths(error.children, path) : [path];
17
+ });
18
+ }
19
+
20
+ function pathsWithErrors<T extends object>(cls: ClassConstructor<T>, payload: object): string[] {
21
+ return errorPaths(validateSync(plainToInstance(cls, payload)));
22
+ }
23
+
24
+ const movement = {
25
+ eventId: 'evt_01J8ZE5M8Y2Q4R7T1V3W6X8Z1C',
26
+ eventType: 'InventoryMovementV1',
27
+ occurredAt: '2026-08-17T20:32:07Z',
28
+ payload: { movementType: 'SALE', storeId: '12', targetStoreId: null, pieces: [], stock: [] },
29
+ };
30
+
31
+ describe('RetailSystemPageMeta', () => {
32
+ it('acepta la última página y una intermedia', () => {
33
+ expect(pathsWithErrors(RetailSystemPageMeta, { count: 0, nextToken: null })).toEqual([]);
34
+ expect(pathsWithErrors(RetailSystemPageMeta, { count: 200, nextToken: 'eyJpZCI6MTQwMH0' })).toEqual([]);
35
+ });
36
+
37
+ it.each([-1, 1.5, '3', undefined])('rechaza count=%p', (count) => {
38
+ expect(pathsWithErrors(RetailSystemPageMeta, { count, nextToken: null })).toEqual(['count']);
39
+ });
40
+
41
+ it.each([undefined, 7, 'x'.repeat(2049)])('rechaza nextToken=%p', (nextToken) => {
42
+ expect(pathsWithErrors(RetailSystemPageMeta, { count: 1, nextToken })).toEqual(['nextToken']);
43
+ });
44
+ });
45
+
46
+ describe('RetailSystemPageResponse', () => {
47
+ it('acepta data con renglones crudos', () => {
48
+ expect(pathsWithErrors(RetailSystemPageResponse, { data: [{ any: 'row' }], meta: { count: 1, nextToken: null } })).toEqual([]);
49
+ });
50
+
51
+ it('rechaza data que no es arreglo y meta inválida', () => {
52
+ expect(pathsWithErrors(RetailSystemPageResponse, { data: {}, meta: { count: 1, nextToken: null } })).toEqual(['data']);
53
+ expect(pathsWithErrors(RetailSystemPageResponse, { data: [], meta: { count: -1, nextToken: null } })).toEqual(['meta.count']);
54
+ expect(pathsWithErrors(RetailSystemPageResponse, { data: [] })).toEqual(['meta']);
55
+ });
56
+ });
57
+
58
+ describe('RetailSystemInventoryResponse', () => {
59
+ const valid = {
60
+ data: { pieces: [{ sku: 'SAM-A17' }], stock: [] },
61
+ meta: { storeId: '12', generatedAt: '2026-08-17T15:00:00Z', count: 1, nextToken: null },
62
+ };
63
+
64
+ it('acepta una página del inventario', () => {
65
+ expect(pathsWithErrors(RetailSystemInventoryResponse, valid)).toEqual([]);
66
+ });
67
+
68
+ it('rechaza pieces o stock que no son arreglo', () => {
69
+ expect(pathsWithErrors(RetailSystemInventoryResponse, { ...valid, data: { pieces: null, stock: [] } })).toEqual(['data.pieces']);
70
+ expect(pathsWithErrors(RetailSystemInventoryResponse, { ...valid, data: { pieces: [] } })).toEqual(['data.stock']);
71
+ });
72
+
73
+ it.each([
74
+ ['storeId', 'tienda/12'],
75
+ ['generatedAt', '2026-08-17 15:00'],
76
+ ['count', -2],
77
+ ['nextToken', ''.padEnd(2049, 'a')],
78
+ ])('rechaza meta.%s inválido', (field, value) => {
79
+ expect(pathsWithErrors(RetailSystemInventoryResponse, { ...valid, meta: { ...valid.meta, [field]: value } })).toEqual([`meta.${field}`]);
80
+ });
81
+ });
82
+
83
+ describe('RetailSystemSaleResponse', () => {
84
+ it.each(['58812', 'SALE-12_a.b:c', 'a/b#ñ'])('acepta externalSaleId=%p', (externalSaleId) => {
85
+ expect(pathsWithErrors(RetailSystemSaleResponse, { externalSaleId })).toEqual([]);
86
+ });
87
+
88
+ it.each(['', 'con espacio', ' 58812', 'x'.repeat(65), 58812, undefined])('rechaza externalSaleId=%p', (externalSaleId) => {
89
+ expect(pathsWithErrors(RetailSystemSaleResponse, { externalSaleId })).toEqual(['externalSaleId']);
90
+ });
91
+ });
92
+
93
+ describe('RetailSystemErrorBody', () => {
94
+ it('acepta el error con y sin lineIndex', () => {
95
+ expect(pathsWithErrors(RetailSystemErrorBody, { code: 'IDENTIFIER_NOT_AVAILABLE', msg: 'texto', lineIndex: 1 })).toEqual([]);
96
+ expect(pathsWithErrors(RetailSystemErrorBody, { code: 'VALIDATION_ERROR' })).toEqual([]);
97
+ });
98
+
99
+ it('rechaza code ausente o largo y lineIndex fuera de rango', () => {
100
+ expect(pathsWithErrors(RetailSystemErrorBody, {})).toEqual(['code']);
101
+ expect(pathsWithErrors(RetailSystemErrorBody, { code: 'X'.repeat(65) })).toEqual(['code']);
102
+ expect(pathsWithErrors(RetailSystemErrorBody, { code: 'X', lineIndex: 1000 })).toEqual(['lineIndex']);
103
+ expect(pathsWithErrors(RetailSystemErrorBody, { code: 'X', lineIndex: -1 })).toEqual(['lineIndex']);
104
+ });
105
+ });
106
+
107
+ describe('RetailSystemMovementsResponse', () => {
108
+ it('acepta movimientos con el sobre del webhook', () => {
109
+ expect(pathsWithErrors(RetailSystemMovementsResponse, { data: [movement], meta: { count: 1, nextToken: null } })).toEqual([]);
110
+ expect(pathsWithErrors(RetailSystemMovementsResponse, { data: [], meta: { count: 0, nextToken: null } })).toEqual([]);
111
+ });
112
+
113
+ it('rechaza un movimiento con sobre inválido', () => {
114
+ const bad = { ...movement, eventType: 'StoreUpsertedV1', occurredAt: '2026-08-17' };
115
+ expect(pathsWithErrors(RetailSystemMovementsResponse, { data: [bad], meta: { count: 1, nextToken: null } }).sort()).toEqual([
116
+ 'data.0.eventType',
117
+ 'data.0.occurredAt',
118
+ ]);
119
+ });
120
+ });
@@ -29,7 +29,7 @@ describe('rewards: enums (los valores viajan por cola y por HTTP: no se renombra
29
29
  });
30
30
 
31
31
  it('GrantItemStatusEnum coincide con spec A 7.1', () => {
32
- expect(Object.values(GrantItemStatusEnum).sort()).toEqual(['FAILED', 'GRANTED', 'PENDING', 'PROCESSING', 'SKIPPED']);
32
+ expect(Object.values(GrantItemStatusEnum).sort()).toEqual(['FAILED', 'GRANTED', 'PENDING', 'PROCESSING', 'SKIPPED', 'WAITING_LIMIT']);
33
33
  });
34
34
 
35
35
  it('ItemErrorCategoryEnum coincide con spec A §8', () => {
@@ -0,0 +1,43 @@
1
+ import 'reflect-metadata';
2
+ import {
3
+ BackofficeGrantItemResponse,
4
+ GrantItemStatusEnum,
5
+ LimitWaitReasonEnum,
6
+ RewardItemCodeEnum,
7
+ } from '../../../src/rewards/index';
8
+ import { Rewards } from '../../../src/index';
9
+
10
+ describe('espera por tope de nivel (WAITING_LIMIT)', () => {
11
+ it('GrantItemStatusEnum.WAITING_LIMIT vale WAITING_LIMIT', () => {
12
+ expect(GrantItemStatusEnum.WAITING_LIMIT).toBe('WAITING_LIMIT');
13
+ });
14
+
15
+ it('LimitWaitReasonEnum tiene exactamente los processorCode del spec', () => {
16
+ expect(LimitWaitReasonEnum.MONTHLY_AMOUNT_EXCEEDED).toBe('ACCOUNT_LEVEL_MAX_MONTHLY_AMOUNT_EXCEEDED');
17
+ expect(LimitWaitReasonEnum.AVAILABLE_BALANCE_EXCEEDED).toBe('ACCOUNT_LEVEL_MAX_AVAILABLE_BALANCE_EXCEEDED');
18
+ expect(Object.values(LimitWaitReasonEnum)).toHaveLength(2);
19
+ });
20
+
21
+ it('RewardItemCodeEnum contiene los tres códigos', () => {
22
+ expect(Object.values(RewardItemCodeEnum)).toEqual(
23
+ expect.arrayContaining(['AMOUNT_ABOVE_MAX', 'LIMIT_WAIT_EXPIRED', 'OUTCOME_UNKNOWN']),
24
+ );
25
+ expect(Object.values(RewardItemCodeEnum)).toHaveLength(3);
26
+ });
27
+
28
+ it('BackofficeGrantItemResponse acepta los campos de espera', () => {
29
+ const parcial = {
30
+ waitReason: LimitWaitReasonEnum.MONTHLY_AMOUNT_EXCEEDED,
31
+ retryNotBefore: 1_790_000_000_000,
32
+ waitingSince: 1_789_000_000_000,
33
+ limitRejections: 2,
34
+ } satisfies Partial<BackofficeGrantItemResponse>;
35
+ expect(parcial.limitRejections).toBe(2);
36
+ });
37
+
38
+ it('sale por el index raíz', () => {
39
+ expect(Rewards.GrantItemStatusEnum.WAITING_LIMIT).toBe('WAITING_LIMIT');
40
+ expect(Rewards.LimitWaitReasonEnum.AVAILABLE_BALANCE_EXCEEDED).toBeDefined();
41
+ expect(Rewards.RewardItemCodeEnum.OUTCOME_UNKNOWN).toBe('OUTCOME_UNKNOWN');
42
+ });
43
+ });
@@ -6,5 +6,7 @@ export declare enum DisbursementType {
6
6
  /** Bono. */
7
7
  MEX_BONUS = "MEX_BONUS",
8
8
  /** Comisión de vendedor. */
9
- MEX_SELLER_COMMISSION = "MEX_SELLER_COMMISSION"
9
+ MEX_SELLER_COMMISSION = "MEX_SELLER_COMMISSION",
10
+ /** Abono de recompensas de `rewards-business`. */
11
+ MEX_REWARD = "MEX_REWARD"
10
12
  }
@@ -11,4 +11,6 @@ var DisbursementType;
11
11
  DisbursementType["MEX_BONUS"] = "MEX_BONUS";
12
12
  /** Comisión de vendedor. */
13
13
  DisbursementType["MEX_SELLER_COMMISSION"] = "MEX_SELLER_COMMISSION";
14
+ /** Abono de recompensas de `rewards-business`. */
15
+ DisbursementType["MEX_REWARD"] = "MEX_REWARD";
14
16
  })(DisbursementType || (exports.DisbursementType = DisbursementType = {}));
@@ -6,7 +6,8 @@ import { PermissionScope } from '../enums/PermissionScope';
6
6
  * Consumido por el alta de usuario orquestada de SureKeep F1 (retail-org → RBAC → RetailUser) vía api-invoker.
7
7
  * - Usuario provisionado en estado INVITED (mitiga orphan si el paso siguiente en retail-org falla).
8
8
  * - Idempotente por identidad Cognito.
9
- * - `roleId`/`scope`/`scopeRef`: todos o ninguno; sin rol no hay invitación.
9
+ * - `roleId`/`scope`/`scopeRef`: todos o ninguno; sin rol exige `suppressInvitation: true`
10
+ * y la invitación queda pendiente (`invitationPending`) hasta asignar el primer rol.
10
11
  * - `createdBy` = cognitoSub del admin real (audit + grantedBy del assignment).
11
12
  * - `customFields` = valores de los campos personalizados del tenant; si el correo ya tiene usuario, se fusionan.
12
13
  */
@@ -23,7 +23,8 @@ const hasAnyGrantField = (o) => o.roleId !== undefined || o.scope !== undefined
23
23
  * Consumido por el alta de usuario orquestada de SureKeep F1 (retail-org → RBAC → RetailUser) vía api-invoker.
24
24
  * - Usuario provisionado en estado INVITED (mitiga orphan si el paso siguiente en retail-org falla).
25
25
  * - Idempotente por identidad Cognito.
26
- * - `roleId`/`scope`/`scopeRef`: todos o ninguno; sin rol no hay invitación.
26
+ * - `roleId`/`scope`/`scopeRef`: todos o ninguno; sin rol exige `suppressInvitation: true`
27
+ * y la invitación queda pendiente (`invitationPending`) hasta asignar el primer rol.
27
28
  * - `createdBy` = cognitoSub del admin real (audit + grantedBy del assignment).
28
29
  * - `customFields` = valores de los campos personalizados del tenant; si el correo ya tiene usuario, se fusionan.
29
30
  */
@@ -0,0 +1,5 @@
1
+ /** Body M2M que cambia el correo de un usuario del tenant; el correo se guarda en minúsculas. */
2
+ export declare class UpdateTenantUserEmailRequest {
3
+ email: string;
4
+ actedBy: string;
5
+ }
@@ -0,0 +1,30 @@
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.UpdateTenantUserEmailRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Body M2M que cambia el correo de un usuario del tenant; el correo se guarda en minúsculas. */
16
+ class UpdateTenantUserEmailRequest {
17
+ }
18
+ exports.UpdateTenantUserEmailRequest = UpdateTenantUserEmailRequest;
19
+ __decorate([
20
+ (0, class_transformer_1.Expose)(),
21
+ (0, class_transformer_1.Transform)(({ value }) => (typeof value === 'string' ? value.trim().toLowerCase() : value)),
22
+ (0, class_validator_1.IsEmail)(),
23
+ __metadata("design:type", String)
24
+ ], UpdateTenantUserEmailRequest.prototype, "email", void 0);
25
+ __decorate([
26
+ (0, class_transformer_1.Expose)(),
27
+ (0, class_validator_1.IsString)(),
28
+ (0, class_validator_1.IsNotEmpty)(),
29
+ __metadata("design:type", String)
30
+ ], UpdateTenantUserEmailRequest.prototype, "actedBy", void 0);
@@ -120,7 +120,8 @@ export declare enum Permission {
120
120
  PLATFORM_INTEGRATION_KEY_ISSUE = "platform.integration.key.issue",
121
121
  PLATFORM_INTEGRATION_KEY_REVOKE = "platform.integration.key.revoke",
122
122
  TENANT_INTEGRATION_DIAGNOSTIC = "tenant.integration.diagnostic",
123
- TENANT_API_USER_MANAGE = "tenant.api_user.manage"
123
+ TENANT_API_USER_MANAGE = "tenant.api_user.manage",
124
+ PLATFORM_RETAIL_INTEGRATION_ACTIVATE = "platform.retail_integration.activate"
124
125
  }
125
126
  /**
126
127
  * Orden CANÓNICO y APPEND-ONLY de los permisos para el bitset del token.
@@ -166,6 +166,8 @@ var Permission;
166
166
  Permission["TENANT_INTEGRATION_DIAGNOSTIC"] = "tenant.integration.diagnostic";
167
167
  // Alta, rotación de secreto y edición de IPs de usuarios de API; se exige además de tenant.user.create.
168
168
  Permission["TENANT_API_USER_MANAGE"] = "tenant.api_user.manage";
169
+ // Activa la integración de un retailer externo (Zentral); scope PLATFORM.
170
+ Permission["PLATFORM_RETAIL_INTEGRATION_ACTIVATE"] = "platform.retail_integration.activate";
169
171
  })(Permission || (exports.Permission = Permission = {}));
170
172
  /**
171
173
  * Orden CANÓNICO y APPEND-ONLY de los permisos para el bitset del token.
@@ -286,6 +288,7 @@ exports.PERMISSION_BIT_ORDER = [
286
288
  Permission.PLATFORM_INTEGRATION_KEY_REVOKE,
287
289
  Permission.TENANT_INTEGRATION_DIAGNOSTIC,
288
290
  Permission.TENANT_API_USER_MANAGE,
291
+ Permission.PLATFORM_RETAIL_INTEGRATION_ACTIVATE,
289
292
  ];
290
293
  function djb2(input) {
291
294
  let h = 5381;
@@ -73,6 +73,7 @@ export type { ListTenantUsersResponse } from './dtos/ListTenantUsersResponse';
73
73
  export type { TenantUserListItemResponse } from './dtos/TenantUserListItemResponse';
74
74
  export type { GetTenantUserResponse } from './dtos/GetTenantUserResponse';
75
75
  export * from './dtos/UpdateTenantUserRequest';
76
+ export * from './dtos/UpdateTenantUserEmailRequest';
76
77
  export * from './dtos/DeleteTenantUserRequest';
77
78
  export * from './dtos/DisableTenantUserRequest';
78
79
  export * from './dtos/EnableTenantUserRequest';
@@ -120,6 +120,7 @@ __exportStar(require("./dtos/UpdateIntegrationRequest"), exports);
120
120
  __exportStar(require("./dtos/UpdateLevelEntityRequest"), exports);
121
121
  __exportStar(require("./dtos/DeleteLevelEntityRequest"), exports);
122
122
  __exportStar(require("./dtos/UpdateTenantUserRequest"), exports);
123
+ __exportStar(require("./dtos/UpdateTenantUserEmailRequest"), exports);
123
124
  __exportStar(require("./dtos/DeleteTenantUserRequest"), exports);
124
125
  __exportStar(require("./dtos/DisableTenantUserRequest"), exports);
125
126
  __exportStar(require("./dtos/EnableTenantUserRequest"), exports);
@@ -60,6 +60,7 @@ export declare enum ProductSubtypeEnum {
60
60
  SP_CASH_IN_CASH_IN_OXXO = "CASH_IN_OXXO",
61
61
  SUREKEEP_BONUS_MX = "BONUS_MX",
62
62
  SUREKEEP_SELLER_COMMISSION_MX = "SELLER_COMMISSION_MX",
63
+ REWARD_MX = "REWARD_MX",
63
64
  STORE_PHONE = "PHONE",
64
65
  REFUND_PCF_DICTAMEN_PROCEDE = "DICTAMEN_PROCEDE",
65
66
  REFUND_PCF_AUTO_T2_SIN_2FA = "AUTO_T2_SIN_2FA",
@@ -77,6 +77,9 @@ var ProductSubtypeEnum;
77
77
  ProductSubtypeEnum["SUREKEEP_BONUS_MX"] = "BONUS_MX";
78
78
  // Comisión de vendedor de SureKeep abonada a la wallet.
79
79
  ProductSubtypeEnum["SUREKEEP_SELLER_COMMISSION_MX"] = "SELLER_COMMISSION_MX";
80
+ //REWARDS
81
+ // Abono de recompensas de `rewards-business`.
82
+ ProductSubtypeEnum["REWARD_MX"] = "REWARD_MX";
80
83
  //STORE
81
84
  ProductSubtypeEnum["STORE_PHONE"] = "PHONE";
82
85
  // REFUND_PCF
@@ -63,5 +63,7 @@ export declare enum ProductTypeEnum {
63
63
  * Dispersión SPEI de liquidaciones de beneficios (money-transfer-connector).
64
64
  * Producto del catálogo para la orden STP que sale con `source=BENEFITS_SETTLEMENT`.
65
65
  */
66
- MONEY_TRANSFER_SA = "MONEY_TRANSFER_SA"
66
+ MONEY_TRANSFER_SA = "MONEY_TRANSFER_SA",
67
+ /** Abono de recompensas de `rewards-business`. */
68
+ REWARDS = "REWARDS"
67
69
  }
@@ -68,4 +68,6 @@ var ProductTypeEnum;
68
68
  * Producto del catálogo para la orden STP que sale con `source=BENEFITS_SETTLEMENT`.
69
69
  */
70
70
  ProductTypeEnum["MONEY_TRANSFER_SA"] = "MONEY_TRANSFER_SA";
71
+ /** Abono de recompensas de `rewards-business`. */
72
+ ProductTypeEnum["REWARDS"] = "REWARDS";
71
73
  })(ProductTypeEnum || (exports.ProductTypeEnum = ProductTypeEnum = {}));
@@ -34,3 +34,14 @@ export declare const RETAIL_EVENT_STOCK_MAX = 200;
34
34
  export declare const RETAIL_ELIGIBLE_FINANCIERS_MAX = 50;
35
35
  /** Decimal de los eventos del retailer: hasta 12 enteros y dos decimales, nunca negativo. */
36
36
  export declare const RETAIL_EVENT_DECIMAL_PATTERN: RegExp;
37
+ /** Tope del `nextToken` opaco que devuelve Zentral. */
38
+ export declare const RETAIL_NEXT_TOKEN_MAX_LENGTH = 2048;
39
+ /** Id que asigna Zentral, como `externalSaleId`: no vacío, sin espacios y de a lo más 64. */
40
+ export declare const RETAIL_EXTERNAL_ID_PATTERN: RegExp;
41
+ /** Tope del `code` en el cuerpo de error de Zentral. */
42
+ export declare const RETAIL_ERROR_CODE_MAX_LENGTH = 64;
43
+ /** Rango de `lineIndex` en un error de línea de venta. */
44
+ export declare const RETAIL_LINE_INDEX_MIN = 0;
45
+ export declare const RETAIL_LINE_INDEX_MAX = 999;
46
+ /** Rango máximo de `GET /inventory/movements`: 7 días en milisegundos. */
47
+ export declare const RETAIL_MOVEMENTS_RANGE_MAX_MS: number;