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