@fiado/type-kit 3.613.0 → 3.615.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 (153) hide show
  1. package/_test_/unit/loanCredit/dtos/originateLoanCreditRequest.test.ts +9 -0
  2. package/_test_/unit/loanCredit/segmentFactsDtos.test.ts +80 -0
  3. package/_test_/unit/loanOfferings/promotionRetailerScope.test.ts +79 -0
  4. package/_test_/unit/loanOfferings/segmentCatalog.test.ts +4 -2
  5. package/_test_/unit/loanOfferings/segmentCohorts.test.ts +333 -0
  6. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.d.ts +2 -0
  7. package/bin/loanCredit/dtos/requests/OriginateLoanCreditRequest.js +7 -0
  8. package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.d.ts +4 -0
  9. package/bin/loanCredit/dtos/requests/SegmentFactsBatchRequest.js +27 -0
  10. package/bin/loanCredit/dtos/requests/SegmentFactsPageRequest.d.ts +7 -0
  11. package/bin/loanCredit/dtos/requests/SegmentFactsPageRequest.js +32 -0
  12. package/bin/loanCredit/dtos/responses/BorrowerSegmentFacts.d.ts +22 -0
  13. package/bin/loanCredit/dtos/responses/BorrowerSegmentFacts.js +2 -0
  14. package/bin/loanCredit/dtos/responses/SegmentFactsBatchResponse.d.ts +5 -0
  15. package/bin/loanCredit/dtos/responses/SegmentFactsBatchResponse.js +2 -0
  16. package/bin/loanCredit/dtos/responses/SegmentFactsPageResponse.d.ts +7 -0
  17. package/bin/loanCredit/dtos/responses/SegmentFactsPageResponse.js +2 -0
  18. package/bin/loanCredit/index.d.ts +5 -0
  19. package/bin/loanCredit/index.js +6 -0
  20. package/bin/loanOfferings/constants/promotionRetailerScope.d.ts +2 -0
  21. package/bin/loanOfferings/constants/promotionRetailerScope.js +5 -0
  22. package/bin/loanOfferings/dtos/requests/ChangeCohortStatusRequest.d.ts +9 -0
  23. package/bin/loanOfferings/dtos/requests/ChangeCohortStatusRequest.js +34 -0
  24. package/bin/loanOfferings/dtos/requests/CohortCondition.d.ts +15 -0
  25. package/bin/loanOfferings/dtos/requests/CohortCondition.js +48 -0
  26. package/bin/loanOfferings/dtos/requests/CreateCohortRequest.d.ts +11 -0
  27. package/bin/loanOfferings/dtos/requests/CreateCohortRequest.js +44 -0
  28. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.d.ts +2 -0
  29. package/bin/loanOfferings/dtos/requests/CreatePromotionRequest.js +10 -0
  30. package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.d.ts +7 -8
  31. package/bin/loanOfferings/dtos/requests/CreateSegmentRequest.js +5 -3
  32. package/bin/loanOfferings/dtos/requests/CustomerSegmentFacts.d.ts +30 -0
  33. package/bin/loanOfferings/dtos/requests/CustomerSegmentFacts.js +92 -0
  34. package/bin/loanOfferings/dtos/requests/ListCohortsQuery.d.ts +5 -0
  35. package/bin/loanOfferings/dtos/requests/ListCohortsQuery.js +25 -0
  36. package/bin/loanOfferings/dtos/requests/ListPromotedSkusRequest.d.ts +2 -0
  37. package/bin/loanOfferings/dtos/requests/ListPromotedSkusRequest.js +7 -0
  38. package/bin/loanOfferings/dtos/requests/ListSkuPromotionsRequest.d.ts +2 -0
  39. package/bin/loanOfferings/dtos/requests/ListSkuPromotionsRequest.js +7 -0
  40. package/bin/loanOfferings/dtos/requests/ResolveCustomerSegmentsRequest.d.ts +4 -20
  41. package/bin/loanOfferings/dtos/requests/ResolveCustomerSegmentsRequest.js +4 -48
  42. package/bin/loanOfferings/dtos/requests/ResolveSegmentMembersRequest.d.ts +8 -0
  43. package/bin/loanOfferings/dtos/requests/ResolveSegmentMembersRequest.js +31 -0
  44. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.d.ts +2 -0
  45. package/bin/loanOfferings/dtos/requests/SimulateCreditPlanRequest.js +7 -0
  46. package/bin/loanOfferings/dtos/requests/TestCohortRequest.d.ts +9 -0
  47. package/bin/loanOfferings/dtos/requests/TestCohortRequest.js +36 -0
  48. package/bin/loanOfferings/dtos/requests/TestCustomerSegmentsRequest.d.ts +4 -0
  49. package/bin/loanOfferings/dtos/requests/TestCustomerSegmentsRequest.js +23 -0
  50. package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.d.ts +12 -0
  51. package/bin/loanOfferings/dtos/requests/UpdateCohortRequest.js +53 -0
  52. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.d.ts +2 -0
  53. package/bin/loanOfferings/dtos/requests/UpdatePromotionRequest.js +10 -0
  54. package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.d.ts +2 -2
  55. package/bin/loanOfferings/dtos/requests/UpdateSegmentRequest.js +3 -1
  56. package/bin/loanOfferings/dtos/responses/CohortCatalogResponse.d.ts +33 -0
  57. package/bin/loanOfferings/dtos/responses/CohortCatalogResponse.js +2 -0
  58. package/bin/loanOfferings/dtos/responses/CohortLogEntryResponse.d.ts +11 -0
  59. package/bin/loanOfferings/dtos/responses/CohortLogEntryResponse.js +2 -0
  60. package/bin/loanOfferings/dtos/responses/CohortResponse.d.ts +43 -0
  61. package/bin/loanOfferings/dtos/responses/CohortResponse.js +2 -0
  62. package/bin/loanOfferings/dtos/responses/PromotionResponse.d.ts +2 -0
  63. package/bin/loanOfferings/dtos/responses/SegmentCatalogResponse.d.ts +5 -2
  64. package/bin/loanOfferings/dtos/responses/SegmentLogEntryResponse.d.ts +11 -0
  65. package/bin/loanOfferings/dtos/responses/SegmentLogEntryResponse.js +2 -0
  66. package/bin/loanOfferings/dtos/responses/SegmentMembersResponse.d.ts +19 -0
  67. package/bin/loanOfferings/dtos/responses/SegmentMembersResponse.js +2 -0
  68. package/bin/loanOfferings/dtos/responses/SegmentResponse.d.ts +12 -6
  69. package/bin/loanOfferings/dtos/responses/SegmentUsageResponse.d.ts +18 -0
  70. package/bin/loanOfferings/dtos/responses/SegmentUsageResponse.js +2 -0
  71. package/bin/loanOfferings/dtos/responses/SegmentationRecountRunResponse.d.ts +23 -0
  72. package/bin/loanOfferings/dtos/responses/SegmentationRecountRunResponse.js +2 -0
  73. package/bin/loanOfferings/dtos/responses/SegmentationRecountStartResponse.d.ts +6 -0
  74. package/bin/loanOfferings/dtos/responses/SegmentationRecountStartResponse.js +2 -0
  75. package/bin/loanOfferings/dtos/responses/TestCohortResponse.d.ts +17 -0
  76. package/bin/loanOfferings/dtos/responses/TestCohortResponse.js +2 -0
  77. package/bin/loanOfferings/dtos/responses/TestCustomerSegmentsResponse.d.ts +23 -0
  78. package/bin/loanOfferings/dtos/responses/TestCustomerSegmentsResponse.js +2 -0
  79. package/bin/loanOfferings/enums/CohortConditionOutcomeEnum.d.ts +10 -0
  80. package/bin/loanOfferings/enums/CohortConditionOutcomeEnum.js +14 -0
  81. package/bin/loanOfferings/enums/CohortFactKeyEnum.d.ts +17 -0
  82. package/bin/loanOfferings/enums/CohortFactKeyEnum.js +21 -0
  83. package/bin/loanOfferings/enums/CohortFactTypeEnum.d.ts +9 -0
  84. package/bin/loanOfferings/enums/CohortFactTypeEnum.js +13 -0
  85. package/bin/loanOfferings/enums/CohortLogEventEnum.d.ts +10 -0
  86. package/bin/loanOfferings/enums/CohortLogEventEnum.js +14 -0
  87. package/bin/loanOfferings/enums/CohortOperatorEnum.d.ts +15 -0
  88. package/bin/loanOfferings/enums/CohortOperatorEnum.js +19 -0
  89. package/bin/loanOfferings/enums/CohortStatusEnum.d.ts +9 -0
  90. package/bin/loanOfferings/enums/CohortStatusEnum.js +13 -0
  91. package/bin/loanOfferings/enums/SegmentFilterIdEnum.d.ts +2 -0
  92. package/bin/loanOfferings/enums/SegmentFilterIdEnum.js +2 -0
  93. package/bin/loanOfferings/enums/SegmentLogEventEnum.d.ts +10 -0
  94. package/bin/loanOfferings/enums/SegmentLogEventEnum.js +14 -0
  95. package/bin/loanOfferings/enums/SegmentMembershipReasonEnum.d.ts +9 -0
  96. package/bin/loanOfferings/enums/SegmentMembershipReasonEnum.js +13 -0
  97. package/bin/loanOfferings/enums/SegmentationRecountStatusEnum.d.ts +9 -0
  98. package/bin/loanOfferings/enums/SegmentationRecountStatusEnum.js +13 -0
  99. package/bin/loanOfferings/enums/SegmentationRecountTriggerEnum.d.ts +8 -0
  100. package/bin/loanOfferings/enums/SegmentationRecountTriggerEnum.js +12 -0
  101. package/bin/loanOfferings/index.d.ts +30 -0
  102. package/bin/loanOfferings/index.js +30 -0
  103. package/package.json +1 -1
  104. package/src/loanCredit/dtos/requests/OriginateLoanCreditRequest.ts +7 -0
  105. package/src/loanCredit/dtos/requests/SegmentFactsBatchRequest.ts +13 -0
  106. package/src/loanCredit/dtos/requests/SegmentFactsPageRequest.ts +19 -0
  107. package/src/loanCredit/dtos/responses/BorrowerSegmentFacts.ts +24 -0
  108. package/src/loanCredit/dtos/responses/SegmentFactsBatchResponse.ts +6 -0
  109. package/src/loanCredit/dtos/responses/SegmentFactsPageResponse.ts +8 -0
  110. package/src/loanCredit/index.ts +7 -0
  111. package/src/loanOfferings/constants/promotionRetailerScope.ts +2 -0
  112. package/src/loanOfferings/dtos/requests/ChangeCohortStatusRequest.ts +19 -0
  113. package/src/loanOfferings/dtos/requests/CohortCondition.ts +33 -0
  114. package/src/loanOfferings/dtos/requests/CreateCohortRequest.ts +29 -0
  115. package/src/loanOfferings/dtos/requests/CreatePromotionRequest.ts +12 -0
  116. package/src/loanOfferings/dtos/requests/CreateSegmentRequest.ts +10 -9
  117. package/src/loanOfferings/dtos/requests/CustomerSegmentFacts.ts +79 -0
  118. package/src/loanOfferings/dtos/requests/ListCohortsQuery.ts +11 -0
  119. package/src/loanOfferings/dtos/requests/ListPromotedSkusRequest.ts +7 -0
  120. package/src/loanOfferings/dtos/requests/ListSkuPromotionsRequest.ts +7 -0
  121. package/src/loanOfferings/dtos/requests/ResolveCustomerSegmentsRequest.ts +5 -49
  122. package/src/loanOfferings/dtos/requests/ResolveSegmentMembersRequest.ts +17 -0
  123. package/src/loanOfferings/dtos/requests/SimulateCreditPlanRequest.ts +8 -1
  124. package/src/loanOfferings/dtos/requests/TestCohortRequest.ts +21 -0
  125. package/src/loanOfferings/dtos/requests/TestCustomerSegmentsRequest.ts +9 -0
  126. package/src/loanOfferings/dtos/requests/UpdateCohortRequest.ts +37 -0
  127. package/src/loanOfferings/dtos/requests/UpdatePromotionRequest.ts +12 -0
  128. package/src/loanOfferings/dtos/requests/UpdateSegmentRequest.ts +5 -3
  129. package/src/loanOfferings/dtos/responses/CohortCatalogResponse.ts +36 -0
  130. package/src/loanOfferings/dtos/responses/CohortLogEntryResponse.ts +12 -0
  131. package/src/loanOfferings/dtos/responses/CohortResponse.ts +47 -0
  132. package/src/loanOfferings/dtos/responses/PromotionResponse.ts +2 -0
  133. package/src/loanOfferings/dtos/responses/SegmentCatalogResponse.ts +5 -2
  134. package/src/loanOfferings/dtos/responses/SegmentLogEntryResponse.ts +12 -0
  135. package/src/loanOfferings/dtos/responses/SegmentMembersResponse.ts +21 -0
  136. package/src/loanOfferings/dtos/responses/SegmentResponse.ts +12 -6
  137. package/src/loanOfferings/dtos/responses/SegmentUsageResponse.ts +20 -0
  138. package/src/loanOfferings/dtos/responses/SegmentationRecountRunResponse.ts +25 -0
  139. package/src/loanOfferings/dtos/responses/SegmentationRecountStartResponse.ts +7 -0
  140. package/src/loanOfferings/dtos/responses/TestCohortResponse.ts +19 -0
  141. package/src/loanOfferings/dtos/responses/TestCustomerSegmentsResponse.ts +25 -0
  142. package/src/loanOfferings/enums/CohortConditionOutcomeEnum.ts +10 -0
  143. package/src/loanOfferings/enums/CohortFactKeyEnum.ts +17 -0
  144. package/src/loanOfferings/enums/CohortFactTypeEnum.ts +9 -0
  145. package/src/loanOfferings/enums/CohortLogEventEnum.ts +10 -0
  146. package/src/loanOfferings/enums/CohortOperatorEnum.ts +15 -0
  147. package/src/loanOfferings/enums/CohortStatusEnum.ts +9 -0
  148. package/src/loanOfferings/enums/SegmentFilterIdEnum.ts +2 -0
  149. package/src/loanOfferings/enums/SegmentLogEventEnum.ts +10 -0
  150. package/src/loanOfferings/enums/SegmentMembershipReasonEnum.ts +9 -0
  151. package/src/loanOfferings/enums/SegmentationRecountStatusEnum.ts +9 -0
  152. package/src/loanOfferings/enums/SegmentationRecountTriggerEnum.ts +8 -0
  153. package/src/loanOfferings/index.ts +30 -0
@@ -62,4 +62,13 @@ describe('OriginateLoanCreditRequest', () => {
62
62
  const bad = await validateWhitelisted({ ...BASE, productCategory: 'CAR', productTier: 'ULTRA' });
63
63
  expect(bad.errors.map((error) => error.property).sort()).toEqual(['productCategory', 'productTier']);
64
64
  });
65
+
66
+ it('acepta la cadena opcional y rechaza la cadena vacía', async () => {
67
+ const ok = await validateWhitelisted({ ...BASE, retailerId: 'RT-1' });
68
+ expect(ok.errors).toEqual([]);
69
+ expect(ok.dto.retailerId).toBe('RT-1');
70
+
71
+ const bad = await validateWhitelisted({ ...BASE, retailerId: '' });
72
+ expect(bad.errors.map((error) => error.property)).toEqual(['retailerId']);
73
+ });
65
74
  });
@@ -0,0 +1,80 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import {
5
+ SegmentFactsBatchRequest,
6
+ SegmentFactsBatchResponse,
7
+ SegmentFactsPageRequest,
8
+ SegmentFactsPageResponse,
9
+ } from '../../../src/loanCredit/index';
10
+ import { CreditPlanLevelEnum, CustomerSegmentFacts } from '../../../src/loanOfferings/index';
11
+
12
+ type Ctor<T> = new () => T;
13
+
14
+ const errorProperties = async <T extends object>(cls: Ctor<T>, raw: Record<string, unknown>): Promise<string[]> =>
15
+ (await validate(plainToInstance(cls, raw, { excludeExtraneousValues: true }))).map((error) => error.property);
16
+
17
+ describe('SegmentFactsBatchRequest', () => {
18
+ it('acepta de 1 a 100 ids', async () => {
19
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: ['RC-1'] })).toEqual([]);
20
+ const hundred = Array.from({ length: 100 }, (_, i) => `RC-${i}`);
21
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: hundred })).toEqual([]);
22
+ });
23
+
24
+ it('rechaza el lote vacío, el de más de 100 y los ids que no son texto', async () => {
25
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: [] })).toEqual(['retailCustomerIds']);
26
+ const tooMany = Array.from({ length: 101 }, (_, i) => `RC-${i}`);
27
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: tooMany })).toEqual(['retailCustomerIds']);
28
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: [1] })).toEqual(['retailCustomerIds']);
29
+ expect(await errorProperties(SegmentFactsBatchRequest, {})).toEqual(['retailCustomerIds']);
30
+ });
31
+
32
+ it('rechaza ids vacíos', async () => {
33
+ expect(await errorProperties(SegmentFactsBatchRequest, { retailCustomerIds: ['RC-1', ''] })).toEqual(['retailCustomerIds']);
34
+ });
35
+ });
36
+
37
+ describe('SegmentFactsPageRequest', () => {
38
+ it('la primera página va sin cursor ni límite', async () => {
39
+ expect(await errorProperties(SegmentFactsPageRequest, {})).toEqual([]);
40
+ });
41
+
42
+ it('acepta cursor y un límite entre 1 y 100', async () => {
43
+ expect(await errorProperties(SegmentFactsPageRequest, { cursor: 'abc', limit: 100 })).toEqual([]);
44
+ expect(await errorProperties(SegmentFactsPageRequest, { limit: 0 })).toEqual(['limit']);
45
+ expect(await errorProperties(SegmentFactsPageRequest, { limit: 101 })).toEqual(['limit']);
46
+ expect(await errorProperties(SegmentFactsPageRequest, { limit: 2.5 })).toEqual(['limit']);
47
+ expect(await errorProperties(SegmentFactsPageRequest, { limit: '50' })).toEqual(['limit']);
48
+ });
49
+ });
50
+
51
+ describe('Responses de segment-facts', () => {
52
+ it('los datos del acreditado sirven directo como datos del cliente para segmentar', () => {
53
+ const batch: SegmentFactsBatchResponse = {
54
+ items: [
55
+ {
56
+ retailCustomerId: 'RC-1',
57
+ borrowerId: null,
58
+ facts: {
59
+ totalCredits: 0,
60
+ activeCredits: 0,
61
+ completedCredits: 0,
62
+ lifetimeWriteOffs: 0,
63
+ currentArrearsDays: 0,
64
+ delinquencyDaysLast6M: 0,
65
+ lifetimeRestructures: 0,
66
+ },
67
+ },
68
+ ],
69
+ };
70
+ const page: SegmentFactsPageResponse = {
71
+ items: [{ ...batch.items[0], borrowerId: 'BRW-1', facts: { ...batch.items[0].facts, sciLevel: CreditPlanLevelEnum.GOLD } }],
72
+ nextCursor: null,
73
+ };
74
+ const facts: CustomerSegmentFacts = page.items[0].facts;
75
+
76
+ expect(facts.sciLevel).toBe(CreditPlanLevelEnum.GOLD);
77
+ expect(batch.items[0].facts.sciLevel).toBeUndefined();
78
+ expect(page.nextCursor).toBeNull();
79
+ });
80
+ });
@@ -0,0 +1,79 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate } from 'class-validator';
4
+ import {
5
+ CreatePromotionRequest,
6
+ ListPromotedSkusRequest,
7
+ ListSkuPromotionsRequest,
8
+ PROMOTION_RETAILER_IDS_MAX_ITEMS,
9
+ SimulateCreditPlanRequest,
10
+ UpdatePromotionRequest,
11
+ } from '../../../src/loanOfferings/index';
12
+ import type { PromotionResponse } from '../../../src/loanOfferings/index';
13
+
14
+ type DtoClass = new () => object;
15
+
16
+ const errorsOf = async (dtoClass: DtoClass, body: object): Promise<string[]> =>
17
+ (await validate(plainToInstance(dtoClass, body, { excludeExtraneousValues: true }) as object)).map((e) => e.property);
18
+
19
+ const createBase = {
20
+ name: 'Tasa preferente cadena',
21
+ description: 'Baja la tasa en una cadena',
22
+ effect: { downPaymentDeltaPct: 0.05 },
23
+ segmentId: 'SEG_SILVER',
24
+ targetLevels: ['SILVER'],
25
+ };
26
+
27
+ describe('alcance por cadena en promociones de crédito', () => {
28
+ it('Create acepta cadenas, null y lista vacía como «todas»', async () => {
29
+ const dto = plainToInstance(CreatePromotionRequest, { ...createBase, retailerIds: ['RT-1', 'RT-2'] }, { excludeExtraneousValues: true });
30
+ expect(await validate(dto)).toHaveLength(0);
31
+ expect(dto.retailerIds).toEqual(['RT-1', 'RT-2']);
32
+ expect(await errorsOf(CreatePromotionRequest, { ...createBase, retailerIds: null })).toEqual([]);
33
+ expect(await errorsOf(CreatePromotionRequest, { ...createBase, retailerIds: [] })).toEqual([]);
34
+ });
35
+
36
+ it.each([CreatePromotionRequest, UpdatePromotionRequest])('%p rechaza cadenas mal formadas', async (dtoClass) => {
37
+ const body = dtoClass === CreatePromotionRequest ? createBase : {};
38
+ const tooMany = Array.from({ length: PROMOTION_RETAILER_IDS_MAX_ITEMS + 1 }, (_v, i) => `RT-${i}`);
39
+ expect(await errorsOf(dtoClass, { ...body, retailerIds: 'RT-1' })).toContain('retailerIds');
40
+ expect(await errorsOf(dtoClass, { ...body, retailerIds: [7] })).toContain('retailerIds');
41
+ expect(await errorsOf(dtoClass, { ...body, retailerIds: [''] })).toContain('retailerIds');
42
+ expect(await errorsOf(dtoClass, { ...body, retailerIds: tooMany })).toContain('retailerIds');
43
+ expect(await errorsOf(dtoClass, { ...body, retailerIds: tooMany.slice(1) })).toEqual([]);
44
+ });
45
+
46
+ it('Update acepta el parche solo con cadenas', async () => {
47
+ expect(PROMOTION_RETAILER_IDS_MAX_ITEMS).toBe(50);
48
+ expect(await errorsOf(UpdatePromotionRequest, { retailerIds: ['RT-1'] })).toEqual([]);
49
+ expect(await errorsOf(UpdatePromotionRequest, { retailerIds: null })).toEqual([]);
50
+ });
51
+
52
+ it('la respuesta expone las cadenas con null como «todas»', () => {
53
+ const scope: Pick<PromotionResponse, 'retailerIds'> = { retailerIds: null };
54
+ expect(scope.retailerIds).toBeNull();
55
+ });
56
+
57
+ it('el simulador recibe la cadena opcional como string', async () => {
58
+ const base = { productPriceCents: 500000, downPaymentPct: 0.2, termWeeks: 26 };
59
+ const dto = plainToInstance(SimulateCreditPlanRequest, { ...base, retailerId: 'RT-1' }, { excludeExtraneousValues: true });
60
+ expect(await validate(dto)).toHaveLength(0);
61
+ expect(dto.retailerId).toBe('RT-1');
62
+ expect(await errorsOf(SimulateCreditPlanRequest, { ...base, retailerId: 7 })).toContain('retailerId');
63
+ expect(await errorsOf(SimulateCreditPlanRequest, { ...base, retailerId: '' })).toContain('retailerId');
64
+ });
65
+ });
66
+
67
+ describe.each([ListPromotedSkusRequest, ListSkuPromotionsRequest])('%p con cadena', (dtoClass) => {
68
+ const valid = { storeId: 'st-1', items: [{ sku: 'SKU-1' }] };
69
+
70
+ it('acepta la cadena opcional junto a la tienda', async () => {
71
+ expect(await errorsOf(dtoClass, valid)).toEqual([]);
72
+ expect(await errorsOf(dtoClass, { ...valid, retailerId: 'RT-1' })).toEqual([]);
73
+ });
74
+
75
+ it('rechaza la cadena vacía o no string', async () => {
76
+ expect(await errorsOf(dtoClass, { ...valid, retailerId: '' })).toContain('retailerId');
77
+ expect(await errorsOf(dtoClass, { ...valid, retailerId: 7 })).toContain('retailerId');
78
+ });
79
+ });
@@ -5,7 +5,6 @@ import {
5
5
  ListSegmentCatalogQuery,
6
6
  SegmentCatalogItem,
7
7
  SegmentCatalogResponse,
8
- SegmentFilterIdEnum,
9
8
  SegmentStatusEnum,
10
9
  } from '../../../src/loanOfferings/index';
11
10
 
@@ -45,10 +44,13 @@ describe('SegmentCatalogResponse', () => {
45
44
  segmentId: 'seg-1',
46
45
  name: 'Nuevos',
47
46
  status: SegmentStatusEnum.ACTIVE,
48
- filterIds: Object.values(SegmentFilterIdEnum).slice(0, 1),
47
+ cohortIds: ['COH-LEVEL-GOLD'],
48
+ dependsOnFacts: true,
49
+ filterIds: ['COH-LEVEL-GOLD'],
49
50
  isDefault: false,
50
51
  };
51
52
  const response: SegmentCatalogResponse = { segments: [item] };
52
53
  expect(response.segments[0].segmentId).toBe('seg-1');
54
+ expect(response.segments[0].filterIds).toEqual(response.segments[0].cohortIds);
53
55
  });
54
56
  });
@@ -0,0 +1,333 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validate, ValidationError } from 'class-validator';
4
+ import {
5
+ ChangeCohortStatusRequest,
6
+ CohortConditionOutcomeEnum,
7
+ CohortFactKeyEnum,
8
+ CohortOperatorEnum,
9
+ CohortResponse,
10
+ CohortStatusEnum,
11
+ CreateCohortRequest,
12
+ CreateSegmentRequest,
13
+ CreditPlanLevelEnum,
14
+ CustomerSegmentFacts,
15
+ ListCohortsQuery,
16
+ ResolveCustomerSegmentsRequest,
17
+ ResolveSegmentMembersRequest,
18
+ SegmentMembersResponse,
19
+ SegmentMembershipReasonEnum,
20
+ SegmentResponse,
21
+ SegmentationRecountRunsResponse,
22
+ SegmentationRecountStatusEnum,
23
+ SegmentationRecountTriggerEnum,
24
+ SegmentColorSlotEnum,
25
+ SegmentStatusEnum,
26
+ TestCohortRequest,
27
+ TestCohortResponse,
28
+ TestCustomerSegmentsRequest,
29
+ TestCustomerSegmentsResponse,
30
+ UpdateCohortRequest,
31
+ UpdateSegmentRequest,
32
+ } from '../../../src/loanOfferings/index';
33
+
34
+ type Ctor<T> = new () => T;
35
+
36
+ const toDto = <T extends object>(cls: Ctor<T>, raw: Record<string, unknown>): T =>
37
+ plainToInstance(cls, raw, { excludeExtraneousValues: true });
38
+
39
+ const errorProperties = async <T extends object>(cls: Ctor<T>, raw: Record<string, unknown>): Promise<string[]> =>
40
+ (await validate(toDto(cls, raw))).map((error) => error.property).sort();
41
+
42
+ const nestedErrors = (errors: ValidationError[]): string[] =>
43
+ errors.flatMap((error) => [error.property, ...nestedErrors(error.children ?? [])]);
44
+
45
+ const GOLD = { field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.EQUALS, value: 'GOLD' };
46
+ const COMPLETED_GTE_3 = { field: CohortFactKeyEnum.COMPLETED_CREDITS, op: CohortOperatorEnum.GTE, value: 3 };
47
+
48
+ describe('Enums de cohorts', () => {
49
+ it('las llaves del catálogo coinciden con los campos de CustomerSegmentFacts', () => {
50
+ expect(Object.values(CohortFactKeyEnum).sort()).toEqual(
51
+ [
52
+ 'activeCredits',
53
+ 'completedCredits',
54
+ 'currentArrearsDays',
55
+ 'delinquencyDaysLast6M',
56
+ 'hasPcfAccount',
57
+ 'isEmployee',
58
+ 'lifetimeRestructures',
59
+ 'lifetimeWriteOffs',
60
+ 'sciLevel',
61
+ 'totalCredits',
62
+ ].sort(),
63
+ );
64
+ });
65
+
66
+ it('declara los operadores del contrato', () => {
67
+ expect(Object.values(CohortOperatorEnum)).toEqual(['EQUALS', 'NOT_EQUALS', 'GT', 'GTE', 'LT', 'LTE', 'IN', 'NOT_IN']);
68
+ });
69
+ });
70
+
71
+ describe('CustomerSegmentFacts y ResolveCustomerSegmentsRequest', () => {
72
+ const FULL_FACTS = {
73
+ sciLevel: CreditPlanLevelEnum.GOLD,
74
+ totalCredits: 4,
75
+ activeCredits: 1,
76
+ completedCredits: 3,
77
+ lifetimeWriteOffs: 0,
78
+ currentArrearsDays: 0,
79
+ delinquencyDaysLast6M: 0,
80
+ lifetimeRestructures: 0,
81
+ isEmployee: false,
82
+ hasPcfAccount: true,
83
+ };
84
+
85
+ it('acepta todos los datos opcionales vacíos', async () => {
86
+ expect(await errorProperties(CustomerSegmentFacts, {})).toEqual([]);
87
+ });
88
+
89
+ it('acepta el juego completo de datos', async () => {
90
+ expect(await errorProperties(CustomerSegmentFacts, FULL_FACTS)).toEqual([]);
91
+ });
92
+
93
+ it('rechaza conteos negativos, con decimales o como texto', async () => {
94
+ expect(await errorProperties(CustomerSegmentFacts, { totalCredits: -1 })).toEqual(['totalCredits']);
95
+ expect(await errorProperties(CustomerSegmentFacts, { activeCredits: 1.5 })).toEqual(['activeCredits']);
96
+ expect(await errorProperties(CustomerSegmentFacts, { currentArrearsDays: '3' })).toEqual(['currentArrearsDays']);
97
+ });
98
+
99
+ it('rechaza un nivel fuera del enum y un booleano como texto', async () => {
100
+ expect(await errorProperties(CustomerSegmentFacts, { sciLevel: 'PLATINUM' })).toEqual(['sciLevel']);
101
+ expect(await errorProperties(CustomerSegmentFacts, { hasPcfAccount: 'true' })).toEqual(['hasPcfAccount']);
102
+ });
103
+
104
+ it('el resolve sigue aceptando solo el retailCustomerId', async () => {
105
+ expect(await errorProperties(ResolveCustomerSegmentsRequest, { retailCustomerId: 'RC-1' })).toEqual([]);
106
+ });
107
+
108
+ it('el resolve hereda y conserva los datos del cliente', async () => {
109
+ const dto = toDto(ResolveCustomerSegmentsRequest, { retailCustomerId: 'RC-1', ...FULL_FACTS, extra: 'x' });
110
+ expect(await validate(dto)).toHaveLength(0);
111
+ expect(dto).toEqual({ retailCustomerId: 'RC-1', ...FULL_FACTS });
112
+ expect(await errorProperties(ResolveCustomerSegmentsRequest, { retailCustomerId: 'RC-1', totalCredits: -1 })).toEqual([
113
+ 'totalCredits',
114
+ ]);
115
+ });
116
+
117
+ it('el resolve exige retailCustomerId', async () => {
118
+ expect(await errorProperties(ResolveCustomerSegmentsRequest, {})).toEqual(['retailCustomerId']);
119
+ });
120
+ });
121
+
122
+ describe('CreateCohortRequest', () => {
123
+ it('acepta un cohort con condiciones de número y de selección', async () => {
124
+ expect(await errorProperties(CreateCohortRequest, { name: 'VIP', conditions: [GOLD, COMPLETED_GTE_3] })).toEqual([]);
125
+ });
126
+
127
+ it('acepta IN con lista de valores', async () => {
128
+ const condition = { field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.IN, values: ['GOLD', 'SILVER'] };
129
+ expect(await errorProperties(CreateCohortRequest, { name: 'Oro o Plata', conditions: [condition] })).toEqual([]);
130
+ });
131
+
132
+ it('exige entre 1 y 10 condiciones', async () => {
133
+ expect(await errorProperties(CreateCohortRequest, { name: 'Vacío', conditions: [] })).toEqual(['conditions']);
134
+ expect(
135
+ await errorProperties(CreateCohortRequest, { name: 'Largo', conditions: Array.from({ length: 11 }, () => GOLD) }),
136
+ ).toEqual(['conditions']);
137
+ expect(await errorProperties(CreateCohortRequest, { name: 'Sin condiciones' })).toEqual(['conditions']);
138
+ });
139
+
140
+ it('valida cada condición: campo y operador del catálogo, lista de hasta 10 textos', async () => {
141
+ const dto = toDto(CreateCohortRequest, {
142
+ name: 'Malo',
143
+ conditions: [
144
+ { field: 'age', op: CohortOperatorEnum.GT, value: 18 },
145
+ { field: CohortFactKeyEnum.SCI_LEVEL, op: 'CONTAINS', value: 'G' },
146
+ { field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.IN, values: Array.from({ length: 11 }, () => 'GOLD') },
147
+ { field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.IN, values: [1] },
148
+ ],
149
+ });
150
+ const errors = nestedErrors(await validate(dto));
151
+ expect(errors.filter((property) => ['field', 'op', 'values'].includes(property)).sort()).toEqual([
152
+ 'field',
153
+ 'op',
154
+ 'values',
155
+ 'values',
156
+ ]);
157
+ });
158
+
159
+ it('rechaza opciones vacías en la lista de valores', async () => {
160
+ const condition = { field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.IN, values: ['GOLD', ''] };
161
+ const errors = nestedErrors(await validate(toDto(CreateCohortRequest, { name: 'Vacía', conditions: [condition] })));
162
+ expect(errors).toContain('values');
163
+ });
164
+
165
+ it('limita el nombre a 120 y la descripción a 500', async () => {
166
+ expect(
167
+ await errorProperties(CreateCohortRequest, {
168
+ name: 'n'.repeat(121),
169
+ description: 'd'.repeat(501),
170
+ conditions: [GOLD],
171
+ }),
172
+ ).toEqual(['description', 'name']);
173
+ });
174
+ });
175
+
176
+ describe('UpdateCohortRequest', () => {
177
+ it('es parcial: acepta un body vacío', async () => {
178
+ expect(await errorProperties(UpdateCohortRequest, {})).toEqual([]);
179
+ });
180
+
181
+ it('si trae condiciones, aplica las mismas reglas que el alta', async () => {
182
+ expect(await errorProperties(UpdateCohortRequest, { conditions: [] })).toEqual(['conditions']);
183
+ expect(await errorProperties(UpdateCohortRequest, { conditions: [COMPLETED_GTE_3], reason: 'Ajuste' })).toEqual([]);
184
+ });
185
+
186
+ it('limita el motivo a 280', async () => {
187
+ expect(await errorProperties(UpdateCohortRequest, { reason: 'r'.repeat(281) })).toEqual(['reason']);
188
+ });
189
+ });
190
+
191
+ describe('ChangeCohortStatusRequest, TestCohortRequest y ListCohortsQuery', () => {
192
+ it('cambia el estado con un valor del enum', async () => {
193
+ expect(await errorProperties(ChangeCohortStatusRequest, { status: CohortStatusEnum.ARCHIVED })).toEqual([]);
194
+ expect(await errorProperties(ChangeCohortStatusRequest, { status: 'DELETED' })).toEqual(['status']);
195
+ });
196
+
197
+ it('prueba exige cliente y condiciones', async () => {
198
+ expect(await errorProperties(TestCohortRequest, { retailCustomerId: 'RC-1', conditions: [GOLD] })).toEqual([]);
199
+ expect(await errorProperties(TestCohortRequest, {})).toEqual(['conditions', 'retailCustomerId']);
200
+ });
201
+
202
+ it('la lista filtra por estado opcional', async () => {
203
+ expect(await errorProperties(ListCohortsQuery, {})).toEqual([]);
204
+ expect(await errorProperties(ListCohortsQuery, { status: CohortStatusEnum.ACTIVE })).toEqual([]);
205
+ expect(await errorProperties(ListCohortsQuery, { status: 'DRAFT' })).toEqual(['status']);
206
+ });
207
+ });
208
+
209
+ describe('Segmento con cohorts', () => {
210
+ it('el alta acepta cohortIds y descarta los filterIds viejos', async () => {
211
+ const dto = toDto(CreateSegmentRequest, {
212
+ name: 'VIP',
213
+ colorSlot: SegmentColorSlotEnum.SLATE,
214
+ cohortIds: ['COH-LEVEL-GOLD'],
215
+ filterIds: ['levelGold'],
216
+ });
217
+ expect(await validate(dto)).toHaveLength(0);
218
+ expect(dto.cohortIds).toEqual(['COH-LEVEL-GOLD']);
219
+ expect('filterIds' in dto).toBe(false);
220
+ });
221
+
222
+ it('limita los cohorts a 20 y exige textos', async () => {
223
+ const many = Array.from({ length: 21 }, (_, i) => `COH-${i}`);
224
+ expect(await errorProperties(CreateSegmentRequest, { name: 'X', cohortIds: many })).toEqual(['cohortIds']);
225
+ expect(await errorProperties(UpdateSegmentRequest, { cohortIds: many })).toEqual(['cohortIds']);
226
+ expect(await errorProperties(UpdateSegmentRequest, { cohortIds: [1] })).toEqual(['cohortIds']);
227
+ expect(await errorProperties(UpdateSegmentRequest, { cohortIds: [] })).toEqual([]);
228
+ });
229
+
230
+ it('rechaza ids de cohort vacíos', async () => {
231
+ expect(await errorProperties(CreateSegmentRequest, { name: 'X', cohortIds: ['COH-A', ''] })).toEqual(['cohortIds']);
232
+ expect(await errorProperties(UpdateSegmentRequest, { cohortIds: ['COH-A', ''] })).toEqual(['cohortIds']);
233
+ });
234
+
235
+ it('members en lote exige entre 1 y 100 clientes y valida cada uno', async () => {
236
+ expect(await errorProperties(ResolveSegmentMembersRequest, { customers: [{ retailCustomerId: 'RC-1' }] })).toEqual([]);
237
+ expect(await errorProperties(ResolveSegmentMembersRequest, { customers: [] })).toEqual(['customers']);
238
+ expect(
239
+ await errorProperties(ResolveSegmentMembersRequest, {
240
+ customers: Array.from({ length: 101 }, (_, i) => ({ retailCustomerId: `RC-${i}` })),
241
+ }),
242
+ ).toEqual(['customers']);
243
+
244
+ const dto = toDto(ResolveSegmentMembersRequest, { customers: [{ retailCustomerId: 'RC-1', completedCredits: -2 }] });
245
+ expect(dto.customers[0]).toBeInstanceOf(ResolveCustomerSegmentsRequest);
246
+ expect(nestedErrors(await validate(dto))).toContain('completedCredits');
247
+ });
248
+
249
+ it('probar segmentos exige el cliente', async () => {
250
+ expect(await errorProperties(TestCustomerSegmentsRequest, { retailCustomerId: 'RC-1' })).toEqual([]);
251
+ expect(await errorProperties(TestCustomerSegmentsRequest, {})).toEqual(['retailCustomerId']);
252
+ });
253
+ });
254
+
255
+ describe('Responses de cohorts y segmentos', () => {
256
+ it('tipan las formas del contrato', () => {
257
+ const cohort: CohortResponse = {
258
+ cohortId: 'COH-LEVEL-GOLD',
259
+ name: 'Nivel Oro',
260
+ description: null,
261
+ conditions: [{ field: CohortFactKeyEnum.SCI_LEVEL, op: CohortOperatorEnum.EQUALS, value: 'GOLD', values: null }],
262
+ status: CohortStatusEnum.ACTIVE,
263
+ version: 1,
264
+ memberCount: null,
265
+ pctTotal: null,
266
+ countedAt: null,
267
+ countPending: true,
268
+ usedBySegments: [{ segmentId: 'SEG-VIP', name: 'VIP' }],
269
+ createdAt: '2026-10-29T00:00:00.000Z',
270
+ updatedAt: '2026-10-29T00:00:00.000Z',
271
+ createdBy: 'system',
272
+ updatedBy: 'system',
273
+ };
274
+ const segment: SegmentResponse = {
275
+ segmentId: 'SEG-VIP',
276
+ name: 'VIP',
277
+ description: null,
278
+ colorSlot: SegmentColorSlotEnum.SLATE,
279
+ cohortIds: [cohort.cohortId],
280
+ memberIds: [],
281
+ isDefault: false,
282
+ status: SegmentStatusEnum.ACTIVE,
283
+ version: 1,
284
+ clientCount: 10,
285
+ pctTotal: 40,
286
+ countedAt: '2026-10-29T09:00:00.000Z',
287
+ countPending: false,
288
+ createdAt: '2026-10-29T00:00:00.000Z',
289
+ updatedAt: '2026-10-29T00:00:00.000Z',
290
+ createdBy: 'system',
291
+ updatedBy: 'system',
292
+ };
293
+ const test: TestCohortResponse = {
294
+ retailCustomerId: 'RC-1',
295
+ matched: false,
296
+ facts: { completedCredits: 2 },
297
+ conditions: [{ ...COMPLETED_GTE_3, values: null, actual: 2, outcome: CohortConditionOutcomeEnum.NO_MATCH }],
298
+ };
299
+ const trace: TestCustomerSegmentsResponse = {
300
+ retailCustomerId: 'RC-1',
301
+ facts: {},
302
+ segments: [
303
+ { segmentId: 'SEG-VIP', name: 'VIP', matched: true, reason: SegmentMembershipReasonEnum.COHORT, matchedCohortIds: ['COH-LEVEL-GOLD'] },
304
+ ],
305
+ defaultApplies: false,
306
+ evaluatorAvailable: true,
307
+ };
308
+ const members: SegmentMembersResponse = {
309
+ items: [{ retailCustomerId: 'RC-1', segments: [{ segmentId: 'SEG-VIP', name: 'VIP', isDefault: false }] }],
310
+ };
311
+ const runs: SegmentationRecountRunsResponse = {
312
+ runs: [
313
+ {
314
+ runId: 'RUN-1',
315
+ trigger: SegmentationRecountTriggerEnum.NIGHTLY,
316
+ status: SegmentationRecountStatusEnum.RUNNING,
317
+ actor: 'system',
318
+ startedAt: '2026-10-29T09:00:00.000Z',
319
+ finishedAt: null,
320
+ evaluated: null,
321
+ durationMs: null,
322
+ error: null,
323
+ },
324
+ ],
325
+ };
326
+
327
+ expect(segment.cohortIds).toEqual([cohort.cohortId]);
328
+ expect(test.conditions[0].outcome).toBe('NO_MATCH');
329
+ expect(trace.segments[0].reason).toBe('COHORT');
330
+ expect(members.items[0].segments).toHaveLength(1);
331
+ expect(runs.runs[0].status).toBe('RUNNING');
332
+ });
333
+ });
@@ -46,4 +46,6 @@ export declare class OriginateLoanCreditRequest {
46
46
  productCategory?: ProductCategoryEnum;
47
47
  /** Gama del equipo: resuelve promociones acotadas por gama. */
48
48
  productTier?: ProductTierEnum;
49
+ /** Cadena de la tienda que vende; sin ella no se resuelven las promociones acotadas por cadena. */
50
+ retailerId?: string;
49
51
  }
@@ -122,3 +122,10 @@ __decorate([
122
122
  (0, class_validator_1.IsEnum)(ProductTierEnum_1.ProductTierEnum),
123
123
  __metadata("design:type", String)
124
124
  ], OriginateLoanCreditRequest.prototype, "productTier", void 0);
125
+ __decorate([
126
+ (0, class_transformer_1.Expose)(),
127
+ (0, class_validator_1.IsOptional)(),
128
+ (0, class_validator_1.IsString)(),
129
+ (0, class_validator_1.IsNotEmpty)(),
130
+ __metadata("design:type", String)
131
+ ], OriginateLoanCreditRequest.prototype, "retailerId", void 0);
@@ -0,0 +1,4 @@
1
+ /** Body de `POST /private/borrowers/segment-facts`: datos para segmentar de hasta 100 clientes retail. */
2
+ export declare class SegmentFactsBatchRequest {
3
+ retailCustomerIds: string[];
4
+ }
@@ -0,0 +1,27 @@
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.SegmentFactsBatchRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Body de `POST /private/borrowers/segment-facts`: datos para segmentar de hasta 100 clientes retail. */
16
+ class SegmentFactsBatchRequest {
17
+ }
18
+ exports.SegmentFactsBatchRequest = SegmentFactsBatchRequest;
19
+ __decorate([
20
+ (0, class_transformer_1.Expose)(),
21
+ (0, class_validator_1.IsArray)(),
22
+ (0, class_validator_1.ArrayMinSize)(1),
23
+ (0, class_validator_1.ArrayMaxSize)(100),
24
+ (0, class_validator_1.IsString)({ each: true }),
25
+ (0, class_validator_1.IsNotEmpty)({ each: true }),
26
+ __metadata("design:type", Array)
27
+ ], SegmentFactsBatchRequest.prototype, "retailCustomerIds", void 0);
@@ -0,0 +1,7 @@
1
+ /** Body de `POST /private/borrowers/segment-facts/page`: una página de la cartera de acreditados. */
2
+ export declare class SegmentFactsPageRequest {
3
+ /** Cursor opaco de la página anterior. Ausente en la primera página. */
4
+ cursor?: string;
5
+ /** Acreditados por página. Ausente = 100. */
6
+ limit?: number;
7
+ }
@@ -0,0 +1,32 @@
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.SegmentFactsPageRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ /** Body de `POST /private/borrowers/segment-facts/page`: una página de la cartera de acreditados. */
16
+ class SegmentFactsPageRequest {
17
+ }
18
+ exports.SegmentFactsPageRequest = SegmentFactsPageRequest;
19
+ __decorate([
20
+ (0, class_transformer_1.Expose)(),
21
+ (0, class_validator_1.IsOptional)(),
22
+ (0, class_validator_1.IsString)(),
23
+ __metadata("design:type", String)
24
+ ], SegmentFactsPageRequest.prototype, "cursor", void 0);
25
+ __decorate([
26
+ (0, class_transformer_1.Expose)(),
27
+ (0, class_validator_1.IsOptional)(),
28
+ (0, class_validator_1.IsInt)(),
29
+ (0, class_validator_1.Min)(1),
30
+ (0, class_validator_1.Max)(100),
31
+ __metadata("design:type", Number)
32
+ ], SegmentFactsPageRequest.prototype, "limit", void 0);
@@ -0,0 +1,22 @@
1
+ import { CreditPlanLevelEnum } from '../../../loanOfferings/enums/CreditPlanLevelEnum';
2
+ /**
3
+ * Datos del acreditado para evaluar cohorts, ya normalizados: sin acreditado, conteos y días en
4
+ * `0`; `sciLevel` ausente si no hay evaluación o el nivel es `ALL` / `NOT_ELIGIBLE`.
5
+ */
6
+ export interface BorrowerSegmentFacts {
7
+ sciLevel?: CreditPlanLevelEnum;
8
+ totalCredits: number;
9
+ activeCredits: number;
10
+ completedCredits: number;
11
+ lifetimeWriteOffs: number;
12
+ currentArrearsDays: number;
13
+ /** Sin cuotas en la ventana de 180 días cuenta como `0`. */
14
+ delinquencyDaysLast6M: number;
15
+ lifetimeRestructures: number;
16
+ }
17
+ /** Un cliente retail y sus datos para segmentar. `borrowerId` en `null` = no tiene acreditado. */
18
+ export interface BorrowerSegmentFactsItem {
19
+ retailCustomerId: string;
20
+ borrowerId: string | null;
21
+ facts: BorrowerSegmentFacts;
22
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,5 @@
1
+ import { BorrowerSegmentFactsItem } from './BorrowerSegmentFacts';
2
+ /** Respuesta de `POST /private/borrowers/segment-facts`: un ítem por id pedido, en el mismo orden. */
3
+ export interface SegmentFactsBatchResponse {
4
+ items: BorrowerSegmentFactsItem[];
5
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });