@aderis-erp/cli 1.0.3 → 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 3.js +1072 -0
- package/dist/commands/access-groups.d.ts +1 -0
- package/dist/commands/access-groups.js +79 -23
- package/dist/commands/account-transactions.d.ts +1 -0
- package/dist/commands/account-transactions.js +91 -35
- package/dist/commands/accounts 2.d.ts +41 -0
- package/dist/commands/accounts 2.js +1078 -0
- package/dist/commands/accounts.d.ts +1 -0
- package/dist/commands/accounts.js +84 -28
- package/dist/commands/agent-runs.d.ts +1 -0
- package/dist/commands/agent-runs.js +80 -22
- package/dist/commands/async-endpoints-internal 3.d.ts +40 -0
- package/dist/commands/async-endpoints-internal 3.js +1062 -0
- package/dist/commands/async-endpoints-internal.d.ts +1 -0
- package/dist/commands/async-endpoints-internal.js +77 -21
- 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 +1 -0
- package/dist/commands/audit-log.js +77 -21
- package/dist/commands/bank-accounts.d.ts +1 -0
- package/dist/commands/bank-accounts.js +91 -35
- package/dist/commands/bank-integrations.d.ts +1 -0
- package/dist/commands/bank-integrations.js +92 -36
- 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 +84 -28
- package/dist/commands/business-partners.d.ts +1 -0
- package/dist/commands/business-partners.js +84 -28
- 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 +89 -33
- package/dist/commands/documents.d.ts +1 -0
- package/dist/commands/documents.js +80 -24
- 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 +79 -23
- package/dist/commands/dunning-reminders.d.ts +1 -0
- package/dist/commands/dunning-reminders.js +82 -26
- package/dist/commands/dunning-runs 3.d.ts +40 -0
- package/dist/commands/dunning-runs 3.js +1061 -0
- package/dist/commands/dunning-runs.d.ts +1 -0
- package/dist/commands/dunning-runs.js +75 -19
- package/dist/commands/employees.d.ts +1 -0
- package/dist/commands/employees.js +106 -50
- 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 +79 -23
- 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 +83 -27
- 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 +79 -23
- package/dist/commands/inbox 3.d.ts +40 -0
- package/dist/commands/inbox 3.js +1065 -0
- package/dist/commands/inbox.d.ts +1 -0
- package/dist/commands/inbox.js +79 -23
- 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 +77 -21
- 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 +79 -23
- 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 +79 -23
- package/dist/commands/journal-lines.d.ts +1 -0
- package/dist/commands/journal-lines.js +75 -19
- 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 +93 -37
- package/dist/commands/ledgers.d.ts +1 -0
- package/dist/commands/ledgers.js +90 -34
- 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 +79 -23
- 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 +79 -23
- 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 +79 -23
- 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 +92 -36
- 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 +80 -24
- 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 +84 -28
- 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 +78 -22
- package/dist/commands/processors.d.ts +1 -0
- package/dist/commands/processors.js +83 -27
- 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 +80 -24
- package/dist/commands/reports.d.ts +1 -0
- package/dist/commands/reports.js +80 -21
- 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 +79 -23
- 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 +104 -48
- 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 +102 -46
- package/dist/commands/tags 3.d.ts +40 -0
- package/dist/commands/tags 3.js +1072 -0
- package/dist/commands/tags.d.ts +1 -0
- package/dist/commands/tags.js +79 -23
- 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 +82 -26
- package/dist/commands/tax-matrix.d.ts +1 -0
- package/dist/commands/tax-matrix.js +79 -23
- package/dist/commands/tenants.d.ts +1 -0
- package/dist/commands/tenants.js +79 -23
- 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 +79 -23
- package/dist/commands/tools.d.ts +1 -0
- package/dist/commands/tools.js +76 -20
- package/dist/commands/vendor-invoices.d.ts +1 -0
- package/dist/commands/vendor-invoices.js +108 -52
- 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 +81 -25
- package/dist/commands/workflows.d.ts +1 -0
- package/dist/commands/workflows.js +77 -21
- package/dist/lib/api-client.d.ts +5 -1
- package/dist/lib/api-client.js +19 -5
- package/dist/lib/auth.js +17 -0
- package/dist/lib/config.js +1 -1
- package/oclif.manifest.json +6099 -711
- package/package.json +1 -1
- package/dist/index 2.js +0 -1
- package/dist/index.d 2.ts +0 -1
|
@@ -8,12 +8,12 @@ const HAS_CAMT053 = false;
|
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
9
|
const SUBCOMMANDS = [...["list", "transactions-matches", "transactions-summary", "matches-confirm", "matches-reject", "process-transaction"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
10
10
|
const ENDPOINTS = {
|
|
11
|
-
'list': { method: 'GET', path: '/v1/reconciliation/pending', summary: 'Get pending reconciliation matches', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'transactions-matches': { method: 'GET', path: '/v1/reconciliation/transactions/{transactionId}/matches', summary: 'Get matches for a transaction', pathParams: ['transactionId'], hasBody: false, isMultipart: false },
|
|
13
|
-
'transactions-summary': { method: 'GET', path: '/v1/reconciliation/transactions/{transactionId}/summary', summary: 'Get transaction allocation summary', pathParams: ['transactionId'], hasBody: false, isMultipart: false },
|
|
14
|
-
'matches-confirm': { method: 'POST', path: '/v1/reconciliation/matches/{matchId}/confirm', summary: 'Confirm a reconciliation match', pathParams: ['matchId'], hasBody: false, isMultipart: false },
|
|
15
|
-
'matches-reject': { method: 'POST', path: '/v1/reconciliation/matches/{matchId}/reject', summary: 'Reject a reconciliation match', pathParams: ['matchId'], hasBody: true, isMultipart: false },
|
|
16
|
-
'process-transaction': { method: 'POST', path: '/v1/reconciliation/transactions/{transactionId}/process', summary: 'Trigger reconciliation for a transaction', pathParams: ['transactionId'], hasBody: false, isMultipart: false },
|
|
11
|
+
'list': { method: 'GET', path: '/v1/reconciliation/pending', summary: 'Get pending reconciliation matches', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'transactions-matches': { method: 'GET', path: '/v1/reconciliation/transactions/{transactionId}/matches', summary: 'Get matches for a transaction', pathParams: ['transactionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
13
|
+
'transactions-summary': { method: 'GET', path: '/v1/reconciliation/transactions/{transactionId}/summary', summary: 'Get transaction allocation summary', pathParams: ['transactionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
14
|
+
'matches-confirm': { method: 'POST', path: '/v1/reconciliation/matches/{matchId}/confirm', summary: 'Confirm a reconciliation match', pathParams: ['matchId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
15
|
+
'matches-reject': { method: 'POST', path: '/v1/reconciliation/matches/{matchId}/reject', summary: 'Reject a reconciliation match', pathParams: ['matchId'], hasBody: true, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
16
|
+
'process-transaction': { method: 'POST', path: '/v1/reconciliation/transactions/{transactionId}/process', summary: 'Trigger reconciliation for a transaction', pathParams: ['transactionId'], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
17
17
|
};
|
|
18
18
|
export default class Reconciliation extends Command {
|
|
19
19
|
static description = 'Manage reconciliation';
|
|
@@ -127,15 +127,7 @@ export default class Reconciliation extends Command {
|
|
|
127
127
|
if (eqIndex === -1)
|
|
128
128
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
129
129
|
const key = f.slice(0, eqIndex);
|
|
130
|
-
|
|
131
|
-
// Check for operator prefix (with or without $)
|
|
132
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
133
|
-
if (opMatch) {
|
|
134
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
135
|
-
}
|
|
136
|
-
else {
|
|
137
|
-
value = `\$eq:${value}`;
|
|
138
|
-
}
|
|
130
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
139
131
|
const filterKey = `filter.${key}`;
|
|
140
132
|
if (filterKey in queryParams) {
|
|
141
133
|
// Multiple filters on same field: collect as array
|
|
@@ -168,7 +160,9 @@ export default class Reconciliation extends Command {
|
|
|
168
160
|
data = form;
|
|
169
161
|
}
|
|
170
162
|
else if (endpoint.hasBody) {
|
|
171
|
-
|
|
163
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
164
|
+
// input file — skip the read-as-body branch.
|
|
165
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
172
166
|
const fsModule = await import('node:fs');
|
|
173
167
|
try {
|
|
174
168
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -190,6 +184,7 @@ export default class Reconciliation extends Command {
|
|
|
190
184
|
if (!data)
|
|
191
185
|
data = {};
|
|
192
186
|
const body = data;
|
|
187
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
193
188
|
for (const pair of flags.set) {
|
|
194
189
|
const eqIndex = pair.indexOf('=');
|
|
195
190
|
if (eqIndex === -1)
|
|
@@ -201,6 +196,19 @@ export default class Reconciliation extends Command {
|
|
|
201
196
|
value = JSON.parse(value);
|
|
202
197
|
}
|
|
203
198
|
catch { /* keep as string */ }
|
|
199
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
200
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
201
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
202
|
+
// already provided a JSON array.
|
|
203
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
204
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
205
|
+
if (typeof value === 'string') {
|
|
206
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
207
|
+
}
|
|
208
|
+
else if (value !== undefined && value !== null) {
|
|
209
|
+
value = [value];
|
|
210
|
+
}
|
|
211
|
+
}
|
|
204
212
|
// Support nested keys like "address.city"
|
|
205
213
|
const keys = key.split('.');
|
|
206
214
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -251,6 +259,26 @@ export default class Reconciliation extends Command {
|
|
|
251
259
|
process.stderr.write('\n');
|
|
252
260
|
}
|
|
253
261
|
try {
|
|
262
|
+
// Binary response: download to file
|
|
263
|
+
if (endpoint.isBinaryResponse) {
|
|
264
|
+
if (!flags.file) {
|
|
265
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
266
|
+
}
|
|
267
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
268
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
269
|
+
data,
|
|
270
|
+
contextName: flags.context,
|
|
271
|
+
binary: true,
|
|
272
|
+
});
|
|
273
|
+
const fsModule = await import('node:fs');
|
|
274
|
+
const binData = binResponse.data;
|
|
275
|
+
const buffer = Buffer.isBuffer(binData)
|
|
276
|
+
? binData
|
|
277
|
+
: Buffer.from(binData);
|
|
278
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
279
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
254
282
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
255
283
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
256
284
|
data,
|
|
@@ -341,6 +369,7 @@ export default class Reconciliation extends Command {
|
|
|
341
369
|
}
|
|
342
370
|
else if (flags.set && flags.set.length > 0) {
|
|
343
371
|
const item = {};
|
|
372
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
344
373
|
for (const pair of flags.set) {
|
|
345
374
|
const eqIndex = pair.indexOf('=');
|
|
346
375
|
if (eqIndex === -1)
|
|
@@ -351,6 +380,15 @@ export default class Reconciliation extends Command {
|
|
|
351
380
|
value = JSON.parse(value);
|
|
352
381
|
}
|
|
353
382
|
catch { /* keep as string */ }
|
|
383
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
384
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
385
|
+
if (typeof value === 'string') {
|
|
386
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
387
|
+
}
|
|
388
|
+
else if (value !== undefined && value !== null) {
|
|
389
|
+
value = [value];
|
|
390
|
+
}
|
|
391
|
+
}
|
|
354
392
|
const keys = key.split('.');
|
|
355
393
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
356
394
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -500,14 +538,7 @@ export default class Reconciliation extends Command {
|
|
|
500
538
|
if (eqIndex === -1)
|
|
501
539
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
502
540
|
const key = f.slice(0, eqIndex);
|
|
503
|
-
|
|
504
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
505
|
-
if (opMatch) {
|
|
506
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
507
|
-
}
|
|
508
|
-
else {
|
|
509
|
-
value = `\$eq:${value}`;
|
|
510
|
-
}
|
|
541
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
511
542
|
const filterKey = `filter.${key}`;
|
|
512
543
|
if (filterKey in baseParams) {
|
|
513
544
|
const existing = baseParams[filterKey];
|
|
@@ -684,6 +715,7 @@ export default class Reconciliation extends Command {
|
|
|
684
715
|
}
|
|
685
716
|
else if (flags.set && flags.set.length > 0) {
|
|
686
717
|
const item = {};
|
|
718
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
687
719
|
for (const pair of flags.set) {
|
|
688
720
|
const eqIndex = pair.indexOf('=');
|
|
689
721
|
if (eqIndex === -1)
|
|
@@ -694,6 +726,15 @@ export default class Reconciliation extends Command {
|
|
|
694
726
|
value = JSON.parse(value);
|
|
695
727
|
}
|
|
696
728
|
catch { /* keep as string */ }
|
|
729
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
730
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
731
|
+
if (typeof value === 'string') {
|
|
732
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
733
|
+
}
|
|
734
|
+
else if (value !== undefined && value !== null) {
|
|
735
|
+
value = [value];
|
|
736
|
+
}
|
|
737
|
+
}
|
|
697
738
|
const keys = key.split('.');
|
|
698
739
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
699
740
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -813,6 +854,21 @@ export default class Reconciliation extends Command {
|
|
|
813
854
|
this.log('');
|
|
814
855
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
815
856
|
}
|
|
857
|
+
parseFilterValue(value) {
|
|
858
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
859
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
860
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
861
|
+
// no operator prefix is present.
|
|
862
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
863
|
+
const m = value.match(opRe);
|
|
864
|
+
if (!m)
|
|
865
|
+
return `\$eq:${value}`;
|
|
866
|
+
const op = m[1];
|
|
867
|
+
const rest = value.slice(m[0].length);
|
|
868
|
+
if (rest === '')
|
|
869
|
+
return `\$${op}`;
|
|
870
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
871
|
+
}
|
|
816
872
|
filterFields(data, fields) {
|
|
817
873
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
818
874
|
const pick = (obj, keys) => {
|
package/dist/commands/reports.js
CHANGED
|
@@ -6,10 +6,13 @@ const HAS_IMPORT = false;
|
|
|
6
6
|
const HAS_EXPORT = false;
|
|
7
7
|
const HAS_CAMT053 = false;
|
|
8
8
|
const NESTED_IMPORTS = [];
|
|
9
|
-
const SUBCOMMANDS = [...["profit-and-loss", "balance-sheet"], ...(HAS_IMPORT ? ['import'] : []), ...(HAS_EXPORT ? ['export'] : []), ...NESTED_IMPORTS.map(n => `import-${n}`)];
|
|
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: 'GET', path: '/v1/reports/profit-and-loss', summary: 'Get Profit & Loss report', pathParams: [], hasBody: false, isMultipart: false },
|
|
12
|
-
'balance-sheet': { method: 'GET', path: '/v1/reports/balance-sheet', summary: 'Generate Balance Sheet report', pathParams: [], hasBody: false, isMultipart: false },
|
|
11
|
+
'profit-and-loss': { method: 'GET', path: '/v1/reports/profit-and-loss', summary: 'Get Profit & Loss report', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
12
|
+
'balance-sheet': { method: 'GET', path: '/v1/reports/balance-sheet', summary: 'Generate Balance Sheet report', pathParams: [], hasBody: false, isMultipart: false, isBinaryResponse: false, bodyArrayFields: [] },
|
|
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
|
+
'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'] },
|
|
15
|
+
'gl-detail-export-pdf': { method: 'POST', path: '/v1/reports/gl-detail/export/pdf', summary: 'Export GL Detail report as PDF (one page per account)', pathParams: [], hasBody: true, isMultipart: false, isBinaryResponse: true, bodyArrayFields: ['accountIds', 'accountCodes'] },
|
|
13
16
|
};
|
|
14
17
|
export default class Reports extends Command {
|
|
15
18
|
static description = 'Manage reports';
|
|
@@ -120,15 +123,7 @@ export default class Reports extends Command {
|
|
|
120
123
|
if (eqIndex === -1)
|
|
121
124
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
122
125
|
const key = f.slice(0, eqIndex);
|
|
123
|
-
|
|
124
|
-
// Check for operator prefix (with or without $)
|
|
125
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
126
|
-
if (opMatch) {
|
|
127
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
value = `\$eq:${value}`;
|
|
131
|
-
}
|
|
126
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
132
127
|
const filterKey = `filter.${key}`;
|
|
133
128
|
if (filterKey in queryParams) {
|
|
134
129
|
// Multiple filters on same field: collect as array
|
|
@@ -161,7 +156,9 @@ export default class Reports extends Command {
|
|
|
161
156
|
data = form;
|
|
162
157
|
}
|
|
163
158
|
else if (endpoint.hasBody) {
|
|
164
|
-
|
|
159
|
+
// For binary-response endpoints, --file is the output path, not a JSON
|
|
160
|
+
// input file — skip the read-as-body branch.
|
|
161
|
+
if (flags.file && !endpoint.isBinaryResponse) {
|
|
165
162
|
const fsModule = await import('node:fs');
|
|
166
163
|
try {
|
|
167
164
|
data = JSON.parse(fsModule.readFileSync(flags.file, 'utf-8'));
|
|
@@ -183,6 +180,7 @@ export default class Reports extends Command {
|
|
|
183
180
|
if (!data)
|
|
184
181
|
data = {};
|
|
185
182
|
const body = data;
|
|
183
|
+
const arrayFields = new Set(endpoint.bodyArrayFields);
|
|
186
184
|
for (const pair of flags.set) {
|
|
187
185
|
const eqIndex = pair.indexOf('=');
|
|
188
186
|
if (eqIndex === -1)
|
|
@@ -194,6 +192,19 @@ export default class Reports extends Command {
|
|
|
194
192
|
value = JSON.parse(value);
|
|
195
193
|
}
|
|
196
194
|
catch { /* keep as string */ }
|
|
195
|
+
// For top-level keys typed as array in the API schema, split bare
|
|
196
|
+
// comma-separated input (e.g. "1171,1170" -> ["1171","1170"]) and
|
|
197
|
+
// wrap a lone scalar in a single-element array. Skips when --set
|
|
198
|
+
// already provided a JSON array.
|
|
199
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
200
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
201
|
+
if (typeof value === 'string') {
|
|
202
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
203
|
+
}
|
|
204
|
+
else if (value !== undefined && value !== null) {
|
|
205
|
+
value = [value];
|
|
206
|
+
}
|
|
207
|
+
}
|
|
197
208
|
// Support nested keys like "address.city"
|
|
198
209
|
const keys = key.split('.');
|
|
199
210
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
@@ -244,6 +255,26 @@ export default class Reports extends Command {
|
|
|
244
255
|
process.stderr.write('\n');
|
|
245
256
|
}
|
|
246
257
|
try {
|
|
258
|
+
// Binary response: download to file
|
|
259
|
+
if (endpoint.isBinaryResponse) {
|
|
260
|
+
if (!flags.file) {
|
|
261
|
+
this.error(`Endpoint returns a binary file. Specify the output path with -f / --file (e.g. -f report.xlsx).`);
|
|
262
|
+
}
|
|
263
|
+
const binResponse = await apiRequest(endpoint.method, apiPath, {
|
|
264
|
+
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
265
|
+
data,
|
|
266
|
+
contextName: flags.context,
|
|
267
|
+
binary: true,
|
|
268
|
+
});
|
|
269
|
+
const fsModule = await import('node:fs');
|
|
270
|
+
const binData = binResponse.data;
|
|
271
|
+
const buffer = Buffer.isBuffer(binData)
|
|
272
|
+
? binData
|
|
273
|
+
: Buffer.from(binData);
|
|
274
|
+
fsModule.writeFileSync(flags.file, buffer);
|
|
275
|
+
this.log(`Saved ${buffer.length} bytes to ${flags.file}`);
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
247
278
|
const response = await apiRequest(endpoint.method, apiPath, {
|
|
248
279
|
params: Object.keys(queryParams).length > 0 ? queryParams : undefined,
|
|
249
280
|
data,
|
|
@@ -334,6 +365,7 @@ export default class Reports extends Command {
|
|
|
334
365
|
}
|
|
335
366
|
else if (flags.set && flags.set.length > 0) {
|
|
336
367
|
const item = {};
|
|
368
|
+
const arrayFields = new Set(ENDPOINTS['create']?.bodyArrayFields ?? []);
|
|
337
369
|
for (const pair of flags.set) {
|
|
338
370
|
const eqIndex = pair.indexOf('=');
|
|
339
371
|
if (eqIndex === -1)
|
|
@@ -344,6 +376,15 @@ export default class Reports extends Command {
|
|
|
344
376
|
value = JSON.parse(value);
|
|
345
377
|
}
|
|
346
378
|
catch { /* keep as string */ }
|
|
379
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
380
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
381
|
+
if (typeof value === 'string') {
|
|
382
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
383
|
+
}
|
|
384
|
+
else if (value !== undefined && value !== null) {
|
|
385
|
+
value = [value];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
347
388
|
const keys = key.split('.');
|
|
348
389
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
349
390
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -493,14 +534,7 @@ export default class Reports extends Command {
|
|
|
493
534
|
if (eqIndex === -1)
|
|
494
535
|
this.error(`Invalid --filter format: ${f}. Use: --filter key=value`);
|
|
495
536
|
const key = f.slice(0, eqIndex);
|
|
496
|
-
|
|
497
|
-
const opMatch = value.match(/^\$?(eq|ne|in|like|gt|gte|lt|lte|null):/);
|
|
498
|
-
if (opMatch) {
|
|
499
|
-
value = `\$${opMatch[1]}:${value.slice(opMatch[0].length)}`;
|
|
500
|
-
}
|
|
501
|
-
else {
|
|
502
|
-
value = `\$eq:${value}`;
|
|
503
|
-
}
|
|
537
|
+
const value = this.parseFilterValue(f.slice(eqIndex + 1));
|
|
504
538
|
const filterKey = `filter.${key}`;
|
|
505
539
|
if (filterKey in baseParams) {
|
|
506
540
|
const existing = baseParams[filterKey];
|
|
@@ -677,6 +711,7 @@ export default class Reports extends Command {
|
|
|
677
711
|
}
|
|
678
712
|
else if (flags.set && flags.set.length > 0) {
|
|
679
713
|
const item = {};
|
|
714
|
+
const arrayFields = new Set(ENDPOINTS[`create-${suffix}`]?.bodyArrayFields ?? []);
|
|
680
715
|
for (const pair of flags.set) {
|
|
681
716
|
const eqIndex = pair.indexOf('=');
|
|
682
717
|
if (eqIndex === -1)
|
|
@@ -687,6 +722,15 @@ export default class Reports extends Command {
|
|
|
687
722
|
value = JSON.parse(value);
|
|
688
723
|
}
|
|
689
724
|
catch { /* keep as string */ }
|
|
725
|
+
const isArrayField = !key.includes('.') && arrayFields.has(key);
|
|
726
|
+
if (isArrayField && !Array.isArray(value)) {
|
|
727
|
+
if (typeof value === 'string') {
|
|
728
|
+
value = value.split(',').map(v => v.trim()).filter(v => v.length > 0);
|
|
729
|
+
}
|
|
730
|
+
else if (value !== undefined && value !== null) {
|
|
731
|
+
value = [value];
|
|
732
|
+
}
|
|
733
|
+
}
|
|
690
734
|
const keys = key.split('.');
|
|
691
735
|
if (keys.some(k => k === '__proto__' || k === 'constructor' || k === 'prototype')) {
|
|
692
736
|
this.error(`Invalid --set key: ${key}`);
|
|
@@ -806,6 +850,21 @@ export default class Reports extends Command {
|
|
|
806
850
|
this.log('');
|
|
807
851
|
this.log(`Summary: ${created} created, ${updated} updated, ${errors.length} failed`);
|
|
808
852
|
}
|
|
853
|
+
parseFilterValue(value) {
|
|
854
|
+
// Recognises the nestjs-paginate operator set (with or without the leading $)
|
|
855
|
+
// and recursively unwraps chained operators like 'not:null' -> '$not:$null'
|
|
856
|
+
// or 'not:like:John' -> '$not:$like:John'. Falls back to '$eq:<value>' when
|
|
857
|
+
// no operator prefix is present.
|
|
858
|
+
const opRe = /^\$?(and|or|not|eq|gt|gte|in|null|lt|lte|btw|ne|ilike|like|sw|contains)(?::|$)/;
|
|
859
|
+
const m = value.match(opRe);
|
|
860
|
+
if (!m)
|
|
861
|
+
return `\$eq:${value}`;
|
|
862
|
+
const op = m[1];
|
|
863
|
+
const rest = value.slice(m[0].length);
|
|
864
|
+
if (rest === '')
|
|
865
|
+
return `\$${op}`;
|
|
866
|
+
return opRe.test(rest) ? `\$${op}:${this.parseFilterValue(rest)}` : `\$${op}:${rest}`;
|
|
867
|
+
}
|
|
809
868
|
filterFields(data, fields) {
|
|
810
869
|
const isUnsafeKey = (k) => k === '__proto__' || k === 'constructor' || k === 'prototype';
|
|
811
870
|
const pick = (obj, keys) => {
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class SalaryTypes 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
|
+
}
|