dcos-core-monalisav2-latam 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. package/README.md +130 -0
  2. package/index.js +350 -0
  3. package/package.json +52 -0
  4. package/src/auth/handler.js +3 -0
  5. package/src/common/MondelezCastOrder.js +449 -0
  6. package/src/common/utils/AuthSecurity.js +46 -0
  7. package/src/common/utils/account-error-handler.js +279 -0
  8. package/src/common/utils/account-error-helper.js +231 -0
  9. package/src/common/utils/account-properties-handler.js +355 -0
  10. package/src/common/utils/api-response.js +62 -0
  11. package/src/common/utils/aws-services.js +186 -0
  12. package/src/common/utils/constants/account-error-codes.json +801 -0
  13. package/src/common/utils/constants.js +37 -0
  14. package/src/common/utils/convert/MondelezClientsItemsCast.js +52 -0
  15. package/src/common/utils/convert/MondelezInventoryItemsCast.js +15 -0
  16. package/src/common/utils/convert/MondelezOrderStatusCast.js +34 -0
  17. package/src/common/utils/convert/MondelezPricesItemsCast.js +37 -0
  18. package/src/common/utils/cron-ftp-get.js +143 -0
  19. package/src/common/utils/data-tables-helper.js +213 -0
  20. package/src/common/utils/date-range-calculator.js +113 -0
  21. package/src/common/utils/delay.js +17 -0
  22. package/src/common/utils/ftp-sftp.js +320 -0
  23. package/src/common/utils/logger.js +126 -0
  24. package/src/common/utils/nodemailerLib.js +61 -0
  25. package/src/common/utils/product-unit-converter.js +168 -0
  26. package/src/common/utils/schemas-utils.js +101 -0
  27. package/src/common/utils/seller-email-sharing-service.js +441 -0
  28. package/src/common/utils/sftp-utils.js +202 -0
  29. package/src/common/utils/status.js +15 -0
  30. package/src/common/utils/util.js +236 -0
  31. package/src/common/utils/validate-state-order.js +35 -0
  32. package/src/common/utils/validateProviders.js +67 -0
  33. package/src/common/utils/validation-data.js +45 -0
  34. package/src/common/utils/vtex/save-hooks.js +65 -0
  35. package/src/common/utils/vtex/save-schemas.js +65 -0
  36. package/src/common/utils/vtex-hook-handler.js +71 -0
  37. package/src/common/validation/AccountCoordinatesValidation.js +350 -0
  38. package/src/common/validation/GeneralErrorValidation.js +11 -0
  39. package/src/common/validation/MainErrorValidation.js +8 -0
  40. package/src/entities/account.js +639 -0
  41. package/src/entities/clients.js +104 -0
  42. package/src/entities/controlprice.js +196 -0
  43. package/src/entities/controlstock.js +206 -0
  44. package/src/entities/cron.js +77 -0
  45. package/src/entities/cronjob.js +71 -0
  46. package/src/entities/orders.js +195 -0
  47. package/src/entities/sftp-inbound.js +88 -0
  48. package/src/entities/sku.js +220 -0
  49. package/src/entities/taxpromotion.js +249 -0
  50. package/src/functions/account/account-get.js +262 -0
  51. package/src/functions/account/account-handler.js +299 -0
  52. package/src/functions/account/clients.js +10 -0
  53. package/src/functions/account/index.js +208 -0
  54. package/src/functions/actions/save-promotions-order-history.js +324 -0
  55. package/src/functions/affiliates/affiliates-hook-consumer.js +87 -0
  56. package/src/functions/affiliates/affiliates-hook-producer.js +45 -0
  57. package/src/functions/clients/clients-audience.js +62 -0
  58. package/src/functions/clients/clients-consumer.js +648 -0
  59. package/src/functions/clients/clients-producer.js +362 -0
  60. package/src/functions/clients/clients-suggested-product-consumer.js +166 -0
  61. package/src/functions/clients/helpers/suggested-product-mdlz.js +233 -0
  62. package/src/functions/clients_peru/email.html +129 -0
  63. package/src/functions/clients_peru/splitfile.js +357 -0
  64. package/src/functions/clients_peru/updateClients.js +1334 -0
  65. package/src/functions/clients_peru/utils.js +243 -0
  66. package/src/functions/cronjobs/cron-jobs-manager.js +40 -0
  67. package/src/functions/cronjobs/cron-jobs.js +171 -0
  68. package/src/functions/crons/cron.js +39 -0
  69. package/src/functions/distributors/distributor-handler.js +81 -0
  70. package/src/functions/distributors/distributor.js +535 -0
  71. package/src/functions/distributors/index.js +60 -0
  72. package/src/functions/financialpolicy/assign-financialpolicy.js +111 -0
  73. package/src/functions/financialpolicy/get-financialpolicy.js +91 -0
  74. package/src/functions/financialpolicy/index.js +28 -0
  75. package/src/functions/inventory/catalog-sync-consumer.js +17 -0
  76. package/src/functions/inventory/catalog-sync-handler.js +311 -0
  77. package/src/functions/inventory/inventory-consumer.js +119 -0
  78. package/src/functions/inventory/inventory-producer.js +197 -0
  79. package/src/functions/multiPresentation/multipre-queue.js +155 -0
  80. package/src/functions/multiPresentation/multipres.js +459 -0
  81. package/src/functions/nodeflow/index.js +83 -0
  82. package/src/functions/nodeflow/nodeflow-cron.js +200 -0
  83. package/src/functions/nodeflow/nodeflow-pub.js +203 -0
  84. package/src/functions/nodeflow/nodeflow-pvt.js +266 -0
  85. package/src/functions/notifications/download-leads-handler.js +67 -0
  86. package/src/functions/notifications/new-leads-notification-consumer.js +17 -0
  87. package/src/functions/notifications/new-leads-notification-handler.js +359 -0
  88. package/src/functions/notifications/order-status-notification-handler.js +482 -0
  89. package/src/functions/notifications/promotion-notification-handler.js +193 -0
  90. package/src/functions/orders/index.js +32 -0
  91. package/src/functions/orders/orders-cancel-handler.js +74 -0
  92. package/src/functions/orders/orders-handler.js +280 -0
  93. package/src/functions/orders/orders-hook-consumer.js +137 -0
  94. package/src/functions/orders/orders-hook-producer.js +170 -0
  95. package/src/functions/orders/orders-notifications-handler.js +137 -0
  96. package/src/functions/orders/orders-status-consumer.js +461 -0
  97. package/src/functions/orders/orders-status-producer.js +443 -0
  98. package/src/functions/prices/index.js +75 -0
  99. package/src/functions/prices/prices-consumer.js +236 -0
  100. package/src/functions/prices/prices-producer.js +323 -0
  101. package/src/functions/prices/promotion-and-tax.js +1284 -0
  102. package/src/functions/routesflow/assign-routeflow-queue.js +77 -0
  103. package/src/functions/schemas/vtex/handle-schemas.js +102 -0
  104. package/src/functions/security/process_gas.js +221 -0
  105. package/src/functions/security/security-handler.js +950 -0
  106. package/src/functions/sftp/sftp-consumer.js +453 -0
  107. package/src/functions/sftpIntegrations/processes/redirectServices.js +184 -0
  108. package/src/functions/sftpIntegrations/processes/validateFileSchema.js +226 -0
  109. package/src/functions/sftpIntegrations/schemas/credential-schema.js +123 -0
  110. package/src/functions/sftpIntegrations/schemas/record-schema.js +131 -0
  111. package/src/functions/sftpIntegrations/schemas/sftp_required_fields.json +3 -0
  112. package/src/functions/sftpIntegrations/sftp-config-producer.js +112 -0
  113. package/src/functions/sftpIntegrations/sftp-consumer.js +700 -0
  114. package/src/functions/sftpIntegrations/test/validateFile.test.js +122 -0
  115. package/src/functions/sftpIntegrations/utils/connect-dynamo.js +29 -0
  116. package/src/functions/sftpIntegrations/utils/split-data.js +25 -0
  117. package/src/functions/utils/index.js +130 -0
  118. package/src/functions/vtex/vtex-helpers.js +694 -0
  119. package/src/integrations/accountErrors/AccountErrorManager.js +437 -0
  120. package/src/integrations/audience/Audience.js +70 -0
  121. package/src/integrations/financialPolicy/FinancialPolicyApi.js +377 -0
  122. package/src/integrations/index.js +0 -0
  123. package/src/integrations/mobilvendor/MobilvendorApi.js +405 -0
  124. package/src/integrations/productmultipresentation/ProductMultiPresentation.js +200 -0
  125. package/src/mdlz/auth/SecretManagerApi.js +77 -0
  126. package/src/mdlz/client/MdlzApi.js +70 -0
  127. package/src/vtex/clients/ProvidersApi.js +51 -0
  128. package/src/vtex/clients/VtexApi.js +511 -0
  129. package/src/vtex/models/VtexOrder.js +87 -0
@@ -0,0 +1,362 @@
1
+ const AWSServices = require("../../common/utils/aws-services");
2
+ const ApiResponse = require("../../common/utils/api-response");
3
+
4
+ const ClientData = require("../../entities/clients");
5
+ const AccountData = require("../../entities/account");
6
+
7
+ const Logger = require("../../common/utils/logger");
8
+ const VtexApi = require("../../vtex/clients/VtexApi");
9
+
10
+ const suggestedProductMdlz = require("./helpers/suggested-product-mdlz");
11
+
12
+ const LIMIT_ITEMS_TO_PROCESS = process?.env?.LIMIT_ITEMS_TO_PROCESS || 150;
13
+ const SQS_CLIENT_QUEUE_URL = process.env.SQS_CLIENT_QUEUE_URL;
14
+
15
+ /**
16
+ *
17
+ * @param {import("aws-lambda").APIGatewayProxyEvent} event
18
+ * @returns
19
+ */
20
+ const producer = async (event) => {
21
+ // Se extrae el method y el path del endpoint asociado a la función lambda
22
+ const { httpMethod, resource } = event;
23
+
24
+ // Si el endpoint corresponde a la creación de registros
25
+ if (httpMethod == 'POST' && resource == '/clients') {
26
+ return handleCreateClient(event);
27
+ }
28
+
29
+ // Si el endpoint corresponde a la consulta de registros
30
+ if (httpMethod == 'GET' && resource == '/clients/{an}/{method}') {
31
+ return handleFindClients(event);
32
+ }
33
+
34
+ if (httpMethod == 'POST' && resource == '/internal_process/suggestedProductMdlz') {
35
+ return suggestedProductMdlz(event);
36
+ }
37
+
38
+ return ApiResponse.response(400, 'Resource not available');
39
+ };
40
+
41
+ const handleCreateClient = async (event) => {
42
+ let statusCode = 200, message;
43
+
44
+ if (!event.body) {
45
+ return ApiResponse.response(400, "No body was found");
46
+ }
47
+
48
+ try {
49
+ Logger.log(JSON.stringify(event));
50
+ const request = JSON.parse(event.body);
51
+
52
+ const clientId = event?.requestContext?.authorizer?.claims?.client_id;
53
+ const scope = event?.requestContext?.authorizer?.claims?.scope;
54
+ const accountName = request?.an;
55
+ const clients = request?.clients;
56
+
57
+ if (clients?.length >= LIMIT_ITEMS_TO_PROCESS) {
58
+ Logger.error("Clients size INVALID: ", clients?.length);
59
+ return errorResponse(404, "Clients limit of items exceeded");
60
+ }
61
+
62
+ if (!event?.internalService) {
63
+ if (!scope?.includes("admin")) {
64
+ const validateAccount = await ClientData.validateAccount(clientId, accountName);
65
+ if (!validateAccount) {
66
+ Logger.error("Account not found: ", accountName, ". ClientId: ", clientId);
67
+ return ApiResponse.response(404, "Account not found");
68
+ }
69
+ }
70
+ }
71
+ const account = event?.accountData && event?.internalService === true ? event?.accountData : await AccountData.getAccountDataByAccountName(accountName);
72
+ const parentAccount = await AccountData.getAccountDataByAccountName(account?.ParentAccountName);
73
+ Logger.setAccount(account);
74
+ Logger.debug("Account-Data: ", account);
75
+ Logger.debug("Parent-Account-Data: ", parentAccount);
76
+
77
+ //for each client
78
+ let notProcessed = [];
79
+
80
+ for (let item of clients) {
81
+ try {
82
+ Logger.debug("Object client to process: ", item);
83
+ const priceTableParam = item?.additional_information?.priceTable;
84
+ let priceTableVtex = null;
85
+ if (priceTableParam && priceTableParam !== "") {
86
+ priceTableVtex = account?.PriceList?.[priceTableParam];
87
+ item.additional_information.priceTable = priceTableVtex ?? `L${priceTableParam}`;
88
+ }
89
+
90
+ const dataSend = {
91
+ an: accountName,
92
+ parentAccount: parentAccount?.AccountName,
93
+ key: parentAccount?.Credentials?.key,
94
+ token: parentAccount?.Credentials?.token,
95
+ ...item,
96
+ };
97
+
98
+ // start valid fields requerid
99
+ let validRequired = validFieldsRequerid({ item: item, config: account });
100
+ if (validRequired?.valid === false) {
101
+ item.error = validRequired?.error;
102
+ notProcessed = [...notProcessed, item];
103
+ } else if (validDataClientFirstLevel({ data: item, ParametersClusterClient: account?.ParametersClusterClient }) === false) {
104
+ item.error = `validate the following properties ${JSON.stringify(account?.ParametersClusterClient)}`;
105
+ notProcessed = [...notProcessed, item];
106
+ } else {
107
+ Logger.debug("Datasend for SQS: ", dataSend);
108
+ await AWSServices.sendSQSMessage(SQS_CLIENT_QUEUE_URL, dataSend);
109
+ }
110
+
111
+ } catch (error) {
112
+ Logger.error("Error while client was processing: ", error);
113
+ notProcessed.push(item);
114
+ }
115
+ }
116
+
117
+ if (notProcessed.length > 0) {
118
+ Logger.debug("Not Processed: ", notProcessed);
119
+ const message = {
120
+ message: "Not all items were processed.",
121
+ notProcessedItems: notProcessed,
122
+ };
123
+ return ApiResponse.response(207, message);
124
+ }
125
+
126
+ message = "Message accepted!";
127
+ } catch (error) {
128
+ Logger.error("Error clients producer: ", error);
129
+ statusCode = 500;
130
+ }
131
+ return ApiResponse.response(statusCode, message);
132
+ };
133
+
134
+ const handleFindClients = async (event) => {
135
+ const { headers, pathParameters, queryStringParameters, internalService, requestContext } = event;
136
+ const { an: accountName, method } = pathParameters;
137
+
138
+ const { client_id: clientId, scope } = requestContext?.authorizer?.claims ?? {};
139
+
140
+ if (!internalService) {
141
+ if (!scope?.includes("admin")) {
142
+ const validateAccount = await ClientData.validateAccount(clientId, accountName);
143
+ if (!validateAccount) {
144
+ Logger.error("Account not found: ", accountName, ". ClientId: ", clientId);
145
+ return ApiResponse.response(404, "Account not found");
146
+ }
147
+ }
148
+ }
149
+
150
+ // Se valida el valor del método de búsqueda de registros
151
+ if (!['search', 'scroll'].includes(method)) {
152
+ return ApiResponse.response(400, `Method '${method}' is not allowed`);
153
+ }
154
+
155
+ // Se consulta los datos del account en DynamoDB
156
+ let { accountData = null } = event;
157
+ if (!(accountData && internalService)) {
158
+ accountData = await AccountData.getAccountDataByAccountName(accountName);
159
+ }
160
+
161
+ // Se obtiene los datos del account "padre"
162
+ const parentAccount = await AccountData.getAccountDataByAccountName(accountData?.ParentAccountName);
163
+ Logger.setAccount(accountData);
164
+
165
+ const { AccountName, Credentials, AcronymClientVtex } = parentAccount;
166
+ const { key, token } = Credentials;
167
+
168
+ Logger.debug('AccountName', AccountName);
169
+ Logger.debug('Method', method);
170
+
171
+ const apiClient = new VtexApi(AccountName, key, token);
172
+ let requestUrl = `/dataentities/${AcronymClientVtex}/${method}`, options = {
173
+ // Propiedad de AXIOS que permite retornar una respuesta sin importar el código HTTP retornado en la petición
174
+ validateStatus: () => true
175
+ };
176
+
177
+ // En caso que venga en los Query Params el atributo "an", se elimina el valor enviado
178
+ if (queryStringParameters.hasOwnProperty('an')) {
179
+ delete queryStringParameters.an;
180
+ }
181
+
182
+ // Se establece la Query Param "_fields" para indicar los campos a retornar en la consulta, en caso que este parámetro no venga
183
+ if (!queryStringParameters.hasOwnProperty('_fields') || !queryStringParameters['_fields']) {
184
+ queryStringParameters['_fields'] = '_all';
185
+ }
186
+
187
+ // Se verifica la condicional definida en la consulta
188
+ if (queryStringParameters.hasOwnProperty('_where')) {
189
+ // Se valida que en el Query Params "_where" no tenga consulta por el campo "an"
190
+ let match = queryStringParameters['_where'].match(/\b(an)\b/g)
191
+ if (match) {
192
+ queryStringParameters['_where'] = `(an=${accountName})`;
193
+ } else {
194
+ queryStringParameters['_where'] = `${queryStringParameters['_where']} AND (an=${accountName})`
195
+ }
196
+ } else {
197
+ queryStringParameters['_where'] = `(an=${accountName})`;
198
+ }
199
+
200
+ if (method == 'search') {
201
+ requestUrl = setQueryParams(requestUrl, queryStringParameters);
202
+
203
+ // Se obtiene el parámetro de Headers
204
+ const { 'rest-range': restRange = 'resources=0-100' } = headers;
205
+ Logger.debug('Header REST-Range', restRange);
206
+ options.headers = { 'REST-Range': restRange };
207
+ }
208
+
209
+ if (method == 'scroll') {
210
+ // En caso que a la petición se envíe el Query Param "_token", se establece solamente dicha propiedad en la petición a VTEX
211
+ if (queryStringParameters?.token) {
212
+ requestUrl = setQueryParams(requestUrl, { '_token': queryStringParameters.token });
213
+ } else {
214
+ requestUrl = setQueryParams(requestUrl, queryStringParameters);
215
+ }
216
+ }
217
+
218
+ // Se ejecuta la consulta a VTEX
219
+ let resultQuery = await apiClient.fetch(requestUrl, options, false);
220
+ if (resultQuery) {
221
+ // Se extrae el código de respuesta, cuerpo y cabecera de la respuesta de la petición
222
+ const { status, data, headers } = resultQuery;
223
+ const {
224
+ 'x-vtex-md-token': xVtexMdToken,
225
+ 'rest-content-range': restContentRange
226
+ } = headers;
227
+
228
+ // Se retorna la respuesta proveniente de VTEX
229
+ return ApiResponse.response(status, data, {
230
+ ...(xVtexMdToken && { 'X-VTEX-MD-TOKEN': xVtexMdToken }),
231
+ ...(restContentRange && { 'REST-Content-Range': restContentRange })
232
+ });
233
+ } else {
234
+ return ApiResponse.response(400, 'Error en handleFindClients');
235
+ }
236
+ };
237
+
238
+ /**
239
+ * Metodo que valida campos con un texto espesifico en su valor.
240
+ **/
241
+ const validDataClientFirstLevel = ({ data = null, ParametersClusterClient = null }) => {
242
+ if (data && ParametersClusterClient && typeof ParametersClusterClient == 'object') {
243
+ let valid = true;
244
+ for (let index in ParametersClusterClient) {
245
+ try {
246
+ if (data[index] && typeof ParametersClusterClient[index] == 'object' && !ParametersClusterClient[index].includes(data[index])) {
247
+ valid = false;
248
+ }
249
+ } catch (e) {
250
+ Logger.error(e);
251
+ valid = false;
252
+ }
253
+ }
254
+
255
+ return valid;
256
+ } else {
257
+ return true;
258
+ }
259
+ };
260
+
261
+ /**
262
+ * Metodo para realizar validacion de campos requeridos de clientes
263
+ * @param item -- objeto del registro cliente enviado
264
+ * @param config -- objeto con la informacion de configuracion del account
265
+ **/
266
+ const validFieldsRequerid = ({
267
+ item = null,
268
+ config = null
269
+ }) => {
270
+
271
+ // Valicadion lista de parametros requeridos por la marca para insertar registro a entidad de datos
272
+ if (config?.ParametersRequiredClient && typeof config?.ParametersRequiredClient === 'object') {
273
+ for (let key in config?.ParametersRequiredClient) {
274
+ if (config?.ParametersRequiredClient[key] && config?.ParametersRequiredClient[key] != '') {
275
+ try {
276
+ let exitRegister = null;
277
+ eval(`exitRegister= ${config?.ParametersRequiredClient[key]}`);
278
+
279
+ if (!exitRegister || exitRegister == '') {
280
+ return {
281
+ valid: false,
282
+ error: `validate the following properties ${config?.ParametersRequiredClient[key]} is Required.`
283
+ };
284
+ }
285
+ } catch (error) {
286
+ Logger.error(error);
287
+ return {
288
+ valid: false,
289
+ error: "Error configurations ParametersRequiredClient"
290
+ };
291
+ }
292
+ }
293
+ }
294
+ }
295
+
296
+ // validacion parametor requiridos index
297
+ if (config?.IndexClientRegister && typeof config?.IndexClientRegister === 'object' && config?.IndexClientRegister?.length > 0) {
298
+ for (let key in config?.IndexClientRegister) {
299
+ if (config?.IndexClientRegister[key] && config?.IndexClientRegister[key] != '') {
300
+ try {
301
+ let valueIndex = config?.IndexClientRegister[key], validIndex = false;
302
+
303
+ if (valueIndex === 'an') {
304
+ validIndex = true;
305
+ } else if (item[valueIndex] && item[valueIndex] != '' && typeof item[valueIndex] === 'string') {
306
+ validIndex = true;
307
+ } else if (item?.address[valueIndex] && item?.address[valueIndex] != '' && typeof item?.address[valueIndex] === 'string') {
308
+ validIndex = true;
309
+ } else if (item?.additional_information[valueIndex] && item?.additional_information[valueIndex] != '' && typeof item?.additional_information[valueIndex] === 'string') {
310
+ validIndex = true;
311
+ }
312
+
313
+ if (validIndex === false) {
314
+ return {
315
+ valid: false,
316
+ error: `validate the following properties ${valueIndex} is requerid and type string for create index.`
317
+ };
318
+ }
319
+
320
+ } catch (error) {
321
+ Logger.error(error);
322
+ return {
323
+ valid: false,
324
+ error: "Error configurations ParametersRequeridClient"
325
+ };
326
+ }
327
+ }
328
+ }
329
+ } else {
330
+ return {
331
+ valid: false,
332
+ error: "Error configurations IndexClientRegister is requerid"
333
+ };
334
+ }
335
+
336
+ return {
337
+ valid: true,
338
+ error: null
339
+ };
340
+ };
341
+
342
+ const setQueryParams = (url, queryParams, allowEmpty = true) => {
343
+ let queryText = ''
344
+ for (let key in queryParams) {
345
+ if (key && (allowEmpty || !queryParams[key])) {
346
+ if (queryText) {
347
+ queryText += '&'
348
+ }
349
+ queryText += `${key}=${queryParams[key]}`
350
+ }
351
+ }
352
+
353
+ if (queryText) {
354
+ queryText = '?' + queryText
355
+ }
356
+
357
+ return url + queryText
358
+ };
359
+
360
+ module.exports = {
361
+ producer
362
+ };
@@ -0,0 +1,166 @@
1
+ const Logger = require("../../common/utils/logger");
2
+ const VtexApi = require("../../vtex/clients/VtexApi");
3
+
4
+ const DATA_ENTITY_CLIENT_ACRONYM = process.env.DATA_ENTITY_CLIENT_ACRONYM || 'CT';
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
+ const consumer = async (event) => {
108
+ try {
109
+ const records = event.Records || [];
110
+
111
+ for (const record of records) {
112
+ const messageBody = JSON.parse(record.body);
113
+ const {
114
+ an,
115
+ parentAccount,
116
+ key,
117
+ token,
118
+ pdv,
119
+ productsRegular,
120
+ productsIncremental,
121
+ originalData,
122
+ nameEntityProductSuggested
123
+ } = messageBody;
124
+
125
+ Logger.log("Processing suggested products for client", { pdv, an });
126
+
127
+ const vtexApi = new VtexApi(parentAccount, key, token);
128
+
129
+ const userId = await getClientUserId(vtexApi, pdv, an);
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
+ }
143
+
144
+ for (const sku of productsIncremental) {
145
+ const status = await getCatalogProductStatus(vtexApi, sku.sku);
146
+ incrementalProducts.push(buildProductJson(sku, status, sku.quantity, 'incremental', sku.priority));
147
+ }
148
+
149
+ await saveSuggestedProducts(vtexApi, userId, {
150
+ regular: regularProducts,
151
+ incremental: incrementalProducts
152
+ }, an, nameEntityProductSuggested);
153
+
154
+ await saveOriginalDataToClient(vtexApi, pdv, originalData, an);
155
+
156
+ Logger.log("Suggested products processed successfully", { pdv, userId });
157
+ }
158
+ } catch (error) {
159
+ Logger.error("Error in suggested products consumer", error);
160
+ throw error;
161
+ }
162
+ };
163
+
164
+ module.exports = {
165
+ consumer
166
+ };