dcos-core-monalisav2-latam 1.1.3 → 1.1.5
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/package.json
CHANGED
|
@@ -3,107 +3,6 @@ const VtexApi = require("../../vtex/clients/VtexApi");
|
|
|
3
3
|
|
|
4
4
|
const DATA_ENTITY_CLIENT_ACRONYM = process.env.DATA_ENTITY_CLIENT_ACRONYM || 'CT';
|
|
5
5
|
|
|
6
|
-
const buildProductJson = (sku, status, quantity, type, priority) => ({
|
|
7
|
-
refId: sku.sku,
|
|
8
|
-
status,
|
|
9
|
-
quantity: quantity || sku.quantity,
|
|
10
|
-
type,
|
|
11
|
-
priority: priority || sku.priority
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
const getClientUserId = async (vtexApi, pdv, an) => {
|
|
15
|
-
try {
|
|
16
|
-
const clientId = pdv;
|
|
17
|
-
const searchResult = await vtexApi.fetch(`/dataentities/${DATA_ENTITY_CLIENT_ACRONYM}/search?_fields=_all&id=${clientId}`, {});
|
|
18
|
-
if (searchResult?.status === 200 && searchResult.data?.length > 0) {
|
|
19
|
-
return searchResult.data[0]?.id;
|
|
20
|
-
}
|
|
21
|
-
return null;
|
|
22
|
-
} catch (error) {
|
|
23
|
-
Logger.error("Error getting client userId", error);
|
|
24
|
-
return null;
|
|
25
|
-
}
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
const getCatalogProductStatus = async (vtexApi, skuId) => {
|
|
29
|
-
try {
|
|
30
|
-
const skuData = await vtexApi.fetch(`/catalog_system/pvt/sku/stockkeepingunitbyid/${skuId}`, {});
|
|
31
|
-
|
|
32
|
-
if (skuData?.status === 200 && skuData.data) {
|
|
33
|
-
const { IsActive, ProductId } = skuData.data;
|
|
34
|
-
|
|
35
|
-
if (!IsActive) return 'inactive';
|
|
36
|
-
|
|
37
|
-
const productData = await vtexApi.fetch(`/catalog_system/pvt/products/productget/${ProductId}`, {
|
|
38
|
-
validateStatus: () => true
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
if (productData?.status === 200 && productData.data) {
|
|
42
|
-
const { IsVisible, IsActive: productActive } = productData.data;
|
|
43
|
-
|
|
44
|
-
if (!productActive) return 'inactive';
|
|
45
|
-
if (!IsVisible) return 'not_visible';
|
|
46
|
-
return 'active';
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
return 'invalid';
|
|
51
|
-
} catch (error) {
|
|
52
|
-
Logger.error("Error getting catalog product status", error);
|
|
53
|
-
return 'invalid';
|
|
54
|
-
}
|
|
55
|
-
};
|
|
56
|
-
|
|
57
|
-
const saveSuggestedProducts = async (vtexApi, userId, productsData, an, entityProductSuggested) => {
|
|
58
|
-
try {
|
|
59
|
-
const orderDate = new Date().toISOString();
|
|
60
|
-
const data = {
|
|
61
|
-
id: `${userId}_${Date.now()}`,
|
|
62
|
-
userId,
|
|
63
|
-
orderDate,
|
|
64
|
-
type: 'suggested',
|
|
65
|
-
productsRegular: productsData?.regular,
|
|
66
|
-
productsIncremental: productsData?.incremental,
|
|
67
|
-
an
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
await vtexApi.fetch(`/dataentities/${entityProductSuggested}/documents`, {
|
|
71
|
-
method: 'POST',
|
|
72
|
-
data,
|
|
73
|
-
headers: {
|
|
74
|
-
'authorization': 'Basic Og=='
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
Logger.log("Suggested products saved successfully", { userId, orderDate });
|
|
79
|
-
} catch (error) {
|
|
80
|
-
Logger.error("Error saving suggested products", error);
|
|
81
|
-
}
|
|
82
|
-
};
|
|
83
|
-
|
|
84
|
-
const saveOriginalDataToClient = async (vtexApi, pdv, originalData, an) => {
|
|
85
|
-
try {
|
|
86
|
-
const clientId = `${pdv}${an}dist1`;
|
|
87
|
-
const updateData = {
|
|
88
|
-
productsRegular: originalData.productsRegular || [],
|
|
89
|
-
productsIncremental: originalData.productsIncremental || [],
|
|
90
|
-
lastSuggestedProductsUpdate: new Date().toISOString()
|
|
91
|
-
};
|
|
92
|
-
|
|
93
|
-
await vtexApi.fetch(`/dataentities/${DATA_ENTITY_CLIENT_ACRONYM}/documents/${clientId}`, {
|
|
94
|
-
method: 'PUT',
|
|
95
|
-
data: updateData,
|
|
96
|
-
headers: {
|
|
97
|
-
'authorization': 'Basic Og=='
|
|
98
|
-
}
|
|
99
|
-
});
|
|
100
|
-
|
|
101
|
-
Logger.log("Original data saved to client", { pdv });
|
|
102
|
-
} catch (error) {
|
|
103
|
-
Logger.error("Error saving original data to client", error);
|
|
104
|
-
}
|
|
105
|
-
};
|
|
106
|
-
|
|
107
6
|
const consumer = async (event) => {
|
|
108
7
|
try {
|
|
109
8
|
const records = event.Records || [];
|
|
@@ -115,45 +14,46 @@ const consumer = async (event) => {
|
|
|
115
14
|
parentAccount,
|
|
116
15
|
key,
|
|
117
16
|
token,
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
nameEntityProductSuggested
|
|
17
|
+
suggestedProductEntityName,
|
|
18
|
+
Startemail,
|
|
19
|
+
Domainemail,
|
|
20
|
+
...client
|
|
123
21
|
} = messageBody;
|
|
124
22
|
|
|
125
|
-
|
|
126
|
-
|
|
23
|
+
// Referencia a la API de VTEX sobre el marketplace asociado
|
|
127
24
|
const vtexApi = new VtexApi(parentAccount, key, token);
|
|
128
25
|
|
|
129
|
-
const
|
|
130
|
-
console.log("Fetched userId:", userId);
|
|
131
|
-
if (!userId) {
|
|
132
|
-
Logger.error("Client userId not found", { pdv, an });
|
|
133
|
-
continue;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const regularProducts = [];
|
|
137
|
-
const incrementalProducts = [];
|
|
138
|
-
|
|
139
|
-
for (const sku of productsRegular) {
|
|
140
|
-
const status = await getCatalogProductStatus(vtexApi, sku.sku);
|
|
141
|
-
regularProducts.push(buildProductJson(sku, status, sku.quantity, 'regular', sku.priority));
|
|
142
|
-
}
|
|
26
|
+
const { clientId, productsRegular, productsIncremental } = client;
|
|
143
27
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
28
|
+
// Objeto de datos para el producto sugerido
|
|
29
|
+
const suggestedProductData = {
|
|
30
|
+
orderDate: new Date().toISOString(),
|
|
31
|
+
clientId,
|
|
32
|
+
type: 'SUGGESTED_ORDER',
|
|
33
|
+
productsRegular,
|
|
34
|
+
productsIncremental,
|
|
35
|
+
};
|
|
36
|
+
const email = `${Startemail}+${clientId}@${Domainemail}`;
|
|
37
|
+
// Consulta del userProfileId
|
|
38
|
+
await findUserProfileId(vtexApi, email, suggestedProductData);
|
|
39
|
+
|
|
40
|
+
// Insertar datos en entidad de Productos Sugeridos
|
|
41
|
+
await vtexApi.fetch(`/dataentities/${suggestedProductEntityName}/documents`, {
|
|
42
|
+
method: 'PATCH',
|
|
43
|
+
data: suggestedProductData
|
|
44
|
+
});
|
|
153
45
|
|
|
154
|
-
|
|
46
|
+
// Insertar datos en Clientes Totales
|
|
47
|
+
await vtexApi.fetch(`/dataentities/${DATA_ENTITY_CLIENT_ACRONYM}/documents`, {
|
|
48
|
+
method: 'PATCH',
|
|
49
|
+
data: {
|
|
50
|
+
id: clientId,
|
|
51
|
+
productsRegular: JSON.stringify(productsRegular),
|
|
52
|
+
productsIncremental: JSON.stringify(productsIncremental),
|
|
53
|
+
}
|
|
54
|
+
});
|
|
155
55
|
|
|
156
|
-
Logger.log("Suggested products processed successfully", {
|
|
56
|
+
Logger.log("Suggested products processed successfully", { an, clientId });
|
|
157
57
|
}
|
|
158
58
|
} catch (error) {
|
|
159
59
|
Logger.error("Error in suggested products consumer", error);
|
|
@@ -161,6 +61,22 @@ const consumer = async (event) => {
|
|
|
161
61
|
}
|
|
162
62
|
};
|
|
163
63
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
64
|
+
/**
|
|
65
|
+
* Consulta el userProfile del usuario con base al email.
|
|
66
|
+
* @param {*} vtexApi Instancia de conexión a la API de VTEX.
|
|
67
|
+
* @param {String} email Correo electrónico del usuario
|
|
68
|
+
* @param {Object} suggestedProductData Objeto donde se consolida la información de producto sugerido.
|
|
69
|
+
* @throws Si la consulta del profile genera un error, o el userProfile no está correcto.
|
|
70
|
+
*/
|
|
71
|
+
async function findUserProfileId(vtexApi, email, suggestedProductData) {
|
|
72
|
+
const searchResult = await vtexApi.fetch(`/checkout/pub/profiles?email=${encodeURIComponent(email)}`, {});
|
|
73
|
+
if (searchResult?.status == 200) {
|
|
74
|
+
const { userProfileId, isComplete } = searchResult.data;
|
|
75
|
+
if (!(userProfileId && isComplete)) {
|
|
76
|
+
throw new Error(`User with id ${email} is not valid`);
|
|
77
|
+
}
|
|
78
|
+
suggestedProductData.id = userProfileId;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
module.exports = { consumer };
|
|
@@ -2,232 +2,276 @@ const AWSServices = require("../../../common/utils/aws-services");
|
|
|
2
2
|
const ApiResponse = require("../../../common/utils/api-response");
|
|
3
3
|
const Logger = require("../../../common/utils/logger");
|
|
4
4
|
const AccountData = require("../../../entities/account");
|
|
5
|
+
const SkuData = require("../../../entities/sku");
|
|
5
6
|
const VtexApi = require("../../../vtex/clients/VtexApi");
|
|
6
7
|
|
|
7
|
-
const
|
|
8
|
-
|
|
8
|
+
const {
|
|
9
|
+
SQS_CLIENT_PRODUCT_QUEUE_URL,
|
|
10
|
+
MIN_PRODUCTS_COUNT = 5
|
|
11
|
+
} = process.env;
|
|
9
12
|
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
const body = JSON.parse(event.body);
|
|
16
|
-
const { an, clients } = body;
|
|
17
|
-
|
|
18
|
-
if (!an || typeof an !== 'string') {
|
|
19
|
-
return { valid: false, error: ApiResponse.response(400, "Field 'an' is required and must be a string") };
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
if (!clients || !Array.isArray(clients) || clients.length === 0) {
|
|
23
|
-
return { valid: false, error: ApiResponse.response(400, "Field 'clients' is required and must be a non-empty array") };
|
|
24
|
-
}
|
|
13
|
+
const suggestedProductMdlz = async (event) => {
|
|
14
|
+
try {
|
|
15
|
+
const { an, clients } = JSON.parse(event.body);
|
|
16
|
+
// return ApiResponse.response(200, { an, clients });
|
|
25
17
|
|
|
26
|
-
|
|
27
|
-
if (!
|
|
28
|
-
return
|
|
18
|
+
const account = await AccountData.getAccountDataByAccountName(an);
|
|
19
|
+
if (!account) {
|
|
20
|
+
return ApiResponse.response(404, "Account not found");
|
|
29
21
|
}
|
|
30
|
-
|
|
31
|
-
|
|
22
|
+
|
|
23
|
+
// Se extraen las propiedades del account requeridas
|
|
24
|
+
const {
|
|
25
|
+
Credentials, ProductSuggestedConfig = {},
|
|
26
|
+
IndexClientRegister,
|
|
27
|
+
Startemail,
|
|
28
|
+
Domainemail
|
|
29
|
+
} = account;
|
|
30
|
+
if (!Credentials?.key || !Credentials?.token) {
|
|
31
|
+
return ApiResponse.response(400, "VTEX credentials not configured for account");
|
|
32
32
|
}
|
|
33
|
-
if (
|
|
34
|
-
return
|
|
33
|
+
if (!IndexClientRegister?.length) {
|
|
34
|
+
return ApiResponse.response(400, "IndexClientRegister not configured for account");
|
|
35
35
|
}
|
|
36
|
-
|
|
37
|
-
|
|
36
|
+
// Se valida que exista el índice 'an'
|
|
37
|
+
if (!IndexClientRegister.includes('an')) {
|
|
38
|
+
return ApiResponse.response(400, "IndexClientRegister must have a 'an' property");
|
|
38
39
|
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return { valid: true, data: body };
|
|
42
|
-
};
|
|
43
|
-
|
|
44
|
-
const validateSkusInVtex = async (skus, vtexApi) => {
|
|
45
|
-
if (!skus || skus.length === 0) {
|
|
46
|
-
Logger.info("No SKUs to validate");
|
|
47
|
-
return { validSkus: [], invalidSkus: [] };
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const skuIds = skus.map(s => s.sku);
|
|
52
|
-
Logger.info(`Requesting VTEX validation for SKUs: [${skuIds.join(', ')}]`);
|
|
53
40
|
|
|
54
|
-
|
|
55
|
-
|
|
41
|
+
// Se obtiene las propiedades de la configuración de ProductSuggestedConfig
|
|
42
|
+
const {
|
|
43
|
+
minProductsCount = MIN_PRODUCTS_COUNT,
|
|
44
|
+
entityName: suggestedProductEntityName = 'suggestedProducts'
|
|
45
|
+
} = ProductSuggestedConfig;
|
|
56
46
|
|
|
57
|
-
|
|
58
|
-
const
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
return { validSkus: [], invalidSkus: skus };
|
|
47
|
+
// Se agrupan los ítems de cada una de las listas por el "sku", de modo que no se tengan items con duplicados.
|
|
48
|
+
const skusList = {};
|
|
49
|
+
for (const client of clients) {
|
|
50
|
+
removeDuplicatesSku(client.productsRegular, skusList);
|
|
51
|
+
removeDuplicatesSku(client.productsIncremental, skusList);
|
|
63
52
|
}
|
|
64
53
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
if (found && found.IsActive !== false) {
|
|
68
|
-
validSkus.push(sku);
|
|
69
|
-
} else {
|
|
70
|
-
invalidSkus.push(sku);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
54
|
+
// Se consulta la información de cada uno de los SKU's
|
|
55
|
+
await getSkuData(skusList, account);
|
|
73
56
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
};
|
|
80
|
-
|
|
81
|
-
const processClient = async (client, vtexApi, accountName, account) => {
|
|
82
|
-
try {
|
|
83
|
-
const { pdv, productsRegular = [], productsIncremental = [] } = client;
|
|
84
|
-
|
|
85
|
-
const suggestedConfig = account?.ProductSuggestedConfig
|
|
86
|
-
const entityName = suggestedConfig?.entityName || 'suggestedProducts';
|
|
87
|
-
const minProductsCount = suggestedConfig?.minProductsCount || DEFAULT_MIN_PRODUCTS_COUNT;
|
|
88
|
-
|
|
89
|
-
const regularValidation = await validateSkusInVtex(productsRegular, vtexApi);
|
|
90
|
-
const incrementalValidation = await validateSkusInVtex(productsIncremental, vtexApi);
|
|
91
|
-
|
|
92
|
-
const hasMinimumValid = regularValidation.validSkus.length >= minProductsCount &&
|
|
93
|
-
incrementalValidation.validSkus.length >= minProductsCount;
|
|
94
|
-
|
|
95
|
-
if (!hasMinimumValid) {
|
|
96
|
-
return {
|
|
97
|
-
pdv,
|
|
98
|
-
success: false,
|
|
99
|
-
error: `Minimum ${minProductsCount} valid SKUs required`,
|
|
100
|
-
regularValid: regularValidation.validSkus.length,
|
|
101
|
-
incrementalValid: incrementalValidation.validSkus.length,
|
|
102
|
-
originalData: client
|
|
103
|
-
};
|
|
57
|
+
// Se valida la información de cada uno de los registros de "clients"
|
|
58
|
+
const validBody = validateClientsData(clients, minProductsCount, IndexClientRegister, an, skusList);
|
|
59
|
+
if (!validBody.success) {
|
|
60
|
+
return ApiResponse.response(207, validBody.data);
|
|
104
61
|
}
|
|
105
62
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
await AWSServices.sendSQSMessage(SQS_CLIENT_PRODUCT_QUEUE_URL, dataSend);
|
|
123
|
-
Logger.info(`Message sent to SQS for client ${pdv}`);
|
|
124
|
-
} catch (sqsError) {
|
|
125
|
-
Logger.error(`Failed to send SQS message for client ${pdv}`, {
|
|
126
|
-
error: sqsError.message
|
|
63
|
+
do {
|
|
64
|
+
// Se extrae las primeras filas de la lista para ejecutar las Promises
|
|
65
|
+
let dataToProcess = clients.splice(0, 10);
|
|
66
|
+
await Promise.allSettled(dataToProcess.map(client => {
|
|
67
|
+
return new Promise((resolve, reject) => {
|
|
68
|
+
const sqsData = {
|
|
69
|
+
...client,
|
|
70
|
+
an,
|
|
71
|
+
parentAccount: account?.ParentAccountName,
|
|
72
|
+
key: account?.Credentials?.key,
|
|
73
|
+
token: account?.Credentials?.token,
|
|
74
|
+
suggestedProductEntityName,
|
|
75
|
+
Startemail,
|
|
76
|
+
Domainemail
|
|
77
|
+
}
|
|
78
|
+
AWSServices.sendMessageToSqs(SQS_CLIENT_PRODUCT_QUEUE_URL, sqsData).then(resolve).catch(reject);
|
|
127
79
|
});
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
Logger.warn(`SQS URL not configured`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
return {
|
|
134
|
-
pdv,
|
|
135
|
-
success: true,
|
|
136
|
-
validRegular: regularValidation.validSkus.length,
|
|
137
|
-
validIncremental: incrementalValidation.validSkus.length,
|
|
138
|
-
invalidRegular: regularValidation.invalidSkus.length,
|
|
139
|
-
invalidIncremental: incrementalValidation.invalidSkus.length,
|
|
140
|
-
warnings: regularValidation.invalidSkus.length > 0 || incrementalValidation.invalidSkus.length > 0
|
|
141
|
-
? "Some SKUs were excluded as they don't exist in VTEX" : null
|
|
142
|
-
};
|
|
80
|
+
})).catch(Logger.error);
|
|
81
|
+
} while (clients.length);
|
|
143
82
|
|
|
83
|
+
return ApiResponse.response(200, { message: 'Message accepted!' });
|
|
144
84
|
} catch (error) {
|
|
145
|
-
Logger.error(
|
|
85
|
+
Logger.error("Critical error in suggestedProductMdlz", {
|
|
146
86
|
error: error.message,
|
|
147
|
-
stack: error.stack
|
|
148
|
-
pdv: client?.pdv
|
|
87
|
+
stack: error.stack
|
|
149
88
|
});
|
|
150
|
-
|
|
89
|
+
return ApiResponse.response(500, "Internal server error");
|
|
151
90
|
}
|
|
152
91
|
};
|
|
153
92
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
93
|
+
/**
|
|
94
|
+
* Valida la información de la lista de clientes
|
|
95
|
+
* @param {Object[]} clients Lista de clientes
|
|
96
|
+
* @param {Number} minProductsCount Cantidad mínima requerida para el producto sugerido.
|
|
97
|
+
* @param {String[]} indexClientRegister Lista de índices para el Id del cliente.
|
|
98
|
+
* @param {Object} skusList Objeto donde se consolida la lista de SKU´s.
|
|
99
|
+
* @returns
|
|
100
|
+
*/
|
|
101
|
+
function validateClientsData(clients, minProductsCount, indexClientRegister, an, skusList) {
|
|
102
|
+
const response = { success: true }, notProcessedItems = [];
|
|
157
103
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
104
|
+
for (const client of clients) {
|
|
105
|
+
let remain = [], clientId = '';
|
|
106
|
+
for (let prop of indexClientRegister) {
|
|
107
|
+
if (prop == 'an') {
|
|
108
|
+
clientId += an;
|
|
109
|
+
} else {
|
|
110
|
+
if (client.hasOwnProperty(prop)) {
|
|
111
|
+
clientId += client[prop] ?? '';
|
|
112
|
+
} else {
|
|
113
|
+
remain.push(prop);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
161
116
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
117
|
+
if (remain.length) {
|
|
118
|
+
client.error = `The properties ${remain.join(',')} are required`;
|
|
119
|
+
notProcessedItems.push(client);
|
|
120
|
+
continue;
|
|
165
121
|
}
|
|
166
122
|
|
|
167
|
-
|
|
168
|
-
|
|
123
|
+
// Se valida si las propiedades existen en cada cliente
|
|
124
|
+
if (!client?.productsRegular || !client?.productsIncremental) {
|
|
125
|
+
client.error = `Validate the following properties productsRegular and productsIncremental are required`;
|
|
126
|
+
notProcessedItems.push(client);
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
169
129
|
|
|
170
|
-
|
|
171
|
-
if (
|
|
172
|
-
|
|
130
|
+
// Se valida si la longitud de alguno de los ítems del cliente es menos al número mínimo de productos
|
|
131
|
+
if (client.productsRegular?.length < minProductsCount || client.productsIncremental?.length < minProductsCount) {
|
|
132
|
+
client.error = `Validate the following properties productsRegular and productsIncremental minimum ${minProductsCount} elements`;
|
|
133
|
+
notProcessedItems.push(client);
|
|
134
|
+
continue;
|
|
173
135
|
}
|
|
174
136
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
);
|
|
137
|
+
client.clientId = clientId;
|
|
138
|
+
setItemInformation(client.productsRegular, 'REGULAR', skusList);
|
|
139
|
+
setItemInformation(client.productsIncremental, 'INCREMENTAL', skusList);
|
|
140
|
+
}
|
|
180
141
|
|
|
181
|
-
|
|
182
|
-
|
|
142
|
+
// Si hay ítems no procesados, se retorna el mensaje de error
|
|
143
|
+
if (notProcessedItems.length) {
|
|
144
|
+
response.success = false;
|
|
145
|
+
response.data = {
|
|
146
|
+
message: 'Not all items were processed.',
|
|
147
|
+
notProcessedItems
|
|
148
|
+
};
|
|
149
|
+
}
|
|
183
150
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
151
|
+
return response;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Consulta la información de los SKU definidos en la petición.
|
|
156
|
+
* @param {*} skusList Lista de SKU's.
|
|
157
|
+
* @param {*} account Información del account sobre el cual se realiza la consulta.
|
|
158
|
+
*/
|
|
159
|
+
async function getSkuData(skusList, account) {
|
|
160
|
+
let skuRefUsed = account?.SkuRefUsed;
|
|
161
|
+
if (typeof skuRefUsed !== "boolean") {
|
|
162
|
+
skuRefUsed = true;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
// Instancia para consumo de la API de VTEX
|
|
166
|
+
const vtexApi = new VtexApi(
|
|
167
|
+
account.AccountName,
|
|
168
|
+
account.Credentials.key,
|
|
169
|
+
account.Credentials.token
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
const items = Object.keys(skusList);
|
|
173
|
+
do {
|
|
174
|
+
// Se extrae las primeras filas de la lista para ejecutar las Promises
|
|
175
|
+
let rowsData = items.splice(0, 10);
|
|
176
|
+
await Promise.allSettled(rowsData.map(sku => {
|
|
177
|
+
return new Promise((resolve, reject) => {
|
|
178
|
+
// Se consulta el SKU en MonalisaV2
|
|
179
|
+
getSkuInformation(sku, account, skuRefUsed).then(skuId => {
|
|
180
|
+
// Se consulta en VTEX información del producto
|
|
181
|
+
skusList[sku].SkuId = skuId;
|
|
182
|
+
vtexApi.getDetailSkuById([skuId]).then(res => {
|
|
183
|
+
const { IsActive, ProductIsVisible } = res[0];
|
|
184
|
+
skusList[sku].IsActive = IsActive;
|
|
185
|
+
skusList[sku].ProductIsVisible = ProductIsVisible;
|
|
186
|
+
resolve(sku);
|
|
187
|
+
}).catch(reject);
|
|
188
|
+
}).catch(reject);
|
|
189
|
+
});
|
|
190
|
+
})).catch(Logger.error);
|
|
191
|
+
} while (items.length);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Consulta de información del sku en MonalisaV2.
|
|
196
|
+
* @param {String} sku SKU del producto.
|
|
197
|
+
* @param {Object} account Información del account en MonalisaV2.
|
|
198
|
+
* @param {Boolean} skuRefUsed
|
|
199
|
+
* @returns
|
|
200
|
+
*/
|
|
201
|
+
async function getSkuInformation(sku, account, skuRefUsed) {
|
|
202
|
+
return new Promise((resolve, reject) => {
|
|
203
|
+
if (account?.ValidateSkuPriceInventoryInProcess === true) {
|
|
204
|
+
resolve(sku);
|
|
205
|
+
} else {
|
|
206
|
+
if (skuRefUsed) {
|
|
207
|
+
SkuData.getSkuByParentAccountNameAndSku(account?.ParentAccountName, sku).then(res => {
|
|
208
|
+
if (res?.SkuId) {
|
|
209
|
+
const { SkuId } = res;
|
|
210
|
+
resolve(SkuId);
|
|
211
|
+
} else {
|
|
212
|
+
reject(new Error(`SKU ${sku} not found`));
|
|
213
|
+
}
|
|
214
|
+
}).catch(reject);
|
|
215
|
+
} else {
|
|
216
|
+
SkuData.getSkuByParentAccountNameAndEan(account?.ParentAccountName, sku).then(res => {
|
|
217
|
+
if (res?.[0]) {
|
|
218
|
+
const { SkuId } = res[0];
|
|
219
|
+
resolve(SkuId);
|
|
220
|
+
} else {
|
|
221
|
+
reject(new Error(`SKU ${sku} not found`));
|
|
222
|
+
}
|
|
223
|
+
}).catch(reject);
|
|
204
224
|
}
|
|
205
225
|
}
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
/**
|
|
230
|
+
* Recorre la lista de productos para eliminar registros con "sku" duplicado.
|
|
231
|
+
* @param {Object[]} items Lista de ítems
|
|
232
|
+
* @param {Object} skusList Objeto donde se consolida la lista de SKU´s.
|
|
233
|
+
*/
|
|
234
|
+
function removeDuplicatesSku(items, skusList) {
|
|
235
|
+
const indexes = {};
|
|
236
|
+
// Se recorre la lista en orden invertido, para que en caso de encontrar registros con "sku" duplicado se eliminen y quede solamente el elemento de la última posición.
|
|
237
|
+
if (items?.length) {
|
|
238
|
+
for (let i = items.length - 1; i >= 0; i--) {
|
|
239
|
+
const { sku } = items[i];
|
|
240
|
+
!skusList.hasOwnProperty(sku) && (skusList[sku] = {});
|
|
241
|
+
if (indexes.hasOwnProperty(sku)) {
|
|
242
|
+
items.splice(i, 1);
|
|
243
|
+
} else {
|
|
244
|
+
indexes[sku] = true;
|
|
212
245
|
}
|
|
213
|
-
|
|
214
|
-
message: "All clients processed successfully",
|
|
215
|
-
processedClients: results.length,
|
|
216
|
-
results
|
|
217
|
-
};
|
|
218
|
-
|
|
219
|
-
const statusCode = failedClients.length > 0 ? 207 : 200;
|
|
220
|
-
Logger.info(`Process completed with status ${statusCode}, processed: ${results.length}, failed: ${failedClients.length}`);
|
|
221
|
-
|
|
222
|
-
return ApiResponse.response(statusCode, response);
|
|
223
|
-
|
|
224
|
-
} catch (error) {
|
|
225
|
-
Logger.error("Critical error in suggestedProductMdlz", {
|
|
226
|
-
error: error.message,
|
|
227
|
-
stack: error.stack
|
|
228
|
-
});
|
|
229
|
-
return ApiResponse.response(500, "Internal server error");
|
|
246
|
+
}
|
|
230
247
|
}
|
|
231
|
-
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Organiza la información de los SKU en cada una de las listas de productos del cliente.
|
|
252
|
+
* @param {Object[]} list Lista de ítems de producto sugerido.
|
|
253
|
+
* @param {Object} skusList Objeto donde se consolida la información de los SKU.
|
|
254
|
+
* @param {Object} skusList Objeto donde se consolida la información de los SKU que no fueron encontrados.
|
|
255
|
+
*/
|
|
256
|
+
function setItemInformation(list, productType, skusList) {
|
|
257
|
+
for (let item of list) {
|
|
258
|
+
item.type = productType;
|
|
259
|
+
const { sku } = item;
|
|
260
|
+
// Se elimina la propiedad "sku", para luego insertar este valor en la entidad de productos sugeridos
|
|
261
|
+
delete item.sku;
|
|
262
|
+
if (skusList?.[sku]) {
|
|
263
|
+
const { SkuId, IsActive, ProductIsVisible } = skusList[sku];
|
|
264
|
+
item.refId = sku;
|
|
265
|
+
item.skuId = SkuId;
|
|
266
|
+
if (IsActive) {
|
|
267
|
+
item.status = ProductIsVisible ? 'active' : 'not_visible';
|
|
268
|
+
} else {
|
|
269
|
+
item.status = 'inactive';
|
|
270
|
+
}
|
|
271
|
+
} else {
|
|
272
|
+
item.status = 'invalid';
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
232
276
|
|
|
233
277
|
module.exports = suggestedProductMdlz;
|