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