@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.
- package/README.md +1 -0
- package/dist/commands/access-groups.js +196 -92
- package/dist/commands/account-transaction-allocations.js +196 -92
- package/dist/commands/account-transactions.js +199 -93
- package/dist/commands/accounts.js +196 -92
- package/dist/commands/agent-runs.d.ts +1 -0
- package/dist/commands/agent-runs.js +201 -93
- package/dist/commands/audit-log.js +196 -92
- package/dist/commands/auth/login.js +4 -3
- package/dist/commands/bank-accounts.js +196 -92
- package/dist/commands/bank-integrations.js +196 -92
- package/dist/commands/business-partners.js +196 -92
- package/dist/commands/config/add.js +4 -3
- package/dist/commands/credit-card-accounts.js +196 -92
- package/dist/commands/documents.js +196 -92
- package/dist/commands/dunning-configs.js +198 -94
- package/dist/commands/dunning-reminders.js +196 -92
- package/dist/commands/dunning-runs.js +196 -92
- package/dist/commands/employees.js +198 -94
- package/dist/commands/entity-configurations.d.ts +40 -0
- package/dist/commands/entity-configurations.js +1169 -0
- package/dist/commands/event-processor-mappings.js +196 -92
- package/dist/commands/files-internal.js +196 -92
- package/dist/commands/health.js +196 -92
- package/dist/commands/inbox.js +199 -95
- package/dist/commands/integrations.js +196 -92
- package/dist/commands/item-categories.js +196 -92
- package/dist/commands/items.js +196 -92
- package/dist/commands/journal-lines.js +196 -92
- package/dist/commands/journals.js +196 -92
- package/dist/commands/ledgers.js +196 -92
- package/dist/commands/office-locations.js +196 -92
- package/dist/commands/payment-methods.js +196 -92
- package/dist/commands/payment-runs.js +196 -92
- package/dist/commands/payment-terms.js +196 -92
- package/dist/commands/price-lists.js +196 -92
- package/dist/commands/processor-mappings.js +196 -92
- package/dist/commands/processors.js +199 -93
- package/dist/commands/purchase-order-types.d.ts +40 -0
- package/dist/commands/purchase-order-types.js +1178 -0
- package/dist/commands/purchase-orders.d.ts +40 -0
- package/dist/commands/purchase-orders.js +1182 -0
- package/dist/commands/reports.js +196 -92
- package/dist/commands/salary-types.js +196 -92
- package/dist/commands/sales-invoices.js +196 -92
- package/dist/commands/sales-orders.js +200 -95
- package/dist/commands/saved-views.js +196 -92
- package/dist/commands/tags.js +196 -92
- package/dist/commands/tax-codes.js +196 -92
- package/dist/commands/tax-matrix.js +196 -92
- package/dist/commands/tenants.js +196 -92
- package/dist/commands/text-templates.js +196 -92
- package/dist/commands/tools.js +196 -92
- package/dist/commands/vat-declarations.d.ts +1 -0
- package/dist/commands/vat-declarations.js +206 -95
- package/dist/commands/vendor-invoices.js +196 -92
- package/dist/commands/webhooks.js +196 -92
- package/dist/commands/workflows.js +196 -92
- package/dist/lib/auth.js +2 -2
- package/dist/lib/config.d.ts +1 -1
- package/dist/lib/config.js +5 -0
- package/oclif.manifest.json +516 -9
- package/package.json +1 -1
|
@@ -5,8 +5,8 @@ import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
|
5
5
|
const HAS_IMPORT = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
|
-
const NESTED_IMPORTS = [];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "preview", "get", "update", "delete", "regenerate", "replace", "fields-contributions", "fields-contributions-export", "approve", "mark-as-filed", "xml"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
8
|
+
const NESTED_IMPORTS = ["files"];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "preview", "get", "update", "delete", "regenerate", "replace", "fields-contributions", "fields-contributions-export", "approve", "mark-as-filed", "xml", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/vat-declarations', summary: 'List VAT reports (paginated)', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
12
|
'create': { method: 'POST', path: '/v1/vat-declarations', summary: 'Create a VAT declaration report (always async-accepted)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -19,8 +19,14 @@ const ENDPOINTS = {
|
|
|
19
19
|
'fields-contributions': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions', summary: 'Drill into the contributions for a single Ziffer', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
20
|
'fields-contributions-export': { method: 'GET', path: '/v1/vat-declarations/{id}/fields/{fieldId}/contributions/export', summary: 'Export the drill-down result set as an Excel workbook', pathParams: ['id', 'fieldId'], hasBody: false, isMultipart: false, isBinaryResponse: true, bodyArrayFields: [] },
|
|
21
21
|
'approve': { method: 'POST', path: '/v1/vat-declarations/{id}/approve', summary: 'Approve a VAT report and produce the eCH-0217 XML submission file.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'mark-as-filed': { method: 'POST', path: '/v1/vat-declarations/{id}/mark-as-filed', summary: '
|
|
22
|
+
'mark-as-filed': { method: 'POST', path: '/v1/vat-declarations/{id}/mark-as-filed', summary: 'Mark a FINALIZED VAT declaration as filed.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
23
|
'xml': { method: 'GET', path: '/v1/vat-declarations/{id}/xml', summary: 'Download the eCH-0217 XML submission file for a FINALIZED or FILED VAT report.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: true, bodyArrayFields: [] },
|
|
24
|
+
'list-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files', summary: 'List file attachments for a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
|
+
'create-files': { method: 'POST', path: '/v1/vat-declarations/{parentId}/files', summary: 'Upload a file attachment to a VAT declaration', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
|
+
'get-files': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Get a file attachment for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
|
+
'update-files': { method: 'PATCH', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
|
+
'delete-files': { method: 'DELETE', path: '/v1/vat-declarations/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
+
'files-download': { method: 'GET', path: '/v1/vat-declarations/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a VAT declaration', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
30
|
};
|
|
25
31
|
export default class VatDeclarations extends Command {
|
|
26
32
|
static description = 'Manage vat declarations';
|
|
@@ -45,6 +51,7 @@ export default class VatDeclarations extends Command {
|
|
|
45
51
|
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
46
52
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
47
53
|
'field-id': Flags.string({ description: 'Path param: fieldId' }),
|
|
54
|
+
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
48
55
|
page: Flags.string({ description: 'Page number' }),
|
|
49
56
|
limit: Flags.string({ description: 'Items per page' }),
|
|
50
57
|
include: Flags.string({ description: 'Relations to include' }),
|
|
@@ -440,76 +447,126 @@ export default class VatDeclarations extends Command {
|
|
|
440
447
|
return '';
|
|
441
448
|
};
|
|
442
449
|
const results = [];
|
|
443
|
-
|
|
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) => {
|
|
444
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
|
+
}
|
|
445
516
|
try {
|
|
446
|
-
// Try to create
|
|
447
517
|
const response = await apiRequest('POST', createEndpoint.path, {
|
|
448
|
-
data:
|
|
518
|
+
data: body,
|
|
449
519
|
contextName: flags.context,
|
|
450
520
|
});
|
|
451
521
|
const id = response.data?.id;
|
|
452
|
-
|
|
522
|
+
return { status: 'created', id: id ? String(id) : undefined, identifier };
|
|
453
523
|
}
|
|
454
524
|
catch (error) {
|
|
455
525
|
if (error instanceof ApiError && error.status === 409) {
|
|
456
|
-
// Conflict - try to update existing
|
|
457
526
|
const errorData = error.data;
|
|
458
527
|
const existingId = errorData?.existingId;
|
|
459
528
|
if (existingId) {
|
|
460
529
|
try {
|
|
461
530
|
const updatePath = updateEndpoint.path.replace('{id}', existingId);
|
|
462
531
|
await apiRequest('PATCH', updatePath, {
|
|
463
|
-
data:
|
|
532
|
+
data: body,
|
|
464
533
|
contextName: flags.context,
|
|
465
534
|
});
|
|
466
|
-
|
|
535
|
+
return { status: 'updated', id: existingId, identifier };
|
|
467
536
|
}
|
|
468
537
|
catch (updateError) {
|
|
469
538
|
const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
|
|
470
|
-
|
|
539
|
+
return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
|
|
471
540
|
}
|
|
472
541
|
}
|
|
473
|
-
|
|
474
|
-
results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
|
|
475
|
-
}
|
|
476
|
-
}
|
|
477
|
-
else {
|
|
478
|
-
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
479
|
-
results.push({ status: 'error', identifier, error: msg });
|
|
542
|
+
return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
|
|
480
543
|
}
|
|
544
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
545
|
+
return { status: 'error', identifier, error: msg };
|
|
481
546
|
}
|
|
482
|
-
}
|
|
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
|
+
}));
|
|
483
563
|
// Summary counts
|
|
484
564
|
const created = results.filter(r => r.status === 'created').length;
|
|
485
565
|
const updated = results.filter(r => r.status === 'updated').length;
|
|
486
566
|
const errors = results.filter(r => r.status === 'error');
|
|
487
|
-
|
|
488
|
-
const outputFormat = flags.output || 'table';
|
|
489
|
-
if (outputFormat === 'json') {
|
|
567
|
+
if (useJson) {
|
|
490
568
|
this.log(JSON.stringify(results, null, 2));
|
|
491
569
|
}
|
|
492
|
-
else if (outputFormat === 'csv' || flags.csv) {
|
|
493
|
-
// CSV output
|
|
494
|
-
this.log('status,identifier,id,error');
|
|
495
|
-
for (const r of results) {
|
|
496
|
-
const err = (r.error || '').replace(/"/g, '""');
|
|
497
|
-
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
else {
|
|
501
|
-
// Table output (default)
|
|
502
|
-
if (results.length > 0) {
|
|
503
|
-
const statusWidth = 8;
|
|
504
|
-
const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
|
|
505
|
-
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
506
|
-
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
507
|
-
for (const r of results) {
|
|
508
|
-
const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
|
|
509
|
-
this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
510
|
-
}
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
570
|
// Always show summary at the end
|
|
514
571
|
this.log('');
|
|
515
572
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
@@ -789,80 +846,122 @@ export default class VatDeclarations extends Command {
|
|
|
789
846
|
return '';
|
|
790
847
|
};
|
|
791
848
|
const results = [];
|
|
792
|
-
|
|
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) => {
|
|
793
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
|
+
}
|
|
794
913
|
try {
|
|
795
|
-
// Try to create
|
|
796
914
|
const response = await apiRequest('POST', basePath, {
|
|
797
|
-
data:
|
|
915
|
+
data: body,
|
|
798
916
|
contextName: flags.context,
|
|
799
917
|
});
|
|
800
918
|
const id = response.data?.id;
|
|
801
|
-
|
|
919
|
+
return { status: 'created', id: id ? String(id) : undefined, identifier };
|
|
802
920
|
}
|
|
803
921
|
catch (error) {
|
|
804
922
|
if (error instanceof ApiError && error.status === 409) {
|
|
805
|
-
// Conflict - try to update existing
|
|
806
923
|
const errorData = error.data;
|
|
807
924
|
const existingId = errorData?.existingId || item.id;
|
|
808
925
|
if (existingId) {
|
|
809
926
|
try {
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
const nestedIdParam = updateEndpoint.pathParams.find((p, i) => i > 0);
|
|
813
|
-
let updatePath = updateEndpoint.path.replace(`{${firstUpdateParam}}`, parentId);
|
|
814
|
-
if (nestedIdParam) {
|
|
815
|
-
updatePath = updatePath.replace(`{${nestedIdParam}}`, existingId);
|
|
816
|
-
}
|
|
817
|
-
await apiRequest('PATCH', updatePath, {
|
|
818
|
-
data: item,
|
|
927
|
+
await apiRequest('PATCH', buildUpdatePath(existingId), {
|
|
928
|
+
data: body,
|
|
819
929
|
contextName: flags.context,
|
|
820
930
|
});
|
|
821
|
-
|
|
931
|
+
return { status: 'updated', id: existingId, identifier };
|
|
822
932
|
}
|
|
823
933
|
catch (updateError) {
|
|
824
934
|
const msg = updateError instanceof ApiError ? updateError.message : String(updateError);
|
|
825
|
-
|
|
935
|
+
return { status: 'error', id: existingId, identifier, error: `Update failed: ${msg}` };
|
|
826
936
|
}
|
|
827
937
|
}
|
|
828
|
-
|
|
829
|
-
results.push({ status: 'error', identifier, error: 'Conflict but no existingId returned' });
|
|
830
|
-
}
|
|
831
|
-
}
|
|
832
|
-
else {
|
|
833
|
-
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
834
|
-
results.push({ status: 'error', identifier, error: msg });
|
|
938
|
+
return { status: 'error', identifier, error: 'Conflict but no existingId returned' };
|
|
835
939
|
}
|
|
940
|
+
const msg = error instanceof ApiError ? `(${error.status}) ${error.message}` : String(error);
|
|
941
|
+
return { status: 'error', identifier, error: msg };
|
|
836
942
|
}
|
|
837
|
-
}
|
|
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
|
+
}));
|
|
838
958
|
// Summary counts
|
|
839
959
|
const created = results.filter(r => r.status === 'created').length;
|
|
840
960
|
const updated = results.filter(r => r.status === 'updated').length;
|
|
841
961
|
const errors = results.filter(r => r.status === 'error');
|
|
842
|
-
|
|
843
|
-
const outputFormat = flags.output || 'table';
|
|
844
|
-
if (outputFormat === 'json') {
|
|
962
|
+
if (useJson) {
|
|
845
963
|
this.log(JSON.stringify(results, null, 2));
|
|
846
964
|
}
|
|
847
|
-
else if (outputFormat === 'csv' || flags.csv) {
|
|
848
|
-
this.log('status,identifier,id,error');
|
|
849
|
-
for (const r of results) {
|
|
850
|
-
const err = (r.error || '').replace(/"/g, '""');
|
|
851
|
-
this.log(`${r.status},${r.identifier || ''},${r.id || ''},"${err}"`);
|
|
852
|
-
}
|
|
853
|
-
}
|
|
854
|
-
else {
|
|
855
|
-
if (results.length > 0) {
|
|
856
|
-
const statusWidth = 8;
|
|
857
|
-
const identifierWidth = Math.max(10, ...results.map(r => (r.identifier || '').length));
|
|
858
|
-
this.log(`${'status'.padEnd(statusWidth)} ${'identifier'.padEnd(identifierWidth)} error`);
|
|
859
|
-
this.log(`${'-'.repeat(statusWidth)} ${'-'.repeat(identifierWidth)} ${'-'.repeat(50)}`);
|
|
860
|
-
for (const r of results) {
|
|
861
|
-
const status = r.status === 'created' ? 'created' : r.status === 'updated' ? 'updated' : 'error';
|
|
862
|
-
this.log(`${status.padEnd(statusWidth)} ${(r.identifier || '').padEnd(identifierWidth)} ${r.error || ''}`);
|
|
863
|
-
}
|
|
864
|
-
}
|
|
865
|
-
}
|
|
866
965
|
this.log('');
|
|
867
966
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
868
967
|
}
|
|
@@ -1026,12 +1125,24 @@ export default class VatDeclarations extends Command {
|
|
|
1026
1125
|
return { cols: flatten ? cols : cols.slice(0, 8), items: items };
|
|
1027
1126
|
}
|
|
1028
1127
|
getValue(row, col) {
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1128
|
+
if (!col.includes('.'))
|
|
1129
|
+
return String(row[col] ?? '');
|
|
1130
|
+
const parts = col.split('.');
|
|
1131
|
+
// Walk the dot-path; descending through an array branches into every
|
|
1132
|
+
// element so leaf values get collected from each one. Multiple leaves are
|
|
1133
|
+
// joined with ';' (e.g. dunningReminders.id -> "uuid1;uuid2").
|
|
1134
|
+
const walk = (o, idx) => {
|
|
1135
|
+
if (idx >= parts.length)
|
|
1136
|
+
return [o];
|
|
1137
|
+
if (o == null)
|
|
1138
|
+
return [];
|
|
1139
|
+
if (Array.isArray(o))
|
|
1140
|
+
return o.flatMap(item => walk(item, idx));
|
|
1141
|
+
if (typeof o === 'object')
|
|
1142
|
+
return walk(o[parts[idx]], idx + 1);
|
|
1143
|
+
return [];
|
|
1144
|
+
};
|
|
1145
|
+
return walk(row, 0).filter(v => v !== undefined && v !== null).map(v => String(v)).join(';');
|
|
1035
1146
|
}
|
|
1036
1147
|
printTable(data, fields) {
|
|
1037
1148
|
const { cols, items } = this.getColumns(data, fields);
|