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