@aderis-erp/cli 1.0.2 → 1.0.4
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 +6 -0
- package/dist/commands/access-groups 3.d.ts +40 -0
- package/dist/commands/{access-groups 2.js → access-groups 3.js } +106 -40
- package/dist/commands/access-groups.d.ts +40 -0
- package/dist/commands/access-groups.js +1074 -0
- package/dist/commands/account-transactions.d.ts +42 -0
- package/dist/commands/account-transactions.js +1088 -0
- package/dist/commands/accounts 2.d.ts +41 -0
- package/dist/commands/accounts 2.js +1078 -0
- package/dist/commands/accounts.d.ts +41 -0
- package/dist/commands/accounts.js +1080 -0
- package/dist/commands/agent-runs.d.ts +40 -0
- package/dist/commands/agent-runs.js +1071 -0
- package/dist/commands/async-endpoints-internal 3.d.ts +40 -0
- package/dist/commands/{processing-logs.js → async-endpoints-internal 3.js } +110 -46
- package/dist/commands/async-endpoints-internal.d.ts +40 -0
- package/dist/commands/async-endpoints-internal.js +1064 -0
- package/dist/commands/audit-log 3.d.ts +40 -0
- package/dist/commands/audit-log 3.js +1066 -0
- package/dist/commands/audit-log.d.ts +40 -0
- package/dist/commands/audit-log.js +1068 -0
- package/dist/commands/auth/login.d.ts +14 -0
- package/dist/commands/auth/login.js +116 -0
- package/dist/commands/auth/logout.d.ts +10 -0
- package/dist/commands/auth/logout.js +58 -0
- package/dist/commands/auth/status.d.ts +12 -0
- package/dist/commands/auth/status.js +101 -0
- package/dist/commands/auth/tenant.d.ts +19 -0
- package/dist/commands/auth/tenant.js +184 -0
- package/dist/commands/auth.d.ts +37 -0
- package/dist/commands/auth.js +871 -0
- package/dist/commands/bank-accounts.d.ts +42 -0
- package/dist/commands/bank-accounts.js +1088 -0
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +122 -54
- package/dist/commands/business-partners 3.d.ts +41 -0
- package/dist/commands/business-partners 3.js +1078 -0
- package/dist/commands/business-partners---contacts 3.d.ts +42 -0
- package/dist/commands/business-partners---contacts 3.js +1072 -0
- package/dist/commands/business-partners---contacts.d.ts +1 -0
- package/dist/commands/business-partners---contacts.js +114 -46
- package/dist/commands/business-partners.d.ts +1 -0
- package/dist/commands/business-partners.js +114 -46
- package/dist/commands/config/add.d.ts +13 -0
- package/dist/commands/config/add.js +120 -0
- package/dist/commands/config/current.d.ts +9 -0
- package/dist/commands/config/current.js +43 -0
- package/dist/commands/config/delete.d.ts +12 -0
- package/dist/commands/config/delete.js +67 -0
- package/dist/commands/config/list.d.ts +9 -0
- package/dist/commands/config/list.js +50 -0
- package/dist/commands/config/use.d.ts +9 -0
- package/dist/commands/config/use.js +40 -0
- package/dist/commands/credit-card-accounts 3.d.ts +42 -0
- package/dist/commands/credit-card-accounts 3.js +1084 -0
- package/dist/commands/credit-card-accounts.d.ts +1 -0
- package/dist/commands/credit-card-accounts.js +119 -51
- package/dist/commands/documents.d.ts +1 -0
- package/dist/commands/documents.js +110 -42
- package/dist/commands/dunning-configs 3.d.ts +40 -0
- package/dist/commands/dunning-configs 3.js +1072 -0
- package/dist/commands/dunning-configs.d.ts +1 -0
- package/dist/commands/dunning-configs.js +109 -41
- package/dist/commands/dunning-reminders.d.ts +1 -0
- package/dist/commands/dunning-reminders.js +112 -44
- package/dist/commands/dunning-runs 3.d.ts +40 -0
- package/dist/commands/{pubsub.js → dunning-runs 3.js } +108 -43
- package/dist/commands/dunning-runs.d.ts +1 -0
- package/dist/commands/dunning-runs.js +105 -37
- package/dist/commands/employees.d.ts +1 -0
- package/dist/commands/employees.js +136 -68
- package/dist/commands/event-processor-mappings 3.d.ts +40 -0
- package/dist/commands/event-processor-mappings 3.js +1071 -0
- package/dist/commands/event-processor-mappings.d.ts +1 -0
- package/dist/commands/event-processor-mappings.js +109 -41
- package/dist/commands/files-internal 3.d.ts +40 -0
- package/dist/commands/files-internal 3.js +1073 -0
- package/dist/commands/files-internal.d.ts +1 -0
- package/dist/commands/files-internal.js +113 -45
- package/dist/commands/health 3.d.ts +40 -0
- package/dist/commands/health 3.js +1067 -0
- package/dist/commands/health.d.ts +1 -0
- package/dist/commands/health.js +109 -41
- package/dist/commands/{pubsub.d.ts → inbox 3.d.ts } +2 -1
- package/dist/commands/inbox 3.js +1065 -0
- package/dist/commands/inbox.d.ts +1 -0
- package/dist/commands/inbox.js +110 -40
- package/dist/commands/integrations 3.d.ts +41 -0
- package/dist/commands/integrations 3.js +1067 -0
- package/dist/commands/integrations.d.ts +1 -0
- package/dist/commands/integrations.js +107 -39
- package/dist/commands/item-categories 3.d.ts +40 -0
- package/dist/commands/item-categories 3.js +1072 -0
- package/dist/commands/item-categories.d.ts +1 -0
- package/dist/commands/item-categories.js +109 -41
- package/dist/commands/items 3.d.ts +40 -0
- package/dist/commands/items 3.js +1072 -0
- package/dist/commands/items.d.ts +1 -0
- package/dist/commands/items.js +109 -41
- package/dist/commands/journal-lines.d.ts +1 -0
- package/dist/commands/journal-lines.js +105 -37
- package/dist/commands/journals 3.d.ts +42 -0
- package/dist/commands/journals 3.js +1088 -0
- package/dist/commands/journals.d.ts +1 -0
- package/dist/commands/journals.js +123 -55
- package/dist/commands/ledgers.d.ts +1 -0
- package/dist/commands/ledgers.js +120 -52
- package/dist/commands/office-locations 3.d.ts +40 -0
- package/dist/commands/office-locations 3.js +1072 -0
- package/dist/commands/office-locations.d.ts +1 -0
- package/dist/commands/office-locations.js +109 -41
- package/dist/commands/payment-method-ledger-mappings 3.d.ts +41 -0
- package/dist/commands/payment-method-ledger-mappings 3.js +1066 -0
- package/dist/commands/payment-method-ledger-mappings.d.ts +1 -0
- package/dist/commands/payment-method-ledger-mappings.js +109 -41
- package/dist/commands/payment-methods 3.d.ts +40 -0
- package/dist/commands/payment-methods 3.js +1072 -0
- package/dist/commands/payment-methods.d.ts +1 -0
- package/dist/commands/payment-methods.js +109 -41
- package/dist/commands/payment-runs 3.d.ts +42 -0
- package/dist/commands/payment-runs 3.js +1087 -0
- package/dist/commands/payment-runs.d.ts +1 -0
- package/dist/commands/payment-runs.js +122 -54
- package/dist/commands/payment-terms 3.d.ts +40 -0
- package/dist/commands/payment-terms 3.js +1073 -0
- package/dist/commands/payment-terms.d.ts +1 -0
- package/dist/commands/payment-terms.js +110 -42
- package/dist/commands/price-lists 3.d.ts +41 -0
- package/dist/commands/price-lists 3.js +1078 -0
- package/dist/commands/price-lists.d.ts +1 -0
- package/dist/commands/price-lists.js +114 -46
- package/dist/commands/processor-mappings 2.d.ts +40 -0
- package/dist/commands/processor-mappings 2.js +1070 -0
- package/dist/commands/processor-mappings.d.ts +1 -0
- package/dist/commands/processor-mappings.js +108 -40
- package/dist/commands/processors.d.ts +1 -0
- package/dist/commands/processors.js +113 -45
- package/dist/commands/reconciliation 3.d.ts +40 -0
- package/dist/commands/reconciliation 3.js +1069 -0
- package/dist/commands/reconciliation.d.ts +1 -0
- package/dist/commands/reconciliation.js +110 -42
- package/dist/commands/reports.d.ts +1 -0
- package/dist/commands/reports.js +110 -39
- package/dist/commands/salary-types 3.d.ts +40 -0
- package/dist/commands/salary-types 3.js +1072 -0
- package/dist/commands/salary-types.d.ts +1 -0
- package/dist/commands/salary-types.js +1074 -0
- package/dist/commands/sales-invoices 3.d.ts +43 -0
- package/dist/commands/sales-invoices 3.js +1100 -0
- package/dist/commands/sales-invoices.d.ts +1 -0
- package/dist/commands/sales-invoices.js +134 -66
- package/dist/commands/sales-orders 3.d.ts +43 -0
- package/dist/commands/sales-orders 3.js +1098 -0
- package/dist/commands/sales-orders.d.ts +1 -0
- package/dist/commands/sales-orders.js +133 -64
- package/dist/commands/switch.d.ts +1 -0
- package/dist/commands/switch.js +58 -3
- package/dist/commands/{processing-logs.d.ts → tags 3.d.ts } +2 -1
- package/dist/commands/tags 3.js +1072 -0
- package/dist/commands/tags.d.ts +1 -0
- package/dist/commands/tags.js +109 -41
- package/dist/commands/tax-codes 3.d.ts +40 -0
- package/dist/commands/tax-codes 3.js +1075 -0
- package/dist/commands/tax-codes.d.ts +1 -0
- package/dist/commands/tax-codes.js +112 -44
- package/dist/commands/tax-matrix.d.ts +1 -0
- package/dist/commands/tax-matrix.js +109 -41
- package/dist/commands/tenants.d.ts +1 -0
- package/dist/commands/tenants.js +109 -41
- package/dist/commands/text-templates 3.d.ts +40 -0
- package/dist/commands/text-templates 3.js +1072 -0
- package/dist/commands/text-templates.d.ts +1 -0
- package/dist/commands/text-templates.js +109 -41
- package/dist/commands/tools.d.ts +1 -0
- package/dist/commands/tools.js +106 -38
- package/dist/commands/vendor-invoices.d.ts +1 -0
- package/dist/commands/vendor-invoices.js +139 -69
- package/dist/commands/webhooks 3.d.ts +40 -0
- package/dist/commands/webhooks 3.js +1074 -0
- package/dist/commands/webhooks.d.ts +1 -0
- package/dist/commands/webhooks.js +111 -43
- package/dist/commands/workflows.d.ts +1 -0
- package/dist/commands/workflows.js +107 -39
- package/dist/lib/api-client.d.ts +5 -1
- package/dist/lib/api-client.js +19 -5
- package/dist/lib/auth.d.ts +16 -1
- package/dist/lib/auth.js +170 -45
- package/dist/lib/config.d.ts +7 -0
- package/dist/lib/config.js +7 -1
- package/oclif.manifest.json +7464 -653
- package/package.json +2 -2
|
@@ -8,10 +8,10 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "create", "update", "delete"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/processor-mappings', summary: 'List all document type processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'create': { method: 'POST', path: '/v1/processor-mappings', summary: 'Create a new document type processor mapping', pathParams: [], hasBody: true, isMultipart: false },
|
|
13
|
-
'update': { method: 'PATCH', path: '/v1/processor-mappings/{id}', summary: 'Update a document type processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
14
|
-
'delete': { method: 'DELETE', path: '/v1/processor-mappings/{id}', summary: 'Delete a document type processor mapping', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/processor-mappings', summary: 'List all document type processor mappings for the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'create': { method: 'POST', path: '/v1/processor-mappings', summary: 'Create a new document type processor mapping', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['documentTypes'] },
|
|
13
|
+
'update': { method: 'PATCH', path: '/v1/processor-mappings/{id}', summary: 'Update a document type processor mapping', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['documentTypes'] },
|
|
14
|
+
'delete': { method: 'DELETE', path: '/v1/processor-mappings/{id}', summary: 'Delete a document type processor mapping', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
15
|
};
|
|
16
16
|
export default class ProcessorMappings extends Command {
|
|
17
17
|
static description = 'Manage processor mappings';
|
|
@@ -128,15 +128,7 @@ export default class ProcessorMappings extends Command {
|
|
|
128
128
|
if (eqIndex === -1)
|
|
129
129
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
130
130
|
const key = f.slice(0, eqIndex);
|
|
131
|
-
|
|
132
|
-
// Check for operator prefix (with or without $)
|
|
133
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
134
|
-
if (opMatch) {
|
|
135
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
136
|
-
}
|
|
137
|
-
else {
|
|
138
|
-
value = `\$eq:${value}`;
|
|
139
|
-
}
|
|
131
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
140
132
|
const filterKey = `filter.${key}`;
|
|
141
133
|
if (filterKey in queryParams) {
|
|
142
134
|
// Multiple filters on same field: collect as array
|
|
@@ -169,7 +161,9 @@ export default class ProcessorMappings extends Command {
|
|
|
169
161
|
data = form;
|
|
170
162
|
}
|
|
171
163
|
else if (endpoint.hasBody) {
|
|
172
|
-
|
|
164
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
165
|
+
// input file — skip the read-as-body branch.
|
|
166
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
173
167
|
const fsModule = await import('node:fs');
|
|
174
168
|
try {
|
|
175
169
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -191,6 +185,7 @@ export default class ProcessorMappings extends Command {
|
|
|
191
185
|
if (!data)
|
|
192
186
|
data = {};
|
|
193
187
|
const body = data;
|
|
188
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
194
189
|
for (const pair of flags.set) {
|
|
195
190
|
const eqIndex = pair.indexOf('=');
|
|
196
191
|
if (eqIndex === -1)
|
|
@@ -202,6 +197,19 @@ export default class ProcessorMappings extends Command {
|
|
|
202
197
|
value = JSON.parse(value);
|
|
203
198
|
}
|
|
204
199
|
catch { /* keep as string */ }
|
|
200
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
201
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
202
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
203
|
+
// already provided a JSON array.
|
|
204
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
205
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
206
|
+
if (typeof value === 'string') {
|
|
207
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
208
|
+
}
|
|
209
|
+
else if (value !== undefined && value !== null) {
|
|
210
|
+
value = [value];
|
|
211
|
+
}
|
|
212
|
+
}
|
|
205
213
|
// Support nested keys like "address.city"
|
|
206
214
|
const keys = key.split('.');
|
|
207
215
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -252,6 +260,26 @@ export default class ProcessorMappings extends Command {
|
|
|
252
260
|
process.stderr.write('\n');
|
|
253
261
|
}
|
|
254
262
|
try {
|
|
263
|
+
// Binary response: download to file
|
|
264
|
+
if (endpoint.isBinaryResponse) {
|
|
265
|
+
if (!flags.file) {
|
|
266
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
267
|
+
}
|
|
268
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
269
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
270
|
+
data,
|
|
271
|
+
contextName: flags.context,
|
|
272
|
+
binary: true,
|
|
273
|
+
});
|
|
274
|
+
const fsModule = await import('node:fs');
|
|
275
|
+
const binData = binResponse.data;
|
|
276
|
+
const buffer = Buffer.isBuffer(binData)
|
|
277
|
+
? binData
|
|
278
|
+
: Buffer.from(binData);
|
|
279
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
280
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
255
283
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
256
284
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
257
285
|
data,
|
|
@@ -342,6 +370,7 @@ export default class ProcessorMappings extends Command {
|
|
|
342
370
|
}
|
|
343
371
|
else if (flags.set && flags.set.length > 0) {
|
|
344
372
|
const item = {};
|
|
373
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
345
374
|
for (const pair of flags.set) {
|
|
346
375
|
const eqIndex = pair.indexOf('=');
|
|
347
376
|
if (eqIndex === -1)
|
|
@@ -352,6 +381,15 @@ export default class ProcessorMappings extends Command {
|
|
|
352
381
|
value = JSON.parse(value);
|
|
353
382
|
}
|
|
354
383
|
catch { /* keep as string */ }
|
|
384
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
385
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
386
|
+
if (typeof value === 'string') {
|
|
387
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
388
|
+
}
|
|
389
|
+
else if (value !== undefined && value !== null) {
|
|
390
|
+
value = [value];
|
|
391
|
+
}
|
|
392
|
+
}
|
|
355
393
|
const keys = key.split('.');
|
|
356
394
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
357
395
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -501,14 +539,7 @@ export default class ProcessorMappings extends Command {
|
|
|
501
539
|
if (eqIndex === -1)
|
|
502
540
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
503
541
|
const key = f.slice(0, eqIndex);
|
|
504
|
-
|
|
505
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
506
|
-
if (opMatch) {
|
|
507
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
508
|
-
}
|
|
509
|
-
else {
|
|
510
|
-
value = `\$eq:${value}`;
|
|
511
|
-
}
|
|
542
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
512
543
|
const filterKey = `filter.${key}`;
|
|
513
544
|
if (filterKey in baseParams) {
|
|
514
545
|
const existing = baseParams[filterKey];
|
|
@@ -685,6 +716,7 @@ export default class ProcessorMappings extends Command {
|
|
|
685
716
|
}
|
|
686
717
|
else if (flags.set && flags.set.length > 0) {
|
|
687
718
|
const item = {};
|
|
719
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
688
720
|
for (const pair of flags.set) {
|
|
689
721
|
const eqIndex = pair.indexOf('=');
|
|
690
722
|
if (eqIndex === -1)
|
|
@@ -695,6 +727,15 @@ export default class ProcessorMappings extends Command {
|
|
|
695
727
|
value = JSON.parse(value);
|
|
696
728
|
}
|
|
697
729
|
catch { /* keep as string */ }
|
|
730
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
731
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
732
|
+
if (typeof value === 'string') {
|
|
733
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
734
|
+
}
|
|
735
|
+
else if (value !== undefined && value !== null) {
|
|
736
|
+
value = [value];
|
|
737
|
+
}
|
|
738
|
+
}
|
|
698
739
|
const keys = key.split('.');
|
|
699
740
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
700
741
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -814,35 +855,62 @@ export default class ProcessorMappings extends Command {
|
|
|
814
855
|
this.log('');
|
|
815
856
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
816
857
|
}
|
|
858
|
+
parseFilterValue(value) {
|
|
859
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
860
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
861
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
862
|
+
// no operator prefix is present.
|
|
863
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
864
|
+
const m = value.match(opRe);
|
|
865
|
+
if (!m)
|
|
866
|
+
return `\$eq:${value}`;
|
|
867
|
+
const op = m[1];
|
|
868
|
+
const rest = value.slice(m[0].length);
|
|
869
|
+
if (rest === '')
|
|
870
|
+
return `\$${op}`;
|
|
871
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
872
|
+
}
|
|
817
873
|
filterFields(data, fields) {
|
|
818
874
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
819
875
|
const pick = (obj, keys) => {
|
|
820
|
-
|
|
876
|
+
// Group keys by their first segment so sibling sub-paths
|
|
877
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
878
|
+
// and don't clobber each other on merge.
|
|
879
|
+
const directKeys = new Set();
|
|
880
|
+
const nestedKeys = new Map();
|
|
821
881
|
for (const key of keys) {
|
|
822
882
|
if (key.split('.').some(isUnsafeKey))
|
|
823
883
|
continue;
|
|
824
884
|
if (key.includes('.')) {
|
|
825
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
826
885
|
const [first, ...rest] = key.split('.');
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
...pick(item, [restKey]),
|
|
836
|
-
}));
|
|
837
|
-
}
|
|
838
|
-
else {
|
|
839
|
-
const nested = pick(val, [restKey]);
|
|
840
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
841
|
-
}
|
|
886
|
+
if (isUnsafeKey(first))
|
|
887
|
+
continue;
|
|
888
|
+
const list = nestedKeys.get(first);
|
|
889
|
+
if (list) {
|
|
890
|
+
list.push(rest.join('.'));
|
|
891
|
+
}
|
|
892
|
+
else {
|
|
893
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
842
894
|
}
|
|
843
895
|
}
|
|
844
|
-
else
|
|
896
|
+
else {
|
|
897
|
+
directKeys.add(key);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
900
|
+
const result = {};
|
|
901
|
+
for (const key of directKeys) {
|
|
902
|
+
if (key in obj)
|
|
845
903
|
result[key] = obj[key];
|
|
904
|
+
}
|
|
905
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
906
|
+
const val = obj[first];
|
|
907
|
+
if (!val || typeof val !== 'object')
|
|
908
|
+
continue;
|
|
909
|
+
if (Array.isArray(val)) {
|
|
910
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
911
|
+
}
|
|
912
|
+
else {
|
|
913
|
+
result[first] = pick(val, restKeys);
|
|
846
914
|
}
|
|
847
915
|
}
|
|
848
916
|
return result;
|
|
@@ -8,15 +8,15 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list-tools", "list", "create", "resolve", "resolve-event", "get", "update", "delete", "template"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list-tools': { method: 'GET', path: '/v1/processors/tools', summary: 'List all available tools', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'list': { method: 'GET', path: '/v1/processors', summary: 'List
|
|
13
|
-
'create': { method: 'POST', path: '/v1/processors', summary: 'Create a new tenant-scoped processor', pathParams: [], hasBody: true, isMultipart: false },
|
|
14
|
-
'resolve': { method: 'GET', path: '/v1/processors/resolve/{documentType}', summary: 'Resolve processor for a document type', pathParams: ['documentType'], hasBody: false, isMultipart: false },
|
|
15
|
-
'resolve-event': { method: 'GET', path: '/v1/processors/resolve/event/{eventType}', summary: 'Resolve processors for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false },
|
|
16
|
-
'get': { method: 'GET', path: '/v1/processors/{id}', summary: 'Get a single processor by ID', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
17
|
-
'update': { method: 'PATCH', path: '/v1/processors/{id}', summary: 'Update a tenant-scoped processor', pathParams: ['id'], hasBody: true, isMultipart: false },
|
|
18
|
-
'delete': { method: 'DELETE', path: '/v1/processors/{id}', summary: 'Delete a tenant-scoped processor', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
19
|
-
'template': { method: 'POST', path: '/v1/processors/{id}/template', summary: 'Upload a PDF template for a processor', pathParams: ['id'], hasBody: false, isMultipart: false },
|
|
11
|
+
'list-tools': { method: 'GET', path: '/v1/processors/tools', summary: 'List all available tools', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'list': { method: 'GET', path: '/v1/processors', summary: 'List processors available to the tenant', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
+
'create': { method: 'POST', path: '/v1/processors', summary: 'Create a new tenant-scoped processor', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['processorTools', 'options'] },
|
|
14
|
+
'resolve': { method: 'GET', path: '/v1/processors/resolve/{documentType}', summary: 'Resolve processor for a document type', pathParams: ['documentType'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'resolve-event': { method: 'GET', path: '/v1/processors/resolve/event/{eventType}', summary: 'Resolve processors for an event type', pathParams: ['eventType'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
+
'get': { method: 'GET', path: '/v1/processors/{id}', summary: 'Get a single processor by ID', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
|
+
'update': { method: 'PATCH', path: '/v1/processors/{id}', summary: 'Update a tenant-scoped processor', pathParams: ['id'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: ['processorTools', 'options'] },
|
|
18
|
+
'delete': { method: 'DELETE', path: '/v1/processors/{id}', summary: 'Delete a tenant-scoped processor', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
19
|
+
'template': { method: 'POST', path: '/v1/processors/{id}/template', summary: 'Upload a PDF template for a processor', pathParams: ['id'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
20
20
|
};
|
|
21
21
|
export default class Processors extends Command {
|
|
22
22
|
static description = 'Manage processors';
|
|
@@ -134,15 +134,7 @@ export default class Processors extends Command {
|
|
|
134
134
|
if (eqIndex === -1)
|
|
135
135
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
136
136
|
const key = f.slice(0, eqIndex);
|
|
137
|
-
|
|
138
|
-
// Check for operator prefix (with or without $)
|
|
139
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
140
|
-
if (opMatch) {
|
|
141
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
142
|
-
}
|
|
143
|
-
else {
|
|
144
|
-
value = `\$eq:${value}`;
|
|
145
|
-
}
|
|
137
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
146
138
|
const filterKey = `filter.${key}`;
|
|
147
139
|
if (filterKey in queryParams) {
|
|
148
140
|
// Multiple filters on same field: collect as array
|
|
@@ -175,7 +167,9 @@ export default class Processors extends Command {
|
|
|
175
167
|
data = form;
|
|
176
168
|
}
|
|
177
169
|
else if (endpoint.hasBody) {
|
|
178
|
-
|
|
170
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
171
|
+
// input file — skip the read-as-body branch.
|
|
172
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
179
173
|
const fsModule = await import('node:fs');
|
|
180
174
|
try {
|
|
181
175
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -197,6 +191,7 @@ export default class Processors extends Command {
|
|
|
197
191
|
if (!data)
|
|
198
192
|
data = {};
|
|
199
193
|
const body = data;
|
|
194
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
200
195
|
for (const pair of flags.set) {
|
|
201
196
|
const eqIndex = pair.indexOf('=');
|
|
202
197
|
if (eqIndex === -1)
|
|
@@ -208,6 +203,19 @@ export default class Processors extends Command {
|
|
|
208
203
|
value = JSON.parse(value);
|
|
209
204
|
}
|
|
210
205
|
catch { /* keep as string */ }
|
|
206
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
207
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
208
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
209
|
+
// already provided a JSON array.
|
|
210
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
211
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
212
|
+
if (typeof value === 'string') {
|
|
213
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
214
|
+
}
|
|
215
|
+
else if (value !== undefined && value !== null) {
|
|
216
|
+
value = [value];
|
|
217
|
+
}
|
|
218
|
+
}
|
|
211
219
|
// Support nested keys like "address.city"
|
|
212
220
|
const keys = key.split('.');
|
|
213
221
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -258,6 +266,26 @@ export default class Processors extends Command {
|
|
|
258
266
|
process.stderr.write('\n');
|
|
259
267
|
}
|
|
260
268
|
try {
|
|
269
|
+
// Binary response: download to file
|
|
270
|
+
if (endpoint.isBinaryResponse) {
|
|
271
|
+
if (!flags.file) {
|
|
272
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
273
|
+
}
|
|
274
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
275
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
276
|
+
data,
|
|
277
|
+
contextName: flags.context,
|
|
278
|
+
binary: true,
|
|
279
|
+
});
|
|
280
|
+
const fsModule = await import('node:fs');
|
|
281
|
+
const binData = binResponse.data;
|
|
282
|
+
const buffer = Buffer.isBuffer(binData)
|
|
283
|
+
? binData
|
|
284
|
+
: Buffer.from(binData);
|
|
285
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
286
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
261
289
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
262
290
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
263
291
|
data,
|
|
@@ -348,6 +376,7 @@ export default class Processors extends Command {
|
|
|
348
376
|
}
|
|
349
377
|
else if (flags.set && flags.set.length > 0) {
|
|
350
378
|
const item = {};
|
|
379
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
351
380
|
for (const pair of flags.set) {
|
|
352
381
|
const eqIndex = pair.indexOf('=');
|
|
353
382
|
if (eqIndex === -1)
|
|
@@ -358,6 +387,15 @@ export default class Processors extends Command {
|
|
|
358
387
|
value = JSON.parse(value);
|
|
359
388
|
}
|
|
360
389
|
catch { /* keep as string */ }
|
|
390
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
391
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
392
|
+
if (typeof value === 'string') {
|
|
393
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
394
|
+
}
|
|
395
|
+
else if (value !== undefined && value !== null) {
|
|
396
|
+
value = [value];
|
|
397
|
+
}
|
|
398
|
+
}
|
|
361
399
|
const keys = key.split('.');
|
|
362
400
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
363
401
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -507,14 +545,7 @@ export default class Processors extends Command {
|
|
|
507
545
|
if (eqIndex === -1)
|
|
508
546
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
509
547
|
const key = f.slice(0, eqIndex);
|
|
510
|
-
|
|
511
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
512
|
-
if (opMatch) {
|
|
513
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
514
|
-
}
|
|
515
|
-
else {
|
|
516
|
-
value = `\$eq:${value}`;
|
|
517
|
-
}
|
|
548
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
518
549
|
const filterKey = `filter.${key}`;
|
|
519
550
|
if (filterKey in baseParams) {
|
|
520
551
|
const existing = baseParams[filterKey];
|
|
@@ -691,6 +722,7 @@ export default class Processors extends Command {
|
|
|
691
722
|
}
|
|
692
723
|
else if (flags.set && flags.set.length > 0) {
|
|
693
724
|
const item = {};
|
|
725
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
694
726
|
for (const pair of flags.set) {
|
|
695
727
|
const eqIndex = pair.indexOf('=');
|
|
696
728
|
if (eqIndex === -1)
|
|
@@ -701,6 +733,15 @@ export default class Processors extends Command {
|
|
|
701
733
|
value = JSON.parse(value);
|
|
702
734
|
}
|
|
703
735
|
catch { /* keep as string */ }
|
|
736
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
737
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
738
|
+
if (typeof value === 'string') {
|
|
739
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
740
|
+
}
|
|
741
|
+
else if (value !== undefined && value !== null) {
|
|
742
|
+
value = [value];
|
|
743
|
+
}
|
|
744
|
+
}
|
|
704
745
|
const keys = key.split('.');
|
|
705
746
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
706
747
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -820,35 +861,62 @@ export default class Processors extends Command {
|
|
|
820
861
|
this.log('');
|
|
821
862
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
822
863
|
}
|
|
864
|
+
parseFilterValue(value) {
|
|
865
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
866
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
867
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
868
|
+
// no operator prefix is present.
|
|
869
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
870
|
+
const m = value.match(opRe);
|
|
871
|
+
if (!m)
|
|
872
|
+
return `\$eq:${value}`;
|
|
873
|
+
const op = m[1];
|
|
874
|
+
const rest = value.slice(m[0].length);
|
|
875
|
+
if (rest === '')
|
|
876
|
+
return `\$${op}`;
|
|
877
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
878
|
+
}
|
|
823
879
|
filterFields(data, fields) {
|
|
824
880
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
825
881
|
const pick = (obj, keys) => {
|
|
826
|
-
|
|
882
|
+
// Group keys by their first segment so sibling sub-paths
|
|
883
|
+
// (e.g. 'items.amount' + 'items._type') resolve in one recursive pass
|
|
884
|
+
// and don't clobber each other on merge.
|
|
885
|
+
const directKeys = new Set();
|
|
886
|
+
const nestedKeys = new Map();
|
|
827
887
|
for (const key of keys) {
|
|
828
888
|
if (key.split('.').some(isUnsafeKey))
|
|
829
889
|
continue;
|
|
830
890
|
if (key.includes('.')) {
|
|
831
|
-
// Handle nested fields like 'customer.name' or 'allocations.salesInvoiceId'
|
|
832
891
|
const [first, ...rest] = key.split('.');
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
...pick(item, [restKey]),
|
|
842
|
-
}));
|
|
843
|
-
}
|
|
844
|
-
else {
|
|
845
|
-
const nested = pick(val, [restKey]);
|
|
846
|
-
result[first] = { ...(result[first] || {}), ...nested };
|
|
847
|
-
}
|
|
892
|
+
if (isUnsafeKey(first))
|
|
893
|
+
continue;
|
|
894
|
+
const list = nestedKeys.get(first);
|
|
895
|
+
if (list) {
|
|
896
|
+
list.push(rest.join('.'));
|
|
897
|
+
}
|
|
898
|
+
else {
|
|
899
|
+
nestedKeys.set(first, [rest.join('.')]);
|
|
848
900
|
}
|
|
849
901
|
}
|
|
850
|
-
else
|
|
902
|
+
else {
|
|
903
|
+
directKeys.add(key);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
const result = {};
|
|
907
|
+
for (const key of directKeys) {
|
|
908
|
+
if (key in obj)
|
|
851
909
|
result[key] = obj[key];
|
|
910
|
+
}
|
|
911
|
+
for (const [first, restKeys] of nestedKeys) {
|
|
912
|
+
const val = obj[first];
|
|
913
|
+
if (!val || typeof val !== 'object')
|
|
914
|
+
continue;
|
|
915
|
+
if (Array.isArray(val)) {
|
|
916
|
+
result[first] = val.map(item => pick(item, restKeys));
|
|
917
|
+
}
|
|
918
|
+
else {
|
|
919
|
+
result[first] = pick(val, restKeys);
|
|
852
920
|
}
|
|
853
921
|
}
|
|
854
922
|
return result;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class Reconciliation extends Command {
|
|
3
|
+
static description: string;
|
|
4
|
+
static examples: string[];
|
|
5
|
+
static args: {
|
|
6
|
+
action: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
7
|
+
id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
|
|
8
|
+
};
|
|
9
|
+
static flags: {
|
|
10
|
+
context: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
11
|
+
output: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
table: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
|
+
csv: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
14
|
+
data: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
15
|
+
file: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
16
|
+
set: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
17
|
+
page: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
18
|
+
limit: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
19
|
+
include: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
20
|
+
search: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
21
|
+
'sort-by': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
22
|
+
filter: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
23
|
+
query: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
24
|
+
fields: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
25
|
+
sanitize: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
26
|
+
'show-http-request': import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
27
|
+
};
|
|
28
|
+
run(): Promise<void>;
|
|
29
|
+
private runImport;
|
|
30
|
+
private runExport;
|
|
31
|
+
private runCamt053Import;
|
|
32
|
+
private runNestedImport;
|
|
33
|
+
private parseFilterValue;
|
|
34
|
+
private filterFields;
|
|
35
|
+
private sanitizeOutput;
|
|
36
|
+
private getColumns;
|
|
37
|
+
private getValue;
|
|
38
|
+
private printTable;
|
|
39
|
+
private printCsv;
|
|
40
|
+
}
|