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
- displayName: 'Access Token',
43
- name: 'accessToken',
44
- type: 'hidden',
45
- default: '',
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: 'edb6c989395fa2d22be2505dbbaa202a818ca0fd0eb222a7389fdedb3782a467',
89
- consumer_secret: '750dfb677368a6c070ef24de04e843eaa6085a05be298e98d5ad7139421ef7e6',
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: ${consumerKey ? 'Preenchida' : 'VAZIA!'}`);
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: consumerKey,
125
- consumer_secret: consumerSecret,
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, // Geralmente vem algo como "https://1225878.commercesuite.com.br"
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 (error) {
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 SELECTOR ====================
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 - OPERATIONS ====================
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
- name: 'Buscar Pedido',
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
- name: 'filters',
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 - OPERATIONS ====================
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 - OPERATIONS ====================
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, _b;
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
- const atendixAuthToken = 'Bearer eyJhbGciOiJIUzI1NiJ9.e30.lEvvOyjcYVhrGiBHYZvV_HsmFEsc8lLw0yiNZk8lHJk';
178
- if (!atendixAuthToken) {
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. Get Credentials
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': atendixAuthToken,
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 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!`);
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(), "Erro crítico: Não foi possível validar a licença do Atendix no servidor MariaPinho.");
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: 'edb6c989395fa2d22be2505dbbaa202a818ca0fd0eb222a7389fdedb3782a467',
228
- consumer_secret: '750dfb677368a6c070ef24de04e843eaa6085a05be298e98d5ad7139421ef7e6',
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
- if (i > 0) {
248
- // 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
- 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
- if (i > 0) {
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
- // Nota da Gerente: Se for adicionar UPDATE_STOCK aqui, o código deve ser incluído abaixo
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 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; } });
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.5",
4
- "description": "Conector Atendix para integração nativa com a Tray Commerce",
5
- "keywords": ["n8n-community-node-package"],
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": "echo 'v1.3.0 pre-compiled — no build needed'"
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
- "dependencies": {
31
- "n8n-workflow": "^1.0.0"
32
+ "devDependencies": {
33
+ "@types/node": "^18.19.130",
34
+ "n8n-workflow": "^2.13.0",
35
+ "typescript": "^5.9.3"
32
36
  }
33
37
  }