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