@fiado/type-kit 3.644.0 → 3.646.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 (94) hide show
  1. package/_test_/unit/collection/authorizeCollectionMovementRequest.test.ts +15 -0
  2. package/_test_/unit/collection/costCenter.test.ts +54 -0
  3. package/_test_/unit/collection/disburseRequest.test.ts +45 -0
  4. package/_test_/unit/collection/disbursementType.test.ts +7 -1
  5. package/_test_/unit/productCatalog/rewardTopupEnums.test.ts +16 -0
  6. package/_test_/unit/reconciliation/reconciliationRequests.test.ts +15 -0
  7. package/_test_/unit/rewards/mobileTopup.test.ts +78 -0
  8. package/_test_/unit/rewards/waitingLimit.test.ts +3 -3
  9. package/_test_/unit/transaction/transactionCreateRequestM8.test.ts +21 -6
  10. package/_test_/unit/transactionProcessor/authorizeRewardTopupRequest.test.ts +59 -0
  11. package/_test_/unit/transactionProcessor/rewardTopupCodes.test.ts +17 -0
  12. package/bin/collection/dtos/AuthorizeCollectionMovementRequest.d.ts +9 -0
  13. package/bin/collection/dtos/AuthorizeCollectionMovementRequest.js +6 -0
  14. package/bin/collection/dtos/CollectionProductConfigDto.d.ts +2 -0
  15. package/bin/collection/dtos/CollectionProductConfigDto.js +6 -0
  16. package/bin/collection/dtos/DisburseRequest.d.ts +8 -0
  17. package/bin/collection/dtos/DisburseRequest.js +15 -0
  18. package/bin/collection/dtos/DisburseTopupDetail.d.ts +9 -0
  19. package/bin/collection/dtos/DisburseTopupDetail.js +46 -0
  20. package/bin/collection/dtos/UpsertProductConfigRequest.d.ts +2 -0
  21. package/bin/collection/dtos/UpsertProductConfigRequest.js +6 -0
  22. package/bin/collection/enums/DisbursementType.d.ts +3 -1
  23. package/bin/collection/enums/DisbursementType.js +2 -0
  24. package/bin/collection/index.d.ts +1 -0
  25. package/bin/collection/index.js +1 -0
  26. package/bin/productCatalog/enums/ProductSubtypeEnum.d.ts +1 -0
  27. package/bin/productCatalog/enums/ProductSubtypeEnum.js +2 -0
  28. package/bin/reconciliation/dtos/ReconciliationDefinition.d.ts +2 -0
  29. package/bin/reconciliation/dtos/requests/ReconciliationFeedRequests.d.ts +5 -0
  30. package/bin/reconciliation/dtos/requests/ReconciliationFeedRequests.js +9 -0
  31. package/bin/rewards/dtos/backoffice/BackofficeGrantItemResponse.d.ts +2 -0
  32. package/bin/rewards/dtos/backoffice/MobileTopupConfigRequest.d.ts +10 -0
  33. package/bin/rewards/dtos/backoffice/MobileTopupConfigRequest.js +59 -0
  34. package/bin/rewards/dtos/backoffice/MobileTopupConfigResponse.d.ts +15 -0
  35. package/bin/rewards/dtos/backoffice/MobileTopupConfigResponse.js +2 -0
  36. package/bin/rewards/dtos/backoffice/MobileTopupContributionResponse.d.ts +9 -0
  37. package/bin/rewards/dtos/backoffice/MobileTopupContributionResponse.js +2 -0
  38. package/bin/rewards/dtos/backoffice/MobileTopupPlanListResponse.d.ts +5 -0
  39. package/bin/rewards/dtos/backoffice/MobileTopupPlanListResponse.js +2 -0
  40. package/bin/rewards/dtos/backoffice/MobileTopupPlanResponse.d.ts +22 -0
  41. package/bin/rewards/dtos/backoffice/MobileTopupPlanResponse.js +2 -0
  42. package/bin/rewards/dtos/backoffice/MobileTopupRechargeResponse.d.ts +8 -0
  43. package/bin/rewards/dtos/backoffice/MobileTopupRechargeResponse.js +2 -0
  44. package/bin/rewards/enums/MobileTopupPlanStatusEnum.d.ts +6 -0
  45. package/bin/rewards/enums/MobileTopupPlanStatusEnum.js +10 -0
  46. package/bin/rewards/enums/MobileTopupRechargeStatusEnum.d.ts +7 -0
  47. package/bin/rewards/enums/MobileTopupRechargeStatusEnum.js +11 -0
  48. package/bin/rewards/enums/RewardItemCodeEnum.d.ts +5 -1
  49. package/bin/rewards/enums/RewardItemCodeEnum.js +4 -0
  50. package/bin/rewards/index.d.ts +8 -0
  51. package/bin/rewards/index.js +8 -0
  52. package/bin/transaction/constants/costCenter.d.ts +5 -0
  53. package/bin/transaction/constants/costCenter.js +8 -0
  54. package/bin/transaction/dtos/TransactionCreateRequest.d.ts +5 -2
  55. package/bin/transaction/dtos/TransactionCreateRequest.js +1 -1
  56. package/bin/transaction/index.d.ts +1 -0
  57. package/bin/transaction/index.js +2 -0
  58. package/bin/transactionProcessor/dtos/AuthorizeBonusRequest.d.ts +2 -0
  59. package/bin/transactionProcessor/dtos/AuthorizeBonusRequest.js +6 -0
  60. package/bin/transactionProcessor/dtos/AuthorizeRewardTopupRequest.d.ts +21 -0
  61. package/bin/transactionProcessor/dtos/AuthorizeRewardTopupRequest.js +74 -0
  62. package/bin/transactionProcessor/enums/TransactionProcessorCodesEnum.d.ts +9 -1
  63. package/bin/transactionProcessor/enums/TransactionProcessorCodesEnum.js +8 -0
  64. package/bin/transactionProcessor/index.d.ts +1 -0
  65. package/bin/transactionProcessor/index.js +1 -0
  66. package/package.json +1 -1
  67. package/src/collection/dtos/AuthorizeCollectionMovementRequest.ts +10 -1
  68. package/src/collection/dtos/CollectionProductConfigDto.ts +3 -1
  69. package/src/collection/dtos/DisburseRequest.ts +16 -1
  70. package/src/collection/dtos/DisburseTopupDetail.ts +15 -0
  71. package/src/collection/dtos/UpsertProductConfigRequest.ts +3 -1
  72. package/src/collection/enums/DisbursementType.ts +2 -0
  73. package/src/collection/index.ts +1 -0
  74. package/src/productCatalog/enums/ProductSubtypeEnum.ts +2 -0
  75. package/src/reconciliation/dtos/ReconciliationDefinition.ts +2 -0
  76. package/src/reconciliation/dtos/requests/ReconciliationFeedRequests.ts +14 -2
  77. package/src/rewards/dtos/backoffice/BackofficeGrantItemResponse.ts +2 -0
  78. package/src/rewards/dtos/backoffice/MobileTopupConfigRequest.ts +41 -0
  79. package/src/rewards/dtos/backoffice/MobileTopupConfigResponse.ts +15 -0
  80. package/src/rewards/dtos/backoffice/MobileTopupContributionResponse.ts +9 -0
  81. package/src/rewards/dtos/backoffice/MobileTopupPlanListResponse.ts +6 -0
  82. package/src/rewards/dtos/backoffice/MobileTopupPlanResponse.ts +23 -0
  83. package/src/rewards/dtos/backoffice/MobileTopupRechargeResponse.ts +9 -0
  84. package/src/rewards/enums/MobileTopupPlanStatusEnum.ts +6 -0
  85. package/src/rewards/enums/MobileTopupRechargeStatusEnum.ts +7 -0
  86. package/src/rewards/enums/RewardItemCodeEnum.ts +4 -0
  87. package/src/rewards/index.ts +8 -0
  88. package/src/transaction/constants/costCenter.ts +5 -0
  89. package/src/transaction/dtos/TransactionCreateRequest.ts +5 -2
  90. package/src/transaction/index.ts +3 -0
  91. package/src/transactionProcessor/dtos/AuthorizeBonusRequest.ts +7 -1
  92. package/src/transactionProcessor/dtos/AuthorizeRewardTopupRequest.ts +44 -0
  93. package/src/transactionProcessor/enums/TransactionProcessorCodesEnum.ts +8 -0
  94. package/src/transactionProcessor/index.ts +1 -0
@@ -31,3 +31,18 @@ describe('AuthorizeCollectionMovementRequest.tenantId', () => {
31
31
  expect(await invalidProperties({ ...VALID, tenantId: 42 })).toEqual(['tenantId']);
32
32
  });
33
33
  });
34
+
35
+ describe('AuthorizeCollectionMovementRequest.costCenterId', () => {
36
+ it('es opcional', async () => {
37
+ expect(await invalidProperties(VALID)).toEqual([]);
38
+ });
39
+
40
+ it('acepta el centro de costo del producto', async () => {
41
+ expect(await invalidProperties({ ...VALID, costCenterId: 'SK-COBRANZA' })).toEqual([]);
42
+ });
43
+
44
+ it('rechaza costCenterId vacío o no string', async () => {
45
+ expect(await invalidProperties({ ...VALID, costCenterId: '' })).toEqual(['costCenterId']);
46
+ expect(await invalidProperties({ ...VALID, costCenterId: 42 })).toEqual(['costCenterId']);
47
+ });
48
+ });
@@ -0,0 +1,54 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import { CollectionProductConfigDto, UpsertProductConfigRequest } from '../../../src/collection/index';
5
+ import { AuthorizeBonusRequest } from '../../../src/transactionProcessor/index';
6
+
7
+ async function invalidProperties<T extends object>(cls: new () => T, body: Record<string, unknown>): Promise<string[]> {
8
+ return (await validate(plainToInstance(cls, body) as object)).map((e) => e.property);
9
+ }
10
+
11
+ const UPSERT = {
12
+ sources: [{ order: 1, wallet: 'MXN', currency: 'MXN', mechanism: 'MXN_DIRECT', provider: 'wallet' }],
13
+ allowPartial: false,
14
+ retryPolicy: { maxAttempts: 3, backoffSeconds: 60 },
15
+ reportTargets: [],
16
+ };
17
+
18
+ const CONFIG = {
19
+ ...UPSERT,
20
+ tenantId: 'surekeep',
21
+ productId: 'loan',
22
+ version: 1,
23
+ createdBy: 'system',
24
+ updatedBy: 'system',
25
+ };
26
+
27
+ const BONUS = {
28
+ idempotencyKey: 'bonus-1',
29
+ productCatalogId: 'cat-1',
30
+ amount: 50,
31
+ directoryId: 'dir-1',
32
+ concept: 'Bono SureKeep',
33
+ };
34
+
35
+ describe.each([
36
+ ['CollectionProductConfigDto', CollectionProductConfigDto, CONFIG],
37
+ ['UpsertProductConfigRequest', UpsertProductConfigRequest, UPSERT],
38
+ ['AuthorizeBonusRequest', AuthorizeBonusRequest, BONUS],
39
+ ] as const)('%s.costCenterId', (_name, cls, valid) => {
40
+ const klass = cls as unknown as new () => object;
41
+
42
+ it('es opcional', async () => {
43
+ expect(await invalidProperties(klass, valid)).toEqual([]);
44
+ });
45
+
46
+ it('acepta un centro de costo', async () => {
47
+ expect(await invalidProperties(klass, { ...valid, costCenterId: 'SK-COBRANZA' })).toEqual([]);
48
+ });
49
+
50
+ it('rechaza vacío o no string', async () => {
51
+ expect(await invalidProperties(klass, { ...valid, costCenterId: '' })).toEqual(['costCenterId']);
52
+ expect(await invalidProperties(klass, { ...valid, costCenterId: 3 })).toEqual(['costCenterId']);
53
+ });
54
+ });
@@ -88,6 +88,36 @@ describe('DisburseRequest', () => {
88
88
  );
89
89
  });
90
90
 
91
+ describe('topup (MEX_REWARD_TOPUP)', () => {
92
+ const TOPUP_OK = { phone: '5512345678', moduleName: 'multicenter', benefitId: 'b1', leafId: 'l1', productId: 'p1' };
93
+ const TOPUP_TYPE = { ...VALID, disbursementType: 'MEX_REWARD_TOPUP' };
94
+
95
+ it('acepta MEX_REWARD_TOPUP con topup válido', async () => {
96
+ expect(await invalidProperties({ ...TOPUP_TYPE, topup: TOPUP_OK })).toEqual([]);
97
+ });
98
+
99
+ it('exige topup con MEX_REWARD_TOPUP', async () => {
100
+ expect(await invalidProperties(TOPUP_TYPE)).toContain('topup');
101
+ });
102
+
103
+ it.each(['551234567', '55123456789', '55123abc78', '+525512345678'])('rechaza phone %s', async (phone) => {
104
+ expect(await invalidProperties({ ...TOPUP_TYPE, topup: { ...TOPUP_OK, phone } })).toContain('topup');
105
+ });
106
+
107
+ it('rechaza leafId vacío', async () => {
108
+ expect(await invalidProperties({ ...TOPUP_TYPE, topup: { ...TOPUP_OK, leafId: '' } })).toContain('topup');
109
+ });
110
+
111
+ it('no exige topup a otros tipos y lo ignora si viene', async () => {
112
+ expect(await invalidProperties(VALID)).toEqual([]);
113
+ expect(await invalidProperties({ ...VALID, topup: TOPUP_OK })).toEqual([]);
114
+ });
115
+
116
+ it('acepta idempotencyKey de 64 caracteres (el límite de 60 lo aplica el processor)', async () => {
117
+ expect(await invalidProperties({ ...TOPUP_TYPE, topup: TOPUP_OK, idempotencyKey: 'k'.repeat(64) })).toEqual([]);
118
+ });
119
+ });
120
+
91
121
  it('sale por el index raíz', () => {
92
122
  expect(new Collection.DisburseRequest()).toBeInstanceOf(DisburseRequest);
93
123
  expect(new Collection.DisburseResponse()).toBeInstanceOf(DisburseResponse);
@@ -100,3 +130,18 @@ describe('DisburseResponse', () => {
100
130
  expect(await validate(plainToInstance(DisburseResponse, { transactionId: null }) as object)).toHaveLength(0);
101
131
  });
102
132
  });
133
+
134
+ describe('DisburseRequest.costCenterId', () => {
135
+ it('es opcional', async () => {
136
+ expect(await invalidProperties(VALID)).toEqual([]);
137
+ });
138
+
139
+ it('acepta el centro de costo de quien dispersa', async () => {
140
+ expect(await invalidProperties({ ...VALID, costCenterId: 'SK-BONOS' })).toEqual([]);
141
+ });
142
+
143
+ it('rechaza vacío o no string', async () => {
144
+ expect(await invalidProperties({ ...VALID, costCenterId: '' })).toEqual(['costCenterId']);
145
+ expect(await invalidProperties({ ...VALID, costCenterId: 9 })).toEqual(['costCenterId']);
146
+ });
147
+ });
@@ -4,13 +4,18 @@ import { DisbursementType } from '../../../src/collection/index';
4
4
 
5
5
  describe('DisbursementType', () => {
6
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']);
7
+ expect(Object.values(DisbursementType)).toEqual(['MEX_BONUS', 'MEX_SELLER_COMMISSION', 'MEX_REWARD', 'MEX_REWARD_TOPUP']);
8
8
  });
9
9
 
10
10
  it('MEX_REWARD vale MEX_REWARD', () => {
11
11
  expect(DisbursementType.MEX_REWARD).toBe('MEX_REWARD');
12
12
  });
13
13
 
14
+ it('MEX_REWARD_TOPUP vale MEX_REWARD_TOPUP', () => {
15
+ expect(DisbursementType.MEX_REWARD_TOPUP).toBe('MEX_REWARD_TOPUP');
16
+ expect(Object.values(DisbursementType)).toEqual(expect.arrayContaining(['MEX_BONUS', 'MEX_SELLER_COMMISSION', 'MEX_REWARD', 'MEX_REWARD_TOPUP']));
17
+ });
18
+
14
19
  it('cada clave es igual a su valor', () => {
15
20
  for (const [key, value] of Object.entries(DisbursementType)) {
16
21
  expect(key).toBe(value);
@@ -21,5 +26,6 @@ describe('DisbursementType', () => {
21
26
  expect(Collection.DisbursementType.MEX_BONUS).toBe('MEX_BONUS');
22
27
  expect(Collection.DisbursementType.MEX_SELLER_COMMISSION).toBe('MEX_SELLER_COMMISSION');
23
28
  expect(Collection.DisbursementType.MEX_REWARD).toBe('MEX_REWARD');
29
+ expect(Collection.DisbursementType.MEX_REWARD_TOPUP).toBe('MEX_REWARD_TOPUP');
24
30
  });
25
31
  });
@@ -0,0 +1,16 @@
1
+ import { ProductSubtypeEnum } from '../../../src/productCatalog/enums/ProductSubtypeEnum';
2
+
3
+ describe('ProductSubtypeEnum recarga de recompensa', () => {
4
+ it('REWARD_TOPUP_MX vale "REWARD_TOPUP_MX"', () => {
5
+ expect(ProductSubtypeEnum.REWARD_TOPUP_MX).toBe('REWARD_TOPUP_MX');
6
+ });
7
+
8
+ it('ningún otro miembro vale "REWARD_TOPUP_MX"', () => {
9
+ const others = Object.entries(ProductSubtypeEnum).filter(([key, value]) => value === 'REWARD_TOPUP_MX' && key !== 'REWARD_TOPUP_MX');
10
+ expect(others).toEqual([]);
11
+ });
12
+
13
+ it('REWARD_MX no cambia', () => {
14
+ expect(ProductSubtypeEnum.REWARD_MX).toBe('REWARD_MX');
15
+ });
16
+ });
@@ -206,6 +206,21 @@ describe('feed genérico de los dueños', () => {
206
206
  );
207
207
  });
208
208
 
209
+ it('costCenterIds es opcional y llega como csv del query', () => {
210
+ const base = { tenantId: 'surekeep', from: '2026-10-19T06:00:00.000Z', to: '2026-10-26T05:59:59.999Z' };
211
+ expect(errorsOf(PrivateListReconciliationFeedQuery, base)).toEqual([]);
212
+ expect(errorsOf(PrivateListReconciliationFeedQuery, { ...base, costCenterIds: 'SK-COBRANZA, SK-BONOS' })).toEqual([]);
213
+ const dto = plainToInstance(PrivateListReconciliationFeedQuery, { ...base, costCenterIds: 'SK-COBRANZA, SK-BONOS' }, { excludeExtraneousValues: true });
214
+ expect(dto.costCenterIds).toEqual(['SK-COBRANZA', 'SK-BONOS']);
215
+ expect(errorsOf(PrivateListReconciliationFeedQuery, { ...base, costCenterIds: ['SK-COBRANZA'] })).toEqual([]);
216
+ });
217
+
218
+ it('costCenterIds rechaza elementos vacíos', () => {
219
+ const base = { tenantId: 'surekeep', from: '2026-10-19T06:00:00.000Z', to: '2026-10-26T05:59:59.999Z' };
220
+ expect(errorsOf(PrivateListReconciliationFeedQuery, { ...base, costCenterIds: 'SK-COBRANZA,' })).toContain('costCenterIds');
221
+ expect(errorsOf(PrivateListReconciliationFeedQuery, { ...base, costCenterIds: '' })).toContain('costCenterIds');
222
+ });
223
+
209
224
  it('el registro por recordId pide tenant y el servicio que llama; el actor es opcional', () => {
210
225
  expect(errorsOf(PrivateGetReconciliationFeedRecordQuery, { tenantId: 'surekeep', author: 'reconciliation-engine-business' })).toEqual(
211
226
  [],
@@ -0,0 +1,78 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import {
5
+ BackofficeGrantItemResponse,
6
+ MobileTopupConfigRequest,
7
+ MobileTopupPlanResponse,
8
+ MobileTopupPlanStatusEnum,
9
+ MobileTopupRechargeStatusEnum,
10
+ RewardItemCodeEnum,
11
+ } from '../../../src/rewards';
12
+
13
+ const valid = { moduleName: 'm', benefitId: 'b', leafId: 'l', productId: 'p', monthlyPrice: 99.5 };
14
+
15
+ describe('rewards mobile topup', () => {
16
+ it('enums con valores exactos', () => {
17
+ expect({ ...MobileTopupPlanStatusEnum }).toEqual({ ACTIVE: 'ACTIVE', NEEDS_ATTENTION: 'NEEDS_ATTENTION', DONE: 'DONE' });
18
+ expect({ ...MobileTopupRechargeStatusEnum }).toEqual({
19
+ PAID: 'PAID',
20
+ REJECTED: 'REJECTED',
21
+ UNCONFIRMED: 'UNCONFIRMED',
22
+ NOT_DELIVERED: 'NOT_DELIVERED',
23
+ });
24
+ });
25
+
26
+ it('RewardItemCodeEnum conserva los previos y agrega los nuevos', () => {
27
+ expect(RewardItemCodeEnum.TOPUP_REJECTED).toBe('TOPUP_REJECTED');
28
+ expect(RewardItemCodeEnum.ITEM_DISMISSED).toBe('ITEM_DISMISSED');
29
+ expect(RewardItemCodeEnum.AMOUNT_ABOVE_MAX).toBe('AMOUNT_ABOVE_MAX');
30
+ expect(RewardItemCodeEnum.LIMIT_WAIT_EXPIRED).toBe('LIMIT_WAIT_EXPIRED');
31
+ expect(RewardItemCodeEnum.OUTCOME_UNKNOWN).toBe('OUTCOME_UNKNOWN');
32
+ });
33
+
34
+ it('MobileTopupConfigRequest válido', async () => {
35
+ expect(await validate(plainToInstance(MobileTopupConfigRequest, valid))).toHaveLength(0);
36
+ expect(await validate(plainToInstance(MobileTopupConfigRequest, { ...valid, expectedVersion: 3 }))).toHaveLength(0);
37
+ });
38
+
39
+ it.each([
40
+ ['monthlyPrice 0', { monthlyPrice: 0 }],
41
+ ['monthlyPrice -1', { monthlyPrice: -1 }],
42
+ ['monthlyPrice 10.123', { monthlyPrice: 10.123 }],
43
+ ['moduleName vacío', { moduleName: '' }],
44
+ ['productId 101 chars', { productId: 'x'.repeat(101) }],
45
+ ['expectedVersion 0', { expectedVersion: 0 }],
46
+ ])('MobileTopupConfigRequest inválido: %s', async (_n, patch) => {
47
+ const errors = await validate(plainToInstance(MobileTopupConfigRequest, { ...valid, ...patch }));
48
+ expect(errors.length).toBeGreaterThanOrEqual(1);
49
+ });
50
+
51
+ it('MobileTopupPlanResponse completo compila', () => {
52
+ const plan = {
53
+ phone: '5512345678',
54
+ status: MobileTopupPlanStatusEnum.ACTIVE,
55
+ promised: 3,
56
+ done: 1,
57
+ pending: 2,
58
+ nextDueAt: 1,
59
+ lastRechargeAt: 1,
60
+ lastFolio: 'RW-g-i-M1',
61
+ failedAttempts: 0,
62
+ lastError: 'x',
63
+ closedUndelivered: 0,
64
+ closedBy: 'op',
65
+ closedAt: 1,
66
+ contributions: [{ grantId: 'g', itemKey: 'i', months: 3, sourceService: 's', sourceReference: 'r', campaignId: 'c', addedAt: 1 }],
67
+ recharges: [{ folio: 'RW-g-i-M1', rechargeNumber: 1, status: MobileTopupRechargeStatusEnum.PAID, at: 1, summary: 's' }],
68
+ updatedAt: 1,
69
+ } satisfies MobileTopupPlanResponse;
70
+ expect(plan.pending).toBe(2);
71
+ });
72
+
73
+ it('BackofficeGrantItemResponse acepta type opcional', () => {
74
+ const item = new BackofficeGrantItemResponse();
75
+ item.type = 'MOBILE_TOPUP';
76
+ expect(item.type).toBe('MOBILE_TOPUP');
77
+ });
78
+ });
@@ -18,11 +18,11 @@ describe('espera por tope de nivel (WAITING_LIMIT)', () => {
18
18
  expect(Object.values(LimitWaitReasonEnum)).toHaveLength(2);
19
19
  });
20
20
 
21
- it('RewardItemCodeEnum contiene los tres códigos', () => {
21
+ it('RewardItemCodeEnum contiene los cinco códigos', () => {
22
22
  expect(Object.values(RewardItemCodeEnum)).toEqual(
23
- expect.arrayContaining(['AMOUNT_ABOVE_MAX', 'LIMIT_WAIT_EXPIRED', 'OUTCOME_UNKNOWN']),
23
+ expect.arrayContaining(['AMOUNT_ABOVE_MAX', 'LIMIT_WAIT_EXPIRED', 'OUTCOME_UNKNOWN', 'TOPUP_REJECTED', 'ITEM_DISMISSED']),
24
24
  );
25
- expect(Object.values(RewardItemCodeEnum)).toHaveLength(3);
25
+ expect(Object.values(RewardItemCodeEnum)).toHaveLength(5);
26
26
  });
27
27
 
28
28
  it('BackofficeGrantItemResponse acepta los campos de espera', () => {
@@ -1,27 +1,42 @@
1
1
  import 'reflect-metadata';
2
2
  import { plainToInstance } from 'class-transformer';
3
3
  import { validateSync } from 'class-validator';
4
- import { TransactionCreateRequest } from '../../../src/transaction';
4
+ import { TransactionCreateRequest, UNASSIGNED_COST_CENTER_ID } from '../../../src/transaction';
5
5
 
6
6
  function errorPropsOf(payload: Record<string, unknown>): string[] {
7
7
  return validateSync(plainToInstance(TransactionCreateRequest, payload)).map((e) => e.property);
8
8
  }
9
9
 
10
- describe('TransactionCreateRequest · executionId y tenant SOFOM', () => {
10
+ describe('TransactionCreateRequest · executionId y centro de costo', () => {
11
11
  it('ambos son opcionales', () => {
12
12
  const props = errorPropsOf({});
13
13
  expect(props).not.toContain('executionId');
14
- expect(props).not.toContain('sofomTenantId');
14
+ expect(props).not.toContain('costCenterId');
15
15
  });
16
16
 
17
17
  it('acepta strings no vacíos', () => {
18
- const props = errorPropsOf({ executionId: 'exec-1', sofomTenantId: 'surekeep' });
18
+ const props = errorPropsOf({ executionId: 'exec-1', costCenterId: 'SK-COBRANZA' });
19
19
  expect(props).not.toContain('executionId');
20
- expect(props).not.toContain('sofomTenantId');
20
+ expect(props).not.toContain('costCenterId');
21
+ });
22
+
23
+ it('acepta el centro de costo sin asignar', () => {
24
+ expect(errorPropsOf({ costCenterId: UNASSIGNED_COST_CENTER_ID })).not.toContain('costCenterId');
21
25
  });
22
26
 
23
27
  it('rechaza tipos que no son string o vacíos', () => {
24
28
  expect(errorPropsOf({ executionId: 12 })).toContain('executionId');
25
- expect(errorPropsOf({ sofomTenantId: '' })).toContain('sofomTenantId');
29
+ expect(errorPropsOf({ costCenterId: '' })).toContain('costCenterId');
30
+ expect(errorPropsOf({ costCenterId: 7 })).toContain('costCenterId');
31
+ });
32
+
33
+ it('ya no declara el tenant SOFOM', () => {
34
+ expect(errorPropsOf({ sofomTenantId: '' })).not.toContain('sofomTenantId');
35
+ });
36
+ });
37
+
38
+ describe('UNASSIGNED_COST_CENTER_ID', () => {
39
+ it('es UNASSIGNED', () => {
40
+ expect(UNASSIGNED_COST_CENTER_ID).toBe('UNASSIGNED');
26
41
  });
27
42
  });
@@ -0,0 +1,59 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import { TransactionProcessor } from '../../../src/index';
5
+ import { AuthorizeRewardTopupRequest } from '../../../src/transactionProcessor/index';
6
+
7
+ const VALID = {
8
+ idempotencyKey: 'RW-' + 'a'.repeat(32) + '-bait3m-M1',
9
+ directoryId: 'dir-1',
10
+ phone: '5512345678',
11
+ moduleName: 'multicenter',
12
+ benefitId: 'b1',
13
+ leafId: 'l1',
14
+ productId: 'p1',
15
+ amount: 15,
16
+ concept: 'Recarga Bait de recompensa',
17
+ };
18
+
19
+ async function invalidProperties(body: Record<string, unknown>): Promise<string[]> {
20
+ const dto = plainToInstance(AuthorizeRewardTopupRequest, body);
21
+ return (await validate(dto as object)).map((e) => e.property);
22
+ }
23
+
24
+ describe('AuthorizeRewardTopupRequest', () => {
25
+ it('acepta un request válido', async () => {
26
+ expect(await invalidProperties(VALID)).toEqual([]);
27
+ expect(VALID.idempotencyKey.length).toBeLessThanOrEqual(60);
28
+ });
29
+
30
+ it('acepta idempotencyKey de exactamente 60 y rechaza 61', async () => {
31
+ expect(await invalidProperties({ ...VALID, idempotencyKey: 'k'.repeat(60) })).toEqual([]);
32
+ expect(await invalidProperties({ ...VALID, idempotencyKey: 'k'.repeat(61) })).toContain('idempotencyKey');
33
+ });
34
+
35
+ it('rechaza idempotencyKey con #', async () => {
36
+ expect(await invalidProperties({ ...VALID, idempotencyKey: 'RW-a#b' })).toContain('idempotencyKey');
37
+ });
38
+
39
+ it.each([0, -1, 10.005])('rechaza amount %s', async (amount) => {
40
+ expect(await invalidProperties({ ...VALID, amount })).toContain('amount');
41
+ });
42
+
43
+ it('acepta amount 15 y 15.5', async () => {
44
+ expect(await invalidProperties({ ...VALID, amount: 15 })).not.toContain('amount');
45
+ expect(await invalidProperties({ ...VALID, amount: 15.5 })).not.toContain('amount');
46
+ });
47
+
48
+ it('rechaza phone de 9 dígitos', async () => {
49
+ expect(await invalidProperties({ ...VALID, phone: '551234567' })).toContain('phone');
50
+ });
51
+
52
+ it('rechaza concept de 121', async () => {
53
+ expect(await invalidProperties({ ...VALID, concept: 'c'.repeat(121) })).toContain('concept');
54
+ });
55
+
56
+ it('sale por el index raíz', () => {
57
+ expect(new TransactionProcessor.AuthorizeRewardTopupRequest()).toBeInstanceOf(AuthorizeRewardTopupRequest);
58
+ });
59
+ });
@@ -0,0 +1,17 @@
1
+ import { TransactionProcessorCodesEnum } from '../../../src/transactionProcessor/enums/TransactionProcessorCodesEnum';
2
+
3
+ describe('TransactionProcessorCodesEnum recarga de recompensa', () => {
4
+ it.each([
5
+ 'REWARD_TOPUP_PROVIDER_REJECTED',
6
+ 'REWARD_TOPUP_UNAVAILABLE',
7
+ 'REWARD_TOPUP_OUTCOME_UNKNOWN',
8
+ 'REWARD_TOPUP_IN_FLIGHT',
9
+ ] as const)('%s vale su propio nombre', (code) => {
10
+ expect(TransactionProcessorCodesEnum[code]).toBe(code);
11
+ });
12
+
13
+ it('INVALID_REQUEST y TRANSACTION_APPROVED no cambian', () => {
14
+ expect(TransactionProcessorCodesEnum.INVALID_REQUEST).toBe('INVALID_REQUEST');
15
+ expect(TransactionProcessorCodesEnum.TRANSACTION_APPROVED).toBe('TRANSACTION_APPROVED');
16
+ });
17
+ });
@@ -11,7 +11,16 @@ export declare class AuthorizeCollectionMovementRequest {
11
11
  intentId?: string;
12
12
  chargeId?: string;
13
13
  holdTxId?: string;
14
+ /**
15
+ * Tenant SOFOM del intento de cobro. Se conserva por compatibilidad con transaction-processor; el
16
+ * ledger ya no lo usa: la pertenencia contable del movimiento viaja en `costCenterId`.
17
+ */
14
18
  tenantId?: string;
19
+ /**
20
+ * Centro de costo del producto cobrado, tomado de su configuración en el motor. El processor lo
21
+ * escribe en la transacción del ledger; ausente, queda como `UNASSIGNED`.
22
+ */
23
+ costCenterId?: string;
15
24
  /**
16
25
  * Tarifa con la que se registra el cobro. Presente, el movimiento deja además una transacción
17
26
  * Fiado: asiento contable, historial visible para el cliente y comisión declarada. Ausente, el
@@ -63,6 +63,12 @@ __decorate([
63
63
  (0, class_validator_1.IsNotEmpty)(),
64
64
  __metadata("design:type", String)
65
65
  ], AuthorizeCollectionMovementRequest.prototype, "tenantId", void 0);
66
+ __decorate([
67
+ (0, class_validator_1.IsOptional)(),
68
+ (0, class_validator_1.IsString)(),
69
+ (0, class_validator_1.IsNotEmpty)(),
70
+ __metadata("design:type", String)
71
+ ], AuthorizeCollectionMovementRequest.prototype, "costCenterId", void 0);
66
72
  __decorate([
67
73
  (0, class_validator_1.IsOptional)(),
68
74
  (0, class_validator_1.ValidateNested)(),
@@ -17,6 +17,8 @@ export declare class CollectionProductConfigDto {
17
17
  allowPartial: boolean;
18
18
  retryPolicy: RetryPolicyDto;
19
19
  reportTargets: string[];
20
+ /** Centro de costo con el que el ledger registra los cobros de este producto. */
21
+ costCenterId?: string;
20
22
  createdBy: string;
21
23
  updatedBy: string;
22
24
  }
@@ -58,6 +58,12 @@ __decorate([
58
58
  (0, class_validator_1.IsString)({ each: true }),
59
59
  __metadata("design:type", Array)
60
60
  ], CollectionProductConfigDto.prototype, "reportTargets", void 0);
61
+ __decorate([
62
+ (0, class_validator_1.IsOptional)(),
63
+ (0, class_validator_1.IsString)(),
64
+ (0, class_validator_1.IsNotEmpty)(),
65
+ __metadata("design:type", String)
66
+ ], CollectionProductConfigDto.prototype, "costCenterId", void 0);
61
67
  __decorate([
62
68
  (0, class_validator_1.IsString)(),
63
69
  __metadata("design:type", String)
@@ -1,5 +1,6 @@
1
1
  import { CurrencyId } from '../../currency/enums/CurrencyId';
2
2
  import { DisbursementType } from '../enums/DisbursementType';
3
+ import { DisburseTopupDetail } from './DisburseTopupDetail';
3
4
  /** Dispersión síncrona: el motor deposita `amount` pesos al titular por el riel de `disbursementType`. */
4
5
  export declare class DisburseRequest {
5
6
  tenantId: string;
@@ -17,4 +18,11 @@ export declare class DisburseRequest {
17
18
  concept: string;
18
19
  /** Id del llamador para rastreo: asiento, crédito. */
19
20
  reference?: string;
21
+ /**
22
+ * Centro de costo de la dispersión, tomado de la configuración de quien dispersa. El motor lo
23
+ * reenvía al processor en `AuthorizeBonusRequest.costCenterId`; ausente, el ledger escribe `UNASSIGNED`.
24
+ */
25
+ costCenterId?: string;
26
+ /** Datos de la recarga. Obligatorio si `disbursementType` es `MEX_REWARD_TOPUP`; en otros tipos se ignora. */
27
+ topup?: DisburseTopupDetail;
20
28
  }
@@ -10,9 +10,11 @@ var __metadata = (this && this.__metadata) || function (k, v) {
10
10
  };
11
11
  Object.defineProperty(exports, "__esModule", { value: true });
12
12
  exports.DisburseRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
13
14
  const class_validator_1 = require("class-validator");
14
15
  const CurrencyId_1 = require("../../currency/enums/CurrencyId");
15
16
  const DisbursementType_1 = require("../enums/DisbursementType");
17
+ const DisburseTopupDetail_1 = require("./DisburseTopupDetail");
16
18
  /** Dispersión síncrona: el motor deposita `amount` pesos al titular por el riel de `disbursementType`. */
17
19
  class DisburseRequest {
18
20
  }
@@ -62,3 +64,16 @@ __decorate([
62
64
  (0, class_validator_1.IsString)(),
63
65
  __metadata("design:type", String)
64
66
  ], DisburseRequest.prototype, "reference", void 0);
67
+ __decorate([
68
+ (0, class_validator_1.IsOptional)(),
69
+ (0, class_validator_1.IsString)(),
70
+ (0, class_validator_1.IsNotEmpty)(),
71
+ __metadata("design:type", String)
72
+ ], DisburseRequest.prototype, "costCenterId", void 0);
73
+ __decorate([
74
+ (0, class_validator_1.ValidateIf)((o) => o.disbursementType === DisbursementType_1.DisbursementType.MEX_REWARD_TOPUP),
75
+ (0, class_validator_1.IsDefined)(),
76
+ (0, class_validator_1.ValidateNested)(),
77
+ (0, class_transformer_1.Type)(() => DisburseTopupDetail_1.DisburseTopupDetail),
78
+ __metadata("design:type", DisburseTopupDetail_1.DisburseTopupDetail)
79
+ ], DisburseRequest.prototype, "topup", void 0);
@@ -0,0 +1,9 @@
1
+ /** Datos de la recarga Bait de una dispersión `MEX_REWARD_TOPUP`. */
2
+ export declare class DisburseTopupDetail {
3
+ /** Celular MX normalizado a 10 dígitos. */
4
+ phone: string;
5
+ moduleName: string;
6
+ benefitId: string;
7
+ leafId: string;
8
+ productId: string;
9
+ }
@@ -0,0 +1,46 @@
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.DisburseTopupDetail = void 0;
13
+ const class_validator_1 = require("class-validator");
14
+ /** Datos de la recarga Bait de una dispersión `MEX_REWARD_TOPUP`. */
15
+ class DisburseTopupDetail {
16
+ }
17
+ exports.DisburseTopupDetail = DisburseTopupDetail;
18
+ __decorate([
19
+ (0, class_validator_1.IsString)(),
20
+ (0, class_validator_1.Matches)(/^[0-9]{10}$/),
21
+ __metadata("design:type", String)
22
+ ], DisburseTopupDetail.prototype, "phone", void 0);
23
+ __decorate([
24
+ (0, class_validator_1.IsString)(),
25
+ (0, class_validator_1.IsNotEmpty)(),
26
+ (0, class_validator_1.MaxLength)(64),
27
+ __metadata("design:type", String)
28
+ ], DisburseTopupDetail.prototype, "moduleName", void 0);
29
+ __decorate([
30
+ (0, class_validator_1.IsString)(),
31
+ (0, class_validator_1.IsNotEmpty)(),
32
+ (0, class_validator_1.MaxLength)(64),
33
+ __metadata("design:type", String)
34
+ ], DisburseTopupDetail.prototype, "benefitId", void 0);
35
+ __decorate([
36
+ (0, class_validator_1.IsString)(),
37
+ (0, class_validator_1.IsNotEmpty)(),
38
+ (0, class_validator_1.MaxLength)(256),
39
+ __metadata("design:type", String)
40
+ ], DisburseTopupDetail.prototype, "leafId", void 0);
41
+ __decorate([
42
+ (0, class_validator_1.IsString)(),
43
+ (0, class_validator_1.IsNotEmpty)(),
44
+ (0, class_validator_1.MaxLength)(64),
45
+ __metadata("design:type", String)
46
+ ], DisburseTopupDetail.prototype, "productId", void 0);
@@ -9,4 +9,6 @@ export declare class UpsertProductConfigRequest {
9
9
  allowPartial: boolean;
10
10
  retryPolicy: RetryPolicyDto;
11
11
  reportTargets: string[];
12
+ /** Centro de costo con el que el ledger registra los cobros de este producto. */
13
+ costCenterId?: string;
12
14
  }
@@ -41,3 +41,9 @@ __decorate([
41
41
  (0, class_validator_1.IsString)({ each: true }),
42
42
  __metadata("design:type", Array)
43
43
  ], UpsertProductConfigRequest.prototype, "reportTargets", void 0);
44
+ __decorate([
45
+ (0, class_validator_1.IsOptional)(),
46
+ (0, class_validator_1.IsString)(),
47
+ (0, class_validator_1.IsNotEmpty)(),
48
+ __metadata("design:type", String)
49
+ ], UpsertProductConfigRequest.prototype, "costCenterId", void 0);
@@ -8,5 +8,7 @@ export declare enum DisbursementType {
8
8
  /** Comisión de vendedor. */
9
9
  MEX_SELLER_COMMISSION = "MEX_SELLER_COMMISSION",
10
10
  /** Abono de recompensas de `rewards-business`. */
11
- MEX_REWARD = "MEX_REWARD"
11
+ MEX_REWARD = "MEX_REWARD",
12
+ /** Recarga Bait de recompensas de `rewards-business`. */
13
+ MEX_REWARD_TOPUP = "MEX_REWARD_TOPUP"
12
14
  }
@@ -13,4 +13,6 @@ var DisbursementType;
13
13
  DisbursementType["MEX_SELLER_COMMISSION"] = "MEX_SELLER_COMMISSION";
14
14
  /** Abono de recompensas de `rewards-business`. */
15
15
  DisbursementType["MEX_REWARD"] = "MEX_REWARD";
16
+ /** Recarga Bait de recompensas de `rewards-business`. */
17
+ DisbursementType["MEX_REWARD_TOPUP"] = "MEX_REWARD_TOPUP";
16
18
  })(DisbursementType || (exports.DisbursementType = DisbursementType = {}));
@@ -30,6 +30,7 @@ export * from './dtos/RegisterCollectibleResponse';
30
30
  export * from './dtos/CollectionPricingDto';
31
31
  export * from './dtos/ResolvePendingChargesRequest';
32
32
  export * from './dtos/MoneyInEvent';
33
+ export * from './dtos/DisburseTopupDetail';
33
34
  export * from './dtos/DisburseRequest';
34
35
  export * from './dtos/DisburseResponse';
35
36
  export * from './dtos/CollectionIntentSummaryDto';
@@ -49,6 +49,7 @@ __exportStar(require("./dtos/RegisterCollectibleResponse"), exports);
49
49
  __exportStar(require("./dtos/CollectionPricingDto"), exports);
50
50
  __exportStar(require("./dtos/ResolvePendingChargesRequest"), exports);
51
51
  __exportStar(require("./dtos/MoneyInEvent"), exports);
52
+ __exportStar(require("./dtos/DisburseTopupDetail"), exports);
52
53
  __exportStar(require("./dtos/DisburseRequest"), exports);
53
54
  __exportStar(require("./dtos/DisburseResponse"), exports);
54
55
  // DTOs — backoffice de monitoreo
@@ -61,6 +61,7 @@ export declare enum ProductSubtypeEnum {
61
61
  SUREKEEP_BONUS_MX = "BONUS_MX",
62
62
  SUREKEEP_SELLER_COMMISSION_MX = "SELLER_COMMISSION_MX",
63
63
  REWARD_MX = "REWARD_MX",
64
+ REWARD_TOPUP_MX = "REWARD_TOPUP_MX",
64
65
  STORE_PHONE = "PHONE",
65
66
  REFUND_PCF_DICTAMEN_PROCEDE = "DICTAMEN_PROCEDE",
66
67
  REFUND_PCF_AUTO_T2_SIN_2FA = "AUTO_T2_SIN_2FA",