@fiado/type-kit 3.602.0 → 3.604.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.
- package/_test_/unit/retailIntegration/retailMovementsContracts.test.ts +13 -0
- package/_test_/unit/retailIntegration/retailSaleContracts.test.ts +171 -10
- package/_test_/unit/retailIntegration/retailSystemEnums.test.ts +3 -1
- package/bin/retailIntegration/constants/retailIntegrationLimits.d.ts +16 -0
- package/bin/retailIntegration/constants/retailIntegrationLimits.js +17 -1
- package/bin/retailIntegration/dtos/CancelRetailSaleRequest.d.ts +7 -0
- package/bin/retailIntegration/dtos/CancelRetailSaleRequest.js +34 -1
- package/bin/retailIntegration/dtos/RetailOutboundResponses.d.ts +10 -1
- package/bin/retailIntegration/enums/RetailPaymentFrequencyEnum.d.ts +6 -0
- package/bin/retailIntegration/enums/RetailPaymentFrequencyEnum.js +10 -0
- package/bin/retailIntegration/enums/RetailPaymentMethodEnum.d.ts +3 -2
- package/bin/retailIntegration/enums/RetailPaymentMethodEnum.js +3 -2
- package/bin/retailIntegration/enums/index.d.ts +1 -0
- package/bin/retailIntegration/enums/index.js +1 -0
- package/bin/retailIntegration/index.d.ts +2 -0
- package/bin/retailIntegration/index.js +2 -0
- package/bin/retailIntegration/models/RetailSystemSale.d.ts +23 -2
- package/bin/retailIntegration/models/RetailSystemSale.js +89 -11
- package/bin/retailIntegration/validators/IsCalendarDate.d.ts +6 -0
- package/bin/retailIntegration/validators/IsCalendarDate.js +25 -0
- package/bin/retailIntegration/validators/IsIsoDateTimeWithZone.js +2 -6
- package/bin/retailIntegration/validators/calendarDate.d.ts +2 -0
- package/bin/retailIntegration/validators/calendarDate.js +8 -0
- package/bin/retailIntegration/validators/saleValidators.d.ts +26 -0
- package/bin/retailIntegration/validators/saleValidators.js +100 -0
- package/package.json +1 -1
- package/src/retailIntegration/constants/retailIntegrationLimits.ts +24 -0
- package/src/retailIntegration/dtos/CancelRetailSaleRequest.ts +14 -2
- package/src/retailIntegration/dtos/RetailOutboundResponses.ts +11 -1
- package/src/retailIntegration/enums/RetailPaymentFrequencyEnum.ts +6 -0
- package/src/retailIntegration/enums/RetailPaymentMethodEnum.ts +3 -2
- package/src/retailIntegration/enums/index.ts +1 -0
- package/src/retailIntegration/index.ts +2 -0
- package/src/retailIntegration/models/RetailSystemSale.ts +57 -10
- package/src/retailIntegration/validators/IsCalendarDate.ts +16 -0
- package/src/retailIntegration/validators/IsIsoDateTimeWithZone.ts +1 -6
- package/src/retailIntegration/validators/calendarDate.ts +5 -0
- package/src/retailIntegration/validators/saleValidators.ts +80 -0
|
@@ -4,9 +4,11 @@ import { validateSync } from 'class-validator';
|
|
|
4
4
|
import {
|
|
5
5
|
GetRetailInventoryRequest,
|
|
6
6
|
type GetRetailInventoryResponse,
|
|
7
|
+
type InvalidRetailInventoryMovement,
|
|
7
8
|
ListRetailInventoryMovementsRequest,
|
|
8
9
|
type ListRetailInventoryMovementsResponse,
|
|
9
10
|
ReconciliationEntityEnum,
|
|
11
|
+
RetailEventRejectCodeEnum,
|
|
10
12
|
ReconciliationPageMessage,
|
|
11
13
|
ReconciliationTriggerEnum,
|
|
12
14
|
} from '../../../src/retailIntegration';
|
|
@@ -61,6 +63,17 @@ describe('ListRetailInventoryMovementsRequest', () => {
|
|
|
61
63
|
meta: { count: 1, nextToken: null },
|
|
62
64
|
};
|
|
63
65
|
expect(response.meta.nextToken).toBeNull();
|
|
66
|
+
expect(response.invalidItems).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('la respuesta lista los movimientos inválidos con índice, eventId opcional y rutas', () => {
|
|
70
|
+
const invalid: InvalidRetailInventoryMovement[] = [
|
|
71
|
+
{ index: 3, eventId: 'evt-4', code: RetailEventRejectCodeEnum.INVALID_PAYLOAD, invalidPaths: ['payload.storeId'] },
|
|
72
|
+
{ index: 7, code: RetailEventRejectCodeEnum.INVALID_PAYLOAD, invalidPaths: ['eventId', 'occurredAt'] },
|
|
73
|
+
];
|
|
74
|
+
const response: ListRetailInventoryMovementsResponse = { items: [], meta: { count: 0, nextToken: null }, invalidItems: invalid };
|
|
75
|
+
expect(response.invalidItems?.map((i) => i.index)).toEqual([3, 7]);
|
|
76
|
+
expect(response.invalidItems?.[1].eventId).toBeUndefined();
|
|
64
77
|
});
|
|
65
78
|
});
|
|
66
79
|
|
|
@@ -23,6 +23,12 @@ async function errorsOf(cls: new () => object, plain: object): Promise<string[]>
|
|
|
23
23
|
return paths(await validate(plainToInstance(cls, plain)));
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
const cashPayment = { method: 'CASH', amount: '400.00', cardLast4: null, authCode: null, speiTrackingKey: null, senderClabe: null } as const;
|
|
27
|
+
const cardPayment = { method: 'CARD', amount: '200.00', cardLast4: '4242', authCode: 'A1B2C3', speiTrackingKey: null, senderClabe: null } as const;
|
|
28
|
+
const wirePayment = {
|
|
29
|
+
method: 'WIRE', amount: '200.00', cardLast4: null, authCode: null, speiTrackingKey: 'MBAN01002610160000123ABCD', senderClabe: '012180001234567897',
|
|
30
|
+
} as const;
|
|
31
|
+
|
|
26
32
|
// Literal tipado: prueba en compilación que la clase sigue aceptando objetos planos.
|
|
27
33
|
const sale: RetailSystemSale = {
|
|
28
34
|
saleId: '7f0c2a52-1b7e-4f0e-9d7a-2a1d3c4b5e6f',
|
|
@@ -33,30 +39,39 @@ const sale: RetailSystemSale = {
|
|
|
33
39
|
saleType: 'CREDIT',
|
|
34
40
|
financier: 'SUREKEEP',
|
|
35
41
|
creditId: 'CR-991',
|
|
42
|
+
credit: {
|
|
43
|
+
paymentFrequency: 'WEEKLY', installments: 16, installmentAmount: '260.67', downPayment: '600.00', totalToPay: '4170.72', firstPaymentDate: '2026-10-30',
|
|
44
|
+
},
|
|
36
45
|
currencyId: 'MXN',
|
|
37
46
|
customer: { customerId: 'C-1', names: 'Ana', paternalLastName: 'López', maternalLastName: null, phone: '7221234567' },
|
|
38
47
|
equipmentPrice: '5999.00',
|
|
39
48
|
amountPaid: '600.00',
|
|
40
|
-
|
|
49
|
+
payments: [cashPayment, cardPayment],
|
|
41
50
|
lines: [
|
|
42
51
|
{
|
|
43
52
|
lineId: 'L1', sku: 'IPH-15', qty: 1, basePrice: '5999.00', discount: '0.00', paymentType: 'CREDIT',
|
|
44
|
-
financedAmount: '5399.00', role: null, components: [{ sku: 'IPH-15', qty: 1, identifiers: ['356789012345678'] }],
|
|
53
|
+
financedAmount: '5399.00', role: null, promotionId: null, components: [{ sku: 'IPH-15', qty: 1, identifiers: ['356789012345678'] }],
|
|
45
54
|
},
|
|
46
55
|
{
|
|
47
56
|
lineId: 'L2', sku: 'FUNDA-1', qty: 1, basePrice: '0.00', discount: '0.00', paymentType: 'CASH',
|
|
48
|
-
financedAmount: '0.00', role: 'GIFT', components: [],
|
|
57
|
+
financedAmount: '0.00', role: 'GIFT', promotionId: 'PRM-0012', components: [],
|
|
49
58
|
},
|
|
50
59
|
],
|
|
51
60
|
};
|
|
52
61
|
|
|
62
|
+
const cashSale = { ...sale, saleType: 'CASH', financier: 'NONE', creditId: null, credit: null };
|
|
63
|
+
|
|
53
64
|
describe('RetailSystemSale (body de privateRegisterRetailSale)', () => {
|
|
54
|
-
it('acepta una venta completa', async () => {
|
|
65
|
+
it('acepta una venta a crédito SureKeep completa', async () => {
|
|
55
66
|
expect(await errorsOf(RetailSystemSale, sale)).toEqual([]);
|
|
56
67
|
});
|
|
57
68
|
|
|
58
|
-
it('acepta
|
|
59
|
-
expect(await errorsOf(RetailSystemSale,
|
|
69
|
+
it('acepta una venta de contado con creditId y credit en null', async () => {
|
|
70
|
+
expect(await errorsOf(RetailSystemSale, cashSale)).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
it('acepta una referida sin creditId ni credit', async () => {
|
|
74
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, financier: 'PAYJOY', creditId: null, credit: null })).toEqual([]);
|
|
60
75
|
});
|
|
61
76
|
|
|
62
77
|
it.each([
|
|
@@ -67,14 +82,19 @@ describe('RetailSystemSale (body de privateRegisterRetailSale)', () => {
|
|
|
67
82
|
['financier', { financier: 'OTRO' }],
|
|
68
83
|
['currencyId', { currencyId: 'EUR' }],
|
|
69
84
|
['equipmentPrice', { equipmentPrice: '5999' }],
|
|
70
|
-
['
|
|
71
|
-
['paymentMethod', { paymentMethod: 'BITCOIN' }],
|
|
85
|
+
['equipmentPrice', { equipmentPrice: '1234567890123.00' }],
|
|
72
86
|
['creditId', { creditId: undefined }],
|
|
87
|
+
['credit', { credit: undefined }],
|
|
88
|
+
['payments', { payments: undefined }],
|
|
73
89
|
['lines', { lines: [] }],
|
|
74
90
|
])('rechaza %s inválido', async (field, patch) => {
|
|
75
91
|
expect(await errorsOf(RetailSystemSale, { ...sale, ...patch })).toEqual([field]);
|
|
76
92
|
});
|
|
77
93
|
|
|
94
|
+
it.each(['722123456', '72212345678', '+527221234567', '722-123-4567'])('rechaza customer.phone %s', async (phone) => {
|
|
95
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, customer: { ...sale.customer, phone } })).toEqual(['customer.phone']);
|
|
96
|
+
});
|
|
97
|
+
|
|
78
98
|
it('valida el cliente anidado', async () => {
|
|
79
99
|
expect(await errorsOf(RetailSystemSale, { ...sale, customer: { ...sale.customer, names: '' } })).toEqual(['customer.names']);
|
|
80
100
|
});
|
|
@@ -90,11 +110,139 @@ describe('RetailSystemSale (body de privateRegisterRetailSale)', () => {
|
|
|
90
110
|
});
|
|
91
111
|
});
|
|
92
112
|
|
|
113
|
+
describe('RetailSystemSale.credit', () => {
|
|
114
|
+
it('SUREKEEP exige credit y creditId', async () => {
|
|
115
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, credit: null })).toEqual(['financier']);
|
|
116
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, creditId: null })).toEqual(['financier']);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
it('contado exige credit y creditId en null', async () => {
|
|
120
|
+
expect(await errorsOf(RetailSystemSale, { ...cashSale, credit: sale.credit })).toEqual(['financier']);
|
|
121
|
+
expect(await errorsOf(RetailSystemSale, { ...cashSale, creditId: 'CR-1' })).toEqual(['financier']);
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
it.each([
|
|
125
|
+
['paymentFrequency', { paymentFrequency: 'DAILY' }],
|
|
126
|
+
['installments', { installments: 0 }],
|
|
127
|
+
['installments', { installments: 1.5 }],
|
|
128
|
+
['installmentAmount', { installmentAmount: '260.6' }],
|
|
129
|
+
['downPayment', { downPayment: undefined }],
|
|
130
|
+
['totalToPay', { totalToPay: '1234567890123.00' }],
|
|
131
|
+
['firstPaymentDate', { firstPaymentDate: '2026-02-30' }],
|
|
132
|
+
['firstPaymentDate', { firstPaymentDate: '2026-10-30T00:00:00Z' }],
|
|
133
|
+
])('rechaza credit.%s inválido', async (field, patch) => {
|
|
134
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, credit: { ...sale.credit, ...patch } })).toEqual([`credit.${field}`]);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
describe('RetailSystemSale.payments', () => {
|
|
139
|
+
it('acepta [] cuando amountPaid es 0.00', async () => {
|
|
140
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, amountPaid: '0.00', payments: [] })).toEqual([]);
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
it('acepta WIRE con y sin senderClabe', async () => {
|
|
144
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, wirePayment] })).toEqual([]);
|
|
145
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, senderClabe: null }] })).toEqual([]);
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('acepta speiTrackingKey de 1 a 30 alfanuméricos', async () => {
|
|
149
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, speiTrackingKey: '7' }] })).toEqual([]);
|
|
150
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment, { ...wirePayment, speiTrackingKey: 'A'.repeat(30) }] })).toEqual([]);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it('acepta FIADO sin detalle', async () => {
|
|
154
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [{ ...cashPayment, method: 'FIADO', amount: '600.00' }] })).toEqual([]);
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('rechaza que la suma no cuadre con amountPaid', async () => {
|
|
158
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [cashPayment] })).toEqual(['payments']);
|
|
159
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments: [] })).toEqual(['payments']);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
it.each([
|
|
163
|
+
['method', { method: 'SPEI' }],
|
|
164
|
+
['amount', { amount: '0.00' }],
|
|
165
|
+
['amount', { amount: '400' }],
|
|
166
|
+
['cardLast4', { cardLast4: '4242' }],
|
|
167
|
+
['cardLast4', { cardLast4: undefined }],
|
|
168
|
+
['authCode', { authCode: 'X' }],
|
|
169
|
+
['speiTrackingKey', { speiTrackingKey: undefined }],
|
|
170
|
+
['senderClabe', { senderClabe: undefined }],
|
|
171
|
+
])('rechaza %s fuera de regla en CASH', async (field, patch) => {
|
|
172
|
+
const payments = [{ ...cashPayment, ...patch }, cardPayment];
|
|
173
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments })).toEqual([`payments.0.${field}`]);
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
it.each([
|
|
177
|
+
['cardLast4', { cardLast4: null }],
|
|
178
|
+
['cardLast4', { cardLast4: '424' }],
|
|
179
|
+
['authCode', { authCode: null }],
|
|
180
|
+
['authCode', { authCode: ' ' }],
|
|
181
|
+
['authCode', { authCode: 'A'.repeat(65) }],
|
|
182
|
+
['speiTrackingKey', { speiTrackingKey: '123456789012345678' }],
|
|
183
|
+
])('rechaza %s fuera de regla en CARD', async (field, patch) => {
|
|
184
|
+
const payments = [cashPayment, { ...cardPayment, ...patch }];
|
|
185
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments })).toEqual([`payments.1.${field}`]);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
it.each([
|
|
189
|
+
['speiTrackingKey', { speiTrackingKey: null }],
|
|
190
|
+
['speiTrackingKey', { speiTrackingKey: '' }],
|
|
191
|
+
['speiTrackingKey', { speiTrackingKey: 'A'.repeat(31) }],
|
|
192
|
+
['speiTrackingKey', { speiTrackingKey: 'MBAN-0100-2610' }],
|
|
193
|
+
['speiTrackingKey', { speiTrackingKey: 'MBAN 01002610' }],
|
|
194
|
+
['senderClabe', { senderClabe: '01218000123456789A' }],
|
|
195
|
+
['senderClabe', { senderClabe: '0121' }],
|
|
196
|
+
['cardLast4', { cardLast4: '4242' }],
|
|
197
|
+
])('rechaza %s fuera de regla en WIRE', async (field, patch) => {
|
|
198
|
+
const payments = [cashPayment, { ...wirePayment, ...patch }];
|
|
199
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, payments })).toEqual([`payments.1.${field}`]);
|
|
200
|
+
});
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
describe('RetailSystemSale.lines[].promotionId', () => {
|
|
204
|
+
it('la llave es obligatoria aunque sea null', async () => {
|
|
205
|
+
const lines = [{ ...sale.lines[0], promotionId: undefined }, sale.lines[1]];
|
|
206
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, lines })).toEqual(['lines.0.promotionId']);
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
it('acepta una promoción en una línea con descuento', async () => {
|
|
210
|
+
const lines = [{ ...sale.lines[0], discount: '500.00', promotionId: 'PRM-1' }, sale.lines[1]];
|
|
211
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, lines })).toEqual([]);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it.each([
|
|
215
|
+
['vacío', ' '],
|
|
216
|
+
['más de 64', 'P'.repeat(65)],
|
|
217
|
+
])('rechaza promotionId %s', async (_case, promotionId) => {
|
|
218
|
+
const lines = [{ ...sale.lines[0], promotionId }, sale.lines[1]];
|
|
219
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, lines })).toEqual(['lines.0.promotionId']);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('un regalo exige promotionId', async () => {
|
|
223
|
+
const lines = [sale.lines[0], { ...sale.lines[1], promotionId: null }];
|
|
224
|
+
expect(await errorsOf(RetailSystemSale, { ...sale, lines })).toEqual(['lines.1.promotionId']);
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
|
|
93
228
|
describe('CancelRetailSaleRequest', () => {
|
|
94
|
-
const cancel = {
|
|
229
|
+
const cancel: CancelRetailSaleRequest = {
|
|
230
|
+
saleId: sale.saleId,
|
|
231
|
+
cancelledAt: '2026-10-25T19:00:00-06:00',
|
|
232
|
+
reason: 'Cliente desistió',
|
|
233
|
+
damaged: [
|
|
234
|
+
{ sku: 'IPH-15', identifier: '356789012345678', qty: 1 },
|
|
235
|
+
{ sku: 'FUNDA-1', identifier: null, qty: 3 },
|
|
236
|
+
],
|
|
237
|
+
};
|
|
95
238
|
|
|
96
|
-
it('acepta la cancelación', async () => {
|
|
239
|
+
it('acepta la cancelación con y sin dañados', async () => {
|
|
97
240
|
expect(await errorsOf(CancelRetailSaleRequest, cancel)).toEqual([]);
|
|
241
|
+
expect(await errorsOf(CancelRetailSaleRequest, { ...cancel, damaged: [] })).toEqual([]);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('acepta reason de 200 caracteres', async () => {
|
|
245
|
+
expect(await errorsOf(CancelRetailSaleRequest, { ...cancel, reason: 'r'.repeat(200) })).toEqual([]);
|
|
98
246
|
});
|
|
99
247
|
|
|
100
248
|
it.each([
|
|
@@ -102,10 +250,23 @@ describe('CancelRetailSaleRequest', () => {
|
|
|
102
250
|
['cancelledAt', { cancelledAt: '2026-02-31T10:00:00Z' }],
|
|
103
251
|
['reason', { reason: '' }],
|
|
104
252
|
['reason', { reason: undefined }],
|
|
253
|
+
['reason', { reason: 'r'.repeat(201) }],
|
|
254
|
+
['damaged', { damaged: undefined }],
|
|
105
255
|
])('rechaza %s inválido', async (field, patch) => {
|
|
106
256
|
expect(await errorsOf(CancelRetailSaleRequest, { ...cancel, ...patch })).toEqual([field]);
|
|
107
257
|
});
|
|
108
258
|
|
|
259
|
+
it.each([
|
|
260
|
+
['sku', { sku: 'a/b' }],
|
|
261
|
+
['identifier', { identifier: undefined }],
|
|
262
|
+
['identifier', { identifier: ' ' }],
|
|
263
|
+
['qty', { qty: 0 }],
|
|
264
|
+
['qty', { qty: 2 }],
|
|
265
|
+
])('rechaza damaged[].%s inválido', async (field, patch) => {
|
|
266
|
+
const damaged = [{ ...cancel.damaged[0], ...patch }];
|
|
267
|
+
expect(await errorsOf(CancelRetailSaleRequest, { ...cancel, damaged })).toEqual([`damaged.0.${field}`]);
|
|
268
|
+
});
|
|
269
|
+
|
|
109
270
|
it('la respuesta trae el saleId', () => {
|
|
110
271
|
const response: CancelRetailSaleResponse = { saleId: cancel.saleId };
|
|
111
272
|
expect(response.saleId).toBe(cancel.saleId);
|
|
@@ -5,6 +5,7 @@ import {
|
|
|
5
5
|
RetailFinancierEnum,
|
|
6
6
|
RetailLinePaymentTypeEnum,
|
|
7
7
|
RetailMdmProviderEnum,
|
|
8
|
+
RetailPaymentFrequencyEnum,
|
|
8
9
|
RetailPaymentMethodEnum,
|
|
9
10
|
RetailPieceReasonEnum,
|
|
10
11
|
RetailPieceStatusEnum,
|
|
@@ -35,7 +36,8 @@ describe('enums de los objetos del retailer', () => {
|
|
|
35
36
|
[RetailUserAccessStatusEnum, ['ACTIVE', 'SUSPENDED', 'INACTIVE']],
|
|
36
37
|
[RetailSaleTypeEnum, ['CASH', 'CREDIT', 'EXTERNAL_REFERRAL']],
|
|
37
38
|
[RetailSaleFinancierEnum, ['NONE', 'SUREKEEP', 'PAYJOY', 'CREDIYA', 'LESPAGO', 'INNOVA']],
|
|
38
|
-
[RetailPaymentMethodEnum, ['CASH', 'CARD', '
|
|
39
|
+
[RetailPaymentMethodEnum, ['CASH', 'CARD', 'WIRE', 'FIADO']],
|
|
40
|
+
[RetailPaymentFrequencyEnum, ['WEEKLY', 'BIWEEKLY', 'MONTHLY']],
|
|
39
41
|
[RetailLinePaymentTypeEnum, ['CASH', 'CREDIT']],
|
|
40
42
|
])('%p trae exactamente los valores del contrato', (enumObject, values) => {
|
|
41
43
|
expect(Object.values(enumObject)).toEqual(values);
|
|
@@ -45,3 +45,19 @@ export declare const RETAIL_LINE_INDEX_MIN = 0;
|
|
|
45
45
|
export declare const RETAIL_LINE_INDEX_MAX = 999;
|
|
46
46
|
/** Rango máximo de `GET /inventory/movements`: 7 días en milisegundos. */
|
|
47
47
|
export declare const RETAIL_MOVEMENTS_RANGE_MAX_MS: number;
|
|
48
|
+
/** Decimal de la venta y la cancelación: hasta 12 enteros y dos decimales, nunca negativo. */
|
|
49
|
+
export declare const RETAIL_SALE_DECIMAL_PATTERN: RegExp;
|
|
50
|
+
/** Decimal de la venta mayor que cero, como `payments[].amount`. */
|
|
51
|
+
export declare const RETAIL_SALE_POSITIVE_DECIMAL_PATTERN: RegExp;
|
|
52
|
+
/** Texto libre corto de la venta, como `authCode` y `promotionId`: no vacío y de a lo más 64. */
|
|
53
|
+
export declare const RETAIL_SALE_ID_TEXT_PATTERN: RegExp;
|
|
54
|
+
/** Últimos 4 dígitos de la tarjeta en un pago `CARD`. */
|
|
55
|
+
export declare const RETAIL_CARD_LAST4_PATTERN: RegExp;
|
|
56
|
+
/** Clave de rastreo SPEI de un pago `WIRE`: alfanumérica de 1 a 30, como la define Banxico. */
|
|
57
|
+
export declare const RETAIL_SPEI_TRACKING_KEY_PATTERN: RegExp;
|
|
58
|
+
/** CLABE de la cuenta que envió un pago `WIRE`: 18 dígitos. */
|
|
59
|
+
export declare const RETAIL_CLABE_PATTERN: RegExp;
|
|
60
|
+
/** Tope del motivo de cancelación de una venta. */
|
|
61
|
+
export declare const RETAIL_CANCEL_REASON_MAX_LENGTH = 200;
|
|
62
|
+
/** Teléfono del cliente en la venta: 10 dígitos. */
|
|
63
|
+
export declare const RETAIL_CUSTOMER_PHONE_PATTERN: RegExp;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.RETAIL_MOVEMENTS_RANGE_MAX_MS = exports.RETAIL_LINE_INDEX_MAX = exports.RETAIL_LINE_INDEX_MIN = exports.RETAIL_ERROR_CODE_MAX_LENGTH = exports.RETAIL_EXTERNAL_ID_PATTERN = exports.RETAIL_NEXT_TOKEN_MAX_LENGTH = exports.RETAIL_EVENT_DECIMAL_PATTERN = exports.RETAIL_ELIGIBLE_FINANCIERS_MAX = exports.RETAIL_EVENT_STOCK_MAX = exports.RETAIL_LONG_TEXT_MAX_LENGTH = exports.RETAIL_SHORT_TEXT_MAX_LENGTH = exports.RETAIL_ID_TEXT_MAX_LENGTH = exports.RETAIL_EVENT_PIECES_MAX = exports.RETAIL_LIVE_CHECK_IDENTIFIERS_MAX = exports.RETAIL_DECIMAL_PATTERN = exports.RETAIL_SKU_PATTERN = exports.RETAIL_ZIP_CODE_MAX_LENGTH = exports.RETAIL_ZIP_CODE_MIN_LENGTH = exports.RETAIL_STORE_CODE_MAX_LENGTH = exports.RETAIL_ZONE_ID_PATTERN = exports.RETAIL_ENTITY_ID_PATTERN = exports.RETAIL_TENANT_ID_PATTERN = exports.RETAIL_LIST_LIMIT_MAX = exports.RETAIL_LIST_LIMIT_MIN = exports.RETAIL_EVENT_ID_MAX_LENGTH = void 0;
|
|
3
|
+
exports.RETAIL_CUSTOMER_PHONE_PATTERN = exports.RETAIL_CANCEL_REASON_MAX_LENGTH = exports.RETAIL_CLABE_PATTERN = exports.RETAIL_SPEI_TRACKING_KEY_PATTERN = exports.RETAIL_CARD_LAST4_PATTERN = exports.RETAIL_SALE_ID_TEXT_PATTERN = exports.RETAIL_SALE_POSITIVE_DECIMAL_PATTERN = exports.RETAIL_SALE_DECIMAL_PATTERN = exports.RETAIL_MOVEMENTS_RANGE_MAX_MS = exports.RETAIL_LINE_INDEX_MAX = exports.RETAIL_LINE_INDEX_MIN = exports.RETAIL_ERROR_CODE_MAX_LENGTH = exports.RETAIL_EXTERNAL_ID_PATTERN = exports.RETAIL_NEXT_TOKEN_MAX_LENGTH = exports.RETAIL_EVENT_DECIMAL_PATTERN = exports.RETAIL_ELIGIBLE_FINANCIERS_MAX = exports.RETAIL_EVENT_STOCK_MAX = exports.RETAIL_LONG_TEXT_MAX_LENGTH = exports.RETAIL_SHORT_TEXT_MAX_LENGTH = exports.RETAIL_ID_TEXT_MAX_LENGTH = exports.RETAIL_EVENT_PIECES_MAX = exports.RETAIL_LIVE_CHECK_IDENTIFIERS_MAX = exports.RETAIL_DECIMAL_PATTERN = exports.RETAIL_SKU_PATTERN = exports.RETAIL_ZIP_CODE_MAX_LENGTH = exports.RETAIL_ZIP_CODE_MIN_LENGTH = exports.RETAIL_STORE_CODE_MAX_LENGTH = exports.RETAIL_ZONE_ID_PATTERN = exports.RETAIL_ENTITY_ID_PATTERN = exports.RETAIL_TENANT_ID_PATTERN = exports.RETAIL_LIST_LIMIT_MAX = exports.RETAIL_LIST_LIMIT_MIN = exports.RETAIL_EVENT_ID_MAX_LENGTH = void 0;
|
|
4
4
|
/** Tope de `eventId`: el límite seguro de una llave de DynamoDB. */
|
|
5
5
|
exports.RETAIL_EVENT_ID_MAX_LENGTH = 1024;
|
|
6
6
|
/** Rango de `limit` en las listas paginadas del retailer. */
|
|
@@ -48,3 +48,19 @@ exports.RETAIL_LINE_INDEX_MIN = 0;
|
|
|
48
48
|
exports.RETAIL_LINE_INDEX_MAX = 999;
|
|
49
49
|
/** Rango máximo de `GET /inventory/movements`: 7 días en milisegundos. */
|
|
50
50
|
exports.RETAIL_MOVEMENTS_RANGE_MAX_MS = 7 * 24 * 60 * 60 * 1000;
|
|
51
|
+
/** Decimal de la venta y la cancelación: hasta 12 enteros y dos decimales, nunca negativo. */
|
|
52
|
+
exports.RETAIL_SALE_DECIMAL_PATTERN = /^\d{1,12}\.\d{2}$/;
|
|
53
|
+
/** Decimal de la venta mayor que cero, como `payments[].amount`. */
|
|
54
|
+
exports.RETAIL_SALE_POSITIVE_DECIMAL_PATTERN = /^(?=.*[1-9])\d{1,12}\.\d{2}$/;
|
|
55
|
+
/** Texto libre corto de la venta, como `authCode` y `promotionId`: no vacío y de a lo más 64. */
|
|
56
|
+
exports.RETAIL_SALE_ID_TEXT_PATTERN = /^(?=.*\S).{1,64}$/;
|
|
57
|
+
/** Últimos 4 dígitos de la tarjeta en un pago `CARD`. */
|
|
58
|
+
exports.RETAIL_CARD_LAST4_PATTERN = /^\d{4}$/;
|
|
59
|
+
/** Clave de rastreo SPEI de un pago `WIRE`: alfanumérica de 1 a 30, como la define Banxico. */
|
|
60
|
+
exports.RETAIL_SPEI_TRACKING_KEY_PATTERN = /^[A-Za-z0-9]{1,30}$/;
|
|
61
|
+
/** CLABE de la cuenta que envió un pago `WIRE`: 18 dígitos. */
|
|
62
|
+
exports.RETAIL_CLABE_PATTERN = /^\d{18}$/;
|
|
63
|
+
/** Tope del motivo de cancelación de una venta. */
|
|
64
|
+
exports.RETAIL_CANCEL_REASON_MAX_LENGTH = 200;
|
|
65
|
+
/** Teléfono del cliente en la venta: 10 dígitos. */
|
|
66
|
+
exports.RETAIL_CUSTOMER_PHONE_PATTERN = /^\d{10}$/;
|
|
@@ -1,6 +1,13 @@
|
|
|
1
|
+
/** Lo que regresa dañado al cancelar; `identifier` en `null` si el producto es `QUANTITY`. */
|
|
2
|
+
export declare class RetailSystemDamagedItem {
|
|
3
|
+
sku: string;
|
|
4
|
+
identifier: string | null;
|
|
5
|
+
qty: number;
|
|
6
|
+
}
|
|
1
7
|
/** Cuerpo de `POST /private/retail/sales/cancel`; el `saleId` es el mismo del registro. */
|
|
2
8
|
export declare class CancelRetailSaleRequest {
|
|
3
9
|
saleId: string;
|
|
4
10
|
cancelledAt: string;
|
|
5
11
|
reason: string;
|
|
12
|
+
damaged: RetailSystemDamagedItem[];
|
|
6
13
|
}
|
|
@@ -9,9 +9,35 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.CancelRetailSaleRequest = void 0;
|
|
12
|
+
exports.CancelRetailSaleRequest = exports.RetailSystemDamagedItem = void 0;
|
|
13
|
+
const class_transformer_1 = require("class-transformer");
|
|
13
14
|
const class_validator_1 = require("class-validator");
|
|
15
|
+
const retailIntegrationLimits_1 = require("../constants/retailIntegrationLimits");
|
|
14
16
|
const IsIsoDateTimeWithZone_1 = require("../validators/IsIsoDateTimeWithZone");
|
|
17
|
+
const payloadValidators_1 = require("../validators/payloadValidators");
|
|
18
|
+
const saleValidators_1 = require("../validators/saleValidators");
|
|
19
|
+
/** Lo que regresa dañado al cancelar; `identifier` en `null` si el producto es `QUANTITY`. */
|
|
20
|
+
class RetailSystemDamagedItem {
|
|
21
|
+
}
|
|
22
|
+
exports.RetailSystemDamagedItem = RetailSystemDamagedItem;
|
|
23
|
+
__decorate([
|
|
24
|
+
(0, class_validator_1.IsString)(),
|
|
25
|
+
(0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_SKU_PATTERN),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], RetailSystemDamagedItem.prototype, "sku", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
30
|
+
(0, class_validator_1.IsString)(),
|
|
31
|
+
(0, class_validator_1.Matches)(/\S/),
|
|
32
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
33
|
+
__metadata("design:type", String)
|
|
34
|
+
], RetailSystemDamagedItem.prototype, "identifier", void 0);
|
|
35
|
+
__decorate([
|
|
36
|
+
(0, class_validator_1.IsInt)(),
|
|
37
|
+
(0, class_validator_1.Min)(1),
|
|
38
|
+
(0, class_validator_1.Validate)(saleValidators_1.QtyMatchesIdentifier),
|
|
39
|
+
__metadata("design:type", Number)
|
|
40
|
+
], RetailSystemDamagedItem.prototype, "qty", void 0);
|
|
15
41
|
/** Cuerpo de `POST /private/retail/sales/cancel`; el `saleId` es el mismo del registro. */
|
|
16
42
|
class CancelRetailSaleRequest {
|
|
17
43
|
}
|
|
@@ -28,5 +54,12 @@ __decorate([
|
|
|
28
54
|
__decorate([
|
|
29
55
|
(0, class_validator_1.IsString)(),
|
|
30
56
|
(0, class_validator_1.Matches)(/\S/),
|
|
57
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_CANCEL_REASON_MAX_LENGTH),
|
|
31
58
|
__metadata("design:type", String)
|
|
32
59
|
], CancelRetailSaleRequest.prototype, "reason", void 0);
|
|
60
|
+
__decorate([
|
|
61
|
+
(0, class_validator_1.IsArray)(),
|
|
62
|
+
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
63
|
+
(0, class_transformer_1.Type)(() => RetailSystemDamagedItem),
|
|
64
|
+
__metadata("design:type", Array)
|
|
65
|
+
], CancelRetailSaleRequest.prototype, "damaged", void 0);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { RetailEventRejectCodeEnum } from '../enums';
|
|
1
2
|
import { RetailInventoryMeta, RetailPageMeta } from '../models/RetailPageMeta';
|
|
2
3
|
import { RetailSystemInventoryMovement, RetailSystemPiece, RetailSystemStock } from '../models/RetailSystemInventory';
|
|
3
4
|
import { RetailSystemProduct } from '../models/RetailSystemProduct';
|
|
@@ -19,10 +20,18 @@ export interface GetRetailInventoryResponse {
|
|
|
19
20
|
stock: RetailSystemStock[];
|
|
20
21
|
meta: RetailInventoryMeta;
|
|
21
22
|
}
|
|
22
|
-
/**
|
|
23
|
+
/** Movimiento de la página que no cumple el sobre del webhook; catalog lo salta y lo cuenta. */
|
|
24
|
+
export interface InvalidRetailInventoryMovement {
|
|
25
|
+
index: number;
|
|
26
|
+
eventId?: string;
|
|
27
|
+
code: RetailEventRejectCodeEnum.INVALID_PAYLOAD;
|
|
28
|
+
invalidPaths: string[];
|
|
29
|
+
}
|
|
30
|
+
/** `data` de `GET /private/retail/inventory/movements`; `invalidItems` lista los movimientos descartados. */
|
|
23
31
|
export interface ListRetailInventoryMovementsResponse {
|
|
24
32
|
items: RetailSystemInventoryMovement[];
|
|
25
33
|
meta: RetailPageMeta;
|
|
34
|
+
invalidItems?: InvalidRetailInventoryMovement[];
|
|
26
35
|
}
|
|
27
36
|
/** `data` de `GET /private/retail/users`. */
|
|
28
37
|
export interface ListRetailUsersResponse {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RetailPaymentFrequencyEnum = void 0;
|
|
4
|
+
/** Cada cuánto paga el cliente en `credit.paymentFrequency`. */
|
|
5
|
+
var RetailPaymentFrequencyEnum;
|
|
6
|
+
(function (RetailPaymentFrequencyEnum) {
|
|
7
|
+
RetailPaymentFrequencyEnum["WEEKLY"] = "WEEKLY";
|
|
8
|
+
RetailPaymentFrequencyEnum["BIWEEKLY"] = "BIWEEKLY";
|
|
9
|
+
RetailPaymentFrequencyEnum["MONTHLY"] = "MONTHLY";
|
|
10
|
+
})(RetailPaymentFrequencyEnum || (exports.RetailPaymentFrequencyEnum = RetailPaymentFrequencyEnum = {}));
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.RetailPaymentMethodEnum = void 0;
|
|
4
|
-
/**
|
|
4
|
+
/** Medio de pago de `payments[].method` en la venta. */
|
|
5
5
|
var RetailPaymentMethodEnum;
|
|
6
6
|
(function (RetailPaymentMethodEnum) {
|
|
7
7
|
RetailPaymentMethodEnum["CASH"] = "CASH";
|
|
8
8
|
RetailPaymentMethodEnum["CARD"] = "CARD";
|
|
9
|
-
RetailPaymentMethodEnum["
|
|
9
|
+
RetailPaymentMethodEnum["WIRE"] = "WIRE";
|
|
10
|
+
RetailPaymentMethodEnum["FIADO"] = "FIADO";
|
|
10
11
|
})(RetailPaymentMethodEnum || (exports.RetailPaymentMethodEnum = RetailPaymentMethodEnum = {}));
|
|
@@ -18,6 +18,7 @@ export * from './RetailUserAccessStatusEnum';
|
|
|
18
18
|
export * from './RetailSaleTypeEnum';
|
|
19
19
|
export * from './RetailSaleFinancierEnum';
|
|
20
20
|
export * from './RetailPaymentMethodEnum';
|
|
21
|
+
export * from './RetailPaymentFrequencyEnum';
|
|
21
22
|
export * from './RetailLinePaymentTypeEnum';
|
|
22
23
|
export * from './RetailSaleLineRoleEnum';
|
|
23
24
|
export * from './RetailEventRejectCodeEnum';
|
|
@@ -34,6 +34,7 @@ __exportStar(require("./RetailUserAccessStatusEnum"), exports);
|
|
|
34
34
|
__exportStar(require("./RetailSaleTypeEnum"), exports);
|
|
35
35
|
__exportStar(require("./RetailSaleFinancierEnum"), exports);
|
|
36
36
|
__exportStar(require("./RetailPaymentMethodEnum"), exports);
|
|
37
|
+
__exportStar(require("./RetailPaymentFrequencyEnum"), exports);
|
|
37
38
|
__exportStar(require("./RetailLinePaymentTypeEnum"), exports);
|
|
38
39
|
__exportStar(require("./RetailSaleLineRoleEnum"), exports);
|
|
39
40
|
__exportStar(require("./RetailEventRejectCodeEnum"), exports);
|
|
@@ -3,6 +3,8 @@ export * from './constants/retailIntegrationErrorHttpStatus';
|
|
|
3
3
|
export * from './enums';
|
|
4
4
|
export * from './validators/IsIsoDateTimeWithZone';
|
|
5
5
|
export * from './validators/IsMovementsRangeValid';
|
|
6
|
+
export * from './validators/IsCalendarDate';
|
|
7
|
+
export * from './validators/saleValidators';
|
|
6
8
|
export * from './dtos/RetailEventEnvelope';
|
|
7
9
|
export * from './dtos/ReceiveStoreEventRequest';
|
|
8
10
|
export * from './dtos/ReceiveRetailUserEventRequest';
|
|
@@ -19,6 +19,8 @@ __exportStar(require("./constants/retailIntegrationErrorHttpStatus"), exports);
|
|
|
19
19
|
__exportStar(require("./enums"), exports);
|
|
20
20
|
__exportStar(require("./validators/IsIsoDateTimeWithZone"), exports);
|
|
21
21
|
__exportStar(require("./validators/IsMovementsRangeValid"), exports);
|
|
22
|
+
__exportStar(require("./validators/IsCalendarDate"), exports);
|
|
23
|
+
__exportStar(require("./validators/saleValidators"), exports);
|
|
22
24
|
__exportStar(require("./dtos/RetailEventEnvelope"), exports);
|
|
23
25
|
__exportStar(require("./dtos/ReceiveStoreEventRequest"), exports);
|
|
24
26
|
__exportStar(require("./dtos/ReceiveRetailUserEventRequest"), exports);
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { RetailLinePaymentTypeEnum, RetailPaymentMethodEnum, RetailSaleFinancierEnum, RetailSaleLineRoleEnum, RetailSaleTypeEnum } from '../enums';
|
|
1
|
+
import { RetailLinePaymentTypeEnum, RetailPaymentFrequencyEnum, RetailPaymentMethodEnum, RetailSaleFinancierEnum, RetailSaleLineRoleEnum, RetailSaleTypeEnum } from '../enums';
|
|
2
2
|
import { RetailCurrency } from './RetailSystemProduct';
|
|
3
3
|
export type RetailSaleType = `${RetailSaleTypeEnum}`;
|
|
4
4
|
export type RetailSaleFinancier = `${RetailSaleFinancierEnum}`;
|
|
5
5
|
export type RetailPaymentMethod = `${RetailPaymentMethodEnum}`;
|
|
6
|
+
export type RetailPaymentFrequency = `${RetailPaymentFrequencyEnum}`;
|
|
6
7
|
export type RetailLinePaymentType = `${RetailLinePaymentTypeEnum}`;
|
|
7
8
|
export type RetailSaleLineRole = `${RetailSaleLineRoleEnum}`;
|
|
8
9
|
export declare class RetailSystemSaleCustomer {
|
|
@@ -12,6 +13,24 @@ export declare class RetailSystemSaleCustomer {
|
|
|
12
13
|
maternalLastName: string | null;
|
|
13
14
|
phone: string;
|
|
14
15
|
}
|
|
16
|
+
/** Condiciones del crédito; todas obligatorias cuando `credit` no es `null`. */
|
|
17
|
+
export declare class RetailSystemSaleCredit {
|
|
18
|
+
paymentFrequency: RetailPaymentFrequency;
|
|
19
|
+
installments: number;
|
|
20
|
+
installmentAmount: string;
|
|
21
|
+
downPayment: string;
|
|
22
|
+
totalToPay: string;
|
|
23
|
+
firstPaymentDate: string;
|
|
24
|
+
}
|
|
25
|
+
/** Un medio de pago de `amountPaid`; las seis llaves van siempre, en `null` las que no aplican. */
|
|
26
|
+
export declare class RetailSystemSalePayment {
|
|
27
|
+
method: RetailPaymentMethod;
|
|
28
|
+
amount: string;
|
|
29
|
+
cardLast4: string | null;
|
|
30
|
+
authCode: string | null;
|
|
31
|
+
speiTrackingKey: string | null;
|
|
32
|
+
senderClabe: string | null;
|
|
33
|
+
}
|
|
15
34
|
export declare class RetailSystemSaleComponent {
|
|
16
35
|
sku: string;
|
|
17
36
|
qty: number;
|
|
@@ -26,6 +45,7 @@ export declare class RetailSystemSaleLine {
|
|
|
26
45
|
paymentType: RetailLinePaymentType;
|
|
27
46
|
financedAmount: string;
|
|
28
47
|
role: RetailSaleLineRole | null;
|
|
48
|
+
promotionId: string | null;
|
|
29
49
|
components: RetailSystemSaleComponent[];
|
|
30
50
|
}
|
|
31
51
|
/** Cuerpo de `POST /sales` de la doc y de `privateRegisterRetailSale`; `saleId` es la llave de idempotencia. */
|
|
@@ -38,10 +58,11 @@ export declare class RetailSystemSale {
|
|
|
38
58
|
saleType: RetailSaleType;
|
|
39
59
|
financier: RetailSaleFinancier;
|
|
40
60
|
creditId: string | null;
|
|
61
|
+
credit: RetailSystemSaleCredit | null;
|
|
41
62
|
currencyId: RetailCurrency;
|
|
42
63
|
customer: RetailSystemSaleCustomer;
|
|
43
64
|
equipmentPrice: string;
|
|
44
65
|
amountPaid: string;
|
|
45
|
-
|
|
66
|
+
payments: RetailSystemSalePayment[];
|
|
46
67
|
lines: RetailSystemSaleLine[];
|
|
47
68
|
}
|