@belmontdigitalmarketing/n8n-nodes-flowlu 0.2.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.
|
@@ -0,0 +1,2397 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Flowlu = void 0;
|
|
4
|
+
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
// ============================================================
|
|
6
|
+
// Shared Helpers
|
|
7
|
+
// ============================================================
|
|
8
|
+
async function getFlowluCredentials(context) {
|
|
9
|
+
const credentials = await context.getCredentials('flowluApi');
|
|
10
|
+
const subdomain = credentials.subdomain;
|
|
11
|
+
const apiKey = credentials.apiKey;
|
|
12
|
+
const baseUrl = subdomain.endsWith('.flowlu.com')
|
|
13
|
+
? `https://${subdomain}`
|
|
14
|
+
: `https://${subdomain}.flowlu.com`;
|
|
15
|
+
return { baseUrl, apiKey };
|
|
16
|
+
}
|
|
17
|
+
async function flowluApiRequest(method, baseUrl, endpoint, apiKey, body, queryParams) {
|
|
18
|
+
const qs = { api_key: apiKey };
|
|
19
|
+
if (queryParams) {
|
|
20
|
+
Object.assign(qs, queryParams);
|
|
21
|
+
}
|
|
22
|
+
const requestOptions = {
|
|
23
|
+
method,
|
|
24
|
+
uri: `${baseUrl}${endpoint}`,
|
|
25
|
+
qs,
|
|
26
|
+
json: false,
|
|
27
|
+
};
|
|
28
|
+
if (method === 'POST' && body) {
|
|
29
|
+
requestOptions.form = body;
|
|
30
|
+
}
|
|
31
|
+
const response = await this.helpers.request(requestOptions);
|
|
32
|
+
return typeof response === 'string' ? JSON.parse(response) : response;
|
|
33
|
+
}
|
|
34
|
+
function appendWorkflowFooter(target, contentKey = 'description') {
|
|
35
|
+
const { id } = this.getWorkflow();
|
|
36
|
+
const footer = `<hr><em>Generated via n8n:</em> <a href="${this.getInstanceBaseUrl()}workflow/${id}">View Workflow</a>`;
|
|
37
|
+
const existing = target[contentKey];
|
|
38
|
+
target[contentKey] = existing ? `${existing}<br><br>${footer}` : footer;
|
|
39
|
+
}
|
|
40
|
+
function applyResourceMapperFields(context, body, paramName, itemIndex) {
|
|
41
|
+
try {
|
|
42
|
+
const mappingValues = context.getNodeParameter(`${paramName}.value`, itemIndex);
|
|
43
|
+
if (mappingValues) {
|
|
44
|
+
for (const [key, value] of Object.entries(mappingValues)) {
|
|
45
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
46
|
+
body[key] = value;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
// No custom fields set - ignore
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
// Helper to load custom fields by module/model, with fallback approach
|
|
56
|
+
async function loadCustomFieldsForEntity(context, moduleFilter, modelFilter) {
|
|
57
|
+
try {
|
|
58
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(context);
|
|
59
|
+
// Strategy 1: Get all fieldsets and match by module/model
|
|
60
|
+
const fieldsetsResponse = await flowluApiRequest.call(context, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
|
|
61
|
+
const fieldsetIds = [];
|
|
62
|
+
if (fieldsetsResponse?.response?.items) {
|
|
63
|
+
for (const fs of fieldsetsResponse.response.items) {
|
|
64
|
+
if (fs.module === moduleFilter && fs.model === modelFilter) {
|
|
65
|
+
fieldsetIds.push(fs.id.toString());
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Get all custom fields
|
|
70
|
+
const fieldsResponse = await flowluApiRequest.call(context, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
|
|
71
|
+
if (fieldsResponse?.response?.items) {
|
|
72
|
+
let fields = fieldsResponse.response.items;
|
|
73
|
+
// Filter by fieldset if we found matching fieldsets
|
|
74
|
+
if (fieldsetIds.length > 0) {
|
|
75
|
+
fields = fields.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()));
|
|
76
|
+
}
|
|
77
|
+
else {
|
|
78
|
+
// Fallback: filter by module/model directly on the field
|
|
79
|
+
fields = fields.filter((f) => f.module === moduleFilter && f.model === modelFilter);
|
|
80
|
+
}
|
|
81
|
+
return fields
|
|
82
|
+
.filter((f) => f.active !== 0)
|
|
83
|
+
.map((f) => {
|
|
84
|
+
const cfKey = (f.api_use_alias && f.alias)
|
|
85
|
+
? `cf_${f.alias}`
|
|
86
|
+
: `cf_${f.id}`;
|
|
87
|
+
return {
|
|
88
|
+
name: f.name || cfKey,
|
|
89
|
+
value: cfKey,
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
return [];
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// ============================================================
|
|
100
|
+
// Reusable custom field option definitions
|
|
101
|
+
// ============================================================
|
|
102
|
+
// Helper to load entity custom fields as resourceMapper columns
|
|
103
|
+
async function loadEntityCustomFieldColumns(moduleFilter, modelFilter) {
|
|
104
|
+
try {
|
|
105
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
106
|
+
const fieldsetsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
|
|
107
|
+
const fieldsetIds = [];
|
|
108
|
+
if (fieldsetsResponse?.response?.items) {
|
|
109
|
+
for (const fs of fieldsetsResponse.response.items) {
|
|
110
|
+
if (fs.module === moduleFilter && fs.model === modelFilter) {
|
|
111
|
+
fieldsetIds.push(fs.id.toString());
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
const fieldsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
|
|
116
|
+
if (!fieldsResponse?.response?.items)
|
|
117
|
+
return { fields: [] };
|
|
118
|
+
let filtered = fieldsResponse.response.items.filter((f) => f.active !== 0);
|
|
119
|
+
if (fieldsetIds.length > 0) {
|
|
120
|
+
filtered = filtered.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()));
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
filtered = filtered.filter((f) => f.module === moduleFilter && f.model === modelFilter);
|
|
124
|
+
}
|
|
125
|
+
const fields = filtered.map((f) => {
|
|
126
|
+
const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
|
|
127
|
+
let fieldType = 'string';
|
|
128
|
+
if (f.type === 'int')
|
|
129
|
+
fieldType = 'number';
|
|
130
|
+
else if (f.type === 'boolean')
|
|
131
|
+
fieldType = 'boolean';
|
|
132
|
+
else if (f.type === 'date' || f.type === 'datetime')
|
|
133
|
+
fieldType = 'dateTime';
|
|
134
|
+
else if (f.type === 'price' || f.type === 'decimal')
|
|
135
|
+
fieldType = 'number';
|
|
136
|
+
else if (f.type === 'model.any')
|
|
137
|
+
fieldType = 'number';
|
|
138
|
+
let fieldOptions;
|
|
139
|
+
if (f.type === 'select.single') {
|
|
140
|
+
fieldType = 'options';
|
|
141
|
+
try {
|
|
142
|
+
const opts = JSON.parse(f.extra_fields || '{}');
|
|
143
|
+
const selectOptions = JSON.parse(opts.select_options || '[]');
|
|
144
|
+
fieldOptions = selectOptions
|
|
145
|
+
.filter((o) => o.value)
|
|
146
|
+
.map((o) => ({ name: o.value, value: o.id?.toString() || o.value }));
|
|
147
|
+
}
|
|
148
|
+
catch { /* ignore */ }
|
|
149
|
+
}
|
|
150
|
+
const field = {
|
|
151
|
+
id: cfKey,
|
|
152
|
+
displayName: f.name || cfKey,
|
|
153
|
+
required: f.required === 1,
|
|
154
|
+
defaultMatch: false,
|
|
155
|
+
display: true,
|
|
156
|
+
type: fieldType,
|
|
157
|
+
canBeUsedToMatch: false,
|
|
158
|
+
};
|
|
159
|
+
if (fieldOptions)
|
|
160
|
+
field.options = fieldOptions;
|
|
161
|
+
return field;
|
|
162
|
+
});
|
|
163
|
+
return { fields };
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
return { fields: [] };
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
function makeCustomFieldsOption(loadMethod) {
|
|
170
|
+
return {
|
|
171
|
+
displayName: 'Custom Fields',
|
|
172
|
+
name: 'customFields',
|
|
173
|
+
type: 'fixedCollection',
|
|
174
|
+
default: {},
|
|
175
|
+
placeholder: 'Add Custom Field',
|
|
176
|
+
typeOptions: { multipleValues: true },
|
|
177
|
+
description: 'Custom fields defined in your Flowlu account',
|
|
178
|
+
options: [
|
|
179
|
+
{
|
|
180
|
+
displayName: 'Custom Field',
|
|
181
|
+
name: 'customField',
|
|
182
|
+
values: [
|
|
183
|
+
{
|
|
184
|
+
displayName: 'Field Name',
|
|
185
|
+
name: 'fieldId',
|
|
186
|
+
type: 'options',
|
|
187
|
+
typeOptions: { loadOptionsMethod: loadMethod },
|
|
188
|
+
default: '',
|
|
189
|
+
description: 'Select the custom field',
|
|
190
|
+
},
|
|
191
|
+
{
|
|
192
|
+
displayName: 'Value',
|
|
193
|
+
name: 'value',
|
|
194
|
+
type: 'string',
|
|
195
|
+
default: '',
|
|
196
|
+
description: 'The value for this custom field',
|
|
197
|
+
},
|
|
198
|
+
],
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
const taskCustomFieldsOption = makeCustomFieldsOption('getTaskCustomFields');
|
|
204
|
+
const contactCustomFieldsOption = makeCustomFieldsOption('getContactCustomFields');
|
|
205
|
+
const projectCustomFieldsOption = makeCustomFieldsOption('getProjectCustomFields');
|
|
206
|
+
const opportunityCustomFieldsOption = makeCustomFieldsOption('getOpportunityCustomFields');
|
|
207
|
+
// ============================================================
|
|
208
|
+
// Node Definition
|
|
209
|
+
// ============================================================
|
|
210
|
+
class Flowlu {
|
|
211
|
+
constructor() {
|
|
212
|
+
this.description = {
|
|
213
|
+
displayName: 'Flowlu',
|
|
214
|
+
name: 'flowlu',
|
|
215
|
+
icon: 'file:flowlu.svg',
|
|
216
|
+
group: ['apps'],
|
|
217
|
+
version: 1,
|
|
218
|
+
description: 'Interact with the Flowlu API to manage contacts, tasks, projects, and opportunities',
|
|
219
|
+
defaults: { name: 'Flowlu' },
|
|
220
|
+
inputs: ['main'],
|
|
221
|
+
outputs: ['main'],
|
|
222
|
+
credentials: [{ name: 'flowluApi', required: true }],
|
|
223
|
+
properties: [
|
|
224
|
+
// ========================================
|
|
225
|
+
// RESOURCE SELECTOR
|
|
226
|
+
// ========================================
|
|
227
|
+
{
|
|
228
|
+
displayName: 'Resource',
|
|
229
|
+
name: 'resource',
|
|
230
|
+
type: 'options',
|
|
231
|
+
noDataExpression: true,
|
|
232
|
+
options: [
|
|
233
|
+
{ name: 'Comment', value: 'comment' },
|
|
234
|
+
{ name: 'Contact', value: 'contact' },
|
|
235
|
+
{ name: 'Opportunity', value: 'opportunity' },
|
|
236
|
+
{ name: 'Project', value: 'project' },
|
|
237
|
+
{ name: 'Record List', value: 'recordList' },
|
|
238
|
+
{ name: 'Tag', value: 'tag' },
|
|
239
|
+
{ name: 'Task', value: 'task' },
|
|
240
|
+
],
|
|
241
|
+
default: 'task',
|
|
242
|
+
},
|
|
243
|
+
// ========================================
|
|
244
|
+
// TAG OPERATIONS
|
|
245
|
+
// ========================================
|
|
246
|
+
{
|
|
247
|
+
displayName: 'Operation',
|
|
248
|
+
name: 'operation',
|
|
249
|
+
type: 'options',
|
|
250
|
+
noDataExpression: true,
|
|
251
|
+
displayOptions: { show: { resource: ['tag'] } },
|
|
252
|
+
options: [
|
|
253
|
+
{ name: 'Add', value: 'add', description: 'Add a tag to an entity', action: 'Add a tag' },
|
|
254
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get all tags on an entity', action: 'Get tags on entity' },
|
|
255
|
+
{ name: 'List All', value: 'listAll', description: 'List all tags in your account', action: 'List all tags' },
|
|
256
|
+
{ name: 'Remove', value: 'remove', description: 'Remove a tag from an entity', action: 'Remove a tag' },
|
|
257
|
+
],
|
|
258
|
+
default: 'add',
|
|
259
|
+
},
|
|
260
|
+
// ========================================
|
|
261
|
+
// COMMENT OPERATIONS
|
|
262
|
+
// ========================================
|
|
263
|
+
{
|
|
264
|
+
displayName: 'Operation',
|
|
265
|
+
name: 'operation',
|
|
266
|
+
type: 'options',
|
|
267
|
+
noDataExpression: true,
|
|
268
|
+
displayOptions: { show: { resource: ['comment'] } },
|
|
269
|
+
options: [
|
|
270
|
+
{ name: 'Create', value: 'create', description: 'Create a comment on an entity', action: 'Create a comment' },
|
|
271
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get all comments on an entity', action: 'Get comments on entity' },
|
|
272
|
+
],
|
|
273
|
+
default: 'create',
|
|
274
|
+
},
|
|
275
|
+
// ========================================
|
|
276
|
+
// CONTACT OPERATIONS
|
|
277
|
+
// ========================================
|
|
278
|
+
{
|
|
279
|
+
displayName: 'Operation',
|
|
280
|
+
name: 'operation',
|
|
281
|
+
type: 'options',
|
|
282
|
+
noDataExpression: true,
|
|
283
|
+
displayOptions: { show: { resource: ['contact'] } },
|
|
284
|
+
options: [
|
|
285
|
+
{ name: 'Create', value: 'create', description: 'Create a new contact', action: 'Create a contact' },
|
|
286
|
+
{ name: 'Delete', value: 'delete', description: 'Delete a contact', action: 'Delete a contact' },
|
|
287
|
+
{ name: 'Get', value: 'get', description: 'Get a contact by ID', action: 'Get a contact' },
|
|
288
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get many contacts', action: 'Get many contacts' },
|
|
289
|
+
{ name: 'Update', value: 'update', description: 'Update a contact', action: 'Update a contact' },
|
|
290
|
+
],
|
|
291
|
+
default: 'create',
|
|
292
|
+
},
|
|
293
|
+
// ========================================
|
|
294
|
+
// OPPORTUNITY OPERATIONS
|
|
295
|
+
// ========================================
|
|
296
|
+
{
|
|
297
|
+
displayName: 'Operation',
|
|
298
|
+
name: 'operation',
|
|
299
|
+
type: 'options',
|
|
300
|
+
noDataExpression: true,
|
|
301
|
+
displayOptions: { show: { resource: ['opportunity'] } },
|
|
302
|
+
options: [
|
|
303
|
+
{ name: 'Create', value: 'create', description: 'Create a new opportunity', action: 'Create an opportunity' },
|
|
304
|
+
{ name: 'Delete', value: 'delete', description: 'Delete an opportunity', action: 'Delete an opportunity' },
|
|
305
|
+
{ name: 'Get', value: 'get', description: 'Get an opportunity by ID', action: 'Get an opportunity' },
|
|
306
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get many opportunities', action: 'Get many opportunities' },
|
|
307
|
+
{ name: 'Update', value: 'update', description: 'Update an opportunity', action: 'Update an opportunity' },
|
|
308
|
+
],
|
|
309
|
+
default: 'create',
|
|
310
|
+
},
|
|
311
|
+
// ========================================
|
|
312
|
+
// PROJECT OPERATIONS
|
|
313
|
+
// ========================================
|
|
314
|
+
{
|
|
315
|
+
displayName: 'Operation',
|
|
316
|
+
name: 'operation',
|
|
317
|
+
type: 'options',
|
|
318
|
+
noDataExpression: true,
|
|
319
|
+
displayOptions: { show: { resource: ['project'] } },
|
|
320
|
+
options: [
|
|
321
|
+
{ name: 'Create', value: 'create', description: 'Create a new project', action: 'Create a project' },
|
|
322
|
+
{ name: 'Delete', value: 'delete', description: 'Delete a project', action: 'Delete a project' },
|
|
323
|
+
{ name: 'Get', value: 'get', description: 'Get a project by ID', action: 'Get a project' },
|
|
324
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get many projects', action: 'Get many projects' },
|
|
325
|
+
{ name: 'Update', value: 'update', description: 'Update a project', action: 'Update a project' },
|
|
326
|
+
],
|
|
327
|
+
default: 'create',
|
|
328
|
+
},
|
|
329
|
+
// ========================================
|
|
330
|
+
// RECORD LIST OPERATIONS
|
|
331
|
+
// ========================================
|
|
332
|
+
{
|
|
333
|
+
displayName: 'Operation',
|
|
334
|
+
name: 'operation',
|
|
335
|
+
type: 'options',
|
|
336
|
+
noDataExpression: true,
|
|
337
|
+
displayOptions: { show: { resource: ['recordList'] } },
|
|
338
|
+
options: [
|
|
339
|
+
{ name: 'Create', value: 'create', description: 'Create a new record', action: 'Create a record' },
|
|
340
|
+
{ name: 'Delete', value: 'delete', description: 'Delete a record', action: 'Delete a record' },
|
|
341
|
+
{ name: 'Get', value: 'get', description: 'Get a record by ID', action: 'Get a record' },
|
|
342
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get many records', action: 'Get many records' },
|
|
343
|
+
{ name: 'Update', value: 'update', description: 'Update a record', action: 'Update a record' },
|
|
344
|
+
],
|
|
345
|
+
default: 'create',
|
|
346
|
+
},
|
|
347
|
+
// ========================================
|
|
348
|
+
// TASK OPERATIONS
|
|
349
|
+
// ========================================
|
|
350
|
+
{
|
|
351
|
+
displayName: 'Operation',
|
|
352
|
+
name: 'operation',
|
|
353
|
+
type: 'options',
|
|
354
|
+
noDataExpression: true,
|
|
355
|
+
displayOptions: { show: { resource: ['task'] } },
|
|
356
|
+
options: [
|
|
357
|
+
{ name: 'Create', value: 'create', description: 'Create a new task', action: 'Create a task' },
|
|
358
|
+
{ name: 'Delete', value: 'delete', description: 'Delete a task', action: 'Delete a task' },
|
|
359
|
+
{ name: 'Get', value: 'get', description: 'Get a task by ID', action: 'Get a task' },
|
|
360
|
+
{ name: 'Get Many', value: 'getAll', description: 'Get many tasks', action: 'Get many tasks' },
|
|
361
|
+
{ name: 'Update', value: 'update', description: 'Update a task', action: 'Update a task' },
|
|
362
|
+
],
|
|
363
|
+
default: 'create',
|
|
364
|
+
},
|
|
365
|
+
// ========================================
|
|
366
|
+
// CONTACT FIELDS
|
|
367
|
+
// ========================================
|
|
368
|
+
{
|
|
369
|
+
displayName: 'Contact ID',
|
|
370
|
+
name: 'contactId',
|
|
371
|
+
type: 'number',
|
|
372
|
+
default: 0,
|
|
373
|
+
required: true,
|
|
374
|
+
description: 'The ID of the contact in Flowlu',
|
|
375
|
+
displayOptions: { show: { resource: ['contact'], operation: ['get', 'update', 'delete'] } },
|
|
376
|
+
},
|
|
377
|
+
{
|
|
378
|
+
displayName: 'First Name',
|
|
379
|
+
name: 'first_name',
|
|
380
|
+
type: 'string',
|
|
381
|
+
default: '',
|
|
382
|
+
required: true,
|
|
383
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
384
|
+
},
|
|
385
|
+
{
|
|
386
|
+
displayName: 'Last Name',
|
|
387
|
+
name: 'last_name',
|
|
388
|
+
type: 'string',
|
|
389
|
+
default: '',
|
|
390
|
+
required: true,
|
|
391
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
392
|
+
},
|
|
393
|
+
{
|
|
394
|
+
displayName: 'Email',
|
|
395
|
+
name: 'email',
|
|
396
|
+
type: 'string',
|
|
397
|
+
default: '',
|
|
398
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
399
|
+
},
|
|
400
|
+
{
|
|
401
|
+
displayName: 'Phone',
|
|
402
|
+
name: 'phone',
|
|
403
|
+
type: 'string',
|
|
404
|
+
default: '',
|
|
405
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
displayName: 'Additional Fields',
|
|
409
|
+
name: 'contactAdditionalFields',
|
|
410
|
+
type: 'collection',
|
|
411
|
+
placeholder: 'Add Field',
|
|
412
|
+
default: {},
|
|
413
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
414
|
+
options: [
|
|
415
|
+
{ displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
|
|
416
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
417
|
+
{ displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '', description: 'Assigned user for this contact' },
|
|
418
|
+
{ displayName: 'Date of Birth', name: 'birth_date', type: 'dateTime', default: '' },
|
|
419
|
+
{ displayName: 'Website', name: 'web', type: 'string', default: '' },
|
|
420
|
+
{ displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
|
|
421
|
+
{ displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
|
|
422
|
+
{ displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
|
|
423
|
+
{ displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
|
|
424
|
+
{ displayName: 'Facebook', name: 'social_network_link_3', type: 'string', default: '' },
|
|
425
|
+
{ displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
|
|
426
|
+
{ displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
|
|
427
|
+
{ displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
|
|
428
|
+
{ displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
|
|
429
|
+
{ displayName: 'Address', name: 'address', type: 'string', default: '' },
|
|
430
|
+
{ displayName: 'Shipping Address Line 1', name: 'shipping_address_line_1', type: 'string', default: '' },
|
|
431
|
+
{ displayName: 'Shipping City', name: 'shipping_city', type: 'string', default: '' },
|
|
432
|
+
{ displayName: 'Shipping State', name: 'shipping_state', type: 'string', default: '' },
|
|
433
|
+
{ displayName: 'Shipping Zip', name: 'shipping_zip', type: 'string', default: '' },
|
|
434
|
+
{ displayName: 'Shipping Country', name: 'shipping_country', type: 'string', default: '' },
|
|
435
|
+
{ displayName: 'Billing Address Line 1', name: 'billing_address_line_1', type: 'string', default: '' },
|
|
436
|
+
{ displayName: 'Billing City', name: 'billing_city', type: 'string', default: '' },
|
|
437
|
+
{ displayName: 'Billing State', name: 'billing_state', type: 'string', default: '' },
|
|
438
|
+
{ displayName: 'Billing Zip', name: 'billing_zip', type: 'string', default: '' },
|
|
439
|
+
{ displayName: 'Billing Country', name: 'billing_country', type: 'string', default: '' },
|
|
440
|
+
{ displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
|
|
441
|
+
{ displayName: 'Timezone', name: 'timezone', type: 'string', default: '' },
|
|
442
|
+
{
|
|
443
|
+
displayName: 'Include Link to Workflow',
|
|
444
|
+
name: 'includeLinkToWorkflow',
|
|
445
|
+
type: 'boolean',
|
|
446
|
+
default: false,
|
|
447
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the contact description, linking back to this workflow',
|
|
448
|
+
},
|
|
449
|
+
],
|
|
450
|
+
},
|
|
451
|
+
{
|
|
452
|
+
displayName: 'Custom Fields',
|
|
453
|
+
name: 'contactCustomFields',
|
|
454
|
+
type: 'resourceMapper',
|
|
455
|
+
noDataExpression: true,
|
|
456
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
457
|
+
displayOptions: { show: { resource: ['contact'], operation: ['create'] } },
|
|
458
|
+
typeOptions: {
|
|
459
|
+
resourceMapper: {
|
|
460
|
+
resourceMapperMethod: 'getContactColumns',
|
|
461
|
+
mode: 'add',
|
|
462
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
463
|
+
addAllFields: false,
|
|
464
|
+
multiKeyMatch: false,
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
{
|
|
469
|
+
displayName: 'Update Fields',
|
|
470
|
+
name: 'contactUpdateFields',
|
|
471
|
+
type: 'collection',
|
|
472
|
+
placeholder: 'Add Field',
|
|
473
|
+
default: {},
|
|
474
|
+
displayOptions: { show: { resource: ['contact'], operation: ['update'] } },
|
|
475
|
+
options: [
|
|
476
|
+
{ displayName: 'First Name', name: 'first_name', type: 'string', default: '' },
|
|
477
|
+
{ displayName: 'Last Name', name: 'last_name', type: 'string', default: '' },
|
|
478
|
+
{ displayName: 'Middle Name', name: 'middle_name', type: 'string', default: '' },
|
|
479
|
+
{ displayName: 'Email', name: 'email', type: 'string', default: '' },
|
|
480
|
+
{ displayName: 'Phone', name: 'phone', type: 'string', default: '' },
|
|
481
|
+
{ displayName: 'Phone 2', name: 'phone2', type: 'string', default: '' },
|
|
482
|
+
{ displayName: 'Phone 3', name: 'phone3', type: 'string', default: '' },
|
|
483
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
484
|
+
{ displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
485
|
+
{ displayName: 'Date of Birth', name: 'birth_date', type: 'dateTime', default: '' },
|
|
486
|
+
{ displayName: 'Website', name: 'web', type: 'string', default: '' },
|
|
487
|
+
{ displayName: 'Personal Email', name: 'email_personal', type: 'string', default: '' },
|
|
488
|
+
{ displayName: 'Telegram', name: 'social_network_link_2', type: 'string', default: '' },
|
|
489
|
+
{ displayName: 'Facebook', name: 'social_network_link_3', type: 'string', default: '' },
|
|
490
|
+
{ displayName: 'X (Twitter)', name: 'social_network_link_4', type: 'string', default: '' },
|
|
491
|
+
{ displayName: 'LinkedIn', name: 'social_network_link_5', type: 'string', default: '' },
|
|
492
|
+
{ displayName: 'Instagram', name: 'social_network_link_6', type: 'string', default: '' },
|
|
493
|
+
{ displayName: 'Skype', name: 'social_network_link_1', type: 'string', default: '' },
|
|
494
|
+
{ displayName: 'Address', name: 'address', type: 'string', default: '' },
|
|
495
|
+
{ displayName: 'VAT / Tax ID', name: 'VAT', type: 'string', default: '' },
|
|
496
|
+
{
|
|
497
|
+
displayName: 'Include Link to Workflow',
|
|
498
|
+
name: 'includeLinkToWorkflow',
|
|
499
|
+
type: 'boolean',
|
|
500
|
+
default: false,
|
|
501
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the contact description, linking back to this workflow. Requires Description to be set.',
|
|
502
|
+
},
|
|
503
|
+
],
|
|
504
|
+
},
|
|
505
|
+
{
|
|
506
|
+
displayName: 'Custom Fields',
|
|
507
|
+
name: 'contactUpdateCustomFields',
|
|
508
|
+
type: 'resourceMapper',
|
|
509
|
+
noDataExpression: true,
|
|
510
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
511
|
+
displayOptions: { show: { resource: ['contact'], operation: ['update'] } },
|
|
512
|
+
typeOptions: {
|
|
513
|
+
resourceMapper: {
|
|
514
|
+
resourceMapperMethod: 'getContactColumns',
|
|
515
|
+
mode: 'update',
|
|
516
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
517
|
+
addAllFields: false,
|
|
518
|
+
multiKeyMatch: false,
|
|
519
|
+
},
|
|
520
|
+
},
|
|
521
|
+
},
|
|
522
|
+
// Contact Get Many: Filters
|
|
523
|
+
{
|
|
524
|
+
displayName: 'Limit',
|
|
525
|
+
name: 'limit',
|
|
526
|
+
type: 'number',
|
|
527
|
+
default: 50,
|
|
528
|
+
description: 'Max number of results to return',
|
|
529
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
530
|
+
displayOptions: { show: { resource: ['contact'], operation: ['getAll'] } },
|
|
531
|
+
},
|
|
532
|
+
{
|
|
533
|
+
displayName: 'Filters',
|
|
534
|
+
name: 'contactFilters',
|
|
535
|
+
type: 'collection',
|
|
536
|
+
placeholder: 'Add Filter',
|
|
537
|
+
default: {},
|
|
538
|
+
displayOptions: { show: { resource: ['contact'], operation: ['getAll'] } },
|
|
539
|
+
options: [
|
|
540
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by contact name' },
|
|
541
|
+
{ displayName: 'Email', name: 'email', type: 'string', default: '', description: 'Filter by email address' },
|
|
542
|
+
{ displayName: 'Phone', name: 'phone', type: 'string', default: '', description: 'Filter by phone number' },
|
|
543
|
+
{ displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '', description: 'Filter by assigned owner' },
|
|
544
|
+
],
|
|
545
|
+
},
|
|
546
|
+
// ========================================
|
|
547
|
+
// OPPORTUNITY FIELDS
|
|
548
|
+
// ========================================
|
|
549
|
+
{
|
|
550
|
+
displayName: 'Opportunity ID',
|
|
551
|
+
name: 'opportunityId',
|
|
552
|
+
type: 'number',
|
|
553
|
+
default: 0,
|
|
554
|
+
required: true,
|
|
555
|
+
description: 'The ID of the opportunity in Flowlu',
|
|
556
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['get', 'update', 'delete'] } },
|
|
557
|
+
},
|
|
558
|
+
// Opportunity Create: Main Fields
|
|
559
|
+
{
|
|
560
|
+
displayName: 'Name',
|
|
561
|
+
name: 'opportunityName',
|
|
562
|
+
type: 'string',
|
|
563
|
+
default: '',
|
|
564
|
+
required: true,
|
|
565
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
566
|
+
},
|
|
567
|
+
{
|
|
568
|
+
displayName: 'Pipeline',
|
|
569
|
+
name: 'pipeline_id',
|
|
570
|
+
type: 'options',
|
|
571
|
+
typeOptions: { loadOptionsMethod: 'getPipelines' },
|
|
572
|
+
required: true,
|
|
573
|
+
default: '',
|
|
574
|
+
description: 'The sales pipeline for this opportunity',
|
|
575
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
576
|
+
},
|
|
577
|
+
{
|
|
578
|
+
displayName: 'Pipeline Stage',
|
|
579
|
+
name: 'pipeline_stage_id',
|
|
580
|
+
type: 'options',
|
|
581
|
+
typeOptions: { loadOptionsMethod: 'getPipelineStages' },
|
|
582
|
+
required: true,
|
|
583
|
+
default: '',
|
|
584
|
+
description: 'The stage within the pipeline',
|
|
585
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
586
|
+
},
|
|
587
|
+
{
|
|
588
|
+
displayName: 'Assignee',
|
|
589
|
+
name: 'opportunityAssignee',
|
|
590
|
+
type: 'options',
|
|
591
|
+
typeOptions: { loadOptionsMethod: 'getUsers' },
|
|
592
|
+
default: '',
|
|
593
|
+
description: 'The user responsible for this opportunity',
|
|
594
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
595
|
+
},
|
|
596
|
+
// Opportunity Create: Additional Fields
|
|
597
|
+
{
|
|
598
|
+
displayName: 'Additional Fields',
|
|
599
|
+
name: 'opportunityAdditionalFields',
|
|
600
|
+
type: 'collection',
|
|
601
|
+
placeholder: 'Add Field',
|
|
602
|
+
default: {},
|
|
603
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
604
|
+
options: [
|
|
605
|
+
{ displayName: 'Budget', name: 'budget', type: 'number', default: 0, description: 'Opportunity value/amount' },
|
|
606
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
607
|
+
{ displayName: 'Link to Account', name: 'link_account_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Link this opportunity to an existing CRM account (organization)' },
|
|
608
|
+
{ displayName: 'Link to Contact', name: 'link_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'Link this opportunity to an existing CRM contact' },
|
|
609
|
+
{ displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
|
|
610
|
+
{ displayName: 'Planned Close Date', name: 'deadline', type: 'dateTime', default: '' },
|
|
611
|
+
{ displayName: 'Source', name: 'source_id', type: 'options', typeOptions: { loadOptionsMethod: 'getOpportunitySources' }, default: '', description: 'Where this opportunity came from' },
|
|
612
|
+
{ displayName: 'Contact Name', name: 'contact_name', type: 'string', default: '', description: 'Quick-add contact name (if not linking to existing contact)' },
|
|
613
|
+
{ displayName: 'Contact Email', name: 'contact_email', type: 'string', default: '' },
|
|
614
|
+
{ displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
|
|
615
|
+
{ displayName: 'Contact Mobile', name: 'contact_mobile', type: 'string', default: '' },
|
|
616
|
+
{ displayName: 'Contact Company', name: 'contact_company', type: 'string', default: '' },
|
|
617
|
+
{ displayName: 'Contact Position', name: 'contact_position', type: 'string', default: '' },
|
|
618
|
+
{ displayName: 'Contact Website', name: 'contact_web', type: 'string', default: '' },
|
|
619
|
+
{
|
|
620
|
+
displayName: 'Include Link to Workflow',
|
|
621
|
+
name: 'includeLinkToWorkflow',
|
|
622
|
+
type: 'boolean',
|
|
623
|
+
default: false,
|
|
624
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the opportunity description, linking back to this workflow',
|
|
625
|
+
},
|
|
626
|
+
],
|
|
627
|
+
},
|
|
628
|
+
{
|
|
629
|
+
displayName: 'Custom Fields',
|
|
630
|
+
name: 'opportunityCustomFields',
|
|
631
|
+
type: 'resourceMapper',
|
|
632
|
+
noDataExpression: true,
|
|
633
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
634
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['create'] } },
|
|
635
|
+
typeOptions: {
|
|
636
|
+
resourceMapper: {
|
|
637
|
+
resourceMapperMethod: 'getOpportunityColumns',
|
|
638
|
+
mode: 'add',
|
|
639
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
640
|
+
addAllFields: false,
|
|
641
|
+
multiKeyMatch: false,
|
|
642
|
+
},
|
|
643
|
+
},
|
|
644
|
+
},
|
|
645
|
+
// Opportunity Update: Fields
|
|
646
|
+
{
|
|
647
|
+
displayName: 'Update Fields',
|
|
648
|
+
name: 'opportunityUpdateFields',
|
|
649
|
+
type: 'collection',
|
|
650
|
+
placeholder: 'Add Field',
|
|
651
|
+
default: {},
|
|
652
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['update'] } },
|
|
653
|
+
options: [
|
|
654
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '' },
|
|
655
|
+
{ displayName: 'Pipeline', name: 'pipeline_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPipelines' }, default: '' },
|
|
656
|
+
{ displayName: 'Pipeline Stage', name: 'pipeline_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPipelineStages' }, default: '' },
|
|
657
|
+
{ displayName: 'Assignee', name: 'assignee_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
658
|
+
{ displayName: 'Budget', name: 'budget', type: 'number', default: 0 },
|
|
659
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
660
|
+
{ displayName: 'Link to Account', name: 'link_account_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Link this opportunity to a CRM account' },
|
|
661
|
+
{ displayName: 'Link to Contact', name: 'link_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'Link this opportunity to a CRM contact' },
|
|
662
|
+
{ displayName: 'Start Date', name: 'start_date', type: 'dateTime', default: '' },
|
|
663
|
+
{ displayName: 'Planned Close Date', name: 'deadline', type: 'dateTime', default: '' },
|
|
664
|
+
{
|
|
665
|
+
displayName: 'Status',
|
|
666
|
+
name: 'active',
|
|
667
|
+
type: 'options',
|
|
668
|
+
options: [
|
|
669
|
+
{ name: 'In Progress', value: 1 },
|
|
670
|
+
{ name: 'Lost', value: 2 },
|
|
671
|
+
{ name: 'Won', value: 3 },
|
|
672
|
+
],
|
|
673
|
+
default: 1,
|
|
674
|
+
},
|
|
675
|
+
{ displayName: 'Closing Comment', name: 'closing_comment', type: 'string', default: '', description: 'Reason for win/loss' },
|
|
676
|
+
{ displayName: 'Contact Name', name: 'contact_name', type: 'string', default: '' },
|
|
677
|
+
{ displayName: 'Contact Email', name: 'contact_email', type: 'string', default: '' },
|
|
678
|
+
{ displayName: 'Contact Phone', name: 'contact_phone', type: 'string', default: '' },
|
|
679
|
+
{
|
|
680
|
+
displayName: 'Include Link to Workflow',
|
|
681
|
+
name: 'includeLinkToWorkflow',
|
|
682
|
+
type: 'boolean',
|
|
683
|
+
default: false,
|
|
684
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the opportunity description, linking back to this workflow. Requires Description to be set.',
|
|
685
|
+
},
|
|
686
|
+
],
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
displayName: 'Custom Fields',
|
|
690
|
+
name: 'opportunityUpdateCustomFields',
|
|
691
|
+
type: 'resourceMapper',
|
|
692
|
+
noDataExpression: true,
|
|
693
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
694
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['update'] } },
|
|
695
|
+
typeOptions: {
|
|
696
|
+
resourceMapper: {
|
|
697
|
+
resourceMapperMethod: 'getOpportunityColumns',
|
|
698
|
+
mode: 'update',
|
|
699
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
700
|
+
addAllFields: false,
|
|
701
|
+
multiKeyMatch: false,
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
},
|
|
705
|
+
// Opportunity Get Many: Filters
|
|
706
|
+
{
|
|
707
|
+
displayName: 'Limit',
|
|
708
|
+
name: 'limit',
|
|
709
|
+
type: 'number',
|
|
710
|
+
default: 50,
|
|
711
|
+
description: 'Max number of results to return',
|
|
712
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
713
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
|
|
714
|
+
},
|
|
715
|
+
{
|
|
716
|
+
displayName: 'Pipeline',
|
|
717
|
+
name: 'opportunityFilterPipeline',
|
|
718
|
+
type: 'options',
|
|
719
|
+
typeOptions: { loadOptionsMethod: 'getPipelines' },
|
|
720
|
+
default: '',
|
|
721
|
+
description: 'Filter by pipeline (also controls which stages appear below)',
|
|
722
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
displayName: 'Pipeline Stage',
|
|
726
|
+
name: 'opportunityFilterStage',
|
|
727
|
+
type: 'options',
|
|
728
|
+
typeOptions: {
|
|
729
|
+
loadOptionsMethod: 'getFilteredPipelineStages',
|
|
730
|
+
loadOptionsDependsOn: ['opportunityFilterPipeline'],
|
|
731
|
+
},
|
|
732
|
+
default: '',
|
|
733
|
+
description: 'Filter by stage (select a pipeline first to see its stages)',
|
|
734
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
displayName: 'Filters',
|
|
738
|
+
name: 'opportunityFilters',
|
|
739
|
+
type: 'collection',
|
|
740
|
+
placeholder: 'Add Filter',
|
|
741
|
+
default: {},
|
|
742
|
+
displayOptions: { show: { resource: ['opportunity'], operation: ['getAll'] } },
|
|
743
|
+
options: [
|
|
744
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by opportunity name' },
|
|
745
|
+
{ displayName: 'Assignee', name: 'assignee_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
746
|
+
{ displayName: 'Account / Contact', name: 'company_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAllCrmAccounts' }, default: '', description: 'Filter by linked company or contact (note: Flowlu may not support this filter)' },
|
|
747
|
+
{
|
|
748
|
+
displayName: 'Status',
|
|
749
|
+
name: 'active',
|
|
750
|
+
type: 'options',
|
|
751
|
+
options: [
|
|
752
|
+
{ name: 'In Progress', value: 1 },
|
|
753
|
+
{ name: 'Lost', value: 2 },
|
|
754
|
+
{ name: 'Won', value: 3 },
|
|
755
|
+
],
|
|
756
|
+
default: 1,
|
|
757
|
+
},
|
|
758
|
+
],
|
|
759
|
+
},
|
|
760
|
+
// ========================================
|
|
761
|
+
// PROJECT FIELDS
|
|
762
|
+
// ========================================
|
|
763
|
+
{
|
|
764
|
+
displayName: 'Project ID',
|
|
765
|
+
name: 'projectId',
|
|
766
|
+
type: 'number',
|
|
767
|
+
default: 0,
|
|
768
|
+
required: true,
|
|
769
|
+
description: 'The ID of the project in Flowlu',
|
|
770
|
+
displayOptions: { show: { resource: ['project'], operation: ['get', 'update', 'delete'] } },
|
|
771
|
+
},
|
|
772
|
+
// Project Create: Main Fields
|
|
773
|
+
{
|
|
774
|
+
displayName: 'Project Name',
|
|
775
|
+
name: 'projectName',
|
|
776
|
+
type: 'string',
|
|
777
|
+
default: '',
|
|
778
|
+
required: true,
|
|
779
|
+
displayOptions: { show: { resource: ['project'], operation: ['create'] } },
|
|
780
|
+
},
|
|
781
|
+
{
|
|
782
|
+
displayName: 'Manager',
|
|
783
|
+
name: 'manager_id',
|
|
784
|
+
type: 'options',
|
|
785
|
+
typeOptions: { loadOptionsMethod: 'getUsers' },
|
|
786
|
+
default: '',
|
|
787
|
+
description: 'The user responsible for managing this project',
|
|
788
|
+
displayOptions: { show: { resource: ['project'], operation: ['create'] } },
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
displayName: 'Description',
|
|
792
|
+
name: 'projectDescription',
|
|
793
|
+
type: 'string',
|
|
794
|
+
typeOptions: { rows: 4 },
|
|
795
|
+
default: '',
|
|
796
|
+
displayOptions: { show: { resource: ['project'], operation: ['create'] } },
|
|
797
|
+
},
|
|
798
|
+
// Project Create: Additional Fields
|
|
799
|
+
{
|
|
800
|
+
displayName: 'Additional Fields',
|
|
801
|
+
name: 'projectAdditionalFields',
|
|
802
|
+
type: 'collection',
|
|
803
|
+
placeholder: 'Add Field',
|
|
804
|
+
default: {},
|
|
805
|
+
displayOptions: { show: { resource: ['project'], operation: ['create'] } },
|
|
806
|
+
options: [
|
|
807
|
+
{
|
|
808
|
+
displayName: 'Priority',
|
|
809
|
+
name: 'priority',
|
|
810
|
+
type: 'options',
|
|
811
|
+
options: [
|
|
812
|
+
{ name: 'Low', value: 1 },
|
|
813
|
+
{ name: 'Medium', value: 2 },
|
|
814
|
+
{ name: 'High', value: 3 },
|
|
815
|
+
],
|
|
816
|
+
default: 2,
|
|
817
|
+
},
|
|
818
|
+
{ displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
|
|
819
|
+
{ displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
|
|
820
|
+
{ displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
|
|
821
|
+
{ displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
|
|
822
|
+
{ displayName: 'Customer (Company ID)', name: 'customer_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'CRM company linked to this project' },
|
|
823
|
+
{ displayName: 'Customer Contact', name: 'customer_crm_contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'CRM contact linked to this project' },
|
|
824
|
+
{ displayName: 'Portfolio', name: 'briefcase_id', type: 'options', typeOptions: { loadOptionsMethod: 'getPortfolios' }, default: '' },
|
|
825
|
+
{ displayName: 'Project Stage', name: 'stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjectStages' }, default: '' },
|
|
826
|
+
{ displayName: 'Task Workflow', name: 'tasks_workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '', description: 'Default task workflow for tasks in this project' },
|
|
827
|
+
{
|
|
828
|
+
displayName: 'Billing Type',
|
|
829
|
+
name: 'billing_type',
|
|
830
|
+
type: 'options',
|
|
831
|
+
options: [
|
|
832
|
+
{ name: 'Fixed Values', value: 0 },
|
|
833
|
+
{ name: 'Based on Task Hours', value: 10 },
|
|
834
|
+
{ name: 'Cash Flow Planning', value: 20 },
|
|
835
|
+
{ name: 'Ignore Finances', value: 30 },
|
|
836
|
+
],
|
|
837
|
+
default: 0,
|
|
838
|
+
},
|
|
839
|
+
{ displayName: 'Opportunity', name: 'crm_lead_id', type: 'number', default: 0, description: 'ID of a linked CRM opportunity' },
|
|
840
|
+
{
|
|
841
|
+
displayName: 'Include Link to Workflow',
|
|
842
|
+
name: 'includeLinkToWorkflow',
|
|
843
|
+
type: 'boolean',
|
|
844
|
+
default: false,
|
|
845
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the project description, linking back to this workflow',
|
|
846
|
+
},
|
|
847
|
+
],
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
displayName: 'Custom Fields',
|
|
851
|
+
name: 'projectCustomFields',
|
|
852
|
+
type: 'resourceMapper',
|
|
853
|
+
noDataExpression: true,
|
|
854
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
855
|
+
displayOptions: { show: { resource: ['project'], operation: ['create'] } },
|
|
856
|
+
typeOptions: {
|
|
857
|
+
resourceMapper: {
|
|
858
|
+
resourceMapperMethod: 'getProjectColumns',
|
|
859
|
+
mode: 'add',
|
|
860
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
861
|
+
addAllFields: false,
|
|
862
|
+
multiKeyMatch: false,
|
|
863
|
+
},
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
// Project Update: Fields
|
|
867
|
+
{
|
|
868
|
+
displayName: 'Update Fields',
|
|
869
|
+
name: 'projectUpdateFields',
|
|
870
|
+
type: 'collection',
|
|
871
|
+
placeholder: 'Add Field',
|
|
872
|
+
default: {},
|
|
873
|
+
displayOptions: { show: { resource: ['project'], operation: ['update'] } },
|
|
874
|
+
options: [
|
|
875
|
+
{ displayName: 'Project Name', name: 'name', type: 'string', default: '' },
|
|
876
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
877
|
+
{ displayName: 'Manager', name: 'manager_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
878
|
+
{
|
|
879
|
+
displayName: 'Priority',
|
|
880
|
+
name: 'priority',
|
|
881
|
+
type: 'options',
|
|
882
|
+
options: [
|
|
883
|
+
{ name: 'Low', value: 1 },
|
|
884
|
+
{ name: 'Medium', value: 2 },
|
|
885
|
+
{ name: 'High', value: 3 },
|
|
886
|
+
],
|
|
887
|
+
default: 2,
|
|
888
|
+
},
|
|
889
|
+
{ displayName: 'Start Date', name: 'startdate', type: 'dateTime', default: '' },
|
|
890
|
+
{ displayName: 'End Date', name: 'enddate', type: 'dateTime', default: '' },
|
|
891
|
+
{ displayName: 'Contract Sum (Revenue)', name: 'estimated_revenue', type: 'number', default: 0 },
|
|
892
|
+
{ displayName: 'Expense Sum', name: 'estimated_expenses', type: 'number', default: 0 },
|
|
893
|
+
{ displayName: 'Project Stage', name: 'stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjectStages' }, default: '' },
|
|
894
|
+
{
|
|
895
|
+
displayName: 'Archived',
|
|
896
|
+
name: 'is_archive',
|
|
897
|
+
type: 'boolean',
|
|
898
|
+
default: false,
|
|
899
|
+
},
|
|
900
|
+
{
|
|
901
|
+
displayName: 'Include Link to Workflow',
|
|
902
|
+
name: 'includeLinkToWorkflow',
|
|
903
|
+
type: 'boolean',
|
|
904
|
+
default: false,
|
|
905
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the project description, linking back to this workflow. Requires Description to be set.',
|
|
906
|
+
},
|
|
907
|
+
],
|
|
908
|
+
},
|
|
909
|
+
{
|
|
910
|
+
displayName: 'Custom Fields',
|
|
911
|
+
name: 'projectUpdateCustomFields',
|
|
912
|
+
type: 'resourceMapper',
|
|
913
|
+
noDataExpression: true,
|
|
914
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
915
|
+
displayOptions: { show: { resource: ['project'], operation: ['update'] } },
|
|
916
|
+
typeOptions: {
|
|
917
|
+
resourceMapper: {
|
|
918
|
+
resourceMapperMethod: 'getProjectColumns',
|
|
919
|
+
mode: 'update',
|
|
920
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
921
|
+
addAllFields: false,
|
|
922
|
+
multiKeyMatch: false,
|
|
923
|
+
},
|
|
924
|
+
},
|
|
925
|
+
},
|
|
926
|
+
// Project Get Many: Filters
|
|
927
|
+
{
|
|
928
|
+
displayName: 'Limit',
|
|
929
|
+
name: 'limit',
|
|
930
|
+
type: 'number',
|
|
931
|
+
default: 50,
|
|
932
|
+
description: 'Max number of results to return',
|
|
933
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
934
|
+
displayOptions: { show: { resource: ['project'], operation: ['getAll'] } },
|
|
935
|
+
},
|
|
936
|
+
{
|
|
937
|
+
displayName: 'Filters',
|
|
938
|
+
name: 'projectFilters',
|
|
939
|
+
type: 'collection',
|
|
940
|
+
placeholder: 'Add Filter',
|
|
941
|
+
default: {},
|
|
942
|
+
displayOptions: { show: { resource: ['project'], operation: ['getAll'] } },
|
|
943
|
+
options: [
|
|
944
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by project name' },
|
|
945
|
+
{ displayName: 'Manager', name: 'manager_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
946
|
+
{ displayName: 'Customer', name: 'customer_id', type: 'options', typeOptions: { loadOptionsMethod: 'getAccounts' }, default: '', description: 'Filter by linked company' },
|
|
947
|
+
{
|
|
948
|
+
displayName: 'Include Archived',
|
|
949
|
+
name: 'is_archive',
|
|
950
|
+
type: 'boolean',
|
|
951
|
+
default: false,
|
|
952
|
+
},
|
|
953
|
+
],
|
|
954
|
+
},
|
|
955
|
+
// ========================================
|
|
956
|
+
// RECORD LIST FIELDS
|
|
957
|
+
// ========================================
|
|
958
|
+
{
|
|
959
|
+
displayName: 'Record List',
|
|
960
|
+
name: 'recordListId',
|
|
961
|
+
type: 'options',
|
|
962
|
+
typeOptions: { loadOptionsMethod: 'getRecordLists' },
|
|
963
|
+
required: true,
|
|
964
|
+
default: '',
|
|
965
|
+
description: 'Which record list to work with',
|
|
966
|
+
displayOptions: { show: { resource: ['recordList'] } },
|
|
967
|
+
},
|
|
968
|
+
{
|
|
969
|
+
displayName: 'Record ID',
|
|
970
|
+
name: 'recordId',
|
|
971
|
+
type: 'number',
|
|
972
|
+
default: 0,
|
|
973
|
+
required: true,
|
|
974
|
+
description: 'The ID of the record',
|
|
975
|
+
displayOptions: { show: { resource: ['recordList'], operation: ['get', 'update', 'delete'] } },
|
|
976
|
+
},
|
|
977
|
+
{
|
|
978
|
+
displayName: 'Fields',
|
|
979
|
+
name: 'recordFields',
|
|
980
|
+
type: 'resourceMapper',
|
|
981
|
+
noDataExpression: true,
|
|
982
|
+
default: {
|
|
983
|
+
mappingMode: 'defineBelow',
|
|
984
|
+
value: null,
|
|
985
|
+
},
|
|
986
|
+
required: true,
|
|
987
|
+
displayOptions: { show: { resource: ['recordList'], operation: ['create'] } },
|
|
988
|
+
typeOptions: {
|
|
989
|
+
loadOptionsDependsOn: ['recordListId'],
|
|
990
|
+
resourceMapper: {
|
|
991
|
+
resourceMapperMethod: 'getRecordListColumns',
|
|
992
|
+
mode: 'add',
|
|
993
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
994
|
+
addAllFields: false,
|
|
995
|
+
multiKeyMatch: false,
|
|
996
|
+
},
|
|
997
|
+
},
|
|
998
|
+
},
|
|
999
|
+
{
|
|
1000
|
+
displayName: 'Fields',
|
|
1001
|
+
name: 'recordUpdateFields',
|
|
1002
|
+
type: 'resourceMapper',
|
|
1003
|
+
noDataExpression: true,
|
|
1004
|
+
default: {
|
|
1005
|
+
mappingMode: 'defineBelow',
|
|
1006
|
+
value: null,
|
|
1007
|
+
},
|
|
1008
|
+
required: true,
|
|
1009
|
+
displayOptions: { show: { resource: ['recordList'], operation: ['update'] } },
|
|
1010
|
+
typeOptions: {
|
|
1011
|
+
loadOptionsDependsOn: ['recordListId'],
|
|
1012
|
+
resourceMapper: {
|
|
1013
|
+
resourceMapperMethod: 'getRecordListColumns',
|
|
1014
|
+
mode: 'update',
|
|
1015
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
1016
|
+
addAllFields: false,
|
|
1017
|
+
multiKeyMatch: false,
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
},
|
|
1021
|
+
// Record List Get Many: Limit
|
|
1022
|
+
{
|
|
1023
|
+
displayName: 'Limit',
|
|
1024
|
+
name: 'limit',
|
|
1025
|
+
type: 'number',
|
|
1026
|
+
default: 50,
|
|
1027
|
+
description: 'Max number of results to return',
|
|
1028
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
1029
|
+
displayOptions: { show: { resource: ['recordList'], operation: ['getAll'] } },
|
|
1030
|
+
},
|
|
1031
|
+
{
|
|
1032
|
+
displayName: 'Filters',
|
|
1033
|
+
name: 'recordListFilters',
|
|
1034
|
+
type: 'collection',
|
|
1035
|
+
placeholder: 'Add Filter',
|
|
1036
|
+
default: {},
|
|
1037
|
+
displayOptions: { show: { resource: ['recordList'], operation: ['getAll'] } },
|
|
1038
|
+
options: [
|
|
1039
|
+
{ displayName: 'Search', name: 'search', type: 'string', default: '', description: 'Search records by name/content' },
|
|
1040
|
+
],
|
|
1041
|
+
},
|
|
1042
|
+
// ========================================
|
|
1043
|
+
// TASK FIELDS
|
|
1044
|
+
// ========================================
|
|
1045
|
+
{
|
|
1046
|
+
displayName: 'Task ID',
|
|
1047
|
+
name: 'taskId',
|
|
1048
|
+
type: 'number',
|
|
1049
|
+
default: 0,
|
|
1050
|
+
required: true,
|
|
1051
|
+
description: 'The ID of the task in Flowlu',
|
|
1052
|
+
displayOptions: { show: { resource: ['task'], operation: ['get', 'update', 'delete'] } },
|
|
1053
|
+
},
|
|
1054
|
+
// Task Create: Main Fields
|
|
1055
|
+
{
|
|
1056
|
+
displayName: 'Task Name',
|
|
1057
|
+
name: 'taskName',
|
|
1058
|
+
type: 'string',
|
|
1059
|
+
default: '',
|
|
1060
|
+
required: true,
|
|
1061
|
+
displayOptions: { show: { resource: ['task'], operation: ['create'] } },
|
|
1062
|
+
},
|
|
1063
|
+
{
|
|
1064
|
+
displayName: 'Assignee',
|
|
1065
|
+
name: 'responsible_id',
|
|
1066
|
+
type: 'options',
|
|
1067
|
+
typeOptions: { loadOptionsMethod: 'getUsers' },
|
|
1068
|
+
required: true,
|
|
1069
|
+
default: '',
|
|
1070
|
+
description: 'The user responsible for completing this task',
|
|
1071
|
+
displayOptions: { show: { resource: ['task'], operation: ['create'] } },
|
|
1072
|
+
},
|
|
1073
|
+
{
|
|
1074
|
+
displayName: 'Description',
|
|
1075
|
+
name: 'description',
|
|
1076
|
+
type: 'string',
|
|
1077
|
+
typeOptions: { rows: 4 },
|
|
1078
|
+
default: '',
|
|
1079
|
+
displayOptions: { show: { resource: ['task'], operation: ['create'] } },
|
|
1080
|
+
},
|
|
1081
|
+
// Task Create: Additional Fields
|
|
1082
|
+
{
|
|
1083
|
+
displayName: 'Additional Fields',
|
|
1084
|
+
name: 'taskAdditionalFields',
|
|
1085
|
+
type: 'collection',
|
|
1086
|
+
placeholder: 'Add Field',
|
|
1087
|
+
default: {},
|
|
1088
|
+
displayOptions: { show: { resource: ['task'], operation: ['create'] } },
|
|
1089
|
+
options: [
|
|
1090
|
+
{ displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, description: 'The user who owns/created this task', default: '' },
|
|
1091
|
+
{ displayName: 'Contact', name: 'contact_id', type: 'options', typeOptions: { loadOptionsMethod: 'getContacts' }, default: '', description: 'The contact this task is related to' },
|
|
1092
|
+
{ displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, description: 'The project this task belongs to', default: '' },
|
|
1093
|
+
{ displayName: 'Workflow', name: 'workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '', description: 'The task workflow to use (defaults to the first workflow if not set)' },
|
|
1094
|
+
{ displayName: 'Workflow Stage', name: 'workflow_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' }, default: '', description: 'The starting stage within the workflow' },
|
|
1095
|
+
{
|
|
1096
|
+
displayName: 'Priority',
|
|
1097
|
+
name: 'priority',
|
|
1098
|
+
type: 'options',
|
|
1099
|
+
options: [
|
|
1100
|
+
{ name: 'Low', value: 1 },
|
|
1101
|
+
{ name: 'Medium', value: 2 },
|
|
1102
|
+
{ name: 'High', value: 3 },
|
|
1103
|
+
],
|
|
1104
|
+
default: 2,
|
|
1105
|
+
},
|
|
1106
|
+
{
|
|
1107
|
+
displayName: 'Status',
|
|
1108
|
+
name: 'status',
|
|
1109
|
+
type: 'options',
|
|
1110
|
+
options: [
|
|
1111
|
+
{ name: 'New', value: 1 },
|
|
1112
|
+
{ name: 'In Progress', value: 3 },
|
|
1113
|
+
{ name: 'Pending Approval', value: 4 },
|
|
1114
|
+
{ name: 'Completed', value: 5 },
|
|
1115
|
+
],
|
|
1116
|
+
default: 1,
|
|
1117
|
+
},
|
|
1118
|
+
{ displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
|
|
1119
|
+
{ displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
|
|
1120
|
+
{ displayName: 'Time Estimate (Minutes)', name: 'time_estimate', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1121
|
+
{ displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1122
|
+
{ displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1123
|
+
{ displayName: 'Reviewed by Owner', name: 'task_checkbyowner', type: 'boolean', default: false },
|
|
1124
|
+
{ displayName: 'Allow End Date Change', name: 'deadline_allowchange', type: 'boolean', default: true },
|
|
1125
|
+
{ displayName: 'Parent Task ID', name: 'parent_id', type: 'number', default: 0, description: 'ID of the parent task (for subtasks)' },
|
|
1126
|
+
{
|
|
1127
|
+
displayName: 'Include Link to Workflow',
|
|
1128
|
+
name: 'includeLinkToWorkflow',
|
|
1129
|
+
type: 'boolean',
|
|
1130
|
+
default: false,
|
|
1131
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow',
|
|
1132
|
+
},
|
|
1133
|
+
],
|
|
1134
|
+
},
|
|
1135
|
+
{
|
|
1136
|
+
displayName: 'Custom Fields',
|
|
1137
|
+
name: 'taskCustomFields',
|
|
1138
|
+
type: 'resourceMapper',
|
|
1139
|
+
noDataExpression: true,
|
|
1140
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
1141
|
+
displayOptions: { show: { resource: ['task'], operation: ['create'] } },
|
|
1142
|
+
typeOptions: {
|
|
1143
|
+
resourceMapper: {
|
|
1144
|
+
resourceMapperMethod: 'getTaskColumns',
|
|
1145
|
+
mode: 'add',
|
|
1146
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
1147
|
+
addAllFields: false,
|
|
1148
|
+
multiKeyMatch: false,
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
// Task Update: Fields
|
|
1153
|
+
{
|
|
1154
|
+
displayName: 'Update Fields',
|
|
1155
|
+
name: 'taskUpdateFields',
|
|
1156
|
+
type: 'collection',
|
|
1157
|
+
placeholder: 'Add Field',
|
|
1158
|
+
default: {},
|
|
1159
|
+
displayOptions: { show: { resource: ['task'], operation: ['update'] } },
|
|
1160
|
+
options: [
|
|
1161
|
+
{ displayName: 'Task Name', name: 'name', type: 'string', default: '' },
|
|
1162
|
+
{ displayName: 'Description', name: 'description', type: 'string', typeOptions: { rows: 4 }, default: '' },
|
|
1163
|
+
{ displayName: 'Assignee', name: 'responsible_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
1164
|
+
{ displayName: 'Owner', name: 'owner_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
1165
|
+
{ displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, default: '' },
|
|
1166
|
+
{ displayName: 'Workflow', name: 'workflow_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflows' }, default: '' },
|
|
1167
|
+
{ displayName: 'Workflow Stage', name: 'workflow_stage_id', type: 'options', typeOptions: { loadOptionsMethod: 'getTaskWorkflowStages' }, default: '' },
|
|
1168
|
+
{
|
|
1169
|
+
displayName: 'Priority',
|
|
1170
|
+
name: 'priority',
|
|
1171
|
+
type: 'options',
|
|
1172
|
+
options: [
|
|
1173
|
+
{ name: 'Low', value: 1 },
|
|
1174
|
+
{ name: 'Medium', value: 2 },
|
|
1175
|
+
{ name: 'High', value: 3 },
|
|
1176
|
+
],
|
|
1177
|
+
default: 2,
|
|
1178
|
+
},
|
|
1179
|
+
{
|
|
1180
|
+
displayName: 'Status',
|
|
1181
|
+
name: 'status',
|
|
1182
|
+
type: 'options',
|
|
1183
|
+
options: [
|
|
1184
|
+
{ name: 'New', value: 1 },
|
|
1185
|
+
{ name: 'In Progress', value: 3 },
|
|
1186
|
+
{ name: 'Pending Approval', value: 4 },
|
|
1187
|
+
{ name: 'Completed', value: 5 },
|
|
1188
|
+
],
|
|
1189
|
+
default: 1,
|
|
1190
|
+
},
|
|
1191
|
+
{ displayName: 'Start Date', name: 'plan_start_date', type: 'dateTime', default: '' },
|
|
1192
|
+
{ displayName: 'End Date', name: 'deadline', type: 'dateTime', default: '' },
|
|
1193
|
+
{ displayName: 'Time Estimate (Minutes)', name: 'time_estimate', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1194
|
+
{ displayName: 'Planned Cost', name: 'cost', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1195
|
+
{ displayName: 'Planned Income', name: 'price', type: 'number', default: 0, typeOptions: { minValue: 0 } },
|
|
1196
|
+
{ displayName: 'Parent Task ID', name: 'parent_id', type: 'number', default: 0 },
|
|
1197
|
+
{
|
|
1198
|
+
displayName: 'Include Link to Workflow',
|
|
1199
|
+
name: 'includeLinkToWorkflow',
|
|
1200
|
+
type: 'boolean',
|
|
1201
|
+
default: false,
|
|
1202
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the task description, linking back to this workflow. Requires Description to be set.',
|
|
1203
|
+
},
|
|
1204
|
+
],
|
|
1205
|
+
},
|
|
1206
|
+
{
|
|
1207
|
+
displayName: 'Custom Fields',
|
|
1208
|
+
name: 'taskUpdateCustomFields',
|
|
1209
|
+
type: 'resourceMapper',
|
|
1210
|
+
noDataExpression: true,
|
|
1211
|
+
default: { mappingMode: 'defineBelow', value: null },
|
|
1212
|
+
displayOptions: { show: { resource: ['task'], operation: ['update'] } },
|
|
1213
|
+
typeOptions: {
|
|
1214
|
+
resourceMapper: {
|
|
1215
|
+
resourceMapperMethod: 'getTaskColumns',
|
|
1216
|
+
mode: 'update',
|
|
1217
|
+
fieldWords: { singular: 'field', plural: 'fields' },
|
|
1218
|
+
addAllFields: false,
|
|
1219
|
+
multiKeyMatch: false,
|
|
1220
|
+
},
|
|
1221
|
+
},
|
|
1222
|
+
},
|
|
1223
|
+
// Task Get Many: Filters
|
|
1224
|
+
{
|
|
1225
|
+
displayName: 'Limit',
|
|
1226
|
+
name: 'limit',
|
|
1227
|
+
type: 'number',
|
|
1228
|
+
default: 50,
|
|
1229
|
+
description: 'Max number of results to return',
|
|
1230
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
1231
|
+
displayOptions: { show: { resource: ['task'], operation: ['getAll'] } },
|
|
1232
|
+
},
|
|
1233
|
+
{
|
|
1234
|
+
displayName: 'Filters',
|
|
1235
|
+
name: 'taskFilters',
|
|
1236
|
+
type: 'collection',
|
|
1237
|
+
placeholder: 'Add Filter',
|
|
1238
|
+
default: {},
|
|
1239
|
+
displayOptions: { show: { resource: ['task'], operation: ['getAll'] } },
|
|
1240
|
+
options: [
|
|
1241
|
+
{ displayName: 'Name', name: 'name', type: 'string', default: '', description: 'Filter by task name' },
|
|
1242
|
+
{ displayName: 'Assignee', name: 'responsible_id', type: 'options', typeOptions: { loadOptionsMethod: 'getUsers' }, default: '' },
|
|
1243
|
+
{ displayName: 'Project', name: 'model_id', type: 'options', typeOptions: { loadOptionsMethod: 'getProjects' }, default: '' },
|
|
1244
|
+
{
|
|
1245
|
+
displayName: 'Priority',
|
|
1246
|
+
name: 'priority',
|
|
1247
|
+
type: 'options',
|
|
1248
|
+
options: [
|
|
1249
|
+
{ name: 'Low', value: 1 },
|
|
1250
|
+
{ name: 'Medium', value: 2 },
|
|
1251
|
+
{ name: 'High', value: 3 },
|
|
1252
|
+
],
|
|
1253
|
+
default: 2,
|
|
1254
|
+
},
|
|
1255
|
+
{
|
|
1256
|
+
displayName: 'Status',
|
|
1257
|
+
name: 'status',
|
|
1258
|
+
type: 'options',
|
|
1259
|
+
options: [
|
|
1260
|
+
{ name: 'New', value: 1 },
|
|
1261
|
+
{ name: 'In Progress', value: 3 },
|
|
1262
|
+
{ name: 'Pending Approval', value: 4 },
|
|
1263
|
+
{ name: 'Completed', value: 5 },
|
|
1264
|
+
],
|
|
1265
|
+
default: 1,
|
|
1266
|
+
},
|
|
1267
|
+
],
|
|
1268
|
+
},
|
|
1269
|
+
// ========================================
|
|
1270
|
+
// TAG FIELDS
|
|
1271
|
+
// ========================================
|
|
1272
|
+
{
|
|
1273
|
+
displayName: 'Entity Type',
|
|
1274
|
+
name: 'tagEntityType',
|
|
1275
|
+
type: 'options',
|
|
1276
|
+
noDataExpression: true,
|
|
1277
|
+
displayOptions: { show: { resource: ['tag'], operation: ['add', 'getAll', 'remove'] } },
|
|
1278
|
+
options: [
|
|
1279
|
+
{ name: 'Contact', value: 'crm/account' },
|
|
1280
|
+
{ name: 'Opportunity', value: 'crm/lead' },
|
|
1281
|
+
{ name: 'Task', value: 'task/tasks' },
|
|
1282
|
+
{ name: 'Project', value: 'st/projects' },
|
|
1283
|
+
],
|
|
1284
|
+
default: 'crm/account',
|
|
1285
|
+
description: 'The type of entity to manage tags on',
|
|
1286
|
+
},
|
|
1287
|
+
{
|
|
1288
|
+
displayName: 'Entity ID',
|
|
1289
|
+
name: 'tagEntityId',
|
|
1290
|
+
type: 'number',
|
|
1291
|
+
default: 0,
|
|
1292
|
+
required: true,
|
|
1293
|
+
displayOptions: { show: { resource: ['tag'], operation: ['add', 'getAll', 'remove'] } },
|
|
1294
|
+
description: 'The ID of the entity (Contact, Opportunity, Task, or Project)',
|
|
1295
|
+
},
|
|
1296
|
+
{
|
|
1297
|
+
displayName: 'Tag Name',
|
|
1298
|
+
name: 'tagName',
|
|
1299
|
+
type: 'string',
|
|
1300
|
+
default: '',
|
|
1301
|
+
required: true,
|
|
1302
|
+
displayOptions: { show: { resource: ['tag'], operation: ['add'] } },
|
|
1303
|
+
description: 'The name of the tag to add. If the tag does not exist, it will be created automatically.',
|
|
1304
|
+
},
|
|
1305
|
+
{
|
|
1306
|
+
displayName: 'Tag ID',
|
|
1307
|
+
name: 'tagIdToRemove',
|
|
1308
|
+
type: 'number',
|
|
1309
|
+
default: 0,
|
|
1310
|
+
required: true,
|
|
1311
|
+
displayOptions: { show: { resource: ['tag'], operation: ['remove'] } },
|
|
1312
|
+
description: 'The ID of the tag to remove. Use "Get Many" or "List All" to find tag IDs.',
|
|
1313
|
+
},
|
|
1314
|
+
// ========================================
|
|
1315
|
+
// COMMENT FIELDS
|
|
1316
|
+
// ========================================
|
|
1317
|
+
{
|
|
1318
|
+
displayName: 'Entity Type',
|
|
1319
|
+
name: 'commentEntityType',
|
|
1320
|
+
type: 'options',
|
|
1321
|
+
noDataExpression: true,
|
|
1322
|
+
displayOptions: { show: { resource: ['comment'] } },
|
|
1323
|
+
options: [
|
|
1324
|
+
{ name: 'Contact', value: 'crm/account' },
|
|
1325
|
+
{ name: 'Opportunity', value: 'crm/lead' },
|
|
1326
|
+
{ name: 'Task', value: 'task/tasks' },
|
|
1327
|
+
{ name: 'Project', value: 'st/projects' },
|
|
1328
|
+
],
|
|
1329
|
+
default: 'crm/account',
|
|
1330
|
+
description: 'The type of entity to comment on',
|
|
1331
|
+
},
|
|
1332
|
+
{
|
|
1333
|
+
displayName: 'Entity ID',
|
|
1334
|
+
name: 'commentEntityId',
|
|
1335
|
+
type: 'number',
|
|
1336
|
+
default: 0,
|
|
1337
|
+
required: true,
|
|
1338
|
+
displayOptions: { show: { resource: ['comment'] } },
|
|
1339
|
+
description: 'The ID of the entity (Contact, Opportunity, Task, or Project)',
|
|
1340
|
+
},
|
|
1341
|
+
{
|
|
1342
|
+
displayName: 'Comment Text',
|
|
1343
|
+
name: 'commentText',
|
|
1344
|
+
type: 'string',
|
|
1345
|
+
typeOptions: { rows: 6 },
|
|
1346
|
+
default: '',
|
|
1347
|
+
required: true,
|
|
1348
|
+
displayOptions: { show: { resource: ['comment'], operation: ['create'] } },
|
|
1349
|
+
description: 'The comment text (HTML supported)',
|
|
1350
|
+
},
|
|
1351
|
+
{
|
|
1352
|
+
displayName: 'Include Link to Workflow',
|
|
1353
|
+
name: 'commentIncludeLinkToWorkflow',
|
|
1354
|
+
type: 'boolean',
|
|
1355
|
+
default: false,
|
|
1356
|
+
displayOptions: { show: { resource: ['comment'], operation: ['create'] } },
|
|
1357
|
+
description: 'Whether to append a "Generated via n8n: View Workflow" footer to the comment text, linking back to this workflow',
|
|
1358
|
+
},
|
|
1359
|
+
{
|
|
1360
|
+
displayName: 'Limit',
|
|
1361
|
+
name: 'limit',
|
|
1362
|
+
type: 'number',
|
|
1363
|
+
default: 50,
|
|
1364
|
+
description: 'Max number of results to return',
|
|
1365
|
+
typeOptions: { minValue: 1, maxValue: 500 },
|
|
1366
|
+
displayOptions: { show: { resource: ['comment'], operation: ['getAll'] } },
|
|
1367
|
+
},
|
|
1368
|
+
],
|
|
1369
|
+
};
|
|
1370
|
+
this.methods = {
|
|
1371
|
+
loadOptions: {
|
|
1372
|
+
async getUsers() {
|
|
1373
|
+
try {
|
|
1374
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1375
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/core/user/list', apiKey, undefined, { 'filter[role_login]': '1' });
|
|
1376
|
+
if (response?.response?.items) {
|
|
1377
|
+
return response.response.items.map((user) => ({ name: user.name || user.email || `User ${user.id}`, value: user.id.toString() }));
|
|
1378
|
+
}
|
|
1379
|
+
return [];
|
|
1380
|
+
}
|
|
1381
|
+
catch (error) {
|
|
1382
|
+
return [];
|
|
1383
|
+
}
|
|
1384
|
+
},
|
|
1385
|
+
async getAccounts() {
|
|
1386
|
+
try {
|
|
1387
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1388
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, { 'filter[type]': '1' });
|
|
1389
|
+
if (response?.response?.items) {
|
|
1390
|
+
return response.response.items.map((a) => ({ name: a.name || `Account ${a.id}`, value: a.id.toString() }));
|
|
1391
|
+
}
|
|
1392
|
+
return [];
|
|
1393
|
+
}
|
|
1394
|
+
catch (error) {
|
|
1395
|
+
return [];
|
|
1396
|
+
}
|
|
1397
|
+
},
|
|
1398
|
+
async getContacts() {
|
|
1399
|
+
try {
|
|
1400
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1401
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, { 'filter[type]': '2' });
|
|
1402
|
+
if (response?.response?.items) {
|
|
1403
|
+
return response.response.items.map((c) => ({ name: c.name || `Contact ${c.id}`, value: c.id.toString() }));
|
|
1404
|
+
}
|
|
1405
|
+
return [];
|
|
1406
|
+
}
|
|
1407
|
+
catch (error) {
|
|
1408
|
+
return [];
|
|
1409
|
+
}
|
|
1410
|
+
},
|
|
1411
|
+
async getProjects() {
|
|
1412
|
+
try {
|
|
1413
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1414
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/projects/list', apiKey, undefined, { 'filter[is_archive]': '0' });
|
|
1415
|
+
if (response?.response?.items) {
|
|
1416
|
+
return response.response.items.map((p) => ({ name: p.name || `Project ${p.id}`, value: p.id.toString() }));
|
|
1417
|
+
}
|
|
1418
|
+
return [];
|
|
1419
|
+
}
|
|
1420
|
+
catch (error) {
|
|
1421
|
+
return [];
|
|
1422
|
+
}
|
|
1423
|
+
},
|
|
1424
|
+
async getTaskWorkflows() {
|
|
1425
|
+
try {
|
|
1426
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1427
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/workflows/list', apiKey);
|
|
1428
|
+
if (response?.response?.items) {
|
|
1429
|
+
return response.response.items.map((wf) => ({ name: wf.name || `Workflow ${wf.id}`, value: wf.id.toString() }));
|
|
1430
|
+
}
|
|
1431
|
+
return [];
|
|
1432
|
+
}
|
|
1433
|
+
catch (error) {
|
|
1434
|
+
return [];
|
|
1435
|
+
}
|
|
1436
|
+
},
|
|
1437
|
+
async getTaskWorkflowStages() {
|
|
1438
|
+
try {
|
|
1439
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1440
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/stages/list', apiKey);
|
|
1441
|
+
if (response?.response?.items) {
|
|
1442
|
+
return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString(), description: `Workflow ID: ${s.workflow_id}` }));
|
|
1443
|
+
}
|
|
1444
|
+
return [];
|
|
1445
|
+
}
|
|
1446
|
+
catch (error) {
|
|
1447
|
+
return [];
|
|
1448
|
+
}
|
|
1449
|
+
},
|
|
1450
|
+
async getPipelines() {
|
|
1451
|
+
try {
|
|
1452
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1453
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline/list', apiKey);
|
|
1454
|
+
if (response?.response?.items) {
|
|
1455
|
+
return response.response.items.map((p) => ({ name: p.name || `Pipeline ${p.id}`, value: p.id.toString() }));
|
|
1456
|
+
}
|
|
1457
|
+
return [];
|
|
1458
|
+
}
|
|
1459
|
+
catch (error) {
|
|
1460
|
+
return [];
|
|
1461
|
+
}
|
|
1462
|
+
},
|
|
1463
|
+
async getPipelineStages() {
|
|
1464
|
+
try {
|
|
1465
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1466
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline_stage/list', apiKey);
|
|
1467
|
+
if (response?.response?.items) {
|
|
1468
|
+
return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString(), description: `Pipeline ID: ${s.pipeline_id}` }));
|
|
1469
|
+
}
|
|
1470
|
+
return [];
|
|
1471
|
+
}
|
|
1472
|
+
catch (error) {
|
|
1473
|
+
return [];
|
|
1474
|
+
}
|
|
1475
|
+
},
|
|
1476
|
+
async getFilteredPipelineStages() {
|
|
1477
|
+
try {
|
|
1478
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1479
|
+
let pipelineId;
|
|
1480
|
+
try {
|
|
1481
|
+
pipelineId = this.getNodeParameter('opportunityFilterPipeline');
|
|
1482
|
+
}
|
|
1483
|
+
catch { /* not set yet */ }
|
|
1484
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/pipeline_stage/list', apiKey);
|
|
1485
|
+
if (response?.response?.items) {
|
|
1486
|
+
let stages = response.response.items;
|
|
1487
|
+
if (pipelineId) {
|
|
1488
|
+
stages = stages.filter((s) => s.pipeline_id?.toString() === pipelineId);
|
|
1489
|
+
}
|
|
1490
|
+
return stages.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString() }));
|
|
1491
|
+
}
|
|
1492
|
+
return [];
|
|
1493
|
+
}
|
|
1494
|
+
catch (error) {
|
|
1495
|
+
return [];
|
|
1496
|
+
}
|
|
1497
|
+
},
|
|
1498
|
+
async getAllCrmAccounts() {
|
|
1499
|
+
try {
|
|
1500
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1501
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey);
|
|
1502
|
+
if (response?.response?.items) {
|
|
1503
|
+
return response.response.items.map((a) => {
|
|
1504
|
+
const typeLabel = a.type === 1 ? '(Company)' : a.type === 2 ? '(Contact)' : '';
|
|
1505
|
+
return { name: `${a.name || `#${a.id}`} ${typeLabel}`.trim(), value: a.id.toString() };
|
|
1506
|
+
});
|
|
1507
|
+
}
|
|
1508
|
+
return [];
|
|
1509
|
+
}
|
|
1510
|
+
catch (error) {
|
|
1511
|
+
return [];
|
|
1512
|
+
}
|
|
1513
|
+
},
|
|
1514
|
+
async getOpportunitySources() {
|
|
1515
|
+
try {
|
|
1516
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1517
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/source/list', apiKey);
|
|
1518
|
+
if (response?.response?.items) {
|
|
1519
|
+
return response.response.items.map((s) => ({ name: s.name || `Source ${s.id}`, value: s.id.toString() }));
|
|
1520
|
+
}
|
|
1521
|
+
return [];
|
|
1522
|
+
}
|
|
1523
|
+
catch (error) {
|
|
1524
|
+
return [];
|
|
1525
|
+
}
|
|
1526
|
+
},
|
|
1527
|
+
async getPortfolios() {
|
|
1528
|
+
try {
|
|
1529
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1530
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/portfolio/list', apiKey);
|
|
1531
|
+
if (response?.response?.items) {
|
|
1532
|
+
return response.response.items.map((p) => ({ name: p.name || `Portfolio ${p.id}`, value: p.id.toString() }));
|
|
1533
|
+
}
|
|
1534
|
+
return [];
|
|
1535
|
+
}
|
|
1536
|
+
catch (error) {
|
|
1537
|
+
return [];
|
|
1538
|
+
}
|
|
1539
|
+
},
|
|
1540
|
+
async getProjectStages() {
|
|
1541
|
+
try {
|
|
1542
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1543
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/stages/list', apiKey);
|
|
1544
|
+
if (response?.response?.items) {
|
|
1545
|
+
return response.response.items.map((s) => ({ name: s.name || `Stage ${s.id}`, value: s.id.toString() }));
|
|
1546
|
+
}
|
|
1547
|
+
return [];
|
|
1548
|
+
}
|
|
1549
|
+
catch (error) {
|
|
1550
|
+
return [];
|
|
1551
|
+
}
|
|
1552
|
+
},
|
|
1553
|
+
async getRecordLists() {
|
|
1554
|
+
try {
|
|
1555
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1556
|
+
const response = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customlists/lists/list', apiKey);
|
|
1557
|
+
if (response?.response?.items) {
|
|
1558
|
+
return response.response.items.map((l) => ({ name: l.name || `List ${l.id}`, value: l.id.toString() }));
|
|
1559
|
+
}
|
|
1560
|
+
return [];
|
|
1561
|
+
}
|
|
1562
|
+
catch (error) {
|
|
1563
|
+
return [];
|
|
1564
|
+
}
|
|
1565
|
+
},
|
|
1566
|
+
async getRecordListFields() {
|
|
1567
|
+
try {
|
|
1568
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1569
|
+
// Get the selected list ID from the current node parameters
|
|
1570
|
+
let listId;
|
|
1571
|
+
try {
|
|
1572
|
+
listId = this.getNodeParameter('recordListId');
|
|
1573
|
+
}
|
|
1574
|
+
catch {
|
|
1575
|
+
return [];
|
|
1576
|
+
}
|
|
1577
|
+
if (!listId)
|
|
1578
|
+
return [];
|
|
1579
|
+
// Find the fieldset for this list (customlists/items with group_id matching list_id)
|
|
1580
|
+
const fieldsetsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
|
|
1581
|
+
const fieldsetIds = [];
|
|
1582
|
+
if (fieldsetsResponse?.response?.items) {
|
|
1583
|
+
for (const fs of fieldsetsResponse.response.items) {
|
|
1584
|
+
if (fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === listId) {
|
|
1585
|
+
fieldsetIds.push(fs.id.toString());
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1588
|
+
}
|
|
1589
|
+
if (fieldsetIds.length === 0)
|
|
1590
|
+
return [];
|
|
1591
|
+
// Get fields for those fieldsets
|
|
1592
|
+
const fieldsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
|
|
1593
|
+
if (fieldsResponse?.response?.items) {
|
|
1594
|
+
// Also fetch all fieldsets so we can resolve custom list link names
|
|
1595
|
+
const allFieldsets = fieldsetsResponse?.response?.items || [];
|
|
1596
|
+
return fieldsResponse.response.items
|
|
1597
|
+
.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()))
|
|
1598
|
+
.filter((f) => f.active !== 0)
|
|
1599
|
+
.map((f) => {
|
|
1600
|
+
const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
|
|
1601
|
+
let label = f.name || cfKey;
|
|
1602
|
+
let desc = '';
|
|
1603
|
+
// Detect link fields (model.any) and show what they link to
|
|
1604
|
+
if (f.type === 'model.any' && f.module && f.model) {
|
|
1605
|
+
const linkTargetMap = {
|
|
1606
|
+
'st/project': 'Project',
|
|
1607
|
+
'crm/company': 'Company',
|
|
1608
|
+
'crm/contact': 'Contact',
|
|
1609
|
+
'crm/client': 'Account',
|
|
1610
|
+
'crm/leads': 'Opportunity',
|
|
1611
|
+
'system/user': 'User',
|
|
1612
|
+
};
|
|
1613
|
+
const key = `${f.module}/${f.model}`;
|
|
1614
|
+
if (linkTargetMap[key]) {
|
|
1615
|
+
label = `${f.name} → ${linkTargetMap[key]}`;
|
|
1616
|
+
desc = `Enter the ${linkTargetMap[key]} ID`;
|
|
1617
|
+
}
|
|
1618
|
+
else if (f.module === 'customlists' && f.model === 'items' && f.group_id) {
|
|
1619
|
+
// Link to another custom list - find its name
|
|
1620
|
+
const targetList = allFieldsets.find((fs) => fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === f.group_id.toString());
|
|
1621
|
+
const listName = targetList?.name || `List #${f.group_id}`;
|
|
1622
|
+
label = `${f.name} → ${listName}`;
|
|
1623
|
+
desc = `Enter the Record ID from "${listName}"`;
|
|
1624
|
+
}
|
|
1625
|
+
else {
|
|
1626
|
+
label = `${f.name} → ${f.module}/${f.model}`;
|
|
1627
|
+
desc = `Enter the linked record ID`;
|
|
1628
|
+
}
|
|
1629
|
+
}
|
|
1630
|
+
const option = { name: label, value: cfKey };
|
|
1631
|
+
if (desc)
|
|
1632
|
+
option.description = desc;
|
|
1633
|
+
return option;
|
|
1634
|
+
});
|
|
1635
|
+
}
|
|
1636
|
+
return [];
|
|
1637
|
+
}
|
|
1638
|
+
catch (error) {
|
|
1639
|
+
return [];
|
|
1640
|
+
}
|
|
1641
|
+
},
|
|
1642
|
+
async getTaskCustomFields() {
|
|
1643
|
+
return loadCustomFieldsForEntity(this, 'task', 'task');
|
|
1644
|
+
},
|
|
1645
|
+
async getContactCustomFields() {
|
|
1646
|
+
return loadCustomFieldsForEntity(this, 'crm', 'contact');
|
|
1647
|
+
},
|
|
1648
|
+
async getProjectCustomFields() {
|
|
1649
|
+
return loadCustomFieldsForEntity(this, 'st', 'project');
|
|
1650
|
+
},
|
|
1651
|
+
async getOpportunityCustomFields() {
|
|
1652
|
+
return loadCustomFieldsForEntity(this, 'crm', 'leads');
|
|
1653
|
+
},
|
|
1654
|
+
},
|
|
1655
|
+
resourceMapping: {
|
|
1656
|
+
async getContactColumns() {
|
|
1657
|
+
return loadEntityCustomFieldColumns.call(this, 'crm', 'contact');
|
|
1658
|
+
},
|
|
1659
|
+
async getOpportunityColumns() {
|
|
1660
|
+
return loadEntityCustomFieldColumns.call(this, 'crm', 'leads');
|
|
1661
|
+
},
|
|
1662
|
+
async getProjectColumns() {
|
|
1663
|
+
return loadEntityCustomFieldColumns.call(this, 'st', 'project');
|
|
1664
|
+
},
|
|
1665
|
+
async getTaskColumns() {
|
|
1666
|
+
return loadEntityCustomFieldColumns.call(this, 'task', 'task');
|
|
1667
|
+
},
|
|
1668
|
+
async getRecordListColumns() {
|
|
1669
|
+
try {
|
|
1670
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1671
|
+
let listId;
|
|
1672
|
+
try {
|
|
1673
|
+
listId = this.getNodeParameter('recordListId');
|
|
1674
|
+
}
|
|
1675
|
+
catch {
|
|
1676
|
+
return { fields: [] };
|
|
1677
|
+
}
|
|
1678
|
+
if (!listId)
|
|
1679
|
+
return { fields: [] };
|
|
1680
|
+
// Get fieldsets to find this list's fieldset
|
|
1681
|
+
const fieldsetsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fieldsets/list', apiKey);
|
|
1682
|
+
const allFieldsets = fieldsetsResponse?.response?.items || [];
|
|
1683
|
+
const fieldsetIds = [];
|
|
1684
|
+
for (const fs of allFieldsets) {
|
|
1685
|
+
if (fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === listId) {
|
|
1686
|
+
fieldsetIds.push(fs.id.toString());
|
|
1687
|
+
}
|
|
1688
|
+
}
|
|
1689
|
+
if (fieldsetIds.length === 0)
|
|
1690
|
+
return { fields: [] };
|
|
1691
|
+
// Get all custom fields
|
|
1692
|
+
const fieldsResponse = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customfields/fields/list', apiKey);
|
|
1693
|
+
if (!fieldsResponse?.response?.items)
|
|
1694
|
+
return { fields: [] };
|
|
1695
|
+
// Map of linked entity endpoints for fetching dropdown options
|
|
1696
|
+
const linkEndpoints = {
|
|
1697
|
+
'st/project': '/api/v1/module/st/projects/list',
|
|
1698
|
+
'crm/company': '/api/v1/module/crm/account/list',
|
|
1699
|
+
'crm/contact': '/api/v1/module/crm/account/list',
|
|
1700
|
+
'crm/client': '/api/v1/module/crm/account/list',
|
|
1701
|
+
'crm/leads': '/api/v1/module/crm/lead/list',
|
|
1702
|
+
'system/user': '/api/v1/module/core/user/list',
|
|
1703
|
+
};
|
|
1704
|
+
const linkLabels = {
|
|
1705
|
+
'st/project': 'Project',
|
|
1706
|
+
'crm/company': 'Company',
|
|
1707
|
+
'crm/contact': 'Contact',
|
|
1708
|
+
'crm/client': 'Account',
|
|
1709
|
+
'crm/leads': 'Opportunity',
|
|
1710
|
+
'system/user': 'User',
|
|
1711
|
+
};
|
|
1712
|
+
// Pre-fetch linked record options for all link field types we encounter
|
|
1713
|
+
const linkOptionsCache = {};
|
|
1714
|
+
const filteredFields = fieldsResponse.response.items
|
|
1715
|
+
.filter((f) => fieldsetIds.includes(f.fieldset_id?.toString()))
|
|
1716
|
+
.filter((f) => f.active !== 0);
|
|
1717
|
+
for (const f of filteredFields) {
|
|
1718
|
+
if (f.type === 'model.any' && f.module && f.model) {
|
|
1719
|
+
const key = `${f.module}/${f.model}`;
|
|
1720
|
+
if (linkEndpoints[key] && !linkOptionsCache[key]) {
|
|
1721
|
+
try {
|
|
1722
|
+
const qs = {};
|
|
1723
|
+
if (key === 'st/project')
|
|
1724
|
+
qs['filter[is_archive]'] = '0';
|
|
1725
|
+
if (key === 'crm/contact')
|
|
1726
|
+
qs['filter[type]'] = '2';
|
|
1727
|
+
if (key === 'crm/company')
|
|
1728
|
+
qs['filter[type]'] = '1';
|
|
1729
|
+
if (key === 'system/user')
|
|
1730
|
+
qs['filter[role_login]'] = '1';
|
|
1731
|
+
const resp = await flowluApiRequest.call(this, 'GET', baseUrl, linkEndpoints[key], apiKey, undefined, qs);
|
|
1732
|
+
linkOptionsCache[key] = (resp?.response?.items || []).map((item) => ({
|
|
1733
|
+
name: item.name || item.email || `#${item.id}`,
|
|
1734
|
+
value: item.id,
|
|
1735
|
+
}));
|
|
1736
|
+
}
|
|
1737
|
+
catch {
|
|
1738
|
+
linkOptionsCache[key] = [];
|
|
1739
|
+
}
|
|
1740
|
+
}
|
|
1741
|
+
// Handle links to other custom lists
|
|
1742
|
+
if (f.module === 'customlists' && f.model === 'items' && f.group_id) {
|
|
1743
|
+
const clKey = `customlists/${f.group_id}`;
|
|
1744
|
+
if (!linkOptionsCache[clKey]) {
|
|
1745
|
+
try {
|
|
1746
|
+
const resp = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customlists/items/list', apiKey, undefined, {
|
|
1747
|
+
'filter[list_id]': f.group_id.toString(),
|
|
1748
|
+
});
|
|
1749
|
+
// Find the label field for this list
|
|
1750
|
+
const labelFieldId = f.group_label_field_id;
|
|
1751
|
+
linkOptionsCache[clKey] = (resp?.response?.items || []).map((item) => {
|
|
1752
|
+
const label = labelFieldId ? (item[`cf_${labelFieldId}`] || `#${item.id}`) : `#${item.id}`;
|
|
1753
|
+
return { name: label, value: item.id };
|
|
1754
|
+
});
|
|
1755
|
+
}
|
|
1756
|
+
catch {
|
|
1757
|
+
linkOptionsCache[clKey] = [];
|
|
1758
|
+
}
|
|
1759
|
+
}
|
|
1760
|
+
}
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
const fields = filteredFields.map((f) => {
|
|
1764
|
+
const cfKey = (f.api_use_alias && f.alias) ? `cf_${f.alias}` : `cf_${f.id}`;
|
|
1765
|
+
let displayName = f.name || cfKey;
|
|
1766
|
+
let fieldType = 'string';
|
|
1767
|
+
let fieldOptions;
|
|
1768
|
+
if (f.type === 'int')
|
|
1769
|
+
fieldType = 'number';
|
|
1770
|
+
else if (f.type === 'boolean')
|
|
1771
|
+
fieldType = 'boolean';
|
|
1772
|
+
else if (f.type === 'date' || f.type === 'datetime')
|
|
1773
|
+
fieldType = 'dateTime';
|
|
1774
|
+
else if (f.type === 'price' || f.type === 'decimal')
|
|
1775
|
+
fieldType = 'number';
|
|
1776
|
+
// Dropdown fields (select.single)
|
|
1777
|
+
if (f.type === 'select.single') {
|
|
1778
|
+
fieldType = 'options';
|
|
1779
|
+
try {
|
|
1780
|
+
const opts = JSON.parse(f.extra_fields || '{}');
|
|
1781
|
+
const selectOptions = JSON.parse(opts.select_options || '[]');
|
|
1782
|
+
fieldOptions = selectOptions
|
|
1783
|
+
.filter((o) => o.value)
|
|
1784
|
+
.map((o) => ({ name: o.value, value: o.id?.toString() || o.value }));
|
|
1785
|
+
}
|
|
1786
|
+
catch { /* ignore parse errors */ }
|
|
1787
|
+
}
|
|
1788
|
+
// Link fields - use dropdown with fetched options
|
|
1789
|
+
if (f.type === 'model.any' && f.module && f.model) {
|
|
1790
|
+
const key = `${f.module}/${f.model}`;
|
|
1791
|
+
if (linkLabels[key]) {
|
|
1792
|
+
displayName = `${f.name} → ${linkLabels[key]}`;
|
|
1793
|
+
}
|
|
1794
|
+
else if (f.module === 'customlists' && f.model === 'items' && f.group_id) {
|
|
1795
|
+
const targetList = allFieldsets.find((fs) => fs.module === 'customlists' && fs.model === 'items' && fs.group_id?.toString() === f.group_id.toString());
|
|
1796
|
+
displayName = `${f.name} → ${targetList?.name || `List #${f.group_id}`}`;
|
|
1797
|
+
}
|
|
1798
|
+
const cacheKey = (f.module === 'customlists' && f.model === 'items' && f.group_id)
|
|
1799
|
+
? `customlists/${f.group_id}`
|
|
1800
|
+
: key;
|
|
1801
|
+
if (linkOptionsCache[cacheKey]?.length) {
|
|
1802
|
+
fieldType = 'options';
|
|
1803
|
+
fieldOptions = linkOptionsCache[cacheKey];
|
|
1804
|
+
}
|
|
1805
|
+
else {
|
|
1806
|
+
fieldType = 'number';
|
|
1807
|
+
}
|
|
1808
|
+
}
|
|
1809
|
+
const field = {
|
|
1810
|
+
id: cfKey,
|
|
1811
|
+
displayName,
|
|
1812
|
+
required: f.required === 1,
|
|
1813
|
+
defaultMatch: false,
|
|
1814
|
+
display: true,
|
|
1815
|
+
type: fieldType,
|
|
1816
|
+
canBeUsedToMatch: false,
|
|
1817
|
+
};
|
|
1818
|
+
if (fieldOptions)
|
|
1819
|
+
field.options = fieldOptions;
|
|
1820
|
+
return field;
|
|
1821
|
+
});
|
|
1822
|
+
return { fields };
|
|
1823
|
+
}
|
|
1824
|
+
catch (error) {
|
|
1825
|
+
return { fields: [] };
|
|
1826
|
+
}
|
|
1827
|
+
},
|
|
1828
|
+
},
|
|
1829
|
+
};
|
|
1830
|
+
}
|
|
1831
|
+
async execute() {
|
|
1832
|
+
const items = this.getInputData();
|
|
1833
|
+
const returnData = [];
|
|
1834
|
+
const resource = this.getNodeParameter('resource', 0);
|
|
1835
|
+
const operation = this.getNodeParameter('operation', 0);
|
|
1836
|
+
for (let i = 0; i < items.length; i++) {
|
|
1837
|
+
try {
|
|
1838
|
+
const { baseUrl, apiKey } = await getFlowluCredentials(this);
|
|
1839
|
+
let responseData;
|
|
1840
|
+
// ============================
|
|
1841
|
+
// CONTACT
|
|
1842
|
+
// ============================
|
|
1843
|
+
if (resource === 'contact') {
|
|
1844
|
+
if (operation === 'create') {
|
|
1845
|
+
const body = {
|
|
1846
|
+
type: '2',
|
|
1847
|
+
first_name: this.getNodeParameter('first_name', i),
|
|
1848
|
+
last_name: this.getNodeParameter('last_name', i),
|
|
1849
|
+
};
|
|
1850
|
+
const email = this.getNodeParameter('email', i);
|
|
1851
|
+
if (email)
|
|
1852
|
+
body.email = email;
|
|
1853
|
+
const phone = this.getNodeParameter('phone', i);
|
|
1854
|
+
if (phone)
|
|
1855
|
+
body.phone = phone;
|
|
1856
|
+
const additional = this.getNodeParameter('contactAdditionalFields', i);
|
|
1857
|
+
const contactSimpleFields = [
|
|
1858
|
+
'middle_name', 'description', 'owner_id', 'web',
|
|
1859
|
+
'phone2', 'phone3', 'email_personal',
|
|
1860
|
+
'social_network_link_1', 'social_network_link_2',
|
|
1861
|
+
'social_network_link_3', 'social_network_link_4',
|
|
1862
|
+
'social_network_link_5', 'social_network_link_6',
|
|
1863
|
+
'address', 'shipping_address_line_1', 'shipping_city',
|
|
1864
|
+
'shipping_state', 'shipping_zip', 'shipping_country',
|
|
1865
|
+
'billing_address_line_1', 'billing_city', 'billing_state',
|
|
1866
|
+
'billing_zip', 'billing_country', 'VAT', 'timezone',
|
|
1867
|
+
];
|
|
1868
|
+
for (const field of contactSimpleFields) {
|
|
1869
|
+
if (additional[field] !== undefined && additional[field] !== '') {
|
|
1870
|
+
body[field] = additional[field];
|
|
1871
|
+
}
|
|
1872
|
+
}
|
|
1873
|
+
if (additional.birth_date) {
|
|
1874
|
+
body.birth_date = new Date(additional.birth_date).toISOString().split('T')[0];
|
|
1875
|
+
}
|
|
1876
|
+
applyResourceMapperFields(this, body, 'contactCustomFields', i);
|
|
1877
|
+
if (additional.includeLinkToWorkflow) {
|
|
1878
|
+
appendWorkflowFooter.call(this, body);
|
|
1879
|
+
}
|
|
1880
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/crm/account/create', apiKey, body);
|
|
1881
|
+
}
|
|
1882
|
+
else if (operation === 'get') {
|
|
1883
|
+
const id = this.getNodeParameter('contactId', i);
|
|
1884
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/crm/account/get/${id}`, apiKey);
|
|
1885
|
+
}
|
|
1886
|
+
else if (operation === 'getAll') {
|
|
1887
|
+
const limit = this.getNodeParameter('limit', i);
|
|
1888
|
+
const filters = this.getNodeParameter('contactFilters', i);
|
|
1889
|
+
const qs = { limit: limit.toString(), 'filter[type]': '2' };
|
|
1890
|
+
if (filters.name)
|
|
1891
|
+
qs['filter[name]'] = filters.name;
|
|
1892
|
+
if (filters.email)
|
|
1893
|
+
qs['filter[email]'] = filters.email;
|
|
1894
|
+
if (filters.phone)
|
|
1895
|
+
qs['filter[phone]'] = filters.phone;
|
|
1896
|
+
if (filters.owner_id)
|
|
1897
|
+
qs['filter[owner_id]'] = filters.owner_id;
|
|
1898
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/account/list', apiKey, undefined, qs);
|
|
1899
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
1900
|
+
returnData.push({ json: item });
|
|
1901
|
+
}
|
|
1902
|
+
continue;
|
|
1903
|
+
}
|
|
1904
|
+
else if (operation === 'update') {
|
|
1905
|
+
const id = this.getNodeParameter('contactId', i);
|
|
1906
|
+
const updateFields = this.getNodeParameter('contactUpdateFields', i);
|
|
1907
|
+
const body = { id };
|
|
1908
|
+
const contactUpdateSimpleFields = [
|
|
1909
|
+
'first_name', 'last_name', 'middle_name', 'email', 'phone',
|
|
1910
|
+
'phone2', 'phone3', 'description', 'owner_id', 'web',
|
|
1911
|
+
'email_personal',
|
|
1912
|
+
'social_network_link_1', 'social_network_link_2',
|
|
1913
|
+
'social_network_link_3', 'social_network_link_4',
|
|
1914
|
+
'social_network_link_5', 'social_network_link_6',
|
|
1915
|
+
'address', 'VAT',
|
|
1916
|
+
];
|
|
1917
|
+
for (const field of contactUpdateSimpleFields) {
|
|
1918
|
+
if (updateFields[field] !== undefined && updateFields[field] !== '') {
|
|
1919
|
+
body[field] = updateFields[field];
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
if (updateFields.birth_date) {
|
|
1923
|
+
body.birth_date = new Date(updateFields.birth_date).toISOString().split('T')[0];
|
|
1924
|
+
}
|
|
1925
|
+
applyResourceMapperFields(this, body, 'contactUpdateCustomFields', i);
|
|
1926
|
+
if (updateFields.includeLinkToWorkflow && body.description) {
|
|
1927
|
+
appendWorkflowFooter.call(this, body);
|
|
1928
|
+
}
|
|
1929
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/crm/account/update/${id}`, apiKey, body);
|
|
1930
|
+
}
|
|
1931
|
+
else if (operation === 'delete') {
|
|
1932
|
+
const id = this.getNodeParameter('contactId', i);
|
|
1933
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/crm/account/delete/${id}`, apiKey);
|
|
1934
|
+
}
|
|
1935
|
+
}
|
|
1936
|
+
// ============================
|
|
1937
|
+
// OPPORTUNITY
|
|
1938
|
+
// ============================
|
|
1939
|
+
if (resource === 'opportunity') {
|
|
1940
|
+
if (operation === 'create') {
|
|
1941
|
+
const body = {
|
|
1942
|
+
name: this.getNodeParameter('opportunityName', i),
|
|
1943
|
+
pipeline_id: this.getNodeParameter('pipeline_id', i),
|
|
1944
|
+
pipeline_stage_id: this.getNodeParameter('pipeline_stage_id', i),
|
|
1945
|
+
};
|
|
1946
|
+
const assignee = this.getNodeParameter('opportunityAssignee', i);
|
|
1947
|
+
if (assignee)
|
|
1948
|
+
body.assignee_id = assignee;
|
|
1949
|
+
const additional = this.getNodeParameter('opportunityAdditionalFields', i);
|
|
1950
|
+
const oppSimpleFields = [
|
|
1951
|
+
'budget', 'description', 'contact_name', 'contact_email',
|
|
1952
|
+
'contact_phone', 'contact_mobile', 'contact_company',
|
|
1953
|
+
'contact_position', 'contact_web', 'source_id',
|
|
1954
|
+
];
|
|
1955
|
+
for (const field of oppSimpleFields) {
|
|
1956
|
+
if (additional[field] !== undefined && additional[field] !== '') {
|
|
1957
|
+
body[field] = additional[field];
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
if (additional.start_date) {
|
|
1961
|
+
body.start_date = new Date(additional.start_date).toISOString().split('T')[0];
|
|
1962
|
+
}
|
|
1963
|
+
if (additional.deadline) {
|
|
1964
|
+
body.deadline = new Date(additional.deadline).toISOString().split('T')[0];
|
|
1965
|
+
}
|
|
1966
|
+
applyResourceMapperFields(this, body, 'opportunityCustomFields', i);
|
|
1967
|
+
if (additional.includeLinkToWorkflow) {
|
|
1968
|
+
appendWorkflowFooter.call(this, body);
|
|
1969
|
+
}
|
|
1970
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/crm/lead/create', apiKey, body);
|
|
1971
|
+
// Link to CRM accounts/contacts after creation
|
|
1972
|
+
const leadId = responseData?.response?.id;
|
|
1973
|
+
if (leadId) {
|
|
1974
|
+
if (additional.link_account_id) {
|
|
1975
|
+
await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/crm/lead_accounts/create', apiKey, {
|
|
1976
|
+
lead_id: leadId,
|
|
1977
|
+
account_id: additional.link_account_id,
|
|
1978
|
+
});
|
|
1979
|
+
}
|
|
1980
|
+
if (additional.link_contact_id) {
|
|
1981
|
+
await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/crm/lead_accounts/create', apiKey, {
|
|
1982
|
+
lead_id: leadId,
|
|
1983
|
+
account_id: additional.link_contact_id,
|
|
1984
|
+
});
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
}
|
|
1988
|
+
else if (operation === 'get') {
|
|
1989
|
+
const id = this.getNodeParameter('opportunityId', i);
|
|
1990
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/crm/lead/get/${id}`, apiKey);
|
|
1991
|
+
}
|
|
1992
|
+
else if (operation === 'getAll') {
|
|
1993
|
+
const limit = this.getNodeParameter('limit', i);
|
|
1994
|
+
const pipelineId = this.getNodeParameter('opportunityFilterPipeline', i);
|
|
1995
|
+
const stageId = this.getNodeParameter('opportunityFilterStage', i);
|
|
1996
|
+
const filters = this.getNodeParameter('opportunityFilters', i);
|
|
1997
|
+
const qs = { limit: limit.toString() };
|
|
1998
|
+
if (pipelineId)
|
|
1999
|
+
qs['filter[pipeline_id]'] = pipelineId;
|
|
2000
|
+
if (stageId)
|
|
2001
|
+
qs['filter[pipeline_stage_id]'] = stageId;
|
|
2002
|
+
if (filters.name)
|
|
2003
|
+
qs['filter[name]'] = filters.name;
|
|
2004
|
+
if (filters.assignee_id)
|
|
2005
|
+
qs['filter[assignee_id]'] = filters.assignee_id;
|
|
2006
|
+
if (filters.company_id)
|
|
2007
|
+
qs['filter[company_id]'] = filters.company_id;
|
|
2008
|
+
if (filters.active !== undefined)
|
|
2009
|
+
qs['filter[active]'] = filters.active.toString();
|
|
2010
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/crm/lead/list', apiKey, undefined, qs);
|
|
2011
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2012
|
+
returnData.push({ json: item });
|
|
2013
|
+
}
|
|
2014
|
+
continue;
|
|
2015
|
+
}
|
|
2016
|
+
else if (operation === 'update') {
|
|
2017
|
+
const id = this.getNodeParameter('opportunityId', i);
|
|
2018
|
+
const updateFields = this.getNodeParameter('opportunityUpdateFields', i);
|
|
2019
|
+
const body = { id };
|
|
2020
|
+
const oppUpdateFields = [
|
|
2021
|
+
'name', 'pipeline_id', 'pipeline_stage_id', 'assignee_id',
|
|
2022
|
+
'budget', 'description', 'active', 'closing_comment',
|
|
2023
|
+
'contact_name', 'contact_email', 'contact_phone',
|
|
2024
|
+
];
|
|
2025
|
+
for (const field of oppUpdateFields) {
|
|
2026
|
+
if (updateFields[field] !== undefined && updateFields[field] !== '') {
|
|
2027
|
+
body[field] = updateFields[field];
|
|
2028
|
+
}
|
|
2029
|
+
}
|
|
2030
|
+
if (updateFields.start_date) {
|
|
2031
|
+
body.start_date = new Date(updateFields.start_date).toISOString().split('T')[0];
|
|
2032
|
+
}
|
|
2033
|
+
if (updateFields.deadline) {
|
|
2034
|
+
body.deadline = new Date(updateFields.deadline).toISOString().split('T')[0];
|
|
2035
|
+
}
|
|
2036
|
+
if (updateFields.active === 2 || updateFields.active === 3) {
|
|
2037
|
+
body.closing_date = new Date().toISOString().split('T')[0];
|
|
2038
|
+
}
|
|
2039
|
+
applyResourceMapperFields(this, body, 'opportunityUpdateCustomFields', i);
|
|
2040
|
+
if (updateFields.includeLinkToWorkflow && body.description) {
|
|
2041
|
+
appendWorkflowFooter.call(this, body);
|
|
2042
|
+
}
|
|
2043
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/crm/lead/update/${id}`, apiKey, body);
|
|
2044
|
+
}
|
|
2045
|
+
else if (operation === 'delete') {
|
|
2046
|
+
const id = this.getNodeParameter('opportunityId', i);
|
|
2047
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/crm/lead/delete/${id}`, apiKey);
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
// ============================
|
|
2051
|
+
// PROJECT
|
|
2052
|
+
// ============================
|
|
2053
|
+
if (resource === 'project') {
|
|
2054
|
+
if (operation === 'create') {
|
|
2055
|
+
const body = {
|
|
2056
|
+
name: this.getNodeParameter('projectName', i),
|
|
2057
|
+
};
|
|
2058
|
+
const managerId = this.getNodeParameter('manager_id', i);
|
|
2059
|
+
if (managerId)
|
|
2060
|
+
body.manager_id = managerId;
|
|
2061
|
+
const desc = this.getNodeParameter('projectDescription', i);
|
|
2062
|
+
if (desc)
|
|
2063
|
+
body.description = desc;
|
|
2064
|
+
const additional = this.getNodeParameter('projectAdditionalFields', i);
|
|
2065
|
+
const projSimpleFields = [
|
|
2066
|
+
'priority', 'estimated_revenue', 'estimated_expenses',
|
|
2067
|
+
'customer_id', 'customer_crm_contact_id', 'briefcase_id',
|
|
2068
|
+
'stage_id', 'tasks_workflow_id', 'billing_type', 'crm_lead_id',
|
|
2069
|
+
];
|
|
2070
|
+
for (const field of projSimpleFields) {
|
|
2071
|
+
if (additional[field] !== undefined && additional[field] !== '' && additional[field] !== 0) {
|
|
2072
|
+
body[field] = additional[field];
|
|
2073
|
+
}
|
|
2074
|
+
}
|
|
2075
|
+
if (additional.startdate) {
|
|
2076
|
+
body.startdate = new Date(additional.startdate).toISOString().split('T')[0];
|
|
2077
|
+
}
|
|
2078
|
+
if (additional.enddate) {
|
|
2079
|
+
body.enddate = new Date(additional.enddate).toISOString().split('T')[0];
|
|
2080
|
+
}
|
|
2081
|
+
applyResourceMapperFields(this, body, 'projectCustomFields', i);
|
|
2082
|
+
if (additional.includeLinkToWorkflow) {
|
|
2083
|
+
appendWorkflowFooter.call(this, body);
|
|
2084
|
+
}
|
|
2085
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/st/projects/create', apiKey, body);
|
|
2086
|
+
}
|
|
2087
|
+
else if (operation === 'get') {
|
|
2088
|
+
const id = this.getNodeParameter('projectId', i);
|
|
2089
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/st/projects/get/${id}`, apiKey);
|
|
2090
|
+
}
|
|
2091
|
+
else if (operation === 'getAll') {
|
|
2092
|
+
const limit = this.getNodeParameter('limit', i);
|
|
2093
|
+
const filters = this.getNodeParameter('projectFilters', i);
|
|
2094
|
+
const qs = { limit: limit.toString() };
|
|
2095
|
+
if (filters.name)
|
|
2096
|
+
qs['filter[name]'] = filters.name;
|
|
2097
|
+
if (filters.manager_id)
|
|
2098
|
+
qs['filter[manager_id]'] = filters.manager_id;
|
|
2099
|
+
if (filters.customer_id)
|
|
2100
|
+
qs['filter[customer_id]'] = filters.customer_id;
|
|
2101
|
+
if (!filters.is_archive)
|
|
2102
|
+
qs['filter[is_archive]'] = '0';
|
|
2103
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/st/projects/list', apiKey, undefined, qs);
|
|
2104
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2105
|
+
returnData.push({ json: item });
|
|
2106
|
+
}
|
|
2107
|
+
continue;
|
|
2108
|
+
}
|
|
2109
|
+
else if (operation === 'update') {
|
|
2110
|
+
const id = this.getNodeParameter('projectId', i);
|
|
2111
|
+
const updateFields = this.getNodeParameter('projectUpdateFields', i);
|
|
2112
|
+
const body = { id };
|
|
2113
|
+
const projUpdateFields = [
|
|
2114
|
+
'name', 'description', 'manager_id', 'priority',
|
|
2115
|
+
'estimated_revenue', 'estimated_expenses', 'stage_id',
|
|
2116
|
+
];
|
|
2117
|
+
for (const field of projUpdateFields) {
|
|
2118
|
+
if (updateFields[field] !== undefined && updateFields[field] !== '') {
|
|
2119
|
+
body[field] = updateFields[field];
|
|
2120
|
+
}
|
|
2121
|
+
}
|
|
2122
|
+
if (updateFields.startdate) {
|
|
2123
|
+
body.startdate = new Date(updateFields.startdate).toISOString().split('T')[0];
|
|
2124
|
+
}
|
|
2125
|
+
if (updateFields.enddate) {
|
|
2126
|
+
body.enddate = new Date(updateFields.enddate).toISOString().split('T')[0];
|
|
2127
|
+
}
|
|
2128
|
+
if (updateFields.is_archive !== undefined) {
|
|
2129
|
+
body.is_archive = updateFields.is_archive ? 1 : 0;
|
|
2130
|
+
}
|
|
2131
|
+
applyResourceMapperFields(this, body, 'projectUpdateCustomFields', i);
|
|
2132
|
+
if (updateFields.includeLinkToWorkflow && body.description) {
|
|
2133
|
+
appendWorkflowFooter.call(this, body);
|
|
2134
|
+
}
|
|
2135
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/st/projects/update/${id}`, apiKey, body);
|
|
2136
|
+
}
|
|
2137
|
+
else if (operation === 'delete') {
|
|
2138
|
+
const id = this.getNodeParameter('projectId', i);
|
|
2139
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/st/projects/delete/${id}`, apiKey);
|
|
2140
|
+
}
|
|
2141
|
+
}
|
|
2142
|
+
// ============================
|
|
2143
|
+
// RECORD LIST
|
|
2144
|
+
// ============================
|
|
2145
|
+
if (resource === 'recordList') {
|
|
2146
|
+
const listId = this.getNodeParameter('recordListId', i);
|
|
2147
|
+
if (operation === 'create') {
|
|
2148
|
+
const body = { list_id: listId };
|
|
2149
|
+
const mappingValues = this.getNodeParameter('recordFields.value', i);
|
|
2150
|
+
for (const [key, value] of Object.entries(mappingValues)) {
|
|
2151
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2152
|
+
body[key] = value;
|
|
2153
|
+
}
|
|
2154
|
+
}
|
|
2155
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/customlists/items/create', apiKey, body);
|
|
2156
|
+
}
|
|
2157
|
+
else if (operation === 'get') {
|
|
2158
|
+
const id = this.getNodeParameter('recordId', i);
|
|
2159
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/customlists/items/get/${id}`, apiKey);
|
|
2160
|
+
}
|
|
2161
|
+
else if (operation === 'getAll') {
|
|
2162
|
+
const limit = this.getNodeParameter('limit', i);
|
|
2163
|
+
const filters = this.getNodeParameter('recordListFilters', i);
|
|
2164
|
+
const qs = { limit: limit.toString(), 'filter[list_id]': listId };
|
|
2165
|
+
if (filters.search)
|
|
2166
|
+
qs['search'] = filters.search;
|
|
2167
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/customlists/items/list', apiKey, undefined, qs);
|
|
2168
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2169
|
+
returnData.push({ json: item });
|
|
2170
|
+
}
|
|
2171
|
+
continue;
|
|
2172
|
+
}
|
|
2173
|
+
else if (operation === 'update') {
|
|
2174
|
+
const id = this.getNodeParameter('recordId', i);
|
|
2175
|
+
const body = { id };
|
|
2176
|
+
const mappingValues = this.getNodeParameter('recordUpdateFields.value', i);
|
|
2177
|
+
for (const [key, value] of Object.entries(mappingValues)) {
|
|
2178
|
+
if (value !== undefined && value !== null && value !== '') {
|
|
2179
|
+
body[key] = value;
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/customlists/items/update/${id}`, apiKey, body);
|
|
2183
|
+
}
|
|
2184
|
+
else if (operation === 'delete') {
|
|
2185
|
+
const id = this.getNodeParameter('recordId', i);
|
|
2186
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/customlists/items/delete/${id}`, apiKey);
|
|
2187
|
+
}
|
|
2188
|
+
}
|
|
2189
|
+
// ============================
|
|
2190
|
+
// TASK
|
|
2191
|
+
// ============================
|
|
2192
|
+
if (resource === 'task') {
|
|
2193
|
+
if (operation === 'create') {
|
|
2194
|
+
const body = {
|
|
2195
|
+
name: this.getNodeParameter('taskName', i),
|
|
2196
|
+
responsible_id: this.getNodeParameter('responsible_id', i),
|
|
2197
|
+
};
|
|
2198
|
+
const description = this.getNodeParameter('description', i);
|
|
2199
|
+
if (description)
|
|
2200
|
+
body.description = description;
|
|
2201
|
+
const additional = this.getNodeParameter('taskAdditionalFields', i);
|
|
2202
|
+
body.workflow_id = additional.workflow_id || '1';
|
|
2203
|
+
body.workflow_stage_id = additional.workflow_stage_id || '1';
|
|
2204
|
+
if (additional.owner_id)
|
|
2205
|
+
body.owner_id = additional.owner_id;
|
|
2206
|
+
if (additional.contact_id)
|
|
2207
|
+
body.crm_account_id = parseInt(additional.contact_id, 10);
|
|
2208
|
+
if (additional.priority !== undefined)
|
|
2209
|
+
body.priority = additional.priority;
|
|
2210
|
+
if (additional.status !== undefined)
|
|
2211
|
+
body.status = additional.status;
|
|
2212
|
+
if (additional.plan_start_date) {
|
|
2213
|
+
body.plan_start_date = new Date(additional.plan_start_date).toISOString().split('T')[0];
|
|
2214
|
+
}
|
|
2215
|
+
if (additional.deadline) {
|
|
2216
|
+
body.deadline = new Date(additional.deadline).toISOString().split('T')[0];
|
|
2217
|
+
}
|
|
2218
|
+
if (additional.time_estimate && additional.time_estimate > 0) {
|
|
2219
|
+
body.time_estimate = Math.round(additional.time_estimate * 60);
|
|
2220
|
+
}
|
|
2221
|
+
if (additional.cost)
|
|
2222
|
+
body.cost = additional.cost;
|
|
2223
|
+
if (additional.price)
|
|
2224
|
+
body.price = additional.price;
|
|
2225
|
+
if (additional.task_checkbyowner !== undefined) {
|
|
2226
|
+
body.task_checkbyowner = additional.task_checkbyowner ? 1 : 0;
|
|
2227
|
+
}
|
|
2228
|
+
if (additional.deadline_allowchange !== undefined) {
|
|
2229
|
+
body.deadline_allowchange = additional.deadline_allowchange ? 1 : 0;
|
|
2230
|
+
}
|
|
2231
|
+
if (additional.parent_id && additional.parent_id > 0) {
|
|
2232
|
+
body.parent_id = additional.parent_id;
|
|
2233
|
+
}
|
|
2234
|
+
if (additional.model_id) {
|
|
2235
|
+
body.model_id = additional.model_id;
|
|
2236
|
+
body.model = 'project';
|
|
2237
|
+
body.module = 'st';
|
|
2238
|
+
}
|
|
2239
|
+
applyResourceMapperFields(this, body, 'taskCustomFields', i);
|
|
2240
|
+
if (additional.includeLinkToWorkflow) {
|
|
2241
|
+
appendWorkflowFooter.call(this, body);
|
|
2242
|
+
}
|
|
2243
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, '/api/v1/module/task/tasks/create', apiKey, body);
|
|
2244
|
+
}
|
|
2245
|
+
else if (operation === 'get') {
|
|
2246
|
+
const id = this.getNodeParameter('taskId', i);
|
|
2247
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/task/tasks/get/${id}`, apiKey);
|
|
2248
|
+
}
|
|
2249
|
+
else if (operation === 'getAll') {
|
|
2250
|
+
const limit = this.getNodeParameter('limit', i);
|
|
2251
|
+
const filters = this.getNodeParameter('taskFilters', i);
|
|
2252
|
+
const qs = { limit: limit.toString() };
|
|
2253
|
+
if (filters.name)
|
|
2254
|
+
qs['filter[name]'] = filters.name;
|
|
2255
|
+
if (filters.responsible_id)
|
|
2256
|
+
qs['filter[responsible_id]'] = filters.responsible_id;
|
|
2257
|
+
if (filters.model_id) {
|
|
2258
|
+
qs['filter[model_id]'] = filters.model_id;
|
|
2259
|
+
qs['filter[module]'] = 'st';
|
|
2260
|
+
qs['filter[model]'] = 'project';
|
|
2261
|
+
}
|
|
2262
|
+
if (filters.priority !== undefined)
|
|
2263
|
+
qs['filter[priority]'] = filters.priority.toString();
|
|
2264
|
+
if (filters.status !== undefined)
|
|
2265
|
+
qs['filter[status]'] = filters.status.toString();
|
|
2266
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/task/tasks/list', apiKey, undefined, qs);
|
|
2267
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2268
|
+
returnData.push({ json: item });
|
|
2269
|
+
}
|
|
2270
|
+
continue;
|
|
2271
|
+
}
|
|
2272
|
+
else if (operation === 'update') {
|
|
2273
|
+
const id = this.getNodeParameter('taskId', i);
|
|
2274
|
+
const updateFields = this.getNodeParameter('taskUpdateFields', i);
|
|
2275
|
+
const body = { id };
|
|
2276
|
+
if (updateFields.name)
|
|
2277
|
+
body.name = updateFields.name;
|
|
2278
|
+
if (updateFields.description)
|
|
2279
|
+
body.description = updateFields.description;
|
|
2280
|
+
if (updateFields.responsible_id)
|
|
2281
|
+
body.responsible_id = updateFields.responsible_id;
|
|
2282
|
+
if (updateFields.owner_id)
|
|
2283
|
+
body.owner_id = updateFields.owner_id;
|
|
2284
|
+
if (updateFields.workflow_id)
|
|
2285
|
+
body.workflow_id = updateFields.workflow_id;
|
|
2286
|
+
if (updateFields.workflow_stage_id)
|
|
2287
|
+
body.workflow_stage_id = updateFields.workflow_stage_id;
|
|
2288
|
+
if (updateFields.priority !== undefined)
|
|
2289
|
+
body.priority = updateFields.priority;
|
|
2290
|
+
if (updateFields.status !== undefined)
|
|
2291
|
+
body.status = updateFields.status;
|
|
2292
|
+
if (updateFields.plan_start_date) {
|
|
2293
|
+
body.plan_start_date = new Date(updateFields.plan_start_date).toISOString().split('T')[0];
|
|
2294
|
+
}
|
|
2295
|
+
if (updateFields.deadline) {
|
|
2296
|
+
body.deadline = new Date(updateFields.deadline).toISOString().split('T')[0];
|
|
2297
|
+
}
|
|
2298
|
+
if (updateFields.time_estimate && updateFields.time_estimate > 0) {
|
|
2299
|
+
body.time_estimate = Math.round(updateFields.time_estimate * 60);
|
|
2300
|
+
}
|
|
2301
|
+
if (updateFields.cost)
|
|
2302
|
+
body.cost = updateFields.cost;
|
|
2303
|
+
if (updateFields.price)
|
|
2304
|
+
body.price = updateFields.price;
|
|
2305
|
+
if (updateFields.parent_id && updateFields.parent_id > 0) {
|
|
2306
|
+
body.parent_id = updateFields.parent_id;
|
|
2307
|
+
}
|
|
2308
|
+
if (updateFields.model_id) {
|
|
2309
|
+
body.model_id = updateFields.model_id;
|
|
2310
|
+
body.model = 'project';
|
|
2311
|
+
body.module = 'st';
|
|
2312
|
+
}
|
|
2313
|
+
applyResourceMapperFields(this, body, 'taskUpdateCustomFields', i);
|
|
2314
|
+
if (updateFields.includeLinkToWorkflow && body.description) {
|
|
2315
|
+
appendWorkflowFooter.call(this, body);
|
|
2316
|
+
}
|
|
2317
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/task/tasks/update/${id}`, apiKey, body);
|
|
2318
|
+
}
|
|
2319
|
+
else if (operation === 'delete') {
|
|
2320
|
+
const id = this.getNodeParameter('taskId', i);
|
|
2321
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/task/tasks/delete/${id}`, apiKey);
|
|
2322
|
+
}
|
|
2323
|
+
}
|
|
2324
|
+
// ============================
|
|
2325
|
+
// TAG
|
|
2326
|
+
// ============================
|
|
2327
|
+
if (resource === 'tag') {
|
|
2328
|
+
if (operation === 'add') {
|
|
2329
|
+
const entityType = this.getNodeParameter('tagEntityType', i);
|
|
2330
|
+
const entityId = this.getNodeParameter('tagEntityId', i);
|
|
2331
|
+
const tagName = this.getNodeParameter('tagName', i);
|
|
2332
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/${entityType}/${entityId}/global_tags/create`, apiKey, { name: tagName });
|
|
2333
|
+
}
|
|
2334
|
+
else if (operation === 'getAll') {
|
|
2335
|
+
const entityType = this.getNodeParameter('tagEntityType', i);
|
|
2336
|
+
const entityId = this.getNodeParameter('tagEntityId', i);
|
|
2337
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/${entityType}/${entityId}/global_tags/list`, apiKey);
|
|
2338
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2339
|
+
returnData.push({ json: item });
|
|
2340
|
+
}
|
|
2341
|
+
continue;
|
|
2342
|
+
}
|
|
2343
|
+
else if (operation === 'listAll') {
|
|
2344
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, '/api/v1/module/core/tag/list', apiKey);
|
|
2345
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2346
|
+
returnData.push({ json: item });
|
|
2347
|
+
}
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
else if (operation === 'remove') {
|
|
2351
|
+
const entityType = this.getNodeParameter('tagEntityType', i);
|
|
2352
|
+
const entityId = this.getNodeParameter('tagEntityId', i);
|
|
2353
|
+
const tagId = this.getNodeParameter('tagIdToRemove', i);
|
|
2354
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/${entityType}/${entityId}/global_tags/delete`, apiKey, { id: tagId });
|
|
2355
|
+
}
|
|
2356
|
+
}
|
|
2357
|
+
// ============================
|
|
2358
|
+
// COMMENT
|
|
2359
|
+
// ============================
|
|
2360
|
+
if (resource === 'comment') {
|
|
2361
|
+
if (operation === 'create') {
|
|
2362
|
+
const entityType = this.getNodeParameter('commentEntityType', i);
|
|
2363
|
+
const entityId = this.getNodeParameter('commentEntityId', i);
|
|
2364
|
+
const includeFooter = this.getNodeParameter('commentIncludeLinkToWorkflow', i, false);
|
|
2365
|
+
const body = { text: this.getNodeParameter('commentText', i) };
|
|
2366
|
+
if (includeFooter) {
|
|
2367
|
+
appendWorkflowFooter.call(this, body, 'text');
|
|
2368
|
+
}
|
|
2369
|
+
responseData = await flowluApiRequest.call(this, 'POST', baseUrl, `/api/v1/module/${entityType}/${entityId}/comments/create`, apiKey, body);
|
|
2370
|
+
}
|
|
2371
|
+
else if (operation === 'getAll') {
|
|
2372
|
+
const entityType = this.getNodeParameter('commentEntityType', i);
|
|
2373
|
+
const entityId = this.getNodeParameter('commentEntityId', i);
|
|
2374
|
+
const limit = this.getNodeParameter('limit', i);
|
|
2375
|
+
responseData = await flowluApiRequest.call(this, 'GET', baseUrl, `/api/v1/module/${entityType}/${entityId}/comments/list`, apiKey, undefined, { limit: limit.toString() });
|
|
2376
|
+
for (const item of (responseData?.response?.items || [])) {
|
|
2377
|
+
returnData.push({ json: item });
|
|
2378
|
+
}
|
|
2379
|
+
continue;
|
|
2380
|
+
}
|
|
2381
|
+
}
|
|
2382
|
+
if (responseData !== undefined) {
|
|
2383
|
+
returnData.push({ json: responseData });
|
|
2384
|
+
}
|
|
2385
|
+
}
|
|
2386
|
+
catch (error) {
|
|
2387
|
+
if (this.continueOnFail()) {
|
|
2388
|
+
returnData.push({ json: { error: error instanceof Error ? error.message : String(error) } });
|
|
2389
|
+
continue;
|
|
2390
|
+
}
|
|
2391
|
+
throw new n8n_workflow_1.NodeOperationError(this.getNode(), error instanceof Error ? error : new Error(String(error)));
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
return [this.helpers.returnJsonArray(returnData)];
|
|
2395
|
+
}
|
|
2396
|
+
}
|
|
2397
|
+
exports.Flowlu = Flowlu;
|