@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
@@ -437,76 +437,126 @@ export default class PaymentMethods extends Command {
437
437
  return '';
438
438
  };
439
439
  const results = [];
440
- for (const item of items) {
440
+ // Stream output row-by-row so progress is visible during long imports.
441
+ // Pre-compute the identifier column width from the input items so the
442
+ // table stays aligned without buffering all rows.
443
+ // When stdout is piped (IDE terminals, tee, CI), Node switches to async-buffered
444
+ // mode and rows accumulate in the pipe buffer until it fills. Force blocking
445
+ // mode so each \n-terminated write flushes immediately.
446
+ const stdoutHandle = process.stdout._handle;
447
+ if (stdoutHandle?.setBlocking)
448
+ stdoutHandle.setBlocking(true);
449
+ const outputFormat = flags.output || 'table';
450
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
451
+ const useJson = outputFormat === 'json';
452
+ const statusWidth = 8;
453
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
454
+ if (useCsv) {
455
+ this.log('status,identifier,id,error');
456
+ }
457
+ else if (!useJson) {
458
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
459
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
460
+ }
461
+ const emitRow = (r) => {
462
+ if (useJson)
463
+ return;
464
+ if (useCsv) {
465
+ const err = (r.error || '').replace(/"/g, '""');
466
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
467
+ }
468
+ else {
469
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
470
+ }
471
+ };
472
+ // Server-managed fields the API rejects in create/update bodies. Stripped
473
+ // before every request so re-importing a previously-exported payload Just Works.
474
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
475
+ const stripServerFields = (item) => {
476
+ const out = {};
477
+ for (const [k, v] of Object.entries(item)) {
478
+ if (!STRIP_FIELDS.has(k))
479
+ out[k] = v;
480
+ }
481
+ return out;
482
+ };
483
+ const processItem = async (item) => {
441
484
  const identifier = getItemIdentifier(item);
485
+ const body = stripServerFields(item);
486
+ // If the payload carries an id (e.g. re-importing an export), PATCH directly.
487
+ // Fall through to POST only on 404 (so a missing record still gets created).
488
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
489
+ if (itemId) {
490
+ try {
491
+ const updatePath = updateEndpoint.path.replace('{id}', itemId);
492
+ await apiRequest('PATCH', updatePath, {
493
+ data: body,
494
+ contextName: flags.context,
495
+ });
496
+ return { status: 'updated', id: itemId, identifier };
497
+ }
498
+ catch (error) {
499
+ if (!(error instanceof ApiError && error.status === 404)) {
500
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
501
+ return { status: 'error', id: itemId, identifier, error: msg };
502
+ }
503
+ // 404 → record gone, try create below
504
+ }
505
+ }
442
506
  try {
443
- // Try to create
444
507
  const response = await apiRequest('POST', createEndpoint.path, {
445
- data: item,
508
+ data: body,
446
509
  contextName: flags.context,
447
510
  });
448
511
  const id = response.data?.id;
449
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
512
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
450
513
  }
451
514
  catch (error) {
452
515
  if (error instanceof ApiError && error.status === 409) {
453
- // Conflict - try to update existing
454
516
  const errorData = error.data;
455
517
  const existingId = errorData?.existingId;
456
518
  if (existingId) {
457
519
  try {
458
520
  const updatePath = updateEndpoint.path.replace('{id}', existingId);
459
521
  await apiRequest('PATCH', updatePath, {
460
- data: item,
522
+ data: body,
461
523
  contextName: flags.context,
462
524
  });
463
- results.push({ status: 'updated', id: existingId, identifier });
525
+ return { status: 'updated', id: existingId, identifier };
464
526
  }
465
527
  catch (updateError) {
466
528
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
467
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
529
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
468
530
  }
469
531
  }
470
- else {
471
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
472
- }
473
- }
474
- else {
475
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
476
- results.push({ status: 'error', identifier, error: msg });
532
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
477
533
  }
534
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
535
+ return { status: 'error', identifier, error: msg };
478
536
  }
479
- }
537
+ };
538
+ // Worker pool: keep N requests in flight at all times. Rows are emitted as
539
+ // each request resolves (so output order may differ from input order). The
540
+ // summary at the end remains accurate regardless.
541
+ const concurrency = 4;
542
+ const queue = [...items];
543
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
544
+ for (;;) {
545
+ const item = queue.shift();
546
+ if (!item)
547
+ return;
548
+ const r = await processItem(item);
549
+ results.push(r);
550
+ emitRow(r);
551
+ }
552
+ }));
480
553
  // Summary counts
481
554
  const created = results.filter(r => r.status === 'created').length;
482
555
  const updated = results.filter(r => r.status === 'updated').length;
483
556
  const errors = results.filter(r => r.status === 'error');
484
- // Output results based on format (default: table)
485
- const outputFormat = flags.output || 'table';
486
- if (outputFormat === 'json') {
557
+ if (useJson) {
487
558
  this.log(JSON.stringify(results, null, 2));
488
559
  }
489
- else if (outputFormat === 'csv' || flags.csv) {
490
- // CSV output
491
- this.log('status,identifier,id,error');
492
- for (const r of results) {
493
- const err = (r.error || '').replace(/"/g, '""');
494
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
495
- }
496
- }
497
- else {
498
- // Table output (default)
499
- if (results.length > 0) {
500
- const statusWidth = 8;
501
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
502
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
503
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
504
- for (const r of results) {
505
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
506
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
507
- }
508
- }
509
- }
510
560
  // Always show summary at the end
511
561
  this.log('');
512
562
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
@@ -786,80 +836,122 @@ export default class PaymentMethods extends Command {
786
836
  return '';
787
837
  };
788
838
  const results = [];
789
- for (const item of items) {
839
+ const stdoutHandle = process.stdout._handle;
840
+ if (stdoutHandle?.setBlocking)
841
+ stdoutHandle.setBlocking(true);
842
+ const outputFormat = flags.output || 'table';
843
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
844
+ const useJson = outputFormat === 'json';
845
+ const statusWidth = 8;
846
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
847
+ if (useCsv) {
848
+ this.log('status,identifier,id,error');
849
+ }
850
+ else if (!useJson) {
851
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
852
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
853
+ }
854
+ const emitRow = (r) => {
855
+ if (useJson)
856
+ return;
857
+ if (useCsv) {
858
+ const err = (r.error || '').replace(/"/g, '""');
859
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
860
+ }
861
+ else {
862
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
863
+ }
864
+ };
865
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
866
+ const stripServerFields = (item) => {
867
+ const out = {};
868
+ for (const [k, v] of Object.entries(item)) {
869
+ if (!STRIP_FIELDS.has(k))
870
+ out[k] = v;
871
+ }
872
+ return out;
873
+ };
874
+ const buildUpdatePath = (existingId) => {
875
+ const firstUpdateParam = updateEndpoint.pathParams[0];
876
+ const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
877
+ let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
878
+ if (nestedIdParam) {
879
+ updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
880
+ }
881
+ return updatePath;
882
+ };
883
+ const processItem = async (item) => {
790
884
  const identifier = getItemIdentifier(item);
885
+ const body = stripServerFields(item);
886
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
887
+ if (itemId) {
888
+ try {
889
+ await apiRequest('PATCH', buildUpdatePath(itemId), {
890
+ data: body,
891
+ contextName: flags.context,
892
+ });
893
+ return { status: 'updated', id: itemId, identifier };
894
+ }
895
+ catch (error) {
896
+ if (!(error instanceof ApiError && error.status === 404)) {
897
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
898
+ return { status: 'error', id: itemId, identifier, error: msg };
899
+ }
900
+ // 404 → fall through and try create
901
+ }
902
+ }
791
903
  try {
792
- // Try to create
793
904
  const response = await apiRequest('POST', basePath, {
794
- data: item,
905
+ data: body,
795
906
  contextName: flags.context,
796
907
  });
797
908
  const id = response.data?.id;
798
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
909
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
799
910
  }
800
911
  catch (error) {
801
912
  if (error instanceof ApiError && error.status === 409) {
802
- // Conflict - try to update existing
803
913
  const errorData = error.data;
804
914
  const existingId = errorData?.existingId || item.id;
805
915
  if (existingId) {
806
916
  try {
807
- // Find the nested ID param name (e.g., 'priceId' for prices)
808
- const firstUpdateParam = updateEndpoint.pathParams[0];
809
- const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
810
- let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
811
- if (nestedIdParam) {
812
- updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
813
- }
814
- await apiRequest('PATCH', updatePath, {
815
- data: item,
917
+ await apiRequest('PATCH', buildUpdatePath(existingId), {
918
+ data: body,
816
919
  contextName: flags.context,
817
920
  });
818
- results.push({ status: 'updated', id: existingId, identifier });
921
+ return { status: 'updated', id: existingId, identifier };
819
922
  }
820
923
  catch (updateError) {
821
924
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
822
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
925
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
823
926
  }
824
927
  }
825
- else {
826
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
827
- }
828
- }
829
- else {
830
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
831
- results.push({ status: 'error', identifier, error: msg });
928
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
832
929
  }
930
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
931
+ return { status: 'error', identifier, error: msg };
833
932
  }
834
- }
933
+ };
934
+ // Worker pool: 4 requests in flight at a time. Rows are emitted as each
935
+ // request resolves so output order may differ from input order.
936
+ const concurrency = 4;
937
+ const queue = [...items];
938
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
939
+ for (;;) {
940
+ const item = queue.shift();
941
+ if (!item)
942
+ return;
943
+ const r = await processItem(item);
944
+ results.push(r);
945
+ emitRow(r);
946
+ }
947
+ }));
835
948
  // Summary counts
836
949
  const created = results.filter(r => r.status === 'created').length;
837
950
  const updated = results.filter(r => r.status === 'updated').length;
838
951
  const errors = results.filter(r => r.status === 'error');
839
- // Output results based on format (default: table)
840
- const outputFormat = flags.output || 'table';
841
- if (outputFormat === 'json') {
952
+ if (useJson) {
842
953
  this.log(JSON.stringify(results, null, 2));
843
954
  }
844
- else if (outputFormat === 'csv' || flags.csv) {
845
- this.log('status,identifier,id,error');
846
- for (const r of results) {
847
- const err = (r.error || '').replace(/"/g, '""');
848
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
849
- }
850
- }
851
- else {
852
- if (results.length > 0) {
853
- const statusWidth = 8;
854
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
855
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
856
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
857
- for (const r of results) {
858
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
859
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
860
- }
861
- }
862
- }
863
955
  this.log('');
864
956
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
865
957
  }
@@ -1023,12 +1115,24 @@ export default class PaymentMethods extends Command {
1023
1115
  return { cols: flatten ? cols : cols.slice(0, 8), items: items };
1024
1116
  }
1025
1117
  getValue(row, col) {
1026
- let val = row[col];
1027
- if (col.includes('.')) {
1028
- const parts = col.split('.');
1029
- val = parts.reduce((o, k) => (o && typeof o === 'object' ? o[k] : undefined), row);
1030
- }
1031
- return String(val ?? '');
1118
+ if (!col.includes('.'))
1119
+ return String(row[col] ?? '');
1120
+ const parts = col.split('.');
1121
+ // Walk the dot-path; descending through an array branches into every
1122
+ // element so leaf values get collected from each one. Multiple leaves are
1123
+ // joined with ';' (e.g. dunningReminders.id -> "uuid1;uuid2").
1124
+ const walk = (o, idx) => {
1125
+ if (idx >= parts.length)
1126
+ return [o];
1127
+ if (o == null)
1128
+ return [];
1129
+ if (Array.isArray(o))
1130
+ return o.flatMap(item => walk(item, idx));
1131
+ if (typeof o === 'object')
1132
+ return walk(o[parts[idx]], idx + 1);
1133
+ return [];
1134
+ };
1135
+ return walk(row, 0).filter(v => v !== undefined && v !== null).map(v => String(v)).join(';');
1032
1136
  }
1033
1137
  printTable(data, fields) {
1034
1138
  const { cols, items } = this.getColumns(data, fields);