@pegasuss/n8n-nodes-suitecrm 0.1.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,1294 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SuiteCRM = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ // ─────────────────────────────────────────────────────────────────────────────
6
+ // Helper: obtain OAuth2 access token
7
+ // ─────────────────────────────────────────────────────────────────────────────
8
+ async function getAccessToken(context, baseUrl, clientId, clientSecret) {
9
+ const response = await context.helpers.request({
10
+ method: 'POST',
11
+ url: `${baseUrl}/api/v8/oauth/access_token`,
12
+ form: {
13
+ grant_type: 'client_credentials',
14
+ client_id: clientId,
15
+ client_secret: clientSecret,
16
+ },
17
+ });
18
+ const parsed = typeof response === 'string' ? JSON.parse(response) : response;
19
+ if (!parsed.access_token) {
20
+ throw new Error(`SuiteCRM OAuth2 error: ${parsed.error || 'no access_token returned'}`);
21
+ }
22
+ return parsed.access_token;
23
+ }
24
+ // ─────────────────────────────────────────────────────────────────────────────
25
+ // Helper: generic API call
26
+ // ─────────────────────────────────────────────────────────────────────────────
27
+ async function apiRequest(context, token, baseUrl, method, endpoint, body, qs) {
28
+ var _a, _b, _c;
29
+ const options = {
30
+ method,
31
+ url: `${baseUrl}/api/v8${endpoint}`,
32
+ headers: {
33
+ Authorization: `Bearer ${token}`,
34
+ 'Content-Type': 'application/vnd.api+json',
35
+ Accept: 'application/vnd.api+json',
36
+ },
37
+ json: true,
38
+ };
39
+ if (body)
40
+ options.body = body;
41
+ if (qs)
42
+ options.qs = qs;
43
+ try {
44
+ return await context.helpers.request(options);
45
+ }
46
+ catch (error) {
47
+ const msg = ((_c = (_b = (_a = error === null || error === void 0 ? void 0 : error.error) === null || _a === void 0 ? void 0 : _a.errors) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.detail) ||
48
+ (error === null || error === void 0 ? void 0 : error.message) ||
49
+ JSON.stringify(error);
50
+ throw new n8n_workflow_1.NodeOperationError(context.getNode(), `SuiteCRM API error [${method} ${endpoint}]: ${msg}`);
51
+ }
52
+ }
53
+ // ─────────────────────────────────────────────────────────────────────────────
54
+ // Helper: build JSON:API create/update body
55
+ // ─────────────────────────────────────────────────────────────────────────────
56
+ function buildJsonApiBody(moduleName, attributes, id) {
57
+ const data = {
58
+ type: moduleName,
59
+ attributes,
60
+ };
61
+ if (id)
62
+ data.id = id;
63
+ return { data };
64
+ }
65
+ // ─────────────────────────────────────────────────────────────────────────────
66
+ // Helper: extract attributes from JSON:API response
67
+ // ─────────────────────────────────────────────────────────────────────────────
68
+ function extractData(response) {
69
+ if (!response)
70
+ return {};
71
+ if (response.data) {
72
+ const { id, type, attributes } = response.data;
73
+ return { id, type, ...attributes };
74
+ }
75
+ return response;
76
+ }
77
+ function extractDataList(response) {
78
+ if (!(response === null || response === void 0 ? void 0 : response.data))
79
+ return [];
80
+ return response.data.map((item) => ({
81
+ id: item.id,
82
+ type: item.type,
83
+ ...item.attributes,
84
+ }));
85
+ }
86
+ // ─────────────────────────────────────────────────────────────────────────────
87
+ // Node definition
88
+ // ─────────────────────────────────────────────────────────────────────────────
89
+ class SuiteCRM {
90
+ constructor() {
91
+ this.description = {
92
+ displayName: 'SuiteCRM',
93
+ name: 'suiteCRM',
94
+ icon: 'file:suitecrm.svg',
95
+ group: ['transform'],
96
+ version: 1,
97
+ subtitle: '={{$parameter["operation"] + ": " + $parameter["resource"]}}',
98
+ description: 'Interact with SuiteCRM – open source enterprise CRM. Manage Contacts, Accounts, Leads, Opportunities and Activities via the V8 REST API.',
99
+ defaults: {
100
+ name: 'SuiteCRM',
101
+ },
102
+ inputs: ['main'],
103
+ outputs: ['main'],
104
+ credentials: [
105
+ {
106
+ name: 'suiteCRMApi',
107
+ required: true,
108
+ },
109
+ ],
110
+ properties: [
111
+ // ── Resource selector ────────────────────────────────────────────────
112
+ {
113
+ displayName: 'Resource',
114
+ name: 'resource',
115
+ type: 'options',
116
+ noDataExpression: true,
117
+ options: [
118
+ { name: 'Account', value: 'account' },
119
+ { name: 'Contact', value: 'contact' },
120
+ { name: 'Lead', value: 'lead' },
121
+ { name: 'Note / Activity', value: 'note' },
122
+ { name: 'Opportunity', value: 'opportunity' },
123
+ ],
124
+ default: 'contact',
125
+ },
126
+ // ════════════════════════════════════════════════════════════════════
127
+ // CONTACT operations
128
+ // ════════════════════════════════════════════════════════════════════
129
+ {
130
+ displayName: 'Operation',
131
+ name: 'operation',
132
+ type: 'options',
133
+ noDataExpression: true,
134
+ displayOptions: { show: { resource: ['contact'] } },
135
+ options: [
136
+ {
137
+ name: 'Create',
138
+ value: 'create',
139
+ description: 'Create a new contact',
140
+ action: 'Create a contact',
141
+ },
142
+ {
143
+ name: 'Delete',
144
+ value: 'delete',
145
+ description: 'Delete a contact',
146
+ action: 'Delete a contact',
147
+ },
148
+ {
149
+ name: 'Get',
150
+ value: 'get',
151
+ description: 'Get a contact by ID',
152
+ action: 'Get a contact',
153
+ },
154
+ {
155
+ name: 'Get Many',
156
+ value: 'getMany',
157
+ description: 'Get multiple contacts',
158
+ action: 'Get many contacts',
159
+ },
160
+ {
161
+ name: 'Search',
162
+ value: 'search',
163
+ description: 'Search contacts by field',
164
+ action: 'Search contacts',
165
+ },
166
+ {
167
+ name: 'Update',
168
+ value: 'update',
169
+ description: 'Update a contact',
170
+ action: 'Update a contact',
171
+ },
172
+ ],
173
+ default: 'create',
174
+ },
175
+ // ════════════════════════════════════════════════════════════════════
176
+ // ACCOUNT operations
177
+ // ════════════════════════════════════════════════════════════════════
178
+ {
179
+ displayName: 'Operation',
180
+ name: 'operation',
181
+ type: 'options',
182
+ noDataExpression: true,
183
+ displayOptions: { show: { resource: ['account'] } },
184
+ options: [
185
+ {
186
+ name: 'Create',
187
+ value: 'create',
188
+ description: 'Create a new account',
189
+ action: 'Create an account',
190
+ },
191
+ {
192
+ name: 'Delete',
193
+ value: 'delete',
194
+ description: 'Delete an account',
195
+ action: 'Delete an account',
196
+ },
197
+ {
198
+ name: 'Get',
199
+ value: 'get',
200
+ description: 'Get an account by ID',
201
+ action: 'Get an account',
202
+ },
203
+ {
204
+ name: 'Get Many',
205
+ value: 'getMany',
206
+ description: 'Get multiple accounts',
207
+ action: 'Get many accounts',
208
+ },
209
+ {
210
+ name: 'Search',
211
+ value: 'search',
212
+ description: 'Search accounts by field',
213
+ action: 'Search accounts',
214
+ },
215
+ {
216
+ name: 'Update',
217
+ value: 'update',
218
+ description: 'Update an account',
219
+ action: 'Update an account',
220
+ },
221
+ ],
222
+ default: 'create',
223
+ },
224
+ // ════════════════════════════════════════════════════════════════════
225
+ // LEAD operations
226
+ // ════════════════════════════════════════════════════════════════════
227
+ {
228
+ displayName: 'Operation',
229
+ name: 'operation',
230
+ type: 'options',
231
+ noDataExpression: true,
232
+ displayOptions: { show: { resource: ['lead'] } },
233
+ options: [
234
+ {
235
+ name: 'Convert',
236
+ value: 'convert',
237
+ description: 'Convert lead to Contact/Account/Opportunity',
238
+ action: 'Convert a lead',
239
+ },
240
+ {
241
+ name: 'Create',
242
+ value: 'create',
243
+ description: 'Create a new lead',
244
+ action: 'Create a lead',
245
+ },
246
+ {
247
+ name: 'Get',
248
+ value: 'get',
249
+ description: 'Get a lead by ID',
250
+ action: 'Get a lead',
251
+ },
252
+ {
253
+ name: 'Get Many',
254
+ value: 'getMany',
255
+ description: 'Get multiple leads',
256
+ action: 'Get many leads',
257
+ },
258
+ {
259
+ name: 'Update',
260
+ value: 'update',
261
+ description: 'Update a lead',
262
+ action: 'Update a lead',
263
+ },
264
+ ],
265
+ default: 'create',
266
+ },
267
+ // ════════════════════════════════════════════════════════════════════
268
+ // OPPORTUNITY operations
269
+ // ════════════════════════════════════════════════════════════════════
270
+ {
271
+ displayName: 'Operation',
272
+ name: 'operation',
273
+ type: 'options',
274
+ noDataExpression: true,
275
+ displayOptions: { show: { resource: ['opportunity'] } },
276
+ options: [
277
+ {
278
+ name: 'Create',
279
+ value: 'create',
280
+ description: 'Create a new opportunity',
281
+ action: 'Create an opportunity',
282
+ },
283
+ {
284
+ name: 'Delete',
285
+ value: 'delete',
286
+ description: 'Delete an opportunity',
287
+ action: 'Delete an opportunity',
288
+ },
289
+ {
290
+ name: 'Get',
291
+ value: 'get',
292
+ description: 'Get an opportunity by ID',
293
+ action: 'Get an opportunity',
294
+ },
295
+ {
296
+ name: 'Get Many',
297
+ value: 'getMany',
298
+ description: 'Get multiple opportunities',
299
+ action: 'Get many opportunities',
300
+ },
301
+ {
302
+ name: 'Update',
303
+ value: 'update',
304
+ description: 'Update an opportunity',
305
+ action: 'Update an opportunity',
306
+ },
307
+ ],
308
+ default: 'create',
309
+ },
310
+ // ════════════════════════════════════════════════════════════════════
311
+ // NOTE / ACTIVITY operations
312
+ // ════════════════════════════════════════════════════════════════════
313
+ {
314
+ displayName: 'Operation',
315
+ name: 'operation',
316
+ type: 'options',
317
+ noDataExpression: true,
318
+ displayOptions: { show: { resource: ['note'] } },
319
+ options: [
320
+ {
321
+ name: 'Create Note',
322
+ value: 'createNote',
323
+ description: 'Create a note linked to a record',
324
+ action: 'Create a note',
325
+ },
326
+ {
327
+ name: 'List Activities',
328
+ value: 'listActivities',
329
+ description: 'List activities (Notes) for a parent record',
330
+ action: 'List activities',
331
+ },
332
+ ],
333
+ default: 'createNote',
334
+ },
335
+ // ════════════════════════════════════════════════════════════════════
336
+ // SHARED: Record ID field
337
+ // ════════════════════════════════════════════════════════════════════
338
+ {
339
+ displayName: 'Record ID',
340
+ name: 'recordId',
341
+ type: 'string',
342
+ required: true,
343
+ default: '',
344
+ description: 'The unique ID of the record in SuiteCRM',
345
+ displayOptions: {
346
+ show: {
347
+ operation: ['get', 'update', 'delete', 'convert'],
348
+ },
349
+ },
350
+ },
351
+ // ════════════════════════════════════════════════════════════════════
352
+ // CONTACT: Create fields
353
+ // ════════════════════════════════════════════════════════════════════
354
+ {
355
+ displayName: 'First Name',
356
+ name: 'firstName',
357
+ type: 'string',
358
+ default: '',
359
+ displayOptions: {
360
+ show: { resource: ['contact'], operation: ['create'] },
361
+ },
362
+ },
363
+ {
364
+ displayName: 'Last Name',
365
+ name: 'lastName',
366
+ type: 'string',
367
+ required: true,
368
+ default: '',
369
+ displayOptions: {
370
+ show: { resource: ['contact'], operation: ['create'] },
371
+ },
372
+ },
373
+ {
374
+ displayName: 'Email',
375
+ name: 'email',
376
+ type: 'string',
377
+ placeholder: 'name@email.com',
378
+ default: '',
379
+ displayOptions: {
380
+ show: { resource: ['contact'], operation: ['create'] },
381
+ },
382
+ },
383
+ {
384
+ displayName: 'Phone',
385
+ name: 'phone',
386
+ type: 'string',
387
+ default: '',
388
+ displayOptions: {
389
+ show: { resource: ['contact'], operation: ['create'] },
390
+ },
391
+ },
392
+ {
393
+ displayName: 'Account ID',
394
+ name: 'accountId',
395
+ type: 'string',
396
+ default: '',
397
+ description: 'ID of the account to link this contact to',
398
+ displayOptions: {
399
+ show: { resource: ['contact'], operation: ['create'] },
400
+ },
401
+ },
402
+ {
403
+ displayName: 'Additional Fields',
404
+ name: 'contactAdditionalFields',
405
+ type: 'collection',
406
+ placeholder: 'Add Field',
407
+ default: {},
408
+ displayOptions: {
409
+ show: { resource: ['contact'], operation: ['create'] },
410
+ },
411
+ options: [
412
+ { displayName: 'Department', name: 'department', type: 'string', default: '' },
413
+ { displayName: 'Do Not Call', name: 'do_not_call', type: 'boolean', default: false },
414
+ { displayName: 'Lead Source', name: 'lead_source', type: 'string', default: '' },
415
+ { displayName: 'Mobile', name: 'phone_mobile', type: 'string', default: '' },
416
+ { displayName: 'Salutation', name: 'salutation', type: 'string', default: '' },
417
+ { displayName: 'Title', name: 'title', type: 'string', default: '' },
418
+ {
419
+ displayName: 'Billing City',
420
+ name: 'primary_address_city',
421
+ type: 'string',
422
+ default: '',
423
+ },
424
+ {
425
+ displayName: 'Billing Country',
426
+ name: 'primary_address_country',
427
+ type: 'string',
428
+ default: '',
429
+ },
430
+ {
431
+ displayName: 'Billing State',
432
+ name: 'primary_address_state',
433
+ type: 'string',
434
+ default: '',
435
+ },
436
+ {
437
+ displayName: 'Billing Street',
438
+ name: 'primary_address_street',
439
+ type: 'string',
440
+ default: '',
441
+ },
442
+ {
443
+ displayName: 'Billing Zip',
444
+ name: 'primary_address_postalcode',
445
+ type: 'string',
446
+ default: '',
447
+ },
448
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
449
+ ],
450
+ },
451
+ // ════════════════════════════════════════════════════════════════════
452
+ // CONTACT: Update fields
453
+ // ════════════════════════════════════════════════════════════════════
454
+ {
455
+ displayName: 'Update Fields',
456
+ name: 'contactUpdateFields',
457
+ type: 'collection',
458
+ placeholder: 'Add Field',
459
+ default: {},
460
+ displayOptions: {
461
+ show: { resource: ['contact'], operation: ['update'] },
462
+ },
463
+ options: [
464
+ { displayName: 'Account ID', name: 'account_id', type: 'string', default: '' },
465
+ { displayName: 'Department', name: 'department', type: 'string', default: '' },
466
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
467
+ { displayName: 'Email', name: 'email1', type: 'string', default: '' },
468
+ { displayName: 'First Name', name: 'first_name', type: 'string', default: '' },
469
+ { displayName: 'Last Name', name: 'last_name', type: 'string', default: '' },
470
+ { displayName: 'Lead Source', name: 'lead_source', type: 'string', default: '' },
471
+ { displayName: 'Mobile', name: 'phone_mobile', type: 'string', default: '' },
472
+ { displayName: 'Phone', name: 'phone_work', type: 'string', default: '' },
473
+ { displayName: 'Title', name: 'title', type: 'string', default: '' },
474
+ ],
475
+ },
476
+ // ════════════════════════════════════════════════════════════════════
477
+ // ACCOUNT: Create fields
478
+ // ════════════════════════════════════════════════════════════════════
479
+ {
480
+ displayName: 'Account Name',
481
+ name: 'accountName',
482
+ type: 'string',
483
+ required: true,
484
+ default: '',
485
+ displayOptions: {
486
+ show: { resource: ['account'], operation: ['create'] },
487
+ },
488
+ },
489
+ {
490
+ displayName: 'Additional Fields',
491
+ name: 'accountAdditionalFields',
492
+ type: 'collection',
493
+ placeholder: 'Add Field',
494
+ default: {},
495
+ displayOptions: {
496
+ show: { resource: ['account'], operation: ['create'] },
497
+ },
498
+ options: [
499
+ { displayName: 'Annual Revenue', name: 'annual_revenue', type: 'number', default: 0 },
500
+ {
501
+ displayName: 'Billing City',
502
+ name: 'billing_address_city',
503
+ type: 'string',
504
+ default: '',
505
+ },
506
+ {
507
+ displayName: 'Billing Country',
508
+ name: 'billing_address_country',
509
+ type: 'string',
510
+ default: '',
511
+ },
512
+ {
513
+ displayName: 'Billing State',
514
+ name: 'billing_address_state',
515
+ type: 'string',
516
+ default: '',
517
+ },
518
+ {
519
+ displayName: 'Billing Street',
520
+ name: 'billing_address_street',
521
+ type: 'string',
522
+ default: '',
523
+ },
524
+ {
525
+ displayName: 'Billing Zip',
526
+ name: 'billing_address_postalcode',
527
+ type: 'string',
528
+ default: '',
529
+ },
530
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
531
+ { displayName: 'Email', name: 'email1', type: 'string', default: '' },
532
+ { displayName: 'Employees', name: 'employees', type: 'number', default: 0 },
533
+ { displayName: 'Industry', name: 'industry', type: 'string', default: '' },
534
+ { displayName: 'Phone', name: 'phone_office', type: 'string', default: '' },
535
+ { displayName: 'Rating', name: 'rating', type: 'string', default: '' },
536
+ { displayName: 'Type', name: 'account_type', type: 'string', default: '' },
537
+ { displayName: 'Website', name: 'website', type: 'string', default: '' },
538
+ ],
539
+ },
540
+ // ACCOUNT: Update fields
541
+ {
542
+ displayName: 'Update Fields',
543
+ name: 'accountUpdateFields',
544
+ type: 'collection',
545
+ placeholder: 'Add Field',
546
+ default: {},
547
+ displayOptions: {
548
+ show: { resource: ['account'], operation: ['update'] },
549
+ },
550
+ options: [
551
+ { displayName: 'Annual Revenue', name: 'annual_revenue', type: 'number', default: 0 },
552
+ {
553
+ displayName: 'Billing City',
554
+ name: 'billing_address_city',
555
+ type: 'string',
556
+ default: '',
557
+ },
558
+ {
559
+ displayName: 'Billing Country',
560
+ name: 'billing_address_country',
561
+ type: 'string',
562
+ default: '',
563
+ },
564
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
565
+ { displayName: 'Email', name: 'email1', type: 'string', default: '' },
566
+ { displayName: 'Industry', name: 'industry', type: 'string', default: '' },
567
+ { displayName: 'Name', name: 'name', type: 'string', default: '' },
568
+ { displayName: 'Phone', name: 'phone_office', type: 'string', default: '' },
569
+ { displayName: 'Website', name: 'website', type: 'string', default: '' },
570
+ ],
571
+ },
572
+ // ════════════════════════════════════════════════════════════════════
573
+ // LEAD: Create fields
574
+ // ════════════════════════════════════════════════════════════════════
575
+ {
576
+ displayName: 'First Name',
577
+ name: 'leadFirstName',
578
+ type: 'string',
579
+ default: '',
580
+ displayOptions: {
581
+ show: { resource: ['lead'], operation: ['create'] },
582
+ },
583
+ },
584
+ {
585
+ displayName: 'Last Name',
586
+ name: 'leadLastName',
587
+ type: 'string',
588
+ required: true,
589
+ default: '',
590
+ displayOptions: {
591
+ show: { resource: ['lead'], operation: ['create'] },
592
+ },
593
+ },
594
+ {
595
+ displayName: 'Company',
596
+ name: 'leadCompany',
597
+ type: 'string',
598
+ required: true,
599
+ default: '',
600
+ displayOptions: {
601
+ show: { resource: ['lead'], operation: ['create'] },
602
+ },
603
+ },
604
+ {
605
+ displayName: 'Email',
606
+ name: 'leadEmail',
607
+ type: 'string',
608
+ placeholder: 'name@email.com',
609
+ default: '',
610
+ displayOptions: {
611
+ show: { resource: ['lead'], operation: ['create'] },
612
+ },
613
+ },
614
+ {
615
+ displayName: 'Phone',
616
+ name: 'leadPhone',
617
+ type: 'string',
618
+ default: '',
619
+ displayOptions: {
620
+ show: { resource: ['lead'], operation: ['create'] },
621
+ },
622
+ },
623
+ {
624
+ displayName: 'Additional Fields',
625
+ name: 'leadAdditionalFields',
626
+ type: 'collection',
627
+ placeholder: 'Add Field',
628
+ default: {},
629
+ displayOptions: {
630
+ show: { resource: ['lead'], operation: ['create'] },
631
+ },
632
+ options: [
633
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
634
+ { displayName: 'Do Not Call', name: 'do_not_call', type: 'boolean', default: false },
635
+ { displayName: 'Lead Source', name: 'lead_source', type: 'string', default: '' },
636
+ { displayName: 'Lead Source Description', name: 'lead_source_description', type: 'string', default: '' },
637
+ { displayName: 'Mobile', name: 'phone_mobile', type: 'string', default: '' },
638
+ { displayName: 'Referred By', name: 'refered_by', type: 'string', default: '' },
639
+ { displayName: 'Salutation', name: 'salutation', type: 'string', default: '' },
640
+ { displayName: 'Status', name: 'status', type: 'string', default: '' },
641
+ { displayName: 'Title', name: 'title', type: 'string', default: '' },
642
+ { displayName: 'Website', name: 'website', type: 'string', default: '' },
643
+ ],
644
+ },
645
+ // LEAD: Update fields
646
+ {
647
+ displayName: 'Update Fields',
648
+ name: 'leadUpdateFields',
649
+ type: 'collection',
650
+ placeholder: 'Add Field',
651
+ default: {},
652
+ displayOptions: {
653
+ show: { resource: ['lead'], operation: ['update'] },
654
+ },
655
+ options: [
656
+ { displayName: 'Company', name: 'account_name', type: 'string', default: '' },
657
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
658
+ { displayName: 'Email', name: 'email1', type: 'string', default: '' },
659
+ { displayName: 'First Name', name: 'first_name', type: 'string', default: '' },
660
+ { displayName: 'Last Name', name: 'last_name', type: 'string', default: '' },
661
+ { displayName: 'Lead Source', name: 'lead_source', type: 'string', default: '' },
662
+ { displayName: 'Phone', name: 'phone_work', type: 'string', default: '' },
663
+ { displayName: 'Status', name: 'status', type: 'string', default: '' },
664
+ { displayName: 'Title', name: 'title', type: 'string', default: '' },
665
+ ],
666
+ },
667
+ // LEAD: Convert fields
668
+ {
669
+ displayName: 'Create Contact',
670
+ name: 'convertCreateContact',
671
+ type: 'boolean',
672
+ default: true,
673
+ description: 'Whether to create a Contact from this lead',
674
+ displayOptions: {
675
+ show: { resource: ['lead'], operation: ['convert'] },
676
+ },
677
+ },
678
+ {
679
+ displayName: 'Create Account',
680
+ name: 'convertCreateAccount',
681
+ type: 'boolean',
682
+ default: true,
683
+ description: 'Whether to create an Account from this lead',
684
+ displayOptions: {
685
+ show: { resource: ['lead'], operation: ['convert'] },
686
+ },
687
+ },
688
+ {
689
+ displayName: 'Create Opportunity',
690
+ name: 'convertCreateOpportunity',
691
+ type: 'boolean',
692
+ default: false,
693
+ description: 'Whether to create an Opportunity from this lead',
694
+ displayOptions: {
695
+ show: { resource: ['lead'], operation: ['convert'] },
696
+ },
697
+ },
698
+ {
699
+ displayName: 'Opportunity Name',
700
+ name: 'convertOpportunityName',
701
+ type: 'string',
702
+ default: '',
703
+ description: 'Name of the opportunity to create (required if Create Opportunity is true)',
704
+ displayOptions: {
705
+ show: {
706
+ resource: ['lead'],
707
+ operation: ['convert'],
708
+ convertCreateOpportunity: [true],
709
+ },
710
+ },
711
+ },
712
+ {
713
+ displayName: 'Opportunity Close Date',
714
+ name: 'convertOpportunityCloseDate',
715
+ type: 'dateTime',
716
+ default: '',
717
+ displayOptions: {
718
+ show: {
719
+ resource: ['lead'],
720
+ operation: ['convert'],
721
+ convertCreateOpportunity: [true],
722
+ },
723
+ },
724
+ },
725
+ {
726
+ displayName: 'Opportunity Amount',
727
+ name: 'convertOpportunityAmount',
728
+ type: 'number',
729
+ default: 0,
730
+ displayOptions: {
731
+ show: {
732
+ resource: ['lead'],
733
+ operation: ['convert'],
734
+ convertCreateOpportunity: [true],
735
+ },
736
+ },
737
+ },
738
+ // ════════════════════════════════════════════════════════════════════
739
+ // OPPORTUNITY: Create fields
740
+ // ════════════════════════════════════════════════════════════════════
741
+ {
742
+ displayName: 'Opportunity Name',
743
+ name: 'opportunityName',
744
+ type: 'string',
745
+ required: true,
746
+ default: '',
747
+ displayOptions: {
748
+ show: { resource: ['opportunity'], operation: ['create'] },
749
+ },
750
+ },
751
+ {
752
+ displayName: 'Close Date',
753
+ name: 'closeDate',
754
+ type: 'dateTime',
755
+ required: true,
756
+ default: '',
757
+ displayOptions: {
758
+ show: { resource: ['opportunity'], operation: ['create'] },
759
+ },
760
+ },
761
+ {
762
+ displayName: 'Sales Stage',
763
+ name: 'salesStage',
764
+ type: 'options',
765
+ required: true,
766
+ default: 'Prospecting',
767
+ displayOptions: {
768
+ show: { resource: ['opportunity'], operation: ['create'] },
769
+ },
770
+ options: [
771
+ { name: 'Closed Lost', value: 'Closed Lost' },
772
+ { name: 'Closed Won', value: 'Closed Won' },
773
+ { name: 'Id. Decision Makers', value: 'Id. Decision Makers' },
774
+ { name: 'Needs Analysis', value: 'Needs Analysis' },
775
+ { name: 'Perception Analysis', value: 'Perception Analysis' },
776
+ { name: 'Proposal/Price Quote', value: 'Proposal/Price Quote' },
777
+ { name: 'Prospecting', value: 'Prospecting' },
778
+ { name: 'Qualification', value: 'Qualification' },
779
+ { name: 'Value Proposition', value: 'Value Proposition' },
780
+ ],
781
+ },
782
+ {
783
+ displayName: 'Additional Fields',
784
+ name: 'opportunityAdditionalFields',
785
+ type: 'collection',
786
+ placeholder: 'Add Field',
787
+ default: {},
788
+ displayOptions: {
789
+ show: { resource: ['opportunity'], operation: ['create'] },
790
+ },
791
+ options: [
792
+ { displayName: 'Account ID', name: 'account_id', type: 'string', default: '' },
793
+ { displayName: 'Amount', name: 'amount', type: 'number', default: 0 },
794
+ { displayName: 'Assigned User ID', name: 'assigned_user_id', type: 'string', default: '' },
795
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
796
+ { displayName: 'Lead Source', name: 'lead_source', type: 'string', default: '' },
797
+ { displayName: 'Next Step', name: 'next_step', type: 'string', default: '' },
798
+ { displayName: 'Probability', name: 'probability', type: 'number', default: 0 },
799
+ { displayName: 'Type', name: 'opportunity_type', type: 'string', default: '' },
800
+ ],
801
+ },
802
+ // OPPORTUNITY: Update fields
803
+ {
804
+ displayName: 'Update Fields',
805
+ name: 'opportunityUpdateFields',
806
+ type: 'collection',
807
+ placeholder: 'Add Field',
808
+ default: {},
809
+ displayOptions: {
810
+ show: { resource: ['opportunity'], operation: ['update'] },
811
+ },
812
+ options: [
813
+ { displayName: 'Account ID', name: 'account_id', type: 'string', default: '' },
814
+ { displayName: 'Amount', name: 'amount', type: 'number', default: 0 },
815
+ {
816
+ displayName: 'Close Date',
817
+ name: 'date_closed',
818
+ type: 'dateTime',
819
+ default: '',
820
+ },
821
+ { displayName: 'Description', name: 'description', type: 'string', default: '' },
822
+ { displayName: 'Name', name: 'name', type: 'string', default: '' },
823
+ { displayName: 'Next Step', name: 'next_step', type: 'string', default: '' },
824
+ { displayName: 'Probability', name: 'probability', type: 'number', default: 0 },
825
+ {
826
+ displayName: 'Sales Stage',
827
+ name: 'sales_stage',
828
+ type: 'options',
829
+ default: 'Prospecting',
830
+ options: [
831
+ { name: 'Closed Lost', value: 'Closed Lost' },
832
+ { name: 'Closed Won', value: 'Closed Won' },
833
+ { name: 'Needs Analysis', value: 'Needs Analysis' },
834
+ { name: 'Proposal/Price Quote', value: 'Proposal/Price Quote' },
835
+ { name: 'Prospecting', value: 'Prospecting' },
836
+ { name: 'Qualification', value: 'Qualification' },
837
+ ],
838
+ },
839
+ ],
840
+ },
841
+ // ════════════════════════════════════════════════════════════════════
842
+ // NOTE: Create fields
843
+ // ════════════════════════════════════════════════════════════════════
844
+ {
845
+ displayName: 'Note Name',
846
+ name: 'noteName',
847
+ type: 'string',
848
+ required: true,
849
+ default: '',
850
+ description: 'Short subject/title of the note',
851
+ displayOptions: {
852
+ show: { resource: ['note'], operation: ['createNote'] },
853
+ },
854
+ },
855
+ {
856
+ displayName: 'Note Content',
857
+ name: 'noteContent',
858
+ type: 'string',
859
+ typeOptions: { rows: 4 },
860
+ default: '',
861
+ displayOptions: {
862
+ show: { resource: ['note'], operation: ['createNote'] },
863
+ },
864
+ },
865
+ {
866
+ displayName: 'Parent Type',
867
+ name: 'noteParentType',
868
+ type: 'options',
869
+ default: 'Contacts',
870
+ description: 'Type of record to link this note to',
871
+ displayOptions: {
872
+ show: { resource: ['note'], operation: ['createNote'] },
873
+ },
874
+ options: [
875
+ { name: 'Account', value: 'Accounts' },
876
+ { name: 'Contact', value: 'Contacts' },
877
+ { name: 'Lead', value: 'Leads' },
878
+ { name: 'Opportunity', value: 'Opportunities' },
879
+ ],
880
+ },
881
+ {
882
+ displayName: 'Parent ID',
883
+ name: 'noteParentId',
884
+ type: 'string',
885
+ default: '',
886
+ description: 'ID of the record to link this note to',
887
+ displayOptions: {
888
+ show: { resource: ['note'], operation: ['createNote'] },
889
+ },
890
+ },
891
+ // NOTE: List Activities fields
892
+ {
893
+ displayName: 'Parent Type',
894
+ name: 'listParentType',
895
+ type: 'options',
896
+ default: 'Contacts',
897
+ description: 'Type of parent record to list activities for',
898
+ displayOptions: {
899
+ show: { resource: ['note'], operation: ['listActivities'] },
900
+ },
901
+ options: [
902
+ { name: 'Account', value: 'Accounts' },
903
+ { name: 'Contact', value: 'Contacts' },
904
+ { name: 'Lead', value: 'Leads' },
905
+ { name: 'Opportunity', value: 'Opportunities' },
906
+ ],
907
+ },
908
+ {
909
+ displayName: 'Parent ID',
910
+ name: 'listParentId',
911
+ type: 'string',
912
+ required: true,
913
+ default: '',
914
+ description: 'ID of the parent record',
915
+ displayOptions: {
916
+ show: { resource: ['note'], operation: ['listActivities'] },
917
+ },
918
+ },
919
+ // ════════════════════════════════════════════════════════════════════
920
+ // SEARCH fields (shared Contact + Account)
921
+ // ════════════════════════════════════════════════════════════════════
922
+ {
923
+ displayName: 'Search Field',
924
+ name: 'searchField',
925
+ type: 'string',
926
+ required: true,
927
+ default: 'email1',
928
+ placeholder: 'email1',
929
+ description: 'Field name to filter by (e.g. email1, phone_work, last_name)',
930
+ displayOptions: {
931
+ show: { operation: ['search'] },
932
+ },
933
+ },
934
+ {
935
+ displayName: 'Search Value',
936
+ name: 'searchValue',
937
+ type: 'string',
938
+ required: true,
939
+ default: '',
940
+ displayOptions: {
941
+ show: { operation: ['search'] },
942
+ },
943
+ },
944
+ // ════════════════════════════════════════════════════════════════════
945
+ // GET MANY options
946
+ // ════════════════════════════════════════════════════════════════════
947
+ {
948
+ displayName: 'Return All',
949
+ name: 'returnAll',
950
+ type: 'boolean',
951
+ default: false,
952
+ description: 'Whether to return all results or only up to a given limit',
953
+ displayOptions: {
954
+ show: { operation: ['getMany', 'listActivities'] },
955
+ },
956
+ },
957
+ {
958
+ displayName: 'Limit',
959
+ name: 'limit',
960
+ type: 'number',
961
+ typeOptions: { minValue: 1 },
962
+ default: 50,
963
+ description: 'Max number of results to return',
964
+ displayOptions: {
965
+ show: {
966
+ operation: ['getMany', 'listActivities'],
967
+ returnAll: [false],
968
+ },
969
+ },
970
+ },
971
+ ],
972
+ };
973
+ }
974
+ // ─────────────────────────────────────────────────────────────────────────
975
+ // Execute
976
+ // ─────────────────────────────────────────────────────────────────────────
977
+ async execute() {
978
+ const items = this.getInputData();
979
+ const returnData = [];
980
+ const credentials = await this.getCredentials('suiteCRMApi');
981
+ const baseUrl = credentials.baseUrl.replace(/\/$/, '');
982
+ const clientId = credentials.clientId;
983
+ const clientSecret = credentials.clientSecret;
984
+ // Fetch one token per execution (cache across items)
985
+ const token = await getAccessToken(this, baseUrl, clientId, clientSecret);
986
+ for (let i = 0; i < items.length; i++) {
987
+ const resource = this.getNodeParameter('resource', i);
988
+ const operation = this.getNodeParameter('operation', i);
989
+ let result;
990
+ try {
991
+ // ──────────────────────────────────────────────────────────────
992
+ // CONTACT
993
+ // ──────────────────────────────────────────────────────────────
994
+ if (resource === 'contact') {
995
+ if (operation === 'create') {
996
+ const firstName = this.getNodeParameter('firstName', i, '');
997
+ const lastName = this.getNodeParameter('lastName', i);
998
+ const email = this.getNodeParameter('email', i, '');
999
+ const phone = this.getNodeParameter('phone', i, '');
1000
+ const accountId = this.getNodeParameter('accountId', i, '');
1001
+ const extra = this.getNodeParameter('contactAdditionalFields', i, {});
1002
+ const attributes = {
1003
+ first_name: firstName,
1004
+ last_name: lastName,
1005
+ ...(email && { email1: email }),
1006
+ ...(phone && { phone_work: phone }),
1007
+ ...(accountId && { account_id: accountId }),
1008
+ ...extra,
1009
+ };
1010
+ const body = buildJsonApiBody('Contacts', attributes);
1011
+ const res = await apiRequest(this, token, baseUrl, 'POST', '/modules/Contacts', body);
1012
+ result = extractData(res);
1013
+ }
1014
+ else if (operation === 'get') {
1015
+ const id = this.getNodeParameter('recordId', i);
1016
+ const res = await apiRequest(this, token, baseUrl, 'GET', `/modules/Contacts/${id}`);
1017
+ result = extractData(res);
1018
+ }
1019
+ else if (operation === 'getMany') {
1020
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1021
+ const qs = {};
1022
+ if (!returnAll)
1023
+ qs['page[size]'] = this.getNodeParameter('limit', i, 50);
1024
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Contacts', undefined, qs);
1025
+ result = extractDataList(res);
1026
+ }
1027
+ else if (operation === 'update') {
1028
+ const id = this.getNodeParameter('recordId', i);
1029
+ const updateFields = this.getNodeParameter('contactUpdateFields', i, {});
1030
+ const body = buildJsonApiBody('Contacts', updateFields, id);
1031
+ const res = await apiRequest(this, token, baseUrl, 'PATCH', `/modules/Contacts/${id}`, body);
1032
+ result = extractData(res);
1033
+ }
1034
+ else if (operation === 'delete') {
1035
+ const id = this.getNodeParameter('recordId', i);
1036
+ await apiRequest(this, token, baseUrl, 'DELETE', `/modules/Contacts/${id}`);
1037
+ result = { success: true, id };
1038
+ }
1039
+ else if (operation === 'search') {
1040
+ const field = this.getNodeParameter('searchField', i);
1041
+ const value = this.getNodeParameter('searchValue', i);
1042
+ const qs = { [`filter[${field}][eq]`]: value };
1043
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Contacts', undefined, qs);
1044
+ result = extractDataList(res);
1045
+ }
1046
+ }
1047
+ // ──────────────────────────────────────────────────────────────
1048
+ // ACCOUNT
1049
+ // ──────────────────────────────────────────────────────────────
1050
+ else if (resource === 'account') {
1051
+ if (operation === 'create') {
1052
+ const name = this.getNodeParameter('accountName', i);
1053
+ const extra = this.getNodeParameter('accountAdditionalFields', i, {});
1054
+ const attributes = { name, ...extra };
1055
+ const body = buildJsonApiBody('Accounts', attributes);
1056
+ const res = await apiRequest(this, token, baseUrl, 'POST', '/modules/Accounts', body);
1057
+ result = extractData(res);
1058
+ }
1059
+ else if (operation === 'get') {
1060
+ const id = this.getNodeParameter('recordId', i);
1061
+ const res = await apiRequest(this, token, baseUrl, 'GET', `/modules/Accounts/${id}`);
1062
+ result = extractData(res);
1063
+ }
1064
+ else if (operation === 'getMany') {
1065
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1066
+ const qs = {};
1067
+ if (!returnAll)
1068
+ qs['page[size]'] = this.getNodeParameter('limit', i, 50);
1069
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Accounts', undefined, qs);
1070
+ result = extractDataList(res);
1071
+ }
1072
+ else if (operation === 'update') {
1073
+ const id = this.getNodeParameter('recordId', i);
1074
+ const updateFields = this.getNodeParameter('accountUpdateFields', i, {});
1075
+ const body = buildJsonApiBody('Accounts', updateFields, id);
1076
+ const res = await apiRequest(this, token, baseUrl, 'PATCH', `/modules/Accounts/${id}`, body);
1077
+ result = extractData(res);
1078
+ }
1079
+ else if (operation === 'delete') {
1080
+ const id = this.getNodeParameter('recordId', i);
1081
+ await apiRequest(this, token, baseUrl, 'DELETE', `/modules/Accounts/${id}`);
1082
+ result = { success: true, id };
1083
+ }
1084
+ else if (operation === 'search') {
1085
+ const field = this.getNodeParameter('searchField', i);
1086
+ const value = this.getNodeParameter('searchValue', i);
1087
+ const qs = { [`filter[${field}][eq]`]: value };
1088
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Accounts', undefined, qs);
1089
+ result = extractDataList(res);
1090
+ }
1091
+ }
1092
+ // ──────────────────────────────────────────────────────────────
1093
+ // LEAD
1094
+ // ──────────────────────────────────────────────────────────────
1095
+ else if (resource === 'lead') {
1096
+ if (operation === 'create') {
1097
+ const firstName = this.getNodeParameter('leadFirstName', i, '');
1098
+ const lastName = this.getNodeParameter('leadLastName', i);
1099
+ const company = this.getNodeParameter('leadCompany', i);
1100
+ const email = this.getNodeParameter('leadEmail', i, '');
1101
+ const phone = this.getNodeParameter('leadPhone', i, '');
1102
+ const extra = this.getNodeParameter('leadAdditionalFields', i, {});
1103
+ const attributes = {
1104
+ first_name: firstName,
1105
+ last_name: lastName,
1106
+ account_name: company,
1107
+ ...(email && { email1: email }),
1108
+ ...(phone && { phone_work: phone }),
1109
+ ...extra,
1110
+ };
1111
+ const body = buildJsonApiBody('Leads', attributes);
1112
+ const res = await apiRequest(this, token, baseUrl, 'POST', '/modules/Leads', body);
1113
+ result = extractData(res);
1114
+ }
1115
+ else if (operation === 'get') {
1116
+ const id = this.getNodeParameter('recordId', i);
1117
+ const res = await apiRequest(this, token, baseUrl, 'GET', `/modules/Leads/${id}`);
1118
+ result = extractData(res);
1119
+ }
1120
+ else if (operation === 'getMany') {
1121
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1122
+ const qs = {};
1123
+ if (!returnAll)
1124
+ qs['page[size]'] = this.getNodeParameter('limit', i, 50);
1125
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Leads', undefined, qs);
1126
+ result = extractDataList(res);
1127
+ }
1128
+ else if (operation === 'update') {
1129
+ const id = this.getNodeParameter('recordId', i);
1130
+ const updateFields = this.getNodeParameter('leadUpdateFields', i, {});
1131
+ const body = buildJsonApiBody('Leads', updateFields, id);
1132
+ const res = await apiRequest(this, token, baseUrl, 'PATCH', `/modules/Leads/${id}`, body);
1133
+ result = extractData(res);
1134
+ }
1135
+ else if (operation === 'convert') {
1136
+ // SuiteCRM Lead conversion: mark lead as converted and
1137
+ // create linked records via the Leads convert endpoint
1138
+ const id = this.getNodeParameter('recordId', i);
1139
+ const createContact = this.getNodeParameter('convertCreateContact', i, true);
1140
+ const createAccount = this.getNodeParameter('convertCreateAccount', i, true);
1141
+ const createOpportunity = this.getNodeParameter('convertCreateOpportunity', i, false);
1142
+ // First, fetch the lead to get its data
1143
+ const leadRes = await apiRequest(this, token, baseUrl, 'GET', `/modules/Leads/${id}`);
1144
+ const leadData = extractData(leadRes);
1145
+ const conversionResults = { lead_id: id };
1146
+ if (createAccount) {
1147
+ const accountAttrs = {
1148
+ name: leadData.account_name || `${leadData.first_name} ${leadData.last_name}`,
1149
+ phone_office: leadData.phone_work,
1150
+ email1: leadData.email1,
1151
+ };
1152
+ const accountBody = buildJsonApiBody('Accounts', accountAttrs);
1153
+ const accountRes = await apiRequest(this, token, baseUrl, 'POST', '/modules/Accounts', accountBody);
1154
+ const account = extractData(accountRes);
1155
+ conversionResults.account = account;
1156
+ if (createContact) {
1157
+ const contactAttrs = {
1158
+ first_name: leadData.first_name,
1159
+ last_name: leadData.last_name,
1160
+ email1: leadData.email1,
1161
+ phone_work: leadData.phone_work,
1162
+ account_id: account.id,
1163
+ };
1164
+ const contactBody = buildJsonApiBody('Contacts', contactAttrs);
1165
+ const contactRes = await apiRequest(this, token, baseUrl, 'POST', '/modules/Contacts', contactBody);
1166
+ conversionResults.contact = extractData(contactRes);
1167
+ }
1168
+ if (createOpportunity) {
1169
+ const oppName = this.getNodeParameter('convertOpportunityName', i, '');
1170
+ const oppCloseDate = this.getNodeParameter('convertOpportunityCloseDate', i, '');
1171
+ const oppAmount = this.getNodeParameter('convertOpportunityAmount', i, 0);
1172
+ const oppAttrs = {
1173
+ name: oppName || `Opportunity - ${leadData.first_name} ${leadData.last_name}`,
1174
+ account_id: account.id,
1175
+ sales_stage: 'Prospecting',
1176
+ date_closed: oppCloseDate || new Date(Date.now() + 30 * 24 * 60 * 60 * 1000).toISOString().split('T')[0],
1177
+ amount: oppAmount,
1178
+ };
1179
+ const oppBody = buildJsonApiBody('Opportunities', oppAttrs);
1180
+ const oppRes = await apiRequest(this, token, baseUrl, 'POST', '/modules/Opportunities', oppBody);
1181
+ conversionResults.opportunity = extractData(oppRes);
1182
+ }
1183
+ }
1184
+ // Mark the lead as converted
1185
+ const convertBody = buildJsonApiBody('Leads', { converted: '1' }, id);
1186
+ await apiRequest(this, token, baseUrl, 'PATCH', `/modules/Leads/${id}`, convertBody);
1187
+ conversionResults.converted = true;
1188
+ result = conversionResults;
1189
+ }
1190
+ }
1191
+ // ──────────────────────────────────────────────────────────────
1192
+ // OPPORTUNITY
1193
+ // ──────────────────────────────────────────────────────────────
1194
+ else if (resource === 'opportunity') {
1195
+ if (operation === 'create') {
1196
+ const name = this.getNodeParameter('opportunityName', i);
1197
+ const closeDate = this.getNodeParameter('closeDate', i);
1198
+ const salesStage = this.getNodeParameter('salesStage', i);
1199
+ const extra = this.getNodeParameter('opportunityAdditionalFields', i, {});
1200
+ const attributes = {
1201
+ name,
1202
+ date_closed: closeDate ? closeDate.split('T')[0] : closeDate,
1203
+ sales_stage: salesStage,
1204
+ ...extra,
1205
+ };
1206
+ const body = buildJsonApiBody('Opportunities', attributes);
1207
+ const res = await apiRequest(this, token, baseUrl, 'POST', '/modules/Opportunities', body);
1208
+ result = extractData(res);
1209
+ }
1210
+ else if (operation === 'get') {
1211
+ const id = this.getNodeParameter('recordId', i);
1212
+ const res = await apiRequest(this, token, baseUrl, 'GET', `/modules/Opportunities/${id}`);
1213
+ result = extractData(res);
1214
+ }
1215
+ else if (operation === 'getMany') {
1216
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1217
+ const qs = {};
1218
+ if (!returnAll)
1219
+ qs['page[size]'] = this.getNodeParameter('limit', i, 50);
1220
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Opportunities', undefined, qs);
1221
+ result = extractDataList(res);
1222
+ }
1223
+ else if (operation === 'update') {
1224
+ const id = this.getNodeParameter('recordId', i);
1225
+ const updateFields = this.getNodeParameter('opportunityUpdateFields', i, {});
1226
+ const body = buildJsonApiBody('Opportunities', updateFields, id);
1227
+ const res = await apiRequest(this, token, baseUrl, 'PATCH', `/modules/Opportunities/${id}`, body);
1228
+ result = extractData(res);
1229
+ }
1230
+ else if (operation === 'delete') {
1231
+ const id = this.getNodeParameter('recordId', i);
1232
+ await apiRequest(this, token, baseUrl, 'DELETE', `/modules/Opportunities/${id}`);
1233
+ result = { success: true, id };
1234
+ }
1235
+ }
1236
+ // ──────────────────────────────────────────────────────────────
1237
+ // NOTE / ACTIVITY
1238
+ // ──────────────────────────────────────────────────────────────
1239
+ else if (resource === 'note') {
1240
+ if (operation === 'createNote') {
1241
+ const name = this.getNodeParameter('noteName', i);
1242
+ const description = this.getNodeParameter('noteContent', i, '');
1243
+ const parentType = this.getNodeParameter('noteParentType', i);
1244
+ const parentId = this.getNodeParameter('noteParentId', i, '');
1245
+ const attributes = {
1246
+ name,
1247
+ description,
1248
+ ...(parentType && { parent_type: parentType }),
1249
+ ...(parentId && { parent_id: parentId }),
1250
+ };
1251
+ const body = buildJsonApiBody('Notes', attributes);
1252
+ const res = await apiRequest(this, token, baseUrl, 'POST', '/modules/Notes', body);
1253
+ result = extractData(res);
1254
+ }
1255
+ else if (operation === 'listActivities') {
1256
+ const parentType = this.getNodeParameter('listParentType', i);
1257
+ const parentId = this.getNodeParameter('listParentId', i);
1258
+ const returnAll = this.getNodeParameter('returnAll', i, false);
1259
+ const qs = {
1260
+ 'filter[parent_type][eq]': parentType,
1261
+ 'filter[parent_id][eq]': parentId,
1262
+ };
1263
+ if (!returnAll)
1264
+ qs['page[size]'] = this.getNodeParameter('limit', i, 50);
1265
+ const res = await apiRequest(this, token, baseUrl, 'GET', '/modules/Notes', undefined, qs);
1266
+ result = extractDataList(res);
1267
+ }
1268
+ }
1269
+ // Push result(s) to output
1270
+ if (Array.isArray(result)) {
1271
+ for (const item of result) {
1272
+ returnData.push({ json: item });
1273
+ }
1274
+ }
1275
+ else if (result !== undefined) {
1276
+ returnData.push({ json: result });
1277
+ }
1278
+ }
1279
+ catch (error) {
1280
+ if (this.continueOnFail()) {
1281
+ returnData.push({
1282
+ json: { error: error.message },
1283
+ pairedItem: { item: i },
1284
+ });
1285
+ continue;
1286
+ }
1287
+ throw error;
1288
+ }
1289
+ }
1290
+ return [returnData];
1291
+ }
1292
+ }
1293
+ exports.SuiteCRM = SuiteCRM;
1294
+ //# sourceMappingURL=SuiteCRM.node.js.map