@pokash/n8n-nodes-optima-rest-api 1.1.20 → 1.1.23
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.
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OptimaRestApi = void 0;
|
|
4
4
|
const n8n_workflow_1 = require("n8n-workflow");
|
|
5
|
+
const tokenCache = new Map();
|
|
5
6
|
class OptimaRestApi {
|
|
6
7
|
constructor() {
|
|
7
8
|
this.description = {
|
|
@@ -289,10 +290,16 @@ class OptimaRestApi {
|
|
|
289
290
|
description: 'Create a sales or purchase invoice',
|
|
290
291
|
action: 'Create an invoice',
|
|
291
292
|
},
|
|
293
|
+
{
|
|
294
|
+
name: 'Create Additional Record',
|
|
295
|
+
value: 'createAdditionalRecord',
|
|
296
|
+
description: 'Create additional purchase/sale record (expense/income)',
|
|
297
|
+
action: 'Create an additional record',
|
|
298
|
+
},
|
|
292
299
|
],
|
|
293
300
|
default: 'createInvoice',
|
|
294
301
|
},
|
|
295
|
-
// Document Type
|
|
302
|
+
// Document Type for Invoice
|
|
296
303
|
{
|
|
297
304
|
displayName: 'Document Type',
|
|
298
305
|
name: 'documentType',
|
|
@@ -315,7 +322,30 @@ class OptimaRestApi {
|
|
|
315
322
|
],
|
|
316
323
|
default: 'Sale',
|
|
317
324
|
},
|
|
318
|
-
//
|
|
325
|
+
// Record Type for Additional Record
|
|
326
|
+
{
|
|
327
|
+
displayName: 'Record Type',
|
|
328
|
+
name: 'recordType',
|
|
329
|
+
type: 'options',
|
|
330
|
+
displayOptions: {
|
|
331
|
+
show: {
|
|
332
|
+
resource: ['document'],
|
|
333
|
+
operation: ['createAdditionalRecord'],
|
|
334
|
+
},
|
|
335
|
+
},
|
|
336
|
+
options: [
|
|
337
|
+
{
|
|
338
|
+
name: 'Purchase (Expense)',
|
|
339
|
+
value: 'Purchase',
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
name: 'Sale (Income)',
|
|
343
|
+
value: 'Sale',
|
|
344
|
+
},
|
|
345
|
+
],
|
|
346
|
+
default: 'Purchase',
|
|
347
|
+
},
|
|
348
|
+
// Document Data for Invoice
|
|
319
349
|
{
|
|
320
350
|
displayName: 'Document Data',
|
|
321
351
|
name: 'documentData',
|
|
@@ -330,6 +360,21 @@ class OptimaRestApi {
|
|
|
330
360
|
default: '{\n "platnik": {\n "akronim": "KLIENT01"\n },\n "pozycje": [\n {\n "kod": "PROD01",\n "ilosc": 1,\n "cena": 100\n }\n ]\n}',
|
|
331
361
|
description: 'Document data in JSON format',
|
|
332
362
|
},
|
|
363
|
+
// Record Data for Additional Record
|
|
364
|
+
{
|
|
365
|
+
displayName: 'Record Data',
|
|
366
|
+
name: 'recordData',
|
|
367
|
+
type: 'json',
|
|
368
|
+
required: true,
|
|
369
|
+
displayOptions: {
|
|
370
|
+
show: {
|
|
371
|
+
resource: ['document'],
|
|
372
|
+
operation: ['createAdditionalRecord'],
|
|
373
|
+
},
|
|
374
|
+
},
|
|
375
|
+
default: '{\n "platnik": {\n "akronim": "KLIENT01"\n },\n "pozycje": [\n {\n "kod": "PROD01",\n "ilosc": 1,\n "cena": 100\n }\n ]\n}',
|
|
376
|
+
description: 'Additional record data in JSON format',
|
|
377
|
+
},
|
|
333
378
|
// Product Operations
|
|
334
379
|
{
|
|
335
380
|
displayName: 'Operation',
|
|
@@ -357,22 +402,7 @@ class OptimaRestApi {
|
|
|
357
402
|
],
|
|
358
403
|
default: 'get',
|
|
359
404
|
},
|
|
360
|
-
// Product
|
|
361
|
-
{
|
|
362
|
-
displayName: 'Product ID',
|
|
363
|
-
name: 'productId',
|
|
364
|
-
type: 'number',
|
|
365
|
-
required: true,
|
|
366
|
-
displayOptions: {
|
|
367
|
-
show: {
|
|
368
|
-
resource: ['product'],
|
|
369
|
-
operation: ['get'],
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
default: 0,
|
|
373
|
-
description: 'The ID of the product',
|
|
374
|
-
},
|
|
375
|
-
// Product Options
|
|
405
|
+
// Product Options (for both get and getAll)
|
|
376
406
|
{
|
|
377
407
|
displayName: 'Options',
|
|
378
408
|
name: 'options',
|
|
@@ -382,10 +412,38 @@ class OptimaRestApi {
|
|
|
382
412
|
displayOptions: {
|
|
383
413
|
show: {
|
|
384
414
|
resource: ['product'],
|
|
385
|
-
operation: ['getAll'],
|
|
415
|
+
operation: ['get', 'getAll'],
|
|
386
416
|
},
|
|
387
417
|
},
|
|
388
418
|
options: [
|
|
419
|
+
{
|
|
420
|
+
displayName: 'ID',
|
|
421
|
+
name: 'id',
|
|
422
|
+
type: 'number',
|
|
423
|
+
default: undefined,
|
|
424
|
+
description: 'Product ID to retrieve',
|
|
425
|
+
},
|
|
426
|
+
{
|
|
427
|
+
displayName: 'Filter',
|
|
428
|
+
name: 'filter',
|
|
429
|
+
type: 'string',
|
|
430
|
+
default: '',
|
|
431
|
+
description: 'SQL-like filter expression (e.g., "Twr_Kod=\'PROD01\'" or "Twr_Nazwa LIKE \'%laptop%\'")',
|
|
432
|
+
},
|
|
433
|
+
{
|
|
434
|
+
displayName: 'Skip',
|
|
435
|
+
name: 'skip',
|
|
436
|
+
type: 'number',
|
|
437
|
+
default: 0,
|
|
438
|
+
description: 'Number of records to skip (for pagination)',
|
|
439
|
+
},
|
|
440
|
+
{
|
|
441
|
+
displayName: 'Take',
|
|
442
|
+
name: 'take',
|
|
443
|
+
type: 'number',
|
|
444
|
+
default: 100,
|
|
445
|
+
description: 'Maximum number of records to return (for pagination)',
|
|
446
|
+
},
|
|
389
447
|
{
|
|
390
448
|
displayName: 'Output Mode',
|
|
391
449
|
name: 'outputMode',
|
|
@@ -470,19 +528,34 @@ class OptimaRestApi {
|
|
|
470
528
|
// Get credentials
|
|
471
529
|
const credentials = await this.getCredentials('optimaRestApiCredentials');
|
|
472
530
|
const gatewayUrl = credentials.gatewayUrl;
|
|
473
|
-
//
|
|
474
|
-
const
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
531
|
+
// Create unique cache key based on credentials
|
|
532
|
+
const cacheKey = `${gatewayUrl}:${credentials.username}:${credentials.company}`;
|
|
533
|
+
// Check if we have a valid cached token
|
|
534
|
+
const cached = tokenCache.get(cacheKey);
|
|
535
|
+
const now = Date.now();
|
|
536
|
+
let token;
|
|
537
|
+
if (cached && cached.expiresAt > now) {
|
|
538
|
+
// Token still valid, use from cache
|
|
539
|
+
token = cached.token;
|
|
540
|
+
}
|
|
541
|
+
else {
|
|
542
|
+
// Token expired or doesn't exist, authenticate
|
|
543
|
+
const loginResponse = await this.helpers.request({
|
|
544
|
+
method: 'POST',
|
|
545
|
+
url: `${gatewayUrl}/api/account/login`,
|
|
546
|
+
body: {
|
|
547
|
+
username: credentials.username,
|
|
548
|
+
password: credentials.password,
|
|
549
|
+
company: credentials.company,
|
|
550
|
+
modules: credentials.modules || 'KP',
|
|
551
|
+
},
|
|
552
|
+
json: true,
|
|
553
|
+
});
|
|
554
|
+
token = loginResponse.Token;
|
|
555
|
+
// Cache token for 50 minutes (assuming 60 min expiry, refresh 10 min early)
|
|
556
|
+
const expiresAt = now + (50 * 60 * 1000);
|
|
557
|
+
tokenCache.set(cacheKey, { token, expiresAt });
|
|
558
|
+
}
|
|
486
559
|
for (let i = 0; i < items.length; i++) {
|
|
487
560
|
try {
|
|
488
561
|
if (resource === 'customer') {
|
|
@@ -657,6 +730,26 @@ class OptimaRestApi {
|
|
|
657
730
|
}
|
|
658
731
|
returnData.push(result);
|
|
659
732
|
}
|
|
733
|
+
else if (operation === 'createAdditionalRecord') {
|
|
734
|
+
const recordType = this.getNodeParameter('recordType', i);
|
|
735
|
+
const recordData = JSON.parse(this.getNodeParameter('recordData', i));
|
|
736
|
+
const response = await this.helpers.request({
|
|
737
|
+
method: 'POST',
|
|
738
|
+
url: `${gatewayUrl}/api/Documents/AdditionalRecords/${recordType}`,
|
|
739
|
+
headers: {
|
|
740
|
+
Authorization: `Bearer ${token}`,
|
|
741
|
+
},
|
|
742
|
+
body: recordData,
|
|
743
|
+
json: true,
|
|
744
|
+
});
|
|
745
|
+
const result = {
|
|
746
|
+
json: response
|
|
747
|
+
};
|
|
748
|
+
if (items[i].binary) {
|
|
749
|
+
result.binary = items[i].binary;
|
|
750
|
+
}
|
|
751
|
+
returnData.push(result);
|
|
752
|
+
}
|
|
660
753
|
}
|
|
661
754
|
else if (resource === 'dictionary') {
|
|
662
755
|
const options = this.getNodeParameter('options', i, {});
|
|
@@ -732,40 +825,79 @@ class OptimaRestApi {
|
|
|
732
825
|
}
|
|
733
826
|
}
|
|
734
827
|
else if (resource === 'product') {
|
|
735
|
-
if (operation === 'get') {
|
|
736
|
-
const productId = this.getNodeParameter('productId', i);
|
|
737
|
-
const response = await this.helpers.request({
|
|
738
|
-
method: 'GET',
|
|
739
|
-
url: `${gatewayUrl}/api/product?id=${productId}`,
|
|
740
|
-
headers: {
|
|
741
|
-
Authorization: `Bearer ${token}`,
|
|
742
|
-
},
|
|
743
|
-
json: true,
|
|
744
|
-
});
|
|
745
|
-
const result = {
|
|
746
|
-
json: response
|
|
747
|
-
};
|
|
748
|
-
if (items[i].binary) {
|
|
749
|
-
result.binary = items[i].binary;
|
|
750
|
-
}
|
|
751
|
-
returnData.push(result);
|
|
752
|
-
}
|
|
753
|
-
else if (operation === 'getAll') {
|
|
828
|
+
if (operation === 'get' || operation === 'getAll') {
|
|
754
829
|
const options = this.getNodeParameter('options', i, {});
|
|
830
|
+
// Build query parameters (use PascalCase to match C# API)
|
|
831
|
+
const queryParams = new URLSearchParams();
|
|
832
|
+
if (options.id) {
|
|
833
|
+
queryParams.append('Id', String(options.id));
|
|
834
|
+
}
|
|
835
|
+
if (options.filter) {
|
|
836
|
+
queryParams.append('Filter', String(options.filter));
|
|
837
|
+
}
|
|
838
|
+
if (options.skip) {
|
|
839
|
+
queryParams.append('Skip', String(options.skip));
|
|
840
|
+
}
|
|
841
|
+
if (options.take) {
|
|
842
|
+
queryParams.append('Take', String(options.take));
|
|
843
|
+
}
|
|
844
|
+
const queryString = queryParams.toString();
|
|
845
|
+
const url = queryString
|
|
846
|
+
? `${gatewayUrl}/api/product?${queryString}`
|
|
847
|
+
: `${gatewayUrl}/api/product`;
|
|
755
848
|
const response = await this.helpers.request({
|
|
756
849
|
method: 'GET',
|
|
757
|
-
url
|
|
850
|
+
url,
|
|
758
851
|
headers: {
|
|
759
852
|
Authorization: `Bearer ${token}`,
|
|
760
853
|
},
|
|
761
854
|
json: true,
|
|
762
855
|
});
|
|
763
|
-
|
|
764
|
-
const
|
|
765
|
-
|
|
766
|
-
|
|
856
|
+
// API returns { Success: true, Products: [...], TotalCount: ... }
|
|
857
|
+
const responseData = response;
|
|
858
|
+
// Extract products array from response
|
|
859
|
+
if (responseData.Products && Array.isArray(responseData.Products)) {
|
|
860
|
+
const products = responseData.Products;
|
|
861
|
+
const outputMode = options.outputMode || 'split';
|
|
862
|
+
if (outputMode === 'array') {
|
|
863
|
+
// Return entire response as single item
|
|
864
|
+
const result = {
|
|
865
|
+
json: responseData
|
|
866
|
+
};
|
|
867
|
+
if (items[i].binary) {
|
|
868
|
+
result.binary = items[i].binary;
|
|
869
|
+
}
|
|
870
|
+
returnData.push(result);
|
|
871
|
+
}
|
|
872
|
+
else {
|
|
873
|
+
// Split mode - return each product as separate item
|
|
874
|
+
if (items[i].binary) {
|
|
875
|
+
// First item gets binary
|
|
876
|
+
returnData.push({
|
|
877
|
+
json: products[0] || {},
|
|
878
|
+
binary: items[i].binary
|
|
879
|
+
});
|
|
880
|
+
// Remaining items without binary
|
|
881
|
+
products.slice(1).forEach((item) => {
|
|
882
|
+
returnData.push({
|
|
883
|
+
json: item
|
|
884
|
+
});
|
|
885
|
+
});
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
// No binary - just add all products normally
|
|
889
|
+
products.forEach((item) => {
|
|
890
|
+
returnData.push({
|
|
891
|
+
json: item
|
|
892
|
+
});
|
|
893
|
+
});
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
else if (responseData.Products) {
|
|
898
|
+
// Single product - preserve binary
|
|
767
899
|
const result = {
|
|
768
|
-
json:
|
|
900
|
+
json: responseData.Products
|
|
769
901
|
};
|
|
770
902
|
if (items[i].binary) {
|
|
771
903
|
result.binary = items[i].binary;
|
|
@@ -773,28 +905,14 @@ class OptimaRestApi {
|
|
|
773
905
|
returnData.push(result);
|
|
774
906
|
}
|
|
775
907
|
else {
|
|
776
|
-
//
|
|
908
|
+
// Fallback - return entire response
|
|
909
|
+
const result = {
|
|
910
|
+
json: responseData
|
|
911
|
+
};
|
|
777
912
|
if (items[i].binary) {
|
|
778
|
-
|
|
779
|
-
returnData.push({
|
|
780
|
-
json: products[0] || {},
|
|
781
|
-
binary: items[i].binary
|
|
782
|
-
});
|
|
783
|
-
// Remaining items without binary
|
|
784
|
-
products.slice(1).forEach((item) => {
|
|
785
|
-
returnData.push({
|
|
786
|
-
json: item
|
|
787
|
-
});
|
|
788
|
-
});
|
|
789
|
-
}
|
|
790
|
-
else {
|
|
791
|
-
// No binary - just add all products normally
|
|
792
|
-
products.forEach((item) => {
|
|
793
|
-
returnData.push({
|
|
794
|
-
json: item
|
|
795
|
-
});
|
|
796
|
-
});
|
|
913
|
+
result.binary = items[i].binary;
|
|
797
914
|
}
|
|
915
|
+
returnData.push(result);
|
|
798
916
|
}
|
|
799
917
|
}
|
|
800
918
|
}
|