@aderis-erp/cli 1.0.14 → 1.0.17
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 +30 -8
- package/dist/commands/access-groups.d.ts +0 -1
- package/dist/commands/access-groups.js +22 -78
- package/dist/commands/account-transaction-allocations.d.ts +0 -1
- package/dist/commands/account-transaction-allocations.js +22 -78
- package/dist/commands/account-transactions.d.ts +0 -1
- package/dist/commands/account-transactions.js +28 -82
- package/dist/commands/accounts.d.ts +0 -1
- package/dist/commands/accounts.js +24 -80
- package/dist/commands/agent-platform.d.ts +0 -1
- package/dist/commands/agent-platform.js +24 -79
- package/dist/commands/analytics.d.ts +39 -0
- package/dist/commands/analytics.js +1113 -0
- package/dist/commands/{documents.d.ts → api-keys.d.ts} +1 -2
- package/dist/commands/api-keys.js +1117 -0
- package/dist/commands/audit-log.d.ts +0 -1
- package/dist/commands/audit-log.js +22 -78
- package/dist/commands/auth/login.d.ts +9 -0
- package/dist/commands/auth/login.js +83 -21
- package/dist/commands/auth/logout.d.ts +6 -0
- package/dist/commands/auth/logout.js +23 -6
- package/dist/commands/auth/status.js +46 -23
- package/dist/commands/auth/tenant.js +15 -9
- package/dist/commands/auth.d.ts +0 -1
- package/dist/commands/auth.js +37 -51
- package/dist/commands/bank-accounts.d.ts +0 -1
- package/dist/commands/bank-accounts.js +22 -78
- package/dist/commands/bank-integrations.d.ts +0 -1
- package/dist/commands/bank-integrations.js +22 -78
- package/dist/commands/business-partners.d.ts +1 -1
- package/dist/commands/business-partners.js +38 -84
- package/dist/commands/chat.d.ts +0 -1
- package/dist/commands/chat.js +22 -78
- package/dist/commands/config/add.d.ts +12 -0
- package/dist/commands/config/add.js +97 -17
- package/dist/commands/config/current.js +12 -4
- package/dist/commands/config/list.js +12 -4
- package/dist/commands/config/set-api-key.d.ts +10 -0
- package/dist/commands/config/set-api-key.js +49 -0
- package/dist/commands/config/use.js +3 -4
- package/dist/commands/credit-card-accounts.d.ts +0 -1
- package/dist/commands/credit-card-accounts.js +22 -78
- package/dist/commands/delivery-notes.d.ts +0 -1
- package/dist/commands/delivery-notes.js +29 -84
- package/dist/commands/dunning-configs.d.ts +0 -1
- package/dist/commands/dunning-configs.js +22 -78
- package/dist/commands/dunning-reminders.d.ts +0 -1
- package/dist/commands/dunning-reminders.js +22 -78
- package/dist/commands/dunning-runs.d.ts +0 -1
- package/dist/commands/dunning-runs.js +22 -78
- package/dist/commands/email-transactions.d.ts +0 -1
- package/dist/commands/email-transactions.js +22 -78
- package/dist/commands/employees.d.ts +0 -1
- package/dist/commands/employees.js +24 -80
- package/dist/commands/entity-configurations.d.ts +0 -1
- package/dist/commands/entity-configurations.js +22 -78
- package/dist/commands/event-processor-mappings.d.ts +0 -1
- package/dist/commands/event-processor-mappings.js +22 -78
- package/dist/commands/{files-internal.d.ts → files.d.ts} +1 -2
- package/dist/commands/{files-internal.js → files.js} +40 -90
- package/dist/commands/health.d.ts +0 -1
- package/dist/commands/health.js +22 -78
- package/dist/commands/inbox.d.ts +0 -1
- package/dist/commands/inbox.js +22 -78
- package/dist/commands/integrations.d.ts +0 -1
- package/dist/commands/integrations.js +22 -78
- package/dist/commands/item-categories.d.ts +0 -1
- package/dist/commands/item-categories.js +22 -78
- package/dist/commands/items.d.ts +0 -1
- package/dist/commands/items.js +24 -80
- package/dist/commands/journal-lines.d.ts +0 -1
- package/dist/commands/journal-lines.js +22 -78
- package/dist/commands/journals.d.ts +1 -1
- package/dist/commands/journals.js +29 -81
- package/dist/commands/ledgers.d.ts +0 -1
- package/dist/commands/ledgers.js +26 -79
- package/dist/commands/nango-integrations.d.ts +39 -0
- package/dist/commands/nango-integrations.js +1115 -0
- package/dist/commands/office-locations.d.ts +0 -1
- package/dist/commands/office-locations.js +22 -78
- package/dist/commands/payment-methods.d.ts +0 -1
- package/dist/commands/payment-methods.js +22 -78
- package/dist/commands/payment-runs.d.ts +0 -1
- package/dist/commands/payment-runs.js +24 -79
- package/dist/commands/payment-terms.d.ts +0 -1
- package/dist/commands/payment-terms.js +22 -78
- package/dist/commands/payments.d.ts +0 -1
- package/dist/commands/payments.js +22 -78
- package/dist/commands/price-lists.d.ts +0 -1
- package/dist/commands/price-lists.js +22 -78
- package/dist/commands/processor-mappings.d.ts +0 -1
- package/dist/commands/processor-mappings.js +22 -78
- package/dist/commands/processors.d.ts +0 -1
- package/dist/commands/processors.js +22 -78
- package/dist/commands/purchase-order-types.d.ts +0 -1
- package/dist/commands/purchase-order-types.js +22 -78
- package/dist/commands/purchase-orders.d.ts +1 -1
- package/dist/commands/purchase-orders.js +35 -84
- package/dist/commands/reports.d.ts +0 -1
- package/dist/commands/reports.js +23 -79
- package/dist/commands/salary-types.d.ts +0 -1
- package/dist/commands/salary-types.js +22 -78
- package/dist/commands/sales-invoices.d.ts +0 -1
- package/dist/commands/sales-invoices.js +38 -89
- package/dist/commands/sales-orders.d.ts +0 -1
- package/dist/commands/sales-orders.js +26 -82
- package/dist/commands/saved-views.d.ts +0 -1
- package/dist/commands/saved-views.js +22 -78
- package/dist/commands/scheduled-processor-mappings.d.ts +0 -1
- package/dist/commands/scheduled-processor-mappings.js +22 -78
- package/dist/commands/schedules.d.ts +0 -1
- package/dist/commands/schedules.js +22 -78
- package/dist/commands/switch.js +14 -7
- package/dist/commands/tag-configurations.d.ts +39 -0
- package/dist/commands/{documents.js → tag-configurations.js} +40 -96
- package/dist/commands/tags.d.ts +0 -1
- package/dist/commands/tags.js +22 -78
- package/dist/commands/tax-codes.d.ts +0 -1
- package/dist/commands/tax-codes.js +22 -78
- package/dist/commands/tax-matrix.d.ts +0 -1
- package/dist/commands/tax-matrix.js +22 -78
- package/dist/commands/tenants.d.ts +0 -1
- package/dist/commands/tenants.js +24 -79
- package/dist/commands/text-templates.d.ts +0 -1
- package/dist/commands/text-templates.js +22 -78
- package/dist/commands/tools.d.ts +0 -1
- package/dist/commands/tools.js +22 -78
- package/dist/commands/vat-declarations.d.ts +1 -1
- package/dist/commands/vat-declarations.js +28 -79
- package/dist/commands/vendor-invoices.d.ts +0 -1
- package/dist/commands/vendor-invoices.js +31 -86
- package/dist/commands/webhooks.d.ts +0 -1
- package/dist/commands/webhooks.js +22 -78
- package/dist/commands/workflows.d.ts +0 -1
- package/dist/commands/workflows.js +22 -78
- package/dist/lib/api-client.js +8 -6
- package/dist/lib/auth/constants.d.ts +11 -0
- package/dist/lib/auth/constants.js +11 -0
- package/dist/lib/auth/errors.d.ts +17 -0
- package/dist/lib/auth/errors.js +22 -0
- package/dist/lib/auth/flow.d.ts +27 -0
- package/dist/lib/auth/flow.js +163 -0
- package/dist/lib/auth/guards.d.ts +5 -0
- package/dist/lib/auth/guards.js +14 -0
- package/dist/lib/auth/http.d.ts +16 -0
- package/dist/lib/auth/http.js +49 -0
- package/dist/lib/auth/index.d.ts +49 -0
- package/dist/lib/auth/index.js +217 -0
- package/dist/lib/auth/keycloak-provider.d.ts +75 -0
- package/dist/lib/auth/keycloak-provider.js +215 -0
- package/dist/lib/auth/pages.d.ts +25 -0
- package/dist/lib/auth/pages.js +123 -0
- package/dist/lib/auth/pkce.d.ts +11 -0
- package/dist/lib/auth/pkce.js +17 -0
- package/dist/lib/auth/tokens.d.ts +54 -0
- package/dist/lib/auth/tokens.js +104 -0
- package/dist/lib/auth/types.d.ts +57 -0
- package/dist/lib/auth/types.js +1 -0
- package/dist/lib/auth/validated-url.d.ts +6 -0
- package/dist/lib/auth/validated-url.js +53 -0
- package/dist/lib/base-api-command.js +6 -4
- package/dist/lib/config.d.ts +44 -4
- package/dist/lib/config.js +55 -19
- package/oclif.manifest.json +1181 -330
- package/package.json +3 -2
- package/dist/lib/auth.d.ts +0 -35
- package/dist/lib/auth.js +0 -566
|
@@ -1,34 +1,39 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
3
|
-
import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
3
|
+
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
|
-
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "create-migrated-invoice", "pdf", "get", "update", "delete", "approve", "match-sales-orders", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "merge", "create-line-items", "line-items-from-sales-order", "update-line-items-reorder", "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"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
8
|
+
const NESTED_IMPORTS = ["migrated-invoice", "line-items", "notes", "files"];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "create-migrated-invoice", "pdf", "get", "update", "delete", "payment-derivation", "update-migrated-invoice", "approve", "match-sales-orders", "validate", "request-approval", "send", "send-resend", "cancel", "copy", "replace", "rebuild-journal", "recalculate-payment-status", "merge", "create-line-items", "line-items-from-sales-order", "line-items-from-delivery-note", "update-line-items-reorder", "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"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/sales-invoices', summary: 'Get all sales invoices with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/sales-invoices', summary: 'Create a new sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'invoiceEmailCc'] },
|
|
13
|
-
'create-migrated-invoice': { method: 'POST', path: '/v1/sales-invoices/create-migrated-invoice', summary: 'Create a sales invoice with explicit tax codes on line items (for data migration)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lineItems', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'invoiceEmailCc'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/sales-invoices', summary: 'Create a new sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc'] },
|
|
13
|
+
'create-migrated-invoice': { method: 'POST', path: '/v1/sales-invoices/create-migrated-invoice', summary: 'Create a sales invoice with explicit tax codes on line items (for data migration)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lineItems', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc'] },
|
|
14
14
|
'pdf': { method: 'GET', path: '/v1/sales-invoices/{id}/pdf', summary: 'Generate a PDF preview of a sales invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
15
|
'get': { method: 'GET', path: '/v1/sales-invoices/{id}', summary: 'Get a sales invoice by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
-
'update': { method: 'PATCH', path: '/v1/sales-invoices/{id}', summary: 'Update a sales invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'invoiceEmailCc', 'removeFileIds'] },
|
|
16
|
+
'update': { method: 'PATCH', path: '/v1/sales-invoices/{id}', summary: 'Update a sales invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc', 'removeFileIds'] },
|
|
17
17
|
'delete': { method: 'DELETE', path: '/v1/sales-invoices/{id}', summary: 'Delete a sales invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
|
-
'
|
|
19
|
-
'
|
|
20
|
-
'
|
|
18
|
+
'payment-derivation': { method: 'GET', path: '/v1/sales-invoices/{id}/payment-derivation', summary: 'Explain how a sales invoice\'s paid amount is derived', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
+
'update-migrated-invoice': { method: 'PATCH', path: '/v1/sales-invoices/{id}/update-migrated-invoice', summary: 'Update a migrated sales invoice during migration (before go-live)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lineItems', 'files', 'notes', 'tagIds', 'tagCodes', 'invoiceEmailCc', 'totalAmountByTaxRate'] },
|
|
20
|
+
'approve': { method: 'POST', path: '/v1/sales-invoices/{id}/approve', summary: 'Approve a sales invoice from DRAFT or PENDING_APPROVAL status (generates invoice number and finalizes)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc', 'removeFileIds'] },
|
|
21
|
+
'match-sales-orders': { method: 'POST', path: '/v1/sales-invoices/{id}/match-sales-orders', summary: 'Match this invoice\'s lines to sales-order lines and to delivery-note lines', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
+
'validate': { method: 'POST', path: '/v1/sales-invoices/{id}/validate', summary: 'Validate a sales invoice update without persisting changes', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['earlyPaymentDiscounts', 'lineItems', 'discounts', 'files', 'notes', 'totalAmountByTaxRate', 'tagIds', 'tagCodes', 'invoiceEmailCc', 'removeFileIds'] },
|
|
21
23
|
'request-approval': { method: 'POST', path: '/v1/sales-invoices/{id}/request-approval', summary: 'Request approval for sales invoice (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
24
|
'send': { method: 'POST', path: '/v1/sales-invoices/{id}/send', summary: 'Mark invoice as sent to customer (APPROVED → SENT)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
25
|
'send-resend': { method: 'POST', path: '/v1/sales-invoices/{id}/resend', summary: 'Resend a sales invoice to the original delivery recipient or an alternative one', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['emailCc', 'emailBcc'] },
|
|
24
26
|
'cancel': { method: 'POST', path: '/v1/sales-invoices/{id}/cancel', summary: 'Cancel a sales invoice', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
27
|
'copy': { method: 'POST', path: '/v1/sales-invoices/{id}/copy', summary: 'Create a copy of a cancelled invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
|
-
'replace': { method: 'POST', path: '/v1/sales-invoices/{id}/replace', summary: 'Replace invoice
|
|
28
|
+
'replace': { method: 'POST', path: '/v1/sales-invoices/{id}/replace', summary: 'Replace invoice (creates a new CORRECTED_INVOICE; a SELF_BILLED_INVOICE is replaced by a new SELF_BILLED_INVOICE)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
+
'rebuild-journal': { method: 'POST', path: '/v1/sales-invoices/{id}/rebuild-journal', summary: 'Re-create the booking journal of a sales invoice', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
+
'recalculate-payment-status': { method: 'POST', path: '/v1/sales-invoices/{id}/recalculate-payment-status', summary: 'Recalculate a sales invoice\'s open amount and payment status', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
31
|
'merge': { method: 'POST', path: '/v1/sales-invoices/{id}/merge', summary: 'Merge draft invoices into this master', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['sourceIds'] },
|
|
28
|
-
'create-line-items': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items', summary: 'Add a line item to a sales invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'salesOrderLineItems', 'deliveryNoteLineItems'] },
|
|
32
|
+
'create-line-items': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items', summary: 'Add a line item to a sales invoice', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'salesOrderLineItems', 'deliveryNoteLineItems', 'tagIds', 'tagCodes'] },
|
|
29
33
|
'line-items-from-sales-order': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items/from-sales-order', summary: 'Add line items to a sales invoice from a sales order', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lines'] },
|
|
34
|
+
'line-items-from-delivery-note': { method: 'POST', path: '/v1/sales-invoices/{invoiceId}/line-items/from-delivery-note', summary: 'Add line items to a sales invoice from a delivery note', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lines'] },
|
|
30
35
|
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-invoices/{invoiceId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['invoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['items'] },
|
|
31
|
-
'update-line-items': { method: 'PATCH', path: '/v1/sales-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a sales invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'salesOrderLineItems', 'deliveryNoteLineItems'] },
|
|
36
|
+
'update-line-items': { method: 'PATCH', path: '/v1/sales-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Update a line item on a sales invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'salesOrderLineItems', 'deliveryNoteLineItems', 'tagIds', 'tagCodes'] },
|
|
32
37
|
'delete-line-items': { method: 'DELETE', path: '/v1/sales-invoices/{invoiceId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales invoice', pathParams: ['invoiceId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
38
|
'list-notes': { method: 'GET', path: '/v1/sales-invoices/{salesInvoiceId}/notes', summary: 'Get all notes for a sales invoice', pathParams: ['salesInvoiceId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
34
39
|
'create-notes': { method: 'POST', path: '/v1/sales-invoices/{salesInvoiceId}/notes', summary: 'Create a note for a sales invoice', pathParams: ['salesInvoiceId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -57,7 +62,7 @@ export default class SalesInvoices extends Command {
|
|
|
57
62
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
58
63
|
};
|
|
59
64
|
static flags = {
|
|
60
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
65
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
61
66
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
62
67
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
63
68
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -74,7 +79,7 @@ export default class SalesInvoices extends Command {
|
|
|
74
79
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
75
80
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
76
81
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
77
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
82
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
78
83
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
79
84
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
80
85
|
};
|
|
@@ -132,9 +137,9 @@ export default class SalesInvoices extends Command {
|
|
|
132
137
|
}
|
|
133
138
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
134
139
|
if (!context)
|
|
135
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
136
|
-
if (!context
|
|
137
|
-
this.error(
|
|
140
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
141
|
+
if (!hasCredentials(context))
|
|
142
|
+
this.error(missingCredentialsMessage(context));
|
|
138
143
|
const queryParams = {};
|
|
139
144
|
if (flags.page)
|
|
140
145
|
queryParams.page = flags.page;
|
|
@@ -146,6 +151,8 @@ export default class SalesInvoices extends Command {
|
|
|
146
151
|
queryParams.search = flags.search;
|
|
147
152
|
if (flags['sort-by'])
|
|
148
153
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
154
|
+
if (flags.fields)
|
|
155
|
+
queryParams.fields = flags.fields;
|
|
149
156
|
if (flags.query) {
|
|
150
157
|
try {
|
|
151
158
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -348,11 +355,6 @@ export default class SalesInvoices extends Command {
|
|
|
348
355
|
output = obj[arrayKeys[0]];
|
|
349
356
|
}
|
|
350
357
|
}
|
|
351
|
-
// Filter fields if --fields flag is provided
|
|
352
|
-
if (flags.fields) {
|
|
353
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
354
|
-
output = this.filterFields(output, fieldList);
|
|
355
|
-
}
|
|
356
358
|
// Sanitize output if requested
|
|
357
359
|
if (flags.sanitize) {
|
|
358
360
|
output = this.sanitizeOutput(output);
|
|
@@ -377,9 +379,9 @@ export default class SalesInvoices extends Command {
|
|
|
377
379
|
async runImport(flags) {
|
|
378
380
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
379
381
|
if (!context)
|
|
380
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
381
|
-
if (!context
|
|
382
|
-
this.error(
|
|
382
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
383
|
+
if (!hasCredentials(context))
|
|
384
|
+
this.error(missingCredentialsMessage(context));
|
|
383
385
|
// Get data from file, --data, or --set flags
|
|
384
386
|
let items = [];
|
|
385
387
|
if (flags.file) {
|
|
@@ -589,9 +591,9 @@ export default class SalesInvoices extends Command {
|
|
|
589
591
|
async runExport(flags) {
|
|
590
592
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
591
593
|
if (!context)
|
|
592
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
593
|
-
if (!context
|
|
594
|
-
this.error(
|
|
594
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
595
|
+
if (!hasCredentials(context))
|
|
596
|
+
this.error(missingCredentialsMessage(context));
|
|
595
597
|
const listEndpoint = ENDPOINTS['list'];
|
|
596
598
|
if (!listEndpoint)
|
|
597
599
|
this.error('Export requires a list endpoint');
|
|
@@ -607,6 +609,8 @@ export default class SalesInvoices extends Command {
|
|
|
607
609
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
608
610
|
if (flags.include)
|
|
609
611
|
baseParams.include = flags.include;
|
|
612
|
+
if (flags.fields)
|
|
613
|
+
baseParams.fields = flags.fields;
|
|
610
614
|
if (flags.query) {
|
|
611
615
|
try {
|
|
612
616
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -661,12 +665,7 @@ export default class SalesInvoices extends Command {
|
|
|
661
665
|
}
|
|
662
666
|
}
|
|
663
667
|
process.stderr.write(' done\n');
|
|
664
|
-
// Filter fields if specified
|
|
665
668
|
let output = allItems;
|
|
666
|
-
if (flags.fields) {
|
|
667
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
668
|
-
output = this.filterFields(allItems, fieldList);
|
|
669
|
-
}
|
|
670
669
|
// Sanitize output if requested
|
|
671
670
|
if (flags.sanitize) {
|
|
672
671
|
output = this.sanitizeOutput(output);
|
|
@@ -683,9 +682,9 @@ export default class SalesInvoices extends Command {
|
|
|
683
682
|
async runCamt053Import(flags) {
|
|
684
683
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
685
684
|
if (!context)
|
|
686
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
687
|
-
if (!context
|
|
688
|
-
this.error(
|
|
685
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
686
|
+
if (!hasCredentials(context))
|
|
687
|
+
this.error(missingCredentialsMessage(context));
|
|
689
688
|
if (!flags.file) {
|
|
690
689
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
691
690
|
}
|
|
@@ -773,9 +772,9 @@ export default class SalesInvoices extends Command {
|
|
|
773
772
|
}
|
|
774
773
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
775
774
|
if (!context)
|
|
776
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
777
|
-
if (!context
|
|
778
|
-
this.error(
|
|
775
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
776
|
+
if (!hasCredentials(context))
|
|
777
|
+
this.error(missingCredentialsMessage(context));
|
|
779
778
|
// Get data from file, --data, or --set flags
|
|
780
779
|
let items = [];
|
|
781
780
|
if (flags.file) {
|
|
@@ -995,56 +994,6 @@ export default class SalesInvoices extends Command {
|
|
|
995
994
|
return `\$${op}`;
|
|
996
995
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
997
996
|
}
|
|
998
|
-
filterFields(data, fields) {
|
|
999
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
1000
|
-
const pick = (obj, keys) => {
|
|
1001
|
-
// Group keys by their first segment so sibling sub-paths
|
|
1002
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
1003
|
-
// and don't clobber each other on merge.
|
|
1004
|
-
const directKeys = new Set();
|
|
1005
|
-
const nestedKeys = new Map();
|
|
1006
|
-
for (const key of keys) {
|
|
1007
|
-
if (key.split('.').some(isUnsafeKey))
|
|
1008
|
-
continue;
|
|
1009
|
-
if (key.includes('.')) {
|
|
1010
|
-
const [first, ...rest] = key.split('.');
|
|
1011
|
-
if (isUnsafeKey(first))
|
|
1012
|
-
continue;
|
|
1013
|
-
const list = nestedKeys.get(first);
|
|
1014
|
-
if (list) {
|
|
1015
|
-
list.push(rest.join('.'));
|
|
1016
|
-
}
|
|
1017
|
-
else {
|
|
1018
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1019
|
-
}
|
|
1020
|
-
}
|
|
1021
|
-
else {
|
|
1022
|
-
directKeys.add(key);
|
|
1023
|
-
}
|
|
1024
|
-
}
|
|
1025
|
-
const result = {};
|
|
1026
|
-
for (const key of directKeys) {
|
|
1027
|
-
if (key in obj)
|
|
1028
|
-
result[key] = obj[key];
|
|
1029
|
-
}
|
|
1030
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1031
|
-
const val = obj[first];
|
|
1032
|
-
if (!val || typeof val !== 'object')
|
|
1033
|
-
continue;
|
|
1034
|
-
if (Array.isArray(val)) {
|
|
1035
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1036
|
-
}
|
|
1037
|
-
else {
|
|
1038
|
-
result[first] = pick(val, restKeys);
|
|
1039
|
-
}
|
|
1040
|
-
}
|
|
1041
|
-
return result;
|
|
1042
|
-
};
|
|
1043
|
-
if (Array.isArray(data)) {
|
|
1044
|
-
return data.map(item => pick(item, fields));
|
|
1045
|
-
}
|
|
1046
|
-
return pick(data, fields);
|
|
1047
|
-
}
|
|
1048
997
|
sanitizeOutput(data) {
|
|
1049
998
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1050
999
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Command, Args, Flags } from '@oclif/core';
|
|
2
2
|
import { apiRequest, ApiError, normalizeSortBy } from '../lib/api-client.js';
|
|
3
|
-
import { getCurrentContext, getContext, getBaseUrls } from '../lib/config.js';
|
|
3
|
+
import { getCurrentContext, getContext, getBaseUrls, hasCredentials, missingCredentialsMessage } from '../lib/config.js';
|
|
4
4
|
// Add 'import' action if both create and update exist
|
|
5
5
|
const HAS_IMPORT = true;
|
|
6
6
|
const HAS_EXPORT = true;
|
|
@@ -9,10 +9,10 @@ const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "create-invoices", "get", "update", "delete", "pdf", "submit", "approve", "accept", "terminate", "cancel", "replace", "merge", "create-invoice", "send", "create-line-items", "update-line-items-reorder", "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"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/sales-orders', summary: 'Get all sales orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'discounts', 'lineItems'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/sales-orders', summary: 'Create a new sales order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems'] },
|
|
13
13
|
'create-invoices': { method: 'POST', path: '/v1/sales-orders/create-invoices', summary: 'Create ONE draft invoice from several sales orders (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderIds'] },
|
|
14
14
|
'get': { method: 'GET', path: '/v1/sales-orders/{id}', summary: 'Get a sales order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
-
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'discounts', 'lineItems', 'removeFileIds'] },
|
|
15
|
+
'update': { method: 'PATCH', path: '/v1/sales-orders/{id}', summary: 'Update a sales order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'discounts', 'lineItems', 'removeFileIds'] },
|
|
16
16
|
'delete': { method: 'DELETE', path: '/v1/sales-orders/{id}', summary: 'Delete a sales order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
17
|
'pdf': { method: 'GET', path: '/v1/sales-orders/{id}/pdf', summary: 'Render a sales order PDF using the configured PDF processor', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
18
|
'submit': { method: 'POST', path: '/v1/sales-orders/{id}/submit', summary: 'Submit order for approval (DRAFT or CANCELLED → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -24,9 +24,9 @@ const ENDPOINTS = {
|
|
|
24
24
|
'merge': { method: 'POST', path: '/v1/sales-orders/{id}/merge', summary: 'Merge draft orders into this master', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['sourceIds'] },
|
|
25
25
|
'create-invoice': { method: 'POST', path: '/v1/sales-orders/{id}/create-invoice', summary: 'Create invoice from sales order (ACCEPTED → creates DRAFT invoice)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
26
|
'send': { method: 'POST', path: '/v1/sales-orders/{id}/send', summary: 'Send a sales order to the customer by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['emailCc', 'emailBcc', 'files'] },
|
|
27
|
-
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
|
|
27
|
+
'create-line-items': { method: 'POST', path: '/v1/sales-orders/{orderId}/line-items', summary: 'Add a line item to a sales order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
28
28
|
'update-line-items-reorder': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['items'] },
|
|
29
|
-
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
|
|
29
|
+
'update-line-items': { method: 'PATCH', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
30
30
|
'delete-line-items': { method: 'DELETE', path: '/v1/sales-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a sales order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
31
|
'list-notes': { method: 'GET', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Get all notes for a sales order', pathParams: ['salesOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
32
|
'create-notes': { method: 'POST', path: '/v1/sales-orders/{salesOrderId}/notes', summary: 'Create a note for a sales order', pathParams: ['salesOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -55,7 +55,7 @@ export default class SalesOrders extends Command {
|
|
|
55
55
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
56
56
|
};
|
|
57
57
|
static flags = {
|
|
58
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
58
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
59
59
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
60
60
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
61
61
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -72,7 +72,7 @@ export default class SalesOrders extends Command {
|
|
|
72
72
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
73
73
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
74
74
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
75
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
75
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
76
76
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
77
77
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
78
78
|
};
|
|
@@ -130,9 +130,9 @@ export default class SalesOrders extends Command {
|
|
|
130
130
|
}
|
|
131
131
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
132
132
|
if (!context)
|
|
133
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
134
|
-
if (!context
|
|
135
|
-
this.error(
|
|
133
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
134
|
+
if (!hasCredentials(context))
|
|
135
|
+
this.error(missingCredentialsMessage(context));
|
|
136
136
|
const queryParams = {};
|
|
137
137
|
if (flags.page)
|
|
138
138
|
queryParams.page = flags.page;
|
|
@@ -144,6 +144,8 @@ export default class SalesOrders extends Command {
|
|
|
144
144
|
queryParams.search = flags.search;
|
|
145
145
|
if (flags['sort-by'])
|
|
146
146
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
147
|
+
if (flags.fields)
|
|
148
|
+
queryParams.fields = flags.fields;
|
|
147
149
|
if (flags.query) {
|
|
148
150
|
try {
|
|
149
151
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -346,11 +348,6 @@ export default class SalesOrders extends Command {
|
|
|
346
348
|
output = obj[arrayKeys[0]];
|
|
347
349
|
}
|
|
348
350
|
}
|
|
349
|
-
// Filter fields if --fields flag is provided
|
|
350
|
-
if (flags.fields) {
|
|
351
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
352
|
-
output = this.filterFields(output, fieldList);
|
|
353
|
-
}
|
|
354
351
|
// Sanitize output if requested
|
|
355
352
|
if (flags.sanitize) {
|
|
356
353
|
output = this.sanitizeOutput(output);
|
|
@@ -375,9 +372,9 @@ export default class SalesOrders extends Command {
|
|
|
375
372
|
async runImport(flags) {
|
|
376
373
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
377
374
|
if (!context)
|
|
378
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
379
|
-
if (!context
|
|
380
|
-
this.error(
|
|
375
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
376
|
+
if (!hasCredentials(context))
|
|
377
|
+
this.error(missingCredentialsMessage(context));
|
|
381
378
|
// Get data from file, --data, or --set flags
|
|
382
379
|
let items = [];
|
|
383
380
|
if (flags.file) {
|
|
@@ -587,9 +584,9 @@ export default class SalesOrders extends Command {
|
|
|
587
584
|
async runExport(flags) {
|
|
588
585
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
589
586
|
if (!context)
|
|
590
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
591
|
-
if (!context
|
|
592
|
-
this.error(
|
|
587
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
588
|
+
if (!hasCredentials(context))
|
|
589
|
+
this.error(missingCredentialsMessage(context));
|
|
593
590
|
const listEndpoint = ENDPOINTS['list'];
|
|
594
591
|
if (!listEndpoint)
|
|
595
592
|
this.error('Export requires a list endpoint');
|
|
@@ -605,6 +602,8 @@ export default class SalesOrders extends Command {
|
|
|
605
602
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
606
603
|
if (flags.include)
|
|
607
604
|
baseParams.include = flags.include;
|
|
605
|
+
if (flags.fields)
|
|
606
|
+
baseParams.fields = flags.fields;
|
|
608
607
|
if (flags.query) {
|
|
609
608
|
try {
|
|
610
609
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -659,12 +658,7 @@ export default class SalesOrders extends Command {
|
|
|
659
658
|
}
|
|
660
659
|
}
|
|
661
660
|
process.stderr.write(' done\n');
|
|
662
|
-
// Filter fields if specified
|
|
663
661
|
let output = allItems;
|
|
664
|
-
if (flags.fields) {
|
|
665
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
666
|
-
output = this.filterFields(allItems, fieldList);
|
|
667
|
-
}
|
|
668
662
|
// Sanitize output if requested
|
|
669
663
|
if (flags.sanitize) {
|
|
670
664
|
output = this.sanitizeOutput(output);
|
|
@@ -681,9 +675,9 @@ export default class SalesOrders extends Command {
|
|
|
681
675
|
async runCamt053Import(flags) {
|
|
682
676
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
683
677
|
if (!context)
|
|
684
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
685
|
-
if (!context
|
|
686
|
-
this.error(
|
|
678
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
679
|
+
if (!hasCredentials(context))
|
|
680
|
+
this.error(missingCredentialsMessage(context));
|
|
687
681
|
if (!flags.file) {
|
|
688
682
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
689
683
|
}
|
|
@@ -771,9 +765,9 @@ export default class SalesOrders extends Command {
|
|
|
771
765
|
}
|
|
772
766
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
773
767
|
if (!context)
|
|
774
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
775
|
-
if (!context
|
|
776
|
-
this.error(
|
|
768
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
769
|
+
if (!hasCredentials(context))
|
|
770
|
+
this.error(missingCredentialsMessage(context));
|
|
777
771
|
// Get data from file, --data, or --set flags
|
|
778
772
|
let items = [];
|
|
779
773
|
if (flags.file) {
|
|
@@ -993,56 +987,6 @@ export default class SalesOrders extends Command {
|
|
|
993
987
|
return `\$${op}`;
|
|
994
988
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
995
989
|
}
|
|
996
|
-
filterFields(data, fields) {
|
|
997
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
998
|
-
const pick = (obj, keys) => {
|
|
999
|
-
// Group keys by their first segment so sibling sub-paths
|
|
1000
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
1001
|
-
// and don't clobber each other on merge.
|
|
1002
|
-
const directKeys = new Set();
|
|
1003
|
-
const nestedKeys = new Map();
|
|
1004
|
-
for (const key of keys) {
|
|
1005
|
-
if (key.split('.').some(isUnsafeKey))
|
|
1006
|
-
continue;
|
|
1007
|
-
if (key.includes('.')) {
|
|
1008
|
-
const [first, ...rest] = key.split('.');
|
|
1009
|
-
if (isUnsafeKey(first))
|
|
1010
|
-
continue;
|
|
1011
|
-
const list = nestedKeys.get(first);
|
|
1012
|
-
if (list) {
|
|
1013
|
-
list.push(rest.join('.'));
|
|
1014
|
-
}
|
|
1015
|
-
else {
|
|
1016
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1017
|
-
}
|
|
1018
|
-
}
|
|
1019
|
-
else {
|
|
1020
|
-
directKeys.add(key);
|
|
1021
|
-
}
|
|
1022
|
-
}
|
|
1023
|
-
const result = {};
|
|
1024
|
-
for (const key of directKeys) {
|
|
1025
|
-
if (key in obj)
|
|
1026
|
-
result[key] = obj[key];
|
|
1027
|
-
}
|
|
1028
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1029
|
-
const val = obj[first];
|
|
1030
|
-
if (!val || typeof val !== 'object')
|
|
1031
|
-
continue;
|
|
1032
|
-
if (Array.isArray(val)) {
|
|
1033
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1034
|
-
}
|
|
1035
|
-
else {
|
|
1036
|
-
result[first] = pick(val, restKeys);
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
return result;
|
|
1040
|
-
};
|
|
1041
|
-
if (Array.isArray(data)) {
|
|
1042
|
-
return data.map(item => pick(item, fields));
|
|
1043
|
-
}
|
|
1044
|
-
return pick(data, fields);
|
|
1045
|
-
}
|
|
1046
990
|
sanitizeOutput(data) {
|
|
1047
991
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1048
992
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|