n8n-nodes-atendix 1.0.1 → 1.3.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.
- package/dist/credentials/TrayApiAuto.credentials.js +82 -100
- package/dist/nodes/Atendix/Atendix.node.js +253 -209
- package/index.js +6 -2
- package/package.json +33 -37
- package/README.md +0 -68
- package/dist/credentials/TrayApiAuto.credentials.d.ts +0 -16
- package/dist/icons/tray.svg +0 -21
- package/dist/nodes/Atendix/Atendix.node.d.ts +0 -11
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
2
|
+
/**
|
|
3
|
+
* n8n-nodes-atendix — Credenciais Tray Commerce
|
|
4
|
+
* v1.3.0 — CORREÇÃO CRÍTICA:
|
|
5
|
+
* URL: {store_id}.commercesuite.com.br/web_api (api.tray.com.br não existe)
|
|
6
|
+
* consumer_key/secret agora são campos de credencial (não env vars)
|
|
6
7
|
*/
|
|
7
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
9
|
exports.TrayApiAuto = void 0;
|
|
9
|
-
|
|
10
|
-
const CONSUMER_SECRET = process.env.TRAY_CONSUMER_SECRET;
|
|
10
|
+
|
|
11
11
|
class TrayApiAuto {
|
|
12
12
|
constructor() {
|
|
13
13
|
this.name = 'trayApiAuto';
|
|
@@ -19,150 +19,132 @@ class TrayApiAuto {
|
|
|
19
19
|
this.documentationUrl = 'https://developers.tray.com.br/docs/';
|
|
20
20
|
this.properties = [
|
|
21
21
|
{
|
|
22
|
-
displayName: '
|
|
23
|
-
name: '
|
|
22
|
+
displayName: 'Store ID',
|
|
23
|
+
name: 'storeId',
|
|
24
24
|
type: 'string',
|
|
25
|
-
default: '
|
|
25
|
+
default: '',
|
|
26
26
|
required: true,
|
|
27
|
-
|
|
27
|
+
placeholder: '1225878',
|
|
28
|
+
description: 'ID da sua loja Tray. A URL base (https://{storeId}.commercesuite.com.br/web_api) é gerada automaticamente.',
|
|
28
29
|
},
|
|
29
30
|
{
|
|
30
|
-
displayName: '
|
|
31
|
-
name: '
|
|
31
|
+
displayName: 'Consumer Key',
|
|
32
|
+
name: 'consumerKey',
|
|
32
33
|
type: 'string',
|
|
33
|
-
typeOptions: {
|
|
34
|
-
password: true,
|
|
35
|
-
},
|
|
36
34
|
default: '',
|
|
37
35
|
required: true,
|
|
38
|
-
|
|
39
|
-
description: 'Código de autorização único da loja (fornecido pela Tray)',
|
|
36
|
+
description: 'Consumer Key fornecida pela Tray no painel de apps',
|
|
40
37
|
},
|
|
41
38
|
{
|
|
42
|
-
displayName: '
|
|
43
|
-
name: '
|
|
44
|
-
type: '
|
|
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',
|
|
39
|
+
displayName: 'Consumer Secret',
|
|
40
|
+
name: 'consumerSecret',
|
|
41
|
+
type: 'string',
|
|
42
|
+
typeOptions: { password: true },
|
|
63
43
|
default: '',
|
|
44
|
+
required: true,
|
|
45
|
+
description: 'Consumer Secret fornecido pela Tray no painel de apps',
|
|
64
46
|
},
|
|
65
47
|
{
|
|
66
|
-
displayName: '
|
|
67
|
-
name: '
|
|
68
|
-
type: '
|
|
48
|
+
displayName: 'Authorization Code',
|
|
49
|
+
name: 'authCode',
|
|
50
|
+
type: 'string',
|
|
51
|
+
typeOptions: { password: true },
|
|
69
52
|
default: '',
|
|
53
|
+
required: true,
|
|
54
|
+
placeholder: 'a9777f8cdfe4cf41b0a72c295adef1f9c43a093052c9e6dd98d1e629a09f13f3',
|
|
55
|
+
description: 'Código de autorização único da loja (fornecido pela Tray)',
|
|
70
56
|
},
|
|
57
|
+
// Campos ocultos — cache interno de token
|
|
58
|
+
{ displayName: 'Access Token', name: 'accessToken', type: 'hidden', default: '' },
|
|
59
|
+
{ displayName: 'Refresh Token', name: 'refreshToken', type: 'hidden', default: '' },
|
|
60
|
+
{ displayName: 'Token Expiration',name: 'tokenExpiration',type: 'hidden', default: '' },
|
|
61
|
+
{ displayName: 'API Host', name: 'apiHost', type: 'hidden', default: '' },
|
|
71
62
|
];
|
|
72
|
-
// O segredo para o n8n injetar o token em todas as chamadas
|
|
73
63
|
this.authenticate = {
|
|
74
64
|
type: 'generic',
|
|
75
65
|
properties: {
|
|
76
|
-
qs: {
|
|
77
|
-
access_token: '={{$credentials.accessToken}}',
|
|
78
|
-
},
|
|
66
|
+
qs: { access_token: '={{$credentials.accessToken}}' },
|
|
79
67
|
},
|
|
80
68
|
};
|
|
81
69
|
this.test = {
|
|
82
70
|
request: {
|
|
83
|
-
|
|
84
|
-
baseURL: '={{$credentials.apiAddress}}',
|
|
71
|
+
baseURL: '={{`https://${$credentials.storeId}.commercesuite.com.br/web_api`}}',
|
|
85
72
|
url: '/auth',
|
|
86
73
|
method: 'POST',
|
|
87
74
|
body: {
|
|
88
|
-
consumer_key:
|
|
89
|
-
consumer_secret:
|
|
90
|
-
code:
|
|
75
|
+
consumer_key: '={{$credentials.consumerKey}}',
|
|
76
|
+
consumer_secret: '={{$credentials.consumerSecret}}',
|
|
77
|
+
code: '={{$credentials.authCode}}',
|
|
91
78
|
},
|
|
92
79
|
},
|
|
93
80
|
};
|
|
94
81
|
}
|
|
82
|
+
|
|
83
|
+
_buildBaseUrl(credentials) {
|
|
84
|
+
const storeId = (credentials.storeId || '').toString().trim();
|
|
85
|
+
if (!storeId) throw new Error('[Atendix] Store ID não configurado nas credenciais');
|
|
86
|
+
// v1.3.0 FIX: URL correta — api.tray.com.br não existe
|
|
87
|
+
return `https://${storeId}.commercesuite.com.br/web_api`;
|
|
88
|
+
}
|
|
89
|
+
|
|
95
90
|
async preAuthentication(credentials) {
|
|
96
91
|
const now = new Date();
|
|
97
92
|
const tokenExpiration = credentials.tokenExpiration
|
|
98
93
|
? new Date(credentials.tokenExpiration)
|
|
99
94
|
: null;
|
|
95
|
+
|
|
100
96
|
if (credentials.accessToken && tokenExpiration && tokenExpiration > now) {
|
|
101
|
-
return credentials;
|
|
97
|
+
return credentials; // Token válido em cache
|
|
102
98
|
}
|
|
103
99
|
if (credentials.refreshToken && tokenExpiration && tokenExpiration <= now) {
|
|
104
100
|
return await this._refreshTokenFlow(credentials);
|
|
105
101
|
}
|
|
106
102
|
return await this._authenticateFlow(credentials);
|
|
107
103
|
}
|
|
108
|
-
|
|
109
|
-
* Renomeado para evitar conflito com a propriedade 'authenticate' da interface
|
|
110
|
-
*/
|
|
104
|
+
|
|
111
105
|
async _authenticateFlow(credentials) {
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
});
|
|
129
|
-
const data = (await response.json());
|
|
130
|
-
if (!response.ok) {
|
|
131
|
-
console.error(' Erro retornado pela Tray:', data);
|
|
132
|
-
throw new Error(data.message || 'Erro desconhecido na Tray');
|
|
133
|
-
}
|
|
134
|
-
console.log(' Autenticação com a Tray teve SUCESSO!');
|
|
135
|
-
return {
|
|
136
|
-
...credentials,
|
|
137
|
-
accessToken: data.access_token,
|
|
138
|
-
refreshToken: data.refresh_token,
|
|
139
|
-
apiHost: data.api_host, // Geralmente vem algo como "https://1225878.commercesuite.com.br"
|
|
140
|
-
storeId: data.store_id,
|
|
141
|
-
tokenExpiration: data.date_expiration_access_token,
|
|
142
|
-
};
|
|
143
|
-
}
|
|
144
|
-
catch (error) {
|
|
145
|
-
console.error(' Erro Crítico no Flow de Autenticação:', error.message);
|
|
146
|
-
throw new Error(`Falha ao autenticar na Tray: ${error.message}`);
|
|
106
|
+
const baseUrl = this._buildBaseUrl(credentials);
|
|
107
|
+
console.log(`[Atendix v1.3.0] Autenticando em: ${baseUrl}/auth`);
|
|
108
|
+
|
|
109
|
+
const response = await fetch(`${baseUrl}/auth`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: { 'Content-Type': 'application/json' },
|
|
112
|
+
body: JSON.stringify({
|
|
113
|
+
consumer_key: credentials.consumerKey,
|
|
114
|
+
consumer_secret: credentials.consumerSecret,
|
|
115
|
+
code: credentials.authCode,
|
|
116
|
+
}),
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const data = await response.json();
|
|
120
|
+
if (!response.ok) {
|
|
121
|
+
console.error('[Atendix] Erro na autenticação Tray:', data);
|
|
122
|
+
throw new Error(data.message || `Erro HTTP ${response.status} na autenticação Tray`);
|
|
147
123
|
}
|
|
124
|
+
|
|
125
|
+
console.log('[Atendix v1.3.0] Autenticação OK ✓');
|
|
126
|
+
return {
|
|
127
|
+
...credentials,
|
|
128
|
+
accessToken: data.access_token,
|
|
129
|
+
refreshToken: data.refresh_token,
|
|
130
|
+
apiHost: data.api_host,
|
|
131
|
+
tokenExpiration: data.date_expiration_access_token,
|
|
132
|
+
};
|
|
148
133
|
}
|
|
134
|
+
|
|
149
135
|
async _refreshTokenFlow(credentials) {
|
|
150
|
-
const
|
|
151
|
-
const refreshToken = credentials.refreshToken;
|
|
136
|
+
const baseUrl = this._buildBaseUrl(credentials);
|
|
152
137
|
try {
|
|
153
|
-
const response = await fetch(`${
|
|
154
|
-
const data =
|
|
155
|
-
if (!response.ok)
|
|
156
|
-
return await this._authenticateFlow(credentials);
|
|
157
|
-
}
|
|
138
|
+
const response = await fetch(`${baseUrl}/auth?refresh_token=${credentials.refreshToken}`);
|
|
139
|
+
const data = await response.json();
|
|
140
|
+
if (!response.ok) return await this._authenticateFlow(credentials);
|
|
158
141
|
return {
|
|
159
142
|
...credentials,
|
|
160
|
-
accessToken:
|
|
161
|
-
refreshToken:
|
|
143
|
+
accessToken: data.access_token,
|
|
144
|
+
refreshToken: data.refresh_token,
|
|
162
145
|
tokenExpiration: data.date_expiration_access_token,
|
|
163
146
|
};
|
|
164
|
-
}
|
|
165
|
-
catch (error) {
|
|
147
|
+
} catch {
|
|
166
148
|
return await this._authenticateFlow(credentials);
|
|
167
149
|
}
|
|
168
150
|
}
|
|
@@ -1,335 +1,379 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
3
|
+
* n8n-nodes-atendix — Node Atendix Tray Commerce
|
|
4
|
+
* v1.3.0 — Correções críticas de homologação:
|
|
5
|
+
* - URL: {store_id}.commercesuite.com.br/web_api
|
|
6
|
+
* - Endpoints NF: GET /orders/:id/invoices (não /invoices?order_id=)
|
|
7
|
+
* - Recursos: Pedido, Nota Fiscal, Cliente, Rastreamento, Produto
|
|
7
8
|
*/
|
|
8
9
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
10
|
exports.Atendix = void 0;
|
|
10
11
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
12
|
+
|
|
11
13
|
const wait = (ms) => new Promise(res => setTimeout(res, ms));
|
|
14
|
+
|
|
12
15
|
class Atendix {
|
|
13
16
|
constructor() {
|
|
14
17
|
this.description = {
|
|
15
18
|
displayName: 'Atendix - Tray API',
|
|
16
19
|
name: 'atendix',
|
|
17
|
-
icon: {
|
|
18
|
-
light: 'file:tray.svg',
|
|
19
|
-
dark: 'file:tray.svg',
|
|
20
|
-
},
|
|
20
|
+
icon: { light: 'file:tray.svg', dark: 'file:tray.svg' },
|
|
21
21
|
group: ['transform'],
|
|
22
22
|
version: 1,
|
|
23
23
|
subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
|
|
24
|
-
description: 'Integração com a
|
|
25
|
-
defaults: {
|
|
26
|
-
name: 'Atendix - Tray API',
|
|
27
|
-
},
|
|
24
|
+
description: 'Integração nativa com a Tray Commerce — v1.3.0',
|
|
25
|
+
defaults: { name: 'Atendix - Tray API' },
|
|
28
26
|
inputs: ['main'],
|
|
29
27
|
outputs: ['main'],
|
|
30
|
-
credentials: [
|
|
31
|
-
{
|
|
32
|
-
name: 'trayApiAuto',
|
|
33
|
-
required: true,
|
|
34
|
-
displayName: 'Atendix - Tray API',
|
|
35
|
-
},
|
|
36
|
-
],
|
|
28
|
+
credentials: [{ name: 'trayApiAuto', required: true, displayName: 'Atendix - Tray API' }],
|
|
37
29
|
properties: [
|
|
38
|
-
//
|
|
30
|
+
// ===== RESOURCE =====
|
|
39
31
|
{
|
|
40
|
-
displayName: 'Resource',
|
|
41
|
-
name: 'resource',
|
|
42
|
-
type: 'options',
|
|
32
|
+
displayName: 'Resource', name: 'resource', type: 'options',
|
|
43
33
|
noDataExpression: true,
|
|
44
34
|
options: [
|
|
45
|
-
{ name: 'Pedido',
|
|
46
|
-
{ name: '
|
|
47
|
-
{ name: '
|
|
35
|
+
{ name: 'Pedido', value: 'order' },
|
|
36
|
+
{ name: 'Nota Fiscal', value: 'invoice' },
|
|
37
|
+
{ name: 'Cliente', value: 'customer' },
|
|
38
|
+
{ name: 'Rastreamento', value: 'tracking' },
|
|
39
|
+
{ name: 'Produto', value: 'product' },
|
|
48
40
|
],
|
|
49
41
|
default: 'order',
|
|
50
42
|
},
|
|
51
|
-
|
|
43
|
+
|
|
44
|
+
// ===== PEDIDO — OPERATIONS =====
|
|
52
45
|
{
|
|
53
|
-
displayName: 'Operation',
|
|
54
|
-
name: 'operation',
|
|
55
|
-
type: 'options',
|
|
46
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
56
47
|
noDataExpression: true,
|
|
57
48
|
displayOptions: { show: { resource: ['order'] } },
|
|
58
49
|
options: [
|
|
59
|
-
{
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
-
},
|
|
50
|
+
{ name: 'Buscar Pedido', value: 'get', action: 'Buscar pedido por ID' },
|
|
51
|
+
{ name: 'Dados Completos', value: 'getComplete', action: 'Buscar dados completos' },
|
|
52
|
+
{ name: 'Listar Pedidos', value: 'list', action: 'Listar pedidos com filtros' },
|
|
53
|
+
{ name: 'Atualizar Status', value: 'updateStatus', action: 'Atualizar status do pedido' },
|
|
71
54
|
],
|
|
72
55
|
default: 'get',
|
|
73
56
|
},
|
|
74
57
|
{
|
|
75
|
-
displayName: 'ID do Pedido',
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
required: true,
|
|
79
|
-
displayOptions: { show: { resource: ['order'], operation: ['get'] } },
|
|
80
|
-
default: '',
|
|
58
|
+
displayName: 'ID do Pedido', name: 'orderId', type: 'string', required: true,
|
|
59
|
+
displayOptions: { show: { resource: ['order'], operation: ['get', 'getComplete', 'updateStatus'] } },
|
|
60
|
+
default: '', placeholder: '9847',
|
|
81
61
|
},
|
|
82
62
|
{
|
|
83
|
-
displayName: '
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
63
|
+
displayName: 'Novo Status', name: 'orderStatus', type: 'options',
|
|
64
|
+
displayOptions: { show: { resource: ['order'], operation: ['updateStatus'] } },
|
|
65
|
+
options: [
|
|
66
|
+
{ name: 'Aprovado', value: 'approved' },
|
|
67
|
+
{ name: 'Em Preparação', value: 'preparing' },
|
|
68
|
+
{ name: 'Enviado', value: 'shipped' },
|
|
69
|
+
{ name: 'Entregue', value: 'delivered' },
|
|
70
|
+
{ name: 'Cancelado', value: 'cancelled' },
|
|
71
|
+
],
|
|
72
|
+
default: 'approved',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
76
|
+
placeholder: 'Adicionar Filtro', default: {},
|
|
88
77
|
displayOptions: { show: { resource: ['order'], operation: ['list'] } },
|
|
89
78
|
options: [
|
|
90
79
|
{
|
|
91
|
-
displayName: 'Status',
|
|
92
|
-
name: 'status',
|
|
93
|
-
type: 'options',
|
|
80
|
+
displayName: 'Status', name: 'status', type: 'options', default: '',
|
|
94
81
|
options: [
|
|
95
|
-
{ name: 'Pendente',
|
|
96
|
-
{ name: 'Aprovado',
|
|
97
|
-
{ name: 'Enviado',
|
|
98
|
-
{ name: 'Entregue',
|
|
82
|
+
{ name: 'Pendente', value: 'pending' },
|
|
83
|
+
{ name: 'Aprovado', value: 'approved' },
|
|
84
|
+
{ name: 'Enviado', value: 'shipped' },
|
|
85
|
+
{ name: 'Entregue', value: 'delivered' },
|
|
99
86
|
{ name: 'Cancelado', value: 'cancelled' },
|
|
100
87
|
],
|
|
101
|
-
default: '',
|
|
102
88
|
},
|
|
103
89
|
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
90
|
+
{ displayName: 'Página', name: 'page', type: 'number', default: 1 },
|
|
91
|
+
{ displayName: 'Data Início (YYYY-MM-DD)', name: 'dateFrom', type: 'string', default: '' },
|
|
92
|
+
{ displayName: 'Data Fim (YYYY-MM-DD)', name: 'dateTo', type: 'string', default: '' },
|
|
104
93
|
],
|
|
105
94
|
},
|
|
106
|
-
|
|
95
|
+
|
|
96
|
+
// ===== NOTA FISCAL — OPERATIONS =====
|
|
97
|
+
// v1.3.0 FIX: endpoints corretos são /orders/:id/invoices
|
|
98
|
+
{
|
|
99
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
100
|
+
noDataExpression: true,
|
|
101
|
+
displayOptions: { show: { resource: ['invoice'] } },
|
|
102
|
+
options: [
|
|
103
|
+
{ name: 'Listar NFs do Pedido', value: 'list', action: 'GET /orders/:id/invoices' },
|
|
104
|
+
{ name: 'Buscar NF por ID', value: 'get', action: 'GET /orders/:id/invoices/:inv_id' },
|
|
105
|
+
{ name: 'Cadastrar NF', value: 'create', action: 'POST /invoices' },
|
|
106
|
+
],
|
|
107
|
+
default: 'list',
|
|
108
|
+
},
|
|
109
|
+
{
|
|
110
|
+
displayName: 'ID do Pedido', name: 'orderId', type: 'string', required: true,
|
|
111
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['list', 'get'] } },
|
|
112
|
+
default: '', placeholder: '9847',
|
|
113
|
+
description: 'ID do pedido Tray para buscar as NFs',
|
|
114
|
+
},
|
|
107
115
|
{
|
|
108
|
-
displayName: '
|
|
109
|
-
|
|
110
|
-
|
|
116
|
+
displayName: 'ID da Nota Fiscal', name: 'invoiceId', type: 'string', required: true,
|
|
117
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['get'] } },
|
|
118
|
+
default: '', placeholder: '1347',
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
displayName: 'Dados da NF (JSON)', name: 'invoiceData', type: 'json',
|
|
122
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['create'] } },
|
|
123
|
+
default: '{\n "order_id": "",\n "issue_date": "",\n "number": "",\n "serie": "",\n "value": "",\n "xml": "",\n "key": "",\n "link": ""\n}',
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
// ===== CLIENTES — OPERATIONS =====
|
|
127
|
+
{
|
|
128
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
111
129
|
noDataExpression: true,
|
|
112
130
|
displayOptions: { show: { resource: ['customer'] } },
|
|
113
131
|
options: [
|
|
114
|
-
{ name: 'Buscar
|
|
115
|
-
{ name: '
|
|
132
|
+
{ name: 'Buscar por ID', value: 'get', action: 'Buscar cliente por ID' },
|
|
133
|
+
{ name: 'Listar Clientes', value: 'list', action: 'Listar clientes paginado' },
|
|
134
|
+
{ name: 'Buscar por Tel/E-mail', value: 'search', action: 'Buscar por telefone ou email' },
|
|
116
135
|
],
|
|
117
136
|
default: 'get',
|
|
118
137
|
},
|
|
119
138
|
{
|
|
120
|
-
displayName: '
|
|
121
|
-
name: 'searchType',
|
|
122
|
-
type: 'options',
|
|
139
|
+
displayName: 'ID do Cliente', name: 'customerId', type: 'string', required: true,
|
|
123
140
|
displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
|
|
141
|
+
default: '',
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
displayName: 'Buscar Por', name: 'searchType', type: 'options',
|
|
145
|
+
displayOptions: { show: { resource: ['customer'], operation: ['search'] } },
|
|
124
146
|
options: [
|
|
125
|
-
{ name: 'E-mail',
|
|
126
|
-
{ name: '
|
|
147
|
+
{ name: 'E-mail', value: 'email' },
|
|
148
|
+
{ name: 'Telefone', value: 'phone' },
|
|
149
|
+
{ name: 'CPF/CNPJ', value: 'cpf_cnpj'},
|
|
127
150
|
],
|
|
128
151
|
default: 'email',
|
|
129
152
|
},
|
|
130
153
|
{
|
|
131
|
-
displayName: 'Valor',
|
|
132
|
-
|
|
133
|
-
type: 'string',
|
|
134
|
-
displayOptions: { show: { resource: ['customer'], operation: ['get'] } },
|
|
154
|
+
displayName: 'Valor de Busca', name: 'searchValue', type: 'string',
|
|
155
|
+
displayOptions: { show: { resource: ['customer'], operation: ['search'] } },
|
|
135
156
|
default: '',
|
|
136
157
|
},
|
|
137
|
-
// ==================== PRODUTOS - OPERATIONS ====================
|
|
138
158
|
{
|
|
139
|
-
displayName: '
|
|
140
|
-
|
|
141
|
-
|
|
159
|
+
displayName: 'Filtros', name: 'filters', type: 'collection',
|
|
160
|
+
placeholder: 'Adicionar Filtro', default: {},
|
|
161
|
+
displayOptions: { show: { resource: ['customer'], operation: ['list'] } },
|
|
162
|
+
options: [
|
|
163
|
+
{ displayName: 'Limite', name: 'limit', type: 'number', default: 50 },
|
|
164
|
+
{ displayName: 'Página', name: 'page', type: 'number', default: 1 },
|
|
165
|
+
],
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
// ===== RASTREAMENTO — OPERATIONS =====
|
|
169
|
+
{
|
|
170
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
171
|
+
noDataExpression: true,
|
|
172
|
+
displayOptions: { show: { resource: ['tracking'] } },
|
|
173
|
+
options: [
|
|
174
|
+
{ name: 'Código de Rastreio', value: 'getTracking', action: 'Obter código de rastreio do pedido' },
|
|
175
|
+
{ name: 'Formas de Envio', value: 'getShipping', action: 'Listar formas de envio disponíveis' },
|
|
176
|
+
],
|
|
177
|
+
default: 'getTracking',
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
displayName: 'ID do Pedido', name: 'orderId', type: 'string', required: true,
|
|
181
|
+
displayOptions: { show: { resource: ['tracking'], operation: ['getTracking'] } },
|
|
182
|
+
default: '', placeholder: '9847',
|
|
183
|
+
},
|
|
184
|
+
|
|
185
|
+
// ===== PRODUTO — OPERATIONS =====
|
|
186
|
+
{
|
|
187
|
+
displayName: 'Operation', name: 'operation', type: 'options',
|
|
142
188
|
noDataExpression: true,
|
|
143
189
|
displayOptions: { show: { resource: ['product'] } },
|
|
144
190
|
options: [
|
|
145
|
-
{ name: 'Buscar Produto',
|
|
146
|
-
{ name: 'Atualizar Estoque', value: 'updateStock', action: 'Atualizar estoque
|
|
191
|
+
{ name: 'Buscar Produto', value: 'get', action: 'Buscar produto por ID ou SKU' },
|
|
192
|
+
{ name: 'Atualizar Estoque', value: 'updateStock', action: 'Atualizar estoque' },
|
|
147
193
|
],
|
|
148
194
|
default: 'get',
|
|
149
195
|
},
|
|
150
196
|
{
|
|
151
|
-
displayName: 'Buscar Por',
|
|
152
|
-
name: 'searchType',
|
|
153
|
-
type: 'options',
|
|
197
|
+
displayName: 'Buscar Por', name: 'searchType', type: 'options',
|
|
154
198
|
displayOptions: { show: { resource: ['product'], operation: ['get'] } },
|
|
155
199
|
options: [
|
|
156
|
-
{ name: 'ID do Produto', value: 'id'
|
|
157
|
-
{ name: 'SKU',
|
|
200
|
+
{ name: 'ID do Produto', value: 'id' },
|
|
201
|
+
{ name: 'SKU', value: 'sku' },
|
|
158
202
|
],
|
|
159
203
|
default: 'id',
|
|
160
204
|
},
|
|
161
205
|
{
|
|
162
|
-
displayName: 'Valor',
|
|
163
|
-
name: 'searchValue',
|
|
164
|
-
type: 'string',
|
|
206
|
+
displayName: 'Valor', name: 'searchValue', type: 'string',
|
|
165
207
|
displayOptions: { show: { resource: ['product'], operation: ['get'] } },
|
|
166
208
|
default: '',
|
|
167
209
|
},
|
|
168
210
|
],
|
|
169
211
|
};
|
|
170
212
|
}
|
|
213
|
+
|
|
171
214
|
async execute() {
|
|
172
|
-
|
|
173
|
-
const items = this.getInputData();
|
|
215
|
+
const items = this.getInputData();
|
|
174
216
|
const returnData = [];
|
|
175
|
-
const resource
|
|
217
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
176
218
|
const operation = this.getNodeParameter('operation', 0);
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
console.error('❌ ATENDIX_AUTH_TOKEN não configurado nas variáveis de ambiente');
|
|
180
|
-
throw new Error('Configuração de validação Atendix incompleta. Entre em contato com o suporte.');
|
|
181
|
-
}
|
|
182
|
-
// 1. Get Credentials
|
|
219
|
+
|
|
220
|
+
// Credenciais (n8n chama preAuthentication automaticamente)
|
|
183
221
|
const credentials = await this.getCredentials('trayApiAuto');
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
try {
|
|
192
|
-
licenseCheck = await this.helpers.httpRequest({
|
|
193
|
-
method: 'POST',
|
|
194
|
-
url: 'https://n8n.mariapinho.com.br/webhook/valida-atendix',
|
|
195
|
-
headers: {
|
|
196
|
-
'Content-Type': 'application/json',
|
|
197
|
-
'Authorization': atendixAuthToken,
|
|
198
|
-
},
|
|
199
|
-
body: { url: storeBaseUrl },
|
|
200
|
-
json: true,
|
|
201
|
-
});
|
|
202
|
-
console.error('LICENSE CHECK RESPONSE: ' + licenseCheck.autorizado);
|
|
203
|
-
if (!licenseCheck || licenseCheck.autorizado !== true) {
|
|
204
|
-
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 ou não pode ser validada. Para liberar seu acesso e automatizar sua operação, acesse https://atendix.co e assine um plano agora mesmo!`);
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
catch (error) {
|
|
209
|
-
console.error('ATENDIX_AUTH_TOKEN:', atendixAuthToken);
|
|
210
|
-
console.error('STORE BASE URL:', storeBaseUrl);
|
|
211
|
-
console.error('LICENSECHECK:', licenseCheck);
|
|
212
|
-
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
|
-
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
216
|
-
throw error;
|
|
217
|
-
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
|
-
}
|
|
219
|
-
// 2. TOKEN MANAGER (Fallback)
|
|
220
|
-
let accessToken = credentials.accessToken;
|
|
221
|
-
if (!accessToken) {
|
|
222
|
-
try {
|
|
223
|
-
const authResponse = await this.helpers.httpRequest({
|
|
224
|
-
method: 'POST',
|
|
225
|
-
url: `${baseUrl}/auth`,
|
|
226
|
-
body: {
|
|
227
|
-
consumer_key: process.env.TRAY_CONSUMER_KEY,
|
|
228
|
-
consumer_secret: process.env.TRAY_CONSUMER_SECRET,
|
|
229
|
-
code: credentials.authCode,
|
|
230
|
-
},
|
|
231
|
-
json: true,
|
|
232
|
-
});
|
|
233
|
-
accessToken = authResponse.access_token;
|
|
234
|
-
}
|
|
235
|
-
catch (authError) {
|
|
236
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Falha na Autenticação Tray: ${authError.message}`);
|
|
237
|
-
}
|
|
222
|
+
const storeId = (credentials.storeId || '').toString().trim();
|
|
223
|
+
|
|
224
|
+
if (!storeId) {
|
|
225
|
+
throw new n8n_workflow_1.NodeOperationError(
|
|
226
|
+
this.getNode(),
|
|
227
|
+
'Store ID não configurado nas credenciais Atendix. Preencha o campo Store ID.',
|
|
228
|
+
);
|
|
238
229
|
}
|
|
230
|
+
|
|
231
|
+
// v1.3.0: URL derivada do store_id — nunca mais hardcoded
|
|
232
|
+
const baseUrl = `https://${storeId}.commercesuite.com.br/web_api`;
|
|
233
|
+
const accessToken = credentials.accessToken;
|
|
234
|
+
|
|
239
235
|
for (let i = 0; i < items.length; i++) {
|
|
240
236
|
try {
|
|
237
|
+
if (i > 0) await wait(500); // Rate-limit seguro: ~120 req/min (limite Tray: 180)
|
|
238
|
+
|
|
241
239
|
let responseData = {};
|
|
242
240
|
const qs = { access_token: accessToken };
|
|
243
|
-
|
|
241
|
+
|
|
242
|
+
// ===== PEDIDOS =====
|
|
244
243
|
if (resource === 'order') {
|
|
245
244
|
if (operation === 'get') {
|
|
246
245
|
const orderId = this.getNodeParameter('orderId', i);
|
|
247
|
-
if (i > 0) {
|
|
248
|
-
// Dá uma respirada de 500ms entre cada item da lista
|
|
249
|
-
// 500ms = 2 requisições por segundo = 120 por minuto (Seguro dentro dos 180)
|
|
250
|
-
await wait(500);
|
|
251
|
-
}
|
|
252
246
|
responseData = await this.helpers.httpRequest({
|
|
253
|
-
method: 'GET',
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
247
|
+
method: 'GET', url: `${baseUrl}/orders/${orderId}`, qs, json: true,
|
|
248
|
+
});
|
|
249
|
+
} else if (operation === 'getComplete') {
|
|
250
|
+
const orderId = this.getNodeParameter('orderId', i);
|
|
251
|
+
responseData = await this.helpers.httpRequest({
|
|
252
|
+
method: 'GET', url: `${baseUrl}/orders/${orderId}/complete`, qs, json: true,
|
|
253
|
+
});
|
|
254
|
+
} else if (operation === 'list') {
|
|
255
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
256
|
+
if (filters.status) qs.status = filters.status;
|
|
257
|
+
if (filters.limit) qs.limit = filters.limit;
|
|
258
|
+
if (filters.page) qs.page = filters.page;
|
|
259
|
+
if (filters.dateFrom) qs.date_from = filters.dateFrom;
|
|
260
|
+
if (filters.dateTo) qs.date_to = filters.dateTo;
|
|
261
|
+
responseData = await this.helpers.httpRequest({
|
|
262
|
+
method: 'GET', url: `${baseUrl}/orders`, qs, json: true,
|
|
263
|
+
});
|
|
264
|
+
} else if (operation === 'updateStatus') {
|
|
265
|
+
const orderId = this.getNodeParameter('orderId', i);
|
|
266
|
+
const orderStatus = this.getNodeParameter('orderStatus', i);
|
|
267
|
+
responseData = await this.helpers.httpRequest({
|
|
268
|
+
method: 'PUT', url: `${baseUrl}/orders/${orderId}`,
|
|
269
|
+
qs, body: { status: orderStatus }, json: true,
|
|
257
270
|
});
|
|
258
271
|
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ===== NOTA FISCAL =====
|
|
275
|
+
// v1.3.0 FIX CRÍTICO: endpoints corretos são /orders/:id/invoices
|
|
276
|
+
else if (resource === 'invoice') {
|
|
259
277
|
if (operation === 'list') {
|
|
260
|
-
const
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
}
|
|
278
|
+
const orderId = this.getNodeParameter('orderId', i);
|
|
279
|
+
// ✅ CORRETO: GET /orders/:order_id/invoices
|
|
280
|
+
responseData = await this.helpers.httpRequest({
|
|
281
|
+
method: 'GET', url: `${baseUrl}/orders/${orderId}/invoices`, qs, json: true,
|
|
282
|
+
});
|
|
283
|
+
} else if (operation === 'get') {
|
|
284
|
+
const orderId = this.getNodeParameter('orderId', i);
|
|
285
|
+
const invoiceId = this.getNodeParameter('invoiceId', i);
|
|
286
|
+
// ✅ CORRETO: GET /orders/:order_id/invoices/:invoice_id
|
|
270
287
|
responseData = await this.helpers.httpRequest({
|
|
271
|
-
method: 'GET',
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
288
|
+
method: 'GET', url: `${baseUrl}/orders/${orderId}/invoices/${invoiceId}`, qs, json: true,
|
|
289
|
+
});
|
|
290
|
+
} else if (operation === 'create') {
|
|
291
|
+
const invoiceData = JSON.parse(this.getNodeParameter('invoiceData', i));
|
|
292
|
+
responseData = await this.helpers.httpRequest({
|
|
293
|
+
method: 'POST', url: `${baseUrl}/invoices`, qs, body: invoiceData, json: true,
|
|
275
294
|
});
|
|
276
295
|
}
|
|
277
296
|
}
|
|
278
|
-
|
|
279
|
-
|
|
297
|
+
|
|
298
|
+
// ===== CLIENTES =====
|
|
299
|
+
else if (resource === 'customer') {
|
|
280
300
|
if (operation === 'get') {
|
|
281
|
-
const
|
|
301
|
+
const customerId = this.getNodeParameter('customerId', i);
|
|
302
|
+
responseData = await this.helpers.httpRequest({
|
|
303
|
+
method: 'GET', url: `${baseUrl}/customers/${customerId}`, qs, json: true,
|
|
304
|
+
});
|
|
305
|
+
} else if (operation === 'list') {
|
|
306
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
307
|
+
if (filters.limit) qs.limit = filters.limit;
|
|
308
|
+
if (filters.page) qs.page = filters.page;
|
|
309
|
+
responseData = await this.helpers.httpRequest({
|
|
310
|
+
method: 'GET', url: `${baseUrl}/customers`, qs, json: true,
|
|
311
|
+
});
|
|
312
|
+
} else if (operation === 'search') {
|
|
313
|
+
const searchType = this.getNodeParameter('searchType', i);
|
|
282
314
|
const searchValue = this.getNodeParameter('searchValue', i);
|
|
283
|
-
|
|
284
|
-
qs.email = searchValue;
|
|
285
|
-
else
|
|
286
|
-
qs.cpf_cnpj = searchValue;
|
|
315
|
+
qs[searchType] = searchValue;
|
|
287
316
|
responseData = await this.helpers.httpRequest({
|
|
288
|
-
method: 'GET',
|
|
289
|
-
url: `${baseUrl}/customers`,
|
|
290
|
-
qs,
|
|
291
|
-
json: true,
|
|
317
|
+
method: 'GET', url: `${baseUrl}/customers`, qs, json: true,
|
|
292
318
|
});
|
|
293
319
|
}
|
|
294
|
-
// Nota da Gerente: Se for adicionar UPSERT aqui, o código deve ser incluído abaixo
|
|
295
320
|
}
|
|
296
|
-
|
|
297
|
-
|
|
321
|
+
|
|
322
|
+
// ===== RASTREAMENTO =====
|
|
323
|
+
else if (resource === 'tracking') {
|
|
324
|
+
if (operation === 'getTracking') {
|
|
325
|
+
const orderId = this.getNodeParameter('orderId', i);
|
|
326
|
+
const orderRaw = await this.helpers.httpRequest({
|
|
327
|
+
method: 'GET', url: `${baseUrl}/orders/${orderId}`, qs, json: true,
|
|
328
|
+
});
|
|
329
|
+
const order = (orderRaw.Order || orderRaw);
|
|
330
|
+
responseData = {
|
|
331
|
+
order_id: orderId,
|
|
332
|
+
tracking_code: order.shipping_tracking_code || null,
|
|
333
|
+
shipping_carrier: order.shipping_carrier || null,
|
|
334
|
+
shipping_method: order.shipping_method_name || null,
|
|
335
|
+
};
|
|
336
|
+
} else if (operation === 'getShipping') {
|
|
337
|
+
responseData = await this.helpers.httpRequest({
|
|
338
|
+
method: 'GET', url: `${baseUrl}/shipping`, qs, json: true,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// ===== PRODUTO =====
|
|
344
|
+
else if (resource === 'product') {
|
|
298
345
|
if (operation === 'get') {
|
|
299
|
-
const searchType
|
|
346
|
+
const searchType = this.getNodeParameter('searchType', i);
|
|
300
347
|
const searchValue = this.getNodeParameter('searchValue', i);
|
|
301
348
|
if (searchType === 'id') {
|
|
302
349
|
responseData = await this.helpers.httpRequest({
|
|
303
|
-
method: 'GET',
|
|
304
|
-
url: `${baseUrl}/products/${searchValue}`,
|
|
305
|
-
qs,
|
|
306
|
-
json: true,
|
|
350
|
+
method: 'GET', url: `${baseUrl}/products/${searchValue}`, qs, json: true,
|
|
307
351
|
});
|
|
308
|
-
}
|
|
309
|
-
else {
|
|
352
|
+
} else {
|
|
310
353
|
qs.sku = searchValue;
|
|
311
354
|
responseData = await this.helpers.httpRequest({
|
|
312
|
-
method: 'GET',
|
|
313
|
-
url: `${baseUrl}/products`,
|
|
314
|
-
qs,
|
|
315
|
-
json: true,
|
|
355
|
+
method: 'GET', url: `${baseUrl}/products`, qs, json: true,
|
|
316
356
|
});
|
|
317
357
|
}
|
|
318
358
|
}
|
|
319
|
-
// Nota da Gerente: Se for adicionar UPDATE_STOCK aqui, o código deve ser incluído abaixo
|
|
320
359
|
}
|
|
321
|
-
|
|
360
|
+
|
|
361
|
+
const executionData = this.helpers.constructExecutionMetaData(
|
|
362
|
+
this.helpers.returnJsonArray(responseData),
|
|
363
|
+
{ itemData: { item: i } },
|
|
364
|
+
);
|
|
322
365
|
returnData.push(...executionData);
|
|
323
|
-
|
|
324
|
-
catch (error) {
|
|
325
|
-
const
|
|
366
|
+
|
|
367
|
+
} catch (error) {
|
|
368
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
326
369
|
if (this.continueOnFail()) {
|
|
327
|
-
returnData.push({ json: { error:
|
|
370
|
+
returnData.push({ json: { error: msg, resource, operation }, pairedItem: { item: i } });
|
|
328
371
|
continue;
|
|
329
372
|
}
|
|
330
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(),
|
|
373
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), msg, { itemIndex: i });
|
|
331
374
|
}
|
|
332
375
|
}
|
|
376
|
+
|
|
333
377
|
return [returnData];
|
|
334
378
|
}
|
|
335
379
|
}
|
package/index.js
CHANGED
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
var Atendix_node_1 = require("./dist/nodes/Atendix/Atendix.node");
|
|
4
|
+
Object.defineProperty(exports, "Atendix", { enumerable: true, get: function () { return Atendix_node_1.Atendix; } });
|
|
5
|
+
var TrayApiAuto_credentials_1 = require("./dist/credentials/TrayApiAuto.credentials");
|
|
6
|
+
Object.defineProperty(exports, "TrayApiAuto", { enumerable: true, get: function () { return TrayApiAuto_credentials_1.TrayApiAuto; } });
|
package/package.json
CHANGED
|
@@ -1,37 +1,33 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "n8n-nodes-atendix",
|
|
3
|
-
"version": "1.0
|
|
4
|
-
"description": "Conector Atendix para integração com Tray Commerce",
|
|
5
|
-
"keywords": ["n8n-community-node-package"],
|
|
6
|
-
"license": "MIT",
|
|
7
|
-
"homepage": "https://atendix.co",
|
|
8
|
-
"author": {
|
|
9
|
-
"name": "Atendix",
|
|
10
|
-
"email": "contato@atendix.co"
|
|
11
|
-
},
|
|
12
|
-
"main": "index.js",
|
|
13
|
-
"scripts": {
|
|
14
|
-
"build": "
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
],
|
|
21
|
-
"n8n": {
|
|
22
|
-
"n8nNodesApiVersion": 1,
|
|
23
|
-
"credentials": [
|
|
24
|
-
"dist/credentials/TrayApiAuto.credentials.js"
|
|
25
|
-
],
|
|
26
|
-
"nodes": [
|
|
27
|
-
"dist/nodes/Atendix/Atendix.node.js"
|
|
28
|
-
]
|
|
29
|
-
},
|
|
30
|
-
"dependencies": {
|
|
31
|
-
"n8n-workflow": "^1.0.0"
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
"@types/node": "^18.0.0",
|
|
35
|
-
"typescript": "^5.0.0"
|
|
36
|
-
}
|
|
37
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "n8n-nodes-atendix",
|
|
3
|
+
"version": "1.3.0",
|
|
4
|
+
"description": "Conector Atendix para integração nativa com a Tray Commerce",
|
|
5
|
+
"keywords": ["n8n-community-node-package"],
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"homepage": "https://atendix.co",
|
|
8
|
+
"author": {
|
|
9
|
+
"name": "Atendix",
|
|
10
|
+
"email": "contato@atendix.co"
|
|
11
|
+
},
|
|
12
|
+
"main": "index.js",
|
|
13
|
+
"scripts": {
|
|
14
|
+
"build": "echo 'v1.3.0 pre-compiled — no build needed'"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"icons",
|
|
19
|
+
"index.js"
|
|
20
|
+
],
|
|
21
|
+
"n8n": {
|
|
22
|
+
"n8nNodesApiVersion": 1,
|
|
23
|
+
"credentials": [
|
|
24
|
+
"dist/credentials/TrayApiAuto.credentials.js"
|
|
25
|
+
],
|
|
26
|
+
"nodes": [
|
|
27
|
+
"dist/nodes/Atendix/Atendix.node.js"
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"n8n-workflow": "^1.0.0"
|
|
32
|
+
}
|
|
33
|
+
}
|
package/README.md
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
# Atendix - Conector Tray Commerce para n8n 🚀
|
|
2
|
-
|
|
3
|
-

|
|
4
|
-

|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
O **Atendix** é o conector definitivo para integrar sua loja **Tray Commerce** ao ecossistema de automação do **n8n**. Esqueça a complexidade de lidar com tokens manuais e exposição de chaves sensíveis.
|
|
8
|
-
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
## ✨ Por que usar o Atendix?
|
|
12
|
-
|
|
13
|
-
- 🔒 **Segurança Total:** Suas chaves `Consumer Key` e `Consumer Secret` ficam protegidas e nunca são expostas no workflow.
|
|
14
|
-
- 🔄 **Autenticação Automática:** Gerenciamento inteligente de `Access Token` e `Refresh Token` sem interrupções.
|
|
15
|
-
- ⚖️ **Respeito ao Rate Limit:** Implementação nativa de *Throttling* (500ms) para garantir conformidade com o limite de 180 req/min da Tray.
|
|
16
|
-
- 💼 **Modelo SaaS:** Licenciamento centralizado para garantir suporte e atualizações contínuas.
|
|
17
|
-
|
|
18
|
-
---
|
|
19
|
-
|
|
20
|
-
## 🛠️ Instalação
|
|
21
|
-
|
|
22
|
-
Para instalar este nó em sua instância n8n:
|
|
23
|
-
|
|
24
|
-
1. Acesse **Settings** (Configurações).
|
|
25
|
-
2. Vá em **Community Nodes** (Nós da Comunidade).
|
|
26
|
-
3. Clique em **Install a node**.
|
|
27
|
-
4. Digite o nome do pacote: `n8n-nodes-atendix`.
|
|
28
|
-
5. Aceite os termos e clique em **Install**.
|
|
29
|
-
|
|
30
|
-
---
|
|
31
|
-
|
|
32
|
-
## 🔑 Configuração das Credenciais
|
|
33
|
-
|
|
34
|
-
Após a instalação, configure sua credencial Atendix com apenas dois campos:
|
|
35
|
-
|
|
36
|
-
1. **API Address:** A URL da sua loja (ex: `https://{SUA_LOJA}.commercesuite.com.br/web_api`).
|
|
37
|
-
2. **Authorization Code:** O código de autorização gerado no painel da Tray.
|
|
38
|
-
|
|
39
|
-
> **Nota:** Certifique-se de que sua loja possui uma licença ativa em [Atendix.co](https://atendix.co) para liberar o tráfego de dados.
|
|
40
|
-
|
|
41
|
-
---
|
|
42
|
-
|
|
43
|
-
## 📦 Recursos Disponíveis (v1.0.1)
|
|
44
|
-
|
|
45
|
-
- ✅ **Pedidos:** Busca por ID e Listagem com filtros de status e data.
|
|
46
|
-
- ✅ **Clientes:** Busca por e-mail ou documento (CPF/CNPJ).
|
|
47
|
-
- ✅ **Produtos:** Consulta rápida por ID ou SKU.
|
|
48
|
-
|
|
49
|
-
---
|
|
50
|
-
|
|
51
|
-
## ⚙️ Requisitos Técnicos
|
|
52
|
-
|
|
53
|
-
- **n8n version:** >= 1.0.0
|
|
54
|
-
- **Node.js:** >= 18.x
|
|
55
|
-
|
|
56
|
-
---
|
|
57
|
-
|
|
58
|
-
## 📄 Licença
|
|
59
|
-
|
|
60
|
-
Distribuído sob a licença MIT. Veja `LICENSE` para mais informações.
|
|
61
|
-
|
|
62
|
-
---
|
|
63
|
-
|
|
64
|
-
## 📞 Suporte e Contato
|
|
65
|
-
|
|
66
|
-
Dúvidas ou problemas com a ativação da sua licença?
|
|
67
|
-
- **Site:** [https://atendix.co](https://atendix.co)
|
|
68
|
-
- **E-mail:** contato@atendix.co
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { IAuthenticateGeneric, ICredentialType, INodeProperties, ICredentialDataDecryptedObject, ICredentialTestRequest, Icon } from 'n8n-workflow';
|
|
2
|
-
export declare class TrayApiAuto implements ICredentialType {
|
|
3
|
-
name: string;
|
|
4
|
-
displayName: string;
|
|
5
|
-
icon: Icon;
|
|
6
|
-
documentationUrl: string;
|
|
7
|
-
properties: INodeProperties[];
|
|
8
|
-
authenticate: IAuthenticateGeneric;
|
|
9
|
-
preAuthentication(credentials: ICredentialDataDecryptedObject): Promise<ICredentialDataDecryptedObject>;
|
|
10
|
-
/**
|
|
11
|
-
* Renomeado para evitar conflito com a propriedade 'authenticate' da interface
|
|
12
|
-
*/
|
|
13
|
-
private _authenticateFlow;
|
|
14
|
-
private _refreshTokenFlow;
|
|
15
|
-
test: ICredentialTestRequest;
|
|
16
|
-
}
|
package/dist/icons/tray.svg
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
-
<svg
|
|
3
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
4
|
-
viewBox="0 0 24 24"
|
|
5
|
-
fill="none"
|
|
6
|
-
stroke="#7D7D87"
|
|
7
|
-
stroke-width="1.3"
|
|
8
|
-
stroke-linecap="round"
|
|
9
|
-
stroke-linejoin="round"
|
|
10
|
-
>
|
|
11
|
-
<path d="M3 21 L3 6 C3 4.343 4.343 3 6 3 H18 C19.657 3 22 4.343 22 6 V14 C22 15.657 19.657 17 18 17 H8.5 L3 21 Z" />
|
|
12
|
-
|
|
13
|
-
<path d="M6.8 14V9.5" />
|
|
14
|
-
<circle cx="6.8" cy="8" r="1.5" />
|
|
15
|
-
|
|
16
|
-
<path d="M10.6 14V11.5L12.5 9" />
|
|
17
|
-
<circle cx="13.5" cy="7.8" r="1.5" />
|
|
18
|
-
|
|
19
|
-
<path d="M14 14V12.5C14 11.8 14.5 11.5 15.2 11.5H17" />
|
|
20
|
-
<circle cx="18.5" cy="11.5" r="1.5" />
|
|
21
|
-
</svg>
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Arquivo: n8n-nodes-atendix/nodes/Atendix/Atendix.node.ts
|
|
3
|
-
*
|
|
4
|
-
* Node oficial Atendix - Tray Commerce
|
|
5
|
-
* Versão 1.2.0 - Licenciamento SaaS + Full Operations
|
|
6
|
-
*/
|
|
7
|
-
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
8
|
-
export declare class Atendix implements INodeType {
|
|
9
|
-
description: INodeTypeDescription;
|
|
10
|
-
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
11
|
-
}
|