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,243 @@
1
+ const { SQS } = require('aws-sdk');
2
+ const { AxiosError } = require('axios');
3
+ const Logger = require('../../common/utils/logger');
4
+
5
+ /**
6
+ * Envia un mensaje a una cola SQS.
7
+ * @param {string} url URL de la cola SQS a la que se enviará el mensaje.
8
+ * @param {object} message Objeto JSON con los datos a enviar a la cola.
9
+ * @returns
10
+ */
11
+ module.exports.sendSqsMessage = (url, message) => {
12
+ return new Promise((resolve, reject) => {
13
+ const sqs = new SQS()
14
+ message = JSON.stringify(message);
15
+ const params = { MessageBody: message, QueueUrl: url };
16
+ if (process?.env?.SQS_DELAY_SECONDS) {
17
+ params.DelaySeconds = process.env.SQS_DELAY_SECONDS;
18
+ }
19
+
20
+ // Se ejecuta la tarea de enviar el mensaje a SQS
21
+ sqs.sendMessage(params, (err, data) => {
22
+ if (err) {
23
+ reject(err);
24
+ } else {
25
+ const { MessageId } = data;
26
+ Logger.info(`Message sent with id: ${MessageId}`, message);
27
+ resolve(data);
28
+ }
29
+ });
30
+ });
31
+ };
32
+
33
+ /**
34
+ * Establece en una URL base los parámetros GET.
35
+ * @param {string} url Url base a la que se adicionará los Query Params.
36
+ * @param {object} queryParams Objeto con los Query Params a definir como método GET.
37
+ * @returns Url con los Query Params.
38
+ */
39
+ module.exports.setQueryParams = (url, queryParams) => {
40
+ let queryText = ''
41
+ for (let key in queryParams) {
42
+ if (queryText) {
43
+ queryText += '&'
44
+ }
45
+ queryText += `${key}=${queryParams[key]}`
46
+ }
47
+
48
+ if (queryText) {
49
+ queryText = '?' + queryText
50
+ }
51
+
52
+ return url + queryText
53
+ };
54
+
55
+ /**
56
+ * Obtiene el valor de REST-Range para consultar registros en una entidad de Master Data.
57
+ * @param {number} page Página de registros a consultar en Master Data
58
+ * @param {number} limit Número máximo de registros a retornar.
59
+ * @returns Texto con el parámetro REST-Range de VTEX.
60
+ * @see {@link https://developers.vtex.com/docs/api-reference/masterdata-api#get-/api/dataentities/-acronym-/search|Search documents - Master Data V1}
61
+ */
62
+ module.exports.getRestRange = (page, limit) => {
63
+ let start = (page - 1) * limit, end = page * limit;
64
+ return `resources=${start}-${end}`;
65
+ };
66
+
67
+ /**
68
+ * Genera los valores basados en una determinada fecha para utilizarse en
69
+ * el nombre de archivos y/o carpetas.
70
+ * @param {Date} currentDate Fecha de la cual se extrae la información
71
+ * @returns Valores generados a partir de la fecha.
72
+ */
73
+ module.exports.getDateValues = (currentDate = new Date()) => {
74
+ let year = currentDate.getFullYear(),
75
+ month = String(currentDate.getMonth() + 1).padStart(2, '0'),
76
+ day = String(currentDate.getDate()).padStart(2, '0'),
77
+ hour = String(currentDate.getHours()).padStart(2, '0'),
78
+ minute = String(currentDate.getMinutes()).padStart(2, '0'),
79
+ second = String(currentDate.getSeconds()).padStart(2, '0');
80
+
81
+ return {
82
+ // Marca de tiempo de la fecha y hora actual.
83
+ 'timestamp': year + month + day + hour + minute + second,
84
+ // Marca de tiempo de la fecha actual.
85
+ 'timestampMin': year + month + day,
86
+ // Subcarpeta donde se moveran los archivos grandes que ya fueron divididos
87
+ 'processedSubfolder': `${year}/${month}`
88
+ };
89
+ };
90
+
91
+ /**
92
+ * Ajusta las reglas de validación en Validate.js.
93
+ * @param {*} validate Instancia de la librería validate.js
94
+ */
95
+ module.exports.setValidateOptions = (validate) => {
96
+ // Se ajusta la opción message de error para el parámetro 'format'
97
+ validate.validators.format.options = {
98
+ message: (value, attribute, validatorOptions, attributes, globalOptions) => {
99
+ // Se obtiene el tipo de dato del parámetro 'format' definido en las reglas de validación
100
+ let type = Object.prototype.toString.call(validatorOptions);
101
+ switch (type) {
102
+ case '[object String]':
103
+ return `doesn't match with the pattern '${validatorOptions ?? ''}'`;
104
+ case '[object Object]':
105
+ return `doesn't match with the pattern '${validatorOptions.pattern ?? ''}'`;
106
+ default:
107
+ return `doesn't match with the pattern`;
108
+ }
109
+ }
110
+ };
111
+
112
+ // Se ajusta las validaciones para el parámetro 'presence'
113
+ validate.validators.presence = (value, options, attribute, attributes) => {
114
+ // Se valida si el atributo no está definido
115
+ if (!attributes.hasOwnProperty(attribute)) {
116
+ return `is't defined`;
117
+ }
118
+
119
+ // Se obtiene el tipo de dato del parámetro 'format' definido en las reglas de validación
120
+ let type = Object.prototype.toString.call(options);
121
+
122
+ // Si en la opción se para un valor booleano (presence=true o presence=false)
123
+ if (type == '[object Boolean]') {
124
+ return options && !value ? `can't be blank` : null;
125
+ }
126
+
127
+ // Si se define un valor de texto (condicional con la variable 'attributes')
128
+ if (type == '[object String]') {
129
+ try {
130
+ let result;
131
+ eval('result=' + options);
132
+ return result && !value ? `can't be blank` : null;
133
+ } catch (ex) {
134
+ Logger.error(ex);
135
+ }
136
+ }
137
+
138
+ if (type == '[object Object]') {
139
+ const { allowEmpty, message } = options;
140
+
141
+ let subType = Object.prototype.toString.call(allowEmpty);
142
+ if (subType == '[object Boolean]') {
143
+ return !allowEmpty && !value ? (message ?? `can't be blank`) : null;
144
+ }
145
+ if (subType == '[object String]') {
146
+ try {
147
+ let result;
148
+ eval('result=' + allowEmpty);
149
+ return !result && !value ? (message ?? `can't be blank`) : null;
150
+ } catch (ex) {
151
+ Logger.error(ex);
152
+ }
153
+ }
154
+ }
155
+
156
+ return 'has an validation error';
157
+ };
158
+
159
+ validate.validators.inclusion = (value, options, attribute, attributes) => {
160
+ // Si no se definió un valor se retorna null
161
+ if (!value) {
162
+ return null;
163
+ }
164
+
165
+ // Se obtiene el tipo de dato del parámetro 'format' definido en las reglas de validación
166
+ let type = Object.prototype.toString.call(options);
167
+
168
+ if (type == '[object Array]') {
169
+ return !options.includes(value) ? `is not included in the list` : null;
170
+ }
171
+
172
+ if (type == '[object Object]') {
173
+ const { within, message, sensiitiveCase } = options;
174
+
175
+ switch (sensiitiveCase) {
176
+ case 'U':
177
+ value = value.toUpperCase();
178
+ break;
179
+ case 'L':
180
+ value = value.toLowerCase();
181
+ break
182
+ }
183
+
184
+ return !within.includes(value) ? (message ?? `is not included in the list`) : null;
185
+ }
186
+
187
+ return 'has an validation error';
188
+ };
189
+ }
190
+
191
+ /**
192
+ * Extrae la información básica de un error capturado en un try...catch.
193
+ * @param {*} ex Objeto de datos del error.
194
+ * @returns Información del error presentado.
195
+ */
196
+ module.exports.getErrorData = ex => {
197
+ let error = { message: '', status: 500, info: {} };
198
+ if (ex instanceof AxiosError) {
199
+ const { request = {}, response = {} } = ex;
200
+ const { path, method, host } = request;
201
+ const { status = 500, statusText = '', data = null } = response;
202
+
203
+ let message;
204
+ if (data?.Message) {
205
+ message = data.Message;
206
+ } else if (data) {
207
+ switch (Object.prototype.toString.call(data)) {
208
+ case '[object String]':
209
+ message = data;
210
+ break;
211
+ case '[object Object]':
212
+ message = JSON.stringify(data);
213
+ break;
214
+ }
215
+ } else if (statusText) {
216
+ message = statusText;
217
+ } else {
218
+ Logger.error(ex);
219
+ message = 'Error in Axios request';
220
+ }
221
+
222
+ error.status = status;
223
+ error.message = message;
224
+ error.info = { status, data, message, request: { path, method, host } };
225
+ } else {
226
+ const { message } = ex;
227
+ error.message = message;
228
+ error.info = { message };
229
+ }
230
+ return error;
231
+ };
232
+
233
+ /**
234
+ * Actualiza el valor de una propiedad adicionando el posfijo '_XXX';
235
+ * @param {object} objectData Objeto de datos.
236
+ * @param {string} key Nombre de la propiedad a afectar.
237
+ * @param {number} attemp Posfijo a definir.
238
+ */
239
+ module.exports.addSuffixToTimestamp = (objectData, key, attemp) => {
240
+ if (objectData.hasOwnProperty(key) && objectData[key]) {
241
+ objectData[key] = objectData[key].replace(/(_\d+)?$/, '_' + attemp);
242
+ }
243
+ };
@@ -0,0 +1,40 @@
1
+ const ApiResponse = require('../../common/utils/api-response');
2
+ const Logger = require('../../common/utils/logger');
3
+ const { getErrorData } = require('../../common/utils/util');
4
+ const Cronjob = require('../../entities/cronjob');
5
+
6
+ /**
7
+ *
8
+ * @param {*} event Objeto que contiene información de una petición como: el body, path y método de una petición HTTP, o la lista de registros en una cola SQS.
9
+ * @param {*} context Objeto que contiene información sobre la invocación, la función y el entorno de ejecución.
10
+ */
11
+ module.exports.create = async (event, context) => {
12
+ // ID de la petición en AWS
13
+ const { awsRequestId } = context;
14
+
15
+ try {
16
+ let { body, pathParameters } = event;
17
+ const { cronId } = pathParameters;
18
+
19
+ body = JSON.parse(body);
20
+ if (!body) {
21
+ throw new Error('Body request is not defined');
22
+ }
23
+ if (!cronId) {
24
+ throw new Error('Cron Id is required');
25
+ }
26
+ const result = await Cronjob.addRegisterPerId(cronId, {
27
+ CronId: cronId,
28
+ Crons: body
29
+ });
30
+ if (!result) {
31
+ throw new Error('Cron could not be created');
32
+ }
33
+
34
+ return ApiResponse.response(200, { message: 'Cron jobs saved successfully', data: result });
35
+ } catch (ex) {
36
+ const { message, info } = getErrorData(ex);
37
+ Logger.error(info);
38
+ return ApiResponse.response(500, { 'error': message, awsRequestId });
39
+ }
40
+ }
@@ -0,0 +1,171 @@
1
+ const nodeCron = require('node-cron');
2
+ const parser = require('cron-parser');
3
+ const ApiResponse = require('../../common/utils/api-response');
4
+ const { sendMessageToSqs } = require('../../common/utils/aws-services');
5
+ const { processNewLeadsNotification } = require('../notifications/new-leads-notification-consumer');
6
+ const { getErrorData } = require('../../common/utils/util');
7
+ const Cronjob = require('../../entities/cronjob');
8
+ const { callback: executeSftpProcess, reprocessFailed } = require('../sftpIntegrations/sftp-consumer');
9
+ const { handler: catalogSyncHandler } = require('../inventory/catalog-sync-handler');
10
+ const { handleExecute } = require('../multiPresentation/multipres');
11
+ const { handleConsumer } = require('../clients_peru/updateClients');
12
+ const Logger = require('../../common/utils/logger');
13
+
14
+ const {
15
+ SQS_CRON_JOBS_QUEUE_URL,
16
+ SQS_CRON_JOBS_DLQ_QUEUE_URL,
17
+ CRONJOB_ID
18
+ } = process?.env ?? {};
19
+
20
+ /**
21
+ * Función que se invoca desde el schedule definido en la propiedad 'cron_jobs' del archivo 'functions.yml'.
22
+ * @param {*} event Objeto que contiene información de una petición como: el body, path y método de una petición HTTP, o la lista de registros en una cola SQS.
23
+ * @param {*} context Objeto que contiene información sobre la invocación, la función y el entorno de ejecución.
24
+ */
25
+ module.exports.run = async (event, context) => {
26
+ // Se obtiene la propiedad 'httpMethod' para determinar si la función se ejecutó desde una petición HTTP
27
+ const { httpMethod } = event;
28
+ // Se consulta la lista de cron jobs configurados
29
+ const promises = [];
30
+ const cronJobs = await getCronJobs();
31
+ for (let cronJob of cronJobs) {
32
+ const { Actived = false, Name, Cron, TimeZone = 'America/Bogota', ...jobData } = cronJob;
33
+ // Se define la zona horaria para que la fecha y hora del sistema sea acorde a la misma
34
+ setTimeZone(TimeZone);
35
+ let currentDate = new Date();
36
+ // Se valida si el job se encuentra activo y el cron definido sea válido
37
+ if (Actived && Cron && nodeCron.validate(Cron)) {
38
+ if (timeMatch(Cron, currentDate)) {
39
+ promises.push(jobData);
40
+ }
41
+ } else {
42
+ Logger.warn(`Job ${Name} is inactived and/or cron is not valid`, { Actived, Cron });
43
+ }
44
+ }
45
+ if (promises.length) {
46
+ Logger.log(`Cron jobs to execute: ${promises.length}`);
47
+ await Promise.allSettled(promises.map(jobData => {
48
+ return new Promise((resolve, reject) => {
49
+ if (!jobData || jobData === "") {
50
+ return resolve(null); // O reject si prefieres fallar en este caso
51
+ }
52
+
53
+ sendMessageToSqs(SQS_CRON_JOBS_QUEUE_URL, jobData).then(e => {
54
+ resolve(jobData);
55
+ }).catch(error => {
56
+ Logger.error(`Error sending message to SQS`, {
57
+ error: error.message,
58
+ jobData: jobData
59
+ });
60
+ reject(new Error(error.message)); // Propaga el error a Promise.allSettled
61
+ })
62
+ });
63
+ }));
64
+ }
65
+
66
+ // Si la función se ejecutó desde una petición HTTP se retorna una respuesta
67
+ if (httpMethod) {
68
+ return ApiResponse.response(200, { message: 'Cron jobs executed successfully', promises });
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Función que se invoca desde el trigger de la cola SQS definida en la propiedad 'cronjobs-queue' del archivo 'constructs.yml'.
74
+ * @param {*} event Objeto que contiene información de una petición como: el body, path y método de una petición HTTP, o la lista de registros en una cola SQS.
75
+ * @param {*} context Objeto que contiene información sobre la invocación, la función y el entorno de ejecución.
76
+ */
77
+ module.exports.queue = async (event, context) => {
78
+ // ID de la petición en AWS
79
+ const { awsRequestId } = context;
80
+
81
+ // Se recorre la lista de registros de la cola
82
+ for (const record of event.Records) {
83
+ // Se obtiene el objeto enviado a la cola
84
+ let { body } = record;
85
+ body = JSON.parse(body);
86
+
87
+ const { AccountName, ActionProcess, Values } = body;
88
+ try {
89
+ switch (ActionProcess) {
90
+ case 'new-leads':
91
+ await processNewLeadsNotification(AccountName, ActionProcess, Values);
92
+ break;
93
+ case 'catalog-sync':
94
+ await catalogSyncHandler({
95
+ pathParameters: { accountName: AccountName },
96
+ ...Values
97
+ });
98
+ break;
99
+ case 'multiPresentation':
100
+ await handleExecute(AccountName);
101
+ break
102
+ case 'clientsPeru':
103
+ await handleConsumer(AccountName, Values);
104
+ break
105
+ case 'reprocessSftpFailed':
106
+ await reprocessFailed(body);
107
+ break
108
+ default:
109
+ // Se pasa el cuerpo del mensaje SQS completo, para contemplar el tema de los reitentos dentro de la función.
110
+ await executeSftpProcess(body);
111
+ break;
112
+ }
113
+ } catch (ex) {
114
+ const { message, info } = getErrorData(ex);
115
+ Logger.error('Error in Cron Job queue', info);
116
+
117
+ await sendMessageToSqs(SQS_CRON_JOBS_DLQ_QUEUE_URL, {
118
+ AccountName, ActionProcess,
119
+ Values,
120
+ reason: message,
121
+ awsRequestId
122
+ }).catch(Logger.error);
123
+ }
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Consulta la lista de jobs configurados.
129
+ * @returns Lista de jobs.
130
+ */
131
+ async function getCronJobs() {
132
+ try {
133
+ // Se consulta en DynamoDB la lista de Jobs configurados
134
+ const cronJobsData = await Cronjob.getPerId(CRONJOB_ID);
135
+ return cronJobsData?.Crons ?? [];
136
+ } catch (ex) {
137
+ const { info } = getErrorData(ex);
138
+ Logger.error('Error in getCronJobs', info)
139
+ return [];
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Establece la zona horaria en el servidor para obtener fecha y hora del sistema.
145
+ * @param {string} timeZone Zona horaria.
146
+ */
147
+ function setTimeZone(timeZone) {
148
+ if (timeZone) {
149
+ process.env.TZ = timeZone;
150
+ } else {
151
+ delete process.env.TZ;
152
+ }
153
+ }
154
+
155
+ /**
156
+ * Valida si una fecha corresponde a un cron job.
157
+ * @param {string} cron Valor del cron.
158
+ * @param {Date} date Fecha a validar.
159
+ * @return Valor de verdad que indica si la fecha corresponde o no al cron (TRUE o FALSE).
160
+ */
161
+ function timeMatch(cron, date) {
162
+ let interval = parser.parseExpression(cron);
163
+
164
+ const { minute, hour, dayOfMonth, month, dayOfWeek } = interval.fields;
165
+
166
+ return minute.includes(date.getMinutes()) &&
167
+ hour.includes(date.getHours()) &&
168
+ dayOfMonth.includes(date.getDate()) &&
169
+ month.includes(date.getMonth() + 1) &&
170
+ dayOfWeek.includes(date.getDay());
171
+ }
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+
3
+ const Crons = require("../../entities/cron");
4
+ const ApiResponse = require("../../common/utils/api-response");
5
+ const Logger = require("../../common/utils/logger");
6
+ /**
7
+ *
8
+ * @param {import("aws-lambda")} event
9
+ */
10
+ const create = async (event) => {
11
+ try {
12
+ let message = "Cron created successfully";
13
+ Logger.log(event);
14
+ if (!event.body) {
15
+ return ApiResponse.response(400, "No body was found");
16
+ }
17
+ const body = JSON.parse(event.body);
18
+ const cronId = event?.pathParameters?.cronId;
19
+ if (!cronId || cronId === "") {
20
+ return ApiResponse.response(400, "Cron Id is required");
21
+ }
22
+
23
+ const cron = await Crons.addRegisterPerId({
24
+ CronId: cronId,
25
+ Crons: body
26
+ }, cronId);
27
+ if (!cron) {
28
+ return ApiResponse.response(400, "Cron could not be created");
29
+ }
30
+ return ApiResponse.response(200, { message: message, data: cron });
31
+ } catch (error) {
32
+ Logger.error(error);
33
+ return ApiResponse.response(500, error);
34
+ }
35
+ };
36
+
37
+ module.exports = {
38
+ create
39
+ };
@@ -0,0 +1,81 @@
1
+ const VtexApi = require('../../vtex/clients/VtexApi');
2
+ const DistributorData = require('./distributor');
3
+ const Logger = require("../../common/utils/logger");
4
+
5
+ /**
6
+ * Handles the event triggered by the Lambda function.
7
+ *
8
+ * @param {Object} event - The event object containing the request details.
9
+ * @returns {Promise<Object>} - A promise that resolves to the response object.
10
+ * @throws {Error} - If an error occurs during the execution.
11
+ */
12
+ module.exports.handler = async (event) => {
13
+ let statusCode = 200;
14
+ let response = {};
15
+ for (const record of event.Records) {
16
+ try {
17
+ const order = JSON.parse(record.body);
18
+ const AccountConfig = order?.accountConfig;
19
+ Logger.setAccount(AccountConfig);
20
+ const orderId = order?.orderInfo?.OrderId;
21
+ const vtexApi = new VtexApi(AccountConfig.AccountName, AccountConfig.Credentials.key, AccountConfig.Credentials.token);
22
+ const responseOrder = await vtexApi.fetch(`/oms/pvt/orders/${orderId}`, { method: "GET" });
23
+ Logger.debug("Response Order Info: ", responseOrder?.data);
24
+ //get only items from responseOrder
25
+ const items = responseOrder?.data?.items;
26
+ Logger.debug("Items: ", items);
27
+ let itemsSkuDist = {};
28
+ let skuRefUsed = AccountConfig?.SkuRefUsed;
29
+ if (typeof skuRefUsed !== "boolean") {
30
+ Logger.warn("Sku or ReferenceCode filter not defined, use default: ", skuRefUsed);
31
+ skuRefUsed = true;
32
+ }
33
+ if (!items) {
34
+ Logger.error("Orders without items.");
35
+ return;
36
+ }
37
+ // Create a set to track unique search values
38
+ const uniqueSearchValues = new Set();
39
+ const filteredItems = items.filter(item => {
40
+ const searchValue = skuRefUsed ? item.refId : item.ean;
41
+ if (!uniqueSearchValues.has(searchValue)) {
42
+ uniqueSearchValues.add(searchValue);
43
+ return true;
44
+ }
45
+ return false;
46
+ });
47
+ Logger.debug("Filtered Items: ", filteredItems);
48
+ itemsSkuDist = await getItemsSkuDist(responseOrder, filteredItems, skuRefUsed, AccountConfig);
49
+ const updateAdditionalFields = await vtexApi.fetch(
50
+ `/dataentities/${AccountConfig.SkuDistributorManagementAdditionalInfo.entityName}/documents/`, { method: "PATCH", data: itemsSkuDist });
51
+ Logger.debug("Update Additional Fields: ", updateAdditionalFields);
52
+ if (!updateAdditionalFields && (updateAdditionalFields.status !== 200 || updateAdditionalFields.status !== 201 || updateAdditionalFields.status !== 204)) {
53
+ statusCode = 404;
54
+ response = { error: updateAdditionalFields };
55
+ Logger.error(statusCode, response);
56
+ }
57
+ Logger.info(statusCode, { itemsSkuDist });
58
+ } catch (error) {
59
+ Logger.error(error);
60
+ statusCode = 500;
61
+ response = { error };
62
+ }
63
+ }
64
+ };
65
+
66
+ async function getItemsSkuDist(responseOrder, filteredItems, skuRefUsed, AccountConfig) {
67
+ return {
68
+ id: responseOrder?.data?.orderId,
69
+ items: await Promise.all(filteredItems.map(async (item) => {
70
+ const searchValue = skuRefUsed ? item.refId : item.ean;
71
+ const distributorData = await DistributorData.getDistributorByRefId(AccountConfig.AccountName, searchValue, AccountConfig.SkuDistributorManagement);
72
+ Logger.info(`Distributor Data for ${searchValue}: `, distributorData);
73
+ return {
74
+ id: item.id,
75
+ ean: item.ean,
76
+ refId: item.refId,
77
+ ERPCode: distributorData, // Include distributor data in the item
78
+ };
79
+ }))
80
+ };
81
+ }