n8n-nodes-atendix 1.3.5 → 1.3.7
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.
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { IAuthenticateGeneric, ICredentialType, INodeProperties, ICredentialDataDecryptedObject, ICredentialTestRequest } from 'n8n-workflow';
|
|
2
|
+
export declare class TrayApiAuto implements ICredentialType {
|
|
3
|
+
name: string;
|
|
4
|
+
displayName: string;
|
|
5
|
+
icon: any;
|
|
6
|
+
documentationUrl: string;
|
|
7
|
+
properties: INodeProperties[];
|
|
8
|
+
authenticate: IAuthenticateGeneric;
|
|
9
|
+
preAuthentication(credentials: ICredentialDataDecryptedObject): Promise<ICredentialDataDecryptedObject>;
|
|
10
|
+
private _authenticateFlow;
|
|
11
|
+
private _refreshTokenFlow;
|
|
12
|
+
test: ICredentialTestRequest;
|
|
13
|
+
}
|
|
@@ -1,9 +1,4 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/* Arquivo: n8n-nodes-atendix/credentials/TrayApiAuto.credentials.ts
|
|
3
|
-
*
|
|
4
|
-
* Credenciais Tray API - Autenticação Automática
|
|
5
|
-
* Ajustado para evitar conflitos de nomes e erros de tipagem (unknown).
|
|
6
|
-
*/
|
|
7
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
3
|
exports.TrayApiAuto = void 0;
|
|
9
4
|
const CONSUMER_KEY = 'edb6c989395fa2d22be2505dbbaa202a818ca0fd0eb222a7389fdedb3782a467';
|
|
@@ -12,10 +7,7 @@ class TrayApiAuto {
|
|
|
12
7
|
constructor() {
|
|
13
8
|
this.name = 'trayApiAuto';
|
|
14
9
|
this.displayName = 'Atendix - Tray API';
|
|
15
|
-
this.icon =
|
|
16
|
-
light: 'file:tray.svg',
|
|
17
|
-
dark: 'file:tray.svg',
|
|
18
|
-
};
|
|
10
|
+
this.icon = 'file:tray.svg';
|
|
19
11
|
this.documentationUrl = 'https://developers.tray.com.br/docs/';
|
|
20
12
|
this.properties = [
|
|
21
13
|
{
|
|
@@ -24,69 +16,39 @@ class TrayApiAuto {
|
|
|
24
16
|
type: 'string',
|
|
25
17
|
default: 'https://api.tray.com.br',
|
|
26
18
|
required: true,
|
|
19
|
+
placeholder: 'https://1225878.commercesuite.com.br/web_api',
|
|
27
20
|
description: 'Endereço base da API Tray (geralmente https://api.tray.com.br)',
|
|
28
21
|
},
|
|
29
22
|
{
|
|
30
23
|
displayName: 'Authorization Code',
|
|
31
24
|
name: 'authCode',
|
|
32
25
|
type: 'string',
|
|
33
|
-
typeOptions: {
|
|
34
|
-
password: true,
|
|
35
|
-
},
|
|
26
|
+
typeOptions: { password: true },
|
|
36
27
|
default: '',
|
|
37
28
|
required: true,
|
|
38
29
|
placeholder: 'a9777f8cdfe4cf41b0a72c295adef1f9c43a093052c9e6dd98d1e629a09f13f3',
|
|
39
30
|
description: 'Código de autorização único da loja (fornecido pela Tray)',
|
|
40
31
|
},
|
|
41
|
-
{
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
displayName: 'Refresh Token',
|
|
49
|
-
name: 'refreshToken',
|
|
50
|
-
type: 'hidden',
|
|
51
|
-
default: '',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
displayName: 'API Host',
|
|
55
|
-
name: 'apiHost',
|
|
56
|
-
type: 'hidden',
|
|
57
|
-
default: '',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
displayName: 'Token Expiration',
|
|
61
|
-
name: 'tokenExpiration',
|
|
62
|
-
type: 'hidden',
|
|
63
|
-
default: '',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
displayName: 'Store ID',
|
|
67
|
-
name: 'storeId',
|
|
68
|
-
type: 'hidden',
|
|
69
|
-
default: '',
|
|
70
|
-
},
|
|
32
|
+
{ displayName: 'Access Token', name: 'accessToken', type: 'hidden', default: '' },
|
|
33
|
+
{ displayName: 'Refresh Token', name: 'refreshToken', type: 'hidden', default: '' },
|
|
34
|
+
{ displayName: 'API Host', name: 'apiHost', type: 'hidden', default: '' },
|
|
35
|
+
{ displayName: 'Token Expiration', name: 'tokenExpiration', type: 'hidden', default: '' },
|
|
36
|
+
{ displayName: 'Store ID', name: 'storeId', type: 'hidden', default: '' },
|
|
71
37
|
];
|
|
72
|
-
// O segredo para o n8n injetar o token em todas as chamadas
|
|
73
38
|
this.authenticate = {
|
|
74
39
|
type: 'generic',
|
|
75
40
|
properties: {
|
|
76
|
-
qs: {
|
|
77
|
-
access_token: '={{$credentials.accessToken}}',
|
|
78
|
-
},
|
|
41
|
+
qs: { access_token: '={{$credentials.accessToken}}' },
|
|
79
42
|
},
|
|
80
43
|
};
|
|
81
44
|
this.test = {
|
|
82
45
|
request: {
|
|
83
|
-
// Usamos o apiAddress direto para o teste não quebrar se o apiHost estiver vazio
|
|
84
46
|
baseURL: '={{$credentials.apiAddress}}',
|
|
85
47
|
url: '/auth',
|
|
86
48
|
method: 'POST',
|
|
87
49
|
body: {
|
|
88
|
-
consumer_key:
|
|
89
|
-
consumer_secret:
|
|
50
|
+
consumer_key: CONSUMER_KEY,
|
|
51
|
+
consumer_secret: CONSUMER_SECRET,
|
|
90
52
|
code: '={{$credentials.authCode}}',
|
|
91
53
|
},
|
|
92
54
|
},
|
|
@@ -105,24 +67,18 @@ class TrayApiAuto {
|
|
|
105
67
|
}
|
|
106
68
|
return await this._authenticateFlow(credentials);
|
|
107
69
|
}
|
|
108
|
-
/**
|
|
109
|
-
* Renomeado para evitar conflito com a propriedade 'authenticate' da interface
|
|
110
|
-
*/
|
|
111
70
|
async _authenticateFlow(credentials) {
|
|
112
71
|
const apiAddress = credentials.apiAddress.replace(/\/$/, '');
|
|
113
72
|
const authCode = credentials.authCode;
|
|
114
|
-
// Pega as chaves direto do processo para garantir que não estão vazias
|
|
115
|
-
const consumerKey = 'edb6c989395fa2d22be2505dbbaa202a818ca0fd0eb222a7389fdedb3782a467';
|
|
116
|
-
const consumerSecret = '750dfb677368a6c070ef24de04e843eaa6085a05be298e98d5ad7139421ef7e6';
|
|
117
73
|
console.log(` Tentando autenticar na URL: ${apiAddress}/auth`);
|
|
118
|
-
console.log(` Usando Key: ${
|
|
74
|
+
console.log(` Usando Key: ${CONSUMER_KEY ? 'Preenchida' : 'VAZIA!'}`);
|
|
119
75
|
try {
|
|
120
76
|
const response = await fetch(`${apiAddress}/auth`, {
|
|
121
77
|
method: 'POST',
|
|
122
78
|
headers: { 'Content-Type': 'application/json' },
|
|
123
79
|
body: JSON.stringify({
|
|
124
|
-
consumer_key:
|
|
125
|
-
consumer_secret:
|
|
80
|
+
consumer_key: CONSUMER_KEY,
|
|
81
|
+
consumer_secret: CONSUMER_SECRET,
|
|
126
82
|
code: authCode,
|
|
127
83
|
}),
|
|
128
84
|
});
|
|
@@ -136,7 +92,7 @@ class TrayApiAuto {
|
|
|
136
92
|
...credentials,
|
|
137
93
|
accessToken: data.access_token,
|
|
138
94
|
refreshToken: data.refresh_token,
|
|
139
|
-
apiHost: data.api_host,
|
|
95
|
+
apiHost: data.api_host,
|
|
140
96
|
storeId: data.store_id,
|
|
141
97
|
tokenExpiration: data.date_expiration_access_token,
|
|
142
98
|
};
|
|
@@ -152,9 +108,8 @@ class TrayApiAuto {
|
|
|
152
108
|
try {
|
|
153
109
|
const response = await fetch(`${apiAddress}/auth?refresh_token=${refreshToken}`);
|
|
154
110
|
const data = (await response.json());
|
|
155
|
-
if (!response.ok)
|
|
111
|
+
if (!response.ok)
|
|
156
112
|
return await this._authenticateFlow(credentials);
|
|
157
|
-
}
|
|
158
113
|
return {
|
|
159
114
|
...credentials,
|
|
160
115
|
accessToken: data.access_token,
|
|
@@ -162,7 +117,7 @@ class TrayApiAuto {
|
|
|
162
117
|
tokenExpiration: data.date_expiration_access_token,
|
|
163
118
|
};
|
|
164
119
|
}
|
|
165
|
-
catch
|
|
120
|
+
catch {
|
|
166
121
|
return await this._authenticateFlow(credentials);
|
|
167
122
|
}
|
|
168
123
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Arquivo: n8n-nodes-atendix/nodes/Atendix/Atendix.node.ts
|
|
3
|
+
* v1.3.6 — Código-fonte original completo com variáveis de ambiente internalizadas
|
|
4
|
+
*/
|
|
5
|
+
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
6
|
+
export declare class Atendix implements INodeType {
|
|
7
|
+
description: INodeTypeDescription;
|
|
8
|
+
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
9
|
+
}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Atendix = void 0;
|
|
2
4
|
/**
|
|
3
5
|
* Arquivo: n8n-nodes-atendix/nodes/Atendix/Atendix.node.ts
|
|
4
|
-
*
|
|
5
|
-
* Node oficial Atendix - Tray Commerce
|
|
6
|
-
* Versão 1.2.0 - Licenciamento SaaS + Full Operations
|
|
6
|
+
* v1.3.6 — Código-fonte original completo com variáveis de ambiente internalizadas
|
|
7
7
|
*/
|
|
8
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
-
exports.Atendix = void 0;
|
|
10
8
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
9
|
+
const ATENDIX_AUTH_TOKEN = 'Bearer eyJhbGciOiJIUzI1NiJ9.e30.lEvvOyjcYVhrGiBHYZvV_HsmFEsc8lLw0yiNZk8lHJk';
|
|
10
|
+
const TRAY_CONSUMER_KEY = 'edb6c989395fa2d22be2505dbbaa202a818ca0fd0eb222a7389fdedb3782a467';
|
|
11
|
+
const TRAY_CONSUMER_SECRET = '750dfb677368a6c070ef24de04e843eaa6085a05be298e98d5ad7139421ef7e6';
|
|
11
12
|
const wait = (ms) => new Promise(res => setTimeout(res, ms));
|
|
12
13
|
class Atendix {
|
|
13
14
|
constructor() {
|
|
@@ -22,75 +23,51 @@ class Atendix {
|
|
|
22
23
|
version: 1,
|
|
23
24
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
24
25
|
description: 'Integração com a plataforma Tray Commerce',
|
|
25
|
-
defaults: {
|
|
26
|
-
name: 'Atendix - Tray API',
|
|
27
|
-
},
|
|
26
|
+
defaults: { name: 'Atendix - Tray API' },
|
|
28
27
|
inputs: ['main'],
|
|
29
28
|
outputs: ['main'],
|
|
30
29
|
credentials: [
|
|
31
|
-
{
|
|
32
|
-
name: 'trayApiAuto',
|
|
33
|
-
required: true,
|
|
34
|
-
displayName: 'Atendix - Tray API',
|
|
35
|
-
},
|
|
30
|
+
{ name: 'trayApiAuto', required: true, displayName: 'Atendix - Tray API' },
|
|
36
31
|
],
|
|
37
32
|
properties: [
|
|
38
|
-
// ==================== RESOURCE
|
|
33
|
+
// ==================== RESOURCE ====================
|
|
39
34
|
{
|
|
40
|
-
displayName: 'Resource',
|
|
41
|
-
name: 'resource',
|
|
42
|
-
type: 'options',
|
|
35
|
+
displayName: 'Resource', name: 'resource', type: 'options',
|
|
43
36
|
noDataExpression: true,
|
|
44
37
|
options: [
|
|
45
38
|
{ name: 'Pedido', value: 'order' },
|
|
46
39
|
{ name: 'Cliente', value: 'customer' },
|
|
47
40
|
{ name: 'Produto', value: 'product' },
|
|
41
|
+
{ name: 'Categoria', value: 'category' },
|
|
42
|
+
{ name: 'Variação', value: 'variant' },
|
|
43
|
+
{ name: 'Imagem de Produto', value: 'productImage' },
|
|
44
|
+
{ name: 'Nota Fiscal', value: 'invoice' },
|
|
48
45
|
],
|
|
49
46
|
default: 'order',
|
|
50
47
|
},
|
|
51
|
-
// ==================== PEDIDOS
|
|
48
|
+
// ==================== PEDIDOS ====================
|
|
52
49
|
{
|
|
53
|
-
displayName: 'Operation',
|
|
54
|
-
name: 'operation',
|
|
55
|
-
type: 'options',
|
|
50
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
56
51
|
noDataExpression: true,
|
|
57
52
|
displayOptions: { show: { resource: ['order'] } },
|
|
58
53
|
options: [
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
value: 'get',
|
|
62
|
-
description: 'Busca um pedido específico por ID',
|
|
63
|
-
action: 'Buscar um pedido',
|
|
64
|
-
},
|
|
65
|
-
{
|
|
66
|
-
name: 'Listar Pedidos',
|
|
67
|
-
value: 'list',
|
|
68
|
-
description: 'Lista pedidos com filtros opcionais',
|
|
69
|
-
action: 'Listar pedidos',
|
|
70
|
-
},
|
|
54
|
+
{ name: 'Buscar Pedido', value: 'get', description: 'Busca um pedido específico por ID', action: 'Buscar um pedido' },
|
|
55
|
+
{ name: 'Listar Pedidos', value: 'list', description: 'Lista pedidos com filtros opcionais', action: 'Listar pedidos' },
|
|
71
56
|
],
|
|
72
57
|
default: 'get',
|
|
73
58
|
},
|
|
74
59
|
{
|
|
75
|
-
displayName: 'ID do Pedido',
|
|
76
|
-
name: 'orderId',
|
|
77
|
-
type: 'string',
|
|
78
|
-
required: true,
|
|
60
|
+
displayName: 'ID do Pedido', name: 'orderId', type: 'string', required: true,
|
|
79
61
|
displayOptions: { show: { resource: ['order'], operation: ['get'] } },
|
|
80
62
|
default: '',
|
|
81
63
|
},
|
|
82
64
|
{
|
|
83
|
-
displayName: 'Filtros',
|
|
84
|
-
|
|
85
|
-
type: 'collection',
|
|
86
|
-
placeholder: 'Adicionar Filtro',
|
|
87
|
-
default: {},
|
|
65
|
+
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
66
|
+
placeholder: 'Adicionar Filtro', default: {},
|
|
88
67
|
displayOptions: { show: { resource: ['order'], operation: ['list'] } },
|
|
89
68
|
options: [
|
|
90
69
|
{
|
|
91
|
-
displayName: 'Status',
|
|
92
|
-
name: 'status',
|
|
93
|
-
type: 'options',
|
|
70
|
+
displayName: 'Status', name: 'status', type: 'options', default: '',
|
|
94
71
|
options: [
|
|
95
72
|
{ name: 'Pendente', value: 'pending' },
|
|
96
73
|
{ name: 'Aprovado', value: 'approved' },
|
|
@@ -98,16 +75,13 @@ class Atendix {
|
|
|
98
75
|
{ name: 'Entregue', value: 'delivered' },
|
|
99
76
|
{ name: 'Cancelado', value: 'cancelled' },
|
|
100
77
|
],
|
|
101
|
-
default: '',
|
|
102
78
|
},
|
|
103
79
|
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
104
80
|
],
|
|
105
81
|
},
|
|
106
|
-
// ==================== CLIENTES
|
|
82
|
+
// ==================== CLIENTES ====================
|
|
107
83
|
{
|
|
108
|
-
displayName: 'Operation',
|
|
109
|
-
name: 'operation',
|
|
110
|
-
type: 'options',
|
|
84
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
111
85
|
noDataExpression: true,
|
|
112
86
|
displayOptions: { show: { resource: ['customer'] } },
|
|
113
87
|
options: [
|
|
@@ -117,9 +91,7 @@ class Atendix {
|
|
|
117
91
|
default: 'get',
|
|
118
92
|
},
|
|
119
93
|
{
|
|
120
|
-
displayName: 'Buscar Por',
|
|
121
|
-
name: 'searchType',
|
|
122
|
-
type: 'options',
|
|
94
|
+
displayName: 'Buscar Por', name: 'searchType', type: 'options',
|
|
123
95
|
displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
|
|
124
96
|
options: [
|
|
125
97
|
{ name: 'E-mail', value: 'email' },
|
|
@@ -128,17 +100,13 @@ class Atendix {
|
|
|
128
100
|
default: 'email',
|
|
129
101
|
},
|
|
130
102
|
{
|
|
131
|
-
displayName: 'Valor',
|
|
132
|
-
name: 'searchValue',
|
|
133
|
-
type: 'string',
|
|
103
|
+
displayName: 'Valor', name: 'searchValue', type: 'string',
|
|
134
104
|
displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
|
|
135
105
|
default: '',
|
|
136
106
|
},
|
|
137
|
-
// ==================== PRODUTOS
|
|
107
|
+
// ==================== PRODUTOS ====================
|
|
138
108
|
{
|
|
139
|
-
displayName: 'Operation',
|
|
140
|
-
name: 'operation',
|
|
141
|
-
type: 'options',
|
|
109
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
142
110
|
noDataExpression: true,
|
|
143
111
|
displayOptions: { show: { resource: ['product'] } },
|
|
144
112
|
options: [
|
|
@@ -148,9 +116,7 @@ class Atendix {
|
|
|
148
116
|
default: 'get',
|
|
149
117
|
},
|
|
150
118
|
{
|
|
151
|
-
displayName: 'Buscar Por',
|
|
152
|
-
name: 'searchType',
|
|
153
|
-
type: 'options',
|
|
119
|
+
displayName: 'Buscar Por', name: 'searchType', type: 'options',
|
|
154
120
|
displayOptions: { show: { resource: ['product'], operation: ['get'] } },
|
|
155
121
|
options: [
|
|
156
122
|
{ name: 'ID do Produto', value: 'id' },
|
|
@@ -159,42 +125,157 @@ class Atendix {
|
|
|
159
125
|
default: 'id',
|
|
160
126
|
},
|
|
161
127
|
{
|
|
162
|
-
displayName: 'Valor',
|
|
163
|
-
name: 'searchValue',
|
|
164
|
-
type: 'string',
|
|
128
|
+
displayName: 'Valor', name: 'searchValue', type: 'string',
|
|
165
129
|
displayOptions: { show: { resource: ['product'], operation: ['get'] } },
|
|
166
130
|
default: '',
|
|
167
131
|
},
|
|
132
|
+
// ==================== CATEGORIAS ====================
|
|
133
|
+
{
|
|
134
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
135
|
+
noDataExpression: true,
|
|
136
|
+
displayOptions: { show: { resource: ['category'] } },
|
|
137
|
+
options: [
|
|
138
|
+
{ name: 'Listar Categorias', value: 'list', description: 'Lista todas as categorias da loja com paginação', action: 'Listar categorias' },
|
|
139
|
+
{ name: 'Buscar Categoria', value: 'get', description: 'Busca uma categoria específica por ID', action: 'Buscar uma categoria' },
|
|
140
|
+
],
|
|
141
|
+
default: 'list',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
displayName: 'ID da Categoria', name: 'categoryId', type: 'string', required: true,
|
|
145
|
+
displayOptions: { show: { resource: ['category'], operation: ['get'] } },
|
|
146
|
+
default: '', description: 'Código da categoria na Tray',
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
150
|
+
placeholder: 'Adicionar Filtro', default: {},
|
|
151
|
+
displayOptions: { show: { resource: ['category'], operation: ['list'] } },
|
|
152
|
+
options: [
|
|
153
|
+
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
154
|
+
{ displayName: 'Página', name: 'page', type: 'number', default: 1 },
|
|
155
|
+
],
|
|
156
|
+
},
|
|
157
|
+
// ==================== VARIAÇÕES ====================
|
|
158
|
+
{
|
|
159
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
160
|
+
noDataExpression: true,
|
|
161
|
+
displayOptions: { show: { resource: ['variant'] } },
|
|
162
|
+
options: [
|
|
163
|
+
{ name: 'Listar Variações', value: 'list', description: 'Lista todas as variações de um produto', action: 'Listar variações' },
|
|
164
|
+
{ name: 'Buscar Variação', value: 'get', description: 'Busca uma variação específica por ID', action: 'Buscar uma variação' },
|
|
165
|
+
],
|
|
166
|
+
default: 'list',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
displayName: 'ID do Produto', name: 'productIdForVariant', type: 'string', required: true,
|
|
170
|
+
displayOptions: { show: { resource: ['variant'], operation: ['list'] } },
|
|
171
|
+
default: '', description: 'ID do produto cujas variações serão listadas',
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
displayName: 'ID da Variação', name: 'variantId', type: 'string', required: true,
|
|
175
|
+
displayOptions: { show: { resource: ['variant'], operation: ['get'] } },
|
|
176
|
+
default: '', description: 'Código da variação na Tray',
|
|
177
|
+
},
|
|
178
|
+
{
|
|
179
|
+
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
180
|
+
placeholder: 'Adicionar Filtro', default: {},
|
|
181
|
+
displayOptions: { show: { resource: ['variant'], operation: ['list'] } },
|
|
182
|
+
options: [
|
|
183
|
+
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
184
|
+
{ displayName: 'Página', name: 'page', type: 'number', default: 1 },
|
|
185
|
+
],
|
|
186
|
+
},
|
|
187
|
+
// ==================== IMAGENS DE PRODUTO ====================
|
|
188
|
+
{
|
|
189
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
190
|
+
noDataExpression: true,
|
|
191
|
+
displayOptions: { show: { resource: ['productImage'] } },
|
|
192
|
+
options: [
|
|
193
|
+
{ name: 'Cadastrar Imagem de Produto', value: 'create', description: 'Cadastra ou atualiza a imagem de um produto', action: 'Cadastrar imagem de produto' },
|
|
194
|
+
{ name: 'Cadastrar Imagem de Variação', value: 'createVariant', description: 'Cadastra ou atualiza a imagem de uma variação', action: 'Cadastrar imagem de variação' },
|
|
195
|
+
],
|
|
196
|
+
default: 'create',
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
displayName: 'ID do Produto', name: 'productIdForImage', type: 'string', required: true,
|
|
200
|
+
displayOptions: { show: { resource: ['productImage'] } },
|
|
201
|
+
default: '', description: 'ID do produto na Tray',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
displayName: 'URL da Imagem', name: 'imageUrl', type: 'string', required: true,
|
|
205
|
+
displayOptions: { show: { resource: ['productImage'] } },
|
|
206
|
+
default: '', description: 'URL pública da imagem a ser cadastrada',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
displayName: 'ID da Variação', name: 'variantIdForImage', type: 'string', required: true,
|
|
210
|
+
displayOptions: { show: { resource: ['productImage'], operation: ['createVariant'] } },
|
|
211
|
+
default: '', description: 'ID da variação para associar a imagem',
|
|
212
|
+
},
|
|
213
|
+
// ==================== NOTA FISCAL ====================
|
|
214
|
+
{
|
|
215
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
216
|
+
noDataExpression: true,
|
|
217
|
+
displayOptions: { show: { resource: ['invoice'] } },
|
|
218
|
+
options: [
|
|
219
|
+
{ name: 'Listar NFs do Pedido', value: 'list', description: 'Lista notas fiscais de um pedido específico', action: 'Listar notas fiscais do pedido' },
|
|
220
|
+
{ name: 'Buscar Nota Fiscal', value: 'get', description: 'Busca uma nota fiscal por ID', action: 'Buscar nota fiscal por ID' },
|
|
221
|
+
{ name: 'Buscar NF por Pedido', value: 'getByOrder', description: 'Busca a nota fiscal vinculada a um pedido', action: 'Buscar nota fiscal por pedido' },
|
|
222
|
+
{ name: 'Cadastrar Nota Fiscal', value: 'create', description: 'Cadastra uma nova nota fiscal em um pedido', action: 'Cadastrar nota fiscal' },
|
|
223
|
+
],
|
|
224
|
+
default: 'list',
|
|
225
|
+
},
|
|
226
|
+
{
|
|
227
|
+
displayName: 'ID do Pedido', name: 'invoiceOrderId', type: 'string', required: true,
|
|
228
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['list', 'get', 'getByOrder', 'create'] } },
|
|
229
|
+
default: '', description: 'ID do pedido vinculado à nota fiscal',
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
displayName: 'ID da Nota Fiscal', name: 'invoiceId', type: 'string', required: true,
|
|
233
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['get'] } },
|
|
234
|
+
default: '', description: 'ID da nota fiscal (ex: 1347). Requer também o ID do Pedido.',
|
|
235
|
+
},
|
|
236
|
+
// invoice list usa GET /orders/:id/invoices — sem filtros adicionais
|
|
237
|
+
{
|
|
238
|
+
displayName: 'Dados da Nota Fiscal', name: 'invoiceData', type: 'collection',
|
|
239
|
+
placeholder: 'Adicionar Campo', default: {},
|
|
240
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['create'] } },
|
|
241
|
+
options: [
|
|
242
|
+
{ displayName: 'Número da NF', name: 'number', type: 'string', default: '' },
|
|
243
|
+
{ displayName: 'Série', name: 'serie', type: 'string', default: '' },
|
|
244
|
+
{ displayName: 'Data de Emissão', name: 'issue_date', type: 'string', default: '' },
|
|
245
|
+
{ displayName: 'Valor Total', name: 'value', type: 'number', default: 0 },
|
|
246
|
+
{ displayName: 'Chave de Acesso', name: 'key', type: 'string', default: '' },
|
|
247
|
+
{ displayName: 'XML da NF', name: 'xml', type: 'string', default: '' },
|
|
248
|
+
{ displayName: 'Link (DANFE/PDF)', name: 'link', type: 'string', default: '' },
|
|
249
|
+
],
|
|
250
|
+
},
|
|
168
251
|
],
|
|
169
252
|
};
|
|
170
253
|
}
|
|
171
254
|
async execute() {
|
|
172
|
-
var _a
|
|
255
|
+
var _a;
|
|
173
256
|
const items = this.getInputData();
|
|
174
257
|
const returnData = [];
|
|
175
258
|
const resource = this.getNodeParameter('resource', 0);
|
|
176
259
|
const operation = this.getNodeParameter('operation', 0);
|
|
177
|
-
|
|
178
|
-
if (!
|
|
179
|
-
console.error('❌ ATENDIX_AUTH_TOKEN não configurado nas variáveis de ambiente');
|
|
260
|
+
// Chave internalizadas — não dependem de variáveis de ambiente
|
|
261
|
+
if (!ATENDIX_AUTH_TOKEN) {
|
|
180
262
|
throw new Error('Configuração de validação Atendix incompleta. Entre em contato com o suporte.');
|
|
181
263
|
}
|
|
182
|
-
// 1.
|
|
264
|
+
// 1. Credenciais
|
|
183
265
|
const credentials = await this.getCredentials('trayApiAuto');
|
|
184
266
|
let baseUrl = (credentials.apiHost || credentials.apiAddress || '').trim().replace(/\/$/, '');
|
|
185
|
-
let licenseCheck;
|
|
186
267
|
// ==========================================
|
|
187
268
|
// 1.1 VALIDAÇÃO DE LICENÇA (SaaS GATEKEEPER)
|
|
188
269
|
// ==========================================
|
|
189
|
-
// Pegamos a URL base da loja para validar no seu servidor
|
|
190
270
|
const storeBaseUrl = baseUrl.replace('/web_api', '');
|
|
271
|
+
let licenseCheck;
|
|
191
272
|
try {
|
|
192
273
|
licenseCheck = await this.helpers.httpRequest({
|
|
193
274
|
method: 'POST',
|
|
194
275
|
url: 'https://n8n.mariapinho.com.br/webhook/valida-atendix',
|
|
195
276
|
headers: {
|
|
196
277
|
'Content-Type': 'application/json',
|
|
197
|
-
'Authorization':
|
|
278
|
+
'Authorization': ATENDIX_AUTH_TOKEN,
|
|
198
279
|
},
|
|
199
280
|
body: { url: storeBaseUrl },
|
|
200
281
|
json: true,
|
|
@@ -202,19 +283,15 @@ class Atendix {
|
|
|
202
283
|
console.error('LICENSE CHECK RESPONSE: ' + licenseCheck.autorizado);
|
|
203
284
|
if (!licenseCheck || licenseCheck.autorizado !== true) {
|
|
204
285
|
console.error('Resposta inválida do servidor de licenças:', licenseCheck);
|
|
205
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Sua licença Atendix para a loja ${storeBaseUrl} não está ativa
|
|
286
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Sua licença Atendix para a loja ${storeBaseUrl} não está ativa. Acesse https://atendix.co`);
|
|
206
287
|
}
|
|
207
288
|
}
|
|
208
289
|
catch (error) {
|
|
209
|
-
console.error('ATENDIX_AUTH_TOKEN:', atendixAuthToken);
|
|
210
290
|
console.error('STORE BASE URL:', storeBaseUrl);
|
|
211
|
-
console.error('LICENSECHECK:', licenseCheck);
|
|
212
291
|
console.error('Erro HTTP licença:', (_a = error === null || error === void 0 ? void 0 : error.response) === null || _a === void 0 ? void 0 : _a.status);
|
|
213
|
-
console.error('Body licença:', (_b = error === null || error === void 0 ? void 0 : error.response) === null || _b === void 0 ? void 0 : _b.data);
|
|
214
|
-
// Se for erro de acesso bloqueado, repassa. Se for erro de rede, avisa o usuário.
|
|
215
292
|
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
216
293
|
throw error;
|
|
217
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(),
|
|
294
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), 'Erro crítico: Não foi possível validar a licença do Atendix no servidor MariaPinho.');
|
|
218
295
|
}
|
|
219
296
|
// 2. TOKEN MANAGER (Fallback)
|
|
220
297
|
let accessToken = credentials.accessToken;
|
|
@@ -224,8 +301,8 @@ class Atendix {
|
|
|
224
301
|
method: 'POST',
|
|
225
302
|
url: `${baseUrl}/auth`,
|
|
226
303
|
body: {
|
|
227
|
-
consumer_key:
|
|
228
|
-
consumer_secret:
|
|
304
|
+
consumer_key: TRAY_CONSUMER_KEY,
|
|
305
|
+
consumer_secret: TRAY_CONSUMER_SECRET,
|
|
229
306
|
code: credentials.authCode,
|
|
230
307
|
},
|
|
231
308
|
json: true,
|
|
@@ -236,25 +313,21 @@ class Atendix {
|
|
|
236
313
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Falha na Autenticação Tray: ${authError.message}`);
|
|
237
314
|
}
|
|
238
315
|
}
|
|
316
|
+
// ==========================================
|
|
317
|
+
// 3. LOOP PRINCIPAL — Rate limit: 500ms entre itens
|
|
318
|
+
// ==========================================
|
|
239
319
|
for (let i = 0; i < items.length; i++) {
|
|
240
320
|
try {
|
|
321
|
+
if (i > 0)
|
|
322
|
+
await wait(500);
|
|
241
323
|
let responseData = {};
|
|
242
324
|
const qs = { access_token: accessToken };
|
|
243
325
|
// ==================== PEDIDOS ====================
|
|
244
326
|
if (resource === 'order') {
|
|
245
327
|
if (operation === 'get') {
|
|
246
328
|
const orderId = this.getNodeParameter('orderId', i);
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
// 500ms = 2 requisições por segundo = 120 por minuto (Seguro dentro dos 180)
|
|
250
|
-
await wait(500);
|
|
251
|
-
}
|
|
252
|
-
responseData = await this.helpers.httpRequest({
|
|
253
|
-
method: 'GET',
|
|
254
|
-
url: `${baseUrl}/orders/${orderId}`,
|
|
255
|
-
qs,
|
|
256
|
-
json: true,
|
|
257
|
-
});
|
|
329
|
+
console.log(`>>> ATENDIX - GET ${baseUrl}/orders/${orderId}`);
|
|
330
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${orderId}`, qs, json: true });
|
|
258
331
|
}
|
|
259
332
|
if (operation === 'list') {
|
|
260
333
|
const filters = this.getNodeParameter('filters', i, {});
|
|
@@ -262,17 +335,7 @@ class Atendix {
|
|
|
262
335
|
qs.status = filters.status;
|
|
263
336
|
if (filters.limit)
|
|
264
337
|
qs.limit = filters.limit;
|
|
265
|
-
|
|
266
|
-
// Dá uma respirada de 500ms entre cada item da lista
|
|
267
|
-
// 500ms = 2 requisições por segundo = 120 por minuto (Seguro dentro dos 180)
|
|
268
|
-
await wait(500);
|
|
269
|
-
}
|
|
270
|
-
responseData = await this.helpers.httpRequest({
|
|
271
|
-
method: 'GET',
|
|
272
|
-
url: `${baseUrl}/orders`,
|
|
273
|
-
qs,
|
|
274
|
-
json: true,
|
|
275
|
-
});
|
|
338
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders`, qs, json: true });
|
|
276
339
|
}
|
|
277
340
|
}
|
|
278
341
|
// ==================== CLIENTES ====================
|
|
@@ -284,14 +347,8 @@ class Atendix {
|
|
|
284
347
|
qs.email = searchValue;
|
|
285
348
|
else
|
|
286
349
|
qs.cpf_cnpj = searchValue;
|
|
287
|
-
responseData = await this.helpers.httpRequest({
|
|
288
|
-
method: 'GET',
|
|
289
|
-
url: `${baseUrl}/customers`,
|
|
290
|
-
qs,
|
|
291
|
-
json: true,
|
|
292
|
-
});
|
|
350
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/customers`, qs, json: true });
|
|
293
351
|
}
|
|
294
|
-
// Nota da Gerente: Se for adicionar UPSERT aqui, o código deve ser incluído abaixo
|
|
295
352
|
}
|
|
296
353
|
// ==================== PRODUTOS ====================
|
|
297
354
|
if (resource === 'product') {
|
|
@@ -299,24 +356,83 @@ class Atendix {
|
|
|
299
356
|
const searchType = this.getNodeParameter('searchType', i);
|
|
300
357
|
const searchValue = this.getNodeParameter('searchValue', i);
|
|
301
358
|
if (searchType === 'id') {
|
|
302
|
-
responseData = await this.helpers.httpRequest({
|
|
303
|
-
method: 'GET',
|
|
304
|
-
url: `${baseUrl}/products/${searchValue}`,
|
|
305
|
-
qs,
|
|
306
|
-
json: true,
|
|
307
|
-
});
|
|
359
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/products/${searchValue}`, qs, json: true });
|
|
308
360
|
}
|
|
309
361
|
else {
|
|
310
362
|
qs.sku = searchValue;
|
|
311
|
-
responseData = await this.helpers.httpRequest({
|
|
312
|
-
method: 'GET',
|
|
313
|
-
url: `${baseUrl}/products`,
|
|
314
|
-
qs,
|
|
315
|
-
json: true,
|
|
316
|
-
});
|
|
363
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/products`, qs, json: true });
|
|
317
364
|
}
|
|
318
365
|
}
|
|
319
|
-
|
|
366
|
+
}
|
|
367
|
+
// ==================== CATEGORIAS ====================
|
|
368
|
+
if (resource === 'category') {
|
|
369
|
+
if (operation === 'list') {
|
|
370
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
371
|
+
if (filters.limit)
|
|
372
|
+
qs.limit = filters.limit;
|
|
373
|
+
if (filters.page)
|
|
374
|
+
qs.page = filters.page;
|
|
375
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/categories`, qs, json: true });
|
|
376
|
+
}
|
|
377
|
+
if (operation === 'get') {
|
|
378
|
+
const categoryId = this.getNodeParameter('categoryId', i);
|
|
379
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/categories/${categoryId}`, qs, json: true });
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
// ==================== VARIAÇÕES ====================
|
|
383
|
+
if (resource === 'variant') {
|
|
384
|
+
if (operation === 'list') {
|
|
385
|
+
const productIdForVariant = this.getNodeParameter('productIdForVariant', i);
|
|
386
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
387
|
+
qs.product_id = productIdForVariant;
|
|
388
|
+
if (filters.limit)
|
|
389
|
+
qs.limit = filters.limit;
|
|
390
|
+
if (filters.page)
|
|
391
|
+
qs.page = filters.page;
|
|
392
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/variants`, qs, json: true });
|
|
393
|
+
}
|
|
394
|
+
if (operation === 'get') {
|
|
395
|
+
const variantId = this.getNodeParameter('variantId', i);
|
|
396
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/variants/${variantId}`, qs, json: true });
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
// ==================== IMAGENS DE PRODUTO ====================
|
|
400
|
+
if (resource === 'productImage') {
|
|
401
|
+
const productIdForImage = this.getNodeParameter('productIdForImage', i);
|
|
402
|
+
const imageUrl = this.getNodeParameter('imageUrl', i);
|
|
403
|
+
if (operation === 'create') {
|
|
404
|
+
const body = { ProductImage: { product_id: productIdForImage, url: imageUrl } };
|
|
405
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/products/${productIdForImage}/images`, qs, body, json: true });
|
|
406
|
+
}
|
|
407
|
+
if (operation === 'createVariant') {
|
|
408
|
+
const variantIdForImage = this.getNodeParameter('variantIdForImage', i);
|
|
409
|
+
const body = { VariantImage: { product_id: productIdForImage, variant_id: variantIdForImage, url: imageUrl } };
|
|
410
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/variants/${variantIdForImage}/images`, qs, body, json: true });
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// ==================== NOTA FISCAL ====================
|
|
414
|
+
if (resource === 'invoice') {
|
|
415
|
+
if (operation === 'list') {
|
|
416
|
+
// ✅ CORRETO: GET /orders/:order_id/invoices
|
|
417
|
+
// GET /invoices NÃO EXISTE na API Tray (confirmado no PRD §3.3)
|
|
418
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
419
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${invoiceOrderId}/invoices`, qs, json: true });
|
|
420
|
+
}
|
|
421
|
+
if (operation === 'get') {
|
|
422
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
423
|
+
const invoiceId = this.getNodeParameter('invoiceId', i);
|
|
424
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${invoiceOrderId}/invoices/${invoiceId}`, qs, json: true });
|
|
425
|
+
}
|
|
426
|
+
if (operation === 'getByOrder') {
|
|
427
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
428
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${invoiceOrderId}/invoices`, qs, json: true });
|
|
429
|
+
}
|
|
430
|
+
if (operation === 'create') {
|
|
431
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
432
|
+
const invoiceData = this.getNodeParameter('invoiceData', i, {});
|
|
433
|
+
const body = { Invoice: { order_id: invoiceOrderId, ...invoiceData } };
|
|
434
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/invoices`, qs, body, json: true });
|
|
435
|
+
}
|
|
320
436
|
}
|
|
321
437
|
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
322
438
|
returnData.push(...executionData);
|
package/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
var
|
|
4
|
-
Object.defineProperty(exports, "Atendix", { enumerable: true, get: function
|
|
5
|
-
var
|
|
6
|
-
Object.defineProperty(exports, "TrayApiAuto", { enumerable: true, get: function
|
|
3
|
+
var node = require("./dist/nodes/Atendix/Atendix.node");
|
|
4
|
+
Object.defineProperty(exports, "Atendix", { enumerable: true, get: function() { return node.Atendix; }});
|
|
5
|
+
var creds = require("./dist/credentials/TrayApiAuto.credentials");
|
|
6
|
+
Object.defineProperty(exports, "TrayApiAuto", { enumerable: true, get: function() { return creds.TrayApiAuto; }});
|
package/package.json
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "n8n-nodes-atendix",
|
|
3
|
-
"version": "1.3.
|
|
4
|
-
"description": "Conector Atendix para integração
|
|
5
|
-
"keywords": [
|
|
3
|
+
"version": "1.3.7",
|
|
4
|
+
"description": "Conector Atendix para integração com Tray Commerce",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"n8n-community-node-package"
|
|
7
|
+
],
|
|
6
8
|
"license": "MIT",
|
|
7
9
|
"homepage": "https://atendix.co",
|
|
8
10
|
"author": {
|
|
@@ -11,7 +13,7 @@
|
|
|
11
13
|
},
|
|
12
14
|
"main": "index.js",
|
|
13
15
|
"scripts": {
|
|
14
|
-
"build": "
|
|
16
|
+
"build": "tsc"
|
|
15
17
|
},
|
|
16
18
|
"files": [
|
|
17
19
|
"dist",
|
|
@@ -27,7 +29,9 @@
|
|
|
27
29
|
"dist/nodes/Atendix/Atendix.node.js"
|
|
28
30
|
]
|
|
29
31
|
},
|
|
30
|
-
"
|
|
31
|
-
"
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@types/node": "^18.19.130",
|
|
34
|
+
"n8n-workflow": "^2.13.0",
|
|
35
|
+
"typescript": "^5.9.3"
|
|
32
36
|
}
|
|
33
37
|
}
|