@fiado/type-kit 3.631.0 → 3.632.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 (53) hide show
  1. package/_test_/unit/retailIntegration/retailSaleContracts.test.ts +15 -3
  2. package/_test_/unit/retailWizard/accessorySaleRequests.test.ts +2 -2
  3. package/_test_/unit/retailWizard/salePayments.test.ts +140 -0
  4. package/bin/retailIntegration/models/RetailSystemSale.d.ts +1 -1
  5. package/bin/retailIntegration/models/RetailSystemSale.js +4 -4
  6. package/bin/retailIntegration/validators/saleValidators.d.ts +1 -1
  7. package/bin/retailIntegration/validators/saleValidators.js +3 -2
  8. package/bin/retailWizard/constants/retailWizardValidation.d.ts +5 -0
  9. package/bin/retailWizard/constants/retailWizardValidation.js +6 -1
  10. package/bin/retailWizard/dtos/Sale.d.ts +5 -0
  11. package/bin/retailWizard/dtos/SalePayment.d.ts +11 -0
  12. package/bin/retailWizard/dtos/SalePayment.js +69 -0
  13. package/bin/retailWizard/dtos/SalePaymentDetail.d.ts +1 -0
  14. package/bin/retailWizard/dtos/SalePaymentDetail.js +1 -0
  15. package/bin/retailWizard/dtos/SellerPendingRefundItem.d.ts +6 -1
  16. package/bin/retailWizard/dtos/WizardPayment.d.ts +5 -0
  17. package/bin/retailWizard/dtos/WizardPayment.js +7 -0
  18. package/bin/retailWizard/dtos/requests/CreateAccessorySaleRequest.d.ts +8 -3
  19. package/bin/retailWizard/dtos/requests/CreateAccessorySaleRequest.js +10 -2
  20. package/bin/retailWizard/dtos/requests/DownPaymentRequest.d.ts +7 -2
  21. package/bin/retailWizard/dtos/requests/DownPaymentRequest.js +9 -1
  22. package/bin/retailWizard/dtos/requests/RegisterPaymentRequest.d.ts +6 -1
  23. package/bin/retailWizard/dtos/requests/RegisterPaymentRequest.js +8 -0
  24. package/bin/retailWizard/events/RetailPromotionInboundMessage.d.ts +3 -1
  25. package/bin/retailWizard/events/RetailPromotionInboundMessage.js +6 -0
  26. package/bin/retailWizard/index.d.ts +5 -0
  27. package/bin/retailWizard/index.js +5 -0
  28. package/bin/retailWizard/validators/IsSalePaymentList.d.ts +2 -0
  29. package/bin/retailWizard/validators/IsSalePaymentList.js +22 -0
  30. package/bin/retailWizard/validators/OnlyForPaymentMethodConstraint.d.ts +6 -0
  31. package/bin/retailWizard/validators/OnlyForPaymentMethodConstraint.js +25 -0
  32. package/bin/retailWizard/validators/PaymentsHaveSingleUseMethodsConstraint.d.ts +6 -0
  33. package/bin/retailWizard/validators/PaymentsHaveSingleUseMethodsConstraint.js +27 -0
  34. package/bin/retailWizard/validators/PaymentsMatchAmountCentsConstraint.d.ts +6 -0
  35. package/bin/retailWizard/validators/PaymentsMatchAmountCentsConstraint.js +29 -0
  36. package/package.json +1 -1
  37. package/src/retailIntegration/models/RetailSystemSale.ts +4 -4
  38. package/src/retailIntegration/validators/saleValidators.ts +3 -2
  39. package/src/retailWizard/constants/retailWizardValidation.ts +8 -0
  40. package/src/retailWizard/dtos/Sale.ts +5 -0
  41. package/src/retailWizard/dtos/SalePayment.ts +55 -0
  42. package/src/retailWizard/dtos/SalePaymentDetail.ts +1 -0
  43. package/src/retailWizard/dtos/SellerPendingRefundItem.ts +6 -1
  44. package/src/retailWizard/dtos/WizardPayment.ts +10 -0
  45. package/src/retailWizard/dtos/requests/CreateAccessorySaleRequest.ts +17 -5
  46. package/src/retailWizard/dtos/requests/DownPaymentRequest.ts +14 -3
  47. package/src/retailWizard/dtos/requests/RegisterPaymentRequest.ts +13 -2
  48. package/src/retailWizard/events/RetailPromotionInboundMessage.ts +7 -1
  49. package/src/retailWizard/index.ts +5 -0
  50. package/src/retailWizard/validators/IsSalePaymentList.ts +20 -0
  51. package/src/retailWizard/validators/OnlyForPaymentMethodConstraint.ts +18 -0
  52. package/src/retailWizard/validators/PaymentsHaveSingleUseMethodsConstraint.ts +20 -0
  53. package/src/retailWizard/validators/PaymentsMatchAmountCentsConstraint.ts +21 -0
@@ -145,6 +145,18 @@ describe('RetailSystemSale.payments', () => {
145
145
  expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, senderClabe: null }] })).toEqual([]);
146
146
  });
147
147
 
148
+ it('acepta CARD y WIRE sin comprobante', async () => {
149
+ const card = { ...cardPayment, cardLast4: null, authCode: null };
150
+ const wire = { ...wirePayment, speiTrackingKey: null, senderClabe: null };
151
+ expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, card] })).toEqual([]);
152
+ expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, wire] })).toEqual([]);
153
+ });
154
+
155
+ it('acepta tarjeta repetida y tres medios', async () => {
156
+ const cards = [{ ...cardPayment, amount: '100.00' }, { ...cardPayment, amount: '100.00', cardLast4: null }];
157
+ expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, ...cards] })).toEqual([]);
158
+ });
159
+
148
160
  it('acepta speiTrackingKey de 1 a 30 alfanuméricos', async () => {
149
161
  expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, speiTrackingKey: '7' }] })).toEqual([]);
150
162
  expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, speiTrackingKey: 'A'.repeat(30) }] })).toEqual([]);
@@ -174,9 +186,9 @@ describe('RetailSystemSale.payments', () => {
174
186
  });
175
187
 
176
188
  it.each([
177
- ['cardLast4', { cardLast4: null }],
189
+ ['cardLast4', { cardLast4: undefined }],
178
190
  ['cardLast4', { cardLast4: '424' }],
179
- ['authCode', { authCode: null }],
191
+ ['authCode', { authCode: undefined }],
180
192
  ['authCode', { authCode: ' ' }],
181
193
  ['authCode', { authCode: 'A'.repeat(65) }],
182
194
  ['speiTrackingKey', { speiTrackingKey: '123456789012345678' }],
@@ -186,7 +198,7 @@ describe('RetailSystemSale.payments', () => {
186
198
  });
187
199
 
188
200
  it.each([
189
- ['speiTrackingKey', { speiTrackingKey: null }],
201
+ ['speiTrackingKey', { speiTrackingKey: undefined }],
190
202
  ['speiTrackingKey', { speiTrackingKey: '' }],
191
203
  ['speiTrackingKey', { speiTrackingKey: 'A'.repeat(31) }],
192
204
  ['speiTrackingKey', { speiTrackingKey: 'MBAN-0100-2610' }],
@@ -45,9 +45,9 @@ describe('CreateAccessorySaleRequest', () => {
45
45
  expect(errorsOf(CreateAccessorySaleRequest, { ...validSale, amountCents: 10.5 })).toContain('amountCents');
46
46
  });
47
47
 
48
- it('exige el comprobante del cobro', () => {
48
+ it('acepta el cobro sin comprobante', () => {
49
49
  const { paymentDetail: _omit, ...rest } = validSale;
50
- expect(errorsOf(CreateAccessorySaleRequest, rest)).toContain('paymentDetail');
50
+ expect(errorsOf(CreateAccessorySaleRequest, rest)).toEqual([]);
51
51
  });
52
52
 
53
53
  it('no lleva aviso de privacidad: privacyNoticeAccepted y privacyNoticeVersion se descartan', () => {
@@ -0,0 +1,140 @@
1
+ import 'reflect-metadata';
2
+ import { plainToInstance } from 'class-transformer';
3
+ import { validateSync, type ValidationError } from 'class-validator';
4
+ import { SalePayment } from '../../../src/retailWizard/dtos/SalePayment';
5
+ import { WizardPayment } from '../../../src/retailWizard/dtos/WizardPayment';
6
+ import { RegisterPaymentRequest } from '../../../src/retailWizard/dtos/requests/RegisterPaymentRequest';
7
+ import { DownPaymentRequest } from '../../../src/retailWizard/dtos/requests/DownPaymentRequest';
8
+ import { CreateAccessorySaleRequest } from '../../../src/retailWizard/dtos/requests/CreateAccessorySaleRequest';
9
+ import { RetailPromotionInboundMessage } from '../../../src/retailWizard/events/RetailPromotionInboundMessage';
10
+ import { MAX_SALE_PAYMENTS, SINGLE_USE_PAYMENT_METHODS } from '../../../src/retailWizard/constants/retailWizardValidation';
11
+ import { SalePaymentMethodEnum } from '../../../src/retailWizard/enums/SalePaymentMethodEnum';
12
+ import * as RetailWizard from '../../../src/retailWizard';
13
+
14
+ const { CASH, CARD, WIRE } = SalePaymentMethodEnum;
15
+
16
+ function paths(errors: ValidationError[], prefix = ''): string[] {
17
+ return errors.flatMap((e) => {
18
+ const path = `${prefix}${e.property}`;
19
+ return e.children?.length ? paths(e.children, `${path}.`) : [path];
20
+ });
21
+ }
22
+
23
+ function errorsOf<T extends object>(cls: new () => T, payload: Record<string, unknown>): string[] {
24
+ return paths(validateSync(plainToInstance(cls, payload, { excludeExtraneousValues: true })));
25
+ }
26
+
27
+ const cash = { method: CASH, amountCents: 10000, cashConfirmedInRegister: true };
28
+ const card = { method: CARD, amountCents: 5000, cardLast4: '4242', authCode: 'A1B2C3' };
29
+ const wire = { method: WIRE, amountCents: 5000, speiTrackingKey: 'MBAN01002610', senderClabe: '012180001234567897' };
30
+
31
+ describe('SalePayment', () => {
32
+ it('acepta cada medio con comprobante', () => {
33
+ expect(errorsOf(SalePayment, cash)).toEqual([]);
34
+ expect(errorsOf(SalePayment, card)).toEqual([]);
35
+ expect(errorsOf(SalePayment, wire)).toEqual([]);
36
+ });
37
+
38
+ it('acepta tarjeta y SPEI sin comprobante, ausente o en null', () => {
39
+ expect(errorsOf(SalePayment, { method: CARD, amountCents: 5000 })).toEqual([]);
40
+ expect(errorsOf(SalePayment, { method: WIRE, amountCents: 5000, speiTrackingKey: null, senderClabe: null })).toEqual([]);
41
+ expect(errorsOf(SalePayment, { method: CASH, amountCents: 5000, cashConfirmedInRegister: null })).toEqual([]);
42
+ });
43
+
44
+ it.each([
45
+ ['cardLast4', { ...card, cardLast4: '424' }],
46
+ ['authCode', { ...card, authCode: ' ' }],
47
+ ['authCode', { ...card, authCode: 'A'.repeat(65) }],
48
+ ['speiTrackingKey', { ...wire, speiTrackingKey: 'MBAN-01' }],
49
+ ['senderClabe', { ...wire, senderClabe: '0121' }],
50
+ ['cashConfirmedInRegister', { ...cash, cashConfirmedInRegister: 'yes' }],
51
+ ])('rechaza %s con formato inválido', (field, payload) => {
52
+ expect(errorsOf(SalePayment, payload)).toEqual([field]);
53
+ });
54
+
55
+ it.each([
56
+ ['cardLast4', { ...cash, cardLast4: '4242' }],
57
+ ['speiTrackingKey', { ...card, speiTrackingKey: 'MBAN01' }],
58
+ ['cashConfirmedInRegister', { ...wire, cashConfirmedInRegister: true }],
59
+ ])('rechaza %s en un medio que no le corresponde', (field, payload) => {
60
+ expect(errorsOf(SalePayment, payload)).toEqual([field]);
61
+ });
62
+
63
+ it('rechaza un medio fuera del enum y un monto no positivo', () => {
64
+ expect(errorsOf(SalePayment, { method: 'FIADO', amountCents: 100 })).toEqual(['method']);
65
+ expect(errorsOf(SalePayment, { ...cash, amountCents: 0 })).toEqual(['amountCents']);
66
+ expect(errorsOf(SalePayment, { ...cash, amountCents: 10.5 })).toEqual(['amountCents']);
67
+ });
68
+ });
69
+
70
+ describe('payments en RegisterPaymentRequest', () => {
71
+ it('acepta solo payments, sin method ni detail', () => {
72
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 20000, payments: [cash, card, wire] })).toEqual([]);
73
+ });
74
+
75
+ it('acepta tarjeta repetida', () => {
76
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 20000, payments: [cash, card, card] })).toEqual([]);
77
+ });
78
+
79
+ it('sigue aceptando el formato viejo', () => {
80
+ expect(errorsOf(RegisterPaymentRequest, { method: CASH, amountCents: 20000, detail: { cashConfirmedInRegister: true } })).toEqual([]);
81
+ });
82
+
83
+ it('exige method si no vienen payments', () => {
84
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 20000 })).toEqual(['method']);
85
+ });
86
+
87
+ it('rechaza efectivo o SPEI repetidos', () => {
88
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 20000, payments: [cash, cash] })).toEqual(['payments']);
89
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 10000, payments: [wire, wire] })).toEqual(['payments']);
90
+ });
91
+
92
+ it(`rechaza más de ${MAX_SALE_PAYMENTS} pagos o una lista vacía`, () => {
93
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 25000, payments: [cash, card, card, wire] })).toEqual(['payments']);
94
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 0, payments: [] })).toEqual(['payments']);
95
+ });
96
+
97
+ it('rechaza que la suma no cuadre con amountCents', () => {
98
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 20001, payments: [cash, card, wire] })).toEqual(['payments']);
99
+ });
100
+
101
+ it('reporta el campo del pago inválido', () => {
102
+ expect(errorsOf(RegisterPaymentRequest, { amountCents: 15000, payments: [cash, { ...card, cardLast4: '1' }] })).toEqual([
103
+ 'payments.1.cardLast4',
104
+ ]);
105
+ });
106
+ });
107
+
108
+ describe('payments en los demás contratos', () => {
109
+ it('DownPaymentRequest acepta solo payments y valida la suma', () => {
110
+ expect(errorsOf(DownPaymentRequest, { amountCents: 15000, payments: [cash, wire] })).toEqual([]);
111
+ expect(errorsOf(DownPaymentRequest, { amountCents: 1, payments: [cash, wire] })).toEqual(['payments']);
112
+ });
113
+
114
+ it('CreateAccessorySaleRequest acepta solo payments y ya no exige paymentDetail', () => {
115
+ const lines = [{ sku: 'ACC-1', qty: 1 }];
116
+ expect(errorsOf(CreateAccessorySaleRequest, { lines, amountCents: 15000, payments: [cash, card] })).toEqual([]);
117
+ expect(errorsOf(CreateAccessorySaleRequest, { lines, amountCents: 15000 })).toEqual(['paymentMethod']);
118
+ expect(errorsOf(CreateAccessorySaleRequest, { lines, amountCents: 20000, payments: [cash, cash] })).toEqual(['payments']);
119
+ });
120
+
121
+ it('WizardPayment guarda payments tipados', () => {
122
+ const base = { method: CASH, amountCents: 15000, paidAt: '2026-11-03T10:00:00.000Z' };
123
+ expect(errorsOf(WizardPayment, { ...base, payments: [cash, card] })).toEqual([]);
124
+ expect(errorsOf(WizardPayment, base)).toEqual([]);
125
+ expect(errorsOf(WizardPayment, { ...base, payments: [cash, cash] })).toEqual(['payments']);
126
+ });
127
+
128
+ it('RetailPromotionInboundMessage acepta paymentMethods y rechaza valores fuera del enum', () => {
129
+ const plain = { paymentMethods: [CASH, CARD] };
130
+ const dto = plainToInstance(RetailPromotionInboundMessage, plain);
131
+ expect(paths(validateSync(dto)).filter((p) => p === 'paymentMethods')).toEqual([]);
132
+ const bad = plainToInstance(RetailPromotionInboundMessage, { paymentMethods: ['FIADO'] });
133
+ expect(paths(validateSync(bad))).toContain('paymentMethods');
134
+ });
135
+
136
+ it('el barrel exporta los tipos nuevos', () => {
137
+ expect(RetailWizard.SalePayment).toBe(SalePayment);
138
+ expect(SINGLE_USE_PAYMENT_METHODS).toEqual([CASH, WIRE]);
139
+ });
140
+ });
@@ -22,7 +22,7 @@ export declare class RetailSystemSaleCredit {
22
22
  totalToPay: string;
23
23
  firstPaymentDate: string;
24
24
  }
25
- /** Un medio de pago de `amountPaid`; las seis llaves van siempre, en `null` las que no aplican. */
25
+ /** Un medio de pago de `amountPaid`; las seis llaves van siempre. El comprobante es opcional (`null`) en su medio y `null` en los demás. */
26
26
  export declare class RetailSystemSalePayment {
27
27
  method: RetailPaymentMethod;
28
28
  amount: string;
@@ -79,7 +79,7 @@ __decorate([
79
79
  (0, class_validator_1.Validate)(IsCalendarDate_1.IsCalendarDate),
80
80
  __metadata("design:type", String)
81
81
  ], RetailSystemSaleCredit.prototype, "firstPaymentDate", void 0);
82
- /** Un medio de pago de `amountPaid`; las seis llaves van siempre, en `null` las que no aplican. */
82
+ /** Un medio de pago de `amountPaid`; las seis llaves van siempre. El comprobante es opcional (`null`) en su medio y `null` en los demás. */
83
83
  class RetailSystemSalePayment {
84
84
  }
85
85
  exports.RetailSystemSalePayment = RetailSystemSalePayment;
@@ -93,15 +93,15 @@ __decorate([
93
93
  __metadata("design:type", String)
94
94
  ], RetailSystemSalePayment.prototype, "amount", void 0);
95
95
  __decorate([
96
- (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [CARD, retailIntegrationLimits_1.RETAIL_CARD_LAST4_PATTERN]),
96
+ (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [CARD, retailIntegrationLimits_1.RETAIL_CARD_LAST4_PATTERN, true]),
97
97
  __metadata("design:type", String)
98
98
  ], RetailSystemSalePayment.prototype, "cardLast4", void 0);
99
99
  __decorate([
100
- (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [CARD, retailIntegrationLimits_1.RETAIL_SALE_ID_TEXT_PATTERN]),
100
+ (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [CARD, retailIntegrationLimits_1.RETAIL_SALE_ID_TEXT_PATTERN, true]),
101
101
  __metadata("design:type", String)
102
102
  ], RetailSystemSalePayment.prototype, "authCode", void 0);
103
103
  __decorate([
104
- (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [WIRE, retailIntegrationLimits_1.RETAIL_SPEI_TRACKING_KEY_PATTERN]),
104
+ (0, class_validator_1.Validate)(saleValidators_1.MatchesForPaymentMethod, [WIRE, retailIntegrationLimits_1.RETAIL_SPEI_TRACKING_KEY_PATTERN, true]),
105
105
  __metadata("design:type", String)
106
106
  ], RetailSystemSalePayment.prototype, "speiTrackingKey", void 0);
107
107
  __decorate([
@@ -1,5 +1,5 @@
1
1
  import { ValidationArguments, ValidatorConstraintInterface } from 'class-validator';
2
- /** Campo de un pago que solo aplica a un medio: con ese medio cumple el patrón; con los demás es `null`. */
2
+ /** Campo de un pago que solo aplica a un medio: con ese medio cumple el patrón (o es `null` si es nulable); con los demás es `null`. */
3
3
  export declare class MatchesForPaymentMethod implements ValidatorConstraintInterface {
4
4
  validate(value: unknown, args: ValidationArguments): boolean;
5
5
  defaultMessage(args: ValidationArguments): string;
@@ -13,7 +13,7 @@ const enums_1 = require("../enums");
13
13
  function decimalToCents(value) {
14
14
  return Number(value.replace('.', ''));
15
15
  }
16
- /** Campo de un pago que solo aplica a un medio: con ese medio cumple el patrón; con los demás es `null`. */
16
+ /** Campo de un pago que solo aplica a un medio: con ese medio cumple el patrón (o es `null` si es nulable); con los demás es `null`. */
17
17
  let MatchesForPaymentMethod = class MatchesForPaymentMethod {
18
18
  validate(value, args) {
19
19
  const [method, pattern, nullable] = args.constraints;
@@ -24,7 +24,8 @@ let MatchesForPaymentMethod = class MatchesForPaymentMethod {
24
24
  return typeof value === 'string' && pattern.test(value);
25
25
  }
26
26
  defaultMessage(args) {
27
- return `${args.property} es obligatorio y válido en ${args.constraints[0]} y null en los demás medios`;
27
+ const nullable = args.constraints[2] === true;
28
+ return `${args.property} debe ser ${nullable ? 'null o válido' : 'válido'} en ${args.constraints[0]} y null en los demás medios`;
28
29
  }
29
30
  };
30
31
  exports.MatchesForPaymentMethod = MatchesForPaymentMethod;
@@ -1,2 +1,7 @@
1
+ import { SalePaymentMethodEnum } from '../enums/SalePaymentMethodEnum';
1
2
  /** Nombre de lambda en kebab-case, p. ej. `reconciliation-engine-business`. */
2
3
  export declare const RETAIL_WIZARD_CALLER_SERVICE_PATTERN: RegExp;
4
+ /** Tope de pagos con los que se cobra una venta, un enganche o una venta de mostrador. */
5
+ export declare const MAX_SALE_PAYMENTS = 3;
6
+ /** Medios que aparecen a lo más una vez por cobro; la tarjeta se puede repetir. */
7
+ export declare const SINGLE_USE_PAYMENT_METHODS: readonly SalePaymentMethodEnum[];
@@ -1,5 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.RETAIL_WIZARD_CALLER_SERVICE_PATTERN = void 0;
3
+ exports.SINGLE_USE_PAYMENT_METHODS = exports.MAX_SALE_PAYMENTS = exports.RETAIL_WIZARD_CALLER_SERVICE_PATTERN = void 0;
4
+ const SalePaymentMethodEnum_1 = require("../enums/SalePaymentMethodEnum");
4
5
  /** Nombre de lambda en kebab-case, p. ej. `reconciliation-engine-business`. */
5
6
  exports.RETAIL_WIZARD_CALLER_SERVICE_PATTERN = /^[a-z0-9-]{1,64}$/;
7
+ /** Tope de pagos con los que se cobra una venta, un enganche o una venta de mostrador. */
8
+ exports.MAX_SALE_PAYMENTS = 3;
9
+ /** Medios que aparecen a lo más una vez por cobro; la tarjeta se puede repetir. */
10
+ exports.SINGLE_USE_PAYMENT_METHODS = [SalePaymentMethodEnum_1.SalePaymentMethodEnum.CASH, SalePaymentMethodEnum_1.SalePaymentMethodEnum.WIRE];
@@ -7,6 +7,7 @@ import type { CentralSyncStatusEnum } from '../enums/CentralSyncStatusEnum';
7
7
  import type { CentralCancelStatusEnum } from '../enums/CentralCancelStatusEnum';
8
8
  import type { CentralLiveCheckEnum } from '../enums/CentralLiveCheckEnum';
9
9
  import type { SalePaymentDetail } from './SalePaymentDetail';
10
+ import type { SalePayment } from './SalePayment';
10
11
  import type { SaleLine } from './SaleLine';
11
12
  import type { SalePromotionRef } from '../../retailPromotions/dtos/SalePromotionRef';
12
13
  import type { SaleCancellation } from './SaleCancellation';
@@ -56,10 +57,14 @@ export interface Sale {
56
57
  downPaymentCents: number | null;
57
58
  /** Puntero al crédito SOFOM en loan-credit (solo ventas CREDIT; `null` en contado/externa). */
58
59
  creditId: string | null;
60
+ /** @deprecated Usa `payments`; con varios pagos lleva el método del primero. */
59
61
  paymentMethod: SalePaymentMethodEnum | null;
60
62
  /** `LG-T{storeCode}-VTA-{5hex}-{YYYY}W{ww}` (D11) — único vía `UNIQ#FOLIO`. */
61
63
  folio: string;
64
+ /** @deprecated Usa el comprobante de cada elemento de `payments`. */
62
65
  paymentDetail: SalePaymentDetail | null;
66
+ /** Pagos con que se cobró `amountCents` (1 a 3); `null` sin cobro, ausente en ventas anteriores al campo. */
67
+ payments?: SalePayment[] | null;
63
68
  soldAt: string;
64
69
  /** Puntero a la entrega de la tarjeta física en `retail-cards-business`. */
65
70
  cardDeliveryId: string | null;
@@ -0,0 +1,11 @@
1
+ import { SalePaymentMethodEnum } from '../enums/SalePaymentMethodEnum';
2
+ /** Un pago del cobro. El comprobante es opcional: si viene se valida y solo en su medio; si no, `null` o ausente. */
3
+ export declare class SalePayment {
4
+ method: SalePaymentMethodEnum;
5
+ amountCents: number;
6
+ cashConfirmedInRegister?: boolean | null;
7
+ cardLast4?: string | null;
8
+ authCode?: string | null;
9
+ speiTrackingKey?: string | null;
10
+ senderClabe?: string | null;
11
+ }
@@ -0,0 +1,69 @@
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.SalePayment = void 0;
13
+ const class_transformer_1 = require("class-transformer");
14
+ const class_validator_1 = require("class-validator");
15
+ const retailIntegrationLimits_1 = require("../../retailIntegration/constants/retailIntegrationLimits");
16
+ const SalePaymentMethodEnum_1 = require("../enums/SalePaymentMethodEnum");
17
+ const OnlyForPaymentMethodConstraint_1 = require("../validators/OnlyForPaymentMethodConstraint");
18
+ const { CASH, CARD, WIRE } = SalePaymentMethodEnum_1.SalePaymentMethodEnum;
19
+ const hasValue = (field) => (o) => o[field] != null;
20
+ /** Un pago del cobro. El comprobante es opcional: si viene se valida y solo en su medio; si no, `null` o ausente. */
21
+ class SalePayment {
22
+ }
23
+ exports.SalePayment = SalePayment;
24
+ __decorate([
25
+ (0, class_transformer_1.Expose)(),
26
+ (0, class_validator_1.IsEnum)(SalePaymentMethodEnum_1.SalePaymentMethodEnum),
27
+ __metadata("design:type", String)
28
+ ], SalePayment.prototype, "method", void 0);
29
+ __decorate([
30
+ (0, class_transformer_1.Expose)(),
31
+ (0, class_validator_1.IsInt)(),
32
+ (0, class_validator_1.Min)(1),
33
+ __metadata("design:type", Number)
34
+ ], SalePayment.prototype, "amountCents", void 0);
35
+ __decorate([
36
+ (0, class_transformer_1.Expose)(),
37
+ (0, class_validator_1.ValidateIf)(hasValue('cashConfirmedInRegister')),
38
+ (0, class_validator_1.IsBoolean)(),
39
+ (0, class_validator_1.Validate)(OnlyForPaymentMethodConstraint_1.OnlyForPaymentMethodConstraint, [CASH]),
40
+ __metadata("design:type", Boolean)
41
+ ], SalePayment.prototype, "cashConfirmedInRegister", void 0);
42
+ __decorate([
43
+ (0, class_transformer_1.Expose)(),
44
+ (0, class_validator_1.ValidateIf)(hasValue('cardLast4')),
45
+ (0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_CARD_LAST4_PATTERN),
46
+ (0, class_validator_1.Validate)(OnlyForPaymentMethodConstraint_1.OnlyForPaymentMethodConstraint, [CARD]),
47
+ __metadata("design:type", String)
48
+ ], SalePayment.prototype, "cardLast4", void 0);
49
+ __decorate([
50
+ (0, class_transformer_1.Expose)(),
51
+ (0, class_validator_1.ValidateIf)(hasValue('authCode')),
52
+ (0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_SALE_ID_TEXT_PATTERN),
53
+ (0, class_validator_1.Validate)(OnlyForPaymentMethodConstraint_1.OnlyForPaymentMethodConstraint, [CARD]),
54
+ __metadata("design:type", String)
55
+ ], SalePayment.prototype, "authCode", void 0);
56
+ __decorate([
57
+ (0, class_transformer_1.Expose)(),
58
+ (0, class_validator_1.ValidateIf)(hasValue('speiTrackingKey')),
59
+ (0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_SPEI_TRACKING_KEY_PATTERN),
60
+ (0, class_validator_1.Validate)(OnlyForPaymentMethodConstraint_1.OnlyForPaymentMethodConstraint, [WIRE]),
61
+ __metadata("design:type", String)
62
+ ], SalePayment.prototype, "speiTrackingKey", void 0);
63
+ __decorate([
64
+ (0, class_transformer_1.Expose)(),
65
+ (0, class_validator_1.ValidateIf)(hasValue('senderClabe')),
66
+ (0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_CLABE_PATTERN),
67
+ (0, class_validator_1.Validate)(OnlyForPaymentMethodConstraint_1.OnlyForPaymentMethodConstraint, [WIRE]),
68
+ __metadata("design:type", String)
69
+ ], SalePayment.prototype, "senderClabe", void 0);
@@ -2,6 +2,7 @@
2
2
  * Detalle del pago según el medio (`Sale.paymentDetail`): efectivo `{cashConfirmedInRegister}` ·
3
3
  * tarjeta `{cardLast4, authCode}` · SPEI `{speiTrackingKey, senderClabe?}`. SureKeep Fase 2 — pista Retail.
4
4
  */
5
+ /** @deprecated Usa `SalePayment`, un elemento por pago. */
5
6
  export declare class SalePaymentDetail {
6
7
  cashConfirmedInRegister?: boolean;
7
8
  cardLast4?: string;
@@ -16,6 +16,7 @@ const class_validator_1 = require("class-validator");
16
16
  * Detalle del pago según el medio (`Sale.paymentDetail`): efectivo `{cashConfirmedInRegister}` ·
17
17
  * tarjeta `{cardLast4, authCode}` · SPEI `{speiTrackingKey, senderClabe?}`. SureKeep Fase 2 — pista Retail.
18
18
  */
19
+ /** @deprecated Usa `SalePayment`, un elemento por pago. */
19
20
  class SalePaymentDetail {
20
21
  }
21
22
  exports.SalePaymentDetail = SalePaymentDetail;
@@ -1,5 +1,6 @@
1
1
  import { RefundStatusEnum } from '../enums/RefundStatusEnum';
2
2
  import { SalePaymentMethodEnum } from '../enums/SalePaymentMethodEnum';
3
+ import type { SalePayment } from './SalePayment';
3
4
  /** Un reembolso de enganche de la tienda en el Portal Vendedor (GET /portal/pending-refunds). */
4
5
  export interface SellerPendingRefundItem {
5
6
  sessionId: string;
@@ -9,8 +10,12 @@ export interface SellerPendingRefundItem {
9
10
  creditId: string | null;
10
11
  folio: string | null;
11
12
  refundPendingCents: number;
12
- /** `null` = el registro no guardó con qué se cobró el enganche; no se puede marcar entregado. */
13
+ /**
14
+ * @deprecated Usa `payments`. `null` = el registro no guardó con qué se cobró el enganche; no se puede marcar entregado.
15
+ */
13
16
  paymentMethod: SalePaymentMethodEnum | null;
17
+ /** Pagos con que se cobró el enganche; ausente en registros anteriores al campo. */
18
+ payments?: SalePayment[] | null;
14
19
  status: RefundStatusEnum;
15
20
  /** `true` si el vendedor puede marcarlo entregado: efectivo y pendiente. */
16
21
  deliverable: boolean;
@@ -1,8 +1,13 @@
1
1
  import { SalePaymentMethodEnum } from '../enums/SalePaymentMethodEnum';
2
+ import { SalePayment } from './SalePayment';
2
3
  /** Registro del pago de contado dentro de la sesión (`WizardSession.payment`). SureKeep Fase 2 — pista Retail. */
3
4
  export declare class WizardPayment {
5
+ /** @deprecated Usa `payments`; con varios pagos lleva el método del primero. */
4
6
  method: SalePaymentMethodEnum;
5
7
  amountCents: number;
8
+ /** @deprecated Usa `payments`; es el comprobante como texto JSON. */
6
9
  detail?: string;
10
+ /** Pagos del cobro (1 a 3); ausente en sesiones anteriores al campo. */
11
+ payments?: SalePayment[];
7
12
  paidAt: string;
8
13
  }
@@ -14,6 +14,7 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const regex_1 = require("../../helpers/constans/regex");
16
16
  const SalePaymentMethodEnum_1 = require("../enums/SalePaymentMethodEnum");
17
+ const IsSalePaymentList_1 = require("../validators/IsSalePaymentList");
17
18
  /** Registro del pago de contado dentro de la sesión (`WizardSession.payment`). SureKeep Fase 2 — pista Retail. */
18
19
  class WizardPayment {
19
20
  }
@@ -35,6 +36,12 @@ __decorate([
35
36
  (0, class_validator_1.IsString)(),
36
37
  __metadata("design:type", String)
37
38
  ], WizardPayment.prototype, "detail", void 0);
39
+ __decorate([
40
+ (0, class_transformer_1.Expose)(),
41
+ (0, class_validator_1.IsOptional)(),
42
+ (0, IsSalePaymentList_1.IsSalePaymentList)(),
43
+ __metadata("design:type", Array)
44
+ ], WizardPayment.prototype, "payments", void 0);
38
45
  __decorate([
39
46
  (0, class_transformer_1.Expose)(),
40
47
  (0, class_validator_1.IsString)(),
@@ -1,13 +1,18 @@
1
1
  import { SalePaymentMethodEnum } from '../../enums/SalePaymentMethodEnum';
2
2
  import { SalePaymentDetail } from '../SalePaymentDetail';
3
3
  import { AccessorySaleLine } from '../AccessorySaleLine';
4
+ import { SalePayment } from '../SalePayment';
4
5
  /**
5
6
  * POST /wizard/accessory-sales — cierra la venta de mostrador, sin cliente.
6
- * `amountCents` es lo cobrado en caja y debe igualar la suma del catálogo.
7
+ * `amountCents` es lo cobrado en caja: iguala la suma del catálogo y la de `payments`.
7
8
  */
8
9
  export declare class CreateAccessorySaleRequest {
9
10
  lines: AccessorySaleLine[];
10
- paymentMethod: SalePaymentMethodEnum;
11
+ /** @deprecated Usa `payments`; obligatorio solo si `payments` no viene. */
12
+ paymentMethod?: SalePaymentMethodEnum;
11
13
  amountCents: number;
12
- paymentDetail: SalePaymentDetail;
14
+ /** @deprecated Usa el comprobante de cada elemento de `payments`. */
15
+ paymentDetail?: SalePaymentDetail;
16
+ /** Pagos del cobro (1 a 3); si viene, manda sobre `paymentMethod` y `paymentDetail`. */
17
+ payments?: SalePayment[];
13
18
  }
@@ -15,9 +15,10 @@ const class_validator_1 = require("class-validator");
15
15
  const SalePaymentMethodEnum_1 = require("../../enums/SalePaymentMethodEnum");
16
16
  const SalePaymentDetail_1 = require("../SalePaymentDetail");
17
17
  const AccessorySaleLine_1 = require("../AccessorySaleLine");
18
+ const IsSalePaymentList_1 = require("../../validators/IsSalePaymentList");
18
19
  /**
19
20
  * POST /wizard/accessory-sales — cierra la venta de mostrador, sin cliente.
20
- * `amountCents` es lo cobrado en caja y debe igualar la suma del catálogo.
21
+ * `amountCents` es lo cobrado en caja: iguala la suma del catálogo y la de `payments`.
21
22
  */
22
23
  class CreateAccessorySaleRequest {
23
24
  }
@@ -33,6 +34,7 @@ __decorate([
33
34
  ], CreateAccessorySaleRequest.prototype, "lines", void 0);
34
35
  __decorate([
35
36
  (0, class_transformer_1.Expose)(),
37
+ (0, class_validator_1.ValidateIf)((o) => o.payments == null || o.paymentMethod !== undefined),
36
38
  (0, class_validator_1.IsEnum)(SalePaymentMethodEnum_1.SalePaymentMethodEnum),
37
39
  __metadata("design:type", String)
38
40
  ], CreateAccessorySaleRequest.prototype, "paymentMethod", void 0);
@@ -44,8 +46,14 @@ __decorate([
44
46
  ], CreateAccessorySaleRequest.prototype, "amountCents", void 0);
45
47
  __decorate([
46
48
  (0, class_transformer_1.Expose)(),
47
- (0, class_validator_1.IsDefined)(),
49
+ (0, class_validator_1.IsOptional)(),
48
50
  (0, class_validator_1.ValidateNested)(),
49
51
  (0, class_transformer_1.Type)(() => SalePaymentDetail_1.SalePaymentDetail),
50
52
  __metadata("design:type", SalePaymentDetail_1.SalePaymentDetail)
51
53
  ], CreateAccessorySaleRequest.prototype, "paymentDetail", void 0);
54
+ __decorate([
55
+ (0, class_transformer_1.Expose)(),
56
+ (0, class_validator_1.IsOptional)(),
57
+ (0, IsSalePaymentList_1.IsSalePaymentList)(),
58
+ __metadata("design:type", Array)
59
+ ], CreateAccessorySaleRequest.prototype, "payments", void 0);
@@ -1,16 +1,21 @@
1
1
  import { SalePaymentMethodEnum } from '../../enums/SalePaymentMethodEnum';
2
2
  import { SalePaymentDetail } from '../SalePaymentDetail';
3
+ import { SalePayment } from '../SalePayment';
3
4
  /**
4
5
  * POST /wizard/sessions/:id/down-payment — cobro del ENGANCHE de la venta a crédito (paso 07,
5
6
  * pista SOFOM). Endpoint propio, separado del cobro de contado (`/payment`): acá la regla dura es
6
7
  * `amountCents == downPaymentCents` del crédito (`loan-credit GET /credits/{id}`), NO el precio
7
8
  * full. Idempotente por sesión. El folio `SK-…-ENG-…` lo genera el server (no viaja en el body).
8
9
  * Al confirmar, el wizard marca la condición 1 del checklist (`activate-check { downPayment }`).
9
- * SPEI: el vendedor captura el comprobante (`speiTrackingKey`+`senderClabe`) y avanza.
10
+ * El comprobante de tarjeta y SPEI es opcional; `amountCents` es la suma de `payments`.
10
11
  * SureKeep Fase 2 — pista SOFOM.
11
12
  */
12
13
  export declare class DownPaymentRequest {
13
- method: SalePaymentMethodEnum;
14
+ /** @deprecated Usa `payments`; obligatorio solo si `payments` no viene. */
15
+ method?: SalePaymentMethodEnum;
14
16
  amountCents: number;
17
+ /** @deprecated Usa el comprobante de cada elemento de `payments`. */
15
18
  detail?: SalePaymentDetail;
19
+ /** Pagos del cobro (1 a 3); si viene, manda sobre `method` y `detail`. */
20
+ payments?: SalePayment[];
16
21
  }
@@ -14,13 +14,14 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const SalePaymentMethodEnum_1 = require("../../enums/SalePaymentMethodEnum");
16
16
  const SalePaymentDetail_1 = require("../SalePaymentDetail");
17
+ const IsSalePaymentList_1 = require("../../validators/IsSalePaymentList");
17
18
  /**
18
19
  * POST /wizard/sessions/:id/down-payment — cobro del ENGANCHE de la venta a crédito (paso 07,
19
20
  * pista SOFOM). Endpoint propio, separado del cobro de contado (`/payment`): acá la regla dura es
20
21
  * `amountCents == downPaymentCents` del crédito (`loan-credit GET /credits/{id}`), NO el precio
21
22
  * full. Idempotente por sesión. El folio `SK-…-ENG-…` lo genera el server (no viaja en el body).
22
23
  * Al confirmar, el wizard marca la condición 1 del checklist (`activate-check { downPayment }`).
23
- * SPEI: el vendedor captura el comprobante (`speiTrackingKey`+`senderClabe`) y avanza.
24
+ * El comprobante de tarjeta y SPEI es opcional; `amountCents` es la suma de `payments`.
24
25
  * SureKeep Fase 2 — pista SOFOM.
25
26
  */
26
27
  class DownPaymentRequest {
@@ -28,6 +29,7 @@ class DownPaymentRequest {
28
29
  exports.DownPaymentRequest = DownPaymentRequest;
29
30
  __decorate([
30
31
  (0, class_transformer_1.Expose)(),
32
+ (0, class_validator_1.ValidateIf)((o) => o.payments == null || o.method !== undefined),
31
33
  (0, class_validator_1.IsEnum)(SalePaymentMethodEnum_1.SalePaymentMethodEnum),
32
34
  __metadata("design:type", String)
33
35
  ], DownPaymentRequest.prototype, "method", void 0);
@@ -44,3 +46,9 @@ __decorate([
44
46
  (0, class_validator_1.ValidateNested)(),
45
47
  __metadata("design:type", SalePaymentDetail_1.SalePaymentDetail)
46
48
  ], DownPaymentRequest.prototype, "detail", void 0);
49
+ __decorate([
50
+ (0, class_transformer_1.Expose)(),
51
+ (0, class_validator_1.IsOptional)(),
52
+ (0, IsSalePaymentList_1.IsSalePaymentList)(),
53
+ __metadata("design:type", Array)
54
+ ], DownPaymentRequest.prototype, "payments", void 0);
@@ -1,12 +1,17 @@
1
1
  import { SalePaymentMethodEnum } from '../../enums/SalePaymentMethodEnum';
2
2
  import { SalePaymentDetail } from '../SalePaymentDetail';
3
+ import { SalePayment } from '../SalePayment';
3
4
  /**
4
5
  * POST /wizard/sessions/:id/payment — registra el cobro de contado (idempotente por sesión). Regla
5
6
  * dura: `amountCents == cashPrice` de la reserva. `POST /payment/folio` genera el folio server-side
6
7
  * (D11) ANTES del registro; el folio no viaja en el body (lo posee el server). SureKeep Fase 2 — pista Retail.
7
8
  */
8
9
  export declare class RegisterPaymentRequest {
9
- method: SalePaymentMethodEnum;
10
+ /** @deprecated Usa `payments`; obligatorio solo si `payments` no viene. */
11
+ method?: SalePaymentMethodEnum;
10
12
  amountCents: number;
13
+ /** @deprecated Usa el comprobante de cada elemento de `payments`. */
11
14
  detail?: SalePaymentDetail;
15
+ /** Pagos del cobro (1 a 3); si viene, manda sobre `method` y `detail`. */
16
+ payments?: SalePayment[];
12
17
  }
@@ -14,6 +14,7 @@ const class_transformer_1 = require("class-transformer");
14
14
  const class_validator_1 = require("class-validator");
15
15
  const SalePaymentMethodEnum_1 = require("../../enums/SalePaymentMethodEnum");
16
16
  const SalePaymentDetail_1 = require("../SalePaymentDetail");
17
+ const IsSalePaymentList_1 = require("../../validators/IsSalePaymentList");
17
18
  /**
18
19
  * POST /wizard/sessions/:id/payment — registra el cobro de contado (idempotente por sesión). Regla
19
20
  * dura: `amountCents == cashPrice` de la reserva. `POST /payment/folio` genera el folio server-side
@@ -24,6 +25,7 @@ class RegisterPaymentRequest {
24
25
  exports.RegisterPaymentRequest = RegisterPaymentRequest;
25
26
  __decorate([
26
27
  (0, class_transformer_1.Expose)(),
28
+ (0, class_validator_1.ValidateIf)((o) => o.payments == null || o.method !== undefined),
27
29
  (0, class_validator_1.IsEnum)(SalePaymentMethodEnum_1.SalePaymentMethodEnum),
28
30
  __metadata("design:type", String)
29
31
  ], RegisterPaymentRequest.prototype, "method", void 0);
@@ -40,3 +42,9 @@ __decorate([
40
42
  (0, class_validator_1.ValidateNested)(),
41
43
  __metadata("design:type", SalePaymentDetail_1.SalePaymentDetail)
42
44
  ], RegisterPaymentRequest.prototype, "detail", void 0);
45
+ __decorate([
46
+ (0, class_transformer_1.Expose)(),
47
+ (0, class_validator_1.IsOptional)(),
48
+ (0, IsSalePaymentList_1.IsSalePaymentList)(),
49
+ __metadata("design:type", Array)
50
+ ], RegisterPaymentRequest.prototype, "payments", void 0);