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,320 @@
1
+ const FtpClient = require('ftp')
2
+ let SftpClient = require('ssh2-sftp-client');
3
+ const fs = require('fs')
4
+
5
+ class FileSystem {
6
+
7
+ }
8
+
9
+ class FileInfo {
10
+ constructor(original) {
11
+ this.original = original
12
+ }
13
+ getName() {
14
+ return this.original.name
15
+ }
16
+ getSize() {
17
+ return this.original.size
18
+ }
19
+ }
20
+
21
+ // credits going to https://geedew.com/remove-a-directory-that-is-not-empty-in-nodejs/
22
+ var deleteFolderRecursive = function(path) {
23
+ if( fs.existsSync(path) ) {
24
+ fs.readdirSync(path).forEach(function(file,index){
25
+ var curPath = path + "/" + file;
26
+ if(fs.lstatSync(curPath).isDirectory()) { // recurse
27
+ deleteFolderRecursive(curPath);
28
+ } else { // delete file
29
+ fs.unlinkSync(curPath);
30
+ }
31
+ });
32
+ fs.rmdirSync(path);
33
+ }
34
+ };
35
+
36
+ class LocalFileSystem extends FileSystem {
37
+ constructor() {
38
+ super()
39
+ }
40
+ static create() {
41
+ return new Promise((resolve, reject) => {
42
+ return resolve(new LocalFileSystem())
43
+ })
44
+ }
45
+ async list(path) {
46
+ return new Promise((resolve, reject) => {
47
+ fs.readdir(path, async (err, files) => {
48
+ if (err) {
49
+ return reject(err)
50
+ }
51
+ await Promise.all(
52
+ files.map(fileName => new Promise((res, reject) => {
53
+ fs.stat(path + fileName, (err, stats) => {
54
+ if (err) {
55
+ return reject(err)
56
+ }
57
+ stats.name = fileName
58
+ res(new LocalFileInfo(stats))
59
+ })
60
+ }))
61
+ ).then(res => {
62
+ resolve(res)
63
+ }).catch(err => {
64
+ reject(err)
65
+ })
66
+ })
67
+ })
68
+ }
69
+ put(src, toPath) {
70
+ return new Promise((resolve, reject) => {
71
+ try {
72
+ const writeStream = fs.createWriteStream(toPath)
73
+ src.pipe(writeStream)
74
+ resolve(true)
75
+ } catch (e) {
76
+ reject(e)
77
+ }
78
+ })
79
+ }
80
+ get(path) {
81
+ return new Promise((resolve, reject) => {
82
+ try {
83
+ const readStream = fs.createReadStream(path)
84
+ resolve(readStream)
85
+ } catch (e) {
86
+ reject(e)
87
+ }
88
+ })
89
+ }
90
+ mkdir(path, recursive) {
91
+ return fs.mkdir(path, { recursive })
92
+ }
93
+ rmdir(path, recursive) {
94
+ if (!recursive)
95
+ return fs.rmdir(path)
96
+ return new Promise((resolve, reject) => {
97
+ try {
98
+ deleteFolderRecursive(path)
99
+ resolve(true)
100
+ } catch (e) {
101
+ reject(e)
102
+ }
103
+ })
104
+ }
105
+ delete(path) {
106
+ return new Promise((resolve, reject) => {
107
+ fs.unlink(path, err => {
108
+ if (err) {
109
+ return reject(err)
110
+ }
111
+ resolve(true)
112
+ })
113
+ })
114
+ }
115
+ rename(oldPath, newPath) {
116
+ return new Promise((resolve, reject) => {
117
+ fs.rename(oldPath, newPath, err => {
118
+ if (err) {
119
+ return reject(err)
120
+ }
121
+ resolve(true)
122
+ })
123
+ })
124
+ }
125
+ // stats cannot be used with the current FTP library... so this feature will be left out for now
126
+ // stat(path) {
127
+ // return new Promise((resolve, reject) => {
128
+ // fs.stat(path, (err, stats) => {
129
+ // if (err) {
130
+ // return reject(err)
131
+ // }
132
+ // resolve(new LocalFileInfo(stats))
133
+ // })
134
+ // })
135
+ // }
136
+ }
137
+
138
+ class LocalFileInfo extends FileInfo {
139
+ isDirectory() {
140
+ return this.original.isDirectory()
141
+ }
142
+ }
143
+
144
+ class FtpFileSystem extends FileSystem {
145
+ constructor(client) {
146
+ super()
147
+ this.client = client
148
+ }
149
+ static async create(host, port, user, password) {
150
+ const c = new FtpClient()
151
+ return new Promise((resolve, reject) => {
152
+ c.on('ready', () => {
153
+ resolve(new FtpFileSystem(c))
154
+ })
155
+ c.once('error', (err) => {
156
+ reject(err)
157
+ })
158
+ c.connect({
159
+ host,
160
+ port,
161
+ user,
162
+ password
163
+ })
164
+ })
165
+ }
166
+ list(path) {
167
+ return new Promise((resolve, reject) => {
168
+ this.client.list(path, (err, listing) => {
169
+ if (err) {
170
+ return reject(err)
171
+ }
172
+ resolve(listing.map(l => new FtpFileInfo(l)))
173
+ })
174
+ })
175
+ }
176
+ put(src, toPath) {
177
+ return new Promise((resolve, reject) => {
178
+ this.client.put(src, toPath, (err) => {
179
+ if (err) {
180
+ reject(err)
181
+ }
182
+ resolve(true)
183
+ })
184
+ })
185
+ }
186
+ get(path) {
187
+ return new Promise((resolve, reject) => {
188
+ this.client.get(path, (err, stream) => {
189
+ if (err) {
190
+ return reject(err)
191
+ }
192
+ resolve(stream)
193
+ })
194
+ })
195
+ }
196
+ mkdir(path, recursive) {
197
+ return new Promise((resolve, reject) => {
198
+ this.client.mkdir(path, recursive, err => {
199
+ if (err) {
200
+ return reject(err)
201
+ }
202
+ resolve(true)
203
+ })
204
+ })
205
+ }
206
+ rmdir(path, recursive) {
207
+ return new Promise((resolve, reject) => {
208
+ this.client.rmdir(path, recursive, err => {
209
+ if (err) {
210
+ return reject(err)
211
+ }
212
+ resolve(1)
213
+ })
214
+ })
215
+ }
216
+ delete(path) {
217
+ return new Promise((resolve, reject) => {
218
+ this.client.delete(path, err => {
219
+ if (err) {
220
+ return reject(err)
221
+ }
222
+ resolve(path)
223
+ })
224
+ })
225
+ }
226
+ rename(oldPath, newPath) {
227
+ return new Promise((resolve, reject) => {
228
+ this.client.rename(oldPath, newPath, (err) => {
229
+ if (err) {
230
+ return reject(err)
231
+ }
232
+ resolve(true)
233
+ })
234
+ })
235
+ }
236
+ }
237
+
238
+ class FtpFileInfo extends FileInfo {
239
+ constructor(original) {
240
+ super(original)
241
+ }
242
+ isDirectory() {
243
+ return this.original.type === 'd'
244
+ }
245
+ }
246
+
247
+ class SftpFileSystem extends FileSystem {
248
+ constructor(client) {
249
+ super()
250
+ this.client = client
251
+ }
252
+ static async create(host, port, user, password) {
253
+ const c = new SftpClient()
254
+ return new Promise((resolve, reject) => {
255
+ c.connect({
256
+ host,
257
+ port,
258
+ username: user,
259
+ password
260
+ }).then(() => {
261
+ resolve(new SftpFileSystem(c))
262
+ }).catch(err => {
263
+ reject(err)
264
+ })
265
+ })
266
+ }
267
+ async list(path) {
268
+ return new Promise((resolve, reject) => {
269
+ this.client.list(path)
270
+ .then(paths => {
271
+ resolve(paths.map(it => new SftpFileInfo(it)))
272
+ }).catch(err => {
273
+ reject(err)
274
+ })
275
+ })
276
+ }
277
+ put(src, toPath) {
278
+ return this.client.put(src, toPath)
279
+ }
280
+ get(path) {
281
+ return new Promise((resolve, reject) => {
282
+ try {
283
+ resolve(this.client.sftp.createReadStream(path))
284
+ } catch (e) {
285
+ reject(e)
286
+ }
287
+ })
288
+ }
289
+ mkdir(path, recursive) {
290
+ return this.client.mkdir(path, recursive)
291
+ }
292
+ rmdir(path, recursive) {
293
+ return this.client.rmdir(path, recursive)
294
+ }
295
+ delete(path) {
296
+ return this.client.delete(path)
297
+ }
298
+ rename(oldPath, newPath) {
299
+ return this.client.rename(oldPath, newPath)
300
+ }
301
+ }
302
+
303
+ class SftpFileInfo extends FileInfo {
304
+ constructor(original) {
305
+ super(original)
306
+ }
307
+ isDirectory() {
308
+ return this.original.type === 'd'
309
+ }
310
+ }
311
+
312
+
313
+ module.exports = {
314
+ FileSystem,
315
+ FileInfo,
316
+ LocalFileSystem,
317
+ FtpFileSystem,
318
+ SftpFileSystem,
319
+ SftpFileInfo
320
+ }
@@ -0,0 +1,126 @@
1
+ class Logger {
2
+ static config = {
3
+ DebuggerOn: false,
4
+ AccountName: 'System',
5
+ showTimestamps: false,
6
+ showAccount: true,
7
+ showType: false
8
+ };
9
+
10
+ static _originalConsole = {
11
+ log: console.log,
12
+ info: console.info,
13
+ warn: console.warn,
14
+ error: console.error,
15
+ debug: console.debug,
16
+ dir: console.dir
17
+ };
18
+
19
+ // Objeto de configuración asociado a la instancia de la clase
20
+ config = { ...Logger.config };
21
+
22
+ constructor(configAccount = null) {
23
+ if (configAccount) {
24
+ this.setAccount(configAccount);
25
+ }
26
+ }
27
+
28
+ setAccount(configAccount) {
29
+ const { DebuggerOn = false, ...remain } = configAccount;
30
+ this.config = { ...this.config, DebuggerOn, ...remain };
31
+ }
32
+
33
+ log(...args) {
34
+ Logger._handleMessage(this, 'log', ...args);
35
+ }
36
+
37
+ info(...args) {
38
+ Logger._handleMessage(this, 'info', ...args);
39
+ }
40
+
41
+ warn(...args) {
42
+ Logger._handleMessage(this, 'warn', ...args);
43
+ }
44
+
45
+ error(...args) {
46
+ Logger._handleMessage(this, 'error', ...args);
47
+ }
48
+
49
+ debug(...args) {
50
+ Logger._handleMessage(this, 'debug', ...args);
51
+ }
52
+
53
+ /**
54
+ * Establece las opciones de configuración.
55
+ * @param {object} configAccount Objeto JSON del account en Monalisa V2.
56
+ */
57
+ static setAccount(configAccount) {
58
+ if (configAccount) {
59
+ const { DebuggerOn = false, ...remain } = configAccount;
60
+ Logger.config = { ...Logger.config, DebuggerOn, ...remain };
61
+ }
62
+ }
63
+
64
+ // Métodos públicos (sin cambios para mantener compatibilidad)
65
+ static log(...args) {
66
+ Logger._handleMessage(Logger, 'log', ...args);
67
+ }
68
+
69
+ static info(...args) {
70
+ Logger._handleMessage(Logger, 'info', ...args);
71
+ }
72
+
73
+ static warn(...args) {
74
+ Logger._handleMessage(Logger, 'warn', ...args);
75
+ }
76
+
77
+ static error(...args) {
78
+ Logger._handleMessage(Logger, 'error', ...args);
79
+ }
80
+
81
+ static debug(...args) {
82
+ Logger._handleMessage(Logger, 'debug', ...args);
83
+ }
84
+
85
+ static _handleMessage(thisRef, type, ...args) {
86
+ if (thisRef?.config?.DebuggerOn || ['log', 'error'].includes(type)) {
87
+ Logger._originalConsole[type](...Logger._format(thisRef, type, ...args));
88
+ }
89
+ }
90
+
91
+ static _format(thisRef, type, ...args) {
92
+ const header = [];
93
+
94
+ if (thisRef?.config?.showTimestamps) {
95
+ header.push(`[${new Date().toISOString()}]`);
96
+ }
97
+ if (thisRef?.config?.showAccount) {
98
+ header.push(`[${thisRef.config.AccountName}]`);
99
+ }
100
+ if (thisRef?.config?.showType) {
101
+ header.push(`[${type.toUpperCase()}]`);
102
+ }
103
+
104
+ const logsData = [];
105
+ if (header.length) {
106
+ logsData.push(header.join(''))
107
+ }
108
+ for (let arg of args) {
109
+ if (arg instanceof Error) {
110
+ logsData.push(arg.stack);
111
+ } else {
112
+ logsData.push(arg);
113
+ }
114
+ }
115
+ return logsData;
116
+ }
117
+ }
118
+
119
+ // Overloading de las funciones de console
120
+ console.log = Logger.log
121
+ console.info = Logger.info
122
+ console.warn = Logger.warn
123
+ console.error = Logger.error
124
+ console.debug = Logger.debug
125
+
126
+ module.exports = Logger;
@@ -0,0 +1,61 @@
1
+ const nodemailer = require('nodemailer');
2
+ const Logger = require("./logger");
3
+
4
+ /**
5
+ * Obtiene la instancia del servicio para envío de correo electrónico.
6
+ * @param {object} mailSettings Parámetros de conexión.
7
+ * @returns Instancia de conexión al servidor de envío de correo.
8
+ * @see {@link https://nodemailer.com/smtp|NodeMailer SMTP TRANSPORT}
9
+ */
10
+ exports.getEmailSender = async (mailSettings) => {
11
+ const { host, port = '2525', user, pass, encryption = 'tls' } = mailSettings;
12
+
13
+ const emailTransportOptions = { host, port, 'auth': { user, pass } };
14
+ switch (encryption) {
15
+ case 'tls':
16
+ emailTransportOptions.tls = { rejectUnauthorized: true }
17
+ break;
18
+ case 'ssl':
19
+ emailTransportOptions.secure = true
20
+ break;
21
+ }
22
+
23
+ // Se conecta al servidor de envío de correos
24
+ const transporter = nodemailer.createTransport(emailTransportOptions);
25
+ return new Promise((resolve, reject) => {
26
+ transporter.verify(err => {
27
+ if (err) {
28
+ Logger.error(err);
29
+ reject(err);
30
+ } else {
31
+ Logger.debug(`Connected to email server ${host}`);
32
+ resolve(transporter);
33
+ }
34
+ })
35
+ });
36
+ };
37
+
38
+ /**
39
+ * Realiza el envío del mensaje de correo electrónico.
40
+ * @param {*} mailSender Instancia de conexión al servidor de envío de correos.
41
+ * @param {*} emailOptions Objeto con las opciones de correo electrónico.
42
+ * @returns Respuesta del envío del mensaje.
43
+ */
44
+ exports.sendEmail = async (mailSender, emailOptions, skipAttachment = true) => {
45
+ return new Promise((resolve, reject) => {
46
+ mailSender.sendMail(emailOptions).then(resolve).catch(err => {
47
+ if (skipAttachment && emailOptions?.attachments?.length) {
48
+ Logger.error("NodeMailer: Deleting attachments", {
49
+ count: emailOptions.attachments.length,
50
+ action: "cleanup",
51
+ source: "emailService"
52
+ });
53
+ Logger.error(err);
54
+ delete emailOptions.attachments;
55
+ this.sendEmail(mailSender, emailOptions, false).then(resolve).catch(reject);
56
+ } else {
57
+ reject(err);
58
+ }
59
+ });
60
+ });
61
+ }
@@ -0,0 +1,168 @@
1
+ const VtexApi = require("../../vtex/clients/VtexApi");
2
+ const Logger = require("./logger");
3
+ const Account = require("../../entities/account");
4
+
5
+ // VARIABLES ACCOUNT: unitProductValue (especificaciones de producto), componentsKitQuantity (cantidad de componentes del kit)
6
+
7
+ const convertQuantity = async (skuVtex, skuRefId, quantity, accountConfig, individualUnits) => {
8
+
9
+ Logger.setAccount(accountConfig);
10
+ Logger.debug('ProductUnitConversion: Inicio conversión', { skuVtex, skuRefId, quantity, accountConfig });
11
+
12
+ try {
13
+ const config = accountConfig?.ProductUnitConversion;
14
+
15
+ if (!config || !config.isActive) {
16
+ return quantity;
17
+ }
18
+
19
+ const unitProductFieldName = config.unitProduct; // String del nombre del campo
20
+ const componentsKits = config.useComponentsKits; // Booleano que indica si se utilizan kits de componentes
21
+ let unitProductValue = 1; // Valor por defecto
22
+ let componentsKitsQuantity = 1; // Valor por defecto
23
+ let componentsKitsQuantityMain = 1; // Valor por defecto
24
+ let unitMultiplierProduct = 1; // Valor por defecto unidad multiplicadora
25
+ let individualUnitsQuantity = individualUnits || 1;
26
+
27
+ let targetSku = [];
28
+
29
+ const vtexApi = new VtexApi(
30
+ accountConfig.AccountName,
31
+ accountConfig.Credentials.key,
32
+ accountConfig.Credentials.token
33
+ );
34
+
35
+ const detailSkuVtexPrincipal = await vtexApi.getDetailSkuById(skuVtex);
36
+ Logger.debug('ProductUnitConversion: Detalle SKU VTEX principal', { detailSkuVtexPrincipal });
37
+
38
+ if (!detailSkuVtexPrincipal || !Array.isArray(detailSkuVtexPrincipal) || detailSkuVtexPrincipal.length === 0) {
39
+ return quantity;
40
+ }
41
+
42
+ const productDetailVtexPrincipal = detailSkuVtexPrincipal[0];
43
+
44
+ if (productDetailVtexPrincipal && productDetailVtexPrincipal?.UnitMultiplier) {
45
+ unitMultiplierProduct= productDetailVtexPrincipal?.UnitMultiplier;
46
+ }
47
+
48
+ if (productDetailVtexPrincipal?.KitItems && productDetailVtexPrincipal.KitItems.length > 0) {
49
+ componentsKitsQuantityMain = productDetailVtexPrincipal.KitItems[0]?.Amount || 1;
50
+ }
51
+
52
+ if (componentsKits && config?.relationalProductPrefix?.length > 0) {
53
+ const relationSkuRef = buildKitSku(skuRefId, config.relationalProductPrefix);
54
+ targetSku = await vtexApi.getDetailSkuPerSkuRef(relationSkuRef);
55
+
56
+ for (let i = 0; i < targetSku.length; i++) {
57
+ const sku = targetSku[i];
58
+ if (sku.KitItems.length === 1) {
59
+ componentsKitsQuantity = sku.KitItems[0].Amount || 1;
60
+ }
61
+ }
62
+ }
63
+
64
+ if (unitProductFieldName && unitProductFieldName !== "" && accountConfig) {
65
+ const specificationProductVtex = await getProductSpecificationPerProductId(productDetailVtexPrincipal.ProductId, vtexApi);
66
+ if (specificationProductVtex && specificationProductVtex[unitProductFieldName]) {
67
+ unitProductValue = parseInt(specificationProductVtex[unitProductFieldName][0]) || 1;
68
+ }
69
+ }
70
+
71
+ const expression = getExpression(skuRefId, config.rules);
72
+
73
+ let variables = {
74
+ quantity,
75
+ unitProductValue,
76
+ componentsKitsQuantity,
77
+ componentsKitsQuantityMain,
78
+ individualUnitsQuantity,
79
+ unitMultiplierProduct
80
+ };
81
+
82
+ Logger.debug('ProductUnitConversion: Evaluando expresión', { expression, variables });
83
+ const result = evaluateExpression(expression, variables);
84
+
85
+ Logger.debug('ProductUnitConversion: Resultado de conversión', { result });
86
+ return typeof result === 'number' ? result : quantity;
87
+
88
+ } catch (error) {
89
+ Logger.error('ProductUnitConversion: Error en conversión', { skuVtex, skuRefId, quantity, error: error.message });
90
+ return quantity;
91
+ }
92
+ };
93
+
94
+ const buildKitSku = (skuRefId, prefixes) => {
95
+ if (!prefixes || !Array.isArray(prefixes)) {
96
+ return skuRefId;
97
+ }
98
+
99
+ let kitSku = [];
100
+
101
+ prefixes.forEach(prefix => {
102
+ if (prefix && prefix.value) {
103
+ if (prefix.position === 'initial') {
104
+ if (!skuRefId.startsWith(prefix.value)) {
105
+ kitSku.push(`${prefix.value}${skuRefId}`);
106
+ }
107
+ } else if (prefix.position === 'end') {
108
+ if (!skuRefId.endsWith(prefix.value)) {
109
+ kitSku.push(`${skuRefId}${prefix.value}`);
110
+ }
111
+ }
112
+ }
113
+ });
114
+
115
+ return kitSku;
116
+ };
117
+
118
+
119
+ const getExpression = (sku, rules) => {
120
+ if (!rules) {
121
+ return 'quantity';
122
+ }
123
+
124
+ if (rules.items && rules.items[sku]) {
125
+ return rules.items[sku];
126
+ }
127
+
128
+ return rules.default || 'quantity';
129
+ };
130
+
131
+ const evaluateExpression = (expression, variables) => {
132
+ try {
133
+ const { quantity, unitProductValue, componentsKitsQuantity, componentsKitsQuantityMain, individualUnitsQuantity, unitMultiplierProduct } = variables;
134
+ const result = eval(expression);
135
+ return Math.round(parseFloat(result));
136
+ } catch (error) {
137
+ Logger.error('ProductUnitConversion: Error evaluando expresión', {
138
+ expression,
139
+ variables,
140
+ error: error.message
141
+ });
142
+ return variables.quantity;
143
+ }
144
+ };
145
+
146
+ const getProductSpecificationPerProductId = async (productId, vtexApi) => {
147
+ try {
148
+ const response = await vtexApi.fetch(`/catalog_system/pvt/products/${productId}/specification`, {
149
+ method: "GET",
150
+ });
151
+ if (response?.status < 200 || response?.status > 299) {
152
+ throw new Error("Error getting SKU in VTEX");
153
+ }
154
+
155
+ let specifications = {};
156
+ for (let i = 0; i < response.data.length; i++) {
157
+ const esp = response.data[i];
158
+ specifications[esp.Name] = esp.Value;
159
+ }
160
+ return specifications;
161
+
162
+ } catch (error) {
163
+ Logger.error("Error getting product specification", { productId, error: error.message });
164
+ return null;
165
+ }
166
+ };
167
+
168
+ module.exports = convertQuantity;