@fiado/type-kit 3.582.0 → 3.584.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/mdm/MdmProviderEnum.test.ts +12 -0
- package/_test_/unit/retailCatalog/backofficeRequests.test.ts +3 -0
- package/_test_/unit/retailCatalog/catalogEnums.test.ts +4 -0
- package/_test_/unit/retailIntegration/eventPayloadBounds.test.ts +147 -0
- package/_test_/unit/retailIntegration/eventPayloads.test.ts +16 -0
- package/_test_/unit/retailIntegration/retailSystemEnums.test.ts +1 -1
- package/bin/mdm/enums/MdmProviderEnum.d.ts +2 -1
- package/bin/mdm/enums/MdmProviderEnum.js +1 -0
- package/bin/retailCatalog/enums/MdmProviderEnum.d.ts +3 -1
- package/bin/retailCatalog/enums/MdmProviderEnum.js +2 -0
- package/bin/retailIntegration/constants/retailIntegrationLimits.d.ts +14 -0
- package/bin/retailIntegration/constants/retailIntegrationLimits.js +15 -1
- package/bin/retailIntegration/dtos/InventoryEventPayload.js +3 -0
- package/bin/retailIntegration/dtos/ProductEventPayload.js +10 -2
- package/bin/retailIntegration/dtos/RetailUserEventPayload.js +3 -0
- package/bin/retailIntegration/dtos/StoreEventPayload.js +13 -0
- package/bin/retailIntegration/enums/RetailMdmProviderEnum.d.ts +2 -1
- package/bin/retailIntegration/enums/RetailMdmProviderEnum.js +1 -0
- package/package.json +1 -1
- package/src/mdm/README.md +17 -17
- package/src/mdm/enums/MdmProviderEnum.ts +2 -1
- package/src/retailCatalog/enums/MdmProviderEnum.ts +2 -0
- package/src/retailIntegration/constants/retailIntegrationLimits.ts +21 -0
- package/src/retailIntegration/dtos/InventoryEventPayload.ts +11 -5
- package/src/retailIntegration/dtos/ProductEventPayload.ts +19 -12
- package/src/retailIntegration/dtos/RetailUserEventPayload.ts +5 -5
- package/src/retailIntegration/dtos/StoreEventPayload.ts +16 -14
- package/src/retailIntegration/enums/RetailMdmProviderEnum.ts +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { MdmProviderEnum } from '../../../src/mdm/enums/MdmProviderEnum';
|
|
3
|
+
|
|
4
|
+
describe('MdmProviderEnum', () => {
|
|
5
|
+
it('trae exactamente los proveedores MDM del contrato, en orden', () => {
|
|
6
|
+
expect(Object.values(MdmProviderEnum)).toEqual(['TRUSTONIC', 'DATACULTR', 'MOTOSAFE']);
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
it('MOTOSAFE vale su propio nombre', () => {
|
|
10
|
+
expect(MdmProviderEnum.MOTOSAFE).toBe('MOTOSAFE');
|
|
11
|
+
});
|
|
12
|
+
});
|
|
@@ -236,6 +236,9 @@ describe('financiamiento, MDM y externalId en las altas y edición backoffice',
|
|
|
236
236
|
const errors = errorsOf(cls, { ...ok, ...financed, mdmProvider: 'KNOX', mdmLockMode: 'NEVER' });
|
|
237
237
|
expect(errors).toEqual(expect.arrayContaining(['mdmProvider', 'mdmLockMode']));
|
|
238
238
|
});
|
|
239
|
+
it('acepta MOTOSAFE como proveedor MDM', () => {
|
|
240
|
+
expect(errorsOf(cls, { ...ok, ...financed, mdmProvider: 'MOTOSAFE' })).toEqual([]);
|
|
241
|
+
});
|
|
239
242
|
it('rechaza MDM incoherente', () => {
|
|
240
243
|
expect(errorsOf(cls, { ...ok, mdmEnabled: true })).toContain('mdmEnabled');
|
|
241
244
|
expect(errorsOf(cls, { ...ok, mdmEnabled: false, mdmProvider: 'TRUSTONIC', mdmLockMode: 'ONDEMAND' })).toContain('mdmEnabled');
|
|
@@ -10,6 +10,10 @@ describe('retailCatalog — enums del inventario multi-item', () => {
|
|
|
10
10
|
expect(Object.values(RetailCatalog.TrackingModeEnum)).toEqual(['IMEI', 'SERIAL', 'QUANTITY']);
|
|
11
11
|
});
|
|
12
12
|
|
|
13
|
+
it('expone los proveedores MDM, con MOTOSAFE', () => {
|
|
14
|
+
expect(Object.values(RetailCatalog.MdmProviderEnum)).toEqual(['TRUSTONIC', 'DATACULTR', 'MOTOSAFE']);
|
|
15
|
+
});
|
|
16
|
+
|
|
13
17
|
it('expone los tipos de movimiento', () => {
|
|
14
18
|
expect(Object.values(RetailCatalog.InventoryMovementTypeEnum)).toEqual([
|
|
15
19
|
'ENTRY', 'RESERVATION', 'RELEASE', 'SALE', 'RETURN', 'ADJUSTMENT',
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { plainToInstance } from 'class-transformer';
|
|
3
|
+
import { validate, ValidationError } from 'class-validator';
|
|
4
|
+
import { StoreEventPayload } from '../../../src/retailIntegration/dtos/StoreEventPayload';
|
|
5
|
+
import { RetailUserEventPayload } from '../../../src/retailIntegration/dtos/RetailUserEventPayload';
|
|
6
|
+
import { ProductEventPayload } from '../../../src/retailIntegration/dtos/ProductEventPayload';
|
|
7
|
+
import { InventoryEventPayload } from '../../../src/retailIntegration/dtos/InventoryEventPayload';
|
|
8
|
+
import {
|
|
9
|
+
RETAIL_ELIGIBLE_FINANCIERS_MAX,
|
|
10
|
+
RETAIL_EVENT_STOCK_MAX,
|
|
11
|
+
RETAIL_ID_TEXT_MAX_LENGTH,
|
|
12
|
+
RETAIL_LONG_TEXT_MAX_LENGTH,
|
|
13
|
+
RETAIL_SHORT_TEXT_MAX_LENGTH,
|
|
14
|
+
} from '../../../src/retailIntegration/constants/retailIntegrationLimits';
|
|
15
|
+
|
|
16
|
+
function paths(errors: ValidationError[], prefix = ''): string[] {
|
|
17
|
+
return errors.flatMap((e) => {
|
|
18
|
+
const p = prefix ? `${prefix}.${e.property}` : e.property;
|
|
19
|
+
return e.children && e.children.length > 0 ? paths(e.children, p) : [p];
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async function errorsOf(cls: new () => object, plain: Record<string, unknown>): Promise<string[]> {
|
|
24
|
+
return paths(await validate(plainToInstance(cls, plain)));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const text = (n: number): string => 'a'.repeat(n);
|
|
28
|
+
const SHORT = RETAIL_SHORT_TEXT_MAX_LENGTH;
|
|
29
|
+
const LONG = RETAIL_LONG_TEXT_MAX_LENGTH;
|
|
30
|
+
const ID = RETAIL_ID_TEXT_MAX_LENGTH;
|
|
31
|
+
|
|
32
|
+
const address = { address1: 'Av. Juárez', externalNumber: '120', internalNumber: null, neighborhood: 'Centro', city: 'Toluca', state: 'Estado de México', zipCode: '50000', country: 'MX', reference: null };
|
|
33
|
+
const store = {
|
|
34
|
+
storeId: '12', code: 'T12', name: 'Sucursal Centro', zoneId: 'SUR', address,
|
|
35
|
+
location: { lat: 19.2826, lng: -99.6557 }, phone: '7221234567', schedule: 'Lun a Dom 10:00 a 20:00', status: 'ACTIVE',
|
|
36
|
+
};
|
|
37
|
+
const user = {
|
|
38
|
+
retailUserId: '431', displayName: 'Grecia Cruz Mendoza', email: 'gcruz@ejemplo.mx', phone: null,
|
|
39
|
+
homeStoreId: '12', zoneId: null, role: 'SELLER', status: 'ACTIVE',
|
|
40
|
+
};
|
|
41
|
+
const price = { basePrice: '4299.00', listPrice: null, effectiveFrom: '2026-08-17T20:32:05Z' };
|
|
42
|
+
const product = {
|
|
43
|
+
sku: 'SAM-A17-128-NEG', externalId: '9001', brand: 'Samsung', model: 'A17', color: 'Negro', capacity: '128 GB', description: null,
|
|
44
|
+
category: 'PHONE', trackingMode: 'IMEI', tier: null, currencyId: 'MXN', price,
|
|
45
|
+
eligibleFinanciers: [], mdmProvider: null, status: 'ACTIVE',
|
|
46
|
+
};
|
|
47
|
+
const piece = { sku: 'SAM-A17-128-NEG', identifier: '356938035643809', storeId: '12', status: 'SOLD', reason: null };
|
|
48
|
+
const movement = { movementType: 'SALE', storeId: '12', targetStoreId: null, pieces: [piece], stock: [] };
|
|
49
|
+
|
|
50
|
+
describe('StoreEventPayload: topes', () => {
|
|
51
|
+
it('acepta textos justo en su tope', async () => {
|
|
52
|
+
const atMax = {
|
|
53
|
+
...store, name: text(SHORT), phone: text(SHORT), schedule: text(SHORT),
|
|
54
|
+
address: { ...address, address1: text(SHORT), externalNumber: text(SHORT), internalNumber: text(SHORT), neighborhood: text(SHORT), city: text(SHORT), state: text(SHORT), country: text(SHORT), reference: text(LONG) },
|
|
55
|
+
};
|
|
56
|
+
expect(await errorsOf(StoreEventPayload, atMax)).toEqual([]);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it.each([
|
|
60
|
+
['name', { name: text(SHORT + 1) }],
|
|
61
|
+
['phone', { phone: text(SHORT + 1) }],
|
|
62
|
+
['schedule', { schedule: text(SHORT + 1) }],
|
|
63
|
+
['address.address1', { address: { ...address, address1: text(SHORT + 1) } }],
|
|
64
|
+
['address.externalNumber', { address: { ...address, externalNumber: text(SHORT + 1) } }],
|
|
65
|
+
['address.internalNumber', { address: { ...address, internalNumber: text(SHORT + 1) } }],
|
|
66
|
+
['address.neighborhood', { address: { ...address, neighborhood: text(SHORT + 1) } }],
|
|
67
|
+
['address.city', { address: { ...address, city: text(SHORT + 1) } }],
|
|
68
|
+
['address.state', { address: { ...address, state: text(SHORT + 1) } }],
|
|
69
|
+
['address.country', { address: { ...address, country: text(SHORT + 1) } }],
|
|
70
|
+
['address.reference', { address: { ...address, reference: text(LONG + 1) } }],
|
|
71
|
+
])('rechaza %s sobre su tope', async (path, over) => {
|
|
72
|
+
expect(await errorsOf(StoreEventPayload, { ...store, ...over })).toContain(path);
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it.each([
|
|
76
|
+
['address', 'Av. Juárez 120'],
|
|
77
|
+
['address', [address]],
|
|
78
|
+
['location', 'lat,lng'],
|
|
79
|
+
['location', [19.2, -99.6]],
|
|
80
|
+
])('rechaza %s que no es objeto', async (path, value) => {
|
|
81
|
+
const errors = await validate(plainToInstance(StoreEventPayload, { ...store, [path]: value }));
|
|
82
|
+
expect(errors.find((e) => e.property === path)?.constraints).toHaveProperty('isObject');
|
|
83
|
+
});
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
describe('RetailUserEventPayload: topes', () => {
|
|
87
|
+
it('acepta textos justo en su tope', async () => {
|
|
88
|
+
// IsEmail ya topa en 254, uno abajo de RETAIL_SHORT_TEXT_MAX_LENGTH.
|
|
89
|
+
const email = `${text(64)}@${'b'.repeat(63)}.${'c'.repeat(63)}.${'d'.repeat(58)}.mx`;
|
|
90
|
+
expect(email).toHaveLength(SHORT - 1);
|
|
91
|
+
expect(await errorsOf(RetailUserEventPayload, { ...user, displayName: text(SHORT), phone: text(SHORT), email })).toEqual([]);
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
it.each([
|
|
95
|
+
['displayName', { displayName: text(SHORT + 1) }],
|
|
96
|
+
['phone', { phone: text(SHORT + 1) }],
|
|
97
|
+
['email', { email: `${text(SHORT)}@ejemplo.mx` }],
|
|
98
|
+
])('rechaza %s sobre su tope', async (path, over) => {
|
|
99
|
+
expect(await errorsOf(RetailUserEventPayload, { ...user, ...over })).toContain(path);
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
describe('ProductEventPayload: topes', () => {
|
|
104
|
+
it('acepta textos, financieras y montos justo en su tope', async () => {
|
|
105
|
+
const atMax = {
|
|
106
|
+
...product, externalId: text(ID), brand: text(SHORT), model: text(SHORT), color: text(SHORT), capacity: text(SHORT), description: text(LONG),
|
|
107
|
+
price: { ...price, basePrice: '999999999999.99', listPrice: '999999999999.99' },
|
|
108
|
+
eligibleFinanciers: Array.from({ length: RETAIL_ELIGIBLE_FINANCIERS_MAX }, () => 'SUREKEEP'),
|
|
109
|
+
};
|
|
110
|
+
expect(await errorsOf(ProductEventPayload, atMax)).toEqual([]);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it.each([
|
|
114
|
+
['externalId', { externalId: text(ID + 1) }],
|
|
115
|
+
['brand', { brand: text(SHORT + 1) }],
|
|
116
|
+
['model', { model: text(SHORT + 1) }],
|
|
117
|
+
['color', { color: text(SHORT + 1) }],
|
|
118
|
+
['capacity', { capacity: text(SHORT + 1) }],
|
|
119
|
+
['description', { description: text(LONG + 1) }],
|
|
120
|
+
['price.basePrice', { price: { ...price, basePrice: '1000000000000.00' } }],
|
|
121
|
+
['price.listPrice', { price: { ...price, listPrice: '1000000000000.00' } }],
|
|
122
|
+
['eligibleFinanciers', { eligibleFinanciers: Array.from({ length: RETAIL_ELIGIBLE_FINANCIERS_MAX + 1 }, () => 'SUREKEEP') }],
|
|
123
|
+
])('rechaza %s', async (path, over) => {
|
|
124
|
+
expect(await errorsOf(ProductEventPayload, { ...product, ...over })).toContain(path);
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it.each([['4299.00'], [[price]]])('rechaza price que no es objeto: %j', async (value) => {
|
|
128
|
+
const errors = await validate(plainToInstance(ProductEventPayload, { ...product, price: value }));
|
|
129
|
+
expect(errors.find((e) => e.property === 'price')?.constraints).toHaveProperty('isObject');
|
|
130
|
+
});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
describe('InventoryEventPayload: topes', () => {
|
|
134
|
+
const stockOf = (n: number) => Array.from({ length: n }, (_, i) => ({ sku: `MICA-${String(i)}`, storeId: '12', qty: 1 }));
|
|
135
|
+
|
|
136
|
+
it('acepta identifier y stock justo en su tope', async () => {
|
|
137
|
+
expect(await errorsOf(InventoryEventPayload, { ...movement, pieces: [{ ...piece, identifier: text(SHORT) }], stock: stockOf(RETAIL_EVENT_STOCK_MAX) })).toEqual([]);
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('rechaza identifier sobre su tope', async () => {
|
|
141
|
+
expect(await errorsOf(InventoryEventPayload, { ...movement, pieces: [{ ...piece, identifier: text(SHORT + 1) }] })).toContain('pieces.0.identifier');
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
it('rechaza stock sobre su tope', async () => {
|
|
145
|
+
expect(await errorsOf(InventoryEventPayload, { ...movement, stock: stockOf(RETAIL_EVENT_STOCK_MAX + 1) })).toContain('stock');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
@@ -5,6 +5,7 @@ import { StoreEventPayload } from '../../../src/retailIntegration/dtos/StoreEven
|
|
|
5
5
|
import { RetailUserEventPayload } from '../../../src/retailIntegration/dtos/RetailUserEventPayload';
|
|
6
6
|
import { ProductEventPayload } from '../../../src/retailIntegration/dtos/ProductEventPayload';
|
|
7
7
|
import { InventoryEventPayload } from '../../../src/retailIntegration/dtos/InventoryEventPayload';
|
|
8
|
+
import { RETAIL_EVENT_PIECES_MAX } from '../../../src/retailIntegration/constants/retailIntegrationLimits';
|
|
8
9
|
|
|
9
10
|
function paths(errors: ValidationError[], prefix = ''): string[] {
|
|
10
11
|
return errors.flatMap((e) => {
|
|
@@ -88,6 +89,14 @@ describe('ProductEventPayload', () => {
|
|
|
88
89
|
expect(await errorsOf(ProductEventPayload, product)).toEqual([]);
|
|
89
90
|
});
|
|
90
91
|
|
|
92
|
+
it.each(['TRUSTONIC', 'DATACULTR', 'MOTOSAFE'])('acepta mdmProvider %s', async (mdmProvider) => {
|
|
93
|
+
expect(await errorsOf(ProductEventPayload, { ...product, mdmProvider })).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('rechaza un mdmProvider fuera del enum', async () => {
|
|
97
|
+
expect(await errorsOf(ProductEventPayload, { ...product, mdmProvider: 'KNOX' })).toContain('mdmProvider');
|
|
98
|
+
});
|
|
99
|
+
|
|
91
100
|
it.each([
|
|
92
101
|
['sku', { sku: 'SAM_A17' }],
|
|
93
102
|
['price.basePrice', { price: { ...product.price, basePrice: '4299' } }],
|
|
@@ -122,4 +131,11 @@ describe('InventoryEventPayload', () => {
|
|
|
122
131
|
expect(await errorsOf(InventoryEventPayload, { ...movement, stock: [s, s] })).toContain('stock');
|
|
123
132
|
expect(await errorsOf(InventoryEventPayload, { ...movement, stock: [{ ...s, qty: -1 }] })).toContain('stock.0.qty');
|
|
124
133
|
});
|
|
134
|
+
|
|
135
|
+
it('acepta hasta RETAIL_EVENT_PIECES_MAX piezas y rechaza una más', async () => {
|
|
136
|
+
const piecesOf = (n: number) => Array.from({ length: n }, (_, i) => ({ ...piece, identifier: `35693803564${String(i).padStart(4, '0')}` }));
|
|
137
|
+
expect(RETAIL_EVENT_PIECES_MAX).toBe(30);
|
|
138
|
+
expect(await errorsOf(InventoryEventPayload, { ...movement, pieces: piecesOf(RETAIL_EVENT_PIECES_MAX) })).toEqual([]);
|
|
139
|
+
expect(await errorsOf(InventoryEventPayload, { ...movement, pieces: piecesOf(RETAIL_EVENT_PIECES_MAX + 1) })).toContain('pieces');
|
|
140
|
+
});
|
|
125
141
|
});
|
|
@@ -27,7 +27,7 @@ describe('enums de los objetos del retailer', () => {
|
|
|
27
27
|
[RetailProductTierEnum, ['LOW', 'MID', 'HIGH', 'PREMIUM']],
|
|
28
28
|
[RetailCurrencyEnum, ['MXN', 'USD']],
|
|
29
29
|
[RetailFinancierEnum, ['SUREKEEP', 'PAYJOY', 'CREDIYA', 'LESPAGO', 'INNOVA']],
|
|
30
|
-
[RetailMdmProviderEnum, ['TRUSTONIC', 'DATACULTR']],
|
|
30
|
+
[RetailMdmProviderEnum, ['TRUSTONIC', 'DATACULTR', 'MOTOSAFE']],
|
|
31
31
|
[RetailProductStatusEnum, ['ACTIVE', 'INACTIVE', 'DISCONTINUED']],
|
|
32
32
|
[RetailPieceStatusEnum, ['AVAILABLE', 'IN_TRANSIT', 'SOLD', 'DAMAGED']],
|
|
33
33
|
[RetailPieceReasonEnum, ['WARRANTY', 'DAMAGED_RETURN', 'SHRINKAGE']],
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @property {string} TRUSTONIC - Proveedor Trustonic.
|
|
6
6
|
* @property {string} DATACULTR - Proveedor Datacultr.
|
|
7
|
+
* @property {string} MOTOSAFE - Proveedor Motosafe (MDM embebido de Motorola); solo opera en ONDEMAND.
|
|
7
8
|
*/
|
|
8
9
|
export declare enum MdmProviderEnum {
|
|
9
10
|
TRUSTONIC = "TRUSTONIC",
|
|
10
|
-
DATACULTR = "DATACULTR"
|
|
11
|
+
DATACULTR = "DATACULTR",
|
|
12
|
+
MOTOSAFE = "MOTOSAFE"
|
|
11
13
|
}
|
|
@@ -7,9 +7,11 @@ exports.MdmProviderEnum = void 0;
|
|
|
7
7
|
*
|
|
8
8
|
* @property {string} TRUSTONIC - Proveedor Trustonic.
|
|
9
9
|
* @property {string} DATACULTR - Proveedor Datacultr.
|
|
10
|
+
* @property {string} MOTOSAFE - Proveedor Motosafe (MDM embebido de Motorola); solo opera en ONDEMAND.
|
|
10
11
|
*/
|
|
11
12
|
var MdmProviderEnum;
|
|
12
13
|
(function (MdmProviderEnum) {
|
|
13
14
|
MdmProviderEnum["TRUSTONIC"] = "TRUSTONIC";
|
|
14
15
|
MdmProviderEnum["DATACULTR"] = "DATACULTR";
|
|
16
|
+
MdmProviderEnum["MOTOSAFE"] = "MOTOSAFE";
|
|
15
17
|
})(MdmProviderEnum || (exports.MdmProviderEnum = MdmProviderEnum = {}));
|
|
@@ -20,3 +20,17 @@ export declare const RETAIL_SKU_PATTERN: RegExp;
|
|
|
20
20
|
export declare const RETAIL_DECIMAL_PATTERN: RegExp;
|
|
21
21
|
/** Tope de identificadores por consulta de inventario en vivo. */
|
|
22
22
|
export declare const RETAIL_LIVE_CHECK_IDENTIFIERS_MAX = 50;
|
|
23
|
+
/** Tope de piezas por evento `InventoryMovementV1`; más se rechaza con INVALID_PAYLOAD. */
|
|
24
|
+
export declare const RETAIL_EVENT_PIECES_MAX = 30;
|
|
25
|
+
/** Tope de ids y códigos libres del retailer, como `externalId`. */
|
|
26
|
+
export declare const RETAIL_ID_TEXT_MAX_LENGTH = 64;
|
|
27
|
+
/** Tope de textos cortos de los payloads: nombres, dirección, teléfono, marca, modelo, `identifier`. */
|
|
28
|
+
export declare const RETAIL_SHORT_TEXT_MAX_LENGTH = 255;
|
|
29
|
+
/** Tope de textos largos de los payloads: descripción y referencia. */
|
|
30
|
+
export declare const RETAIL_LONG_TEXT_MAX_LENGTH = 1000;
|
|
31
|
+
/** Tope de existencias por evento `InventoryMovementV1`. */
|
|
32
|
+
export declare const RETAIL_EVENT_STOCK_MAX = 200;
|
|
33
|
+
/** Tope de financieras elegibles por producto. */
|
|
34
|
+
export declare const RETAIL_ELIGIBLE_FINANCIERS_MAX = 50;
|
|
35
|
+
/** Decimal de los eventos del retailer: hasta 12 enteros y dos decimales, nunca negativo. */
|
|
36
|
+
export declare const RETAIL_EVENT_DECIMAL_PATTERN: RegExp;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
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_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. */
|
|
@@ -23,3 +23,17 @@ exports.RETAIL_SKU_PATTERN = /^(?=.{1,64}$)[A-Za-z0-9-]+(?: +[A-Za-z0-9-]+)*$/;
|
|
|
23
23
|
exports.RETAIL_DECIMAL_PATTERN = /^\d+\.\d{2}$/;
|
|
24
24
|
/** Tope de identificadores por consulta de inventario en vivo. */
|
|
25
25
|
exports.RETAIL_LIVE_CHECK_IDENTIFIERS_MAX = 50;
|
|
26
|
+
/** Tope de piezas por evento `InventoryMovementV1`; más se rechaza con INVALID_PAYLOAD. */
|
|
27
|
+
exports.RETAIL_EVENT_PIECES_MAX = 30;
|
|
28
|
+
/** Tope de ids y códigos libres del retailer, como `externalId`. */
|
|
29
|
+
exports.RETAIL_ID_TEXT_MAX_LENGTH = 64;
|
|
30
|
+
/** Tope de textos cortos de los payloads: nombres, dirección, teléfono, marca, modelo, `identifier`. */
|
|
31
|
+
exports.RETAIL_SHORT_TEXT_MAX_LENGTH = 255;
|
|
32
|
+
/** Tope de textos largos de los payloads: descripción y referencia. */
|
|
33
|
+
exports.RETAIL_LONG_TEXT_MAX_LENGTH = 1000;
|
|
34
|
+
/** Tope de existencias por evento `InventoryMovementV1`. */
|
|
35
|
+
exports.RETAIL_EVENT_STOCK_MAX = 200;
|
|
36
|
+
/** Tope de financieras elegibles por producto. */
|
|
37
|
+
exports.RETAIL_ELIGIBLE_FINANCIERS_MAX = 50;
|
|
38
|
+
/** Decimal de los eventos del retailer: hasta 12 enteros y dos decimales, nunca negativo. */
|
|
39
|
+
exports.RETAIL_EVENT_DECIMAL_PATTERN = /^\d{1,12}\.\d{2}$/;
|
|
@@ -27,6 +27,7 @@ __decorate([
|
|
|
27
27
|
__decorate([
|
|
28
28
|
(0, class_validator_1.IsString)(),
|
|
29
29
|
(0, class_validator_1.Matches)(/\S/),
|
|
30
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
30
31
|
__metadata("design:type", String)
|
|
31
32
|
], PieceEventPayload.prototype, "identifier", void 0);
|
|
32
33
|
__decorate([
|
|
@@ -80,6 +81,7 @@ __decorate([
|
|
|
80
81
|
], InventoryEventPayload.prototype, "targetStoreId", void 0);
|
|
81
82
|
__decorate([
|
|
82
83
|
(0, class_validator_1.IsArray)(),
|
|
84
|
+
(0, class_validator_1.ArrayMaxSize)(retailIntegrationLimits_1.RETAIL_EVENT_PIECES_MAX),
|
|
83
85
|
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
84
86
|
(0, class_transformer_1.Type)(() => PieceEventPayload),
|
|
85
87
|
(0, class_validator_1.Validate)(payloadValidators_1.HasUniqueKeys, ['identifier']),
|
|
@@ -87,6 +89,7 @@ __decorate([
|
|
|
87
89
|
], InventoryEventPayload.prototype, "pieces", void 0);
|
|
88
90
|
__decorate([
|
|
89
91
|
(0, class_validator_1.IsArray)(),
|
|
92
|
+
(0, class_validator_1.ArrayMaxSize)(retailIntegrationLimits_1.RETAIL_EVENT_STOCK_MAX),
|
|
90
93
|
(0, class_validator_1.ValidateNested)({ each: true }),
|
|
91
94
|
(0, class_transformer_1.Type)(() => StockEventPayload),
|
|
92
95
|
(0, class_validator_1.Validate)(payloadValidators_1.HasUniqueKeys, ['storeId', 'sku']),
|
|
@@ -22,13 +22,13 @@ class ProductEventPricePayload {
|
|
|
22
22
|
exports.ProductEventPricePayload = ProductEventPricePayload;
|
|
23
23
|
__decorate([
|
|
24
24
|
(0, class_validator_1.IsString)(),
|
|
25
|
-
(0, class_validator_1.Matches)(retailIntegrationLimits_1.
|
|
25
|
+
(0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_EVENT_DECIMAL_PATTERN),
|
|
26
26
|
__metadata("design:type", String)
|
|
27
27
|
], ProductEventPricePayload.prototype, "basePrice", void 0);
|
|
28
28
|
__decorate([
|
|
29
29
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
30
30
|
(0, class_validator_1.IsString)(),
|
|
31
|
-
(0, class_validator_1.Matches)(retailIntegrationLimits_1.
|
|
31
|
+
(0, class_validator_1.Matches)(retailIntegrationLimits_1.RETAIL_EVENT_DECIMAL_PATTERN),
|
|
32
32
|
__metadata("design:type", String)
|
|
33
33
|
], ProductEventPricePayload.prototype, "listPrice", void 0);
|
|
34
34
|
__decorate([
|
|
@@ -47,31 +47,37 @@ __decorate([
|
|
|
47
47
|
__decorate([
|
|
48
48
|
(0, class_validator_1.IsString)(),
|
|
49
49
|
(0, class_validator_1.Matches)(/\S/),
|
|
50
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_ID_TEXT_MAX_LENGTH),
|
|
50
51
|
__metadata("design:type", String)
|
|
51
52
|
], ProductEventPayload.prototype, "externalId", void 0);
|
|
52
53
|
__decorate([
|
|
53
54
|
(0, class_validator_1.IsString)(),
|
|
54
55
|
(0, class_validator_1.Matches)(/\S/),
|
|
56
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
55
57
|
__metadata("design:type", String)
|
|
56
58
|
], ProductEventPayload.prototype, "brand", void 0);
|
|
57
59
|
__decorate([
|
|
58
60
|
(0, class_validator_1.IsString)(),
|
|
59
61
|
(0, class_validator_1.Matches)(/\S/),
|
|
62
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
60
63
|
__metadata("design:type", String)
|
|
61
64
|
], ProductEventPayload.prototype, "model", void 0);
|
|
62
65
|
__decorate([
|
|
63
66
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
64
67
|
(0, class_validator_1.IsString)(),
|
|
68
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
65
69
|
__metadata("design:type", String)
|
|
66
70
|
], ProductEventPayload.prototype, "color", void 0);
|
|
67
71
|
__decorate([
|
|
68
72
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
69
73
|
(0, class_validator_1.IsString)(),
|
|
74
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
70
75
|
__metadata("design:type", String)
|
|
71
76
|
], ProductEventPayload.prototype, "capacity", void 0);
|
|
72
77
|
__decorate([
|
|
73
78
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
74
79
|
(0, class_validator_1.IsString)(),
|
|
80
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_LONG_TEXT_MAX_LENGTH),
|
|
75
81
|
__metadata("design:type", String)
|
|
76
82
|
], ProductEventPayload.prototype, "description", void 0);
|
|
77
83
|
__decorate([
|
|
@@ -93,12 +99,14 @@ __decorate([
|
|
|
93
99
|
], ProductEventPayload.prototype, "currencyId", void 0);
|
|
94
100
|
__decorate([
|
|
95
101
|
(0, class_validator_1.IsDefined)(),
|
|
102
|
+
(0, class_validator_1.IsObject)(),
|
|
96
103
|
(0, class_validator_1.ValidateNested)(),
|
|
97
104
|
(0, class_transformer_1.Type)(() => ProductEventPricePayload),
|
|
98
105
|
__metadata("design:type", ProductEventPricePayload)
|
|
99
106
|
], ProductEventPayload.prototype, "price", void 0);
|
|
100
107
|
__decorate([
|
|
101
108
|
(0, class_validator_1.IsArray)(),
|
|
109
|
+
(0, class_validator_1.ArrayMaxSize)(retailIntegrationLimits_1.RETAIL_ELIGIBLE_FINANCIERS_MAX),
|
|
102
110
|
(0, class_validator_1.IsIn)(Object.values(enums_1.RetailFinancierEnum), { each: true }),
|
|
103
111
|
__metadata("design:type", Array)
|
|
104
112
|
], ProductEventPayload.prototype, "eligibleFinanciers", void 0);
|
|
@@ -26,15 +26,18 @@ __decorate([
|
|
|
26
26
|
__decorate([
|
|
27
27
|
(0, class_validator_1.IsString)(),
|
|
28
28
|
(0, class_validator_1.Matches)(/\S/),
|
|
29
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
29
30
|
__metadata("design:type", String)
|
|
30
31
|
], RetailUserEventPayload.prototype, "displayName", void 0);
|
|
31
32
|
__decorate([
|
|
32
33
|
(0, class_validator_1.IsEmail)(),
|
|
34
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
33
35
|
__metadata("design:type", String)
|
|
34
36
|
], RetailUserEventPayload.prototype, "email", void 0);
|
|
35
37
|
__decorate([
|
|
36
38
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
37
39
|
(0, class_validator_1.IsString)(),
|
|
40
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
38
41
|
__metadata("design:type", String)
|
|
39
42
|
], RetailUserEventPayload.prototype, "phone", void 0);
|
|
40
43
|
__decorate([
|
|
@@ -24,31 +24,37 @@ exports.StoreEventAddressPayload = StoreEventAddressPayload;
|
|
|
24
24
|
__decorate([
|
|
25
25
|
(0, class_validator_1.IsString)(),
|
|
26
26
|
(0, class_validator_1.Matches)(/\S/),
|
|
27
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
27
28
|
__metadata("design:type", String)
|
|
28
29
|
], StoreEventAddressPayload.prototype, "address1", void 0);
|
|
29
30
|
__decorate([
|
|
30
31
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
31
32
|
(0, class_validator_1.IsString)(),
|
|
33
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
32
34
|
__metadata("design:type", String)
|
|
33
35
|
], StoreEventAddressPayload.prototype, "externalNumber", void 0);
|
|
34
36
|
__decorate([
|
|
35
37
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
36
38
|
(0, class_validator_1.IsString)(),
|
|
39
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
37
40
|
__metadata("design:type", String)
|
|
38
41
|
], StoreEventAddressPayload.prototype, "internalNumber", void 0);
|
|
39
42
|
__decorate([
|
|
40
43
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
41
44
|
(0, class_validator_1.IsString)(),
|
|
45
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
42
46
|
__metadata("design:type", String)
|
|
43
47
|
], StoreEventAddressPayload.prototype, "neighborhood", void 0);
|
|
44
48
|
__decorate([
|
|
45
49
|
(0, class_validator_1.IsString)(),
|
|
46
50
|
(0, class_validator_1.Matches)(/\S/),
|
|
51
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
47
52
|
__metadata("design:type", String)
|
|
48
53
|
], StoreEventAddressPayload.prototype, "city", void 0);
|
|
49
54
|
__decorate([
|
|
50
55
|
(0, class_validator_1.IsString)(),
|
|
51
56
|
(0, class_validator_1.Matches)(/\S/),
|
|
57
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
52
58
|
__metadata("design:type", String)
|
|
53
59
|
], StoreEventAddressPayload.prototype, "state", void 0);
|
|
54
60
|
__decorate([
|
|
@@ -59,11 +65,13 @@ __decorate([
|
|
|
59
65
|
__decorate([
|
|
60
66
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
61
67
|
(0, class_validator_1.IsString)(),
|
|
68
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
62
69
|
__metadata("design:type", String)
|
|
63
70
|
], StoreEventAddressPayload.prototype, "country", void 0);
|
|
64
71
|
__decorate([
|
|
65
72
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
66
73
|
(0, class_validator_1.IsString)(),
|
|
74
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_LONG_TEXT_MAX_LENGTH),
|
|
67
75
|
__metadata("design:type", String)
|
|
68
76
|
], StoreEventAddressPayload.prototype, "reference", void 0);
|
|
69
77
|
/** Coordenada de la tienda; `null` si no se informa. */
|
|
@@ -101,6 +109,7 @@ __decorate([
|
|
|
101
109
|
__decorate([
|
|
102
110
|
(0, class_validator_1.IsString)(),
|
|
103
111
|
(0, class_validator_1.Matches)(/\S/),
|
|
112
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
104
113
|
__metadata("design:type", String)
|
|
105
114
|
], StoreEventPayload.prototype, "name", void 0);
|
|
106
115
|
__decorate([
|
|
@@ -111,12 +120,14 @@ __decorate([
|
|
|
111
120
|
], StoreEventPayload.prototype, "zoneId", void 0);
|
|
112
121
|
__decorate([
|
|
113
122
|
(0, class_validator_1.IsDefined)(),
|
|
123
|
+
(0, class_validator_1.IsObject)(),
|
|
114
124
|
(0, class_validator_1.ValidateNested)(),
|
|
115
125
|
(0, class_transformer_1.Type)(() => StoreEventAddressPayload),
|
|
116
126
|
__metadata("design:type", StoreEventAddressPayload)
|
|
117
127
|
], StoreEventPayload.prototype, "address", void 0);
|
|
118
128
|
__decorate([
|
|
119
129
|
(0, class_validator_1.IsDefined)(),
|
|
130
|
+
(0, class_validator_1.IsObject)(),
|
|
120
131
|
(0, class_validator_1.ValidateNested)(),
|
|
121
132
|
(0, class_transformer_1.Type)(() => StoreEventLocationPayload),
|
|
122
133
|
__metadata("design:type", StoreEventLocationPayload)
|
|
@@ -124,11 +135,13 @@ __decorate([
|
|
|
124
135
|
__decorate([
|
|
125
136
|
(0, class_validator_1.IsString)(),
|
|
126
137
|
(0, class_validator_1.Matches)(/\S/),
|
|
138
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
127
139
|
__metadata("design:type", String)
|
|
128
140
|
], StoreEventPayload.prototype, "phone", void 0);
|
|
129
141
|
__decorate([
|
|
130
142
|
(0, class_validator_1.ValidateIf)(payloadValidators_1.isNullOrValidated),
|
|
131
143
|
(0, class_validator_1.IsString)(),
|
|
144
|
+
(0, class_validator_1.MaxLength)(retailIntegrationLimits_1.RETAIL_SHORT_TEXT_MAX_LENGTH),
|
|
132
145
|
__metadata("design:type", String)
|
|
133
146
|
], StoreEventPayload.prototype, "schedule", void 0);
|
|
134
147
|
__decorate([
|
|
@@ -6,4 +6,5 @@ var RetailMdmProviderEnum;
|
|
|
6
6
|
(function (RetailMdmProviderEnum) {
|
|
7
7
|
RetailMdmProviderEnum["TRUSTONIC"] = "TRUSTONIC";
|
|
8
8
|
RetailMdmProviderEnum["DATACULTR"] = "DATACULTR";
|
|
9
|
+
RetailMdmProviderEnum["MOTOSAFE"] = "MOTOSAFE";
|
|
9
10
|
})(RetailMdmProviderEnum || (exports.RetailMdmProviderEnum = RetailMdmProviderEnum = {}));
|
package/package.json
CHANGED
package/src/mdm/README.md
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
## Qué es esto
|
|
6
6
|
|
|
7
|
-
DTOs y enums para el servicio MDM (Mobile Device Management) de SureKeep. Son agnósticos al proveedor — la misma forma sirve para Trustonic, Datacultr o cualquier otro proveedor que se integre en el futuro.
|
|
7
|
+
DTOs y enums para el servicio MDM (Mobile Device Management) de SureKeep. Son agnósticos al proveedor — la misma forma sirve para Trustonic, Datacultr, Motosafe o cualquier otro proveedor que se integre en el futuro.
|
|
8
8
|
|
|
9
9
|
## Convenciones
|
|
10
10
|
|
|
@@ -43,22 +43,22 @@ import { Mdm } from "@fiado/type-kit";
|
|
|
43
43
|
|
|
44
44
|
## Acciones soportadas
|
|
45
45
|
|
|
46
|
-
| Acción | Trustonic | Datacultr |
|
|
47
|
-
|
|
48
|
-
| DeviceNotify | HEADSUP / FULLSCREEN | Notification (con `notification_code`) |
|
|
49
|
-
| DeviceRestrictApps | ⚠️ Por confirmar configuración | Nudge (App Block) |
|
|
50
|
-
| DeviceLock (FIRM) | Lock LOCK | Bulk Lock |
|
|
51
|
-
| DeviceLock (TEMPORARY) | Lock DIMISSABLE | ❌ Error: no soporta |
|
|
52
|
-
| DeviceUnlock | Unlock | Unlock |
|
|
53
|
-
| DevicePinUnlock | PIN Unlock | get_device_passcode |
|
|
54
|
-
| DeviceStatus | Device Info | Device Status by IMEI |
|
|
55
|
-
| DeviceEnroll | Upload + Activate (síncrono) | Registra y espera webhook |
|
|
56
|
-
| DeviceRelease | Release | Release |
|
|
57
|
-
| DeviceServiceDeactivate | Deactivate Service | ❌ Error: no soporta |
|
|
58
|
-
| DeviceArchive | Archive | ❌ Error: no soporta |
|
|
59
|
-
| DeviceAutoLockSchedule | ❌ Error: no soporta postpaid | Adhoc Auto Lock |
|
|
60
|
-
| DeviceLastSeen | (incluido en Device Info) | Device Last Seen |
|
|
61
|
-
| DeviceLog | ❌ Error: no expone | Device Log |
|
|
46
|
+
| Acción | Trustonic | Datacultr | Motosafe |
|
|
47
|
+
|---|---|---|---|
|
|
48
|
+
| DeviceNotify | HEADSUP / FULLSCREEN | Notification (con `notification_code`) | send-message (título ≤ 30, cuerpo ≤ 100; un IMEI por llamada) |
|
|
49
|
+
| DeviceRestrictApps | ⚠️ Por confirmar configuración | Nudge (App Block) | ❌ Error: no soporta |
|
|
50
|
+
| DeviceLock (FIRM) | Lock LOCK | Bulk Lock | command `lock` (sin mensaje en pantalla) |
|
|
51
|
+
| DeviceLock (TEMPORARY) | Lock DIMISSABLE | ❌ Error: no soporta | ❌ Error: no soporta |
|
|
52
|
+
| DeviceUnlock | Unlock | Unlock | command `unlock` (sin `unlockUntil`: solo ONDEMAND) |
|
|
53
|
+
| DevicePinUnlock | PIN Unlock | get_device_passcode | ❌ Error: no soporta |
|
|
54
|
+
| DeviceStatus | Device Info | Device Status by IMEI | Device (`status` / `nextStatus`) |
|
|
55
|
+
| DeviceEnroll | Upload + Activate (síncrono) | Registra y espera webhook | register (sin handle; se concilia por estado) |
|
|
56
|
+
| DeviceRelease | Release | Release | command `release` |
|
|
57
|
+
| DeviceServiceDeactivate | Deactivate Service | ❌ Error: no soporta | ❌ Error: no soporta |
|
|
58
|
+
| DeviceArchive | Archive | ❌ Error: no soporta | Cancel Device |
|
|
59
|
+
| DeviceAutoLockSchedule | ❌ Error: no soporta postpaid | Adhoc Auto Lock | ❌ Error: no soporta |
|
|
60
|
+
| DeviceLastSeen | (incluido en Device Info) | Device Last Seen | ❌ Error: no expone |
|
|
61
|
+
| DeviceLog | ❌ Error: no expone | Device Log | ❌ Error: no expone |
|
|
62
62
|
|
|
63
63
|
## Modo de bloqueo (`MdmLockModeEnum`)
|
|
64
64
|
|
|
@@ -4,8 +4,10 @@
|
|
|
4
4
|
*
|
|
5
5
|
* @property {string} TRUSTONIC - Proveedor Trustonic.
|
|
6
6
|
* @property {string} DATACULTR - Proveedor Datacultr.
|
|
7
|
+
* @property {string} MOTOSAFE - Proveedor Motosafe (MDM embebido de Motorola); solo opera en ONDEMAND.
|
|
7
8
|
*/
|
|
8
9
|
export enum MdmProviderEnum {
|
|
9
10
|
TRUSTONIC = 'TRUSTONIC',
|
|
10
11
|
DATACULTR = 'DATACULTR',
|
|
12
|
+
MOTOSAFE = 'MOTOSAFE',
|
|
11
13
|
}
|
|
@@ -29,3 +29,24 @@ export const RETAIL_DECIMAL_PATTERN = /^\d+\.\d{2}$/;
|
|
|
29
29
|
|
|
30
30
|
/** Tope de identificadores por consulta de inventario en vivo. */
|
|
31
31
|
export const RETAIL_LIVE_CHECK_IDENTIFIERS_MAX = 50;
|
|
32
|
+
|
|
33
|
+
/** Tope de piezas por evento `InventoryMovementV1`; más se rechaza con INVALID_PAYLOAD. */
|
|
34
|
+
export const RETAIL_EVENT_PIECES_MAX = 30;
|
|
35
|
+
|
|
36
|
+
/** Tope de ids y códigos libres del retailer, como `externalId`. */
|
|
37
|
+
export const RETAIL_ID_TEXT_MAX_LENGTH = 64;
|
|
38
|
+
|
|
39
|
+
/** Tope de textos cortos de los payloads: nombres, dirección, teléfono, marca, modelo, `identifier`. */
|
|
40
|
+
export const RETAIL_SHORT_TEXT_MAX_LENGTH = 255;
|
|
41
|
+
|
|
42
|
+
/** Tope de textos largos de los payloads: descripción y referencia. */
|
|
43
|
+
export const RETAIL_LONG_TEXT_MAX_LENGTH = 1000;
|
|
44
|
+
|
|
45
|
+
/** Tope de existencias por evento `InventoryMovementV1`. */
|
|
46
|
+
export const RETAIL_EVENT_STOCK_MAX = 200;
|
|
47
|
+
|
|
48
|
+
/** Tope de financieras elegibles por producto. */
|
|
49
|
+
export const RETAIL_ELIGIBLE_FINANCIERS_MAX = 50;
|
|
50
|
+
|
|
51
|
+
/** Decimal de los eventos del retailer: hasta 12 enteros y dos decimales, nunca negativo. */
|
|
52
|
+
export const RETAIL_EVENT_DECIMAL_PATTERN = /^\d{1,12}\.\d{2}$/;
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Type } from 'class-transformer';
|
|
2
|
-
import { IsArray, IsIn, IsInt, IsString, Matches, Min, Validate, ValidateNested } from 'class-validator';
|
|
3
|
-
import {
|
|
2
|
+
import { ArrayMaxSize, IsArray, IsIn, IsInt, IsString, Matches, MaxLength, Min, Validate, ValidateNested } from 'class-validator';
|
|
3
|
+
import {
|
|
4
|
+
RETAIL_EVENT_PIECES_MAX,
|
|
5
|
+
RETAIL_ENTITY_ID_PATTERN,
|
|
6
|
+
RETAIL_EVENT_STOCK_MAX,
|
|
7
|
+
RETAIL_SHORT_TEXT_MAX_LENGTH,
|
|
8
|
+
RETAIL_SKU_PATTERN,
|
|
9
|
+
} from '../constants/retailIntegrationLimits';
|
|
4
10
|
import { RetailMovementTypeEnum, RetailPieceStatusEnum } from '../enums';
|
|
5
11
|
import type { RetailPieceReason, RetailPieceStatus, RetailSystemPiece, RetailSystemStock } from '../models/RetailSystemInventory';
|
|
6
12
|
import { HasUniqueKeys, ReasonMatchesStatus, TargetStoreMatchesMovement } from '../validators/payloadValidators';
|
|
@@ -10,7 +16,7 @@ export type RetailMovementType = `${RetailMovementTypeEnum}`;
|
|
|
10
16
|
/** Pieza con IMEI o serie, como quedó después del movimiento. */
|
|
11
17
|
export class PieceEventPayload implements RetailSystemPiece {
|
|
12
18
|
@IsString() @Matches(RETAIL_SKU_PATTERN) sku!: string;
|
|
13
|
-
@IsString() @Matches(/\S/) identifier!: string;
|
|
19
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) identifier!: string;
|
|
14
20
|
@IsString() @Matches(RETAIL_ENTITY_ID_PATTERN) storeId!: string;
|
|
15
21
|
@IsIn(Object.values(RetailPieceStatusEnum)) status!: RetailPieceStatus;
|
|
16
22
|
@Validate(ReasonMatchesStatus) reason!: RetailPieceReason | null;
|
|
@@ -28,6 +34,6 @@ export class InventoryEventPayload {
|
|
|
28
34
|
@IsIn(Object.values(RetailMovementTypeEnum)) movementType!: RetailMovementType;
|
|
29
35
|
@IsString() @Matches(RETAIL_ENTITY_ID_PATTERN) storeId!: string;
|
|
30
36
|
@Validate(TargetStoreMatchesMovement) targetStoreId!: string | null;
|
|
31
|
-
@IsArray() @ValidateNested({ each: true }) @Type(() => PieceEventPayload) @Validate(HasUniqueKeys, ['identifier']) pieces!: PieceEventPayload[];
|
|
32
|
-
@IsArray() @ValidateNested({ each: true }) @Type(() => StockEventPayload) @Validate(HasUniqueKeys, ['storeId', 'sku']) stock!: StockEventPayload[];
|
|
37
|
+
@IsArray() @ArrayMaxSize(RETAIL_EVENT_PIECES_MAX) @ValidateNested({ each: true }) @Type(() => PieceEventPayload) @Validate(HasUniqueKeys, ['identifier']) pieces!: PieceEventPayload[];
|
|
38
|
+
@IsArray() @ArrayMaxSize(RETAIL_EVENT_STOCK_MAX) @ValidateNested({ each: true }) @Type(() => StockEventPayload) @Validate(HasUniqueKeys, ['storeId', 'sku']) stock!: StockEventPayload[];
|
|
33
39
|
}
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Type } from 'class-transformer';
|
|
2
|
-
import { IsArray, IsDefined, IsIn, IsString, Matches, Validate, ValidateIf, ValidateNested } from 'class-validator';
|
|
3
|
-
import {
|
|
2
|
+
import { ArrayMaxSize, IsArray, IsDefined, IsIn, IsObject, IsString, Matches, MaxLength, Validate, ValidateIf, ValidateNested } from 'class-validator';
|
|
3
|
+
import {
|
|
4
|
+
RETAIL_ELIGIBLE_FINANCIERS_MAX,
|
|
5
|
+
RETAIL_EVENT_DECIMAL_PATTERN,
|
|
6
|
+
RETAIL_ID_TEXT_MAX_LENGTH,
|
|
7
|
+
RETAIL_LONG_TEXT_MAX_LENGTH,
|
|
8
|
+
RETAIL_SHORT_TEXT_MAX_LENGTH,
|
|
9
|
+
RETAIL_SKU_PATTERN,
|
|
10
|
+
} from '../constants/retailIntegrationLimits';
|
|
4
11
|
import {
|
|
5
12
|
RetailCurrencyEnum,
|
|
6
13
|
RetailFinancierEnum,
|
|
@@ -26,26 +33,26 @@ import { isNullOrValidated } from '../validators/payloadValidators';
|
|
|
26
33
|
|
|
27
34
|
/** Precio del producto en decimales string. */
|
|
28
35
|
export class ProductEventPricePayload implements RetailSystemProductPrice {
|
|
29
|
-
@IsString() @Matches(
|
|
30
|
-
@ValidateIf(isNullOrValidated) @IsString() @Matches(
|
|
36
|
+
@IsString() @Matches(RETAIL_EVENT_DECIMAL_PATTERN) basePrice!: string;
|
|
37
|
+
@ValidateIf(isNullOrValidated) @IsString() @Matches(RETAIL_EVENT_DECIMAL_PATTERN) listPrice!: string | null;
|
|
31
38
|
@Validate(IsIsoDateTimeWithZone) effectiveFrom!: string;
|
|
32
39
|
}
|
|
33
40
|
|
|
34
41
|
/** Payload de los eventos de producto: el producto completo. */
|
|
35
42
|
export class ProductEventPayload implements RetailSystemProduct {
|
|
36
43
|
@IsString() @Matches(RETAIL_SKU_PATTERN) sku!: string;
|
|
37
|
-
@IsString() @Matches(/\S/) externalId!: string;
|
|
38
|
-
@IsString() @Matches(/\S/) brand!: string;
|
|
39
|
-
@IsString() @Matches(/\S/) model!: string;
|
|
40
|
-
@ValidateIf(isNullOrValidated) @IsString() color!: string | null;
|
|
41
|
-
@ValidateIf(isNullOrValidated) @IsString() capacity!: string | null;
|
|
42
|
-
@ValidateIf(isNullOrValidated) @IsString() description!: string | null;
|
|
44
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_ID_TEXT_MAX_LENGTH) externalId!: string;
|
|
45
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) brand!: string;
|
|
46
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) model!: string;
|
|
47
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) color!: string | null;
|
|
48
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) capacity!: string | null;
|
|
49
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_LONG_TEXT_MAX_LENGTH) description!: string | null;
|
|
43
50
|
@IsIn(Object.values(RetailProductCategoryEnum)) category!: RetailProductCategory;
|
|
44
51
|
@IsIn(Object.values(RetailTrackingModeEnum)) trackingMode!: RetailTrackingMode;
|
|
45
52
|
@ValidateIf(isNullOrValidated) @IsIn(Object.values(RetailProductTierEnum)) tier!: RetailProductTier | null;
|
|
46
53
|
@IsIn(Object.values(RetailCurrencyEnum)) currencyId!: RetailCurrency;
|
|
47
|
-
@IsDefined() @ValidateNested() @Type(() => ProductEventPricePayload) price!: ProductEventPricePayload;
|
|
48
|
-
@IsArray() @IsIn(Object.values(RetailFinancierEnum), { each: true }) eligibleFinanciers!: RetailFinancier[];
|
|
54
|
+
@IsDefined() @IsObject() @ValidateNested() @Type(() => ProductEventPricePayload) price!: ProductEventPricePayload;
|
|
55
|
+
@IsArray() @ArrayMaxSize(RETAIL_ELIGIBLE_FINANCIERS_MAX) @IsIn(Object.values(RetailFinancierEnum), { each: true }) eligibleFinanciers!: RetailFinancier[];
|
|
49
56
|
@ValidateIf(isNullOrValidated) @IsIn(Object.values(RetailMdmProviderEnum)) mdmProvider!: RetailMdmProvider | null;
|
|
50
57
|
@IsIn(Object.values(RetailProductStatusEnum)) status!: RetailProductStatus;
|
|
51
58
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { IsEmail, IsIn, IsString, Matches, ValidateIf } from 'class-validator';
|
|
2
|
-
import { RETAIL_ENTITY_ID_PATTERN, RETAIL_ZONE_ID_PATTERN } from '../constants/retailIntegrationLimits';
|
|
1
|
+
import { IsEmail, IsIn, IsString, Matches, MaxLength, ValidateIf } from 'class-validator';
|
|
2
|
+
import { RETAIL_ENTITY_ID_PATTERN, RETAIL_SHORT_TEXT_MAX_LENGTH, RETAIL_ZONE_ID_PATTERN } from '../constants/retailIntegrationLimits';
|
|
3
3
|
import { RetailUserAccessStatusEnum, RetailUserRoleEnum } from '../enums';
|
|
4
4
|
import type { RetailSystemUser, RetailUserAccessStatus, RetailUserRole } from '../models/RetailSystemUser';
|
|
5
5
|
import { isNullOrValidated } from '../validators/payloadValidators';
|
|
@@ -7,9 +7,9 @@ import { isNullOrValidated } from '../validators/payloadValidators';
|
|
|
7
7
|
/** Payload de los cuatro eventos de usuario: el usuario completo. */
|
|
8
8
|
export class RetailUserEventPayload implements RetailSystemUser {
|
|
9
9
|
@IsString() @Matches(RETAIL_ENTITY_ID_PATTERN) retailUserId!: string;
|
|
10
|
-
@IsString() @Matches(/\S/) displayName!: string;
|
|
11
|
-
@IsEmail() email!: string;
|
|
12
|
-
@ValidateIf(isNullOrValidated) @IsString() phone!: string | null;
|
|
10
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) displayName!: string;
|
|
11
|
+
@IsEmail() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) email!: string;
|
|
12
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) phone!: string | null;
|
|
13
13
|
@IsString() @Matches(RETAIL_ENTITY_ID_PATTERN) homeStoreId!: string;
|
|
14
14
|
@ValidateIf(isNullOrValidated) @IsString() @Matches(RETAIL_ZONE_ID_PATTERN) zoneId!: string | null;
|
|
15
15
|
@IsIn(Object.values(RetailUserRoleEnum)) role!: RetailUserRole;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Type } from 'class-transformer';
|
|
2
|
-
import { IsDefined, IsIn, IsNumber, IsString, Length, Matches, Max, Min, ValidateIf, ValidateNested } from 'class-validator';
|
|
2
|
+
import { IsDefined, IsIn, IsNumber, IsObject, IsString, Length, Matches, Max, MaxLength, Min, ValidateIf, ValidateNested } from 'class-validator';
|
|
3
3
|
import {
|
|
4
4
|
RETAIL_ENTITY_ID_PATTERN,
|
|
5
|
+
RETAIL_LONG_TEXT_MAX_LENGTH,
|
|
6
|
+
RETAIL_SHORT_TEXT_MAX_LENGTH,
|
|
5
7
|
RETAIL_STORE_CODE_MAX_LENGTH,
|
|
6
8
|
RETAIL_ZIP_CODE_MAX_LENGTH,
|
|
7
9
|
RETAIL_ZIP_CODE_MIN_LENGTH,
|
|
@@ -16,15 +18,15 @@ const LNG_MAX = 180;
|
|
|
16
18
|
|
|
17
19
|
/** Dirección de la tienda; los opcionales llegan como `null`, nunca se omiten. */
|
|
18
20
|
export class StoreEventAddressPayload implements RetailSystemStoreAddress {
|
|
19
|
-
@IsString() @Matches(/\S/) address1!: string;
|
|
20
|
-
@ValidateIf(isNullOrValidated) @IsString() externalNumber!: string | null;
|
|
21
|
-
@ValidateIf(isNullOrValidated) @IsString() internalNumber!: string | null;
|
|
22
|
-
@ValidateIf(isNullOrValidated) @IsString() neighborhood!: string | null;
|
|
23
|
-
@IsString() @Matches(/\S/) city!: string;
|
|
24
|
-
@IsString() @Matches(/\S/) state!: string;
|
|
21
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) address1!: string;
|
|
22
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) externalNumber!: string | null;
|
|
23
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) internalNumber!: string | null;
|
|
24
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) neighborhood!: string | null;
|
|
25
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) city!: string;
|
|
26
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) state!: string;
|
|
25
27
|
@IsString() @Length(RETAIL_ZIP_CODE_MIN_LENGTH, RETAIL_ZIP_CODE_MAX_LENGTH) zipCode!: string;
|
|
26
|
-
@ValidateIf(isNullOrValidated) @IsString() country!: string | null;
|
|
27
|
-
@ValidateIf(isNullOrValidated) @IsString() reference!: string | null;
|
|
28
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) country!: string | null;
|
|
29
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_LONG_TEXT_MAX_LENGTH) reference!: string | null;
|
|
28
30
|
}
|
|
29
31
|
|
|
30
32
|
/** Coordenada de la tienda; `null` si no se informa. */
|
|
@@ -37,11 +39,11 @@ export class StoreEventLocationPayload implements RetailSystemStoreLocation {
|
|
|
37
39
|
export class StoreEventPayload implements RetailSystemStore {
|
|
38
40
|
@IsString() @Matches(RETAIL_ENTITY_ID_PATTERN) storeId!: string;
|
|
39
41
|
@IsString() @Length(1, RETAIL_STORE_CODE_MAX_LENGTH) code!: string;
|
|
40
|
-
@IsString() @Matches(/\S/) name!: string;
|
|
42
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) name!: string;
|
|
41
43
|
@ValidateIf(isNullOrValidated) @IsString() @Matches(RETAIL_ZONE_ID_PATTERN) zoneId!: string | null;
|
|
42
|
-
@IsDefined() @ValidateNested() @Type(() => StoreEventAddressPayload) address!: StoreEventAddressPayload;
|
|
43
|
-
@IsDefined() @ValidateNested() @Type(() => StoreEventLocationPayload) location!: StoreEventLocationPayload;
|
|
44
|
-
@IsString() @Matches(/\S/) phone!: string;
|
|
45
|
-
@ValidateIf(isNullOrValidated) @IsString() schedule!: string | null;
|
|
44
|
+
@IsDefined() @IsObject() @ValidateNested() @Type(() => StoreEventAddressPayload) address!: StoreEventAddressPayload;
|
|
45
|
+
@IsDefined() @IsObject() @ValidateNested() @Type(() => StoreEventLocationPayload) location!: StoreEventLocationPayload;
|
|
46
|
+
@IsString() @Matches(/\S/) @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) phone!: string;
|
|
47
|
+
@ValidateIf(isNullOrValidated) @IsString() @MaxLength(RETAIL_SHORT_TEXT_MAX_LENGTH) schedule!: string | null;
|
|
46
48
|
@IsIn(Object.values(RetailStoreStatusEnum)) status!: RetailStoreStatus;
|
|
47
49
|
}
|