@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
- // Document Data
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 ID
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
- // First, authenticate and get token
474
- const loginResponse = await this.helpers.request({
475
- method: 'POST',
476
- url: `${gatewayUrl}/api/account/login`,
477
- body: {
478
- username: credentials.username,
479
- password: credentials.password,
480
- company: credentials.company,
481
- modules: credentials.modules || 'KP',
482
- },
483
- json: true,
484
- });
485
- const token = loginResponse.Token;
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: `${gatewayUrl}/api/product`,
850
+ url,
758
851
  headers: {
759
852
  Authorization: `Bearer ${token}`,
760
853
  },
761
854
  json: true,
762
855
  });
763
- const products = response;
764
- const outputMode = options.outputMode || 'split';
765
- if (outputMode === 'array') {
766
- // Return all products as array in single item
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: { Products: products, TotalCount: products.length }
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
- // Split mode - return each product as separate item
908
+ // Fallback - return entire response
909
+ const result = {
910
+ json: responseData
911
+ };
777
912
  if (items[i].binary) {
778
- // First item gets binary
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pokash/n8n-nodes-optima-rest-api",
3
- "version": "1.1.20",
3
+ "version": "1.1.23",
4
4
  "description": "n8n node for Comarch Optima REST API integration",
5
5
  "keywords": [
6
6
  "n8n-community-node-package",