@aderis-erp/cli 1.0.9 → 1.0.11

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.
Files changed (63) hide show
  1. package/README.md +1 -0
  2. package/dist/commands/access-groups.js +196 -92
  3. package/dist/commands/account-transaction-allocations.js +196 -92
  4. package/dist/commands/account-transactions.js +199 -93
  5. package/dist/commands/accounts.js +196 -92
  6. package/dist/commands/agent-runs.d.ts +1 -0
  7. package/dist/commands/agent-runs.js +201 -93
  8. package/dist/commands/audit-log.js +196 -92
  9. package/dist/commands/auth/login.js +4 -3
  10. package/dist/commands/bank-accounts.js +196 -92
  11. package/dist/commands/bank-integrations.js +196 -92
  12. package/dist/commands/business-partners.js +196 -92
  13. package/dist/commands/config/add.js +4 -3
  14. package/dist/commands/credit-card-accounts.js +196 -92
  15. package/dist/commands/documents.js +196 -92
  16. package/dist/commands/dunning-configs.js +198 -94
  17. package/dist/commands/dunning-reminders.js +196 -92
  18. package/dist/commands/dunning-runs.js +196 -92
  19. package/dist/commands/employees.js +198 -94
  20. package/dist/commands/entity-configurations.d.ts +40 -0
  21. package/dist/commands/entity-configurations.js +1169 -0
  22. package/dist/commands/event-processor-mappings.js +196 -92
  23. package/dist/commands/files-internal.js +196 -92
  24. package/dist/commands/health.js +196 -92
  25. package/dist/commands/inbox.js +199 -95
  26. package/dist/commands/integrations.js +196 -92
  27. package/dist/commands/item-categories.js +196 -92
  28. package/dist/commands/items.js +196 -92
  29. package/dist/commands/journal-lines.js +196 -92
  30. package/dist/commands/journals.js +196 -92
  31. package/dist/commands/ledgers.js +196 -92
  32. package/dist/commands/office-locations.js +196 -92
  33. package/dist/commands/payment-methods.js +196 -92
  34. package/dist/commands/payment-runs.js +196 -92
  35. package/dist/commands/payment-terms.js +196 -92
  36. package/dist/commands/price-lists.js +196 -92
  37. package/dist/commands/processor-mappings.js +196 -92
  38. package/dist/commands/processors.js +199 -93
  39. package/dist/commands/purchase-order-types.d.ts +40 -0
  40. package/dist/commands/purchase-order-types.js +1178 -0
  41. package/dist/commands/purchase-orders.d.ts +40 -0
  42. package/dist/commands/purchase-orders.js +1182 -0
  43. package/dist/commands/reports.js +196 -92
  44. package/dist/commands/salary-types.js +196 -92
  45. package/dist/commands/sales-invoices.js +196 -92
  46. package/dist/commands/sales-orders.js +200 -95
  47. package/dist/commands/saved-views.js +196 -92
  48. package/dist/commands/tags.js +196 -92
  49. package/dist/commands/tax-codes.js +196 -92
  50. package/dist/commands/tax-matrix.js +196 -92
  51. package/dist/commands/tenants.js +196 -92
  52. package/dist/commands/text-templates.js +196 -92
  53. package/dist/commands/tools.js +196 -92
  54. package/dist/commands/vat-declarations.d.ts +1 -0
  55. package/dist/commands/vat-declarations.js +206 -95
  56. package/dist/commands/vendor-invoices.js +196 -92
  57. package/dist/commands/webhooks.js +196 -92
  58. package/dist/commands/workflows.js +196 -92
  59. package/dist/lib/auth.js +2 -2
  60. package/dist/lib/config.d.ts +1 -1
  61. package/dist/lib/config.js +5 -0
  62. package/oclif.manifest.json +516 -9
  63. package/package.json +1 -1
@@ -430,76 +430,126 @@ export default class EventProcessorMappings extends Command {
430
430
  return '';
431
431
  };
432
432
  const results = [];
433
- for (const item of items) {
433
+ // Stream output row-by-row so progress is visible during long imports.
434
+ // Pre-compute the identifier column width from the input items so the
435
+ // table stays aligned without buffering all rows.
436
+ // When stdout is piped (IDE terminals, tee, CI), Node switches to async-buffered
437
+ // mode and rows accumulate in the pipe buffer until it fills. Force blocking
438
+ // mode so each \n-terminated write flushes immediately.
439
+ const stdoutHandle = process.stdout._handle;
440
+ if (stdoutHandle?.setBlocking)
441
+ stdoutHandle.setBlocking(true);
442
+ const outputFormat = flags.output || 'table';
443
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
444
+ const useJson = outputFormat === 'json';
445
+ const statusWidth = 8;
446
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
447
+ if (useCsv) {
448
+ this.log('status,identifier,id,error');
449
+ }
450
+ else if (!useJson) {
451
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
452
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
453
+ }
454
+ const emitRow = (r) => {
455
+ if (useJson)
456
+ return;
457
+ if (useCsv) {
458
+ const err = (r.error || '').replace(/"/g, '""');
459
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
460
+ }
461
+ else {
462
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
463
+ }
464
+ };
465
+ // Server-managed fields the API rejects in create/update bodies. Stripped
466
+ // before every request so re-importing a previously-exported payload Just Works.
467
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
468
+ const stripServerFields = (item) => {
469
+ const out = {};
470
+ for (const [k, v] of Object.entries(item)) {
471
+ if (!STRIP_FIELDS.has(k))
472
+ out[k] = v;
473
+ }
474
+ return out;
475
+ };
476
+ const processItem = async (item) => {
434
477
  const identifier = getItemIdentifier(item);
478
+ const body = stripServerFields(item);
479
+ // If the payload carries an id (e.g. re-importing an export), PATCH directly.
480
+ // Fall through to POST only on 404 (so a missing record still gets created).
481
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
482
+ if (itemId) {
483
+ try {
484
+ const updatePath = updateEndpoint.path.replace('{id}', itemId);
485
+ await apiRequest('PATCH', updatePath, {
486
+ data: body,
487
+ contextName: flags.context,
488
+ });
489
+ return { status: 'updated', id: itemId, identifier };
490
+ }
491
+ catch (error) {
492
+ if (!(error instanceof ApiError && error.status === 404)) {
493
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
494
+ return { status: 'error', id: itemId, identifier, error: msg };
495
+ }
496
+ // 404 → record gone, try create below
497
+ }
498
+ }
435
499
  try {
436
- // Try to create
437
500
  const response = await apiRequest('POST', createEndpoint.path, {
438
- data: item,
501
+ data: body,
439
502
  contextName: flags.context,
440
503
  });
441
504
  const id = response.data?.id;
442
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
505
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
443
506
  }
444
507
  catch (error) {
445
508
  if (error instanceof ApiError && error.status === 409) {
446
- // Conflict - try to update existing
447
509
  const errorData = error.data;
448
510
  const existingId = errorData?.existingId;
449
511
  if (existingId) {
450
512
  try {
451
513
  const updatePath = updateEndpoint.path.replace('{id}', existingId);
452
514
  await apiRequest('PATCH', updatePath, {
453
- data: item,
515
+ data: body,
454
516
  contextName: flags.context,
455
517
  });
456
- results.push({ status: 'updated', id: existingId, identifier });
518
+ return { status: 'updated', id: existingId, identifier };
457
519
  }
458
520
  catch (updateError) {
459
521
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
460
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
522
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
461
523
  }
462
524
  }
463
- else {
464
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
465
- }
466
- }
467
- else {
468
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
469
- results.push({ status: 'error', identifier, error: msg });
525
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
470
526
  }
527
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
528
+ return { status: 'error', identifier, error: msg };
471
529
  }
472
- }
530
+ };
531
+ // Worker pool: keep N requests in flight at all times. Rows are emitted as
532
+ // each request resolves (so output order may differ from input order). The
533
+ // summary at the end remains accurate regardless.
534
+ const concurrency = 4;
535
+ const queue = [...items];
536
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
537
+ for (;;) {
538
+ const item = queue.shift();
539
+ if (!item)
540
+ return;
541
+ const r = await processItem(item);
542
+ results.push(r);
543
+ emitRow(r);
544
+ }
545
+ }));
473
546
  // Summary counts
474
547
  const created = results.filter(r => r.status === 'created').length;
475
548
  const updated = results.filter(r => r.status === 'updated').length;
476
549
  const errors = results.filter(r => r.status === 'error');
477
- // Output results based on format (default: table)
478
- const outputFormat = flags.output || 'table';
479
- if (outputFormat === 'json') {
550
+ if (useJson) {
480
551
  this.log(JSON.stringify(results, null, 2));
481
552
  }
482
- else if (outputFormat === 'csv' || flags.csv) {
483
- // CSV output
484
- this.log('status,identifier,id,error');
485
- for (const r of results) {
486
- const err = (r.error || '').replace(/"/g, '""');
487
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
488
- }
489
- }
490
- else {
491
- // Table output (default)
492
- if (results.length > 0) {
493
- const statusWidth = 8;
494
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
495
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
496
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
497
- for (const r of results) {
498
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
499
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
500
- }
501
- }
502
- }
503
553
  // Always show summary at the end
504
554
  this.log('');
505
555
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
@@ -779,80 +829,122 @@ export default class EventProcessorMappings extends Command {
779
829
  return '';
780
830
  };
781
831
  const results = [];
782
- for (const item of items) {
832
+ const stdoutHandle = process.stdout._handle;
833
+ if (stdoutHandle?.setBlocking)
834
+ stdoutHandle.setBlocking(true);
835
+ const outputFormat = flags.output || 'table';
836
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
837
+ const useJson = outputFormat === 'json';
838
+ const statusWidth = 8;
839
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
840
+ if (useCsv) {
841
+ this.log('status,identifier,id,error');
842
+ }
843
+ else if (!useJson) {
844
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
845
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
846
+ }
847
+ const emitRow = (r) => {
848
+ if (useJson)
849
+ return;
850
+ if (useCsv) {
851
+ const err = (r.error || '').replace(/"/g, '""');
852
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
853
+ }
854
+ else {
855
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
856
+ }
857
+ };
858
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
859
+ const stripServerFields = (item) => {
860
+ const out = {};
861
+ for (const [k, v] of Object.entries(item)) {
862
+ if (!STRIP_FIELDS.has(k))
863
+ out[k] = v;
864
+ }
865
+ return out;
866
+ };
867
+ const buildUpdatePath = (existingId) => {
868
+ const firstUpdateParam = updateEndpoint.pathParams[0];
869
+ const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
870
+ let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
871
+ if (nestedIdParam) {
872
+ updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
873
+ }
874
+ return updatePath;
875
+ };
876
+ const processItem = async (item) => {
783
877
  const identifier = getItemIdentifier(item);
878
+ const body = stripServerFields(item);
879
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
880
+ if (itemId) {
881
+ try {
882
+ await apiRequest('PATCH', buildUpdatePath(itemId), {
883
+ data: body,
884
+ contextName: flags.context,
885
+ });
886
+ return { status: 'updated', id: itemId, identifier };
887
+ }
888
+ catch (error) {
889
+ if (!(error instanceof ApiError && error.status === 404)) {
890
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
891
+ return { status: 'error', id: itemId, identifier, error: msg };
892
+ }
893
+ // 404 → fall through and try create
894
+ }
895
+ }
784
896
  try {
785
- // Try to create
786
897
  const response = await apiRequest('POST', basePath, {
787
- data: item,
898
+ data: body,
788
899
  contextName: flags.context,
789
900
  });
790
901
  const id = response.data?.id;
791
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
902
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
792
903
  }
793
904
  catch (error) {
794
905
  if (error instanceof ApiError && error.status === 409) {
795
- // Conflict - try to update existing
796
906
  const errorData = error.data;
797
907
  const existingId = errorData?.existingId || item.id;
798
908
  if (existingId) {
799
909
  try {
800
- // Find the nested ID param name (e.g., 'priceId' for prices)
801
- const firstUpdateParam = updateEndpoint.pathParams[0];
802
- const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
803
- let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
804
- if (nestedIdParam) {
805
- updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
806
- }
807
- await apiRequest('PATCH', updatePath, {
808
- data: item,
910
+ await apiRequest('PATCH', buildUpdatePath(existingId), {
911
+ data: body,
809
912
  contextName: flags.context,
810
913
  });
811
- results.push({ status: 'updated', id: existingId, identifier });
914
+ return { status: 'updated', id: existingId, identifier };
812
915
  }
813
916
  catch (updateError) {
814
917
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
815
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
918
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
816
919
  }
817
920
  }
818
- else {
819
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
820
- }
821
- }
822
- else {
823
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
824
- results.push({ status: 'error', identifier, error: msg });
921
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
825
922
  }
923
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
924
+ return { status: 'error', identifier, error: msg };
826
925
  }
827
- }
926
+ };
927
+ // Worker pool: 4 requests in flight at a time. Rows are emitted as each
928
+ // request resolves so output order may differ from input order.
929
+ const concurrency = 4;
930
+ const queue = [...items];
931
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
932
+ for (;;) {
933
+ const item = queue.shift();
934
+ if (!item)
935
+ return;
936
+ const r = await processItem(item);
937
+ results.push(r);
938
+ emitRow(r);
939
+ }
940
+ }));
828
941
  // Summary counts
829
942
  const created = results.filter(r => r.status === 'created').length;
830
943
  const updated = results.filter(r => r.status === 'updated').length;
831
944
  const errors = results.filter(r => r.status === 'error');
832
- // Output results based on format (default: table)
833
- const outputFormat = flags.output || 'table';
834
- if (outputFormat === 'json') {
945
+ if (useJson) {
835
946
  this.log(JSON.stringify(results, null, 2));
836
947
  }
837
- else if (outputFormat === 'csv' || flags.csv) {
838
- this.log('status,identifier,id,error');
839
- for (const r of results) {
840
- const err = (r.error || '').replace(/"/g, '""');
841
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
842
- }
843
- }
844
- else {
845
- if (results.length > 0) {
846
- const statusWidth = 8;
847
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
848
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
849
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
850
- for (const r of results) {
851
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
852
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
853
- }
854
- }
855
- }
856
948
  this.log('');
857
949
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
858
950
  }
@@ -1016,12 +1108,24 @@ export default class EventProcessorMappings extends Command {
1016
1108
  return { cols: flatten ? cols : cols.slice(0, 8), items: items };
1017
1109
  }
1018
1110
  getValue(row, col) {
1019
- let val = row[col];
1020
- if (col.includes('.')) {
1021
- const parts = col.split('.');
1022
- val = parts.reduce((o, k) => (o && typeof o === 'object' ? o[k] : undefined), row);
1023
- }
1024
- return String(val ?? '');
1111
+ if (!col.includes('.'))
1112
+ return String(row[col] ?? '');
1113
+ const parts = col.split('.');
1114
+ // Walk the dot-path; descending through an array branches into every
1115
+ // element so leaf values get collected from each one. Multiple leaves are
1116
+ // joined with ';' (e.g. dunningReminders.id -> "uuid1;uuid2").
1117
+ const walk = (o, idx) => {
1118
+ if (idx >= parts.length)
1119
+ return [o];
1120
+ if (o == null)
1121
+ return [];
1122
+ if (Array.isArray(o))
1123
+ return o.flatMap(item => walk(item, idx));
1124
+ if (typeof o === 'object')
1125
+ return walk(o[parts[idx]], idx + 1);
1126
+ return [];
1127
+ };
1128
+ return walk(row, 0).filter(v => v !== undefined && v !== null).map(v => String(v)).join(';');
1025
1129
  }
1026
1130
  printTable(data, fields) {
1027
1131
  const { cols, items } = this.getColumns(data, fields);