n8n-nodes-atendix 1.3.5 → 1.3.6
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,166 @@ 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 Notas Fiscais', value: 'list', description: 'Lista notas fiscais da loja', action: 'Listar notas fiscais' },
|
|
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: ['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
|
+
{
|
|
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
|
+
},
|
|
246
|
+
{
|
|
247
|
+
displayName: 'Dados da Nota Fiscal', name: 'invoiceData', type: 'collection',
|
|
248
|
+
placeholder: 'Adicionar Campo', default: {},
|
|
249
|
+
displayOptions: { show: { resource: ['invoice'], operation: ['create'] } },
|
|
250
|
+
options: [
|
|
251
|
+
{ displayName: 'Número da NF', name: 'number', type: 'string', default: '' },
|
|
252
|
+
{ displayName: 'Série', name: 'serie', type: 'string', default: '' },
|
|
253
|
+
{ displayName: 'Data de Emissão', name: 'issue_date', type: 'string', default: '' },
|
|
254
|
+
{ displayName: 'Valor Total', name: 'value', type: 'number', default: 0 },
|
|
255
|
+
{ displayName: 'Chave de Acesso', name: 'key', type: 'string', default: '' },
|
|
256
|
+
{ displayName: 'XML da NF', name: 'xml', type: 'string', default: '' },
|
|
257
|
+
{ displayName: 'Link (DANFE/PDF)', name: 'link', type: 'string', default: '' },
|
|
258
|
+
],
|
|
259
|
+
},
|
|
168
260
|
],
|
|
169
261
|
};
|
|
170
262
|
}
|
|
171
263
|
async execute() {
|
|
172
|
-
var _a
|
|
264
|
+
var _a;
|
|
173
265
|
const items = this.getInputData();
|
|
174
266
|
const returnData = [];
|
|
175
267
|
const resource = this.getNodeParameter('resource', 0);
|
|
176
268
|
const operation = this.getNodeParameter('operation', 0);
|
|
177
|
-
|
|
178
|
-
if (!
|
|
179
|
-
console.error('❌ ATENDIX_AUTH_TOKEN não configurado nas variáveis de ambiente');
|
|
269
|
+
// Chave internalizadas — não dependem de variáveis de ambiente
|
|
270
|
+
if (!ATENDIX_AUTH_TOKEN) {
|
|
180
271
|
throw new Error('Configuração de validação Atendix incompleta. Entre em contato com o suporte.');
|
|
181
272
|
}
|
|
182
|
-
// 1.
|
|
273
|
+
// 1. Credenciais
|
|
183
274
|
const credentials = await this.getCredentials('trayApiAuto');
|
|
184
275
|
let baseUrl = (credentials.apiHost || credentials.apiAddress || '').trim().replace(/\/$/, '');
|
|
185
|
-
let licenseCheck;
|
|
186
276
|
// ==========================================
|
|
187
277
|
// 1.1 VALIDAÇÃO DE LICENÇA (SaaS GATEKEEPER)
|
|
188
278
|
// ==========================================
|
|
189
|
-
// Pegamos a URL base da loja para validar no seu servidor
|
|
190
279
|
const storeBaseUrl = baseUrl.replace('/web_api', '');
|
|
280
|
+
let licenseCheck;
|
|
191
281
|
try {
|
|
192
282
|
licenseCheck = await this.helpers.httpRequest({
|
|
193
283
|
method: 'POST',
|
|
194
284
|
url: 'https://n8n.mariapinho.com.br/webhook/valida-atendix',
|
|
195
285
|
headers: {
|
|
196
286
|
'Content-Type': 'application/json',
|
|
197
|
-
'Authorization':
|
|
287
|
+
'Authorization': ATENDIX_AUTH_TOKEN,
|
|
198
288
|
},
|
|
199
289
|
body: { url: storeBaseUrl },
|
|
200
290
|
json: true,
|
|
@@ -202,19 +292,15 @@ class Atendix {
|
|
|
202
292
|
console.error('LICENSE CHECK RESPONSE: ' + licenseCheck.autorizado);
|
|
203
293
|
if (!licenseCheck || licenseCheck.autorizado !== true) {
|
|
204
294
|
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
|
|
295
|
+
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
296
|
}
|
|
207
297
|
}
|
|
208
298
|
catch (error) {
|
|
209
|
-
console.error('ATENDIX_AUTH_TOKEN:', atendixAuthToken);
|
|
210
299
|
console.error('STORE BASE URL:', storeBaseUrl);
|
|
211
|
-
console.error('LICENSECHECK:', licenseCheck);
|
|
212
300
|
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
301
|
if (error instanceof n8n_workflow_1.NodeOperationError)
|
|
216
302
|
throw error;
|
|
217
|
-
throw new n8n_workflow_1.NodeOperationError(this.getNode(),
|
|
303
|
+
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
304
|
}
|
|
219
305
|
// 2. TOKEN MANAGER (Fallback)
|
|
220
306
|
let accessToken = credentials.accessToken;
|
|
@@ -224,8 +310,8 @@ class Atendix {
|
|
|
224
310
|
method: 'POST',
|
|
225
311
|
url: `${baseUrl}/auth`,
|
|
226
312
|
body: {
|
|
227
|
-
consumer_key:
|
|
228
|
-
consumer_secret:
|
|
313
|
+
consumer_key: TRAY_CONSUMER_KEY,
|
|
314
|
+
consumer_secret: TRAY_CONSUMER_SECRET,
|
|
229
315
|
code: credentials.authCode,
|
|
230
316
|
},
|
|
231
317
|
json: true,
|
|
@@ -236,25 +322,21 @@ class Atendix {
|
|
|
236
322
|
throw new n8n_workflow_1.NodeOperationError(this.getNode(), `Falha na Autenticação Tray: ${authError.message}`);
|
|
237
323
|
}
|
|
238
324
|
}
|
|
325
|
+
// ==========================================
|
|
326
|
+
// 3. LOOP PRINCIPAL — Rate limit: 500ms entre itens
|
|
327
|
+
// ==========================================
|
|
239
328
|
for (let i = 0; i < items.length; i++) {
|
|
240
329
|
try {
|
|
330
|
+
if (i > 0)
|
|
331
|
+
await wait(500);
|
|
241
332
|
let responseData = {};
|
|
242
333
|
const qs = { access_token: accessToken };
|
|
243
334
|
// ==================== PEDIDOS ====================
|
|
244
335
|
if (resource === 'order') {
|
|
245
336
|
if (operation === 'get') {
|
|
246
337
|
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
|
-
});
|
|
338
|
+
console.log(`>>> ATENDIX - GET ${baseUrl}/orders/${orderId}`);
|
|
339
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${orderId}`, qs, json: true });
|
|
258
340
|
}
|
|
259
341
|
if (operation === 'list') {
|
|
260
342
|
const filters = this.getNodeParameter('filters', i, {});
|
|
@@ -262,17 +344,7 @@ class Atendix {
|
|
|
262
344
|
qs.status = filters.status;
|
|
263
345
|
if (filters.limit)
|
|
264
346
|
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
|
-
});
|
|
347
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders`, qs, json: true });
|
|
276
348
|
}
|
|
277
349
|
}
|
|
278
350
|
// ==================== CLIENTES ====================
|
|
@@ -284,14 +356,8 @@ class Atendix {
|
|
|
284
356
|
qs.email = searchValue;
|
|
285
357
|
else
|
|
286
358
|
qs.cpf_cnpj = searchValue;
|
|
287
|
-
responseData = await this.helpers.httpRequest({
|
|
288
|
-
method: 'GET',
|
|
289
|
-
url: `${baseUrl}/customers`,
|
|
290
|
-
qs,
|
|
291
|
-
json: true,
|
|
292
|
-
});
|
|
359
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/customers`, qs, json: true });
|
|
293
360
|
}
|
|
294
|
-
// Nota da Gerente: Se for adicionar UPSERT aqui, o código deve ser incluído abaixo
|
|
295
361
|
}
|
|
296
362
|
// ==================== PRODUTOS ====================
|
|
297
363
|
if (resource === 'product') {
|
|
@@ -299,24 +365,87 @@ class Atendix {
|
|
|
299
365
|
const searchType = this.getNodeParameter('searchType', i);
|
|
300
366
|
const searchValue = this.getNodeParameter('searchValue', i);
|
|
301
367
|
if (searchType === 'id') {
|
|
302
|
-
responseData = await this.helpers.httpRequest({
|
|
303
|
-
method: 'GET',
|
|
304
|
-
url: `${baseUrl}/products/${searchValue}`,
|
|
305
|
-
qs,
|
|
306
|
-
json: true,
|
|
307
|
-
});
|
|
368
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/products/${searchValue}`, qs, json: true });
|
|
308
369
|
}
|
|
309
370
|
else {
|
|
310
371
|
qs.sku = searchValue;
|
|
311
|
-
responseData = await this.helpers.httpRequest({
|
|
312
|
-
method: 'GET',
|
|
313
|
-
url: `${baseUrl}/products`,
|
|
314
|
-
qs,
|
|
315
|
-
json: true,
|
|
316
|
-
});
|
|
372
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/products`, qs, json: true });
|
|
317
373
|
}
|
|
318
374
|
}
|
|
319
|
-
|
|
375
|
+
}
|
|
376
|
+
// ==================== CATEGORIAS ====================
|
|
377
|
+
if (resource === 'category') {
|
|
378
|
+
if (operation === 'list') {
|
|
379
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
380
|
+
if (filters.limit)
|
|
381
|
+
qs.limit = filters.limit;
|
|
382
|
+
if (filters.page)
|
|
383
|
+
qs.page = filters.page;
|
|
384
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/categories`, qs, json: true });
|
|
385
|
+
}
|
|
386
|
+
if (operation === 'get') {
|
|
387
|
+
const categoryId = this.getNodeParameter('categoryId', i);
|
|
388
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/categories/${categoryId}`, qs, json: true });
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
// ==================== VARIAÇÕES ====================
|
|
392
|
+
if (resource === 'variant') {
|
|
393
|
+
if (operation === 'list') {
|
|
394
|
+
const productIdForVariant = this.getNodeParameter('productIdForVariant', i);
|
|
395
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
396
|
+
qs.product_id = productIdForVariant;
|
|
397
|
+
if (filters.limit)
|
|
398
|
+
qs.limit = filters.limit;
|
|
399
|
+
if (filters.page)
|
|
400
|
+
qs.page = filters.page;
|
|
401
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/variants`, qs, json: true });
|
|
402
|
+
}
|
|
403
|
+
if (operation === 'get') {
|
|
404
|
+
const variantId = this.getNodeParameter('variantId', i);
|
|
405
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/variants/${variantId}`, qs, json: true });
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
// ==================== IMAGENS DE PRODUTO ====================
|
|
409
|
+
if (resource === 'productImage') {
|
|
410
|
+
const productIdForImage = this.getNodeParameter('productIdForImage', i);
|
|
411
|
+
const imageUrl = this.getNodeParameter('imageUrl', i);
|
|
412
|
+
if (operation === 'create') {
|
|
413
|
+
const body = { ProductImage: { product_id: productIdForImage, url: imageUrl } };
|
|
414
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/products/${productIdForImage}/images`, qs, body, json: true });
|
|
415
|
+
}
|
|
416
|
+
if (operation === 'createVariant') {
|
|
417
|
+
const variantIdForImage = this.getNodeParameter('variantIdForImage', i);
|
|
418
|
+
const body = { VariantImage: { product_id: productIdForImage, variant_id: variantIdForImage, url: imageUrl } };
|
|
419
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/variants/${variantIdForImage}/images`, qs, body, json: true });
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
// ==================== NOTA FISCAL ====================
|
|
423
|
+
if (resource === 'invoice') {
|
|
424
|
+
if (operation === 'list') {
|
|
425
|
+
const filters = this.getNodeParameter('filters', i, {});
|
|
426
|
+
if (filters.orderId)
|
|
427
|
+
qs.order_id = filters.orderId;
|
|
428
|
+
if (filters.limit)
|
|
429
|
+
qs.limit = filters.limit;
|
|
430
|
+
if (filters.page)
|
|
431
|
+
qs.page = filters.page;
|
|
432
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/invoices`, qs, json: true });
|
|
433
|
+
}
|
|
434
|
+
if (operation === 'get') {
|
|
435
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
436
|
+
const invoiceId = this.getNodeParameter('invoiceId', i);
|
|
437
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${invoiceOrderId}/invoices/${invoiceId}`, qs, json: true });
|
|
438
|
+
}
|
|
439
|
+
if (operation === 'getByOrder') {
|
|
440
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
441
|
+
responseData = await this.helpers.httpRequest({ method: 'GET', url: `${baseUrl}/orders/${invoiceOrderId}/invoices`, qs, json: true });
|
|
442
|
+
}
|
|
443
|
+
if (operation === 'create') {
|
|
444
|
+
const invoiceOrderId = this.getNodeParameter('invoiceOrderId', i);
|
|
445
|
+
const invoiceData = this.getNodeParameter('invoiceData', i, {});
|
|
446
|
+
const body = { Invoice: { order_id: invoiceOrderId, ...invoiceData } };
|
|
447
|
+
responseData = await this.helpers.httpRequest({ method: 'POST', url: `${baseUrl}/invoices`, qs, body, json: true });
|
|
448
|
+
}
|
|
320
449
|
}
|
|
321
450
|
const executionData = this.helpers.constructExecutionMetaData(this.helpers.returnJsonArray(responseData), { itemData: { item: i } });
|
|
322
451
|
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.6",
|
|
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
|
}
|