@fiado/type-kit 3.564.0 → 3.566.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 (134) hide show
  1. package/_test_/unit/retailCustomer/customerReferralContract.test.ts +228 -0
  2. package/_test_/unit/retailPromotions/constants.test.ts +1 -1
  3. package/_test_/unit/retailPromotions/referralPromotion.test.ts +177 -0
  4. package/_test_/unit/retailPromotions/referralRewardsByPhones.test.ts +73 -0
  5. package/_test_/unit/retailPromotions/referralWizardContracts.test.ts +119 -0
  6. package/_test_/unit/retailPromotions/salePromotionPaymentCheck.test.ts +29 -0
  7. package/_test_/unit/retailWizard/backofficeCustomerReferrals.test.ts +78 -0
  8. package/_test_/unit/retailWizard/promotionInboundMessages.test.ts +151 -0
  9. package/_test_/unit/retailWizard/referralsCapture.test.ts +66 -0
  10. package/_test_/unit/retailWizard/ventaSagaMessages.test.ts +7 -2
  11. package/bin/retailCustomer/dtos/CustomerReferral.d.ts +25 -0
  12. package/bin/retailCustomer/dtos/CustomerReferral.js +99 -0
  13. package/bin/retailCustomer/dtos/requests/CreateCustomerReferralsRequest.d.ts +19 -0
  14. package/bin/retailCustomer/dtos/requests/CreateCustomerReferralsRequest.js +101 -0
  15. package/bin/retailCustomer/dtos/requests/GetCustomersByPhonesRequest.d.ts +4 -0
  16. package/bin/retailCustomer/dtos/requests/GetCustomersByPhonesRequest.js +28 -0
  17. package/bin/retailCustomer/dtos/requests/GetReferralsByPhoneQuery.d.ts +6 -0
  18. package/bin/retailCustomer/dtos/requests/GetReferralsByPhoneQuery.js +39 -0
  19. package/bin/retailCustomer/dtos/requests/UpdateCustomerReferralRequest.d.ts +8 -0
  20. package/bin/retailCustomer/dtos/requests/UpdateCustomerReferralRequest.js +53 -0
  21. package/bin/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.d.ts +4 -0
  22. package/bin/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.js +15 -0
  23. package/bin/retailCustomer/dtos/responses/CustomerReferralResponses.d.ts +39 -0
  24. package/bin/retailCustomer/dtos/responses/CustomerReferralResponses.js +2 -0
  25. package/bin/retailCustomer/enums/ReferralRoleEnum.d.ts +5 -0
  26. package/bin/retailCustomer/enums/ReferralRoleEnum.js +9 -0
  27. package/bin/retailCustomer/enums/ReferralSourceEnum.d.ts +6 -0
  28. package/bin/retailCustomer/enums/ReferralSourceEnum.js +10 -0
  29. package/bin/retailCustomer/enums/ReferralStatusEnum.d.ts +7 -0
  30. package/bin/retailCustomer/enums/ReferralStatusEnum.js +11 -0
  31. package/bin/retailCustomer/index.d.ts +9 -0
  32. package/bin/retailCustomer/index.js +9 -0
  33. package/bin/retailPromotions/constants/retailPromotionsErrorHttpStatus.js +2 -0
  34. package/bin/retailPromotions/dtos/ReferralPromotion.d.ts +33 -0
  35. package/bin/retailPromotions/dtos/ReferralPromotion.js +2 -0
  36. package/bin/retailPromotions/dtos/SalePromotion.d.ts +3 -0
  37. package/bin/retailPromotions/dtos/requests/CheckSalePromotionPaymentRequest.d.ts +6 -0
  38. package/bin/retailPromotions/dtos/requests/CheckSalePromotionPaymentRequest.js +25 -0
  39. package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.d.ts +3 -0
  40. package/bin/retailPromotions/dtos/requests/CreateSalePromotionRequest.js +8 -0
  41. package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.d.ts +2 -0
  42. package/bin/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.js +7 -0
  43. package/bin/retailPromotions/dtos/requests/GetReferralRewardsByPhonesRequest.d.ts +5 -0
  44. package/bin/retailPromotions/dtos/requests/GetReferralRewardsByPhonesRequest.js +34 -0
  45. package/bin/retailPromotions/dtos/requests/ListLiveReferralPromotionsQuery.d.ts +10 -0
  46. package/bin/retailPromotions/dtos/requests/ListLiveReferralPromotionsQuery.js +38 -0
  47. package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.d.ts +6 -1
  48. package/bin/retailPromotions/dtos/requests/ListSalePromotionsQuery.js +15 -1
  49. package/bin/retailPromotions/dtos/requests/ReferralPromotionInput.d.ts +24 -0
  50. package/bin/retailPromotions/dtos/requests/ReferralPromotionInput.js +119 -0
  51. package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.d.ts +3 -0
  52. package/bin/retailPromotions/dtos/requests/UpdateSalePromotionRequest.js +8 -0
  53. package/bin/retailPromotions/dtos/responses/LiveReferralPromotionsResponse.d.ts +24 -0
  54. package/bin/retailPromotions/dtos/responses/LiveReferralPromotionsResponse.js +2 -0
  55. package/bin/retailPromotions/dtos/responses/ReferralRewardsByPhonesResponses.d.ts +25 -0
  56. package/bin/retailPromotions/dtos/responses/ReferralRewardsByPhonesResponses.js +2 -0
  57. package/bin/retailPromotions/dtos/responses/SalePromotionPaymentCheckResponses.d.ts +16 -0
  58. package/bin/retailPromotions/dtos/responses/SalePromotionPaymentCheckResponses.js +2 -0
  59. package/bin/retailPromotions/dtos/responses/WizardSalePromotionResponses.d.ts +13 -0
  60. package/bin/retailPromotions/enums/ReferralPromotionChannelEnum.d.ts +5 -0
  61. package/bin/retailPromotions/enums/ReferralPromotionChannelEnum.js +9 -0
  62. package/bin/retailPromotions/enums/ReferralRewardStatusEnum.d.ts +9 -0
  63. package/bin/retailPromotions/enums/ReferralRewardStatusEnum.js +13 -0
  64. package/bin/retailPromotions/enums/ReferralRewardTypeEnum.d.ts +10 -0
  65. package/bin/retailPromotions/enums/ReferralRewardTypeEnum.js +14 -0
  66. package/bin/retailPromotions/enums/ReferralTriggerEnum.d.ts +13 -0
  67. package/bin/retailPromotions/enums/ReferralTriggerEnum.js +17 -0
  68. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.d.ts +5 -1
  69. package/bin/retailPromotions/enums/RetailPromotionsErrorCodeEnum.js +4 -0
  70. package/bin/retailPromotions/enums/SalePromotionKindEnum.d.ts +3 -2
  71. package/bin/retailPromotions/enums/SalePromotionKindEnum.js +2 -1
  72. package/bin/retailPromotions/index.d.ts +12 -0
  73. package/bin/retailPromotions/index.js +12 -0
  74. package/bin/retailWizard/dtos/responses/BackofficeCustomerReferralsResponse.d.ts +58 -0
  75. package/bin/retailWizard/dtos/responses/BackofficeCustomerReferralsResponse.js +2 -0
  76. package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.d.ts +4 -2
  77. package/bin/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.js +3 -1
  78. package/bin/retailWizard/enums/WizardStepEnum.d.ts +3 -1
  79. package/bin/retailWizard/enums/WizardStepEnum.js +2 -0
  80. package/bin/retailWizard/events/RetailCommissionInboundMessage.d.ts +7 -0
  81. package/bin/retailWizard/events/RetailCommissionInboundMessage.js +22 -1
  82. package/bin/retailWizard/events/RetailPromotionAccountOpenedMessage.d.ts +15 -0
  83. package/bin/retailWizard/events/RetailPromotionAccountOpenedMessage.js +56 -0
  84. package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +15 -0
  85. package/bin/retailWizard/events/RetailPromotionInboundMessage.js +41 -1
  86. package/bin/retailWizard/events/RetailPromotionInstallmentPaidMessage.d.ts +20 -0
  87. package/bin/retailWizard/events/RetailPromotionInstallmentPaidMessage.js +75 -0
  88. package/bin/retailWizard/events/RetailPromotionQueueMessage.d.ts +5 -0
  89. package/bin/retailWizard/events/RetailPromotionQueueMessage.js +2 -0
  90. package/bin/retailWizard/index.d.ts +4 -0
  91. package/bin/retailWizard/index.js +4 -0
  92. package/package.json +1 -1
  93. package/src/retailCustomer/dtos/CustomerReferral.ts +77 -0
  94. package/src/retailCustomer/dtos/requests/CreateCustomerReferralsRequest.ts +91 -0
  95. package/src/retailCustomer/dtos/requests/GetCustomersByPhonesRequest.ts +15 -0
  96. package/src/retailCustomer/dtos/requests/GetReferralsByPhoneQuery.ts +23 -0
  97. package/src/retailCustomer/dtos/requests/UpdateCustomerReferralRequest.ts +38 -0
  98. package/src/retailCustomer/dtos/requests/UpsertPromotionBonusRequest.ts +15 -1
  99. package/src/retailCustomer/dtos/responses/CustomerReferralResponses.ts +46 -0
  100. package/src/retailCustomer/enums/ReferralRoleEnum.ts +5 -0
  101. package/src/retailCustomer/enums/ReferralSourceEnum.ts +6 -0
  102. package/src/retailCustomer/enums/ReferralStatusEnum.ts +7 -0
  103. package/src/retailCustomer/index.ts +9 -0
  104. package/src/retailPromotions/constants/retailPromotionsErrorHttpStatus.ts +2 -0
  105. package/src/retailPromotions/dtos/ReferralPromotion.ts +35 -0
  106. package/src/retailPromotions/dtos/SalePromotion.ts +3 -0
  107. package/src/retailPromotions/dtos/requests/CheckSalePromotionPaymentRequest.ts +12 -0
  108. package/src/retailPromotions/dtos/requests/CreateSalePromotionRequest.ts +8 -0
  109. package/src/retailPromotions/dtos/requests/EvaluateSalePromotionsRequest.ts +7 -0
  110. package/src/retailPromotions/dtos/requests/GetReferralRewardsByPhonesRequest.ts +20 -0
  111. package/src/retailPromotions/dtos/requests/ListLiveReferralPromotionsQuery.ts +24 -0
  112. package/src/retailPromotions/dtos/requests/ListSalePromotionsQuery.ts +14 -1
  113. package/src/retailPromotions/dtos/requests/ReferralPromotionInput.ts +93 -0
  114. package/src/retailPromotions/dtos/requests/UpdateSalePromotionRequest.ts +8 -0
  115. package/src/retailPromotions/dtos/responses/LiveReferralPromotionsResponse.ts +26 -0
  116. package/src/retailPromotions/dtos/responses/ReferralRewardsByPhonesResponses.ts +28 -0
  117. package/src/retailPromotions/dtos/responses/SalePromotionPaymentCheckResponses.ts +17 -0
  118. package/src/retailPromotions/dtos/responses/WizardSalePromotionResponses.ts +15 -0
  119. package/src/retailPromotions/enums/ReferralPromotionChannelEnum.ts +5 -0
  120. package/src/retailPromotions/enums/ReferralRewardStatusEnum.ts +9 -0
  121. package/src/retailPromotions/enums/ReferralRewardTypeEnum.ts +10 -0
  122. package/src/retailPromotions/enums/ReferralTriggerEnum.ts +13 -0
  123. package/src/retailPromotions/enums/RetailPromotionsErrorCodeEnum.ts +4 -0
  124. package/src/retailPromotions/enums/SalePromotionKindEnum.ts +2 -1
  125. package/src/retailPromotions/index.ts +12 -0
  126. package/src/retailWizard/dtos/responses/BackofficeCustomerReferralsResponse.ts +63 -0
  127. package/src/retailWizard/enums/RetailPromotionInboundMessageTypeEnum.ts +3 -1
  128. package/src/retailWizard/enums/WizardStepEnum.ts +2 -0
  129. package/src/retailWizard/events/RetailCommissionInboundMessage.ts +18 -0
  130. package/src/retailWizard/events/RetailPromotionAccountOpenedMessage.ts +38 -0
  131. package/src/retailWizard/events/RetailPromotionInboundMessage.ts +43 -1
  132. package/src/retailWizard/events/RetailPromotionInstallmentPaidMessage.ts +53 -0
  133. package/src/retailWizard/events/RetailPromotionQueueMessage.ts +9 -0
  134. package/src/retailWizard/index.ts +4 -0
@@ -0,0 +1,78 @@
1
+ import 'reflect-metadata';
2
+ import { RetailWizard } from '../../../src/index';
3
+ import type {
4
+ BackofficeCustomerReferral,
5
+ BackofficeCustomerReferralsResponse,
6
+ BackofficeReferralReward,
7
+ } from '../../../src/retailWizard/index';
8
+ import { ReferralRoleEnum, ReferralSourceEnum, ReferralStatusEnum } from '../../../src/retailCustomer/index';
9
+ import { ReferralRewardStatusEnum, ReferralRewardTypeEnum } from '../../../src/retailPromotions/index';
10
+
11
+ const reward: BackofficeReferralReward = {
12
+ promotionId: 'promo_1',
13
+ promotionName: 'Abre tu cuenta · ganan los dos',
14
+ role: ReferralRoleEnum.REFERRED,
15
+ rewardType: ReferralRewardTypeEnum.FIXED_BONUS,
16
+ amountCents: 10000,
17
+ status: ReferralRewardStatusEnum.PAID,
18
+ statusAt: '2025-03-14T10:00:00.000Z',
19
+ };
20
+
21
+ const referral: BackofficeCustomerReferral = {
22
+ referralId: 'ref_1',
23
+ name: 'Ana Sofía Martínez Ruiz',
24
+ phone: '5587654321',
25
+ email: null,
26
+ status: ReferralStatusEnum.SUREKEEP_CREDIT,
27
+ capture: {
28
+ source: ReferralSourceEnum.SALE,
29
+ storeId: 'store_1',
30
+ storeName: 'Tienda Coyoacán Centro',
31
+ saleId: 'sale_1',
32
+ saleFolio: 'F-10423',
33
+ accountOpeningSessionId: null,
34
+ capturedBy: 'user_1',
35
+ capturedAt: '2026-10-03T10:00:00.000Z',
36
+ },
37
+ updatedBy: 'user_1',
38
+ updatedAt: '2026-10-03T10:00:00.000Z',
39
+ };
40
+
41
+ describe('BackofficeCustomerReferralsResponse', () => {
42
+ it('lista los referidos con estado y captura, sin premios en la tanda A', () => {
43
+ const response: BackofficeCustomerReferralsResponse = { referrals: [referral], referredBy: null };
44
+ expect(response.referrals[0].rewards).toBeUndefined();
45
+ expect(response.referrals[0].capture.source).toBe('SALE');
46
+ });
47
+
48
+ it('suma premios por referido y el bloque «llegó recomendado por» en la tanda B', () => {
49
+ const response: BackofficeCustomerReferralsResponse = {
50
+ referrals: [{ ...referral, rewards: [{ ...reward, role: ReferralRoleEnum.REFERRER }] }],
51
+ referredBy: {
52
+ referralId: 'ref_9',
53
+ referrerCustomerId: 'rc_9',
54
+ referrerName: 'Mariana López Ortega',
55
+ referredAt: '2025-03-02T10:00:00.000Z',
56
+ rewards: [reward],
57
+ },
58
+ };
59
+ expect(response.referredBy?.rewards?.[0].promotionName).toBe('Abre tu cuenta · ganan los dos');
60
+ });
61
+
62
+ it('el bloque «llegó recomendado por» no exige promo', () => {
63
+ const response: BackofficeCustomerReferralsResponse = {
64
+ referrals: [],
65
+ referredBy: {
66
+ referralId: 'ref_9',
67
+ referrerCustomerId: 'rc_9',
68
+ referrerName: 'Mariana López Ortega',
69
+ referredAt: '2025-03-02T10:00:00.000Z',
70
+ },
71
+ };
72
+ expect(response.referredBy?.rewards).toBeUndefined();
73
+ });
74
+
75
+ it('el index raíz sigue exponiendo el módulo', () => {
76
+ expect(RetailWizard.WizardStepEnum).toBeDefined();
77
+ });
78
+ });
@@ -0,0 +1,151 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validateSync } from 'class-validator';
4
+ import { RetailWizard } from '../../../src/index';
5
+ import {
6
+ RetailPromotionAccountOpenedMessage,
7
+ RetailPromotionInboundMessage,
8
+ RetailPromotionInboundMessageTypeEnum,
9
+ RetailPromotionInstallmentPaidMessage,
10
+ } from '../../../src/retailWizard/index';
11
+ import type { RetailPromotionQueueMessage } from '../../../src/retailWizard/index';
12
+
13
+ const errorsOf = <T extends object>(cls: new () => T, payload: Record<string, unknown>): string[] =>
14
+ validateSync(plainToInstance(cls, payload)).map((e) => e.property);
15
+
16
+ describe('RetailPromotionInboundMessageTypeEnum', () => {
17
+ it('suma la apertura y la cuota pagada sin mover los de venta', () => {
18
+ expect(Object.values(RetailPromotionInboundMessageTypeEnum)).toEqual([
19
+ 'SALE_CONFIRMED',
20
+ 'SALE_CANCELLED',
21
+ 'ACCOUNT_OPENED',
22
+ 'CREDIT_INSTALLMENT_PAID',
23
+ ]);
24
+ });
25
+ });
26
+
27
+ const SALE = {
28
+ type: 'SALE_CONFIRMED',
29
+ tenantId: 't-1',
30
+ retailerId: 'r-1',
31
+ storeId: 'st-1',
32
+ saleId: 'sale-1',
33
+ sessionId: 'ws-1',
34
+ customerId: 'c-1',
35
+ soldAt: '2026-10-21T10:00:00Z',
36
+ occurredAt: '2026-10-21T10:00:00Z',
37
+ lines: [{ lineId: 'l-1', sku: 'SKU-1', qty: 1, originalPriceCents: 100_00, identifiers: [] }],
38
+ };
39
+
40
+ describe('RetailPromotionInboundMessage (venta)', () => {
41
+ it('sigue aceptando el mensaje viejo sin los campos nuevos', () => {
42
+ expect(errorsOf(RetailPromotionInboundMessage, SALE)).toEqual([]);
43
+ });
44
+
45
+ it('acepta forma de pago, medio, financiera, monto, número de compra y teléfono', () => {
46
+ const extended = {
47
+ ...SALE,
48
+ saleType: 'EXTERNAL_REFERRAL',
49
+ paymentMethod: 'CARD',
50
+ externalProvider: 'PAYJOY',
51
+ amountCents: 8_000_00,
52
+ purchaseNumber: 2,
53
+ phone: '5512345678',
54
+ };
55
+ expect(errorsOf(RetailPromotionInboundMessage, extended)).toEqual([]);
56
+ });
57
+
58
+ it('rechaza valores nuevos fuera de rango', () => {
59
+ expect(errorsOf(RetailPromotionInboundMessage, { ...SALE, saleType: 'LAYAWAY' })).toEqual(['saleType']);
60
+ expect(errorsOf(RetailPromotionInboundMessage, { ...SALE, purchaseNumber: 0 })).toEqual(['purchaseNumber']);
61
+ expect(errorsOf(RetailPromotionInboundMessage, { ...SALE, amountCents: -1 })).toEqual(['amountCents']);
62
+ expect(errorsOf(RetailPromotionInboundMessage, { ...SALE, phone: '' })).toEqual(['phone']);
63
+ });
64
+
65
+ it('solo valida tipos de venta', () => {
66
+ expect(errorsOf(RetailPromotionInboundMessage, { ...SALE, type: 'ACCOUNT_OPENED' })).toEqual(['type']);
67
+ });
68
+ });
69
+
70
+ const OPENED = {
71
+ type: 'ACCOUNT_OPENED',
72
+ tenantId: 't-1',
73
+ retailerId: 'r-1',
74
+ storeId: 'st-1',
75
+ customerId: 'c-1',
76
+ phone: '5512345678',
77
+ sessionId: 'ws-1',
78
+ openedAt: '2026-10-21T10:00:00Z',
79
+ };
80
+
81
+ describe('RetailPromotionAccountOpenedMessage', () => {
82
+ it('acepta la apertura', () => {
83
+ expect(errorsOf(RetailPromotionAccountOpenedMessage, OPENED)).toEqual([]);
84
+ });
85
+
86
+ it('no hereda de la clase de venta', () => {
87
+ expect(new RetailPromotionAccountOpenedMessage()).not.toBeInstanceOf(RetailPromotionInboundMessage);
88
+ });
89
+
90
+ it('rechaza otro tipo y campos faltantes', () => {
91
+ expect(errorsOf(RetailPromotionAccountOpenedMessage, { ...OPENED, type: 'SALE_CONFIRMED' })).toEqual(['type']);
92
+ const { phone: _phone, ...noPhone } = OPENED;
93
+ expect(errorsOf(RetailPromotionAccountOpenedMessage, { ...noPhone, openedAt: 'hoy' })).toEqual([
94
+ 'phone',
95
+ 'openedAt',
96
+ ]);
97
+ });
98
+ });
99
+
100
+ const PAID = {
101
+ type: 'CREDIT_INSTALLMENT_PAID',
102
+ tenantId: 't-1',
103
+ creditId: 'cr-1',
104
+ installmentNumber: 1,
105
+ paidCents: 500_00,
106
+ paidAt: '2026-10-21T10:00:00Z',
107
+ installmentStatus: 'PAID',
108
+ paymentId: 'pay-1',
109
+ wizardSessionId: 'ws-1',
110
+ storeId: 'st-1',
111
+ onTime: true,
112
+ onTimeStreak: 1,
113
+ };
114
+
115
+ describe('RetailPromotionInstallmentPaidMessage', () => {
116
+ it('acepta la cuota pagada con onTime y racha', () => {
117
+ expect(errorsOf(RetailPromotionInstallmentPaidMessage, PAID)).toEqual([]);
118
+ });
119
+
120
+ it('no hereda de la clase de venta', () => {
121
+ expect(new RetailPromotionInstallmentPaidMessage()).not.toBeInstanceOf(RetailPromotionInboundMessage);
122
+ });
123
+
124
+ it('rechaza racha negativa, cuota 0, onTime no booleano y otro tipo', () => {
125
+ const bad = { ...PAID, type: 'ACCOUNT_OPENED', installmentNumber: 0, onTime: 'sí', onTimeStreak: -1 };
126
+ expect(errorsOf(RetailPromotionInstallmentPaidMessage, bad)).toEqual([
127
+ 'type',
128
+ 'installmentNumber',
129
+ 'onTime',
130
+ 'onTimeStreak',
131
+ ]);
132
+ });
133
+
134
+ it('rechaza un estado de cuota fuera del catálogo', () => {
135
+ expect(errorsOf(RetailPromotionInstallmentPaidMessage, { ...PAID, installmentStatus: 'X' })).toEqual([
136
+ 'installmentStatus',
137
+ ]);
138
+ });
139
+ });
140
+
141
+ describe('RetailPromotionQueueMessage', () => {
142
+ it('une los tres mensajes de la cola y sale por el index raíz', () => {
143
+ const messages: RetailPromotionQueueMessage[] = [
144
+ plainToInstance(RetailPromotionInboundMessage, SALE),
145
+ plainToInstance(RetailPromotionAccountOpenedMessage, OPENED),
146
+ plainToInstance(RetailPromotionInstallmentPaidMessage, PAID),
147
+ ];
148
+ expect(messages.map((m) => m.type)).toEqual(['SALE_CONFIRMED', 'ACCOUNT_OPENED', 'CREDIT_INSTALLMENT_PAID']);
149
+ expect(new RetailWizard.RetailPromotionAccountOpenedMessage()).toBeInstanceOf(RetailPromotionAccountOpenedMessage);
150
+ });
151
+ });
@@ -0,0 +1,66 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { ValidationError, validateSync } from 'class-validator';
4
+ import { RetailCommissionInboundMessage, WizardStepEnum } from '../../../src/retailWizard/index';
5
+
6
+ function pathsOf(errors: ValidationError[], prefix = ''): string[] {
7
+ return errors.flatMap((e) => {
8
+ const path = `${prefix}${e.property}`;
9
+ return e.children && e.children.length > 0 ? pathsOf(e.children, `${path}.`) : [path];
10
+ });
11
+ }
12
+
13
+ const errorsOf = (payload: Record<string, unknown>): string[] =>
14
+ pathsOf(validateSync(plainToInstance(RetailCommissionInboundMessage, payload)));
15
+
16
+ describe('WizardStepEnum', () => {
17
+ it('suma el paso Referidos como S11 sin mover los existentes', () => {
18
+ expect(Object.values(WizardStepEnum)).toEqual([
19
+ 'S01', 'S02', 'S03', 'S04', 'S05', 'S06', 'S07', 'S08', 'S09', 'S10', 'S11',
20
+ ]);
21
+ });
22
+ });
23
+
24
+ const SALE_COMPLETED = {
25
+ type: 'SALE_COMPLETED',
26
+ tenantId: 't-1',
27
+ retailerId: 'r-1',
28
+ storeId: 'st-1',
29
+ saleId: 'sale-1',
30
+ sessionId: 'ws-1',
31
+ sellerRetailUserId: 'seller-1',
32
+ saleType: 'CASH',
33
+ externalProvider: null,
34
+ creditId: null,
35
+ soldAt: '2026-10-21T10:00:00Z',
36
+ occurredAt: '2026-10-21T10:00:00Z',
37
+ lines: [
38
+ {
39
+ lineId: 'l-1',
40
+ sku: 'SKU-1',
41
+ qty: 1,
42
+ netCents: 100_00,
43
+ itemType: 'PRODUCT',
44
+ category: null,
45
+ tier: null,
46
+ brand: null,
47
+ paymentType: 'CASH',
48
+ },
49
+ ],
50
+ };
51
+
52
+ describe('RetailCommissionInboundMessage.referrals', () => {
53
+ it('sigue aceptando mensajes sin referidos', () => {
54
+ expect(errorsOf(SALE_COMPLETED)).toEqual([]);
55
+ });
56
+
57
+ it('acepta los referidos con quién los capturó', () => {
58
+ const referrals = [{ referralId: 'ref-1', capturedBy: 'seller-1' }];
59
+ expect(errorsOf({ ...SALE_COMPLETED, referrals })).toEqual([]);
60
+ });
61
+
62
+ it('rechaza un referido sin id o sin capturista', () => {
63
+ const referrals = [{ referralId: '', capturedBy: 'seller-1' }, { referralId: 'ref-2' }];
64
+ expect(errorsOf({ ...SALE_COMPLETED, referrals })).toEqual(['referrals.0.referralId', 'referrals.1.capturedBy']);
65
+ });
66
+ });
@@ -25,8 +25,13 @@ function errorsOf<T extends object>(cls: ClassConstructor<T>, payload: object):
25
25
  }
26
26
 
27
27
  describe('RetailPromotionInboundMessageTypeEnum', () => {
28
- it('distingue confirmación y cancelación', () => {
29
- expect(Object.values(RetailPromotionInboundMessageTypeEnum)).toEqual(['SALE_CONFIRMED', 'SALE_CANCELLED']);
28
+ it('distingue confirmación, cancelación, apertura y cuota pagada', () => {
29
+ expect(Object.values(RetailPromotionInboundMessageTypeEnum)).toEqual([
30
+ 'SALE_CONFIRMED',
31
+ 'SALE_CANCELLED',
32
+ 'ACCOUNT_OPENED',
33
+ 'CREDIT_INSTALLMENT_PAID',
34
+ ]);
30
35
  });
31
36
  });
32
37
 
@@ -0,0 +1,25 @@
1
+ import { ReferralSourceEnum } from '../enums/ReferralSourceEnum';
2
+ /** Persona que un cliente recomienda (owner: retail-customer-business). Teléfono normalizado a 10 dígitos. */
3
+ export declare class CustomerReferral {
4
+ referralId: string;
5
+ /** Cliente que recomienda. */
6
+ retailCustomerId: string;
7
+ /** Cadena del cliente que recomienda. */
8
+ retailerId: string;
9
+ /** Tienda de captura; `null` si se agregó en el cajón. */
10
+ storeId: string | null;
11
+ name: string;
12
+ phone: string;
13
+ email: string | null;
14
+ source: ReferralSourceEnum;
15
+ /** Sesión del wizard donde se capturó (venta). */
16
+ wizardSessionId: string | null;
17
+ /** Solo en `SALE`. */
18
+ saleId: string | null;
19
+ /** Solo en `OPENING`. */
20
+ accountOpeningSessionId: string | null;
21
+ createdBy: string;
22
+ updatedBy: string;
23
+ createdAt: string;
24
+ updatedAt: string;
25
+ }
@@ -0,0 +1,99 @@
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.CustomerReferral = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ const ReferralSourceEnum_1 = require("../enums/ReferralSourceEnum");
16
+ /** Persona que un cliente recomienda (owner: retail-customer-business). Teléfono normalizado a 10 dígitos. */
17
+ class CustomerReferral {
18
+ }
19
+ exports.CustomerReferral = CustomerReferral;
20
+ __decorate([
21
+ (0, class_transformer_1.Expose)(),
22
+ (0, class_validator_1.IsString)(),
23
+ __metadata("design:type", String)
24
+ ], CustomerReferral.prototype, "referralId", void 0);
25
+ __decorate([
26
+ (0, class_transformer_1.Expose)(),
27
+ (0, class_validator_1.IsString)(),
28
+ __metadata("design:type", String)
29
+ ], CustomerReferral.prototype, "retailCustomerId", void 0);
30
+ __decorate([
31
+ (0, class_transformer_1.Expose)(),
32
+ (0, class_validator_1.IsString)(),
33
+ __metadata("design:type", String)
34
+ ], CustomerReferral.prototype, "retailerId", void 0);
35
+ __decorate([
36
+ (0, class_transformer_1.Expose)(),
37
+ (0, class_validator_1.IsOptional)(),
38
+ (0, class_validator_1.IsString)(),
39
+ __metadata("design:type", String)
40
+ ], CustomerReferral.prototype, "storeId", void 0);
41
+ __decorate([
42
+ (0, class_transformer_1.Expose)(),
43
+ (0, class_validator_1.IsString)(),
44
+ __metadata("design:type", String)
45
+ ], CustomerReferral.prototype, "name", void 0);
46
+ __decorate([
47
+ (0, class_transformer_1.Expose)(),
48
+ (0, class_validator_1.IsString)(),
49
+ __metadata("design:type", String)
50
+ ], CustomerReferral.prototype, "phone", void 0);
51
+ __decorate([
52
+ (0, class_transformer_1.Expose)(),
53
+ (0, class_validator_1.IsOptional)(),
54
+ (0, class_validator_1.IsString)(),
55
+ __metadata("design:type", String)
56
+ ], CustomerReferral.prototype, "email", void 0);
57
+ __decorate([
58
+ (0, class_transformer_1.Expose)(),
59
+ (0, class_validator_1.IsEnum)(ReferralSourceEnum_1.ReferralSourceEnum),
60
+ __metadata("design:type", String)
61
+ ], CustomerReferral.prototype, "source", void 0);
62
+ __decorate([
63
+ (0, class_transformer_1.Expose)(),
64
+ (0, class_validator_1.IsOptional)(),
65
+ (0, class_validator_1.IsString)(),
66
+ __metadata("design:type", String)
67
+ ], CustomerReferral.prototype, "wizardSessionId", void 0);
68
+ __decorate([
69
+ (0, class_transformer_1.Expose)(),
70
+ (0, class_validator_1.IsOptional)(),
71
+ (0, class_validator_1.IsString)(),
72
+ __metadata("design:type", String)
73
+ ], CustomerReferral.prototype, "saleId", void 0);
74
+ __decorate([
75
+ (0, class_transformer_1.Expose)(),
76
+ (0, class_validator_1.IsOptional)(),
77
+ (0, class_validator_1.IsString)(),
78
+ __metadata("design:type", String)
79
+ ], CustomerReferral.prototype, "accountOpeningSessionId", void 0);
80
+ __decorate([
81
+ (0, class_transformer_1.Expose)(),
82
+ (0, class_validator_1.IsString)(),
83
+ __metadata("design:type", String)
84
+ ], CustomerReferral.prototype, "createdBy", void 0);
85
+ __decorate([
86
+ (0, class_transformer_1.Expose)(),
87
+ (0, class_validator_1.IsString)(),
88
+ __metadata("design:type", String)
89
+ ], CustomerReferral.prototype, "updatedBy", void 0);
90
+ __decorate([
91
+ (0, class_transformer_1.Expose)(),
92
+ (0, class_validator_1.IsISO8601)(),
93
+ __metadata("design:type", String)
94
+ ], CustomerReferral.prototype, "createdAt", void 0);
95
+ __decorate([
96
+ (0, class_transformer_1.Expose)(),
97
+ (0, class_validator_1.IsISO8601)(),
98
+ __metadata("design:type", String)
99
+ ], CustomerReferral.prototype, "updatedAt", void 0);
@@ -0,0 +1,19 @@
1
+ import { ReferralSourceEnum } from '../../enums/ReferralSourceEnum';
2
+ /** Un renglón del lote. El teléfono llega como se tecleó; el lambda lo normaliza y valida. */
3
+ export declare class CustomerReferralInput {
4
+ name: string;
5
+ phone: string;
6
+ email?: string;
7
+ }
8
+ /** Body de `privateCreateCustomerReferrals`: lote de referidos de un cliente. */
9
+ export declare class CreateCustomerReferralsRequest {
10
+ source: ReferralSourceEnum;
11
+ /** Tienda de captura; obligatoria en venta y apertura. */
12
+ storeId?: string;
13
+ wizardSessionId?: string;
14
+ saleId?: string;
15
+ accountOpeningSessionId?: string;
16
+ /** Usuario que capturó (vendedor o admin); la llamada M2M no lo trae en el token. */
17
+ capturedBy: string;
18
+ referrals: CustomerReferralInput[];
19
+ }
@@ -0,0 +1,101 @@
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.CreateCustomerReferralsRequest = exports.CustomerReferralInput = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ const Trim_1 = require("../../../common/decorators/Trim");
16
+ const InputPatterns_1 = require("../../../common/constants/InputPatterns");
17
+ const ReferralSourceEnum_1 = require("../../enums/ReferralSourceEnum");
18
+ const REFERRAL_NAME_MAX_LENGTH = 120;
19
+ /** Un renglón del lote. El teléfono llega como se tecleó; el lambda lo normaliza y valida. */
20
+ class CustomerReferralInput {
21
+ }
22
+ exports.CustomerReferralInput = CustomerReferralInput;
23
+ __decorate([
24
+ (0, class_transformer_1.Expose)(),
25
+ (0, Trim_1.Trim)(),
26
+ (0, class_validator_1.IsString)(),
27
+ (0, class_validator_1.IsNotEmpty)(),
28
+ (0, class_validator_1.MaxLength)(REFERRAL_NAME_MAX_LENGTH),
29
+ __metadata("design:type", String)
30
+ ], CustomerReferralInput.prototype, "name", void 0);
31
+ __decorate([
32
+ (0, class_transformer_1.Expose)(),
33
+ (0, Trim_1.Trim)(),
34
+ (0, class_validator_1.IsString)(),
35
+ (0, class_validator_1.IsNotEmpty)(),
36
+ __metadata("design:type", String)
37
+ ], CustomerReferralInput.prototype, "phone", void 0);
38
+ __decorate([
39
+ (0, class_transformer_1.Expose)(),
40
+ (0, class_validator_1.IsOptional)(),
41
+ (0, Trim_1.Trim)(),
42
+ (0, class_validator_1.IsEmail)(),
43
+ (0, class_validator_1.MaxLength)(InputPatterns_1.EMAIL_MAX_LENGTH),
44
+ __metadata("design:type", String)
45
+ ], CustomerReferralInput.prototype, "email", void 0);
46
+ /** Body de `privateCreateCustomerReferrals`: lote de referidos de un cliente. */
47
+ class CreateCustomerReferralsRequest {
48
+ }
49
+ exports.CreateCustomerReferralsRequest = CreateCustomerReferralsRequest;
50
+ __decorate([
51
+ (0, class_transformer_1.Expose)(),
52
+ (0, class_validator_1.IsEnum)(ReferralSourceEnum_1.ReferralSourceEnum),
53
+ __metadata("design:type", String)
54
+ ], CreateCustomerReferralsRequest.prototype, "source", void 0);
55
+ __decorate([
56
+ (0, class_transformer_1.Expose)(),
57
+ (0, class_validator_1.ValidateIf)((o) => o.source !== ReferralSourceEnum_1.ReferralSourceEnum.BACKOFFICE || o.storeId !== undefined),
58
+ (0, Trim_1.Trim)(),
59
+ (0, class_validator_1.IsString)(),
60
+ (0, class_validator_1.IsNotEmpty)(),
61
+ __metadata("design:type", String)
62
+ ], CreateCustomerReferralsRequest.prototype, "storeId", void 0);
63
+ __decorate([
64
+ (0, class_transformer_1.Expose)(),
65
+ (0, class_validator_1.IsOptional)(),
66
+ (0, Trim_1.Trim)(),
67
+ (0, class_validator_1.IsString)(),
68
+ (0, class_validator_1.IsNotEmpty)(),
69
+ __metadata("design:type", String)
70
+ ], CreateCustomerReferralsRequest.prototype, "wizardSessionId", void 0);
71
+ __decorate([
72
+ (0, class_transformer_1.Expose)(),
73
+ (0, class_validator_1.IsOptional)(),
74
+ (0, Trim_1.Trim)(),
75
+ (0, class_validator_1.IsString)(),
76
+ (0, class_validator_1.IsNotEmpty)(),
77
+ __metadata("design:type", String)
78
+ ], CreateCustomerReferralsRequest.prototype, "saleId", void 0);
79
+ __decorate([
80
+ (0, class_transformer_1.Expose)(),
81
+ (0, class_validator_1.IsOptional)(),
82
+ (0, Trim_1.Trim)(),
83
+ (0, class_validator_1.IsString)(),
84
+ (0, class_validator_1.IsNotEmpty)(),
85
+ __metadata("design:type", String)
86
+ ], CreateCustomerReferralsRequest.prototype, "accountOpeningSessionId", void 0);
87
+ __decorate([
88
+ (0, class_transformer_1.Expose)(),
89
+ (0, Trim_1.Trim)(),
90
+ (0, class_validator_1.IsString)(),
91
+ (0, class_validator_1.IsNotEmpty)(),
92
+ __metadata("design:type", String)
93
+ ], CreateCustomerReferralsRequest.prototype, "capturedBy", void 0);
94
+ __decorate([
95
+ (0, class_transformer_1.Expose)(),
96
+ (0, class_validator_1.IsArray)(),
97
+ (0, class_validator_1.ArrayMinSize)(1),
98
+ (0, class_validator_1.ValidateNested)({ each: true }),
99
+ (0, class_transformer_1.Type)(() => CustomerReferralInput),
100
+ __metadata("design:type", Array)
101
+ ], CreateCustomerReferralsRequest.prototype, "referrals", void 0);
@@ -0,0 +1,4 @@
1
+ /** Body de `privateGetCustomersByPhones`: qué teléfonos ya son clientes. */
2
+ export declare class GetCustomersByPhonesRequest {
3
+ phones: string[];
4
+ }
@@ -0,0 +1,28 @@
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.GetCustomersByPhonesRequest = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ const PHONES_MAX_BATCH = 100;
16
+ /** Body de `privateGetCustomersByPhones`: qué teléfonos ya son clientes. */
17
+ class GetCustomersByPhonesRequest {
18
+ }
19
+ exports.GetCustomersByPhonesRequest = GetCustomersByPhonesRequest;
20
+ __decorate([
21
+ (0, class_transformer_1.Expose)(),
22
+ (0, class_validator_1.IsArray)(),
23
+ (0, class_validator_1.ArrayMinSize)(1),
24
+ (0, class_validator_1.ArrayMaxSize)(PHONES_MAX_BATCH),
25
+ (0, class_validator_1.IsString)({ each: true }),
26
+ (0, class_validator_1.IsNotEmpty)({ each: true }),
27
+ __metadata("design:type", Array)
28
+ ], GetCustomersByPhonesRequest.prototype, "phones", void 0);
@@ -0,0 +1,6 @@
1
+ /** Query de `privateGetReferralsByPhone`: quién recomendó ese teléfono en la cadena, capturado antes de `before`. */
2
+ export declare class GetReferralsByPhoneQuery {
3
+ retailerId: string;
4
+ phone: string;
5
+ before?: string;
6
+ }
@@ -0,0 +1,39 @@
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.GetReferralsByPhoneQuery = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ const Trim_1 = require("../../../common/decorators/Trim");
16
+ /** Query de `privateGetReferralsByPhone`: quién recomendó ese teléfono en la cadena, capturado antes de `before`. */
17
+ class GetReferralsByPhoneQuery {
18
+ }
19
+ exports.GetReferralsByPhoneQuery = GetReferralsByPhoneQuery;
20
+ __decorate([
21
+ (0, class_transformer_1.Expose)(),
22
+ (0, Trim_1.Trim)(),
23
+ (0, class_validator_1.IsString)(),
24
+ (0, class_validator_1.IsNotEmpty)(),
25
+ __metadata("design:type", String)
26
+ ], GetReferralsByPhoneQuery.prototype, "retailerId", void 0);
27
+ __decorate([
28
+ (0, class_transformer_1.Expose)(),
29
+ (0, Trim_1.Trim)(),
30
+ (0, class_validator_1.IsString)(),
31
+ (0, class_validator_1.IsNotEmpty)(),
32
+ __metadata("design:type", String)
33
+ ], GetReferralsByPhoneQuery.prototype, "phone", void 0);
34
+ __decorate([
35
+ (0, class_transformer_1.Expose)(),
36
+ (0, class_validator_1.IsOptional)(),
37
+ (0, class_validator_1.IsISO8601)(),
38
+ __metadata("design:type", String)
39
+ ], GetReferralsByPhoneQuery.prototype, "before", void 0);
@@ -0,0 +1,8 @@
1
+ /** Body de `privateUpdateCustomerReferral`: corrige datos sin cambiar dónde se capturó. `email: null` lo quita. */
2
+ export declare class UpdateCustomerReferralRequest {
3
+ name?: string;
4
+ phone?: string;
5
+ email?: string | null;
6
+ /** Usuario que edita; la llamada M2M no lo trae en el token. */
7
+ updatedBy: string;
8
+ }