@aderis-erp/cli 1.0.10 → 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 +178 -86
  3. package/dist/commands/account-transaction-allocations.js +178 -86
  4. package/dist/commands/account-transactions.js +181 -87
  5. package/dist/commands/accounts.js +178 -86
  6. package/dist/commands/agent-runs.d.ts +1 -0
  7. package/dist/commands/agent-runs.js +183 -87
  8. package/dist/commands/audit-log.js +178 -86
  9. package/dist/commands/auth/login.js +4 -3
  10. package/dist/commands/bank-accounts.js +178 -86
  11. package/dist/commands/bank-integrations.js +178 -86
  12. package/dist/commands/business-partners.js +178 -86
  13. package/dist/commands/config/add.js +4 -3
  14. package/dist/commands/credit-card-accounts.js +178 -86
  15. package/dist/commands/documents.js +178 -86
  16. package/dist/commands/dunning-configs.js +180 -88
  17. package/dist/commands/dunning-reminders.js +178 -86
  18. package/dist/commands/dunning-runs.js +178 -86
  19. package/dist/commands/employees.js +180 -88
  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 +178 -86
  23. package/dist/commands/files-internal.js +178 -86
  24. package/dist/commands/health.js +178 -86
  25. package/dist/commands/inbox.js +181 -89
  26. package/dist/commands/integrations.js +178 -86
  27. package/dist/commands/item-categories.js +178 -86
  28. package/dist/commands/items.js +178 -86
  29. package/dist/commands/journal-lines.js +178 -86
  30. package/dist/commands/journals.js +178 -86
  31. package/dist/commands/ledgers.js +178 -86
  32. package/dist/commands/office-locations.js +178 -86
  33. package/dist/commands/payment-methods.js +178 -86
  34. package/dist/commands/payment-runs.js +178 -86
  35. package/dist/commands/payment-terms.js +178 -86
  36. package/dist/commands/price-lists.js +178 -86
  37. package/dist/commands/processor-mappings.js +178 -86
  38. package/dist/commands/processors.js +181 -87
  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 +178 -86
  44. package/dist/commands/salary-types.js +178 -86
  45. package/dist/commands/sales-invoices.js +178 -86
  46. package/dist/commands/sales-orders.js +182 -89
  47. package/dist/commands/saved-views.js +178 -86
  48. package/dist/commands/tags.js +178 -86
  49. package/dist/commands/tax-codes.js +178 -86
  50. package/dist/commands/tax-matrix.js +178 -86
  51. package/dist/commands/tenants.js +178 -86
  52. package/dist/commands/text-templates.js +178 -86
  53. package/dist/commands/tools.js +178 -86
  54. package/dist/commands/vat-declarations.d.ts +1 -0
  55. package/dist/commands/vat-declarations.js +188 -89
  56. package/dist/commands/vendor-invoices.js +178 -86
  57. package/dist/commands/webhooks.js +178 -86
  58. package/dist/commands/workflows.js +178 -86
  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
@@ -444,76 +444,126 @@ export default class Ledgers extends Command {
444
444
  return '';
445
445
  };
446
446
  const results = [];
447
- for (const item of items) {
447
+ // Stream output row-by-row so progress is visible during long imports.
448
+ // Pre-compute the identifier column width from the input items so the
449
+ // table stays aligned without buffering all rows.
450
+ // When stdout is piped (IDE terminals, tee, CI), Node switches to async-buffered
451
+ // mode and rows accumulate in the pipe buffer until it fills. Force blocking
452
+ // mode so each \n-terminated write flushes immediately.
453
+ const stdoutHandle = process.stdout._handle;
454
+ if (stdoutHandle?.setBlocking)
455
+ stdoutHandle.setBlocking(true);
456
+ const outputFormat = flags.output || 'table';
457
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
458
+ const useJson = outputFormat === 'json';
459
+ const statusWidth = 8;
460
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
461
+ if (useCsv) {
462
+ this.log('status,identifier,id,error');
463
+ }
464
+ else if (!useJson) {
465
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
466
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
467
+ }
468
+ const emitRow = (r) => {
469
+ if (useJson)
470
+ return;
471
+ if (useCsv) {
472
+ const err = (r.error || '').replace(/"/g, '""');
473
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
474
+ }
475
+ else {
476
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
477
+ }
478
+ };
479
+ // Server-managed fields the API rejects in create/update bodies. Stripped
480
+ // before every request so re-importing a previously-exported payload Just Works.
481
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
482
+ const stripServerFields = (item) => {
483
+ const out = {};
484
+ for (const [k, v] of Object.entries(item)) {
485
+ if (!STRIP_FIELDS.has(k))
486
+ out[k] = v;
487
+ }
488
+ return out;
489
+ };
490
+ const processItem = async (item) => {
448
491
  const identifier = getItemIdentifier(item);
492
+ const body = stripServerFields(item);
493
+ // If the payload carries an id (e.g. re-importing an export), PATCH directly.
494
+ // Fall through to POST only on 404 (so a missing record still gets created).
495
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
496
+ if (itemId) {
497
+ try {
498
+ const updatePath = updateEndpoint.path.replace('{id}', itemId);
499
+ await apiRequest('PATCH', updatePath, {
500
+ data: body,
501
+ contextName: flags.context,
502
+ });
503
+ return { status: 'updated', id: itemId, identifier };
504
+ }
505
+ catch (error) {
506
+ if (!(error instanceof ApiError && error.status === 404)) {
507
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
508
+ return { status: 'error', id: itemId, identifier, error: msg };
509
+ }
510
+ // 404 → record gone, try create below
511
+ }
512
+ }
449
513
  try {
450
- // Try to create
451
514
  const response = await apiRequest('POST', createEndpoint.path, {
452
- data: item,
515
+ data: body,
453
516
  contextName: flags.context,
454
517
  });
455
518
  const id = response.data?.id;
456
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
519
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
457
520
  }
458
521
  catch (error) {
459
522
  if (error instanceof ApiError && error.status === 409) {
460
- // Conflict - try to update existing
461
523
  const errorData = error.data;
462
524
  const existingId = errorData?.existingId;
463
525
  if (existingId) {
464
526
  try {
465
527
  const updatePath = updateEndpoint.path.replace('{id}', existingId);
466
528
  await apiRequest('PATCH', updatePath, {
467
- data: item,
529
+ data: body,
468
530
  contextName: flags.context,
469
531
  });
470
- results.push({ status: 'updated', id: existingId, identifier });
532
+ return { status: 'updated', id: existingId, identifier };
471
533
  }
472
534
  catch (updateError) {
473
535
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
474
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
536
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
475
537
  }
476
538
  }
477
- else {
478
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
479
- }
480
- }
481
- else {
482
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
483
- results.push({ status: 'error', identifier, error: msg });
539
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
484
540
  }
541
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
542
+ return { status: 'error', identifier, error: msg };
485
543
  }
486
- }
544
+ };
545
+ // Worker pool: keep N requests in flight at all times. Rows are emitted as
546
+ // each request resolves (so output order may differ from input order). The
547
+ // summary at the end remains accurate regardless.
548
+ const concurrency = 4;
549
+ const queue = [...items];
550
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
551
+ for (;;) {
552
+ const item = queue.shift();
553
+ if (!item)
554
+ return;
555
+ const r = await processItem(item);
556
+ results.push(r);
557
+ emitRow(r);
558
+ }
559
+ }));
487
560
  // Summary counts
488
561
  const created = results.filter(r => r.status === 'created').length;
489
562
  const updated = results.filter(r => r.status === 'updated').length;
490
563
  const errors = results.filter(r => r.status === 'error');
491
- // Output results based on format (default: table)
492
- const outputFormat = flags.output || 'table';
493
- if (outputFormat === 'json') {
564
+ if (useJson) {
494
565
  this.log(JSON.stringify(results, null, 2));
495
566
  }
496
- else if (outputFormat === 'csv' || flags.csv) {
497
- // CSV output
498
- this.log('status,identifier,id,error');
499
- for (const r of results) {
500
- const err = (r.error || '').replace(/"/g, '""');
501
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
502
- }
503
- }
504
- else {
505
- // Table output (default)
506
- if (results.length > 0) {
507
- const statusWidth = 8;
508
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
509
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
510
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
511
- for (const r of results) {
512
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
513
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
514
- }
515
- }
516
- }
517
567
  // Always show summary at the end
518
568
  this.log('');
519
569
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
@@ -793,80 +843,122 @@ export default class Ledgers extends Command {
793
843
  return '';
794
844
  };
795
845
  const results = [];
796
- for (const item of items) {
846
+ const stdoutHandle = process.stdout._handle;
847
+ if (stdoutHandle?.setBlocking)
848
+ stdoutHandle.setBlocking(true);
849
+ const outputFormat = flags.output || 'table';
850
+ const useCsv = outputFormat === 'csv' || Boolean(flags.csv);
851
+ const useJson = outputFormat === 'json';
852
+ const statusWidth = 8;
853
+ const identifierWidth = Math.max(10, ...items.map(i => getItemIdentifier(i).length));
854
+ if (useCsv) {
855
+ this.log('status,identifier,id,error');
856
+ }
857
+ else if (!useJson) {
858
+ this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
859
+ this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
860
+ }
861
+ const emitRow = (r) => {
862
+ if (useJson)
863
+ return;
864
+ if (useCsv) {
865
+ const err = (r.error || '').replace(/"/g, '""');
866
+ this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
867
+ }
868
+ else {
869
+ this.log(`${r.status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
870
+ }
871
+ };
872
+ const STRIP_FIELDS = new Set(['id', '_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy']);
873
+ const stripServerFields = (item) => {
874
+ const out = {};
875
+ for (const [k, v] of Object.entries(item)) {
876
+ if (!STRIP_FIELDS.has(k))
877
+ out[k] = v;
878
+ }
879
+ return out;
880
+ };
881
+ const buildUpdatePath = (existingId) => {
882
+ const firstUpdateParam = updateEndpoint.pathParams[0];
883
+ const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
884
+ let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
885
+ if (nestedIdParam) {
886
+ updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
887
+ }
888
+ return updatePath;
889
+ };
890
+ const processItem = async (item) => {
797
891
  const identifier = getItemIdentifier(item);
892
+ const body = stripServerFields(item);
893
+ const itemId = typeof item.id === 'string' ? item.id : undefined;
894
+ if (itemId) {
895
+ try {
896
+ await apiRequest('PATCH', buildUpdatePath(itemId), {
897
+ data: body,
898
+ contextName: flags.context,
899
+ });
900
+ return { status: 'updated', id: itemId, identifier };
901
+ }
902
+ catch (error) {
903
+ if (!(error instanceof ApiError && error.status === 404)) {
904
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
905
+ return { status: 'error', id: itemId, identifier, error: msg };
906
+ }
907
+ // 404 → fall through and try create
908
+ }
909
+ }
798
910
  try {
799
- // Try to create
800
911
  const response = await apiRequest('POST', basePath, {
801
- data: item,
912
+ data: body,
802
913
  contextName: flags.context,
803
914
  });
804
915
  const id = response.data?.id;
805
- results.push({ status: 'created', id: id ? String(id) : undefined, identifier });
916
+ return { status: 'created', id: id ? String(id) : undefined, identifier };
806
917
  }
807
918
  catch (error) {
808
919
  if (error instanceof ApiError && error.status === 409) {
809
- // Conflict - try to update existing
810
920
  const errorData = error.data;
811
921
  const existingId = errorData?.existingId || item.id;
812
922
  if (existingId) {
813
923
  try {
814
- // Find the nested ID param name (e.g., 'priceId' for prices)
815
- const firstUpdateParam = updateEndpoint.pathParams[0];
816
- const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
817
- let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
818
- if (nestedIdParam) {
819
- updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
820
- }
821
- await apiRequest('PATCH', updatePath, {
822
- data: item,
924
+ await apiRequest('PATCH', buildUpdatePath(existingId), {
925
+ data: body,
823
926
  contextName: flags.context,
824
927
  });
825
- results.push({ status: 'updated', id: existingId, identifier });
928
+ return { status: 'updated', id: existingId, identifier };
826
929
  }
827
930
  catch (updateError) {
828
931
  const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
829
- results.push({ status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` });
932
+ return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
830
933
  }
831
934
  }
832
- else {
833
- results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
834
- }
835
- }
836
- else {
837
- const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
838
- results.push({ status: 'error', identifier, error: msg });
935
+ return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
839
936
  }
937
+ const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
938
+ return { status: 'error', identifier, error: msg };
840
939
  }
841
- }
940
+ };
941
+ // Worker pool: 4 requests in flight at a time. Rows are emitted as each
942
+ // request resolves so output order may differ from input order.
943
+ const concurrency = 4;
944
+ const queue = [...items];
945
+ await Promise.all(Array.from({ length: Math.min(concurrency, queue.length) }, async () => {
946
+ for (;;) {
947
+ const item = queue.shift();
948
+ if (!item)
949
+ return;
950
+ const r = await processItem(item);
951
+ results.push(r);
952
+ emitRow(r);
953
+ }
954
+ }));
842
955
  // Summary counts
843
956
  const created = results.filter(r => r.status === 'created').length;
844
957
  const updated = results.filter(r => r.status === 'updated').length;
845
958
  const errors = results.filter(r => r.status === 'error');
846
- // Output results based on format (default: table)
847
- const outputFormat = flags.output || 'table';
848
- if (outputFormat === 'json') {
959
+ if (useJson) {
849
960
  this.log(JSON.stringify(results, null, 2));
850
961
  }
851
- else if (outputFormat === 'csv' || flags.csv) {
852
- this.log('status,identifier,id,error');
853
- for (const r of results) {
854
- const err = (r.error || '').replace(/"/g, '""');
855
- this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
856
- }
857
- }
858
- else {
859
- if (results.length > 0) {
860
- const statusWidth = 8;
861
- const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
862
- this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
863
- this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
864
- for (const r of results) {
865
- const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
866
- this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
867
- }
868
- }
869
- }
870
962
  this.log('');
871
963
  this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
872
964
  }