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,77 @@
1
+ const VtexApi = require('../../vtex/clients/VtexApi');
2
+ const Logger = require("../../common/utils/logger");
3
+
4
+ /**
5
+ * Handles the event triggered by the Lambda function.
6
+ *
7
+ * @param {Object} event - The event object containing the request details.
8
+ * @returns {Promise<Object>} - A promise that resolves to the response object.
9
+ * @throws {Error} - If an error occurs during the execution.
10
+ */
11
+ module.exports.handler= async (event)=>{
12
+ let statusCode = 200;
13
+ let response = {};
14
+ for (const record of event.Records) {
15
+ Logger.debug("Message Body: ", record.body);
16
+ try {
17
+ const order = JSON.parse(record.body);
18
+ const AccountConfig = order?.accountConfig;
19
+ const AccountConfigParent = order?.accountConfigParent;
20
+ const orderId = order?.orderInfo?.OrderId;
21
+ const vtexApiChild = new VtexApi(AccountConfig.AccountName, AccountConfig.Credentials.key, AccountConfig.Credentials.token);
22
+ const vtexApiParent = new VtexApi(AccountConfigParent.AccountName, AccountConfigParent.Credentials.key, AccountConfigParent.Credentials.token);
23
+ const responseOrderInfoChild = await vtexApiChild.fetch(`/oms/pvt/orders/${orderId}`, { method: "GET" });
24
+ const responseOrderInfoParent = await vtexApiParent.fetch(`/oms/pvt/orders/${responseOrderInfoChild?.data?.marketplaceOrderId}`, { method: "GET" });
25
+ if (!responseOrderInfoParent || responseOrderInfoParent.status !== 200) {
26
+ statusCode = 404;
27
+ response = { error: responseOrderInfoParent };
28
+ Logger.error({ statusCode, response});
29
+ }
30
+ const email = responseOrderInfoParent?.data?.clientProfileData?.email;
31
+ const responseMailParent = await vtexApiParent.fetchOrderMail(email);
32
+ const clientId = responseMailParent?.data?.email?.split("+")[1]?.split("@")[0];
33
+ Logger.setAccount(AccountConfig)
34
+ if(!clientId){
35
+ statusCode = 404;
36
+ response = { error: `Client not found, email: ${email}` };
37
+ Logger.error(statusCode, response);
38
+ }
39
+
40
+ const responseMasterDataCT = await vtexApiParent.fetch(
41
+ `/dataentities/${AccountConfig.AcronymClientVtex}/search?_fields=_all&id=${clientId}`, { method: "GET" }
42
+ );
43
+
44
+ if (!responseMasterDataCT || responseMasterDataCT.status !== 200 || !responseMasterDataCT?.data?.length > 0) {
45
+ statusCode = 404;
46
+ response = { error: responseMasterDataCT };
47
+ Logger.error(statusCode, response);
48
+ }
49
+
50
+ let additionalFields = {
51
+ id : orderId,
52
+ }
53
+
54
+ const fields = AccountConfig?.AdditionalInfoOrderFromClient?.fields || [];
55
+ const data = responseMasterDataCT?.data[0] || {};
56
+
57
+ fields.forEach(field => {
58
+ additionalFields[field] = data[field] || ""; // Asigna el valor correspondiente o un string vacío si no existe
59
+ });
60
+ Logger.debug({ additionalFields });
61
+ const updateAdditionalFields = await vtexApiChild.fetch(
62
+ `/dataentities/${AccountConfig.AdditionalInfoOrderFromClient.nameEntity}/documents/`, { method: "PATCH", data: additionalFields });
63
+
64
+ if (!updateAdditionalFields && (updateAdditionalFields.status !== 200 || updateAdditionalFields.status !== 201 || updateAdditionalFields.status !== 204)) {
65
+ statusCode = 404;
66
+ response = { error: updateAdditionalFields };
67
+ Logger.error(statusCode, response);
68
+ }
69
+
70
+ Logger.debug(statusCode, { additionalFields });
71
+ }catch (error) {
72
+ Logger.error(error);
73
+ statusCode = 500;
74
+ response = { error: error.message };
75
+ }
76
+ }
77
+ }
@@ -0,0 +1,102 @@
1
+ const Apiresponse = require('../../../common/utils/api-response.js');
2
+ const {
3
+ handlePutRecordSchema,
4
+ handleGetRecordSchema,
5
+ handleDeleteRecordSchema
6
+ } = require('../../../common/utils/schemas-utils.js');
7
+ const { handleSchemaVtexMD2 } = require('../../../common/utils/vtex/save-schemas.js');
8
+ const { handleHookVtexMD2 } = require('../../../common/utils/vtex/save-hooks.js');
9
+
10
+ const AccountData = require("../../../entities/account.js");
11
+
12
+ const { SCHEMAS_VTEX_TABLE, HOOKS_VTEX_TABLE } = process?.env ?? {};
13
+
14
+
15
+ module.exports.handler = async (event) => {
16
+ const schemaName = event?.pathParameters?.SchemaName ?? '';
17
+ const hookName = event?.pathParameters?.HookName ?? '';
18
+ const httpMethod = event?.httpMethod?.toUpperCase();
19
+ const account = event?.queryStringParameters;
20
+ const body = parseRequestBody(event?.body);
21
+
22
+ const arrPath = event.resource.split('/')
23
+ const [resource, identifier, provider] = arrPath.filter(part => part !== '')
24
+
25
+ try {
26
+ if (identifier === 'schemas' && provider === 'vtex') {
27
+ return await handleRequest(schemaName, httpMethod, account, body, SCHEMAS_VTEX_TABLE, identifier)
28
+ }
29
+
30
+ if (identifier === 'hooks' && provider === 'vtex') {
31
+ return await handleRequest(hookName, httpMethod, account, body, HOOKS_VTEX_TABLE, identifier)
32
+ }
33
+ return Apiresponse.response(404, `Resource: ${identifier} not found`)
34
+ } catch (error) {
35
+ return Apiresponse.response(500, `Internal Server Error: ${error.message}`);
36
+ }
37
+ };
38
+
39
+ const handleRequest = async (id, httpMethod, account, body, tableName, identifier) => {
40
+ switch (httpMethod) {
41
+ case 'PUT':
42
+ return await handlePutRecordSchema(id, body, tableName, identifier);
43
+
44
+ case 'GET':
45
+ return await handleGetRecordSchema({ id, tableName, identifier });
46
+
47
+ case 'DELETE':
48
+ if (identifier === 'schemas') {
49
+ if (account?.an && account?.entity) {
50
+ const accountConfig = await getAccountConfig(account.an);
51
+ return await handleSchemaVtexMD2({
52
+ id,
53
+ entityName: account.entity,
54
+ data: {},
55
+ origin: accountConfig,
56
+ httpMethod,
57
+ });
58
+ }
59
+ }
60
+
61
+ if (identifier === 'hooks') {
62
+ if (account?.an) {
63
+ const accountConfig = await getAccountConfig(account.an);
64
+ return await handleHookVtexMD2({
65
+ id,
66
+ data: {},
67
+ origin: accountConfig,
68
+ httpMethod,
69
+ });
70
+ }
71
+ }
72
+
73
+ return await handleDeleteRecordSchema({ id, tableName, identifier });
74
+
75
+ default:
76
+ return Apiresponse.response(405, `Method ${httpMethod} not allowed`);
77
+ }
78
+ };
79
+
80
+ const getAccountConfig = async (accountName) => {
81
+ const accountConfig = await AccountData.getAccountDataByAccountName(accountName);
82
+ if (!accountConfig) {
83
+ throw new Error(`Account configuration not found for account: ${accountName}`);
84
+ }
85
+ const { AccountName, Credentials } = accountConfig;
86
+ if (!AccountName || !Credentials?.key || !Credentials?.token) {
87
+ throw new Error(`Incomplete account configuration for account: ${accountName}`);
88
+ }
89
+ return {
90
+ AccountName,
91
+ AppKey: Credentials.key,
92
+ AppToken: Credentials.token,
93
+ };
94
+ };
95
+
96
+ const parseRequestBody = (body) => {
97
+ try {
98
+ return body ? JSON.parse(body) : {};
99
+ } catch (error) {
100
+ throw new Error("Invalid JSON body format.");
101
+ }
102
+ };
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Funciones usadas en GAS (Google Apps Script) para ejecutar la actualización
3
+ * de roles con base a la información enviada por correo electrónico.
4
+ */
5
+
6
+ // Se obtiene la referencia a las propiedades del script
7
+ const propsServices = PropertiesService.getScriptProperties();
8
+ const {
9
+ email_subject = 'Roles process',
10
+ auth_username,
11
+ auth_password,
12
+ auth_url,
13
+ service_url,
14
+ from_name = 'User roles manager',
15
+ limit_emails = 5
16
+ } = propsServices.getProperties();
17
+
18
+ /**
19
+ * Función callback del trigger configurado en Activadores(Triggers)
20
+ */
21
+ function callbackTrigger() {
22
+ // Si se desea pausar la ejecución automática, se debe comentar el llamado a la función readEmails()
23
+ readEmails();
24
+ }
25
+
26
+ /**
27
+ * Función que se encarga de ejecutar el proceso de consultar los mensajes de correo para ejecutar la actualización de roles.
28
+ */
29
+ function readEmails() {
30
+ // Se consulta los mensajes de correo sin leer y que tenga el asunto configurado en la propiedad 'email_subject'
31
+ const threads = GmailApp.search(`is:unread subject:(${email_subject})`);
32
+ if (!threads.length) {
33
+ return;
34
+ }
35
+
36
+ // Se obtiene el token de autenticación para ejecutar el servicio
37
+ let authResponse = getAuthToken();
38
+
39
+ // Contador de correos electrónicos
40
+ let counter = 0;
41
+
42
+ // Se consulta la lista de mensajes en cada uno de los hilos de mensajes
43
+ for (let thread of threads) {
44
+ // Se obtiene los mensajes de cada uno de los hilos
45
+ const messages = thread.getMessages();
46
+ for (const message of messages) {
47
+ const messageId = message.getId();
48
+ // Si el mensaje está sin leer, se ejecuta el proceso
49
+ if (message.isUnread()) {
50
+ // Si el contador no ha llegado al límite de correos
51
+ if (counter < limit_emails) {
52
+ // Se obtiene el email de quien envía el mensaje y los archivos adjuntos
53
+ let { name, email } = getFromMail(message), attachments = message.getAttachments();
54
+ if (email) {
55
+ // Se obtiene el template HTML del mensaje de correo
56
+ let templateEmail = HtmlService.createTemplateFromFile('EmailTemplate');
57
+ templateEmail.username = name;
58
+
59
+ // Se valida el número de archivos adjuntos
60
+ if (attachments.length == 1) {
61
+ executeService(authResponse, message, templateEmail, email, attachments[0]);
62
+ } else {
63
+ templateEmail.message = attachments.length == 0 ?
64
+ 'Su solicitud no pudo ser procesada, debido a que el correo no contiene un archivo adjunto.' :
65
+ `Su solicitud no pudo ser procesada, debido a que el correo contiene más de un archivo adjunto (${attachments.length}).`;
66
+ sendEmailNotification(message, templateEmail, email);
67
+ }
68
+ } else {
69
+ console.error(`Email message ${messageId} without from email address`);
70
+ }
71
+
72
+ // Se marca el correo como leido
73
+ message.markRead();
74
+ counter++;
75
+ } else {
76
+ // Si el contador de correos supera el límite, se finaliza el loop de threads
77
+ break;
78
+ }
79
+ } else {
80
+ }
81
+ }
82
+
83
+ // Si el contador de correos supera el límite, se finaliza el loop de threads
84
+ if (counter >= limit_emails) {
85
+ break;
86
+ }
87
+ }
88
+ }
89
+
90
+ /**
91
+ * Obtiene el token de autenticación para ejecutar el servicio que se encarga de actualizar los roles de usuario.
92
+ * @return Objeto JSON con la respuesta de la API.
93
+ */
94
+ function getAuthToken() {
95
+ // Token de autenticación Basic para ejecutar la autenticación al servicio
96
+ const basicToken = Utilities.base64Encode(auth_username + ':' + auth_password);
97
+
98
+ // Petición a la API de autenticación del servicio en Monaliza v2
99
+ let authRequest = UrlFetchApp.fetch(auth_url, {
100
+ method: 'POST',
101
+ headers: { 'Authorization': `Basic ${basicToken}` },
102
+ payload: {
103
+ 'grant_type': 'client_credentials',
104
+ 'scope': 'vtex.rol/vtex.rol.read'
105
+ },
106
+ // Se agrega este parámetro para que no se dispare la excepción en caso que la petición falle
107
+ muteHttpExceptions: true
108
+ });
109
+
110
+ let responseCode = authRequest.getResponseCode(), responseText = authRequest.getContentText();
111
+ if (responseCode !== 200) {
112
+ throw new Error(`Error ${responseCode}: `, responseText);
113
+ }
114
+
115
+ // Se obtiene el token de la respuesta de la petición
116
+ return JSON.parse(responseText);
117
+ }
118
+
119
+ /**
120
+ * Ejecuta el servicio encargado de actualizar los roles de usuario.
121
+ * @param {object} authResponse Objeto JSON con el token de autenticación.
122
+ * @param {GmailApp.GmailMessage} message Referencia al mensaje de correo enviado.
123
+ * @param {HtmlService.HtmlTemplate} templateEmail Referencia al template a usar en el cuerpo del correo electrónico.
124
+ * @param {string} email Dirección de correo del remitente.
125
+ * @param {GmailApp.GmailAttachment} attachment Archivo adjunto del correo electrónico.
126
+ */
127
+ function executeService(authResponse, message, templateEmail, email, attachment) {
128
+ const boundary = 'googleAppsScriptBoundary';
129
+ // Se obtiene el nombre y tipo del archivo adjunto a enviar al servicio de actualización de roles
130
+ const fileName = attachment.getName(), fileType = attachment.getContentType();
131
+ // Content para el email del remitente
132
+ let emailData = '';
133
+ emailData += `--${boundary}\r\n`;
134
+ emailData += `Content-Disposition: form-data; name="email"\r\n\r\n`;
135
+ emailData += `${email}\r\n`;
136
+
137
+ // Se define el blob para el campo email
138
+ let emailFieldBlob = Utilities.newBlob(emailData).getBytes();
139
+
140
+ // Content para el archivo adjunto
141
+ let fileData = '';
142
+ fileData += `--${boundary}\r\n`;
143
+ fileData += `Content-Disposition: form-data; name="file"; filename="${fileName}"\r\n`;
144
+ fileData += `Content-Type: ${fileType}\r\n\r\n`;
145
+
146
+ // Se define el blob para el campo del archivo adjunto
147
+ let fileFieldBlob = Utilities.newBlob(fileData).getBytes().concat(attachment.copyBlob().getBytes());
148
+
149
+ // Se define el cuerpo de la petición con el email del remitente y la data del archivo adjunto, concatenando el cierre del bloque
150
+ let payload = emailFieldBlob.concat(fileFieldBlob)
151
+ .concat(Utilities.newBlob(`\r\n--${boundary}--`).getBytes());
152
+ const serviceRequest = UrlFetchApp.fetch(service_url, {
153
+ method: 'POST',
154
+ headers: { 'Authorization': `Bearer ${authResponse.access_token}` },
155
+ contentType: `multipart/form-data; boundary=${boundary}`,
156
+ payload: payload,
157
+ // Se agrega este parámetro para que no se dispare la excepción en caso que la petición falle
158
+ muteHttpExceptions: true,
159
+ });
160
+
161
+ // Se obtiene el código y cuerpo de la respuesta de la petición.
162
+ const serviceResponseCode = serviceRequest.getResponseCode();
163
+ // Se obtiene la respuesta de la petición en formato JSON
164
+ let serviceResponse = JSON.parse(serviceRequest.getContentText());
165
+
166
+ // Se establece el texto del cuerpo del mensaje de respuesta
167
+ templateEmail.message = serviceResponseCode == 200 ?
168
+ 'Su solicitud ha sido procesada satisfactoriamente. En el archivo adjunto podrá encontrar el resumen de ejecución.' :
169
+ 'Su solicitud no pudo ser sido procesada satisfactoriamente. En el archivo adjunto podrá encontrar el detalle del error presentado.';
170
+
171
+ sendEmailNotification(message, templateEmail, email, [
172
+ Utilities.newBlob(JSON.stringify(serviceResponse, null, 2), 'application/json', 'ServiceResponse.json')
173
+ ]);
174
+ }
175
+
176
+ /**
177
+ * Ejecuta el proceso de responder un mensaje de correo electrónico.
178
+ * @param {GmailApp.GmailMessage} message Referencia al mensaje de correo enviado.
179
+ * @param {HtmlService.HtmlTemplate} templateEmail Referencia al template a usar en el cuerpo del correo electrónico.
180
+ * @param {string} email Dirección de correo del remitente.
181
+ * @param {GmailApp.GmailAttachment[]} attachments Archivos adjuntos al correo de respuesta.
182
+ */
183
+ function sendEmailNotification(message, templateEmail, email, attachments = null) {
184
+ let params = {
185
+ // Cuerpo del mensaje en formato HTML
186
+ htmlBody: templateEmail.evaluate().getContent(),
187
+ // Nombre de quien responde el correo
188
+ name: from_name,
189
+ // Parámetro para que en el correo se incluya el noreply@dominio.com
190
+ noReply: true
191
+ };
192
+
193
+ // Se valida si se incluye archivos adjuntos en la respuesta del correo
194
+ if (attachments?.length) {
195
+ params.attachments = attachments;
196
+ }
197
+
198
+ // Se responde el mensaje
199
+ let replyEmail = message.reply('', params);
200
+ }
201
+
202
+ /**
203
+ * Extrae el nombre y dirección de correo electrónico del remitente del mensaje.
204
+ * @param {GmailApp.GmailMessage} message Mensaje de correo electrónico.
205
+ * @return Nombre y dirección de correo electrónico del remitente.
206
+ */
207
+ function getFromMail(message) {
208
+ let sender = message.getFrom();
209
+ if (sender) {
210
+ let match = sender.match(/"*(.+)"* <(.+)>/);
211
+ if (match?.length == 3) {
212
+ const [, name, email] = match;
213
+ if (name && email) {
214
+ return { name, email };
215
+ }
216
+ }
217
+
218
+ return { name: 'Usuario', email: sender };
219
+ }
220
+ return {};
221
+ }