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,639 @@
1
+ const DynamoAWS = require("aws-sdk/clients/dynamodb");
2
+ const Validation = require("../common/utils/validation-data");
3
+ const Logger = require("../common/utils/logger");
4
+
5
+ const ACCOUNT_DATA_CONFIG_TABLE = process.env.ACCOUNT_DATA_CONFIG_TABLE;
6
+ const MAX_LIMIT = process.env.MAX_LIMIT || 150;
7
+
8
+ /**
9
+ * Procesa respuesta de DynamoDB aplicando filtros adicionales y transformaciones
10
+ * @param {Array} items - Elementos obtenidos de DynamoDB
11
+ * @param {Object} queryParams - Parámetros de consulta
12
+ * @returns {Array} - Elementos filtrados y procesados
13
+ */
14
+ const processItems = (items, queryParams) => {
15
+ if (!items || !items.length) return [];
16
+
17
+ let processedItems = [...items];
18
+
19
+ // Aplicar filtro _moduleActive
20
+ if (queryParams._moduleActive) {
21
+ const module = queryParams._moduleActive;
22
+ processedItems = processedItems.filter(item =>
23
+ item[module] &&
24
+ ((typeof item[module] === 'object' && item[module].isActive === true) ||
25
+ (typeof item[module] === 'boolean' && item[module] === true))
26
+ );
27
+ }
28
+
29
+ // Aplicar filtro _accountPattern
30
+ if (queryParams._accountPattern) {
31
+ const pattern = queryParams._accountPattern.replace(/\*/g, '.*');
32
+ const regex = new RegExp(`^${pattern}$`, 'i');
33
+ processedItems = processedItems.filter(item => regex.test(item.AccountName));
34
+ }
35
+
36
+ // Aplicar filtro _integration
37
+ if (queryParams._integration) {
38
+ const integrationType = queryParams._integration.toLowerCase();
39
+
40
+ processedItems = processedItems.filter(item => {
41
+ switch (integrationType) {
42
+ case 'sftp':
43
+ return item.SftpUsed === true ||
44
+ (item.SftpIntegration && item.SftpIntegration.isActive === true);
45
+ case 'api':
46
+ return item.Credentials && item.Credentials.key && item.Credentials.token;
47
+ case 'webhook':
48
+ return item.HookOrderVtexMDv2 && item.HookOrderVtexMDv2.trim() !== '';
49
+ default:
50
+ return false;
51
+ }
52
+ });
53
+ }
54
+
55
+ // Aplicar filtro _like
56
+ if (queryParams._like) {
57
+ const searchTerm = queryParams._like.toLowerCase();
58
+
59
+ processedItems = processedItems.filter(item => {
60
+ // Buscar en todas las propiedades de primer nivel
61
+ return Object.entries(item).some(([key, value]) => {
62
+ if (typeof value === 'string') {
63
+ return value.toLowerCase().includes(searchTerm);
64
+ } else if (typeof value === 'number') {
65
+ return value.toString().includes(searchTerm);
66
+ } else if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'string') {
67
+ return value.some(val => val.toLowerCase().includes(searchTerm));
68
+ }
69
+ return false;
70
+ });
71
+ });
72
+ }
73
+
74
+ // Proyectar campos si se especifican en _fields
75
+ if (queryParams._fields) {
76
+ const fields = queryParams._fields.split(',').map(f => f.trim());
77
+
78
+ processedItems = processedItems.map(item => {
79
+ const projectedItem = {};
80
+ fields.forEach(field => {
81
+ // Soporte para campos anidados con notación de punto
82
+ if (field.includes('.')) {
83
+ const parts = field.split('.');
84
+ let current = item;
85
+ let valid = true;
86
+
87
+ // Navegar por el objeto hasta el campo anidado
88
+ for (let i = 0; i < parts.length - 1; i++) {
89
+ if (current && current[parts[i]]) {
90
+ current = current[parts[i]];
91
+ } else {
92
+ valid = false;
93
+ break;
94
+ }
95
+ }
96
+
97
+ // Si el camino es válido, extraer el valor final
98
+ const lastPart = parts[parts.length - 1];
99
+ if (valid && current && current[lastPart] !== undefined) {
100
+ // Crear estructura anidada en el objeto proyectado
101
+ let target = projectedItem;
102
+ for (let i = 0; i < parts.length - 1; i++) {
103
+ if (!target[parts[i]]) {
104
+ target[parts[i]] = {};
105
+ }
106
+ target = target[parts[i]];
107
+ }
108
+ target[lastPart] = current[lastPart];
109
+ }
110
+ } else if (item[field] !== undefined) {
111
+ projectedItem[field] = item[field];
112
+ }
113
+ });
114
+
115
+ return projectedItem;
116
+ });
117
+ }
118
+
119
+ // Excluir campos si se especifican en _excludeFields
120
+ if (queryParams._excludeFields) {
121
+ const excludeFields = queryParams._excludeFields.split(',').map(f => f.trim());
122
+
123
+ processedItems = processedItems.map(item => {
124
+ const filteredItem = { ...item };
125
+
126
+ excludeFields.forEach(field => {
127
+ // Soporte para campos anidados con notación de punto
128
+ if (field.includes('.')) {
129
+ const parts = field.split('.');
130
+ let current = filteredItem;
131
+
132
+ // Navegar hasta el penúltimo nivel
133
+ for (let i = 0; i < parts.length - 1; i++) {
134
+ if (!current[parts[i]]) {
135
+ return; // Campo no existe, nada que eliminar
136
+ }
137
+ current = current[parts[i]];
138
+ }
139
+
140
+ // Eliminar propiedad del último nivel
141
+ const lastPart = parts[parts.length - 1];
142
+ if (current[lastPart] !== undefined) {
143
+ delete current[lastPart];
144
+ }
145
+ } else {
146
+ delete filteredItem[field];
147
+ }
148
+ });
149
+
150
+ return filteredItem;
151
+ });
152
+ }
153
+
154
+ return processedItems;
155
+ };
156
+
157
+ /**
158
+ * Obtiene cuentas filtradas por ParentAccountName y parámetros avanzados de consulta
159
+ * @param {string} parentAccountName - Nombre de la cuenta padre (opcional)
160
+ * @param {Object} queryParams - Parámetros adicionales de consulta (opcional)
161
+ * @returns {Promise<{items: Array, lastEvaluatedKey: Object|null, count: number}>} - Resultados paginados
162
+ */
163
+ const getAccountsByParentName = async (parentAccountName, queryParams = {}) => {
164
+ try {
165
+ const dynamodb = new DynamoAWS.DocumentClient();
166
+
167
+ // Configuración de parámetros básicos
168
+ const limit = Math.min(parseInt(queryParams._limit) || MAX_LIMIT, MAX_LIMIT);
169
+ const sortAscending = queryParams._sort?.toLowerCase() !== 'desc';
170
+ const exclusiveStartKey = queryParams._from ? JSON.parse(Buffer.from(queryParams._from, 'base64').toString()) : null;
171
+
172
+ // Parámetros base para DynamoDB
173
+ let params = {
174
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
175
+ Limit: limit,
176
+ };
177
+
178
+ // Si hay un ExclusiveStartKey (para paginación)
179
+ if (exclusiveStartKey) {
180
+ params.ExclusiveStartKey = exclusiveStartKey;
181
+ }
182
+
183
+ // Si se proporciona una cuenta padre, usar query con el índice adecuado
184
+ if (parentAccountName) {
185
+ // Usar el índice ParentAccountNameIndex
186
+ params.IndexName = "ParentAccountNameIndex";
187
+ params.KeyConditionExpression = "ParentAccountName = :parentName";
188
+ params.ExpressionAttributeValues = {
189
+ ":parentName": parentAccountName
190
+ };
191
+ params.ScanIndexForward = sortAscending; // Control del orden
192
+
193
+ // Ejecutar la consulta a DynamoDB
194
+ const response = await dynamodb.query(params).promise();
195
+
196
+ // Procesar los resultados con los filtros adicionales
197
+ const processedItems = processItems(response.Items || [], queryParams);
198
+
199
+ return {
200
+ items: processedItems,
201
+ lastEvaluatedKey: response.LastEvaluatedKey ?
202
+ Buffer.from(JSON.stringify(response.LastEvaluatedKey)).toString('base64') :
203
+ null,
204
+ count: processedItems.length
205
+ };
206
+ }
207
+ // Si no se proporciona cuenta padre, usar scan
208
+ else {
209
+ // Ejecutar scan en DynamoDB
210
+ const response = await dynamodb.scan(params).promise();
211
+
212
+ // Procesar los resultados con los filtros adicionales
213
+ let processedItems = processItems(response.Items || [], queryParams);
214
+
215
+ // Ordenar resultados si se solicita
216
+ if (queryParams._sort) {
217
+ processedItems.sort((a, b) => {
218
+ if (sortAscending) {
219
+ return a.AccountName > b.AccountName ? 1 : -1;
220
+ } else {
221
+ return a.AccountName < b.AccountName ? 1 : -1;
222
+ }
223
+ });
224
+ }
225
+
226
+ return {
227
+ items: processedItems,
228
+ lastEvaluatedKey: response.LastEvaluatedKey ?
229
+ Buffer.from(JSON.stringify(response.LastEvaluatedKey)).toString('base64') :
230
+ null,
231
+ count: processedItems.length
232
+ };
233
+ }
234
+ } catch (err) {
235
+ Logger.error("Error getting accounts data: ", err);
236
+ return { items: [], lastEvaluatedKey: null, count: 0 };
237
+ }
238
+ };
239
+
240
+ /**
241
+ * Obtiene todas las cuentas con soporte para paginación y filtros avanzados
242
+ * @param {Object} queryParams - Parámetros de consulta
243
+ * @returns {Promise<{items: Array, lastEvaluatedKey: Object|null, count: number}>} - Resultados paginados
244
+ */
245
+ const getAccountDataAll = async (queryParams = {}) => {
246
+ return getAccountsByParentName(null, queryParams);
247
+ };
248
+
249
+ /**
250
+ * Obtiene una cuenta específica por su AccountName con soporte para proyección de campos
251
+ * @param {string} accountName - Nombre de la cuenta a buscar
252
+ * @param {Object} queryParams - Parámetros de consulta para proyección
253
+ * @returns {Promise<Object|null>} - Cuenta encontrada o null
254
+ */
255
+ const getAccountDataByAccountName = async (accountName, queryParams = {}) => {
256
+ try {
257
+ const dynamodb = new DynamoAWS.DocumentClient();
258
+ const params = {
259
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
260
+ IndexName: "AccountNameIndex",
261
+ KeyConditionExpression: "AccountName = :accountName",
262
+ ExpressionAttributeValues: {
263
+ ":accountName": accountName,
264
+ },
265
+ };
266
+ const response = await dynamodb.query(params).promise();
267
+
268
+ if (!response?.Items || response.Items.length === 0) {
269
+ return null;
270
+ }
271
+
272
+ // Aplicar proyección a un solo elemento
273
+ const items = processItems([response.Items[0]], queryParams);
274
+ return items[0] || null;
275
+ } catch (err) {
276
+ Logger.error("Error getting account data: ", err);
277
+ return null;
278
+ }
279
+ };
280
+
281
+ // El resto de funciones permanece igual
282
+ const deleteAccountByAccountName = async (accountName) => {
283
+ try {
284
+ const account = await getAccountDataByAccountName(accountName);
285
+ if (!account) {
286
+ Logger.error(`Account ${accountName} not found for deletion`);
287
+ return { success: false, message: `Account ${accountName} not found` };
288
+ }
289
+ const dynamodb = new DynamoAWS.DocumentClient();
290
+ const params = {
291
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
292
+ Key: {
293
+ AccountName: accountName,
294
+ ParentAccountName: account.ParentAccountName
295
+ }
296
+ };
297
+ await dynamodb.delete(params).promise();
298
+ return {
299
+ success: true,
300
+ message: `Account ${accountName} deleted successfully`
301
+ };
302
+ } catch (err) {
303
+ Logger.error(`Error deleting account ${accountName}:`, err);
304
+ return {
305
+ success: false,
306
+ message: `Error deleting account: ${err.message}`
307
+ };
308
+ }
309
+ };
310
+
311
+ const addAccountDataConfig = async (accountData) => {
312
+ const dynamodb = new DynamoAWS.DocumentClient();
313
+ let data = Validation.transformFirstKeysToUpperCaseValue(accountData);
314
+ try {
315
+ const params = {
316
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
317
+ Item: data,
318
+ ConditionExpression: "attribute_not_exists(AccountName)",
319
+ };
320
+ await dynamodb.put(params).promise();
321
+ return accountData;
322
+ } catch (err) {
323
+ Logger.error("Error saving account data config: ", err);
324
+ if (err.code === "ConditionalCheckFailedException") {
325
+ try {
326
+ let updateExpression = 'set';
327
+ let ExpressionAttributeNames = {};
328
+ let ExpressionAttributeValues = {};
329
+ for (const property in data) {
330
+ if (property != 'AccountName' && property != 'ParentAccountName') {
331
+ updateExpression += ` #${property} = :${property} ,`;
332
+ ExpressionAttributeNames['#' + property] = property;
333
+ ExpressionAttributeValues[':' + property] = data[property];
334
+ }
335
+ }
336
+ updateExpression = updateExpression.slice(0, -1);
337
+ const params2 = {
338
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
339
+ Key: {
340
+ AccountName: data.AccountName,
341
+ ParentAccountName: data.ParentAccountName
342
+ },
343
+ UpdateExpression: updateExpression,
344
+ ExpressionAttributeNames: ExpressionAttributeNames,
345
+ ExpressionAttributeValues: ExpressionAttributeValues
346
+ };
347
+ let res2 = await dynamodb.update(params2).promise();
348
+ return accountData;
349
+ } catch (err2) {
350
+ Logger.error("Error updating account data config: ", err2);
351
+ if (err2.code === "ConditionalCheckFailedException") {
352
+ return accountData;
353
+ }
354
+ return null
355
+ }
356
+ }
357
+ return null;
358
+ }
359
+ };
360
+
361
+ /**
362
+ * Función de paginación completa para DataTables
363
+ */
364
+ const getAccountsWithDataTablesPagination = async (dataTablesParams) => {
365
+ try {
366
+ const dynamodb = new DynamoAWS.DocumentClient();
367
+
368
+ const totalCountParams = {
369
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
370
+ Select: 'COUNT'
371
+ };
372
+
373
+ const totalCountResponse = await dynamodb.scan(totalCountParams).promise();
374
+ const recordsTotal = totalCountResponse.Count;
375
+
376
+ const allFilteredItems = await getAllFilteredAccounts(dataTablesParams);
377
+ const sortedItems = applySortingToAccounts(allFilteredItems, dataTablesParams.orderField, dataTablesParams.orderDir);
378
+
379
+ const start = dataTablesParams.start;
380
+ const length = dataTablesParams.length;
381
+ const paginatedItems = sortedItems.slice(start, start + length);
382
+
383
+ return {
384
+ data: paginatedItems,
385
+ recordsTotal: recordsTotal,
386
+ recordsFiltered: sortedItems.length,
387
+ draw: dataTablesParams.draw
388
+ };
389
+
390
+ } catch (err) {
391
+ Logger.error("Error in getAccountsWithDataTablesPagination: ", err);
392
+ return {
393
+ data: [],
394
+ recordsTotal: 0,
395
+ recordsFiltered: 0,
396
+ draw: dataTablesParams.draw
397
+ };
398
+ }
399
+ };
400
+
401
+ /**
402
+ * Obtiene TODOS los registros filtrados usando paginación completa de DynamoDB
403
+ */
404
+ const getAllFilteredAccounts = async (dataTablesParams) => {
405
+ const dynamodb = new DynamoAWS.DocumentClient();
406
+ let allItems = [];
407
+ let lastEvaluatedKey = null;
408
+ const BATCH_SIZE = 100;
409
+
410
+ do {
411
+ const params = {
412
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
413
+ Limit: BATCH_SIZE,
414
+ ExclusiveStartKey: lastEvaluatedKey
415
+ };
416
+
417
+ const response = await dynamodb.scan(params).promise();
418
+ const filteredBatch = applyFiltersToAccounts(response.Items || [], dataTablesParams);
419
+ allItems = [...allItems, ...filteredBatch];
420
+
421
+ lastEvaluatedKey = response.LastEvaluatedKey;
422
+
423
+ } while (lastEvaluatedKey);
424
+
425
+ return allItems;
426
+ };
427
+
428
+ /**
429
+ * Aplica filtros específicos a los accounts
430
+ */
431
+ const applyFiltersToAccounts = (items, dataTablesParams) => {
432
+ if (!items || !items.length) return [];
433
+
434
+ let filteredItems = [...items];
435
+
436
+ if (dataTablesParams.globalSearch) {
437
+ const searchTerm = dataTablesParams.globalSearch.toLowerCase();
438
+ filteredItems = filteredItems.filter(item => {
439
+ return Object.entries(item).some(([key, value]) => {
440
+ if (typeof value === 'string') {
441
+ return value.toLowerCase().includes(searchTerm);
442
+ } else if (typeof value === 'number') {
443
+ return value.toString().includes(searchTerm);
444
+ } else if (Array.isArray(value) && value.length > 0 && typeof value[0] === 'string') {
445
+ return value.some(val => val.toLowerCase().includes(searchTerm));
446
+ }
447
+ return false;
448
+ });
449
+ });
450
+ }
451
+
452
+ Object.entries(dataTablesParams.columnFilters || {}).forEach(([fieldName, filterValue]) => {
453
+ if (filterValue) {
454
+ const searchTerm = filterValue.toLowerCase();
455
+ filteredItems = filteredItems.filter(item => {
456
+ const fieldValue = item[fieldName];
457
+ if (typeof fieldValue === 'string') {
458
+ return fieldValue.toLowerCase().includes(searchTerm);
459
+ } else if (typeof fieldValue === 'number') {
460
+ return fieldValue.toString().includes(searchTerm);
461
+ }
462
+ return false;
463
+ });
464
+ }
465
+ });
466
+
467
+ if (dataTablesParams._moduleActive) {
468
+ const module = dataTablesParams._moduleActive;
469
+ filteredItems = filteredItems.filter(item =>
470
+ item[module] &&
471
+ ((typeof item[module] === 'object' && item[module].isActive === true) ||
472
+ (typeof item[module] === 'boolean' && item[module] === true))
473
+ );
474
+ }
475
+
476
+ if (dataTablesParams._accountPattern) {
477
+ const pattern = dataTablesParams._accountPattern.replace(/\*/g, '.*');
478
+ const regex = new RegExp(`^${pattern}$`, 'i');
479
+ filteredItems = filteredItems.filter(item => regex.test(item.AccountName));
480
+ }
481
+
482
+ if (dataTablesParams._integration) {
483
+ const integrationType = dataTablesParams._integration.toLowerCase();
484
+ filteredItems = filteredItems.filter(item => {
485
+ switch (integrationType) {
486
+ case 'sftp':
487
+ return item.SftpUsed === true ||
488
+ (item.SftpIntegration && item.SftpIntegration.isActive === true);
489
+ case 'api':
490
+ return item.Credentials && item.Credentials.key && item.Credentials.token;
491
+ case 'webhook':
492
+ return item.HookOrderVtexMDv2 && item.HookOrderVtexMDv2.trim() !== '';
493
+ default:
494
+ return false;
495
+ }
496
+ });
497
+ }
498
+
499
+ return filteredItems;
500
+ };
501
+
502
+ /**
503
+ * Aplica ordenamiento a los accounts
504
+ */
505
+ const applySortingToAccounts = (items, orderField, orderDir) => {
506
+ if (!items || !items.length) return items;
507
+
508
+ return items.sort((a, b) => {
509
+ let valueA = a[orderField];
510
+ let valueB = b[orderField];
511
+
512
+ if (valueA == null) valueA = '';
513
+ if (valueB == null) valueB = '';
514
+
515
+ valueA = valueA.toString().toLowerCase();
516
+ valueB = valueB.toString().toLowerCase();
517
+
518
+ if (orderDir === 'desc') {
519
+ return valueB.localeCompare(valueA);
520
+ } else {
521
+ return valueA.localeCompare(valueB);
522
+ }
523
+ });
524
+ };
525
+
526
+ /**
527
+ * Función para búsqueda por múltiples AccountNames
528
+ */
529
+ const getAccountsByMultipleNames = async (accountNames, queryParams = {}) => {
530
+ try {
531
+ const dynamodb = new DynamoAWS.DocumentClient();
532
+ let allItems = [];
533
+
534
+ if (accountNames.length <= 100) {
535
+ const params = {
536
+ TableName: ACCOUNT_DATA_CONFIG_TABLE,
537
+ FilterExpression: "AccountName IN (" + accountNames.map((_, i) => `:an${i}`).join(', ') + ")",
538
+ ExpressionAttributeValues: {}
539
+ };
540
+
541
+ accountNames.forEach((accountName, i) => {
542
+ params.ExpressionAttributeValues[`:an${i}`] = accountName;
543
+ });
544
+
545
+ let lastEvaluatedKey = null;
546
+ do {
547
+ if (lastEvaluatedKey) {
548
+ params.ExclusiveStartKey = lastEvaluatedKey;
549
+ }
550
+
551
+ const response = await dynamodb.scan(params).promise();
552
+ allItems = [...allItems, ...(response.Items || [])];
553
+ lastEvaluatedKey = response.LastEvaluatedKey;
554
+
555
+ } while (lastEvaluatedKey);
556
+ } else {
557
+ const chunks = chunkArray(accountNames, 100);
558
+
559
+ for (const chunk of chunks) {
560
+ const chunkResult = await getAccountsByMultipleNames(chunk, queryParams);
561
+ allItems = [...allItems, ...(chunkResult.items || [])];
562
+ }
563
+ }
564
+
565
+ const filteredItems = applyFiltersToAccounts(allItems, queryParams);
566
+ const sortedItems = applySortingToAccounts(filteredItems, queryParams.orderField || "AccountName", queryParams.orderDir || "asc");
567
+
568
+ return {
569
+ items: sortedItems,
570
+ count: sortedItems.length,
571
+ lastEvaluatedKey: null
572
+ };
573
+
574
+ } catch (err) {
575
+ Logger.error("Error getting accounts by multiple names: ", err);
576
+ return { items: [], lastEvaluatedKey: null, count: 0 };
577
+ }
578
+ };
579
+
580
+ /**
581
+ * Función auxiliar para dividir array en chunks
582
+ */
583
+ const chunkArray = (array, size) => {
584
+ const chunks = [];
585
+ for (let i = 0; i < array.length; i += size) {
586
+ chunks.push(array.slice(i, i + size));
587
+ }
588
+ return chunks;
589
+ };
590
+
591
+ /**
592
+ * Función principal que maneja DataTables y Legacy - CON VALIDACIONES
593
+ */
594
+ const getAccountsForDataTables = async (queryParams = {}) => {
595
+ try {
596
+ if (!queryParams || typeof queryParams !== 'object') {
597
+ Logger.error("Invalid queryParams:", queryParams);
598
+ throw new Error("Invalid query parameters");
599
+ }
600
+
601
+ if (queryParams.accountNamesFilter && queryParams.accountNamesFilter.length > 0) {
602
+ Logger.debug("Searching by multiple AccountNames:", queryParams.accountNamesFilter);
603
+ return getAccountsByMultipleNames(queryParams.accountNamesFilter, queryParams);
604
+ }
605
+
606
+ if (queryParams.parentAccountName) {
607
+ return getAccountsByParentName(queryParams.parentAccountName, queryParams);
608
+ }
609
+
610
+ if (queryParams.draw !== undefined) {
611
+ return getAccountsWithDataTablesPagination(queryParams);
612
+ }
613
+
614
+ return getAccountsByParentName(null, queryParams);
615
+ } catch (error) {
616
+ Logger.error("Error in getAccountsForDataTables:", error);
617
+ return {
618
+ data: [],
619
+ items: [],
620
+ recordsTotal: 0,
621
+ recordsFiltered: 0,
622
+ count: 0,
623
+ draw: queryParams.draw || 1,
624
+ lastEvaluatedKey: null,
625
+ error: error.message
626
+ };
627
+ }
628
+ };
629
+
630
+ module.exports = {
631
+ getAccountDataAll,
632
+ getAccountsByParentName,
633
+ getAccountDataByAccountName,
634
+ getAccountsForDataTables,
635
+ getAccountsWithDataTablesPagination,
636
+ getAccountsByMultipleNames,
637
+ deleteAccountByAccountName,
638
+ addAccountDataConfig,
639
+ };