@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,5 +1,6 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import {
|
|
2
|
+
import { keycloakBaseUrlOf } from '../../lib/auth/index.js';
|
|
3
|
+
import { getAuthType, getCurrentContext, hasCredentials } from '../../lib/config.js';
|
|
3
4
|
export default class ConfigCurrent extends Command {
|
|
4
5
|
static description = 'Show the current context';
|
|
5
6
|
static examples = ['<%= config.bin %> config current', '<%= config.bin %> config current --json'];
|
|
@@ -24,20 +25,27 @@ export default class ConfigCurrent extends Command {
|
|
|
24
25
|
}
|
|
25
26
|
if (flags.json) {
|
|
26
27
|
this.log(JSON.stringify({
|
|
27
|
-
|
|
28
|
+
auth_type: getAuthType(context),
|
|
29
|
+
authenticated: hasCredentials(context),
|
|
28
30
|
client_id: context.client_id,
|
|
29
31
|
environment: context.environment,
|
|
32
|
+
keycloak_base_url: getAuthType(context) === 'oauth2' ? keycloakBaseUrlOf(context) : undefined,
|
|
30
33
|
name: context.name,
|
|
31
34
|
tenant_id: context.tenant_id,
|
|
32
35
|
}, null, 2));
|
|
33
36
|
return;
|
|
34
37
|
}
|
|
38
|
+
const authType = getAuthType(context);
|
|
35
39
|
this.log(`Current context: ${context.name}`);
|
|
36
40
|
this.log(`Environment: ${context.environment}`);
|
|
37
|
-
this.log(`
|
|
41
|
+
this.log(`Auth: ${authType === 'api_key' ? 'API key (x-api-key)' : 'oauth2'}`);
|
|
42
|
+
if (authType === 'oauth2') {
|
|
43
|
+
this.log(`Keycloak URL: ${keycloakBaseUrlOf(context)}`);
|
|
44
|
+
this.log(`Client ID: ${context.client_id ?? 'N/A'}`);
|
|
45
|
+
}
|
|
38
46
|
if (context.tenant_id) {
|
|
39
47
|
this.log(`Tenant ID: ${context.tenant_id}`);
|
|
40
48
|
}
|
|
41
|
-
this.log(`Authenticated: ${context
|
|
49
|
+
this.log(`Authenticated: ${hasCredentials(context) ? 'Yes' : 'No'}`);
|
|
42
50
|
}
|
|
43
51
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command, Flags } from '@oclif/core';
|
|
2
|
-
import {
|
|
2
|
+
import { keycloakBaseUrlOf } from '../../lib/auth/index.js';
|
|
3
|
+
import { getAuthType, getCurrentContext, hasCredentials, listContexts } from '../../lib/config.js';
|
|
3
4
|
export default class ConfigList extends Command {
|
|
4
5
|
static description = 'List all configured contexts';
|
|
5
6
|
static examples = ['<%= config.bin %> config list', '<%= config.bin %> config list --json'];
|
|
@@ -16,9 +17,11 @@ export default class ConfigList extends Command {
|
|
|
16
17
|
if (flags.json) {
|
|
17
18
|
this.log(JSON.stringify({
|
|
18
19
|
contexts: contexts.map((c) => ({
|
|
19
|
-
|
|
20
|
+
auth_type: getAuthType(c),
|
|
21
|
+
authenticated: hasCredentials(c),
|
|
20
22
|
client_id: c.client_id,
|
|
21
23
|
environment: c.environment,
|
|
24
|
+
keycloak_base_url: getAuthType(c) === 'oauth2' ? keycloakBaseUrlOf(c) : undefined,
|
|
22
25
|
name: c.name,
|
|
23
26
|
tenant_id: c.tenant_id,
|
|
24
27
|
})),
|
|
@@ -35,10 +38,15 @@ export default class ConfigList extends Command {
|
|
|
35
38
|
for (const ctx of contexts) {
|
|
36
39
|
const isCurrent = current?.name === ctx.name;
|
|
37
40
|
const marker = isCurrent ? '*' : ' ';
|
|
38
|
-
const auth = ctx
|
|
41
|
+
const auth = hasCredentials(ctx) ? '✓' : '✗';
|
|
42
|
+
const authType = getAuthType(ctx);
|
|
39
43
|
this.log(`${marker} ${ctx.name}`);
|
|
40
44
|
this.log(` Environment: ${ctx.environment}`);
|
|
41
|
-
this.log(`
|
|
45
|
+
this.log(` Auth: ${authType === 'api_key' ? 'API key (x-api-key)' : 'oauth2'}`);
|
|
46
|
+
if (authType === 'oauth2') {
|
|
47
|
+
this.log(` Keycloak URL: ${keycloakBaseUrlOf(ctx)}`);
|
|
48
|
+
this.log(` Client ID: ${ctx.client_id ?? 'N/A'}`);
|
|
49
|
+
}
|
|
42
50
|
this.log(` Authenticated: ${auth}`);
|
|
43
51
|
if (ctx.tenant_id) {
|
|
44
52
|
this.log(` Tenant ID: ${ctx.tenant_id}`);
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class ConfigSetApiKey extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static flags: {
|
|
6
|
+
'api-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
7
|
+
context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
8
|
+
};
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { password } from '@inquirer/prompts';
|
|
2
|
+
import { Command, Flags } from '@oclif/core';
|
|
3
|
+
import { getAuthType, getContext, getCurrentContext, maskApiKey, saveApiKey } from '../../lib/config.js';
|
|
4
|
+
export default class ConfigSetApiKey extends Command {
|
|
5
|
+
static description = 'Set or rotate the API key for a context (switches it to api-key auth)';
|
|
6
|
+
static examples = [
|
|
7
|
+
'<%= config.bin %> config set-api-key',
|
|
8
|
+
'<%= config.bin %> config set-api-key --context ci',
|
|
9
|
+
'<%= config.bin %> config set-api-key --context ci --api-key <key>',
|
|
10
|
+
];
|
|
11
|
+
static flags = {
|
|
12
|
+
'api-key': Flags.string({
|
|
13
|
+
description: 'The API key. Prompted for (hidden) if omitted.',
|
|
14
|
+
}),
|
|
15
|
+
context: Flags.string({
|
|
16
|
+
aliases: ['config'],
|
|
17
|
+
char: 'c',
|
|
18
|
+
description: 'Context to update, overriding the current context (alias: --config)',
|
|
19
|
+
env: 'ADERIS_CONTEXT',
|
|
20
|
+
}),
|
|
21
|
+
};
|
|
22
|
+
async run() {
|
|
23
|
+
const { flags } = await this.parse(ConfigSetApiKey);
|
|
24
|
+
const contextName = flags.context ?? getCurrentContext()?.name;
|
|
25
|
+
if (!contextName) {
|
|
26
|
+
this.error('No context configured. Run: aderis config add');
|
|
27
|
+
}
|
|
28
|
+
const context = getContext(contextName);
|
|
29
|
+
if (!context) {
|
|
30
|
+
this.error(`Context "${contextName}" not found.`);
|
|
31
|
+
}
|
|
32
|
+
const apiKey = flags['api-key'] ??
|
|
33
|
+
(await password({
|
|
34
|
+
mask: true,
|
|
35
|
+
message: `API key for "${contextName}":`,
|
|
36
|
+
validate: (value) => (value.trim() ? true : 'API key is required'),
|
|
37
|
+
}));
|
|
38
|
+
if (!apiKey.trim()) {
|
|
39
|
+
this.error('API key is required');
|
|
40
|
+
}
|
|
41
|
+
const wasOAuth = getAuthType(context) === 'oauth2';
|
|
42
|
+
saveApiKey(contextName, apiKey.trim());
|
|
43
|
+
this.log(`API key set for context "${contextName}" (${maskApiKey(apiKey.trim())})`);
|
|
44
|
+
if (wasOAuth) {
|
|
45
|
+
this.log('Auth method switched from oauth2 to api-key; requests now send the x-api-key header.');
|
|
46
|
+
this.log(`Any stored OAuth tokens are kept but unused. Remove them with: aderis auth logout --context ${contextName}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { select } from '@inquirer/prompts';
|
|
2
2
|
import { Args, Command } from '@oclif/core';
|
|
3
|
-
import { getContext, listContexts, setCurrentContext } from '../../lib/config.js';
|
|
3
|
+
import { getContext, hasCredentials, listContexts, missingCredentialsMessage, setCurrentContext, } from '../../lib/config.js';
|
|
4
4
|
export default class ConfigUse extends Command {
|
|
5
5
|
static args = {
|
|
6
6
|
name: Args.string({
|
|
@@ -32,9 +32,8 @@ export default class ConfigUse extends Command {
|
|
|
32
32
|
}
|
|
33
33
|
setCurrentContext(contextName);
|
|
34
34
|
this.log(`Switched to context "${contextName}" (${context.environment})`);
|
|
35
|
-
if (!context
|
|
36
|
-
this.log(`\nNote:
|
|
37
|
-
this.log(`Run: aderis auth login`);
|
|
35
|
+
if (!hasCredentials(context)) {
|
|
36
|
+
this.log(`\nNote: ${missingCredentialsMessage(context)}`);
|
|
38
37
|
}
|
|
39
38
|
}
|
|
40
39
|
}
|
|
@@ -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;
|
|
@@ -39,7 +39,7 @@ export default class CreditCardAccounts extends Command {
|
|
|
39
39
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
40
40
|
};
|
|
41
41
|
static flags = {
|
|
42
|
-
context: Flags.string({ char: 'c', description: 'Context to use' }),
|
|
42
|
+
context: Flags.string({ char: 'c', aliases: ['config'], env: 'ADERIS_CONTEXT', description: 'Context to use for this command, overriding the current context (alias: --config)' }),
|
|
43
43
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
44
44
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
45
45
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -55,7 +55,7 @@ export default class CreditCardAccounts extends Command {
|
|
|
55
55
|
'sort-by': Flags.string({ description: 'Sort field(s), comma-separated. Direction (:ASC/:DESC) is optional; date-like fields default to DESC, others to ASC.' }),
|
|
56
56
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
57
57
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
58
|
-
fields: Flags.string({ description: 'Comma-separated list of fields to
|
|
58
|
+
fields: Flags.string({ description: 'Comma-separated list of fields to return, applied by the API (e.g. --fields=id,name,status)' }),
|
|
59
59
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
60
60
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
61
61
|
};
|
|
@@ -113,9 +113,9 @@ export default class CreditCardAccounts extends Command {
|
|
|
113
113
|
}
|
|
114
114
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
115
115
|
if (!context)
|
|
116
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
117
|
-
if (!context
|
|
118
|
-
this.error(
|
|
116
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
117
|
+
if (!hasCredentials(context))
|
|
118
|
+
this.error(missingCredentialsMessage(context));
|
|
119
119
|
const queryParams = {};
|
|
120
120
|
if (flags.page)
|
|
121
121
|
queryParams.page = flags.page;
|
|
@@ -127,6 +127,8 @@ export default class CreditCardAccounts extends Command {
|
|
|
127
127
|
queryParams.search = flags.search;
|
|
128
128
|
if (flags['sort-by'])
|
|
129
129
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
130
|
+
if (flags.fields)
|
|
131
|
+
queryParams.fields = flags.fields;
|
|
130
132
|
if (flags.query) {
|
|
131
133
|
try {
|
|
132
134
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -329,11 +331,6 @@ export default class CreditCardAccounts extends Command {
|
|
|
329
331
|
output = obj[arrayKeys[0]];
|
|
330
332
|
}
|
|
331
333
|
}
|
|
332
|
-
// Filter fields if --fields flag is provided
|
|
333
|
-
if (flags.fields) {
|
|
334
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
335
|
-
output = this.filterFields(output, fieldList);
|
|
336
|
-
}
|
|
337
334
|
// Sanitize output if requested
|
|
338
335
|
if (flags.sanitize) {
|
|
339
336
|
output = this.sanitizeOutput(output);
|
|
@@ -358,9 +355,9 @@ export default class CreditCardAccounts extends Command {
|
|
|
358
355
|
async runImport(flags) {
|
|
359
356
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
360
357
|
if (!context)
|
|
361
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
362
|
-
if (!context
|
|
363
|
-
this.error(
|
|
358
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
359
|
+
if (!hasCredentials(context))
|
|
360
|
+
this.error(missingCredentialsMessage(context));
|
|
364
361
|
// Get data from file, --data, or --set flags
|
|
365
362
|
let items = [];
|
|
366
363
|
if (flags.file) {
|
|
@@ -570,9 +567,9 @@ export default class CreditCardAccounts extends Command {
|
|
|
570
567
|
async runExport(flags) {
|
|
571
568
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
572
569
|
if (!context)
|
|
573
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
574
|
-
if (!context
|
|
575
|
-
this.error(
|
|
570
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
571
|
+
if (!hasCredentials(context))
|
|
572
|
+
this.error(missingCredentialsMessage(context));
|
|
576
573
|
const listEndpoint = ENDPOINTS['list'];
|
|
577
574
|
if (!listEndpoint)
|
|
578
575
|
this.error('Export requires a list endpoint');
|
|
@@ -588,6 +585,8 @@ export default class CreditCardAccounts extends Command {
|
|
|
588
585
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
589
586
|
if (flags.include)
|
|
590
587
|
baseParams.include = flags.include;
|
|
588
|
+
if (flags.fields)
|
|
589
|
+
baseParams.fields = flags.fields;
|
|
591
590
|
if (flags.query) {
|
|
592
591
|
try {
|
|
593
592
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -642,12 +641,7 @@ export default class CreditCardAccounts extends Command {
|
|
|
642
641
|
}
|
|
643
642
|
}
|
|
644
643
|
process.stderr.write(' done\n');
|
|
645
|
-
// Filter fields if specified
|
|
646
644
|
let output = allItems;
|
|
647
|
-
if (flags.fields) {
|
|
648
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
649
|
-
output = this.filterFields(allItems, fieldList);
|
|
650
|
-
}
|
|
651
645
|
// Sanitize output if requested
|
|
652
646
|
if (flags.sanitize) {
|
|
653
647
|
output = this.sanitizeOutput(output);
|
|
@@ -664,9 +658,9 @@ export default class CreditCardAccounts extends Command {
|
|
|
664
658
|
async runCamt053Import(flags) {
|
|
665
659
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
666
660
|
if (!context)
|
|
667
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
668
|
-
if (!context
|
|
669
|
-
this.error(
|
|
661
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
662
|
+
if (!hasCredentials(context))
|
|
663
|
+
this.error(missingCredentialsMessage(context));
|
|
670
664
|
if (!flags.file) {
|
|
671
665
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
672
666
|
}
|
|
@@ -754,9 +748,9 @@ export default class CreditCardAccounts extends Command {
|
|
|
754
748
|
}
|
|
755
749
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
756
750
|
if (!context)
|
|
757
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
758
|
-
if (!context
|
|
759
|
-
this.error(
|
|
751
|
+
this.error(flags.context ? `Context "${flags.context}" not found. List contexts with: aderis config list` : 'No context configured. Run: aderis auth login --new');
|
|
752
|
+
if (!hasCredentials(context))
|
|
753
|
+
this.error(missingCredentialsMessage(context));
|
|
760
754
|
// Get data from file, --data, or --set flags
|
|
761
755
|
let items = [];
|
|
762
756
|
if (flags.file) {
|
|
@@ -976,56 +970,6 @@ export default class CreditCardAccounts extends Command {
|
|
|
976
970
|
return `\$${op}`;
|
|
977
971
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
978
972
|
}
|
|
979
|
-
filterFields(data, fields) {
|
|
980
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
981
|
-
const pick = (obj, keys) => {
|
|
982
|
-
// Group keys by their first segment so sibling sub-paths
|
|
983
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
984
|
-
// and don't clobber each other on merge.
|
|
985
|
-
const directKeys = new Set();
|
|
986
|
-
const nestedKeys = new Map();
|
|
987
|
-
for (const key of keys) {
|
|
988
|
-
if (key.split('.').some(isUnsafeKey))
|
|
989
|
-
continue;
|
|
990
|
-
if (key.includes('.')) {
|
|
991
|
-
const [first, ...rest] = key.split('.');
|
|
992
|
-
if (isUnsafeKey(first))
|
|
993
|
-
continue;
|
|
994
|
-
const list = nestedKeys.get(first);
|
|
995
|
-
if (list) {
|
|
996
|
-
list.push(rest.join('.'));
|
|
997
|
-
}
|
|
998
|
-
else {
|
|
999
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1000
|
-
}
|
|
1001
|
-
}
|
|
1002
|
-
else {
|
|
1003
|
-
directKeys.add(key);
|
|
1004
|
-
}
|
|
1005
|
-
}
|
|
1006
|
-
const result = {};
|
|
1007
|
-
for (const key of directKeys) {
|
|
1008
|
-
if (key in obj)
|
|
1009
|
-
result[key] = obj[key];
|
|
1010
|
-
}
|
|
1011
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1012
|
-
const val = obj[first];
|
|
1013
|
-
if (!val || typeof val !== 'object')
|
|
1014
|
-
continue;
|
|
1015
|
-
if (Array.isArray(val)) {
|
|
1016
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
result[first] = pick(val, restKeys);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
return result;
|
|
1023
|
-
};
|
|
1024
|
-
if (Array.isArray(data)) {
|
|
1025
|
-
return data.map(item => pick(item, fields));
|
|
1026
|
-
}
|
|
1027
|
-
return pick(data, fields);
|
|
1028
|
-
}
|
|
1029
973
|
sanitizeOutput(data) {
|
|
1030
974
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1031
975
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
@@ -1,31 +1,32 @@
|
|
|
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
8
|
const NESTED_IMPORTS = ["line-items", "notes", "files"];
|
|
9
|
-
const SUBCOMMANDS = [...["list", "create", "create-from-orders", "create-from-invoice", "create-invoice", "get", "update", "delete", "pdf", "submit", "approve", "cancel", "deliver", "ship", "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}`)];
|
|
9
|
+
const SUBCOMMANDS = [...["list", "create", "create-from-orders", "create-from-invoice", "create-invoice", "get", "update", "delete", "pdf", "submit", "approve", "cancel", "replace", "deliver", "ship", "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/delivery-notes', summary: 'Get all delivery notes with pagination', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/delivery-notes', summary: 'Create a new delivery note', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'lineItems'] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/delivery-notes', summary: 'Create a new delivery note', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'lineItems'] },
|
|
13
13
|
'create-from-orders': { method: 'POST', path: '/v1/delivery-notes/create-from-orders', summary: 'Create a DRAFT delivery note from several sales orders', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderIds'] },
|
|
14
14
|
'create-from-invoice': { method: 'POST', path: '/v1/delivery-notes/create-from-invoice', summary: 'Create a DRAFT delivery note from a sales invoice', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
15
|
'create-invoice': { method: 'POST', path: '/v1/delivery-notes/create-invoice', summary: 'Create ONE draft sales invoice from several delivery notes (same customer)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['deliveryNoteIds'] },
|
|
16
16
|
'get': { method: 'GET', path: '/v1/delivery-notes/{id}', summary: 'Get a delivery note by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/delivery-notes/{id}', summary: 'Update a delivery note', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'removeFileIds'] },
|
|
17
|
+
'update': { method: 'PATCH', path: '/v1/delivery-notes/{id}', summary: 'Update a delivery note', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['files', 'notes', 'tagIds', 'tagCodes', 'removeFileIds'] },
|
|
18
18
|
'delete': { method: 'DELETE', path: '/v1/delivery-notes/{id}', summary: 'Delete a delivery note', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
19
|
'pdf': { method: 'GET', path: '/v1/delivery-notes/{id}/pdf', summary: 'Render a delivery note PDF using the configured PDF processor', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
20
|
'submit': { method: 'POST', path: '/v1/delivery-notes/{id}/submit', summary: 'Submit a delivery note for approval (DRAFT → PENDING_APPROVAL)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
21
21
|
'approve': { method: 'POST', path: '/v1/delivery-notes/{id}/approve', summary: 'Approve a delivery note (DRAFT or PENDING_APPROVAL → APPROVED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
-
'cancel': { method: 'POST', path: '/v1/delivery-notes/{id}/cancel', summary: 'Cancel a delivery note (DRAFT / PENDING_APPROVAL / APPROVED → CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
22
|
+
'cancel': { method: 'POST', path: '/v1/delivery-notes/{id}/cancel', summary: 'Cancel a delivery note (DRAFT / PENDING_APPROVAL / APPROVED / SHIPPED / DELIVERED → CANCELLED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
|
+
'replace': { method: 'POST', path: '/v1/delivery-notes/{id}/replace', summary: 'Replace a delivery note (APPROVED / SHIPPED / DELIVERED / CANCELLED → REPLACED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
23
24
|
'deliver': { method: 'POST', path: '/v1/delivery-notes/{id}/deliver', summary: 'Mark a delivery note as delivered (SHIPPED → DELIVERED)', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
24
25
|
'ship': { method: 'POST', path: '/v1/delivery-notes/{id}/ship', summary: 'Ship a delivery note (APPROVED → SHIPPED)', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['lineItems'] },
|
|
25
26
|
'send': { method: 'POST', path: '/v1/delivery-notes/{id}/send', summary: 'Send a delivery note to its recipient by email', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['emailCc', 'emailBcc'] },
|
|
26
|
-
'create-line-items': { method: 'POST', path: '/v1/delivery-notes/{noteId}/line-items', summary: 'Add a line item to a delivery note', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderLineItems'] },
|
|
27
|
+
'create-line-items': { method: 'POST', path: '/v1/delivery-notes/{noteId}/line-items', summary: 'Add a line item to a delivery note', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'] },
|
|
27
28
|
'update-line-items-reorder': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/reorder', summary: 'Bulk reorder line items (drag-and-drop)', pathParams: ['noteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['items'] },
|
|
28
|
-
'update-line-items': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Update a line item on a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderLineItems'] },
|
|
29
|
+
'update-line-items': { method: 'PATCH', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Update a line item on a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['salesOrderLineItems', 'tagIds', 'tagCodes'] },
|
|
29
30
|
'delete-line-items': { method: 'DELETE', path: '/v1/delivery-notes/{noteId}/line-items/{lineItemId}', summary: 'Remove a line item from a delivery note', pathParams: ['noteId', 'lineItemId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
30
31
|
'list-notes': { method: 'GET', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Get all notes for a delivery note', pathParams: ['deliveryNoteId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
31
32
|
'create-notes': { method: 'POST', path: '/v1/delivery-notes/{deliveryNoteId}/notes', summary: 'Create a note for a delivery note', pathParams: ['deliveryNoteId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
@@ -54,7 +55,7 @@ export default class DeliveryNotes extends Command {
|
|
|
54
55
|
id: Args.string({ description: 'Resource ID (for get, update, delete operations)', required: false }),
|
|
55
56
|
};
|
|
56
57
|
static flags = {
|
|
57
|
-
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)' }),
|
|
58
59
|
output: Flags.string({ char: 'o', description: 'Output format (default: table for import, json for other commands)', options: ['json', 'table', 'csv'] }),
|
|
59
60
|
table: Flags.boolean({ char: 't', description: 'Output as table (shortcut for --output table)' }),
|
|
60
61
|
csv: Flags.boolean({ description: 'Output as CSV (shortcut for --output csv)' }),
|
|
@@ -71,7 +72,7 @@ export default class DeliveryNotes extends Command {
|
|
|
71
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.' }),
|
|
72
73
|
filter: Flags.string({ description: 'Filter (e.g. --filter status=ACTIVE). Operators: in:A,B, like:%, gt:, lt:, null:', multiple: true }),
|
|
73
74
|
query: Flags.string({ description: 'Additional query params as JSON' }),
|
|
74
|
-
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)' }),
|
|
75
76
|
sanitize: Flags.boolean({ description: 'Remove null values, audit fields, and duplicate UUIDs from output' }),
|
|
76
77
|
'show-http-request': Flags.boolean({ description: 'Show HTTP request details (method, URL, body)' }),
|
|
77
78
|
};
|
|
@@ -129,9 +130,9 @@ export default class DeliveryNotes extends Command {
|
|
|
129
130
|
}
|
|
130
131
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
131
132
|
if (!context)
|
|
132
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
133
|
-
if (!context
|
|
134
|
-
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));
|
|
135
136
|
const queryParams = {};
|
|
136
137
|
if (flags.page)
|
|
137
138
|
queryParams.page = flags.page;
|
|
@@ -143,6 +144,8 @@ export default class DeliveryNotes extends Command {
|
|
|
143
144
|
queryParams.search = flags.search;
|
|
144
145
|
if (flags['sort-by'])
|
|
145
146
|
queryParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
147
|
+
if (flags.fields)
|
|
148
|
+
queryParams.fields = flags.fields;
|
|
146
149
|
if (flags.query) {
|
|
147
150
|
try {
|
|
148
151
|
Object.assign(queryParams, JSON.parse(flags.query));
|
|
@@ -345,11 +348,6 @@ export default class DeliveryNotes extends Command {
|
|
|
345
348
|
output = obj[arrayKeys[0]];
|
|
346
349
|
}
|
|
347
350
|
}
|
|
348
|
-
// Filter fields if --fields flag is provided
|
|
349
|
-
if (flags.fields) {
|
|
350
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
351
|
-
output = this.filterFields(output, fieldList);
|
|
352
|
-
}
|
|
353
351
|
// Sanitize output if requested
|
|
354
352
|
if (flags.sanitize) {
|
|
355
353
|
output = this.sanitizeOutput(output);
|
|
@@ -374,9 +372,9 @@ export default class DeliveryNotes extends Command {
|
|
|
374
372
|
async runImport(flags) {
|
|
375
373
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
376
374
|
if (!context)
|
|
377
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
378
|
-
if (!context
|
|
379
|
-
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));
|
|
380
378
|
// Get data from file, --data, or --set flags
|
|
381
379
|
let items = [];
|
|
382
380
|
if (flags.file) {
|
|
@@ -586,9 +584,9 @@ export default class DeliveryNotes extends Command {
|
|
|
586
584
|
async runExport(flags) {
|
|
587
585
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
588
586
|
if (!context)
|
|
589
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
590
|
-
if (!context
|
|
591
|
-
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));
|
|
592
590
|
const listEndpoint = ENDPOINTS['list'];
|
|
593
591
|
if (!listEndpoint)
|
|
594
592
|
this.error('Export requires a list endpoint');
|
|
@@ -604,6 +602,8 @@ export default class DeliveryNotes extends Command {
|
|
|
604
602
|
baseParams.sortBy = normalizeSortBy(flags['sort-by']);
|
|
605
603
|
if (flags.include)
|
|
606
604
|
baseParams.include = flags.include;
|
|
605
|
+
if (flags.fields)
|
|
606
|
+
baseParams.fields = flags.fields;
|
|
607
607
|
if (flags.query) {
|
|
608
608
|
try {
|
|
609
609
|
Object.assign(baseParams, JSON.parse(flags.query));
|
|
@@ -658,12 +658,7 @@ export default class DeliveryNotes extends Command {
|
|
|
658
658
|
}
|
|
659
659
|
}
|
|
660
660
|
process.stderr.write(' done\n');
|
|
661
|
-
// Filter fields if specified
|
|
662
661
|
let output = allItems;
|
|
663
|
-
if (flags.fields) {
|
|
664
|
-
const fieldList = flags.fields.split(',').map(f => f.trim());
|
|
665
|
-
output = this.filterFields(allItems, fieldList);
|
|
666
|
-
}
|
|
667
662
|
// Sanitize output if requested
|
|
668
663
|
if (flags.sanitize) {
|
|
669
664
|
output = this.sanitizeOutput(output);
|
|
@@ -680,9 +675,9 @@ export default class DeliveryNotes extends Command {
|
|
|
680
675
|
async runCamt053Import(flags) {
|
|
681
676
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
682
677
|
if (!context)
|
|
683
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
684
|
-
if (!context
|
|
685
|
-
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));
|
|
686
681
|
if (!flags.file) {
|
|
687
682
|
this.error('CAMT.053 import requires --file (-f) flag with path to XML file');
|
|
688
683
|
}
|
|
@@ -770,9 +765,9 @@ export default class DeliveryNotes extends Command {
|
|
|
770
765
|
}
|
|
771
766
|
const context = flags.context ? getContext(flags.context) : getCurrentContext();
|
|
772
767
|
if (!context)
|
|
773
|
-
this.error('No context configured. Run: aderis auth login --new');
|
|
774
|
-
if (!context
|
|
775
|
-
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));
|
|
776
771
|
// Get data from file, --data, or --set flags
|
|
777
772
|
let items = [];
|
|
778
773
|
if (flags.file) {
|
|
@@ -992,56 +987,6 @@ export default class DeliveryNotes extends Command {
|
|
|
992
987
|
return `\$${op}`;
|
|
993
988
|
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
994
989
|
}
|
|
995
|
-
filterFields(data, fields) {
|
|
996
|
-
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
997
|
-
const pick = (obj, keys) => {
|
|
998
|
-
// Group keys by their first segment so sibling sub-paths
|
|
999
|
-
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
1000
|
-
// and don't clobber each other on merge.
|
|
1001
|
-
const directKeys = new Set();
|
|
1002
|
-
const nestedKeys = new Map();
|
|
1003
|
-
for (const key of keys) {
|
|
1004
|
-
if (key.split('.').some(isUnsafeKey))
|
|
1005
|
-
continue;
|
|
1006
|
-
if (key.includes('.')) {
|
|
1007
|
-
const [first, ...rest] = key.split('.');
|
|
1008
|
-
if (isUnsafeKey(first))
|
|
1009
|
-
continue;
|
|
1010
|
-
const list = nestedKeys.get(first);
|
|
1011
|
-
if (list) {
|
|
1012
|
-
list.push(rest.join('.'));
|
|
1013
|
-
}
|
|
1014
|
-
else {
|
|
1015
|
-
nestedKeys.set(first, [rest.join('.')]);
|
|
1016
|
-
}
|
|
1017
|
-
}
|
|
1018
|
-
else {
|
|
1019
|
-
directKeys.add(key);
|
|
1020
|
-
}
|
|
1021
|
-
}
|
|
1022
|
-
const result = {};
|
|
1023
|
-
for (const key of directKeys) {
|
|
1024
|
-
if (key in obj)
|
|
1025
|
-
result[key] = obj[key];
|
|
1026
|
-
}
|
|
1027
|
-
for (const [first, restKeys] of nestedKeys) {
|
|
1028
|
-
const val = obj[first];
|
|
1029
|
-
if (!val || typeof val !== 'object')
|
|
1030
|
-
continue;
|
|
1031
|
-
if (Array.isArray(val)) {
|
|
1032
|
-
result[first] = val.map(item => pick(item, restKeys));
|
|
1033
|
-
}
|
|
1034
|
-
else {
|
|
1035
|
-
result[first] = pick(val, restKeys);
|
|
1036
|
-
}
|
|
1037
|
-
}
|
|
1038
|
-
return result;
|
|
1039
|
-
};
|
|
1040
|
-
if (Array.isArray(data)) {
|
|
1041
|
-
return data.map(item => pick(item, fields));
|
|
1042
|
-
}
|
|
1043
|
-
return pick(data, fields);
|
|
1044
|
-
}
|
|
1045
990
|
sanitizeOutput(data) {
|
|
1046
991
|
const auditFields = ['_type', 'tenantId', 'createdAt', 'updatedAt', 'createdBy', 'updatedBy'];
|
|
1047
992
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|