@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,30 +1,36 @@
|
|
|
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"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "cancel", "create-line-items", "update-line-items", "delete-line-items", "list-notes", "create-notes", "get-notes", "update-notes", "delete-notes"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
8
|
+
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "get", "update", "delete", "submit", "approve", "decline", "cancel", "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"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
11
|
'list': { method: 'GET', path: '/v1/purchase-orders', summary: 'Get all purchase orders with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/purchase-orders', summary: 'Create a new purchase order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'notes', 'discounts', 'lineItems'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/purchase-orders', summary: 'Create a new purchase order', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'files', 'notes', 'discounts', 'lineItems'] },
|
|
13
13
|
'get': { method: 'GET', path: '/v1/purchase-orders/{id}', summary: 'Get a purchase order by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
-
'update': { method: 'PATCH', path: '/v1/purchase-orders/{id}', summary: 'Update a purchase order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'notes', 'discounts', 'lineItems'] },
|
|
14
|
+
'update': { method: 'PATCH', path: '/v1/purchase-orders/{id}', summary: 'Update a purchase order', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['tagIds', 'tagCodes', 'files', 'notes', 'discounts', 'lineItems', 'removeFileIds'] },
|
|
15
15
|
'delete': { method: 'DELETE', path: '/v1/purchase-orders/{id}', summary: 'Delete a purchase order', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
16
|
'submit': { method: 'POST', path: '/v1/purchase-orders/{id}/submit', summary: 'Submit order for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
17
|
'approve': { method: 'POST', path: '/v1/purchase-orders/{id}/approve', summary: 'Approve order (PENDING_APPROVAL → APPROVED). Generates order number if missing.', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
18
18
|
'decline': { method: 'POST', path: '/v1/purchase-orders/{id}/decline', summary: 'Decline order (PENDING_APPROVAL → DECLINED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
19
|
'cancel': { method: 'POST', path: '/v1/purchase-orders/{id}/cancel', summary: 'Cancel order (DRAFT, PENDING_APPROVAL or APPROVED → CANCELLED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
|
-
'create-line-items': { method: 'POST', path: '/v1/purchase-orders/{orderId}/line-items', summary: 'Add a line item to a purchase order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
|
|
21
|
-
'update-line-items': { method: 'PATCH', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts'] },
|
|
20
|
+
'create-line-items': { method: 'POST', path: '/v1/purchase-orders/{orderId}/line-items', summary: 'Add a line item to a purchase order', pathParams: ['orderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
21
|
+
'update-line-items': { method: 'PATCH', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Update a line item on a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['discounts', 'tagIds', 'tagCodes'] },
|
|
22
22
|
'delete-line-items': { method: 'DELETE', path: '/v1/purchase-orders/{orderId}/line-items/{lineItemId}', summary: 'Remove a line item from a purchase order', pathParams: ['orderId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
23
|
'list-notes': { method: 'GET', path: '/v1/purchase-orders/{purchaseOrderId}/notes', summary: 'Get all notes for a purchase order', pathParams: ['purchaseOrderId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
24
|
'create-notes': { method: 'POST', path: '/v1/purchase-orders/{purchaseOrderId}/notes', summary: 'Create a note for a purchase order', pathParams: ['purchaseOrderId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
25
25
|
'get-notes': { method: 'GET', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Get a specific note for a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
26
26
|
'update-notes': { method: 'PATCH', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Update a note for a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
27
27
|
'delete-notes': { method: 'DELETE', path: '/v1/purchase-orders/{purchaseOrderId}/notes/{noteId}', summary: 'Delete a note from a purchase order', pathParams: ['purchaseOrderId', 'noteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
|
+
'list-files': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files', summary: 'List file attachments for a purchase order', pathParams: ['parentId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
29
|
+
'create-files': { method: 'POST', path: '/v1/purchase-orders/{parentId}/files', summary: 'Upload a file attachment to a purchase order', pathParams: ['parentId'], hasBody: false, isMultipart: true, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
|
+
'get-files': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Get a file attachment for a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
|
+
'update-files': { method: 'PATCH', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Update file attachment metadata for a purchase order', pathParams: ['parentId', 'fileId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
32
|
+
'delete-files': { method: 'DELETE', path: '/v1/purchase-orders/{parentId}/files/{fileId}', summary: 'Delete a file attachment from a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
33
|
+
'files-download': { method: 'GET', path: '/v1/purchase-orders/{parentId}/files/{fileId}/download', summary: 'Download a file attachment from a purchase order', pathParams: ['parentId', 'fileId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
28
34
|
};
|
|
29
35
|
export default class PurchaseOrders extends Command {
|
|
30
36
|
static description = 'Manage purchase orders';
|
|
@@ -41,7 +47,7 @@ export default class PurchaseOrders extends Command {
|
|
|
41
47
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
42
48
|
};
|
|
43
49
|
static flags = {
|
|
44
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
50
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
45
51
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
46
52
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
47
53
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -50,6 +56,7 @@ export default class PurchaseOrders extends Command {
|
|
|
50
56
|
set: Flags.string({ char: 's', description: 'Set individual field (e.g. --set firstName=John)', multiple: true }),
|
|
51
57
|
'line-item-id': Flags.string({ description: 'Path param: lineItemId' }),
|
|
52
58
|
'note-id': Flags.string({ description: 'Path param: noteId' }),
|
|
59
|
+
'file-id': Flags.string({ description: 'Path param: fileId' }),
|
|
53
60
|
page: Flags.string({ description: 'Page number' }),
|
|
54
61
|
limit: Flags.string({ description: 'Items per page' }),
|
|
55
62
|
include: Flags.string({ description: 'Relations to include' }),
|
|
@@ -57,7 +64,7 @@ export default class PurchaseOrders extends Command {
|
|
|
57
64
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
58
65
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
59
66
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
60
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
67
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
61
68
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
62
69
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
63
70
|
};
|
|
@@ -115,9 +122,9 @@ export default class PurchaseOrders extends Command {
|
|
|
115
122
|
}
|
|
116
123
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
117
124
|
if (!context)
|
|
118
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
119
|
-
if (!context
|
|
120
|
-
this.error(
|
|
125
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
126
|
+
if (!hasCredentials(context))
|
|
127
|
+
this.error(missingCredentialsMessage(context));
|
|
121
128
|
const queryParams = {};
|
|
122
129
|
if (flags.page)
|
|
123
130
|
queryParams.page = flags.page;
|
|
@@ -129,6 +136,8 @@ export default class PurchaseOrders extends Command {
|
|
|
129
136
|
queryParams.search = flags.search;
|
|
130
137
|
if (flags['sort-by'])
|
|
131
138
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
139
|
+
if (flags.fields)
|
|
140
|
+
queryParams.fields = flags.fields;
|
|
132
141
|
if (flags.query) {
|
|
133
142
|
try {
|
|
134
143
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -331,11 +340,6 @@ export default class PurchaseOrders extends Command {
|
|
|
331
340
|
output = obj[arrayKeys[0]];
|
|
332
341
|
}
|
|
333
342
|
}
|
|
334
|
-
// Filter fields if --fields flag is provided
|
|
335
|
-
if (flags.fields) {
|
|
336
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
337
|
-
output = this.filterFields(output, fieldList);
|
|
338
|
-
}
|
|
339
343
|
// Sanitize output if requested
|
|
340
344
|
if (flags.sanitize) {
|
|
341
345
|
output = this.sanitizeOutput(output);
|
|
@@ -360,9 +364,9 @@ export default class PurchaseOrders extends Command {
|
|
|
360
364
|
async runImport(flags) {
|
|
361
365
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
362
366
|
if (!context)
|
|
363
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
364
|
-
if (!context
|
|
365
|
-
this.error(
|
|
367
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
368
|
+
if (!hasCredentials(context))
|
|
369
|
+
this.error(missingCredentialsMessage(context));
|
|
366
370
|
// Get data from file, --data, or --set flags
|
|
367
371
|
let items = [];
|
|
368
372
|
if (flags.file) {
|
|
@@ -572,9 +576,9 @@ export default class PurchaseOrders extends Command {
|
|
|
572
576
|
async runExport(flags) {
|
|
573
577
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
574
578
|
if (!context)
|
|
575
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
576
|
-
if (!context
|
|
577
|
-
this.error(
|
|
579
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
580
|
+
if (!hasCredentials(context))
|
|
581
|
+
this.error(missingCredentialsMessage(context));
|
|
578
582
|
const listEndpoint = ENDPOINTS['list'];
|
|
579
583
|
if (!listEndpoint)
|
|
580
584
|
this.error('Export requires a list endpoint');
|
|
@@ -590,6 +594,8 @@ export default class PurchaseOrders extends Command {
|
|
|
590
594
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
591
595
|
if (flags.include)
|
|
592
596
|
baseParams.include = flags.include;
|
|
597
|
+
if (flags.fields)
|
|
598
|
+
baseParams.fields = flags.fields;
|
|
593
599
|
if (flags.query) {
|
|
594
600
|
try {
|
|
595
601
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -644,12 +650,7 @@ export default class PurchaseOrders extends Command {
|
|
|
644
650
|
}
|
|
645
651
|
}
|
|
646
652
|
process.stderr.write(' done\n');
|
|
647
|
-
// Filter fields if specified
|
|
648
653
|
let output = allItems;
|
|
649
|
-
if (flags.fields) {
|
|
650
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
651
|
-
output = this.filterFields(allItems, fieldList);
|
|
652
|
-
}
|
|
653
654
|
// Sanitize output if requested
|
|
654
655
|
if (flags.sanitize) {
|
|
655
656
|
output = this.sanitizeOutput(output);
|
|
@@ -666,9 +667,9 @@ export default class PurchaseOrders extends Command {
|
|
|
666
667
|
async runCamt053Import(flags) {
|
|
667
668
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
668
669
|
if (!context)
|
|
669
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
670
|
-
if (!context
|
|
671
|
-
this.error(
|
|
670
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
671
|
+
if (!hasCredentials(context))
|
|
672
|
+
this.error(missingCredentialsMessage(context));
|
|
672
673
|
if (!flags.file) {
|
|
673
674
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
674
675
|
}
|
|
@@ -756,9 +757,9 @@ export default class PurchaseOrders extends Command {
|
|
|
756
757
|
}
|
|
757
758
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
758
759
|
if (!context)
|
|
759
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
760
|
-
if (!context
|
|
761
|
-
this.error(
|
|
760
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
761
|
+
if (!hasCredentials(context))
|
|
762
|
+
this.error(missingCredentialsMessage(context));
|
|
762
763
|
// Get data from file, --data, or --set flags
|
|
763
764
|
let items = [];
|
|
764
765
|
if (flags.file) {
|
|
@@ -978,56 +979,6 @@ export default class PurchaseOrders extends Command {
|
|
|
978
979
|
return `\$${op}`;
|
|
979
980
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
980
981
|
}
|
|
981
|
-
filterFields(data, fields) {
|
|
982
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
983
|
-
const pick = (obj, keys) => {
|
|
984
|
-
// Group keys by their first segment so sibling sub-paths
|
|
985
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
986
|
-
// and don't clobber each other on merge.
|
|
987
|
-
const directKeys = new Set();
|
|
988
|
-
const nestedKeys = new Map();
|
|
989
|
-
for (const key of keys) {
|
|
990
|
-
if (key.split('.').some(isUnsafeKey))
|
|
991
|
-
continue;
|
|
992
|
-
if (key.includes('.')) {
|
|
993
|
-
const [first, ...rest] = key.split('.');
|
|
994
|
-
if (isUnsafeKey(first))
|
|
995
|
-
continue;
|
|
996
|
-
const list = nestedKeys.get(first);
|
|
997
|
-
if (list) {
|
|
998
|
-
list.push(rest.join('.'));
|
|
999
|
-
}
|
|
1000
|
-
else {
|
|
1001
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1002
|
-
}
|
|
1003
|
-
}
|
|
1004
|
-
else {
|
|
1005
|
-
directKeys.add(key);
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
const result = {};
|
|
1009
|
-
for (const key of directKeys) {
|
|
1010
|
-
if (key in obj)
|
|
1011
|
-
result[key] = obj[key];
|
|
1012
|
-
}
|
|
1013
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1014
|
-
const val = obj[first];
|
|
1015
|
-
if (!val || typeof val !== 'object')
|
|
1016
|
-
continue;
|
|
1017
|
-
if (Array.isArray(val)) {
|
|
1018
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1019
|
-
}
|
|
1020
|
-
else {
|
|
1021
|
-
result[first] = pick(val, restKeys);
|
|
1022
|
-
}
|
|
1023
|
-
}
|
|
1024
|
-
return result;
|
|
1025
|
-
};
|
|
1026
|
-
if (Array.isArray(data)) {
|
|
1027
|
-
return data.map(item => pick(item, fields));
|
|
1028
|
-
}
|
|
1029
|
-
return pick(data, fields);
|
|
1030
|
-
}
|
|
1031
982
|
sanitizeOutput(data) {
|
|
1032
983
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1033
984
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
package/dist/commands/reports.js
CHANGED
|
@@ -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 = false;
|
|
6
6
|
const HAS_EXPORT = false;
|
|
@@ -8,7 +8,7 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["profit-and-loss", "balance-sheet", "gl-detail", "gl-detail-export-xlsx", "gl-detail-export-pdf"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'profit-and-loss': { method: '
|
|
11
|
+
'profit-and-loss': { method: 'POST', path: '/v1/reports/profit-and-loss', summary: 'Get Profit & Loss report', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['ledgerIds', 'periods'] },
|
|
12
12
|
'balance-sheet': { method: 'GET', path: '/v1/reports/balance-sheet', summary: 'Generate Balance Sheet report', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
13
|
'gl-detail': { method: 'POST', path: '/v1/reports/gl-detail', summary: 'Get GL Detail report', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['accountIds', 'accountCodes'] },
|
|
14
14
|
'gl-detail-export-xlsx': { method: 'POST', path: '/v1/reports/gl-detail/export/xlsx', summary: 'Export GL Detail report as Excel (one sheet per account)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: true, bodyArrayFields: ['accountIds', 'accountCodes'] },
|
|
@@ -22,7 +22,7 @@ export default class Reports extends Command {
|
|
|
22
22
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
23
23
|
};
|
|
24
24
|
static flags = {
|
|
25
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
25
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
26
26
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
27
27
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
28
28
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -36,7 +36,7 @@ export default class Reports extends Command {
|
|
|
36
36
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
37
37
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
38
38
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
39
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
39
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
40
40
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
41
41
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
42
42
|
};
|
|
@@ -94,9 +94,9 @@ export default class Reports extends Command {
|
|
|
94
94
|
}
|
|
95
95
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
96
96
|
if (!context)
|
|
97
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
98
|
-
if (!context
|
|
99
|
-
this.error(
|
|
97
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
98
|
+
if (!hasCredentials(context))
|
|
99
|
+
this.error(missingCredentialsMessage(context));
|
|
100
100
|
const queryParams = {};
|
|
101
101
|
if (flags.page)
|
|
102
102
|
queryParams.page = flags.page;
|
|
@@ -108,6 +108,8 @@ export default class Reports extends Command {
|
|
|
108
108
|
queryParams.search = flags.search;
|
|
109
109
|
if (flags['sort-by'])
|
|
110
110
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
111
|
+
if (flags.fields)
|
|
112
|
+
queryParams.fields = flags.fields;
|
|
111
113
|
if (flags.query) {
|
|
112
114
|
try {
|
|
113
115
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -310,11 +312,6 @@ export default class Reports extends Command {
|
|
|
310
312
|
output = obj[arrayKeys[0]];
|
|
311
313
|
}
|
|
312
314
|
}
|
|
313
|
-
// Filter fields if --fields flag is provided
|
|
314
|
-
if (flags.fields) {
|
|
315
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
316
|
-
output = this.filterFields(output, fieldList);
|
|
317
|
-
}
|
|
318
315
|
// Sanitize output if requested
|
|
319
316
|
if (flags.sanitize) {
|
|
320
317
|
output = this.sanitizeOutput(output);
|
|
@@ -339,9 +336,9 @@ export default class Reports extends Command {
|
|
|
339
336
|
async runImport(flags) {
|
|
340
337
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
341
338
|
if (!context)
|
|
342
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
343
|
-
if (!context
|
|
344
|
-
this.error(
|
|
339
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
340
|
+
if (!hasCredentials(context))
|
|
341
|
+
this.error(missingCredentialsMessage(context));
|
|
345
342
|
// Get data from file, --data, or --set flags
|
|
346
343
|
let items = [];
|
|
347
344
|
if (flags.file) {
|
|
@@ -551,9 +548,9 @@ export default class Reports extends Command {
|
|
|
551
548
|
async runExport(flags) {
|
|
552
549
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
553
550
|
if (!context)
|
|
554
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
555
|
-
if (!context
|
|
556
|
-
this.error(
|
|
551
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
552
|
+
if (!hasCredentials(context))
|
|
553
|
+
this.error(missingCredentialsMessage(context));
|
|
557
554
|
const listEndpoint = ENDPOINTS['list'];
|
|
558
555
|
if (!listEndpoint)
|
|
559
556
|
this.error('Export requires a list endpoint');
|
|
@@ -569,6 +566,8 @@ export default class Reports extends Command {
|
|
|
569
566
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
570
567
|
if (flags.include)
|
|
571
568
|
baseParams.include = flags.include;
|
|
569
|
+
if (flags.fields)
|
|
570
|
+
baseParams.fields = flags.fields;
|
|
572
571
|
if (flags.query) {
|
|
573
572
|
try {
|
|
574
573
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -623,12 +622,7 @@ export default class Reports extends Command {
|
|
|
623
622
|
}
|
|
624
623
|
}
|
|
625
624
|
process.stderr.write(' done\n');
|
|
626
|
-
// Filter fields if specified
|
|
627
625
|
let output = allItems;
|
|
628
|
-
if (flags.fields) {
|
|
629
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
630
|
-
output = this.filterFields(allItems, fieldList);
|
|
631
|
-
}
|
|
632
626
|
// Sanitize output if requested
|
|
633
627
|
if (flags.sanitize) {
|
|
634
628
|
output = this.sanitizeOutput(output);
|
|
@@ -645,9 +639,9 @@ export default class Reports extends Command {
|
|
|
645
639
|
async runCamt053Import(flags) {
|
|
646
640
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
647
641
|
if (!context)
|
|
648
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
649
|
-
if (!context
|
|
650
|
-
this.error(
|
|
642
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
643
|
+
if (!hasCredentials(context))
|
|
644
|
+
this.error(missingCredentialsMessage(context));
|
|
651
645
|
if (!flags.file) {
|
|
652
646
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
653
647
|
}
|
|
@@ -735,9 +729,9 @@ export default class Reports extends Command {
|
|
|
735
729
|
}
|
|
736
730
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
737
731
|
if (!context)
|
|
738
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
739
|
-
if (!context
|
|
740
|
-
this.error(
|
|
732
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
733
|
+
if (!hasCredentials(context))
|
|
734
|
+
this.error(missingCredentialsMessage(context));
|
|
741
735
|
// Get data from file, --data, or --set flags
|
|
742
736
|
let items = [];
|
|
743
737
|
if (flags.file) {
|
|
@@ -957,56 +951,6 @@ export default class Reports extends Command {
|
|
|
957
951
|
return `\$${op}`;
|
|
958
952
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
959
953
|
}
|
|
960
|
-
filterFields(data, fields) {
|
|
961
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
962
|
-
const pick = (obj, keys) => {
|
|
963
|
-
// Group keys by their first segment so sibling sub-paths
|
|
964
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
965
|
-
// and don't clobber each other on merge.
|
|
966
|
-
const directKeys = new Set();
|
|
967
|
-
const nestedKeys = new Map();
|
|
968
|
-
for (const key of keys) {
|
|
969
|
-
if (key.split('.').some(isUnsafeKey))
|
|
970
|
-
continue;
|
|
971
|
-
if (key.includes('.')) {
|
|
972
|
-
const [first, ...rest] = key.split('.');
|
|
973
|
-
if (isUnsafeKey(first))
|
|
974
|
-
continue;
|
|
975
|
-
const list = nestedKeys.get(first);
|
|
976
|
-
if (list) {
|
|
977
|
-
list.push(rest.join('.'));
|
|
978
|
-
}
|
|
979
|
-
else {
|
|
980
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
981
|
-
}
|
|
982
|
-
}
|
|
983
|
-
else {
|
|
984
|
-
directKeys.add(key);
|
|
985
|
-
}
|
|
986
|
-
}
|
|
987
|
-
const result = {};
|
|
988
|
-
for (const key of directKeys) {
|
|
989
|
-
if (key in obj)
|
|
990
|
-
result[key] = obj[key];
|
|
991
|
-
}
|
|
992
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
993
|
-
const val = obj[first];
|
|
994
|
-
if (!val || typeof val !== 'object')
|
|
995
|
-
continue;
|
|
996
|
-
if (Array.isArray(val)) {
|
|
997
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
998
|
-
}
|
|
999
|
-
else {
|
|
1000
|
-
result[first] = pick(val, restKeys);
|
|
1001
|
-
}
|
|
1002
|
-
}
|
|
1003
|
-
return result;
|
|
1004
|
-
};
|
|
1005
|
-
if (Array.isArray(data)) {
|
|
1006
|
-
return data.map(item => pick(item, fields));
|
|
1007
|
-
}
|
|
1008
|
-
return pick(data, fields);
|
|
1009
|
-
}
|
|
1010
954
|
sanitizeOutput(data) {
|
|
1011
955
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1012
956
|
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;
|
|
@@ -29,7 +29,7 @@ export default class SalaryTypes extends Command {
|
|
|
29
29
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
30
30
|
};
|
|
31
31
|
static flags = {
|
|
32
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
32
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
33
33
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
34
34
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
35
35
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -43,7 +43,7 @@ export default class SalaryTypes extends Command {
|
|
|
43
43
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
44
44
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
45
45
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
46
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
46
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
47
47
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
48
48
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
49
49
|
};
|
|
@@ -101,9 +101,9 @@ export default class SalaryTypes extends Command {
|
|
|
101
101
|
}
|
|
102
102
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
103
103
|
if (!context)
|
|
104
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
105
|
-
if (!context
|
|
106
|
-
this.error(
|
|
104
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
105
|
+
if (!hasCredentials(context))
|
|
106
|
+
this.error(missingCredentialsMessage(context));
|
|
107
107
|
const queryParams = {};
|
|
108
108
|
if (flags.page)
|
|
109
109
|
queryParams.page = flags.page;
|
|
@@ -115,6 +115,8 @@ export default class SalaryTypes extends Command {
|
|
|
115
115
|
queryParams.search = flags.search;
|
|
116
116
|
if (flags['sort-by'])
|
|
117
117
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
118
|
+
if (flags.fields)
|
|
119
|
+
queryParams.fields = flags.fields;
|
|
118
120
|
if (flags.query) {
|
|
119
121
|
try {
|
|
120
122
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -317,11 +319,6 @@ export default class SalaryTypes extends Command {
|
|
|
317
319
|
output = obj[arrayKeys[0]];
|
|
318
320
|
}
|
|
319
321
|
}
|
|
320
|
-
// Filter fields if --fields flag is provided
|
|
321
|
-
if (flags.fields) {
|
|
322
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
323
|
-
output = this.filterFields(output, fieldList);
|
|
324
|
-
}
|
|
325
322
|
// Sanitize output if requested
|
|
326
323
|
if (flags.sanitize) {
|
|
327
324
|
output = this.sanitizeOutput(output);
|
|
@@ -346,9 +343,9 @@ export default class SalaryTypes extends Command {
|
|
|
346
343
|
async runImport(flags) {
|
|
347
344
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
348
345
|
if (!context)
|
|
349
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
350
|
-
if (!context
|
|
351
|
-
this.error(
|
|
346
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
347
|
+
if (!hasCredentials(context))
|
|
348
|
+
this.error(missingCredentialsMessage(context));
|
|
352
349
|
// Get data from file, --data, or --set flags
|
|
353
350
|
let items = [];
|
|
354
351
|
if (flags.file) {
|
|
@@ -558,9 +555,9 @@ export default class SalaryTypes extends Command {
|
|
|
558
555
|
async runExport(flags) {
|
|
559
556
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
560
557
|
if (!context)
|
|
561
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
562
|
-
if (!context
|
|
563
|
-
this.error(
|
|
558
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
559
|
+
if (!hasCredentials(context))
|
|
560
|
+
this.error(missingCredentialsMessage(context));
|
|
564
561
|
const listEndpoint = ENDPOINTS['list'];
|
|
565
562
|
if (!listEndpoint)
|
|
566
563
|
this.error('Export requires a list endpoint');
|
|
@@ -576,6 +573,8 @@ export default class SalaryTypes extends Command {
|
|
|
576
573
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
577
574
|
if (flags.include)
|
|
578
575
|
baseParams.include = flags.include;
|
|
576
|
+
if (flags.fields)
|
|
577
|
+
baseParams.fields = flags.fields;
|
|
579
578
|
if (flags.query) {
|
|
580
579
|
try {
|
|
581
580
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -630,12 +629,7 @@ export default class SalaryTypes extends Command {
|
|
|
630
629
|
}
|
|
631
630
|
}
|
|
632
631
|
process.stderr.write(' done\n');
|
|
633
|
-
// Filter fields if specified
|
|
634
632
|
let output = allItems;
|
|
635
|
-
if (flags.fields) {
|
|
636
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
637
|
-
output = this.filterFields(allItems, fieldList);
|
|
638
|
-
}
|
|
639
633
|
// Sanitize output if requested
|
|
640
634
|
if (flags.sanitize) {
|
|
641
635
|
output = this.sanitizeOutput(output);
|
|
@@ -652,9 +646,9 @@ export default class SalaryTypes extends Command {
|
|
|
652
646
|
async runCamt053Import(flags) {
|
|
653
647
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
654
648
|
if (!context)
|
|
655
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
656
|
-
if (!context
|
|
657
|
-
this.error(
|
|
649
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
650
|
+
if (!hasCredentials(context))
|
|
651
|
+
this.error(missingCredentialsMessage(context));
|
|
658
652
|
if (!flags.file) {
|
|
659
653
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
660
654
|
}
|
|
@@ -742,9 +736,9 @@ export default class SalaryTypes extends Command {
|
|
|
742
736
|
}
|
|
743
737
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
744
738
|
if (!context)
|
|
745
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
746
|
-
if (!context
|
|
747
|
-
this.error(
|
|
739
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
740
|
+
if (!hasCredentials(context))
|
|
741
|
+
this.error(missingCredentialsMessage(context));
|
|
748
742
|
// Get data from file, --data, or --set flags
|
|
749
743
|
let items = [];
|
|
750
744
|
if (flags.file) {
|
|
@@ -964,56 +958,6 @@ export default class SalaryTypes extends Command {
|
|
|
964
958
|
return `\$${op}`;
|
|
965
959
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
966
960
|
}
|
|
967
|
-
filterFields(data, fields) {
|
|
968
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
969
|
-
const pick = (obj, keys) => {
|
|
970
|
-
// Group keys by their first segment so sibling sub-paths
|
|
971
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
972
|
-
// and don't clobber each other on merge.
|
|
973
|
-
const directKeys = new Set();
|
|
974
|
-
const nestedKeys = new Map();
|
|
975
|
-
for (const key of keys) {
|
|
976
|
-
if (key.split('.').some(isUnsafeKey))
|
|
977
|
-
continue;
|
|
978
|
-
if (key.includes('.')) {
|
|
979
|
-
const [first, ...rest] = key.split('.');
|
|
980
|
-
if (isUnsafeKey(first))
|
|
981
|
-
continue;
|
|
982
|
-
const list = nestedKeys.get(first);
|
|
983
|
-
if (list) {
|
|
984
|
-
list.push(rest.join('.'));
|
|
985
|
-
}
|
|
986
|
-
else {
|
|
987
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
988
|
-
}
|
|
989
|
-
}
|
|
990
|
-
else {
|
|
991
|
-
directKeys.add(key);
|
|
992
|
-
}
|
|
993
|
-
}
|
|
994
|
-
const result = {};
|
|
995
|
-
for (const key of directKeys) {
|
|
996
|
-
if (key in obj)
|
|
997
|
-
result[key] = obj[key];
|
|
998
|
-
}
|
|
999
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1000
|
-
const val = obj[first];
|
|
1001
|
-
if (!val || typeof val !== 'object')
|
|
1002
|
-
continue;
|
|
1003
|
-
if (Array.isArray(val)) {
|
|
1004
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1005
|
-
}
|
|
1006
|
-
else {
|
|
1007
|
-
result[first] = pick(val, restKeys);
|
|
1008
|
-
}
|
|
1009
|
-
}
|
|
1010
|
-
return result;
|
|
1011
|
-
};
|
|
1012
|
-
if (Array.isArray(data)) {
|
|
1013
|
-
return data.map(item => pick(item, fields));
|
|
1014
|
-
}
|
|
1015
|
-
return pick(data, fields);
|
|
1016
|
-
}
|
|
1017
961
|
sanitizeOutput(data) {
|
|
1018
962
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1019
963
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|