@novacodehq/n8n-nodes-servicefusion 0.1.5

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,1168 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ServiceFusion = void 0;
4
+ const n8n_workflow_1 = require("n8n-workflow");
5
+ const GenericFunctions_1 = require("./GenericFunctions");
6
+ const RESOURCES = ['customer', 'job', 'estimate', 'invoice', 'technician', 'webhook'];
7
+ const OPERATIONS = {
8
+ customer: ['getAll', 'get', 'create', 'update', 'delete', 'search'],
9
+ job: ['getAll', 'get', 'create', 'update', 'delete', 'search', 'getAllPaged', 'batchSync'],
10
+ estimate: ['getAll', 'get', 'create', 'update', 'convertToJob'],
11
+ invoice: ['getAll', 'get', 'create', 'update', 'send'],
12
+ technician: ['getAll', 'get', 'getSchedule', 'assignJob'],
13
+ webhook: ['getAll', 'create', 'delete'],
14
+ };
15
+ function allProperties() {
16
+ const props = [];
17
+ props.push({
18
+ displayName: 'Resource',
19
+ name: 'resource',
20
+ type: 'options',
21
+ noDataExpression: true,
22
+ options: RESOURCES.map((r) => ({ name: r.charAt(0).toUpperCase() + r.slice(1), value: r })),
23
+ default: 'customer',
24
+ });
25
+ props.push(...RESOURCES.map((resource) => ({
26
+ displayName: 'Operation',
27
+ name: 'operation',
28
+ type: 'options',
29
+ noDataExpression: true,
30
+ displayOptions: { show: { resource: [resource] } },
31
+ options: OPERATIONS[resource].map((op) => ({
32
+ name: op
33
+ .replace(/([A-Z])/g, ' $1')
34
+ .replace(/^./, (s) => s.toUpperCase())
35
+ .trim(),
36
+ value: op,
37
+ })),
38
+ default: OPERATIONS[resource][0],
39
+ })));
40
+ const C = 'customer', J = 'job', E = 'estimate', I = 'invoice', T = 'technician', W = 'webhook';
41
+ props.push({
42
+ displayName: 'Customer ID',
43
+ name: 'customerId',
44
+ type: 'string',
45
+ default: '',
46
+ required: true,
47
+ displayOptions: { show: { resource: [C], operation: ['get'] } },
48
+ });
49
+ props.push({
50
+ displayName: 'Limit',
51
+ name: 'limit',
52
+ type: 'number',
53
+ typeOptions: {
54
+ minValue: 1,
55
+ },
56
+ description: 'Max number of results to return',
57
+ default: 50,
58
+ displayOptions: { show: { resource: [C], operation: ['getAll'] } },
59
+ });
60
+ props.push({
61
+ displayName: 'Offset',
62
+ name: 'offset',
63
+ type: 'number',
64
+ default: 0,
65
+ displayOptions: { show: { resource: [C], operation: ['getAll'] } },
66
+ });
67
+ props.push({
68
+ displayName: 'Customer Name',
69
+ name: 'customerName',
70
+ type: 'string',
71
+ default: '',
72
+ required: true,
73
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
74
+ });
75
+ props.push({
76
+ displayName: 'Type',
77
+ name: 'customerType',
78
+ type: 'options',
79
+ default: 'Residential',
80
+ options: [
81
+ { name: 'Residential', value: 'Residential' },
82
+ { name: 'Commercial', value: 'Commercial' },
83
+ ],
84
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
85
+ });
86
+ props.push({
87
+ displayName: 'Email',
88
+ name: 'customerEmail',
89
+ type: 'string',
90
+ default: '',
91
+ displayOptions: { show: { resource: [C], operation: ['create', 'update'] } },
92
+ });
93
+ props.push({
94
+ displayName: 'Phone',
95
+ name: 'customerPhone',
96
+ type: 'string',
97
+ default: '',
98
+ displayOptions: { show: { resource: [C], operation: ['create', 'update'] } },
99
+ });
100
+ props.push({
101
+ displayName: 'Mobile',
102
+ name: 'customerMobile',
103
+ type: 'string',
104
+ default: '',
105
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
106
+ });
107
+ props.push({
108
+ displayName: 'Street Address',
109
+ name: 'customerStreet1',
110
+ type: 'string',
111
+ default: '',
112
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
113
+ });
114
+ props.push({
115
+ displayName: 'City',
116
+ name: 'customerCity',
117
+ type: 'string',
118
+ default: '',
119
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
120
+ });
121
+ props.push({
122
+ displayName: 'State',
123
+ name: 'customerState',
124
+ type: 'string',
125
+ default: '',
126
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
127
+ });
128
+ props.push({
129
+ displayName: 'Zip Code',
130
+ name: 'customerZipCode',
131
+ type: 'string',
132
+ default: '',
133
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
134
+ });
135
+ props.push({
136
+ displayName: 'Additional Fields',
137
+ name: 'additionalCustomerFields',
138
+ type: 'collection',
139
+ default: {},
140
+ displayOptions: { show: { resource: [C], operation: ['create'] } },
141
+ options: [
142
+ { displayName: 'Notes', name: 'notes', type: 'string', default: '' },
143
+ {
144
+ displayName: 'Tags',
145
+ name: 'tags',
146
+ type: 'string',
147
+ default: '',
148
+ placeholder: 'Comma-separated',
149
+ },
150
+ { displayName: 'External ID', name: 'externalId', type: 'string', default: '' },
151
+ ],
152
+ });
153
+ props.push({
154
+ displayName: 'Customer ID',
155
+ name: 'customerId',
156
+ type: 'string',
157
+ default: '',
158
+ required: true,
159
+ displayOptions: { show: { resource: [C], operation: ['update', 'delete'] } },
160
+ });
161
+ props.push({
162
+ displayName: 'Customer Name',
163
+ name: 'customerName',
164
+ type: 'string',
165
+ default: '',
166
+ displayOptions: { show: { resource: [C], operation: ['update'] } },
167
+ });
168
+ props.push({
169
+ displayName: 'Notes',
170
+ name: 'customerNotes',
171
+ type: 'string',
172
+ default: '',
173
+ displayOptions: { show: { resource: [C], operation: ['update'] } },
174
+ });
175
+ props.push({
176
+ displayName: 'Name',
177
+ name: 'searchName',
178
+ type: 'string',
179
+ default: '',
180
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
181
+ });
182
+ props.push({
183
+ displayName: 'Email',
184
+ name: 'searchEmail',
185
+ type: 'string',
186
+ default: '',
187
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
188
+ });
189
+ props.push({
190
+ displayName: 'Phone',
191
+ name: 'searchPhone',
192
+ type: 'string',
193
+ default: '',
194
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
195
+ });
196
+ props.push({
197
+ displayName: 'City',
198
+ name: 'searchCity',
199
+ type: 'string',
200
+ default: '',
201
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
202
+ });
203
+ props.push({
204
+ displayName: 'State',
205
+ name: 'searchState',
206
+ type: 'string',
207
+ default: '',
208
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
209
+ });
210
+ props.push({
211
+ displayName: 'Zip Code',
212
+ name: 'searchZipCode',
213
+ type: 'string',
214
+ default: '',
215
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
216
+ });
217
+ props.push({
218
+ displayName: 'Type',
219
+ name: 'searchType',
220
+ type: 'options',
221
+ default: '',
222
+ options: [
223
+ { name: 'All', value: '' },
224
+ { name: 'Residential', value: 'Residential' },
225
+ { name: 'Commercial', value: 'Commercial' },
226
+ ],
227
+ displayOptions: { show: { resource: [C], operation: ['search'] } },
228
+ });
229
+ props.push({
230
+ displayName: 'Job ID',
231
+ name: 'jobId',
232
+ type: 'string',
233
+ default: '',
234
+ required: true,
235
+ displayOptions: { show: { resource: [J], operation: ['get'] } },
236
+ });
237
+ props.push({
238
+ displayName: 'Customer ID',
239
+ name: 'jobCustomerId',
240
+ type: 'string',
241
+ default: '',
242
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
243
+ });
244
+ props.push({
245
+ displayName: 'Status',
246
+ name: 'jobStatus',
247
+ type: 'multiOptions',
248
+ default: [],
249
+ options: [
250
+ { name: 'Cancelled', value: 'Cancelled' },
251
+ { name: 'Completed', value: 'Completed' },
252
+ { name: 'In Progress', value: 'In Progress' },
253
+ { name: 'On Hold', value: 'On Hold' },
254
+ { name: 'Open', value: 'Open' },
255
+ { name: 'Scheduled', value: 'Scheduled' },
256
+ ],
257
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
258
+ });
259
+ props.push({
260
+ displayName: 'Priority',
261
+ name: 'jobPriority',
262
+ type: 'multiOptions',
263
+ default: [],
264
+ options: [
265
+ { name: 'Low', value: 'Low' },
266
+ { name: 'Medium', value: 'Medium' },
267
+ { name: 'High', value: 'High' },
268
+ { name: 'Emergency', value: 'Emergency' },
269
+ ],
270
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
271
+ });
272
+ props.push({
273
+ displayName: 'Technician ID',
274
+ name: 'jobTechnicianId',
275
+ type: 'string',
276
+ default: '',
277
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
278
+ });
279
+ props.push({
280
+ displayName: 'Scheduled Date From',
281
+ name: 'scheduledDateFrom',
282
+ type: 'dateTime',
283
+ default: '',
284
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
285
+ });
286
+ props.push({
287
+ displayName: 'Scheduled Date To',
288
+ name: 'scheduledDateTo',
289
+ type: 'dateTime',
290
+ default: '',
291
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'search', 'getAllPaged'] } },
292
+ });
293
+ props.push({
294
+ displayName: 'Limit',
295
+ name: 'limit',
296
+ type: 'number',
297
+ typeOptions: {
298
+ minValue: 1,
299
+ },
300
+ description: 'Max number of results to return',
301
+ default: 50,
302
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'getAllPaged'] } },
303
+ });
304
+ props.push({
305
+ displayName: 'Offset',
306
+ name: 'offset',
307
+ type: 'number',
308
+ default: 0,
309
+ displayOptions: { show: { resource: [J], operation: ['getAll', 'getAllPaged'] } },
310
+ });
311
+ props.push({
312
+ displayName: 'Page Size',
313
+ name: 'pageSize',
314
+ type: 'number',
315
+ default: 100,
316
+ displayOptions: { show: { resource: [J], operation: ['getAllPaged'] } },
317
+ });
318
+ props.push({
319
+ displayName: 'Customer ID',
320
+ name: 'jobCustomerId',
321
+ type: 'string',
322
+ default: '',
323
+ required: true,
324
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
325
+ });
326
+ props.push({
327
+ displayName: 'Job Type',
328
+ name: 'jobType',
329
+ type: 'string',
330
+ default: '',
331
+ displayOptions: { show: { resource: [J], operation: ['create', 'update'] } },
332
+ });
333
+ props.push({
334
+ displayName: 'Priority',
335
+ name: 'jobPriority',
336
+ type: 'options',
337
+ default: 'Medium',
338
+ options: [
339
+ { name: 'Low', value: 'Low' },
340
+ { name: 'Medium', value: 'Medium' },
341
+ { name: 'High', value: 'High' },
342
+ { name: 'Emergency', value: 'Emergency' },
343
+ ],
344
+ displayOptions: { show: { resource: [J], operation: ['create', 'update'] } },
345
+ });
346
+ props.push({
347
+ displayName: 'Status',
348
+ name: 'jobStatus',
349
+ type: 'options',
350
+ default: 'Open',
351
+ options: [
352
+ { name: 'Cancelled', value: 'Cancelled' },
353
+ { name: 'Completed', value: 'Completed' },
354
+ { name: 'In Progress', value: 'In Progress' },
355
+ { name: 'On Hold', value: 'On Hold' },
356
+ { name: 'Open', value: 'Open' },
357
+ { name: 'Scheduled', value: 'Scheduled' },
358
+ ],
359
+ displayOptions: { show: { resource: [J], operation: ['create', 'update'] } },
360
+ });
361
+ props.push({
362
+ displayName: 'Description',
363
+ name: 'jobDescription',
364
+ type: 'string',
365
+ default: '',
366
+ displayOptions: { show: { resource: [J], operation: ['create', 'update'] } },
367
+ });
368
+ props.push({
369
+ displayName: 'Scheduled Date',
370
+ name: 'scheduledDate',
371
+ type: 'dateTime',
372
+ default: '',
373
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
374
+ });
375
+ props.push({
376
+ displayName: 'Assigned Technician ID',
377
+ name: 'jobAssignedTo',
378
+ type: 'string',
379
+ default: '',
380
+ displayOptions: { show: { resource: [J], operation: ['create', 'update'] } },
381
+ });
382
+ props.push({
383
+ displayName: 'Street Address',
384
+ name: 'jobStreet1',
385
+ type: 'string',
386
+ default: '',
387
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
388
+ });
389
+ props.push({
390
+ displayName: 'City',
391
+ name: 'jobCity',
392
+ type: 'string',
393
+ default: '',
394
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
395
+ });
396
+ props.push({
397
+ displayName: 'State',
398
+ name: 'jobState',
399
+ type: 'string',
400
+ default: '',
401
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
402
+ });
403
+ props.push({
404
+ displayName: 'Zip Code',
405
+ name: 'jobZipCode',
406
+ type: 'string',
407
+ default: '',
408
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
409
+ });
410
+ props.push({
411
+ displayName: 'Additional Fields',
412
+ name: 'additionalJobFields',
413
+ type: 'collection',
414
+ default: {},
415
+ displayOptions: { show: { resource: [J], operation: ['create'] } },
416
+ options: [
417
+ { displayName: 'Notes', name: 'notes', type: 'string', default: '' },
418
+ { displayName: 'Internal Notes', name: 'internalNotes', type: 'string', default: '' },
419
+ {
420
+ displayName: 'Estimated Duration (Min)',
421
+ name: 'estimatedDuration',
422
+ type: 'number',
423
+ default: 0,
424
+ },
425
+ ],
426
+ });
427
+ props.push({
428
+ displayName: 'Job ID',
429
+ name: 'jobId',
430
+ type: 'string',
431
+ default: '',
432
+ required: true,
433
+ displayOptions: { show: { resource: [J], operation: ['update', 'delete'] } },
434
+ });
435
+ props.push({
436
+ displayName: 'Notes',
437
+ name: 'jobNotes',
438
+ type: 'string',
439
+ default: '',
440
+ displayOptions: { show: { resource: [J], operation: ['update'] } },
441
+ });
442
+ props.push({
443
+ displayName: 'External ID',
444
+ name: 'searchExternalId',
445
+ type: 'string',
446
+ default: '',
447
+ displayOptions: { show: { resource: [J], operation: ['search'] } },
448
+ });
449
+ props.push({
450
+ displayName: 'Description (text search)',
451
+ name: 'searchDescription',
452
+ type: 'string',
453
+ default: '',
454
+ displayOptions: { show: { resource: [J], operation: ['search'] } },
455
+ });
456
+ props.push({
457
+ displayName: 'Jobs (JSON Array)',
458
+ name: 'jobsJson',
459
+ type: 'json',
460
+ default: '',
461
+ typeOptions: { alwaysOpenEditWindow: true },
462
+ displayOptions: { show: { resource: [J], operation: ['batchSync'] } },
463
+ description: 'Array of job objects to sync',
464
+ });
465
+ props.push({
466
+ displayName: 'Match By',
467
+ name: 'matchBy',
468
+ type: 'options',
469
+ default: 'externalId',
470
+ options: [
471
+ { name: 'External ID', value: 'externalId' },
472
+ { name: 'Customer ID', value: 'customerId' },
473
+ { name: 'Description', value: 'description' },
474
+ ],
475
+ displayOptions: { show: { resource: [J], operation: ['batchSync'] } },
476
+ });
477
+ props.push({
478
+ displayName: 'Update Existing',
479
+ name: 'updateExisting',
480
+ type: 'options',
481
+ default: 'true',
482
+ options: [
483
+ { name: 'True', value: 'true' },
484
+ { name: 'False', value: 'false' },
485
+ ],
486
+ displayOptions: { show: { resource: [J], operation: ['batchSync'] } },
487
+ });
488
+ props.push({
489
+ displayName: 'Create New',
490
+ name: 'createNew',
491
+ type: 'options',
492
+ default: 'true',
493
+ options: [
494
+ { name: 'True', value: 'true' },
495
+ { name: 'False', value: 'false' },
496
+ ],
497
+ displayOptions: { show: { resource: [J], operation: ['batchSync'] } },
498
+ });
499
+ props.push({
500
+ displayName: 'Estimate ID',
501
+ name: 'estimateId',
502
+ type: 'string',
503
+ default: '',
504
+ required: true,
505
+ displayOptions: { show: { resource: [E], operation: ['get', 'update', 'convertToJob'] } },
506
+ });
507
+ props.push({
508
+ displayName: 'Job ID (optional filter)',
509
+ name: 'estimateJobId',
510
+ type: 'string',
511
+ default: '',
512
+ displayOptions: { show: { resource: [E], operation: ['getAll'] } },
513
+ });
514
+ props.push({
515
+ displayName: 'Customer ID',
516
+ name: 'estimateCustomerId',
517
+ type: 'string',
518
+ default: '',
519
+ required: true,
520
+ displayOptions: { show: { resource: [E], operation: ['create'] } },
521
+ });
522
+ props.push({
523
+ displayName: 'Job ID',
524
+ name: 'estimateJobId',
525
+ type: 'string',
526
+ default: '',
527
+ displayOptions: { show: { resource: [E], operation: ['create'] } },
528
+ });
529
+ props.push({
530
+ displayName: 'Description',
531
+ name: 'estimateDescription',
532
+ type: 'string',
533
+ default: '',
534
+ displayOptions: { show: { resource: [E], operation: ['create', 'update'] } },
535
+ });
536
+ props.push({
537
+ displayName: 'Invoice ID',
538
+ name: 'invoiceId',
539
+ type: 'string',
540
+ default: '',
541
+ required: true,
542
+ displayOptions: { show: { resource: [I], operation: ['get', 'update', 'send'] } },
543
+ });
544
+ props.push({
545
+ displayName: 'Customer ID',
546
+ name: 'invoiceCustomerId',
547
+ type: 'string',
548
+ default: '',
549
+ displayOptions: { show: { resource: [I], operation: ['getAll'] } },
550
+ });
551
+ props.push({
552
+ displayName: 'Job ID',
553
+ name: 'invoiceJobId',
554
+ type: 'string',
555
+ default: '',
556
+ displayOptions: { show: { resource: [I], operation: ['getAll'] } },
557
+ });
558
+ props.push({
559
+ displayName: 'Status',
560
+ name: 'invoiceStatus',
561
+ type: 'multiOptions',
562
+ default: [],
563
+ options: [
564
+ { name: 'Cancelled', value: 'Cancelled' },
565
+ { name: 'Draft', value: 'Draft' },
566
+ { name: 'Overdue', value: 'Overdue' },
567
+ { name: 'Paid', value: 'Paid' },
568
+ { name: 'Partial', value: 'Partial' },
569
+ { name: 'Sent', value: 'Sent' },
570
+ ],
571
+ displayOptions: { show: { resource: [I], operation: ['getAll'] } },
572
+ });
573
+ props.push({
574
+ displayName: 'Date From',
575
+ name: 'invoiceDateFrom',
576
+ type: 'dateTime',
577
+ default: '',
578
+ displayOptions: { show: { resource: [I], operation: ['getAll'] } },
579
+ });
580
+ props.push({
581
+ displayName: 'Date To',
582
+ name: 'invoiceDateTo',
583
+ type: 'dateTime',
584
+ default: '',
585
+ displayOptions: { show: { resource: [I], operation: ['getAll'] } },
586
+ });
587
+ props.push({
588
+ displayName: 'Customer ID',
589
+ name: 'invoiceCustomerId',
590
+ type: 'string',
591
+ default: '',
592
+ required: true,
593
+ displayOptions: { show: { resource: [I], operation: ['create'] } },
594
+ });
595
+ props.push({
596
+ displayName: 'Job ID',
597
+ name: 'invoiceJobId',
598
+ type: 'string',
599
+ default: '',
600
+ displayOptions: { show: { resource: [I], operation: ['create'] } },
601
+ });
602
+ props.push({
603
+ displayName: 'Description',
604
+ name: 'invoiceDescription',
605
+ type: 'string',
606
+ default: '',
607
+ displayOptions: { show: { resource: [I], operation: ['create', 'update'] } },
608
+ });
609
+ props.push({
610
+ displayName: 'Update Status',
611
+ name: 'invoiceUpdateStatus',
612
+ type: 'options',
613
+ default: 'Draft',
614
+ options: [
615
+ { name: 'Cancelled', value: 'Cancelled' },
616
+ { name: 'Draft', value: 'Draft' },
617
+ { name: 'Overdue', value: 'Overdue' },
618
+ { name: 'Paid', value: 'Paid' },
619
+ { name: 'Partial', value: 'Partial' },
620
+ { name: 'Sent', value: 'Sent' },
621
+ ],
622
+ displayOptions: { show: { resource: [I], operation: ['update'] } },
623
+ });
624
+ props.push({
625
+ displayName: 'Send To Email',
626
+ name: 'sendToEmail',
627
+ type: 'string',
628
+ default: '',
629
+ required: true,
630
+ displayOptions: { show: { resource: [I], operation: ['send'] } },
631
+ });
632
+ props.push({
633
+ displayName: 'Technician ID',
634
+ name: 'technicianId',
635
+ type: 'string',
636
+ default: '',
637
+ required: true,
638
+ displayOptions: { show: { resource: [T], operation: ['get', 'getSchedule'] } },
639
+ });
640
+ props.push({
641
+ displayName: 'Limit',
642
+ name: 'technicianLimit',
643
+ type: 'number',
644
+ default: 100,
645
+ displayOptions: { show: { resource: [T], operation: ['getAll'] } },
646
+ });
647
+ props.push({
648
+ displayName: 'Offset',
649
+ name: 'technicianOffset',
650
+ type: 'number',
651
+ default: 0,
652
+ displayOptions: { show: { resource: [T], operation: ['getAll'] } },
653
+ });
654
+ props.push({
655
+ displayName: 'Schedule Date',
656
+ name: 'scheduleDate',
657
+ type: 'dateTime',
658
+ default: '',
659
+ required: true,
660
+ displayOptions: { show: { resource: [T], operation: ['getSchedule'] } },
661
+ });
662
+ props.push({
663
+ displayName: 'Job ID',
664
+ name: 'assignJobId',
665
+ type: 'string',
666
+ default: '',
667
+ required: true,
668
+ displayOptions: { show: { resource: [T], operation: ['assignJob'] } },
669
+ });
670
+ props.push({
671
+ displayName: 'Technician ID',
672
+ name: 'assignTechnicianId',
673
+ type: 'string',
674
+ default: '',
675
+ required: true,
676
+ displayOptions: { show: { resource: [T], operation: ['assignJob'] } },
677
+ });
678
+ props.push({
679
+ displayName: 'URL',
680
+ name: 'webhookUrl',
681
+ type: 'string',
682
+ default: '',
683
+ required: true,
684
+ description: 'Webhook callback URL',
685
+ displayOptions: { show: { resource: [W], operation: ['create'] } },
686
+ });
687
+ props.push({
688
+ displayName: 'Events (JSON Array)',
689
+ name: 'webhookEvents',
690
+ type: 'json',
691
+ default: '',
692
+ typeOptions: { alwaysOpenEditWindow: true },
693
+ displayOptions: { show: { resource: [W], operation: ['create'] } },
694
+ description: 'Array of event strings',
695
+ });
696
+ props.push({
697
+ displayName: 'Secret',
698
+ name: 'webhookSecret',
699
+ type: 'string',
700
+ typeOptions: { password: true },
701
+ default: '',
702
+ displayOptions: { show: { resource: [W], operation: ['create'] } },
703
+ });
704
+ props.push({
705
+ displayName: 'Webhook ID',
706
+ name: 'webhookId',
707
+ type: 'string',
708
+ default: '',
709
+ required: true,
710
+ displayOptions: { show: { resource: [W], operation: ['delete'] } },
711
+ });
712
+ return props;
713
+ }
714
+ async function executeCustomer(ctx, adapter, operation, itemIndex) {
715
+ var _a, _b, _c, _d;
716
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
717
+ switch (operation) {
718
+ case 'getAll': {
719
+ const r = await adapter.getCustomers({
720
+ limit: p('limit') || undefined,
721
+ offset: p('offset') || undefined,
722
+ });
723
+ return r.map((x) => ({ json: x }));
724
+ }
725
+ case 'get': {
726
+ const r = await adapter.getCustomer(p('customerId'));
727
+ return [{ json: r }];
728
+ }
729
+ case 'create': {
730
+ const d = {};
731
+ if (p('customerName'))
732
+ d.name = p('customerName');
733
+ if (p('customerType'))
734
+ d.type = p('customerType');
735
+ if (p('customerEmail'))
736
+ d.email = p('customerEmail');
737
+ if (p('customerPhone'))
738
+ d.phone = p('customerPhone');
739
+ if (p('customerMobile'))
740
+ d.mobile = p('customerMobile');
741
+ if (p('customerStreet1') || p('customerCity')) {
742
+ d.address = {
743
+ street1: (_a = p('customerStreet1')) !== null && _a !== void 0 ? _a : '',
744
+ city: (_b = p('customerCity')) !== null && _b !== void 0 ? _b : '',
745
+ state: (_c = p('customerState')) !== null && _c !== void 0 ? _c : '',
746
+ zipCode: (_d = p('customerZipCode')) !== null && _d !== void 0 ? _d : '',
747
+ };
748
+ }
749
+ const a = p('additionalCustomerFields', {});
750
+ if (a.notes)
751
+ d.notes = a.notes;
752
+ if (a.tags)
753
+ d.tags = a.tags.split(',').map((s) => s.trim());
754
+ if (a.externalId)
755
+ d.externalId = a.externalId;
756
+ const r = await adapter.createCustomer(d);
757
+ return [{ json: r }];
758
+ }
759
+ case 'update': {
760
+ const id = p('customerId');
761
+ const d = {};
762
+ if (p('customerName'))
763
+ d.name = p('customerName');
764
+ if (p('customerEmail'))
765
+ d.email = p('customerEmail');
766
+ if (p('customerPhone'))
767
+ d.phone = p('customerPhone');
768
+ if (p('customerNotes'))
769
+ d.notes = p('customerNotes');
770
+ const r = await adapter.updateCustomer(id, d);
771
+ return [{ json: r }];
772
+ }
773
+ case 'delete': {
774
+ const id = p('customerId');
775
+ await adapter.deleteCustomer(id);
776
+ return [{ json: { success: true, customerId: id } }];
777
+ }
778
+ case 'search': {
779
+ const f = {};
780
+ if (p('searchName'))
781
+ f.name = p('searchName');
782
+ if (p('searchEmail'))
783
+ f.email = p('searchEmail');
784
+ if (p('searchPhone'))
785
+ f.phone = p('searchPhone');
786
+ if (p('searchCity'))
787
+ f.city = p('searchCity');
788
+ if (p('searchState'))
789
+ f.state = p('searchState');
790
+ if (p('searchZipCode'))
791
+ f.zipCode = p('searchZipCode');
792
+ if (p('searchType'))
793
+ f.type = p('searchType');
794
+ const r = await adapter.searchCustomers(f);
795
+ return r.map((x) => ({ json: x }));
796
+ }
797
+ default:
798
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown customer operation: ${operation}`);
799
+ }
800
+ }
801
+ async function executeJob(ctx, adapter, operation, itemIndex) {
802
+ var _a, _b, _c, _d;
803
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
804
+ switch (operation) {
805
+ case 'getAll': {
806
+ const f = {};
807
+ if (p('jobCustomerId'))
808
+ f.customerId = p('jobCustomerId');
809
+ const js = p('jobStatus');
810
+ if (Array.isArray(js) && js.length)
811
+ f.status = js;
812
+ const jp = p('jobPriority');
813
+ if (Array.isArray(jp) && jp.length)
814
+ f.priority = jp;
815
+ if (p('jobTechnicianId'))
816
+ f.technicianId = p('jobTechnicianId');
817
+ if (p('scheduledDateFrom'))
818
+ f.scheduledDateFrom = new Date(p('scheduledDateFrom'));
819
+ if (p('scheduledDateTo'))
820
+ f.scheduledDateTo = new Date(p('scheduledDateTo'));
821
+ if (p('limit'))
822
+ f.limit = p('limit');
823
+ if (p('offset'))
824
+ f.offset = p('offset');
825
+ const r = await adapter.getJobs(f);
826
+ return r.map((x) => ({ json: x }));
827
+ }
828
+ case 'get': {
829
+ const r = await adapter.getJob(p('jobId'));
830
+ return [{ json: r }];
831
+ }
832
+ case 'create': {
833
+ const d = {};
834
+ if (p('jobCustomerId'))
835
+ d.customerId = p('jobCustomerId');
836
+ if (p('jobType'))
837
+ d.jobType = p('jobType');
838
+ if (p('jobPriority'))
839
+ d.priority = p('jobPriority');
840
+ if (p('jobStatus'))
841
+ d.status = p('jobStatus');
842
+ if (p('jobDescription'))
843
+ d.description = p('jobDescription');
844
+ if (p('scheduledDate'))
845
+ d.scheduledDate = new Date(p('scheduledDate'));
846
+ if (p('jobAssignedTo'))
847
+ d.assignedTo = p('jobAssignedTo');
848
+ if (p('jobStreet1') || p('jobCity')) {
849
+ d.address = {
850
+ street1: (_a = p('jobStreet1')) !== null && _a !== void 0 ? _a : '',
851
+ city: (_b = p('jobCity')) !== null && _b !== void 0 ? _b : '',
852
+ state: (_c = p('jobState')) !== null && _c !== void 0 ? _c : '',
853
+ zipCode: (_d = p('jobZipCode')) !== null && _d !== void 0 ? _d : '',
854
+ };
855
+ }
856
+ const a = p('additionalJobFields', {});
857
+ if (a.notes)
858
+ d.notes = a.notes;
859
+ if (a.internalNotes)
860
+ d.internalNotes = a.internalNotes;
861
+ if (a.estimatedDuration)
862
+ d.estimatedDuration = a.estimatedDuration;
863
+ const r = await adapter.createJob(d);
864
+ return [{ json: r }];
865
+ }
866
+ case 'update': {
867
+ const id = p('jobId');
868
+ const d = {};
869
+ if (p('jobType'))
870
+ d.jobType = p('jobType');
871
+ if (p('jobPriority'))
872
+ d.priority = p('jobPriority');
873
+ if (p('jobStatus'))
874
+ d.status = p('jobStatus');
875
+ if (p('jobDescription'))
876
+ d.description = p('jobDescription');
877
+ if (p('jobNotes'))
878
+ d.notes = p('jobNotes');
879
+ const r = await adapter.updateJob(id, d);
880
+ return [{ json: r }];
881
+ }
882
+ case 'delete': {
883
+ const id = p('jobId');
884
+ await adapter.deleteJob(id);
885
+ return [{ json: { success: true, jobId: id } }];
886
+ }
887
+ case 'search': {
888
+ const f = {};
889
+ if (p('jobCustomerId'))
890
+ f.customerId = p('jobCustomerId');
891
+ const js = p('jobStatus');
892
+ if (Array.isArray(js) && js.length)
893
+ f.status = js;
894
+ const jp = p('jobPriority');
895
+ if (Array.isArray(jp) && jp.length)
896
+ f.priority = jp;
897
+ if (p('jobTechnicianId'))
898
+ f.technicianId = p('jobTechnicianId');
899
+ if (p('scheduledDateFrom'))
900
+ f.scheduledDateFrom = new Date(p('scheduledDateFrom'));
901
+ if (p('scheduledDateTo'))
902
+ f.scheduledDateTo = new Date(p('scheduledDateTo'));
903
+ if (p('searchExternalId'))
904
+ f.externalId = p('searchExternalId');
905
+ if (p('searchDescription'))
906
+ f.description = p('searchDescription');
907
+ const r = await adapter.searchJobs(f);
908
+ return r.map((x) => ({ json: x }));
909
+ }
910
+ case 'getAllPaged': {
911
+ const f = {};
912
+ if (p('jobCustomerId'))
913
+ f.customerId = p('jobCustomerId');
914
+ const js = p('jobStatus');
915
+ if (Array.isArray(js) && js.length)
916
+ f.status = js;
917
+ const jp = p('jobPriority');
918
+ if (Array.isArray(jp) && jp.length)
919
+ f.priority = jp;
920
+ if (p('jobTechnicianId'))
921
+ f.technicianId = p('jobTechnicianId');
922
+ if (p('scheduledDateFrom'))
923
+ f.scheduledDateFrom = new Date(p('scheduledDateFrom'));
924
+ if (p('scheduledDateTo'))
925
+ f.scheduledDateTo = new Date(p('scheduledDateTo'));
926
+ if (p('pageSize'))
927
+ f.pageSize = p('pageSize');
928
+ const r = await adapter.getAllJobs(f);
929
+ return r.map((x) => ({ json: x }));
930
+ }
931
+ case 'batchSync': {
932
+ const raw = p('jobsJson');
933
+ let jobs;
934
+ try {
935
+ jobs = JSON.parse(raw);
936
+ }
937
+ catch {
938
+ throw new n8n_workflow_1.NodeApiError(ctx.getNode(), { message: 'Invalid JSON in Jobs field' });
939
+ }
940
+ const r = await adapter.batchSyncJobs(jobs, {
941
+ matchBy: p('matchBy'),
942
+ updateExisting: p('updateExisting') === 'true',
943
+ createNew: p('createNew') === 'true',
944
+ });
945
+ return [{ json: r }];
946
+ }
947
+ default:
948
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown job operation: ${operation}`);
949
+ }
950
+ }
951
+ async function executeEstimate(ctx, adapter, operation, itemIndex) {
952
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
953
+ switch (operation) {
954
+ case 'getAll': {
955
+ const r = await adapter.getEstimates(p('estimateJobId') || undefined);
956
+ return r.map((x) => ({ json: x }));
957
+ }
958
+ case 'get': {
959
+ const r = await adapter.getEstimate(p('estimateId'));
960
+ return [{ json: r }];
961
+ }
962
+ case 'create': {
963
+ const d = {};
964
+ if (p('estimateCustomerId'))
965
+ d.customerId = p('estimateCustomerId');
966
+ if (p('estimateJobId'))
967
+ d.jobId = p('estimateJobId');
968
+ if (p('estimateDescription'))
969
+ d.description = p('estimateDescription');
970
+ const r = await adapter.createEstimate(d);
971
+ return [{ json: r }];
972
+ }
973
+ case 'update': {
974
+ const id = p('estimateId');
975
+ const d = {};
976
+ if (p('estimateDescription'))
977
+ d.description = p('estimateDescription');
978
+ const r = await adapter.updateEstimate(id, d);
979
+ return [{ json: r }];
980
+ }
981
+ case 'convertToJob': {
982
+ const r = await adapter.convertEstimateToJob(p('estimateId'));
983
+ return [{ json: r }];
984
+ }
985
+ default:
986
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown estimate operation: ${operation}`);
987
+ }
988
+ }
989
+ async function executeInvoice(ctx, adapter, operation, itemIndex) {
990
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
991
+ switch (operation) {
992
+ case 'getAll': {
993
+ const f = {};
994
+ if (p('invoiceCustomerId'))
995
+ f.customerId = p('invoiceCustomerId');
996
+ if (p('invoiceJobId'))
997
+ f.jobId = p('invoiceJobId');
998
+ const s = p('invoiceStatus');
999
+ if (Array.isArray(s) && s.length)
1000
+ f.status = s;
1001
+ if (p('invoiceDateFrom'))
1002
+ f.dateFrom = new Date(p('invoiceDateFrom'));
1003
+ if (p('invoiceDateTo'))
1004
+ f.dateTo = new Date(p('invoiceDateTo'));
1005
+ const r = await adapter.getInvoices(f);
1006
+ return r.map((x) => ({ json: x }));
1007
+ }
1008
+ case 'get': {
1009
+ const r = await adapter.getInvoice(p('invoiceId'));
1010
+ return [{ json: r }];
1011
+ }
1012
+ case 'create': {
1013
+ const d = {};
1014
+ if (p('invoiceCustomerId'))
1015
+ d.customerId = p('invoiceCustomerId');
1016
+ if (p('invoiceJobId'))
1017
+ d.jobId = p('invoiceJobId');
1018
+ if (p('invoiceDescription'))
1019
+ d.description = p('invoiceDescription');
1020
+ const r = await adapter.createInvoice(d);
1021
+ return [{ json: r }];
1022
+ }
1023
+ case 'update': {
1024
+ const id = p('invoiceId');
1025
+ const d = {};
1026
+ if (p('invoiceDescription'))
1027
+ d.description = p('invoiceDescription');
1028
+ if (p('invoiceUpdateStatus'))
1029
+ d.status = p('invoiceUpdateStatus');
1030
+ const r = await adapter.updateInvoice(id, d);
1031
+ return [{ json: r }];
1032
+ }
1033
+ case 'send': {
1034
+ const id = p('invoiceId');
1035
+ const email = p('sendToEmail');
1036
+ await adapter.sendInvoice(id, email);
1037
+ return [{ json: { success: true, invoiceId: id, sentTo: email } }];
1038
+ }
1039
+ default:
1040
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown invoice operation: ${operation}`);
1041
+ }
1042
+ }
1043
+ async function executeTechnician(ctx, adapter, operation, itemIndex) {
1044
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
1045
+ switch (operation) {
1046
+ case 'getAll': {
1047
+ const r = await adapter.getTechnicians({
1048
+ limit: p('technicianLimit') || undefined,
1049
+ offset: p('technicianOffset') || undefined,
1050
+ });
1051
+ return r.map((x) => ({ json: x }));
1052
+ }
1053
+ case 'get': {
1054
+ const r = await adapter.getTechnician(p('technicianId'));
1055
+ return [{ json: r }];
1056
+ }
1057
+ case 'getSchedule': {
1058
+ const r = await adapter.getSchedule(p('technicianId'), new Date(p('scheduleDate')));
1059
+ return [{ json: r }];
1060
+ }
1061
+ case 'assignJob': {
1062
+ const r = await adapter.assignJob(p('assignJobId'), p('assignTechnicianId'));
1063
+ return [{ json: r }];
1064
+ }
1065
+ default:
1066
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown technician operation: ${operation}`);
1067
+ }
1068
+ }
1069
+ async function executeWebhook(ctx, adapter, operation, itemIndex) {
1070
+ const p = (n, f) => ctx.getNodeParameter(n, itemIndex, f);
1071
+ switch (operation) {
1072
+ case 'getAll': {
1073
+ const r = await adapter.getWebhooks();
1074
+ return r.map((x) => ({ json: x }));
1075
+ }
1076
+ case 'create': {
1077
+ const d = {};
1078
+ if (p('webhookUrl'))
1079
+ d.url = p('webhookUrl');
1080
+ if (p('webhookEvents')) {
1081
+ try {
1082
+ d.events = JSON.parse(p('webhookEvents'));
1083
+ }
1084
+ catch {
1085
+ d.events = [p('webhookEvents')];
1086
+ }
1087
+ }
1088
+ if (p('webhookSecret'))
1089
+ d.secret = p('webhookSecret');
1090
+ const r = await adapter.createWebhook(d);
1091
+ return [{ json: r }];
1092
+ }
1093
+ case 'delete': {
1094
+ const id = p('webhookId');
1095
+ await adapter.deleteWebhook(id);
1096
+ return [{ json: { success: true, webhookId: id } }];
1097
+ }
1098
+ default:
1099
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown webhook operation: ${operation}`);
1100
+ }
1101
+ }
1102
+ async function executeOp(ctx, adapter, resource, operation, itemIndex) {
1103
+ switch (resource) {
1104
+ case 'customer':
1105
+ return executeCustomer(ctx, adapter, operation, itemIndex);
1106
+ case 'job':
1107
+ return executeJob(ctx, adapter, operation, itemIndex);
1108
+ case 'estimate':
1109
+ return executeEstimate(ctx, adapter, operation, itemIndex);
1110
+ case 'invoice':
1111
+ return executeInvoice(ctx, adapter, operation, itemIndex);
1112
+ case 'technician':
1113
+ return executeTechnician(ctx, adapter, operation, itemIndex);
1114
+ case 'webhook':
1115
+ return executeWebhook(ctx, adapter, operation, itemIndex);
1116
+ default:
1117
+ throw new n8n_workflow_1.NodeOperationError(ctx.getNode(), `Unknown resource: ${resource}`);
1118
+ }
1119
+ }
1120
+ class ServiceFusion {
1121
+ constructor() {
1122
+ this.description = {
1123
+ displayName: 'ServiceFusion',
1124
+ name: 'serviceFusion',
1125
+ icon: { light: 'file:servicefusion.svg', dark: 'file:servicefusion.dark.svg' },
1126
+ group: ['transform'],
1127
+ version: [1],
1128
+ description: 'Access ServiceFusion field service management API',
1129
+ subtitle: '={{$parameter["operation"]}}',
1130
+ defaults: { name: 'ServiceFusion' },
1131
+ inputs: [n8n_workflow_1.NodeConnectionTypes.Main],
1132
+ outputs: [n8n_workflow_1.NodeConnectionTypes.Main],
1133
+ usableAsTool: true,
1134
+ credentials: [{ name: 'serviceFusionApi', required: true, testedBy: 'serviceFusionApi' }],
1135
+ properties: allProperties(),
1136
+ };
1137
+ }
1138
+ async execute() {
1139
+ const items = this.getInputData();
1140
+ const returnData = [];
1141
+ const credentials = await this.getCredentials('serviceFusionApi');
1142
+ let adapter = null;
1143
+ try {
1144
+ adapter = await (0, GenericFunctions_1.createAdapter)(credentials);
1145
+ const resource = this.getNodeParameter('resource', 0);
1146
+ const operation = this.getNodeParameter('operation', 0);
1147
+ for (let itemIndex = 0; itemIndex < items.length; itemIndex++) {
1148
+ try {
1149
+ const result = await executeOp(this, adapter, resource, operation, itemIndex);
1150
+ returnData.push(...result);
1151
+ }
1152
+ catch (error) {
1153
+ if (this.continueOnFail()) {
1154
+ returnData.push({ json: { error: error.message }, pairedItem: itemIndex });
1155
+ continue;
1156
+ }
1157
+ throw new n8n_workflow_1.NodeOperationError(this.getNode(), error, { itemIndex });
1158
+ }
1159
+ }
1160
+ }
1161
+ finally {
1162
+ await (0, GenericFunctions_1.disconnectAdapter)(adapter);
1163
+ }
1164
+ return [returnData];
1165
+ }
1166
+ }
1167
+ exports.ServiceFusion = ServiceFusion;
1168
+ //# sourceMappingURL=ServiceFusion.node.js.map