@aderis-erp/cli 1.1.3 → 1.1.5
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 +42 -8
- package/dist/commands/account-transaction-allocations.js +38 -4
- package/dist/commands/account-transactions.d.ts +2 -0
- package/dist/commands/account-transactions.js +68 -32
- package/dist/commands/accounts.js +46 -12
- package/dist/commands/agent-platform.js +37 -3
- package/dist/commands/agent-runs.js +49 -15
- package/dist/commands/analytics.js +39 -5
- package/dist/commands/api-clients.d.ts +58 -0
- package/dist/commands/api-clients.js +1375 -0
- package/dist/commands/api-keys.js +40 -6
- package/dist/commands/async-jobs.js +45 -11
- package/dist/commands/audit-log.js +38 -4
- package/dist/commands/bank-accounts.js +54 -20
- package/dist/commands/bank-integrations.js +56 -22
- package/dist/commands/business-partners.js +77 -43
- package/dist/commands/chat.js +43 -9
- package/dist/commands/credit-card-accounts.d.ts +4 -0
- package/dist/commands/credit-card-accounts.js +64 -25
- package/dist/commands/currencies.d.ts +56 -0
- package/dist/commands/currencies.js +1361 -0
- package/dist/commands/custody-accounts.d.ts +56 -0
- package/dist/commands/custody-accounts.js +1369 -0
- package/dist/commands/{integration-identities.d.ts → dashboards.d.ts} +3 -3
- package/dist/commands/dashboards.js +1381 -0
- package/dist/commands/delivery-notes.js +69 -35
- package/dist/commands/document-checksums.d.ts +57 -0
- package/dist/commands/{integration-identities.js → document-checksums.js} +48 -21
- package/dist/commands/dunning-configs.js +41 -7
- package/dist/commands/dunning-reminders.js +44 -10
- package/dist/commands/dunning-runs.js +37 -3
- package/dist/commands/email-transactions.js +39 -5
- package/dist/commands/employees.js +77 -43
- package/dist/commands/entity-configurations.js +39 -5
- package/dist/commands/event-mapping-rules.js +37 -3
- package/dist/commands/event-processor-mappings.js +41 -7
- package/dist/commands/files.js +48 -14
- package/dist/commands/financial-instrument-transactions.d.ts +56 -0
- package/dist/commands/financial-instrument-transactions.js +1369 -0
- package/dist/commands/financial-instrument-types.d.ts +56 -0
- package/dist/commands/financial-instrument-types.js +1369 -0
- package/dist/commands/financial-instruments.d.ts +56 -0
- package/dist/commands/financial-instruments.js +1369 -0
- package/dist/commands/health.js +41 -7
- package/dist/commands/imports.js +39 -5
- package/dist/commands/inbox.js +42 -8
- package/dist/commands/integrations.js +39 -5
- package/dist/commands/item-categories.js +41 -7
- package/dist/commands/items.js +42 -8
- package/dist/commands/journal-lines.js +37 -3
- package/dist/commands/journals.js +61 -27
- package/dist/commands/{workflows.d.ts → kpis.d.ts} +1 -1
- package/dist/commands/kpis.js +1371 -0
- package/dist/commands/ledgers.js +83 -49
- package/dist/commands/my-account.js +54 -20
- package/dist/commands/notifications.d.ts +56 -0
- package/dist/commands/notifications.js +1360 -0
- package/dist/commands/office-locations.js +41 -7
- package/dist/commands/payment-methods.js +46 -12
- package/dist/commands/payment-runs.js +54 -20
- package/dist/commands/payment-terms.js +42 -8
- package/dist/commands/payments.js +43 -9
- package/dist/commands/payroll-runs.js +71 -37
- package/dist/commands/permissions.js +37 -3
- package/dist/commands/price-lists.js +48 -14
- package/dist/commands/processor-mappings.js +40 -6
- package/dist/commands/processors.js +46 -12
- package/dist/commands/purchase-order-types.js +41 -7
- package/dist/commands/purchase-orders.js +60 -26
- package/dist/commands/reports.js +43 -8
- package/dist/commands/roles.js +40 -6
- package/dist/commands/salary-types.js +41 -7
- package/dist/commands/sales-invoices.js +85 -51
- package/dist/commands/sales-orders.js +69 -35
- package/dist/commands/saved-views.js +39 -5
- package/dist/commands/scheduled-processor-mappings.js +41 -7
- package/dist/commands/schedules.js +45 -11
- package/dist/commands/settings.d.ts +56 -0
- package/dist/commands/settings.js +1361 -0
- package/dist/commands/tags.js +42 -8
- package/dist/commands/tax-codes.js +44 -10
- package/dist/commands/tax-matrix.js +41 -7
- package/dist/commands/templates.js +50 -15
- package/dist/commands/tenants.js +44 -10
- package/dist/commands/tools.js +38 -4
- package/dist/commands/users.js +58 -24
- package/dist/commands/vat-declarations.js +61 -27
- package/dist/commands/vendor-invoices.js +82 -47
- package/dist/commands/webhooks.js +43 -9
- package/dist/commands/work-summary.d.ts +56 -0
- package/dist/commands/{workflows.js → work-summary.js} +49 -18
- package/dist/lib/auth/index.js +5 -0
- package/dist/lib/paths.d.ts +8 -0
- package/dist/lib/paths.js +16 -0
- package/dist/lib/production-guard.d.ts +42 -0
- package/dist/lib/production-guard.js +94 -0
- package/oclif.manifest.json +2142 -333
- package/package.json +9 -3
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -9,52 +10,53 @@ const HAS_EXPORT = true;
|
|
|
9
10
|
const HAS_CAMT053 = false;
|
|
10
11
|
const SERVER_IMPORTS = [];
|
|
11
12
|
const NESTED_UPSERTS = ["line-items", "notes", "files", "payments"];
|
|
12
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "beneficiary-check", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel", "create-offsets", "delete-offsets", "offset-candidates", "create-nettings", "delete-nettings", "netting-candidates"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "payment-derivation", "beneficiary-check", "submit", "approve", "decline", "book", "rebuild-journal", "cancel", "replace", "change-vendor", "recalculate-payment-status", "validate", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes", "list-files", "create-files", "get-files", "update-files", "delete-files", "files-download", "list-payments", "create-payments", "get-payments", "update-payments", "delete-payments", "payments-approve", "payments-reopen", "payments-cancel", "create-offsets", "delete-offsets", "offset-candidates", "create-nettings", "delete-nettings", "netting-candidates"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'] },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
20
|
-
'beneficiary-check': { method: 'GET', path: '/v1/vendor-invoices/{id}/beneficiary-check', summary: 'Check a vendor invoice\'s beneficiary account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
21
|
-
'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
22
|
-
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
23
|
-
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
24
|
-
'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
25
|
-
'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
26
|
-
'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
27
|
-
'
|
|
28
|
-
'
|
|
29
|
-
'
|
|
30
|
-
'
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
34
|
-
'
|
|
35
|
-
'
|
|
36
|
-
'
|
|
37
|
-
'
|
|
38
|
-
'
|
|
39
|
-
'
|
|
40
|
-
'
|
|
41
|
-
'
|
|
42
|
-
'
|
|
43
|
-
'files
|
|
44
|
-
'
|
|
45
|
-
'
|
|
46
|
-
'
|
|
47
|
-
'
|
|
48
|
-
'
|
|
49
|
-
'payments
|
|
50
|
-
'payments-
|
|
51
|
-
'payments-
|
|
52
|
-
'
|
|
53
|
-
'
|
|
54
|
-
'
|
|
55
|
-
'
|
|
56
|
-
'
|
|
57
|
-
'
|
|
15
|
+
'list': { method: 'GET', path: '/v1/vendor-invoices', summary: 'Get all vendor invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/vendor-invoices', summary: 'Create a new vendor invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'payments'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'get': { method: 'GET', path: '/v1/vendor-invoices/{id}', summary: 'Get a vendor invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'update': { method: 'PATCH', path: '/v1/vendor-invoices/{id}', summary: 'Update a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'delete': { method: 'DELETE', path: '/v1/vendor-invoices/{id}', summary: 'Delete a vendor invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'payment-derivation': { method: 'GET', path: '/v1/vendor-invoices/{id}/payment-derivation', summary: 'Explain how a vendor invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'beneficiary-check': { method: 'GET', path: '/v1/vendor-invoices/{id}/beneficiary-check', summary: 'Check a vendor invoice\'s beneficiary account', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
22
|
+
'submit': { method: 'POST', path: '/v1/vendor-invoices/{id}/submit', summary: 'Submit a vendor invoice for approval (transitions DRAFT to PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
23
|
+
'approve': { method: 'POST', path: '/v1/vendor-invoices/{id}/approve', summary: 'Approve a vendor invoice (transitions DRAFT/PENDING_APPROVAL to APPROVED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'], bodyMediaType: '', specParams: [] },
|
|
24
|
+
'decline': { method: 'POST', path: '/v1/vendor-invoices/{id}/decline', summary: 'Decline a vendor invoice (transitions PENDING_APPROVAL to DECLINED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
25
|
+
'book': { method: 'POST', path: '/v1/vendor-invoices/{id}/book', summary: 'Manually book a vendor invoice (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
26
|
+
'rebuild-journal': { method: 'POST', path: '/v1/vendor-invoices/{id}/rebuild-journal', summary: 'Rebuild a vendor invoice booking journal (dispatch to ledger-booking-agent)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
27
|
+
'cancel': { method: 'POST', path: '/v1/vendor-invoices/{id}/cancel', summary: 'Cancel a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
28
|
+
'replace': { method: 'POST', path: '/v1/vendor-invoices/{id}/replace', summary: 'Replace a vendor invoice with a new draft', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
29
|
+
'change-vendor': { method: 'POST', path: '/v1/vendor-invoices/{id}/change-vendor', summary: 'Change the vendor on a vendor invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
30
|
+
'recalculate-payment-status': { method: 'POST', path: '/v1/vendor-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a vendor invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
31
|
+
'validate': { method: 'POST', path: '/v1/vendor-invoices/{id}/validate', summary: 'Validate a vendor invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['earlyPaymentDiscounts', 'totalAmountByTaxRate', 'files', 'notes', 'lineItems', 'tagIds', 'tagCodes', 'removeFileIds'], bodyMediaType: '', specParams: [] },
|
|
32
|
+
'create-line-items': { method: 'POST', path: '/v1/vendor-invoices/{invoiceId}/line-items', summary: 'Add a line item to a vendor invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems', 'creditedVendorInvoiceLineItems'], bodyMediaType: '', specParams: [] },
|
|
33
|
+
'update-line-items': { method: 'PATCH', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tagIds', 'tagCodes', 'purchaseOrderLineItems', 'creditedVendorInvoiceLineItems'], bodyMediaType: '', specParams: [] },
|
|
34
|
+
'delete-line-items': { method: 'DELETE', path: '/v1/vendor-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a vendor invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
35
|
+
'list-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Get all notes for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
36
|
+
'create-notes': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes', summary: 'Create a note for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
37
|
+
'get-notes': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Get a specific note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
38
|
+
'update-notes': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Update a note for a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
39
|
+
'delete-notes': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/notes/{noteId}', summary: 'Delete a note from a vendor invoice', pathParams: ['vendorInvoiceId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
40
|
+
'list-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files', summary: 'List file attachments for a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
41
|
+
'create-files': { method: 'POST', path: '/v1/vendor-invoices/{parentId}/files', summary: 'Upload a file attachment to a vendor invoice', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
42
|
+
'get-files': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Get a file attachment for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
43
|
+
'update-files': { method: 'PATCH', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
44
|
+
'delete-files': { method: 'DELETE', path: '/v1/vendor-invoices/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
45
|
+
'files-download': { method: 'GET', path: '/v1/vendor-invoices/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a vendor invoice', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: true, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
46
|
+
'list-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Get all payments for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
47
|
+
'create-payments': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments', summary: 'Create a payment for a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
48
|
+
'get-payments': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Get a specific payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
49
|
+
'update-payments': { method: 'PATCH', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Update a payment for a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
50
|
+
'delete-payments': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}', summary: 'Delete a payment from a vendor invoice', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
51
|
+
'payments-approve': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/approve', summary: 'Approve a payment entry (OPEN → APPROVED)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
52
|
+
'payments-reopen': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/reopen', summary: 'Reopen an approved payment entry (APPROVED → OPEN)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
53
|
+
'payments-cancel': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/payments/{paymentId}/cancel', summary: 'Cancel a sent payment entry (SENT -> ERROR)', pathParams: ['vendorInvoiceId', 'paymentId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
54
|
+
'create-offsets': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/offsets', summary: 'Offset a prepayment or credit note against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
55
|
+
'delete-offsets': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/offsets/{offsetId}', summary: 'Remove an offset of a prepayment or credit note from a vendor invoice', pathParams: ['vendorInvoiceId', 'offsetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
56
|
+
'offset-candidates': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/offset-candidates', summary: 'List the prepayments and credit notes that can be offset against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
57
|
+
'create-nettings': { method: 'POST', path: '/v1/vendor-invoices/{vendorInvoiceId}/nettings', summary: 'Net a sales invoice of the same business partner against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
58
|
+
'delete-nettings': { method: 'DELETE', path: '/v1/vendor-invoices/{vendorInvoiceId}/nettings/{offsetId}', summary: 'Remove a netting of a vendor invoice against a sales invoice', pathParams: ['vendorInvoiceId', 'offsetId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
59
|
+
'netting-candidates': { method: 'GET', path: '/v1/vendor-invoices/{vendorInvoiceId}/netting-candidates', summary: 'List the sales invoices that can be netted against a vendor invoice', pathParams: ['vendorInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
58
60
|
};
|
|
59
61
|
export default class VendorInvoices extends Command {
|
|
60
62
|
static description = 'Manage vendor invoices';
|
|
@@ -76,7 +78,7 @@ export default class VendorInvoices extends Command {
|
|
|
76
78
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
77
79
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
78
80
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
79
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
81
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
80
82
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
81
83
|
'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
|
|
82
84
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
@@ -212,6 +214,17 @@ export default class VendorInvoices extends Command {
|
|
|
212
214
|
}
|
|
213
215
|
}
|
|
214
216
|
}
|
|
217
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
218
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
219
|
+
for (const param of endpoint.specParams) {
|
|
220
|
+
const value = flags[param.flag];
|
|
221
|
+
if (value === undefined || value === '') {
|
|
222
|
+
if (param.required)
|
|
223
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
queryParams[param.name] = value;
|
|
227
|
+
}
|
|
215
228
|
let data;
|
|
216
229
|
if (endpoint.isMultipart) {
|
|
217
230
|
if (!flags.file)
|
|
@@ -232,6 +245,25 @@ export default class VendorInvoices extends Command {
|
|
|
232
245
|
}
|
|
233
246
|
data = form;
|
|
234
247
|
}
|
|
248
|
+
else if (endpoint.bodyMediaType) {
|
|
249
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
250
|
+
// verbatim, under the content type the endpoint asks for.
|
|
251
|
+
if (flags.file) {
|
|
252
|
+
const fsModule = await import('node:fs');
|
|
253
|
+
try {
|
|
254
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
255
|
+
}
|
|
256
|
+
catch {
|
|
257
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
else if (flags.data) {
|
|
261
|
+
data = flags.data;
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
235
267
|
else if (endpoint.hasBody) {
|
|
236
268
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
237
269
|
// input file — skip the read-as-body branch.
|
|
@@ -327,7 +359,7 @@ export default class VendorInvoices extends Command {
|
|
|
327
359
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
328
360
|
}
|
|
329
361
|
else if (data) {
|
|
330
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
362
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
331
363
|
}
|
|
332
364
|
process.stderr.write('\n');
|
|
333
365
|
}
|
|
@@ -341,6 +373,7 @@ export default class VendorInvoices extends Command {
|
|
|
341
373
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
342
374
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
343
375
|
data,
|
|
376
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
344
377
|
contextName: flags.context,
|
|
345
378
|
binary: true,
|
|
346
379
|
});
|
|
@@ -366,6 +399,7 @@ export default class VendorInvoices extends Command {
|
|
|
366
399
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
367
400
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
368
401
|
data,
|
|
402
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
369
403
|
contextName: flags.context,
|
|
370
404
|
binary: true,
|
|
371
405
|
});
|
|
@@ -386,6 +420,7 @@ export default class VendorInvoices extends Command {
|
|
|
386
420
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
387
421
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
388
422
|
data,
|
|
423
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
389
424
|
contextName: flags.context,
|
|
390
425
|
acceptAny: true,
|
|
391
426
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
@@ -11,13 +12,13 @@ const SERVER_IMPORTS = [];
|
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
13
|
const SUBCOMMANDS = [...["list", "create", "events", "events-relations", "get", "update", "delete"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'list': { method: 'GET', path: '/v1/webhooks', summary: 'List all webhook subscriptions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
-
'create': { method: 'POST', path: '/v1/webhooks', summary: 'Create a new webhook subscription', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'] },
|
|
16
|
-
'events': { method: 'GET', path: '/v1/webhooks/events', summary: 'Get available event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
17
|
-
'events-relations': { method: 'GET', path: '/v1/webhooks/events/{eventType}/relations', summary: 'Get valid relations for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
18
|
-
'get': { method: 'GET', path: '/v1/webhooks/{id}', summary: 'Get a webhook subscription by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
19
|
-
'update': { method: 'PATCH', path: '/v1/webhooks/{id}', summary: 'Update a webhook subscription', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'] },
|
|
20
|
-
'delete': { method: 'DELETE', path: '/v1/webhooks/{id}', summary: 'Delete a webhook subscription', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'list': { method: 'GET', path: '/v1/webhooks', summary: 'List all webhook subscriptions', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
16
|
+
'create': { method: 'POST', path: '/v1/webhooks', summary: 'Create a new webhook subscription', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'], bodyMediaType: '', specParams: [] },
|
|
17
|
+
'events': { method: 'GET', path: '/v1/webhooks/events', summary: 'Get available event types', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
18
|
+
'events-relations': { method: 'GET', path: '/v1/webhooks/events/{eventType}/relations', summary: 'Get valid relations for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
19
|
+
'get': { method: 'GET', path: '/v1/webhooks/{id}', summary: 'Get a webhook subscription by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
20
|
+
'update': { method: 'PATCH', path: '/v1/webhooks/{id}', summary: 'Update a webhook subscription', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: ['tenantIds'], bodyMediaType: '', specParams: [] },
|
|
21
|
+
'delete': { method: 'DELETE', path: '/v1/webhooks/{id}', summary: 'Delete a webhook subscription', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
21
22
|
};
|
|
22
23
|
export default class Webhooks extends Command {
|
|
23
24
|
static description = 'Manage webhooks';
|
|
@@ -39,7 +40,7 @@ export default class Webhooks extends Command {
|
|
|
39
40
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
40
41
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
41
42
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
42
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
43
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
43
44
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
44
45
|
page: Flags.string({ description: 'Page number' }),
|
|
45
46
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -170,6 +171,17 @@ export default class Webhooks extends Command {
|
|
|
170
171
|
}
|
|
171
172
|
}
|
|
172
173
|
}
|
|
174
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
175
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
176
|
+
for (const param of endpoint.specParams) {
|
|
177
|
+
const value = flags[param.flag];
|
|
178
|
+
if (value === undefined || value === '') {
|
|
179
|
+
if (param.required)
|
|
180
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
181
|
+
continue;
|
|
182
|
+
}
|
|
183
|
+
queryParams[param.name] = value;
|
|
184
|
+
}
|
|
173
185
|
let data;
|
|
174
186
|
if (endpoint.isMultipart) {
|
|
175
187
|
if (!flags.file)
|
|
@@ -190,6 +202,25 @@ export default class Webhooks extends Command {
|
|
|
190
202
|
}
|
|
191
203
|
data = form;
|
|
192
204
|
}
|
|
205
|
+
else if (endpoint.bodyMediaType) {
|
|
206
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
207
|
+
// verbatim, under the content type the endpoint asks for.
|
|
208
|
+
if (flags.file) {
|
|
209
|
+
const fsModule = await import('node:fs');
|
|
210
|
+
try {
|
|
211
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
212
|
+
}
|
|
213
|
+
catch {
|
|
214
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
else if (flags.data) {
|
|
218
|
+
data = flags.data;
|
|
219
|
+
}
|
|
220
|
+
else {
|
|
221
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
193
224
|
else if (endpoint.hasBody) {
|
|
194
225
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
195
226
|
// input file — skip the read-as-body branch.
|
|
@@ -285,7 +316,7 @@ export default class Webhooks extends Command {
|
|
|
285
316
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
286
317
|
}
|
|
287
318
|
else if (data) {
|
|
288
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
319
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
289
320
|
}
|
|
290
321
|
process.stderr.write('\n');
|
|
291
322
|
}
|
|
@@ -299,6 +330,7 @@ export default class Webhooks extends Command {
|
|
|
299
330
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
300
331
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
301
332
|
data,
|
|
333
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
302
334
|
contextName: flags.context,
|
|
303
335
|
binary: true,
|
|
304
336
|
});
|
|
@@ -324,6 +356,7 @@ export default class Webhooks extends Command {
|
|
|
324
356
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
325
357
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
326
358
|
data,
|
|
359
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
327
360
|
contextName: flags.context,
|
|
328
361
|
binary: true,
|
|
329
362
|
});
|
|
@@ -344,6 +377,7 @@ export default class Webhooks extends Command {
|
|
|
344
377
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
345
378
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
346
379
|
data,
|
|
380
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
347
381
|
contextName: flags.context,
|
|
348
382
|
acceptAny: true,
|
|
349
383
|
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class WorkSummary extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
table: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'sort-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
meta: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
28
|
+
};
|
|
29
|
+
run(): Promise<void>;
|
|
30
|
+
private runUpsert;
|
|
31
|
+
/**
|
|
32
|
+
* The API's own import: the whole array goes up in one request and comes
|
|
33
|
+
* back as an import run, which records what happened to every record. A bad
|
|
34
|
+
* record lands in errorLog and the rest are still applied, and the run stays
|
|
35
|
+
* readable afterwards with: aderis imports get <id>.
|
|
36
|
+
*/
|
|
37
|
+
private runServerImport;
|
|
38
|
+
/** Renders an import run: its counts, then whatever the server refused. */
|
|
39
|
+
private printImportRun;
|
|
40
|
+
private runExport;
|
|
41
|
+
private runCamt053Import;
|
|
42
|
+
private runNestedUpsert;
|
|
43
|
+
private parseFilterValue;
|
|
44
|
+
/** The page line under a table: which page, how many, and any sums the API returned. */
|
|
45
|
+
private printPageSummary;
|
|
46
|
+
/**
|
|
47
|
+
* The API caps how many records a page may hold. Asking for more and getting
|
|
48
|
+
* fewer looks like "that is everything" unless it is said out loud.
|
|
49
|
+
*/
|
|
50
|
+
private warnShortPage;
|
|
51
|
+
private sanitizeOutput;
|
|
52
|
+
private getColumns;
|
|
53
|
+
private getValue;
|
|
54
|
+
private printTable;
|
|
55
|
+
private printCsv;
|
|
56
|
+
}
|
|
@@ -1,25 +1,23 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, isTextualMediaType, normalizeSortBy } from '../lib/api-client.js';
|
|
3
3
|
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
|
+
import { resolveUserPath } from '../lib/paths.js';
|
|
4
5
|
// 'upsert' loops create/update from the CLI, one request per record. It exists
|
|
5
6
|
// only where the API has no import endpoint of its own; those keep the name
|
|
6
7
|
// 'import' and are listed in SERVER_IMPORTS.
|
|
7
8
|
const HAS_UPSERT = false;
|
|
8
|
-
const HAS_EXPORT =
|
|
9
|
+
const HAS_EXPORT = false;
|
|
9
10
|
const HAS_CAMT053 = false;
|
|
10
11
|
const SERVER_IMPORTS = [];
|
|
11
12
|
const NESTED_UPSERTS = [];
|
|
12
|
-
const SUBCOMMANDS = [...["
|
|
13
|
+
const SUBCOMMANDS = [...["get"], ...(HAS_UPSERT ? ['upsert'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_UPSERTS.map(n => `upsert-${n}`)];
|
|
13
14
|
const ENDPOINTS = {
|
|
14
|
-
'
|
|
15
|
-
'status': { method: 'POST', path: '/v1/workflows/status', summary: 'Create a new workflow status update', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
16
|
-
'find': { method: 'GET', path: '/v1/workflows/{workflowId}', summary: 'Get workflow details including status updates and entity changes', pathParams: ['workflowId'], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [] },
|
|
15
|
+
'get': { method: 'GET', path: '/v1/work-summary', summary: 'Count what is open for the caller', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, hasBinaryVariant: false, bodyArrayFields: [], bodyMediaType: '', specParams: [] },
|
|
17
16
|
};
|
|
18
|
-
export default class
|
|
19
|
-
static description = 'Manage
|
|
17
|
+
export default class WorkSummary extends Command {
|
|
18
|
+
static description = 'Manage work summary';
|
|
20
19
|
static examples = [
|
|
21
|
-
'<%= config.bin %>
|
|
22
|
-
'<%= config.bin %> workflows export --csv > workflows.csv',
|
|
20
|
+
'<%= config.bin %> work-summary get <id>',
|
|
23
21
|
];
|
|
24
22
|
static args = {
|
|
25
23
|
action: Args.string({ description: 'Action: ' + SUBCOMMANDS.join(', '), required: true }),
|
|
@@ -31,7 +29,7 @@ export default class Workflows extends Command {
|
|
|
31
29
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
32
30
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
33
31
|
data: Flags.string({ char: 'd', description: 'JSON data for request body' }),
|
|
34
|
-
file: Flags.string({ char: 'f', description: 'Read request body from file' }),
|
|
32
|
+
file: Flags.string({ char: 'f', description: 'Read request body from file', parse: async (input) => resolveUserPath(input) }),
|
|
35
33
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
36
34
|
page: Flags.string({ description: 'Page number' }),
|
|
37
35
|
limit: Flags.string({ description: 'Items per page' }),
|
|
@@ -46,14 +44,14 @@ export default class Workflows extends Command {
|
|
|
46
44
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
47
45
|
};
|
|
48
46
|
async run() {
|
|
49
|
-
const { args, flags } = await this.parse(
|
|
47
|
+
const { args, flags } = await this.parse(WorkSummary);
|
|
50
48
|
// Bulk actions. 'import' is the API's own endpoint: one request, answered
|
|
51
49
|
// by a run that records what happened to every record. 'upsert' is the CLI
|
|
52
50
|
// looping create/update. A resource has one or the other, never both.
|
|
53
51
|
if (args.action === 'upsert') {
|
|
54
52
|
if (!HAS_UPSERT) {
|
|
55
53
|
this.error(SERVER_IMPORTS.includes('import')
|
|
56
|
-
? 'This resource is imported by the API. Use: aderis
|
|
54
|
+
? 'This resource is imported by the API. Use: aderis work-summary import --file data.json'
|
|
57
55
|
: 'Upsert needs both a create and an update endpoint, which this resource does not have');
|
|
58
56
|
}
|
|
59
57
|
await this.runUpsert(flags);
|
|
@@ -63,7 +61,7 @@ export default class Workflows extends Command {
|
|
|
63
61
|
const suffix = args.action.replace('upsert-', '');
|
|
64
62
|
if (!NESTED_UPSERTS.includes(suffix)) {
|
|
65
63
|
this.error(SERVER_IMPORTS.includes('import-' + suffix)
|
|
66
|
-
? `That resource is imported by the API. Use: aderis
|
|
64
|
+
? `That resource is imported by the API. Use: aderis work-summary import-${suffix} <id> --file data.json`
|
|
67
65
|
: `Unknown action: ${args.action}. Available: ${SUBCOMMANDS.join(', ')}`);
|
|
68
66
|
}
|
|
69
67
|
await this.runNestedUpsert(suffix, args.id, flags);
|
|
@@ -93,7 +91,7 @@ export default class Workflows extends Command {
|
|
|
93
91
|
const suffix = args.action === 'import' ? '' : '-' + args.action.replace('import-', '');
|
|
94
92
|
const loops = args.action === 'import' ? HAS_UPSERT : NESTED_UPSERTS.includes(args.action.replace('import-', ''));
|
|
95
93
|
if (loops) {
|
|
96
|
-
this.error(`This resource has no import endpoint. Use: aderis
|
|
94
|
+
this.error(`This resource has no import endpoint. Use: aderis work-summary upsert${suffix}${suffix ? ' <id>' : ''} --file data.json`);
|
|
97
95
|
}
|
|
98
96
|
}
|
|
99
97
|
const endpoint = ENDPOINTS[args.action];
|
|
@@ -107,7 +105,7 @@ export default class Workflows extends Command {
|
|
|
107
105
|
// First path param always comes from positional argument
|
|
108
106
|
value = args.id;
|
|
109
107
|
if (!value)
|
|
110
|
-
this.error('Missing required argument: id. Usage: aderis
|
|
108
|
+
this.error('Missing required argument: id. Usage: aderis work-summary <action> <id>');
|
|
111
109
|
}
|
|
112
110
|
else {
|
|
113
111
|
const flagName = param.replace(/\./g, '-').replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
|
|
@@ -162,6 +160,17 @@ export default class Workflows extends Command {
|
|
|
162
160
|
}
|
|
163
161
|
}
|
|
164
162
|
}
|
|
163
|
+
// Flags generated from this endpoint's own parameters. Raw-body endpoints
|
|
164
|
+
// have them: their body is not JSON, so --set cannot carry a value there.
|
|
165
|
+
for (const param of endpoint.specParams) {
|
|
166
|
+
const value = flags[param.flag];
|
|
167
|
+
if (value === undefined || value === '') {
|
|
168
|
+
if (param.required)
|
|
169
|
+
this.error(`Missing required flag: --${param.flag}`);
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
queryParams[param.name] = value;
|
|
173
|
+
}
|
|
165
174
|
let data;
|
|
166
175
|
if (endpoint.isMultipart) {
|
|
167
176
|
if (!flags.file)
|
|
@@ -182,6 +191,25 @@ export default class Workflows extends Command {
|
|
|
182
191
|
}
|
|
183
192
|
data = form;
|
|
184
193
|
}
|
|
194
|
+
else if (endpoint.bodyMediaType) {
|
|
195
|
+
// The body is not JSON (today only application/x-ndjson): it is sent
|
|
196
|
+
// verbatim, under the content type the endpoint asks for.
|
|
197
|
+
if (flags.file) {
|
|
198
|
+
const fsModule = await import('node:fs');
|
|
199
|
+
try {
|
|
200
|
+
data = fsModule.readFileSync(flags.file, 'utf-8');
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
this.error(`Failed to read file: ${flags.file}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
else if (flags.data) {
|
|
207
|
+
data = flags.data;
|
|
208
|
+
}
|
|
209
|
+
else {
|
|
210
|
+
this.error(`This endpoint takes its body as ${endpoint.bodyMediaType}. Pass the file with -f / --file, or the text with --data.`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
185
213
|
else if (endpoint.hasBody) {
|
|
186
214
|
// For binary-response endpoints, --file is the output path, not a JSON
|
|
187
215
|
// input file — skip the read-as-body branch.
|
|
@@ -277,7 +305,7 @@ export default class Workflows extends Command {
|
|
|
277
305
|
process.stderr.write(`Body: [multipart/form-data file upload]\n`);
|
|
278
306
|
}
|
|
279
307
|
else if (data) {
|
|
280
|
-
process.stderr.write(`Body: ${JSON.stringify(data, null, 2)}\n`);
|
|
308
|
+
process.stderr.write(`Body: ${typeof data === 'string' ? data : JSON.stringify(data, null, 2)}\n`);
|
|
281
309
|
}
|
|
282
310
|
process.stderr.write('\n');
|
|
283
311
|
}
|
|
@@ -291,6 +319,7 @@ export default class Workflows extends Command {
|
|
|
291
319
|
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
292
320
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
293
321
|
data,
|
|
322
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
294
323
|
contextName: flags.context,
|
|
295
324
|
binary: true,
|
|
296
325
|
});
|
|
@@ -316,6 +345,7 @@ export default class Workflows extends Command {
|
|
|
316
345
|
const raw = await apiRequest(endpoint.method, apiPath, {
|
|
317
346
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
318
347
|
data,
|
|
348
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
319
349
|
contextName: flags.context,
|
|
320
350
|
binary: true,
|
|
321
351
|
});
|
|
@@ -336,6 +366,7 @@ export default class Workflows extends Command {
|
|
|
336
366
|
response = await apiRequest(endpoint.method, apiPath, {
|
|
337
367
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
338
368
|
data,
|
|
369
|
+
contentType: endpoint.bodyMediaType || undefined,
|
|
339
370
|
contextName: flags.context,
|
|
340
371
|
acceptAny: true,
|
|
341
372
|
});
|
|
@@ -638,7 +669,7 @@ export default class Workflows extends Command {
|
|
|
638
669
|
let apiPath = endpoint.path;
|
|
639
670
|
if (endpoint.pathParams.length > 0) {
|
|
640
671
|
if (!parentId)
|
|
641
|
-
this.error(`Missing required argument: id. Usage: aderis
|
|
672
|
+
this.error(`Missing required argument: id. Usage: aderis work-summary ${action} <parent-id> --file data.json`);
|
|
642
673
|
apiPath = apiPath.replace(`{${endpoint.pathParams[0]}}`, parentId);
|
|
643
674
|
}
|
|
644
675
|
// The endpoint takes an array. A single object is wrapped, so importing
|
|
@@ -911,7 +942,7 @@ export default class Workflows extends Command {
|
|
|
911
942
|
}
|
|
912
943
|
async runNestedUpsert(suffix, parentId, flags) {
|
|
913
944
|
if (!parentId) {
|
|
914
|
-
this.error(`Missing required argument: id. Usage: aderis
|
|
945
|
+
this.error(`Missing required argument: id. Usage: aderis work-summary upsert-${suffix} <parent-id> --file data.json`);
|
|
915
946
|
}
|
|
916
947
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
917
948
|
if (!context)
|