n8n-nodes-confirm8 0.20.0 → 0.21.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.
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { IExecuteFunctions, INodeExecutionData, INodeType, INodeTypeDescription } from 'n8n-workflow';
|
|
2
2
|
export declare class Confirm8AgentTool implements INodeType {
|
|
3
3
|
description: INodeTypeDescription;
|
|
4
|
+
/**
|
|
5
|
+
* Parse natural date queries into filter JSON
|
|
6
|
+
*/
|
|
7
|
+
private parseDateQuery;
|
|
4
8
|
execute(this: IExecuteFunctions): Promise<INodeExecutionData[][]>;
|
|
5
9
|
}
|
|
@@ -10,7 +10,7 @@ class Confirm8AgentTool {
|
|
|
10
10
|
icon: 'file:tool.svg',
|
|
11
11
|
group: ['transform'],
|
|
12
12
|
version: 1,
|
|
13
|
-
description: 'AI
|
|
13
|
+
description: 'AI tool for Confirm8. IMPORTANT: For "OS/ordem de serviço" use resource="order". For dates use filters with start_date/end_date. Example: {"start_date":{"gte":"2025-11-23"},"end_date":{"lte":"2025-11-25"}}',
|
|
14
14
|
defaults: {
|
|
15
15
|
name: 'Confirm8 AI Tool',
|
|
16
16
|
},
|
|
@@ -56,60 +56,126 @@ class Confirm8AgentTool {
|
|
|
56
56
|
name: 'resource',
|
|
57
57
|
type: 'options',
|
|
58
58
|
options: [
|
|
59
|
-
{ name: 'User', value: 'user', description: 'Users/
|
|
60
|
-
{ name: 'Client', value: 'client', description: 'Clients/
|
|
61
|
-
{ name: 'Item', value: 'item', description: 'Items/
|
|
62
|
-
{ name: 'Item Type', value: 'itemType', description: 'Item
|
|
63
|
-
{ name: 'Task', value: 'task', description: 'Tasks/
|
|
64
|
-
{ name: 'Service', value: 'service', description: 'Services' },
|
|
65
|
-
{ name: 'Product', value: 'product', description: 'Products' },
|
|
66
|
-
{ name: 'Order', value: 'order', description: 'Work
|
|
67
|
-
{ name: 'Modality', value: 'modality', description: '
|
|
68
|
-
{ name: 'Ticket', value: 'ticket', description: 'Tickets' },
|
|
69
|
-
{ name: 'Property', value: 'property', description: 'Properties' },
|
|
59
|
+
{ name: 'User', value: 'user', description: 'Users/Employees/Funcionários' },
|
|
60
|
+
{ name: 'Client', value: 'client', description: 'Clients/Customers/Clientes' },
|
|
61
|
+
{ name: 'Item', value: 'item', description: 'Items/Itens' },
|
|
62
|
+
{ name: 'Item Type', value: 'itemType', description: 'Item Types/Tipos' },
|
|
63
|
+
{ name: 'Task', value: 'task', description: 'Tasks/Tarefas/Checklists' },
|
|
64
|
+
{ name: 'Service', value: 'service', description: 'Services/Serviços' },
|
|
65
|
+
{ name: 'Product', value: 'product', description: 'Products/Produtos' },
|
|
66
|
+
{ name: 'Order (OS)', value: 'order', description: 'Work Orders/OS/Ordens de Serviço/WOS' },
|
|
67
|
+
{ name: 'Modality', value: 'modality', description: 'Modalities/Modalidades' },
|
|
68
|
+
{ name: 'Ticket', value: 'ticket', description: 'Tickets/Chamados' },
|
|
69
|
+
{ name: 'Property', value: 'property', description: 'Properties/Propriedades' },
|
|
70
70
|
],
|
|
71
71
|
default: 'user',
|
|
72
|
-
description: '
|
|
72
|
+
description: 'CRITICAL: For "OS" or "ordem de serviço" ALWAYS use "order"',
|
|
73
73
|
},
|
|
74
74
|
{
|
|
75
75
|
displayName: 'Operation',
|
|
76
76
|
name: 'operation',
|
|
77
77
|
type: 'options',
|
|
78
78
|
options: [
|
|
79
|
-
{ name: 'Get All', value: 'getAll', description: 'List
|
|
80
|
-
{ name: 'Get', value: 'get', description: 'Get
|
|
81
|
-
{ name: 'Create', value: 'create', description: 'Create
|
|
82
|
-
{ name: 'Update', value: 'update', description: 'Update
|
|
83
|
-
{ name: 'Activate', value: 'activate', description: 'Activate
|
|
84
|
-
{ name: 'Deactivate', value: 'deactivate', description: 'Deactivate
|
|
79
|
+
{ name: 'Get All (List)', value: 'getAll', description: 'List/Listar/Buscar todos/Mostrar todos' },
|
|
80
|
+
{ name: 'Get (Single)', value: 'get', description: 'Get one/Buscar um/Obter' },
|
|
81
|
+
{ name: 'Create', value: 'create', description: 'Create/Criar/Adicionar' },
|
|
82
|
+
{ name: 'Update', value: 'update', description: 'Update/Atualizar/Modificar' },
|
|
83
|
+
{ name: 'Activate', value: 'activate', description: 'Activate/Ativar' },
|
|
84
|
+
{ name: 'Deactivate', value: 'deactivate', description: 'Deactivate/Desativar' },
|
|
85
85
|
],
|
|
86
86
|
default: 'getAll',
|
|
87
|
-
description: '
|
|
87
|
+
description: 'CRITICAL: To list/search use "getAll"',
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
displayName: 'Record ID',
|
|
91
91
|
name: 'recordId',
|
|
92
92
|
type: 'string',
|
|
93
93
|
default: '',
|
|
94
|
-
description: 'ID
|
|
94
|
+
description: 'Record ID (only for get/update/activate/deactivate)',
|
|
95
95
|
},
|
|
96
96
|
{
|
|
97
97
|
displayName: 'Data',
|
|
98
98
|
name: 'data',
|
|
99
99
|
type: 'string',
|
|
100
100
|
default: '',
|
|
101
|
-
description: 'JSON data for create/update',
|
|
101
|
+
description: 'JSON data (only for create/update)',
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
displayName: 'Filters',
|
|
105
105
|
name: 'filters',
|
|
106
106
|
type: 'string',
|
|
107
107
|
default: '',
|
|
108
|
-
description: 'Filters as JSON
|
|
108
|
+
description: 'Filters as JSON. For dates: {"start_date":{"gte":"YYYY-MM-DD"},"end_date":{"lte":"YYYY-MM-DD"}}. For status: {"status":{"eq":"complete"}}',
|
|
109
109
|
},
|
|
110
110
|
],
|
|
111
111
|
};
|
|
112
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Parse natural date queries into filter JSON
|
|
115
|
+
*/
|
|
116
|
+
parseDateQuery(query) {
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const today = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
119
|
+
query = query.toLowerCase().trim();
|
|
120
|
+
// Helper to format date as YYYY-MM-DD
|
|
121
|
+
const formatDate = (date) => {
|
|
122
|
+
return date.toISOString().split('T')[0];
|
|
123
|
+
};
|
|
124
|
+
// Today
|
|
125
|
+
if (query.includes('hoje') || query.includes('today')) {
|
|
126
|
+
const endOfDay = new Date(today);
|
|
127
|
+
endOfDay.setHours(23, 59, 59, 999);
|
|
128
|
+
return {
|
|
129
|
+
start_date: { gte: formatDate(today) },
|
|
130
|
+
end_date: { lte: formatDate(endOfDay) }
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
// This week / semana atual
|
|
134
|
+
if (query.includes('semana') || query.includes('week')) {
|
|
135
|
+
const dayOfWeek = today.getDay();
|
|
136
|
+
const diff = dayOfWeek === 0 ? -6 : 1 - dayOfWeek; // Monday is start
|
|
137
|
+
const weekStart = new Date(today);
|
|
138
|
+
weekStart.setDate(today.getDate() + diff);
|
|
139
|
+
const weekEnd = new Date(weekStart);
|
|
140
|
+
weekEnd.setDate(weekStart.getDate() + 6);
|
|
141
|
+
return {
|
|
142
|
+
start_date: { gte: formatDate(weekStart) },
|
|
143
|
+
end_date: { lte: formatDate(weekEnd) }
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
// This month / mês atual
|
|
147
|
+
if (query.includes('mês') || query.includes('mes') || query.includes('month')) {
|
|
148
|
+
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
|
|
149
|
+
const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 0);
|
|
150
|
+
return {
|
|
151
|
+
start_date: { gte: formatDate(monthStart) },
|
|
152
|
+
end_date: { lte: formatDate(monthEnd) }
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// Last N days
|
|
156
|
+
const lastDaysMatch = query.match(/últimos?\s+(\d+)\s+dias?|last\s+(\d+)\s+days?/);
|
|
157
|
+
if (lastDaysMatch) {
|
|
158
|
+
const days = parseInt(lastDaysMatch[1] || lastDaysMatch[2]);
|
|
159
|
+
const startDate = new Date(today);
|
|
160
|
+
startDate.setDate(today.getDate() - days);
|
|
161
|
+
return {
|
|
162
|
+
start_date: { gte: formatDate(startDate) },
|
|
163
|
+
end_date: { lte: formatDate(today) }
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
// Next N days
|
|
167
|
+
const nextDaysMatch = query.match(/próximos?\s+(\d+)\s+dias?|next\s+(\d+)\s+days?/);
|
|
168
|
+
if (nextDaysMatch) {
|
|
169
|
+
const days = parseInt(nextDaysMatch[1] || nextDaysMatch[2]);
|
|
170
|
+
const endDate = new Date(today);
|
|
171
|
+
endDate.setDate(today.getDate() + days);
|
|
172
|
+
return {
|
|
173
|
+
start_date: { gte: formatDate(today) },
|
|
174
|
+
end_date: { lte: formatDate(endDate) }
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return null;
|
|
178
|
+
}
|
|
113
179
|
async execute() {
|
|
114
180
|
const items = this.getInputData();
|
|
115
181
|
const returnData = [];
|
|
@@ -138,39 +204,110 @@ class Confirm8AgentTool {
|
|
|
138
204
|
const filtersParam = this.getNodeParameter('filters', i, '');
|
|
139
205
|
if (filtersParam) {
|
|
140
206
|
try {
|
|
207
|
+
// Try to parse as JSON first
|
|
141
208
|
filters = JSON.parse(filtersParam);
|
|
142
209
|
}
|
|
143
210
|
catch (e) {
|
|
144
|
-
//
|
|
211
|
+
// If not JSON, try to parse as natural language date query
|
|
212
|
+
const parsedDate = this.parseDateQuery(filtersParam);
|
|
213
|
+
if (parsedDate) {
|
|
214
|
+
filters = parsedDate;
|
|
215
|
+
}
|
|
145
216
|
}
|
|
146
217
|
}
|
|
147
|
-
//
|
|
218
|
+
// Aggressive normalization - EXPAND THIS
|
|
148
219
|
const resourceMap = {
|
|
149
|
-
|
|
220
|
+
// Standard
|
|
221
|
+
'users': 'user', 'usuarios': 'user', 'usuários': 'user', 'employees': 'user', 'funcionários': 'user', 'funcionarios': 'user',
|
|
150
222
|
'clients': 'client', 'clientes': 'client', 'customers': 'client',
|
|
151
223
|
'items': 'item', 'itens': 'item',
|
|
152
224
|
'itemtypes': 'itemType', 'tipos': 'itemType',
|
|
153
225
|
'tasks': 'task', 'tarefas': 'task',
|
|
154
226
|
'services': 'service', 'serviços': 'service', 'servicos': 'service',
|
|
155
227
|
'products': 'product', 'produtos': 'product',
|
|
156
|
-
'orders': 'order', 'ordens': 'order', 'wos': 'order',
|
|
157
228
|
'modalities': 'modality', 'modalidades': 'modality',
|
|
158
229
|
'tickets': 'ticket', 'chamados': 'ticket',
|
|
159
230
|
'properties': 'property', 'propriedades': 'property',
|
|
231
|
+
// CRITICAL: OS mappings
|
|
232
|
+
'os': 'order',
|
|
233
|
+
'o.s': 'order',
|
|
234
|
+
'o.s.': 'order',
|
|
235
|
+
'orders': 'order',
|
|
236
|
+
'ordens': 'order',
|
|
237
|
+
'ordem': 'order',
|
|
238
|
+
'ordem de serviço': 'order',
|
|
239
|
+
'ordem de servico': 'order',
|
|
240
|
+
'ordens de serviço': 'order',
|
|
241
|
+
'ordens de servico': 'order',
|
|
242
|
+
'wos': 'order',
|
|
243
|
+
'work order': 'order',
|
|
244
|
+
'work orders': 'order',
|
|
245
|
+
'pedidos': 'order',
|
|
246
|
+
'pedido': 'order',
|
|
160
247
|
};
|
|
161
248
|
const operationMap = {
|
|
162
|
-
|
|
163
|
-
'
|
|
164
|
-
'
|
|
165
|
-
'
|
|
166
|
-
'
|
|
249
|
+
// List/Get All
|
|
250
|
+
'list': 'getAll',
|
|
251
|
+
'listar': 'getAll',
|
|
252
|
+
'mostrar': 'getAll',
|
|
253
|
+
'buscar': 'getAll',
|
|
254
|
+
'busque': 'getAll',
|
|
255
|
+
'buscar todos': 'getAll',
|
|
256
|
+
'buscar todas': 'getAll',
|
|
257
|
+
'buscar todo': 'getAll',
|
|
258
|
+
'buscar toda': 'getAll',
|
|
259
|
+
'get all': 'getAll',
|
|
260
|
+
'show all': 'getAll',
|
|
261
|
+
'list all': 'getAll',
|
|
262
|
+
'todos': 'getAll',
|
|
263
|
+
'todas': 'getAll',
|
|
264
|
+
'obter todos': 'getAll',
|
|
265
|
+
'obter todas': 'getAll',
|
|
266
|
+
'pegar todos': 'getAll',
|
|
267
|
+
'pegar todas': 'getAll',
|
|
268
|
+
// Get Single
|
|
269
|
+
'obter': 'get',
|
|
270
|
+
'pegar': 'get',
|
|
271
|
+
'ver': 'get',
|
|
272
|
+
'visualizar': 'get',
|
|
273
|
+
// Create
|
|
274
|
+
'criar': 'create',
|
|
275
|
+
'adicionar': 'create',
|
|
276
|
+
'add': 'create',
|
|
277
|
+
'novo': 'create',
|
|
278
|
+
'nova': 'create',
|
|
279
|
+
'new': 'create',
|
|
280
|
+
// Update
|
|
281
|
+
'atualizar': 'update',
|
|
282
|
+
'modificar': 'update',
|
|
283
|
+
'modify': 'update',
|
|
284
|
+
'editar': 'update',
|
|
285
|
+
'edit': 'update',
|
|
286
|
+
// Activate/Deactivate
|
|
287
|
+
'ativar': 'activate',
|
|
288
|
+
'enable': 'activate',
|
|
289
|
+
'habilitar': 'activate',
|
|
290
|
+
'desativar': 'deactivate',
|
|
291
|
+
'disable': 'deactivate',
|
|
292
|
+
'desabilitar': 'deactivate',
|
|
167
293
|
};
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
294
|
+
// Normalize - clean and map
|
|
295
|
+
const cleanResource = resource.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
296
|
+
const cleanOperation = operation.toLowerCase().trim().replace(/\s+/g, ' ');
|
|
297
|
+
resource = resourceMap[cleanResource] || resource;
|
|
298
|
+
operation = operationMap[cleanOperation] || operation;
|
|
299
|
+
// If still not valid, throw clear error
|
|
300
|
+
const validResources = ['user', 'client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality', 'ticket', 'property'];
|
|
301
|
+
const validOperations = ['getAll', 'get', 'create', 'update', 'activate', 'deactivate'];
|
|
302
|
+
if (!validResources.includes(resource)) {
|
|
303
|
+
throw new Error(`Invalid resource: "${resource}". Must be one of: ${validResources.join(', ')}. ` +
|
|
304
|
+
`For OS/Ordem de Serviço use "order".`);
|
|
305
|
+
}
|
|
306
|
+
if (!validOperations.includes(operation)) {
|
|
307
|
+
throw new Error(`Invalid operation: "${operation}". Must be one of: ${validOperations.join(', ')}. ` +
|
|
308
|
+
`To list/search use "getAll".`);
|
|
172
309
|
}
|
|
173
|
-
//
|
|
310
|
+
// Resource config with relations
|
|
174
311
|
const resourceConfig = {
|
|
175
312
|
user: {
|
|
176
313
|
endpoint: 'users',
|
|
@@ -218,14 +355,11 @@ class Confirm8AgentTool {
|
|
|
218
355
|
},
|
|
219
356
|
};
|
|
220
357
|
const config = resourceConfig[resource];
|
|
221
|
-
if (!config) {
|
|
222
|
-
throw new Error(`Unknown resource: ${resource}`);
|
|
223
|
-
}
|
|
224
358
|
const baseEndpoint = config.endpoint;
|
|
225
359
|
let endpoint = '';
|
|
226
360
|
let method = 'GET';
|
|
227
361
|
let body = {};
|
|
228
|
-
// Build endpoint
|
|
362
|
+
// Build endpoint
|
|
229
363
|
switch (operation) {
|
|
230
364
|
case 'getAll':
|
|
231
365
|
endpoint = `/v3/${baseEndpoint}`;
|
|
@@ -261,12 +395,10 @@ class Confirm8AgentTool {
|
|
|
261
395
|
method = ['client', 'item', 'itemType', 'task', 'service', 'product', 'order', 'modality'].includes(resource)
|
|
262
396
|
? 'PUT' : 'PATCH';
|
|
263
397
|
break;
|
|
264
|
-
default:
|
|
265
|
-
throw new Error(`Unknown operation: ${operation}`);
|
|
266
398
|
}
|
|
267
399
|
// Build query string
|
|
268
400
|
const queryParams = [];
|
|
269
|
-
// Add relations (only
|
|
401
|
+
// Add relations (only GET)
|
|
270
402
|
if (method === 'GET' && config.relations.length > 0) {
|
|
271
403
|
config.relations.forEach(relation => {
|
|
272
404
|
queryParams.push(`relations=${relation}`);
|
|
@@ -282,7 +414,7 @@ class Confirm8AgentTool {
|
|
|
282
414
|
});
|
|
283
415
|
});
|
|
284
416
|
}
|
|
285
|
-
//
|
|
417
|
+
// Full URL
|
|
286
418
|
let fullUrl = `${baseUrl}${endpoint}`;
|
|
287
419
|
if (queryParams.length > 0) {
|
|
288
420
|
fullUrl += '?' + queryParams.join('&');
|
|
@@ -306,6 +438,12 @@ class Confirm8AgentTool {
|
|
|
306
438
|
returnData.push({
|
|
307
439
|
json: {
|
|
308
440
|
success: true,
|
|
441
|
+
normalized: {
|
|
442
|
+
originalResource: this.getNodeParameter('resource', i, ''),
|
|
443
|
+
originalOperation: this.getNodeParameter('operation', i, ''),
|
|
444
|
+
normalizedResource: resource,
|
|
445
|
+
normalizedOperation: operation,
|
|
446
|
+
},
|
|
309
447
|
operation,
|
|
310
448
|
resource,
|
|
311
449
|
endpoint,
|