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