n8n-nodes-atendix 1.3.6 → 1.3.8
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.
- package/dist/nodes/Atendix/Atendix.node.js +104 -45
- package/package.json +1 -1
|
@@ -216,7 +216,7 @@ class Atendix {
|
|
|
216
216
|
noDataExpression: true,
|
|
217
217
|
displayOptions: { show: { resource: ['invoice'] } },
|
|
218
218
|
options: [
|
|
219
|
-
{ name: 'Listar
|
|
219
|
+
{ name: 'Listar NFs do Pedido', value: 'list', description: 'Lista notas fiscais de um pedido específico', action: 'Listar notas fiscais do pedido' },
|
|
220
220
|
{ name: 'Buscar Nota Fiscal', value: 'get', description: 'Busca uma nota fiscal por ID', action: 'Buscar nota fiscal por ID' },
|
|
221
221
|
{ name: 'Buscar NF por Pedido', value: 'getByOrder', description: 'Busca a nota fiscal vinculada a um pedido', action: 'Buscar nota fiscal por pedido' },
|
|
222
222
|
{ name: 'Cadastrar Nota Fiscal', value: 'create', description: 'Cadastra uma nova nota fiscal em um pedido', action: 'Cadastrar nota fiscal' },
|
|
@@ -225,7 +225,7 @@ class Atendix {
|
|
|
225
225
|
},
|
|
226
226
|
{
|
|
227
227
|
displayName: 'ID do Pedido', name: 'invoiceOrderId', type: 'string', required: true,
|
|
228
|
-
displayOptions: { show: { resource: ['invoice'], operation: ['get', 'getByOrder', 'create'] } },
|
|
228
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['list', 'get', 'getByOrder', 'create'] } },
|
|
229
229
|
default: '', description: 'ID do pedido vinculado à nota fiscal',
|
|
230
230
|
},
|
|
231
231
|
{
|
|
@@ -233,16 +233,7 @@ class Atendix {
|
|
|
233
233
|
displayOptions: { show: { resource: ['invoice'], operation: ['get'] } },
|
|
234
234
|
default: '', description: 'ID da nota fiscal (ex: 1347). Requer também o ID do Pedido.',
|
|
235
235
|
},
|
|
236
|
-
|
|
237
|
-
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
238
|
-
placeholder: 'Adicionar Filtro', default: {},
|
|
239
|
-
displayOptions: { show: { resource: ['invoice'], operation: ['list'] } },
|
|
240
|
-
options: [
|
|
241
|
-
{ displayName: 'ID do Pedido', name: 'orderId', type: 'string', default: '' },
|
|
242
|
-
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
243
|
-
{ displayName: 'Página', name: 'page', type: 'number', default: 1 },
|
|
244
|
-
],
|
|
245
|
-
},
|
|
236
|
+
// invoice list usa GET /orders/:id/invoices — sem filtros adicionais
|
|
246
237
|
{
|
|
247
238
|
displayName: 'Dados da Nota Fiscal', name: 'invoiceData', type: 'collection',
|
|
248
239
|
placeholder: 'Adicionar Campo', default: {},
|
|
@@ -261,27 +252,50 @@ class Atendix {
|
|
|
261
252
|
};
|
|
262
253
|
}
|
|
263
254
|
async execute() {
|
|
264
|
-
var _a;
|
|
255
|
+
var _a, _b, _c, _d;
|
|
265
256
|
const items = this.getInputData();
|
|
266
257
|
const returnData = [];
|
|
267
258
|
const resource = this.getNodeParameter('resource', 0);
|
|
268
259
|
const operation = this.getNodeParameter('operation', 0);
|
|
269
|
-
|
|
260
|
+
console.log(`\n========================================`);
|
|
261
|
+
console.log(`[ATENDIX v1.3.7] Iniciando execução`);
|
|
262
|
+
console.log(`[ATENDIX] resource=${resource} | operation=${operation} | items=${items.length}`);
|
|
263
|
+
console.log(`========================================`);
|
|
264
|
+
// Chaves internalizadas — não dependem de variáveis de ambiente
|
|
270
265
|
if (!ATENDIX_AUTH_TOKEN) {
|
|
266
|
+
console.error('[ATENDIX] ❌ ATENDIX_AUTH_TOKEN não encontrado no pacote');
|
|
271
267
|
throw new Error('Configuração de validação Atendix incompleta. Entre em contato com o suporte.');
|
|
272
268
|
}
|
|
273
269
|
// 1. Credenciais
|
|
274
270
|
const credentials = await this.getCredentials('trayApiAuto');
|
|
275
|
-
|
|
271
|
+
const apiHost = (credentials.apiHost || '').trim().replace(/\/$/, '');
|
|
272
|
+
const apiAddress = (credentials.apiAddress || '').trim().replace(/\/$/, '');
|
|
273
|
+
let baseUrl = (apiHost || apiAddress);
|
|
274
|
+
console.log(`[ATENDIX] apiHost = "${apiHost}"`);
|
|
275
|
+
console.log(`[ATENDIX] apiAddress = "${apiAddress}"`);
|
|
276
|
+
console.log(`[ATENDIX] baseUrl = "${baseUrl}"`);
|
|
277
|
+
console.log(`[ATENDIX] accessToken = ${credentials.accessToken ? '✅ presente em cache' : '⚠️ ausente — vai autenticar'}`);
|
|
278
|
+
console.log(`[ATENDIX] tokenExpiry = ${credentials.tokenExpiration || 'não definido'}`);
|
|
276
279
|
// ==========================================
|
|
277
280
|
// 1.1 VALIDAÇÃO DE LICENÇA (SaaS GATEKEEPER)
|
|
278
281
|
// ==========================================
|
|
279
282
|
const storeBaseUrl = baseUrl.replace('/web_api', '');
|
|
280
283
|
let licenseCheck;
|
|
284
|
+
// Detecta se está rodando no servidor da Maria Pinho (mesma rede Docker)
|
|
285
|
+
// Se sim, usa endereço interno do container para evitar loop via Traefik
|
|
286
|
+
const n8nHost = process.env.N8N_HOST || '';
|
|
287
|
+
const isInternalServer = n8nHost.includes('mariapinho.com.br');
|
|
288
|
+
const licenseUrlExternal = 'https://n8n.mariapinho.com.br/webhook/valida-atendix';
|
|
289
|
+
const licenseUrlInternal = 'http://n8n_webhook:5678/webhook/valida-atendix';
|
|
290
|
+
const licenseUrl = isInternalServer ? licenseUrlInternal : licenseUrlExternal;
|
|
291
|
+
console.log(`[ATENDIX] 🔐 Validando licença para: ${storeBaseUrl}`);
|
|
292
|
+
console.log(`[ATENDIX] 🔐 N8N_HOST detectado: "${n8nHost}"`);
|
|
293
|
+
console.log(`[ATENDIX] 🔐 Modo: ${isInternalServer ? 'INTERNO (Docker)' : 'EXTERNO (internet)'}`);
|
|
294
|
+
console.log(`[ATENDIX] 🔐 License URL: ${licenseUrl}`);
|
|
281
295
|
try {
|
|
282
296
|
licenseCheck = await this.helpers.httpRequest({
|
|
283
297
|
method: 'POST',
|
|
284
|
-
url:
|
|
298
|
+
url: licenseUrl,
|
|
285
299
|
headers: {
|
|
286
300
|
'Content-Type': 'application/json',
|
|
287
301
|
'Authorization': ATENDIX_AUTH_TOKEN,
|
|
@@ -289,22 +303,29 @@ class Atendix {
|
|
|
289
303
|
body: { url: storeBaseUrl },
|
|
290
304
|
json: true,
|
|
291
305
|
});
|
|
292
|
-
console.
|
|
306
|
+
console.log(`[ATENDIX] 🔐 License check response:`, JSON.stringify(licenseCheck));
|
|
293
307
|
if (!licenseCheck || licenseCheck.autorizado !== true) {
|
|
294
|
-
console.error(
|
|
308
|
+
console.error(`[ATENDIX] ❌ Licença NÃO autorizada para ${storeBaseUrl}`);
|
|
309
|
+
console.error(`[ATENDIX] ❌ Resposta do servidor:`, JSON.stringify(licenseCheck));
|
|
295
310
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Sua licença Atendix para a loja ${storeBaseUrl} não está ativa. Acesse https://atendix.co`);
|
|
296
311
|
}
|
|
312
|
+
console.log(`[ATENDIX] ✅ Licença OK`);
|
|
297
313
|
}
|
|
298
314
|
catch (error) {
|
|
299
|
-
console.error(
|
|
300
|
-
console.error(
|
|
315
|
+
console.error(`[ATENDIX] ❌ Erro na validação de licença`);
|
|
316
|
+
console.error(`[ATENDIX] licenseUrl = ${licenseUrl}`);
|
|
317
|
+
console.error(`[ATENDIX] storeBaseUrl = ${storeBaseUrl}`);
|
|
318
|
+
console.error(`[ATENDIX] HTTP status = ${(_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status}`);
|
|
319
|
+
console.error(`[ATENDIX] HTTP body = ${JSON.stringify((_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data)}`);
|
|
320
|
+
console.error(`[ATENDIX] message = ${error === null || error === void 0 ? void 0 : error.message}`);
|
|
301
321
|
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
302
322
|
throw error;
|
|
303
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(),
|
|
323
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Erro crítico na validação de licença: ${error === null || error === void 0 ? void 0 : error.message}`);
|
|
304
324
|
}
|
|
305
|
-
// 2. TOKEN MANAGER (Fallback)
|
|
325
|
+
// 2. TOKEN MANAGER (Fallback quando preAuthentication não preencheu)
|
|
306
326
|
let accessToken = credentials.accessToken;
|
|
307
327
|
if (!accessToken) {
|
|
328
|
+
console.log(`[ATENDIX] ⚠️ accessToken vazio — autenticando manualmente via ${baseUrl}/auth`);
|
|
308
329
|
try {
|
|
309
330
|
const authResponse = await this.helpers.httpRequest({
|
|
310
331
|
method: 'POST',
|
|
@@ -317,11 +338,16 @@ class Atendix {
|
|
|
317
338
|
json: true,
|
|
318
339
|
});
|
|
319
340
|
accessToken = authResponse.access_token;
|
|
341
|
+
console.log(`[ATENDIX] ✅ Token obtido via fallback: ${accessToken ? accessToken.substring(0, 20) + '...' : 'VAZIO'}`);
|
|
320
342
|
}
|
|
321
343
|
catch (authError) {
|
|
344
|
+
console.error(`[ATENDIX] ❌ Falha no fallback de autenticação: ${authError.message}`);
|
|
322
345
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Falha na Autenticação Tray: ${authError.message}`);
|
|
323
346
|
}
|
|
324
347
|
}
|
|
348
|
+
else {
|
|
349
|
+
console.log(`[ATENDIX] ✅ Usando token em cache: ${accessToken.substring(0, 20)}...`);
|
|
350
|
+
}
|
|
325
351
|
// ==========================================
|
|
326
352
|
// 3. LOOP PRINCIPAL — Rate limit: 500ms entre itens
|
|
327
353
|
// ==========================================
|
|
@@ -335,8 +361,10 @@ class Atendix {
|
|
|
335
361
|
if (resource === 'order') {
|
|
336
362
|
if (operation === 'get') {
|
|
337
363
|
const orderId = this.getNodeParameter('orderId', i);
|
|
338
|
-
|
|
339
|
-
|
|
364
|
+
const url = `${baseUrl}/orders/${orderId}`;
|
|
365
|
+
console.log(`[ATENDIX] 📦 GET ${url}`);
|
|
366
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
367
|
+
console.log(`[ATENDIX] ✅ Pedido retornado: ${JSON.stringify(responseData).substring(0, 200)}`);
|
|
340
368
|
}
|
|
341
369
|
if (operation === 'list') {
|
|
342
370
|
const filters = this.getNodeParameter('filters', i, {});
|
|
@@ -344,7 +372,9 @@ class Atendix {
|
|
|
344
372
|
qs.status = filters.status;
|
|
345
373
|
if (filters.limit)
|
|
346
374
|
qs.limit = filters.limit;
|
|
347
|
-
|
|
375
|
+
const url = `${baseUrl}/orders`;
|
|
376
|
+
console.log(`[ATENDIX] 📦 GET ${url} | qs=${JSON.stringify(qs)}`);
|
|
377
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
348
378
|
}
|
|
349
379
|
}
|
|
350
380
|
// ==================== CLIENTES ====================
|
|
@@ -356,7 +386,9 @@ class Atendix {
|
|
|
356
386
|
qs.email = searchValue;
|
|
357
387
|
else
|
|
358
388
|
qs.cpf_cnpj = searchValue;
|
|
359
|
-
|
|
389
|
+
const url = `${baseUrl}/customers`;
|
|
390
|
+
console.log(`[ATENDIX] 👤 GET ${url} | searchType=${searchType} value=${searchValue}`);
|
|
391
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
360
392
|
}
|
|
361
393
|
}
|
|
362
394
|
// ==================== PRODUTOS ====================
|
|
@@ -365,11 +397,15 @@ class Atendix {
|
|
|
365
397
|
const searchType = this.getNodeParameter('searchType', i);
|
|
366
398
|
const searchValue = this.getNodeParameter('searchValue', i);
|
|
367
399
|
if (searchType === 'id') {
|
|
368
|
-
|
|
400
|
+
const url = `${baseUrl}/products/${searchValue}`;
|
|
401
|
+
console.log(`[ATENDIX] 🛍️ GET ${url}`);
|
|
402
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
369
403
|
}
|
|
370
404
|
else {
|
|
405
|
+
const url = `${baseUrl}/products`;
|
|
371
406
|
qs.sku = searchValue;
|
|
372
|
-
|
|
407
|
+
console.log(`[ATENDIX] 🛍️ GET ${url} | sku=${searchValue}`);
|
|
408
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
373
409
|
}
|
|
374
410
|
}
|
|
375
411
|
}
|
|
@@ -381,11 +417,15 @@ class Atendix {
|
|
|
381
417
|
qs.limit = filters.limit;
|
|
382
418
|
if (filters.page)
|
|
383
419
|
qs.page = filters.page;
|
|
384
|
-
|
|
420
|
+
const url = `${baseUrl}/categories`;
|
|
421
|
+
console.log(`[ATENDIX] 📂 GET ${url} | qs=${JSON.stringify(qs)}`);
|
|
422
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
385
423
|
}
|
|
386
424
|
if (operation === 'get') {
|
|
387
425
|
const categoryId = this.getNodeParameter('categoryId', i);
|
|
388
|
-
|
|
426
|
+
const url = `${baseUrl}/categories/${categoryId}`;
|
|
427
|
+
console.log(`[ATENDIX] 📂 GET ${url}`);
|
|
428
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
389
429
|
}
|
|
390
430
|
}
|
|
391
431
|
// ==================== VARIAÇÕES ====================
|
|
@@ -398,11 +438,15 @@ class Atendix {
|
|
|
398
438
|
qs.limit = filters.limit;
|
|
399
439
|
if (filters.page)
|
|
400
440
|
qs.page = filters.page;
|
|
401
|
-
|
|
441
|
+
const url = `${baseUrl}/variants`;
|
|
442
|
+
console.log(`[ATENDIX] 🔀 GET ${url} | product_id=${productIdForVariant}`);
|
|
443
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
402
444
|
}
|
|
403
445
|
if (operation === 'get') {
|
|
404
446
|
const variantId = this.getNodeParameter('variantId', i);
|
|
405
|
-
|
|
447
|
+
const url = `${baseUrl}/variants/${variantId}`;
|
|
448
|
+
console.log(`[ATENDIX] 🔀 GET ${url}`);
|
|
449
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
406
450
|
}
|
|
407
451
|
}
|
|
408
452
|
// ==================== IMAGENS DE PRODUTO ====================
|
|
@@ -411,47 +455,61 @@ class Atendix {
|
|
|
411
455
|
const imageUrl = this.getNodeParameter('imageUrl', i);
|
|
412
456
|
if (operation === 'create') {
|
|
413
457
|
const body = { ProductImage: { product_id: productIdForImage, url: imageUrl } };
|
|
414
|
-
|
|
458
|
+
const url = `${baseUrl}/products/${productIdForImage}/images`;
|
|
459
|
+
console.log(`[ATENDIX] 🖼️ POST ${url} | body=${JSON.stringify(body)}`);
|
|
460
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url, qs, body, json: true });
|
|
415
461
|
}
|
|
416
462
|
if (operation === 'createVariant') {
|
|
417
463
|
const variantIdForImage = this.getNodeParameter('variantIdForImage', i);
|
|
418
464
|
const body = { VariantImage: { product_id: productIdForImage, variant_id: variantIdForImage, url: imageUrl } };
|
|
419
|
-
|
|
465
|
+
const url = `${baseUrl}/variants/${variantIdForImage}/images`;
|
|
466
|
+
console.log(`[ATENDIX] 🖼️ POST ${url} | body=${JSON.stringify(body)}`);
|
|
467
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url, qs, body, json: true });
|
|
420
468
|
}
|
|
421
469
|
}
|
|
422
470
|
// ==================== NOTA FISCAL ====================
|
|
423
471
|
if (resource === 'invoice') {
|
|
424
472
|
if (operation === 'list') {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
qs.page = filters.page;
|
|
432
|
-
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/invoices`, qs, json: true });
|
|
473
|
+
// ✅ CORRETO: GET /orders/:order_id/invoices
|
|
474
|
+
// GET /invoices NÃO EXISTE na API Tray (confirmado no PRD §3.3)
|
|
475
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
476
|
+
const url = `${baseUrl}/orders/${invoiceOrderId}/invoices`;
|
|
477
|
+
console.log(`[ATENDIX] 🧾 GET ${url}`);
|
|
478
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
433
479
|
}
|
|
434
480
|
if (operation === 'get') {
|
|
435
481
|
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
436
482
|
const invoiceId = this.getNodeParameter('invoiceId', i);
|
|
437
|
-
|
|
483
|
+
const url = `${baseUrl}/orders/${invoiceOrderId}/invoices/${invoiceId}`;
|
|
484
|
+
console.log(`[ATENDIX] 🧾 GET ${url}`);
|
|
485
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
438
486
|
}
|
|
439
487
|
if (operation === 'getByOrder') {
|
|
440
488
|
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
441
|
-
|
|
489
|
+
const url = `${baseUrl}/orders/${invoiceOrderId}/invoices`;
|
|
490
|
+
console.log(`[ATENDIX] 🧾 GET ${url}`);
|
|
491
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url, qs, json: true });
|
|
442
492
|
}
|
|
443
493
|
if (operation === 'create') {
|
|
444
494
|
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
445
495
|
const invoiceData = this.getNodeParameter('invoiceData', i, {});
|
|
446
496
|
const body = { Invoice: { order_id: invoiceOrderId, ...invoiceData } };
|
|
447
|
-
|
|
497
|
+
const url = `${baseUrl}/invoices`;
|
|
498
|
+
console.log(`[ATENDIX] 🧾 POST ${url} | body=${JSON.stringify(body)}`);
|
|
499
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url, qs, body, json: true });
|
|
448
500
|
}
|
|
449
501
|
}
|
|
502
|
+
console.log(`[ATENDIX] ✅ item[${i}] concluído`);
|
|
450
503
|
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
451
504
|
returnData.push(...executionData);
|
|
452
505
|
}
|
|
453
506
|
catch (error) {
|
|
454
507
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
508
|
+
console.error(`[ATENDIX] ❌ Erro no item[${i}]: ${errorMessage}`);
|
|
509
|
+
if (error instanceof Error && error.response) {
|
|
510
|
+
console.error(`[ATENDIX] ❌ HTTP status : ${(_c = error.response) === null || _c === void 0 ? void 0 : _c.status}`);
|
|
511
|
+
console.error(`[ATENDIX] ❌ HTTP body : ${JSON.stringify((_d = error.response) === null || _d === void 0 ? void 0 : _d.data)}`);
|
|
512
|
+
}
|
|
455
513
|
if (this.continueOnFail()) {
|
|
456
514
|
returnData.push({ json: { error: errorMessage }, pairedItem: { item: i } });
|
|
457
515
|
continue;
|
|
@@ -459,6 +517,7 @@ class Atendix {
|
|
|
459
517
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), errorMessage, { itemIndex: i });
|
|
460
518
|
}
|
|
461
519
|
}
|
|
520
|
+
console.log(`[ATENDIX] ✅ Execução finalizada — ${returnData.length} item(s) retornados`);
|
|
462
521
|
return [returnData];
|
|
463
522
|
}
|
|
464
523
|
}
|