jaz-clio 4.28.0 → 4.29.0
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/assets/skills/api/SKILL.md +1 -1
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +1 -1
- package/assets/skills/transaction-recipes/SKILL.md +1 -1
- package/dist/commands/bills.js +9 -35
- package/dist/commands/cashflow.js +5 -16
- package/dist/commands/contacts.js +8 -10
- package/dist/commands/customer-credit-notes.js +2 -8
- package/dist/commands/fixed-assets.js +1 -1
- package/dist/commands/invoices.js +9 -35
- package/dist/commands/journals.js +6 -19
- package/dist/commands/supplier-credit-notes.js +2 -8
- package/dist/core/api/pagination.js +12 -7
- package/dist/core/registry/index.js +1 -1
- package/dist/core/registry/pagination.js +103 -42
- package/dist/core/registry/tools.js +146 -141
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.29.0
|
|
4
4
|
description: Complete reference for the Jaz REST API — the accounting platform backend. Use this skill whenever building, modifying, debugging, or extending any code that calls the API — including API clients, integrations, data seeding, test data, or new endpoint work. Contains every field name, response shape, error, gotcha, and edge case discovered through live production testing.
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Requires Jaz API key (x-jk-api-key header). Works with Claude Code, Google Antigravity, OpenAI Codex, GitHub Copilot, Cursor, and any agent that reads markdown.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-conversion
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.29.0
|
|
4
4
|
description: Accounting data conversion skill — migrates customer data from Xero, QuickBooks, Sage, MYOB, and Excel exports to Jaz. Covers config, quick, and full conversion workflows, Excel parsing, CoA/contact/tax/items mapping, clearing accounts, TTB, and TB verification.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-jobs
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.29.0
|
|
4
4
|
description: 12 accounting jobs for SMB bookkeepers and accountants — month-end, quarter-end, and year-end close playbooks plus 9 ad-hoc operational jobs (bank recon, document collection, GST/VAT filing, payment runs, credit control, supplier recon, audit prep, fixed asset review, statutory filing). Jobs can have paired tools as nested subcommands (e.g., `clio jobs bank-recon match`, `clio jobs document-collection ingest`, `clio jobs statutory-filing sg-cs`). Paired with an interactive CLI blueprint generator (clio jobs).
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill. For individual transaction patterns, load the jaz-recipes skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-recipes
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.29.0
|
|
4
4
|
description: 16 IFRS-compliant recipes for complex multi-step accounting in Jaz — prepaid amortization, deferred revenue, loan schedules, IFRS 16 leases, hire purchase, fixed deposits, asset disposal, FX revaluation, ECL provisioning, IAS 37 provisions, dividends, intercompany, and capital WIP. Each recipe includes journal entries, capsule structure, and verification steps. Paired with 13 financial calculators that produce execution-ready blueprints with workings.
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill alongside this one.
|
package/dist/commands/bills.js
CHANGED
|
@@ -6,6 +6,7 @@ import { apiAction } from './api-action.js';
|
|
|
6
6
|
import { resolveContactFlag, resolveAccountFlag, resolveTaxProfileFlag } from './resolve.js';
|
|
7
7
|
import { parsePositiveInt, parseNonNegativeInt, parseMoney, parseRate, parseLineItems, parseCustomFields, readBodyInput, requireFields } from './parsers.js';
|
|
8
8
|
import { paginatedFetch } from './pagination.js';
|
|
9
|
+
import { buildInvoiceBillFilter } from '../core/registry/pagination.js';
|
|
9
10
|
import { outputList } from './output.js';
|
|
10
11
|
import { BILL_REQUIRED_FIELDS, buildDraftReport, formatDraftTable, addDraftFinalizeOptions, mergeDraftFlags, validateDraft, buildValidation, normalizeDate, sanitizeLineItem, } from './draft-helpers.js';
|
|
11
12
|
const BILL_COLUMNS = [
|
|
@@ -73,7 +74,7 @@ export function registerBillsCommand(program) {
|
|
|
73
74
|
.command('search')
|
|
74
75
|
.description('Search bills with filters')
|
|
75
76
|
.option('--ref <reference>', 'Filter by reference (contains)')
|
|
76
|
-
.option('--status <status>', 'Filter by status (DRAFT, UNPAID, PAID, OVERDUE,
|
|
77
|
+
.option('--status <status>', 'Filter by status (DRAFT, UNPAID, PARTIALLY_PAID, PAID, OVERDUE, VOID)')
|
|
77
78
|
.option('--contact <resourceId>', 'Filter by contact name or resourceId')
|
|
78
79
|
.option('--tag <name>', 'Filter by tag')
|
|
79
80
|
.option('--min-amount <n>', 'Minimum total amount', parseMoney)
|
|
@@ -97,40 +98,13 @@ export function registerBillsCommand(program) {
|
|
|
97
98
|
const resolved = await resolveContactFlag(client, opts.contact, { silent: opts.json });
|
|
98
99
|
opts.contact = resolved.resourceId;
|
|
99
100
|
}
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
if (opts.tag)
|
|
108
|
-
filter.tags = { eq: opts.tag };
|
|
109
|
-
if (opts.minAmount !== undefined || opts.maxAmount !== undefined) {
|
|
110
|
-
const af = {};
|
|
111
|
-
if (opts.minAmount !== undefined)
|
|
112
|
-
af.gte = opts.minAmount;
|
|
113
|
-
if (opts.maxAmount !== undefined)
|
|
114
|
-
af.lte = opts.maxAmount;
|
|
115
|
-
filter.totalAmount = af;
|
|
116
|
-
}
|
|
117
|
-
if (opts.from || opts.to) {
|
|
118
|
-
const dateFilter = {};
|
|
119
|
-
if (opts.from)
|
|
120
|
-
dateFilter.gte = opts.from;
|
|
121
|
-
if (opts.to)
|
|
122
|
-
dateFilter.lte = opts.to;
|
|
123
|
-
filter.valueDate = dateFilter;
|
|
124
|
-
}
|
|
125
|
-
if (opts.dueFrom || opts.dueTo) {
|
|
126
|
-
const df = {};
|
|
127
|
-
if (opts.dueFrom)
|
|
128
|
-
df.gte = opts.dueFrom;
|
|
129
|
-
if (opts.dueTo)
|
|
130
|
-
df.lte = opts.dueTo;
|
|
131
|
-
filter.dueDate = df;
|
|
132
|
-
}
|
|
133
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
101
|
+
const searchFilter = buildInvoiceBillFilter({
|
|
102
|
+
reference: opts.ref, status: opts.status,
|
|
103
|
+
contactResourceId: opts.contact, tag: opts.tag,
|
|
104
|
+
minAmount: opts.minAmount, maxAmount: opts.maxAmount,
|
|
105
|
+
startDate: opts.from, endDate: opts.to,
|
|
106
|
+
dueDateFrom: opts.dueFrom, dueDateTo: opts.dueTo,
|
|
107
|
+
});
|
|
134
108
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
135
109
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchBills(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching bills', defaultLimit: 20 });
|
|
136
110
|
outputList(result, BILL_COLUMNS, opts, 'Bills'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -3,6 +3,7 @@ import { searchCashflowTransactions } from '../core/api/cashflow.js';
|
|
|
3
3
|
import { apiAction } from './api-action.js';
|
|
4
4
|
import { parsePositiveInt, parseNonNegativeInt } from './parsers.js';
|
|
5
5
|
import { paginatedFetch } from './pagination.js';
|
|
6
|
+
import { buildCashflowFilter } from '../core/registry/pagination.js';
|
|
6
7
|
import { outputList } from './output.js';
|
|
7
8
|
import { formatId, formatReference, formatDirection, formatEpochDate } from './format-helpers.js';
|
|
8
9
|
const CASHFLOW_COLUMNS = [
|
|
@@ -37,22 +38,10 @@ export function registerCashflowCommand(program) {
|
|
|
37
38
|
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
38
39
|
.option('--json', 'Output as JSON')
|
|
39
40
|
.action(apiAction(async (client, opts) => {
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
filter.businessTransactionType = { eq: opts.type };
|
|
45
|
-
if (opts.direction)
|
|
46
|
-
filter.direction = { eq: opts.direction };
|
|
47
|
-
if (opts.from || opts.to) {
|
|
48
|
-
const dateFilter = {};
|
|
49
|
-
if (opts.from)
|
|
50
|
-
dateFilter.gte = opts.from;
|
|
51
|
-
if (opts.to)
|
|
52
|
-
dateFilter.lte = opts.to;
|
|
53
|
-
filter.valueDate = dateFilter;
|
|
54
|
-
}
|
|
55
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
41
|
+
const searchFilter = buildCashflowFilter({
|
|
42
|
+
reference: opts.ref, businessTransactionType: opts.type,
|
|
43
|
+
direction: opts.direction, startDate: opts.from, endDate: opts.to,
|
|
44
|
+
});
|
|
56
45
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
57
46
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchCashflowTransactions(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching cashflow transactions', defaultLimit: 20 });
|
|
58
47
|
outputList(result, CASHFLOW_COLUMNS, opts, 'Cashflow'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -4,6 +4,7 @@ import { apiAction } from './api-action.js';
|
|
|
4
4
|
import { outputList } from './output.js';
|
|
5
5
|
import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
|
|
6
6
|
import { paginatedFetch } from './pagination.js';
|
|
7
|
+
import { buildContactFilter } from '../core/registry/pagination.js';
|
|
7
8
|
import { formatId } from './format-helpers.js';
|
|
8
9
|
const CONTACTS_COLUMNS = [
|
|
9
10
|
{ key: 'resourceId', header: 'ID', format: formatId },
|
|
@@ -48,16 +49,13 @@ export function registerContactsCommand(program) {
|
|
|
48
49
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
49
50
|
.option('--json', 'Output as JSON')
|
|
50
51
|
.action((query, opts) => apiAction(async (client) => {
|
|
51
|
-
const filter = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
filter.supplier = { eq: true };
|
|
59
|
-
if (opts.email)
|
|
60
|
-
filter.email = { contains: opts.email };
|
|
52
|
+
const filter = buildContactFilter({
|
|
53
|
+
query,
|
|
54
|
+
isCustomer: opts.customer ? true : undefined,
|
|
55
|
+
isSupplier: opts.supplier ? true : undefined,
|
|
56
|
+
status: opts.status ?? 'ACTIVE',
|
|
57
|
+
email: opts.email,
|
|
58
|
+
});
|
|
61
59
|
const sort = { sortBy: [opts.sort ?? 'name'], order: (opts.order ?? 'ASC') };
|
|
62
60
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchContacts(client, { filter, limit, offset, sort }), { label: 'Searching contacts', defaultLimit: 20 });
|
|
63
61
|
outputList(result, CONTACTS_COLUMNS, opts, 'Contacts'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
|
+
import { buildCnFilter } from '../core/registry/index.js';
|
|
3
4
|
import { listCustomerCreditNotes, getCustomerCreditNote, searchCustomerCreditNotes, createCustomerCreditNote, updateCustomerCreditNote, deleteCustomerCreditNote, createCustomerCreditNoteRefund, listCustomerCreditNoteRefunds, finalizeCustomerCreditNote, downloadCustomerCreditNotePdf, } from '../core/api/customer-cn.js';
|
|
4
5
|
import { listAttachments } from '../core/api/attachments.js';
|
|
5
6
|
import { apiAction } from './api-action.js';
|
|
@@ -87,14 +88,7 @@ export function registerCustomerCreditNotesCommand(program) {
|
|
|
87
88
|
const resolved = await resolveContactFlag(client, opts.contact, { silent: opts.json });
|
|
88
89
|
opts.contact = resolved.resourceId;
|
|
89
90
|
}
|
|
90
|
-
const
|
|
91
|
-
if (opts.ref)
|
|
92
|
-
filter.reference = { contains: opts.ref };
|
|
93
|
-
if (opts.status)
|
|
94
|
-
filter.status = { eq: opts.status };
|
|
95
|
-
if (opts.contact)
|
|
96
|
-
filter.contactResourceId = { eq: opts.contact };
|
|
97
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
91
|
+
const searchFilter = buildCnFilter({ reference: opts.ref, status: opts.status, contactResourceId: opts.contact });
|
|
98
92
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
99
93
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchCustomerCreditNotes(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching customer credit notes', defaultLimit: 20 });
|
|
100
94
|
outputList(result, CCN_COLUMNS, opts, 'Customer Credit Notes'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -72,7 +72,7 @@ export function registerFixedAssetsCommand(program) {
|
|
|
72
72
|
if (opts.status)
|
|
73
73
|
filter.status = { eq: opts.status };
|
|
74
74
|
if (opts.tag)
|
|
75
|
-
filter.tags = { eq: opts.tag };
|
|
75
|
+
filter.tags = { name: { eq: opts.tag } };
|
|
76
76
|
if (opts.category)
|
|
77
77
|
filter.category = { eq: opts.category };
|
|
78
78
|
if (opts.reference)
|
|
@@ -6,6 +6,7 @@ import { apiAction } from './api-action.js';
|
|
|
6
6
|
import { resolveContactFlag, resolveAccountFlag, resolveTaxProfileFlag } from './resolve.js';
|
|
7
7
|
import { parsePositiveInt, parseNonNegativeInt, parseMoney, parseRate, parseLineItems, parseCustomFields, readBodyInput, requireFields } from './parsers.js';
|
|
8
8
|
import { paginatedFetch } from './pagination.js';
|
|
9
|
+
import { buildInvoiceBillFilter } from '../core/registry/pagination.js';
|
|
9
10
|
import { outputList } from './output.js';
|
|
10
11
|
import { INVOICE_REQUIRED_FIELDS, buildDraftReport, formatDraftTable, addDraftFinalizeOptions, mergeDraftFlags, validateDraft, buildValidation, normalizeDate, sanitizeLineItem, } from './draft-helpers.js';
|
|
11
12
|
const INVOICE_COLUMNS = [
|
|
@@ -75,7 +76,7 @@ export function registerInvoicesCommand(program) {
|
|
|
75
76
|
.command('search')
|
|
76
77
|
.description('Search invoices with filters')
|
|
77
78
|
.option('--ref <reference>', 'Filter by reference (contains)')
|
|
78
|
-
.option('--status <status>', 'Filter by status (DRAFT, UNPAID, PAID, OVERDUE,
|
|
79
|
+
.option('--status <status>', 'Filter by status (DRAFT, UNPAID, PARTIALLY_PAID, PAID, OVERDUE, VOID)')
|
|
79
80
|
.option('--contact <resourceId>', 'Filter by contact name or resourceId')
|
|
80
81
|
.option('--tag <name>', 'Filter by tag')
|
|
81
82
|
.option('--min-amount <n>', 'Minimum total amount', parseMoney)
|
|
@@ -99,40 +100,13 @@ export function registerInvoicesCommand(program) {
|
|
|
99
100
|
const resolved = await resolveContactFlag(client, opts.contact, { silent: opts.json });
|
|
100
101
|
opts.contact = resolved.resourceId;
|
|
101
102
|
}
|
|
102
|
-
const
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
if (opts.tag)
|
|
110
|
-
filter.tags = { eq: opts.tag };
|
|
111
|
-
if (opts.minAmount !== undefined || opts.maxAmount !== undefined) {
|
|
112
|
-
const af = {};
|
|
113
|
-
if (opts.minAmount !== undefined)
|
|
114
|
-
af.gte = opts.minAmount;
|
|
115
|
-
if (opts.maxAmount !== undefined)
|
|
116
|
-
af.lte = opts.maxAmount;
|
|
117
|
-
filter.totalAmount = af;
|
|
118
|
-
}
|
|
119
|
-
if (opts.from || opts.to) {
|
|
120
|
-
const dateFilter = {};
|
|
121
|
-
if (opts.from)
|
|
122
|
-
dateFilter.gte = opts.from;
|
|
123
|
-
if (opts.to)
|
|
124
|
-
dateFilter.lte = opts.to;
|
|
125
|
-
filter.valueDate = dateFilter;
|
|
126
|
-
}
|
|
127
|
-
if (opts.dueFrom || opts.dueTo) {
|
|
128
|
-
const df = {};
|
|
129
|
-
if (opts.dueFrom)
|
|
130
|
-
df.gte = opts.dueFrom;
|
|
131
|
-
if (opts.dueTo)
|
|
132
|
-
df.lte = opts.dueTo;
|
|
133
|
-
filter.dueDate = df;
|
|
134
|
-
}
|
|
135
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
103
|
+
const searchFilter = buildInvoiceBillFilter({
|
|
104
|
+
reference: opts.ref, status: opts.status,
|
|
105
|
+
contactResourceId: opts.contact, tag: opts.tag,
|
|
106
|
+
minAmount: opts.minAmount, maxAmount: opts.maxAmount,
|
|
107
|
+
startDate: opts.from, endDate: opts.to,
|
|
108
|
+
dueDateFrom: opts.dueFrom, dueDateTo: opts.dueTo,
|
|
109
|
+
});
|
|
136
110
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
137
111
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchInvoices(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching invoices', defaultLimit: 20 });
|
|
138
112
|
outputList(result, INVOICE_COLUMNS, opts, 'Invoices'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -6,6 +6,7 @@ import { apiAction } from './api-action.js';
|
|
|
6
6
|
import { resolveAccountFlag } from './resolve.js';
|
|
7
7
|
import { parsePositiveInt, parseNonNegativeInt, parseJournalEntries, readBodyInput, requireFields } from './parsers.js';
|
|
8
8
|
import { paginatedFetch } from './pagination.js';
|
|
9
|
+
import { buildJournalFilter } from '../core/registry/pagination.js';
|
|
9
10
|
import { outputList } from './output.js';
|
|
10
11
|
import { JOURNAL_REQUIRED_FIELDS, buildDraftReport, formatDraftTable, addJournalDraftFinalizeOptions, mergeJournalDraftFlags, validateDraft, buildValidation, normalizeDate, sanitizeJournalEntry, } from './draft-helpers.js';
|
|
11
12
|
const JOURNAL_COLUMNS = [
|
|
@@ -41,7 +42,7 @@ export function registerJournalsCommand(program) {
|
|
|
41
42
|
.option('--ref <reference>', 'Filter by reference (contains)')
|
|
42
43
|
.option('--from <YYYY-MM-DD>', 'Filter from date (inclusive)')
|
|
43
44
|
.option('--to <YYYY-MM-DD>', 'Filter to date (inclusive)')
|
|
44
|
-
.option('--status <status>', 'Filter by status (DRAFT, FINALIZED)')
|
|
45
|
+
.option('--status <status>', 'Filter by status (DRAFT, FINALIZED, VOID)')
|
|
45
46
|
.option('--tag <name>', 'Filter by tag')
|
|
46
47
|
.option('--type <type>', 'Filter by type (JOURNAL_MANUAL, JOURNAL_DIRECT_CASH_IN, JOURNAL_DIRECT_CASH_OUT)')
|
|
47
48
|
.option('--sort <field>', 'Sort field (default: valueDate)')
|
|
@@ -54,24 +55,10 @@ export function registerJournalsCommand(program) {
|
|
|
54
55
|
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
55
56
|
.option('--json', 'Output as JSON')
|
|
56
57
|
.action(apiAction(async (client, opts) => {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
filter.status = { eq: opts.status };
|
|
62
|
-
if (opts.tag)
|
|
63
|
-
filter.tags = { eq: opts.tag };
|
|
64
|
-
if (opts.type)
|
|
65
|
-
filter.type = { eq: opts.type };
|
|
66
|
-
if (opts.from || opts.to) {
|
|
67
|
-
const dateFilter = {};
|
|
68
|
-
if (opts.from)
|
|
69
|
-
dateFilter.gte = opts.from;
|
|
70
|
-
if (opts.to)
|
|
71
|
-
dateFilter.lte = opts.to;
|
|
72
|
-
filter.valueDate = dateFilter;
|
|
73
|
-
}
|
|
74
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
58
|
+
const searchFilter = buildJournalFilter({
|
|
59
|
+
reference: opts.ref, status: opts.status, tag: opts.tag,
|
|
60
|
+
type: opts.type, startDate: opts.from, endDate: opts.to,
|
|
61
|
+
});
|
|
75
62
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
76
63
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchJournals(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching journals', defaultLimit: 20 });
|
|
77
64
|
outputList(result, JOURNAL_COLUMNS, opts, 'Journals'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
|
+
import { buildCnFilter } from '../core/registry/index.js';
|
|
3
4
|
import { listSupplierCreditNotes, getSupplierCreditNote, searchSupplierCreditNotes, createSupplierCreditNote, updateSupplierCreditNote, deleteSupplierCreditNote, createSupplierCreditNoteRefund, listSupplierCreditNoteRefunds, finalizeSupplierCreditNote, } from '../core/api/supplier-cn.js';
|
|
4
5
|
import { listAttachments } from '../core/api/attachments.js';
|
|
5
6
|
import { apiAction } from './api-action.js';
|
|
@@ -87,14 +88,7 @@ export function registerSupplierCreditNotesCommand(program) {
|
|
|
87
88
|
const resolved = await resolveContactFlag(client, opts.contact, { silent: opts.json });
|
|
88
89
|
opts.contact = resolved.resourceId;
|
|
89
90
|
}
|
|
90
|
-
const
|
|
91
|
-
if (opts.ref)
|
|
92
|
-
filter.reference = { contains: opts.ref };
|
|
93
|
-
if (opts.status)
|
|
94
|
-
filter.status = { eq: opts.status };
|
|
95
|
-
if (opts.contact)
|
|
96
|
-
filter.contactResourceId = { eq: opts.contact };
|
|
97
|
-
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
91
|
+
const searchFilter = buildCnFilter({ reference: opts.ref, status: opts.status, contactResourceId: opts.contact });
|
|
98
92
|
const sort = { sortBy: [opts.sort ?? 'valueDate'], order: (opts.order ?? 'DESC') };
|
|
99
93
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchSupplierCreditNotes(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching supplier credit notes', defaultLimit: 20 });
|
|
100
94
|
outputList(result, SCN_COLUMNS, opts, 'Supplier Credit Notes'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
@@ -24,18 +24,20 @@ export async function fetchAllPages(fetcher, options = {}) {
|
|
|
24
24
|
const pageSize = Math.min(Math.max(options.pageSize ?? DEFAULT_PAGE_SIZE, 1), 1000);
|
|
25
25
|
const rawConcurrency = options.concurrency ?? DEFAULT_CONCURRENCY;
|
|
26
26
|
const concurrency = Math.max(Math.floor(rawConcurrency), 1);
|
|
27
|
+
const maxItems = options.maxItems ?? Infinity;
|
|
27
28
|
const onProgress = options.onProgress;
|
|
28
29
|
// ── Step 1: First page ──
|
|
29
30
|
const firstPage = await fetcher(0, pageSize);
|
|
30
31
|
const total = firstPage.totalElements;
|
|
31
32
|
let requestCount = 1;
|
|
32
|
-
// Short-circuit: single page or
|
|
33
|
-
if (total <= pageSize) {
|
|
34
|
-
|
|
33
|
+
// Short-circuit: single page, empty, or maxItems satisfied by first page
|
|
34
|
+
if (total <= pageSize || firstPage.data.length >= maxItems) {
|
|
35
|
+
const data = maxItems < Infinity ? firstPage.data.slice(0, maxItems) : firstPage.data;
|
|
36
|
+
onProgress?.(data.length, total);
|
|
35
37
|
return {
|
|
36
|
-
data
|
|
38
|
+
data,
|
|
37
39
|
totalElements: total,
|
|
38
|
-
truncated:
|
|
40
|
+
truncated: data.length < total,
|
|
39
41
|
requestCount,
|
|
40
42
|
};
|
|
41
43
|
}
|
|
@@ -59,11 +61,14 @@ export async function fetchAllPages(fetcher, options = {}) {
|
|
|
59
61
|
allData.push(...page.data);
|
|
60
62
|
}
|
|
61
63
|
onProgress?.(allData.length, total);
|
|
64
|
+
if (allData.length >= maxItems)
|
|
65
|
+
break;
|
|
62
66
|
}
|
|
67
|
+
const cappedData = maxItems < Infinity ? allData.slice(0, maxItems) : allData;
|
|
63
68
|
return {
|
|
64
|
-
data:
|
|
69
|
+
data: cappedData,
|
|
65
70
|
totalElements: total,
|
|
66
|
-
truncated,
|
|
71
|
+
truncated: truncated || cappedData.length < allData.length,
|
|
67
72
|
requestCount,
|
|
68
73
|
};
|
|
69
74
|
}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
export { TOOL_DEFINITIONS } from './tools.js';
|
|
2
2
|
export { getToolByName, getAllTools, getToolsByGroup, getReadOnlyTools, getWriteTools, getToolCount, } from './lookup.js';
|
|
3
|
-
export {
|
|
3
|
+
export { handlePagination, buildCnFilter, buildBankRecordFilter, buildInvoiceBillFilter, buildJournalFilter, buildContactFilter, buildCashflowFilter } from './pagination.js';
|
|
@@ -1,45 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
*/
|
|
5
|
-
import { fetchAllPages } from '../api/pagination.js';
|
|
6
|
-
/**
|
|
7
|
-
* Handle pagination mode for list/search tools.
|
|
8
|
-
* DRY helper — all list/search tools route through this.
|
|
9
|
-
*
|
|
10
|
-
* Modes:
|
|
11
|
-
* - default (omit mode): caller sets limit/offset (offset = page number, 0-indexed)
|
|
12
|
-
* - 'sample': returns 20 items + totalElements metadata
|
|
13
|
-
* - 'all': fetches all pages concurrently via fetchAllPages
|
|
14
|
-
*/
|
|
15
|
-
export async function handlePaginationMode(mode, fetcher, limit, offset, defaultLimit) {
|
|
16
|
-
switch (mode) {
|
|
17
|
-
case 'all': {
|
|
18
|
-
const pageSize = Math.min(Math.max(limit ?? 200, 1), 1000);
|
|
19
|
-
const result = await fetchAllPages(fetcher, { pageSize, concurrency: 3 });
|
|
20
|
-
return {
|
|
21
|
-
_mode: 'all',
|
|
22
|
-
_note: result.truncated
|
|
23
|
-
? `Fetched ${result.data.length} of ${result.totalElements} items (offset cap: 65,536). ${result.requestCount} API calls.`
|
|
24
|
-
: `Fetched all ${result.totalElements} items. ${result.requestCount} API calls.`,
|
|
25
|
-
data: result.data,
|
|
26
|
-
totalElements: result.totalElements,
|
|
27
|
-
truncated: result.truncated,
|
|
28
|
-
};
|
|
29
|
-
}
|
|
30
|
-
case 'sample': {
|
|
31
|
-
const result = await fetcher(0, 20);
|
|
32
|
-
return {
|
|
33
|
-
_mode: 'sample',
|
|
34
|
-
_note: `Showing ${result.data.length} of ${result.totalElements} items. Use limit/offset to page, or mode 'all' to fetch everything.`,
|
|
35
|
-
data: result.data,
|
|
36
|
-
totalElements: result.totalElements,
|
|
37
|
-
totalPages: result.totalPages,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
default:
|
|
41
|
-
return fetcher(offset ?? 0, limit ?? defaultLimit);
|
|
42
|
-
}
|
|
1
|
+
/** Paginate a tool call with sensible defaults. */
|
|
2
|
+
export async function handlePagination(fetcher, limit, offset, defaultLimit) {
|
|
3
|
+
return fetcher(offset ?? 0, limit ?? defaultLimit);
|
|
43
4
|
}
|
|
44
5
|
/** Build a credit note search filter from agent input (shared by customer + supplier CN search). */
|
|
45
6
|
export function buildCnFilter(input) {
|
|
@@ -52,6 +13,106 @@ export function buildCnFilter(input) {
|
|
|
52
13
|
f.contactResourceId = { eq: input.contactResourceId };
|
|
53
14
|
return Object.keys(f).length > 0 ? f : undefined;
|
|
54
15
|
}
|
|
16
|
+
/** Build invoice/bill search filter (shared by CLI + tool + MCP). */
|
|
17
|
+
export function buildInvoiceBillFilter(input) {
|
|
18
|
+
const f = {};
|
|
19
|
+
if (input.reference)
|
|
20
|
+
f.reference = { contains: input.reference };
|
|
21
|
+
if (input.status)
|
|
22
|
+
f.status = { eq: input.status };
|
|
23
|
+
if (input.contactResourceId)
|
|
24
|
+
f.contactResourceId = { eq: input.contactResourceId };
|
|
25
|
+
if (input.contactName)
|
|
26
|
+
f.contact = { name: { contains: input.contactName } };
|
|
27
|
+
if (input.tag)
|
|
28
|
+
f.tags = { name: { eq: input.tag } };
|
|
29
|
+
if (input.currencyCode)
|
|
30
|
+
f.currencyCode = { eq: input.currencyCode };
|
|
31
|
+
const minAmt = typeof input.minAmount === 'number' && Number.isFinite(input.minAmount) ? input.minAmount : undefined;
|
|
32
|
+
const maxAmt = typeof input.maxAmount === 'number' && Number.isFinite(input.maxAmount) ? input.maxAmount : undefined;
|
|
33
|
+
if (minAmt != null || maxAmt != null) {
|
|
34
|
+
const a = {};
|
|
35
|
+
if (minAmt != null)
|
|
36
|
+
a.gte = minAmt;
|
|
37
|
+
if (maxAmt != null)
|
|
38
|
+
a.lte = maxAmt;
|
|
39
|
+
f.totalAmount = a;
|
|
40
|
+
}
|
|
41
|
+
if (input.startDate || input.endDate) {
|
|
42
|
+
const d = {};
|
|
43
|
+
if (input.startDate)
|
|
44
|
+
d.gte = input.startDate;
|
|
45
|
+
if (input.endDate)
|
|
46
|
+
d.lte = input.endDate;
|
|
47
|
+
f.valueDate = d;
|
|
48
|
+
}
|
|
49
|
+
if (input.dueDateFrom || input.dueDateTo) {
|
|
50
|
+
const d = {};
|
|
51
|
+
if (input.dueDateFrom)
|
|
52
|
+
d.gte = input.dueDateFrom;
|
|
53
|
+
if (input.dueDateTo)
|
|
54
|
+
d.lte = input.dueDateTo;
|
|
55
|
+
f.dueDate = d;
|
|
56
|
+
}
|
|
57
|
+
return Object.keys(f).length > 0 ? f : undefined;
|
|
58
|
+
}
|
|
59
|
+
/** Build journal search filter (shared by CLI + tool + MCP). */
|
|
60
|
+
export function buildJournalFilter(input) {
|
|
61
|
+
const f = {};
|
|
62
|
+
if (input.reference)
|
|
63
|
+
f.reference = { contains: input.reference };
|
|
64
|
+
if (input.status)
|
|
65
|
+
f.status = { eq: input.status };
|
|
66
|
+
if (input.tag)
|
|
67
|
+
f.tags = { name: { eq: input.tag } };
|
|
68
|
+
if (input.type)
|
|
69
|
+
f.type = { eq: input.type };
|
|
70
|
+
if (input.startDate || input.endDate) {
|
|
71
|
+
const d = {};
|
|
72
|
+
if (input.startDate)
|
|
73
|
+
d.gte = input.startDate;
|
|
74
|
+
if (input.endDate)
|
|
75
|
+
d.lte = input.endDate;
|
|
76
|
+
f.valueDate = d;
|
|
77
|
+
}
|
|
78
|
+
return Object.keys(f).length > 0 ? f : undefined;
|
|
79
|
+
}
|
|
80
|
+
/** Build contact search filter (shared by CLI + tool + MCP). */
|
|
81
|
+
export function buildContactFilter(input) {
|
|
82
|
+
const f = {};
|
|
83
|
+
if (input.query)
|
|
84
|
+
f.or = { billingName: { contains: input.query }, name: { contains: input.query } };
|
|
85
|
+
if (input.isCustomer !== undefined)
|
|
86
|
+
f.customer = { eq: input.isCustomer };
|
|
87
|
+
if (input.isSupplier !== undefined)
|
|
88
|
+
f.supplier = { eq: input.isSupplier };
|
|
89
|
+
if (input.status)
|
|
90
|
+
f.status = { eq: input.status };
|
|
91
|
+
if (input.email)
|
|
92
|
+
f.email = { contains: input.email };
|
|
93
|
+
return Object.keys(f).length > 0 ? f : undefined;
|
|
94
|
+
}
|
|
95
|
+
/** Build cashflow transaction search filter (shared by CLI + tool + MCP). */
|
|
96
|
+
export function buildCashflowFilter(input) {
|
|
97
|
+
const f = {};
|
|
98
|
+
if (input.businessTransactionType)
|
|
99
|
+
f.businessTransactionType = { eq: input.businessTransactionType };
|
|
100
|
+
if (input.direction)
|
|
101
|
+
f.direction = { eq: input.direction };
|
|
102
|
+
if (input.status)
|
|
103
|
+
f.businessTransactionStatus = { eq: input.status };
|
|
104
|
+
if (input.reference)
|
|
105
|
+
f.transactionReference = { contains: input.reference };
|
|
106
|
+
if (input.startDate || input.endDate) {
|
|
107
|
+
const d = {};
|
|
108
|
+
if (input.startDate)
|
|
109
|
+
d.gte = input.startDate;
|
|
110
|
+
if (input.endDate)
|
|
111
|
+
d.lte = input.endDate;
|
|
112
|
+
f.valueDate = d;
|
|
113
|
+
}
|
|
114
|
+
return Object.keys(f).length > 0 ? f : undefined;
|
|
115
|
+
}
|
|
55
116
|
/** Build a bank record search filter (shared by CLI + MCP tool). */
|
|
56
117
|
export function buildBankRecordFilter(input) {
|
|
57
118
|
const f = {};
|
|
@@ -29,7 +29,7 @@ import { downloadExport } from '../api/data-exports.js';
|
|
|
29
29
|
import { listAttachments, addAttachment, fetchAttachmentTable } from '../api/attachments.js';
|
|
30
30
|
import { createFromAttachment, searchMagicWorkflows } from '../api/magic.js';
|
|
31
31
|
import { messageToPdf } from '../api/message-pdf.js';
|
|
32
|
-
import {
|
|
32
|
+
import { handlePagination, buildCnFilter, buildBankRecordFilter, buildInvoiceBillFilter, buildJournalFilter, buildContactFilter, buildCashflowFilter } from './pagination.js';
|
|
33
33
|
// New API modules (200-tools expansion)
|
|
34
34
|
import { listBankRules, getBankRule, searchBankRules, createBankRule, updateBankRule, deleteBankRule, } from '../api/bank-rules.js';
|
|
35
35
|
import { listFixedAssets, getFixedAsset, searchFixedAssets, createFixedAsset, updateFixedAsset, deleteFixedAsset, discardFixedAsset, markFixedAssetSold, transferFixedAsset, undoFixedAssetDisposal, } from '../api/fixed-assets.js';
|
|
@@ -55,13 +55,12 @@ import { generateStatutoryFilingBlueprint } from '../jobs/statutory-filing/bluep
|
|
|
55
55
|
import { buildDraftReport, INVOICE_REQUIRED_FIELDS, BILL_REQUIRED_FIELDS, CREDIT_NOTE_REQUIRED_FIELDS, JOURNAL_REQUIRED_FIELDS, } from '../drafts/index.js';
|
|
56
56
|
// ── Shared param snippets (DRY) ─────────────────────────────────
|
|
57
57
|
const PAGINATION_PARAMS = {
|
|
58
|
-
limit: { type: 'number', description: 'Max results per page (default
|
|
59
|
-
offset: { type: 'number', description: '
|
|
60
|
-
mode: { type: 'string', enum: ['sample', 'all'], description: "Pagination mode: 'sample' (20 items + total count), 'all' (fetch everything — may be slow for large datasets). Omit for default limit/offset behavior." },
|
|
58
|
+
limit: { type: 'number', description: 'Max results per page (default 20, max 1000).' },
|
|
59
|
+
offset: { type: 'number', description: 'Page offset (0-indexed). Use with limit to paginate.' },
|
|
61
60
|
};
|
|
62
61
|
const SEARCH_PARAMS = {
|
|
63
62
|
...PAGINATION_PARAMS,
|
|
64
|
-
sortBy: { type: 'string', description: 'Sort field (
|
|
63
|
+
sortBy: { type: 'string', description: 'Sort field (always sent with queries). Common: valueDate, reference, status, name. Default: valueDate' },
|
|
65
64
|
order: { type: 'string', enum: ['ASC', 'DESC'], description: 'Sort direction' },
|
|
66
65
|
};
|
|
67
66
|
const CURRENCY_PARAM = {
|
|
@@ -122,13 +121,12 @@ const LINE_ITEM_PARAM = {
|
|
|
122
121
|
};
|
|
123
122
|
// ── Helper to extract common input fields ────────────────────────
|
|
124
123
|
function extractPaginationInput(input) {
|
|
125
|
-
const mode = input.mode;
|
|
126
124
|
const limit = input.limit;
|
|
127
125
|
const offset = input.offset;
|
|
128
126
|
const sortBy = input.sortBy;
|
|
129
127
|
const rawOrder = input.order;
|
|
130
128
|
const sortOrder = rawOrder === 'ASC' || rawOrder === 'DESC' ? rawOrder : undefined;
|
|
131
|
-
return {
|
|
129
|
+
return { limit, offset, sortBy, sortOrder };
|
|
132
130
|
}
|
|
133
131
|
// ── List tool factory (DRY — all 10 list tools share this) ───────
|
|
134
132
|
function listTool(name, description, group, fetcher) {
|
|
@@ -140,8 +138,8 @@ function listTool(name, description, group, fetcher) {
|
|
|
140
138
|
group,
|
|
141
139
|
readOnly: true,
|
|
142
140
|
execute: async (ctx, input) => {
|
|
143
|
-
const {
|
|
144
|
-
return
|
|
141
|
+
const { limit, offset } = extractPaginationInput(input);
|
|
142
|
+
return handlePagination((off, lim) => fetcher(ctx.client, off, lim), limit, offset, 20);
|
|
145
143
|
},
|
|
146
144
|
};
|
|
147
145
|
}
|
|
@@ -197,13 +195,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
197
195
|
group: 'accounts',
|
|
198
196
|
readOnly: true,
|
|
199
197
|
execute: async (ctx, input) => {
|
|
200
|
-
const {
|
|
198
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
201
199
|
const query = input.query;
|
|
202
|
-
return
|
|
200
|
+
return handlePagination((off, lim) => searchAccounts(ctx.client, {
|
|
203
201
|
filter: { or: { name: { contains: query }, code: { contains: query } } },
|
|
204
202
|
limit: lim, offset: off,
|
|
205
203
|
sort: { sortBy: [sortBy ?? 'code'], order: (sortOrder ?? 'ASC') },
|
|
206
|
-
}), limit, offset,
|
|
204
|
+
}), limit, offset, 20);
|
|
207
205
|
},
|
|
208
206
|
},
|
|
209
207
|
{
|
|
@@ -263,22 +261,25 @@ export const TOOL_DEFINITIONS = [
|
|
|
263
261
|
listTool('list_contacts', 'List contacts (customers/suppliers). Returns billingName, name, emails, status. Paginated — response includes totalElements. Use limit/offset to page.', 'contacts', (client, off, lim) => listContacts(client, { limit: lim, offset: off })),
|
|
264
262
|
{
|
|
265
263
|
name: 'search_contacts',
|
|
266
|
-
description: 'Search contacts
|
|
264
|
+
description: 'Search contacts with filters. Searches both billingName and name fields when query is provided.',
|
|
267
265
|
params: {
|
|
268
|
-
query: { type: 'string', description: 'Search
|
|
266
|
+
query: { type: 'string', description: 'Search by name or billing name' },
|
|
267
|
+
isCustomer: { type: 'boolean', description: 'Filter to customers only' },
|
|
268
|
+
isSupplier: { type: 'boolean', description: 'Filter to suppliers only' },
|
|
269
|
+
status: { type: 'string', enum: ['ACTIVE', 'INACTIVE'], description: 'Filter by status' },
|
|
270
|
+
email: { type: 'string', description: 'Filter by email (contains)' },
|
|
269
271
|
...SEARCH_PARAMS,
|
|
270
272
|
},
|
|
271
|
-
required: [
|
|
273
|
+
required: [],
|
|
272
274
|
group: 'contacts',
|
|
273
275
|
readOnly: true,
|
|
274
276
|
execute: async (ctx, input) => {
|
|
275
|
-
const {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
filter: { or: { billingName: { contains: query }, name: { contains: query } } },
|
|
277
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
278
|
+
return handlePagination((off, lim) => searchContacts(ctx.client, {
|
|
279
|
+
filter: buildContactFilter(input),
|
|
279
280
|
limit: lim, offset: off,
|
|
280
281
|
sort: { sortBy: [sortBy ?? 'billingName'], order: (sortOrder ?? 'ASC') },
|
|
281
|
-
}), limit, offset,
|
|
282
|
+
}), limit, offset, 20);
|
|
282
283
|
},
|
|
283
284
|
},
|
|
284
285
|
{
|
|
@@ -334,42 +335,32 @@ export const TOOL_DEFINITIONS = [
|
|
|
334
335
|
listTool('list_invoices', 'List invoices. Returns reference, date, status, contact, totalAmount. Paginated — response includes totalElements. Use limit/offset to page.', 'invoices', (client, off, lim) => listInvoices(client, { limit: lim, offset: off })),
|
|
335
336
|
{
|
|
336
337
|
name: 'search_invoices',
|
|
337
|
-
description: 'Search invoices
|
|
338
|
+
description: 'Search invoices with filters. Provide any combination: reference, contact, status, date range, due date range, amount range, tag, currency.',
|
|
338
339
|
params: {
|
|
339
340
|
query: { type: 'string', description: 'Search by invoice reference number' },
|
|
340
341
|
contactName: { type: 'string', description: 'Search by contact name' },
|
|
342
|
+
status: { type: 'string', enum: ['DRAFT', 'UNPAID', 'PARTIALLY_PAID', 'PAID', 'OVERDUE', 'VOID'], description: 'Filter by status' },
|
|
343
|
+
tag: { type: 'string', description: 'Filter by tag name' },
|
|
341
344
|
startDate: { type: 'string', description: 'Filter invoices on or after this date (YYYY-MM-DD)' },
|
|
342
345
|
endDate: { type: 'string', description: 'Filter invoices on or before this date (YYYY-MM-DD)' },
|
|
346
|
+
dueDateFrom: { type: 'string', description: 'Filter by due date from (YYYY-MM-DD)' },
|
|
347
|
+
dueDateTo: { type: 'string', description: 'Filter by due date to (YYYY-MM-DD)' },
|
|
348
|
+
minAmount: { type: 'number', description: 'Minimum total amount' },
|
|
349
|
+
maxAmount: { type: 'number', description: 'Maximum total amount' },
|
|
350
|
+
currencyCode: { type: 'string', description: 'Filter by currency code (e.g. USD, SGD)' },
|
|
343
351
|
...SEARCH_PARAMS,
|
|
344
352
|
},
|
|
345
353
|
required: [],
|
|
346
354
|
group: 'invoices',
|
|
347
355
|
readOnly: true,
|
|
348
356
|
execute: async (ctx, input) => {
|
|
349
|
-
const {
|
|
350
|
-
const
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
const endDate = input.endDate;
|
|
354
|
-
// Build filter — avoid nested `contact` inside `or` (causes 400)
|
|
355
|
-
const filter = {};
|
|
356
|
-
if (query)
|
|
357
|
-
filter.reference = { contains: query };
|
|
358
|
-
if (contactName)
|
|
359
|
-
filter.contact = { name: { contains: contactName } };
|
|
360
|
-
if (startDate || endDate) {
|
|
361
|
-
const dateFilter = {};
|
|
362
|
-
if (startDate)
|
|
363
|
-
dateFilter.gte = startDate;
|
|
364
|
-
if (endDate)
|
|
365
|
-
dateFilter.lte = endDate;
|
|
366
|
-
filter.valueDate = dateFilter;
|
|
367
|
-
}
|
|
368
|
-
return handlePaginationMode(mode, (off, lim) => searchInvoices(ctx.client, {
|
|
369
|
-
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
357
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
358
|
+
const filterInput = { ...input, reference: input.reference ?? input.query };
|
|
359
|
+
return handlePagination((off, lim) => searchInvoices(ctx.client, {
|
|
360
|
+
filter: buildInvoiceBillFilter(filterInput),
|
|
370
361
|
limit: lim, offset: off,
|
|
371
362
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
372
|
-
}), limit, offset,
|
|
363
|
+
}), limit, offset, 20);
|
|
373
364
|
},
|
|
374
365
|
},
|
|
375
366
|
{
|
|
@@ -493,6 +484,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
493
484
|
readOnly: false,
|
|
494
485
|
execute: async (ctx, input) => {
|
|
495
486
|
const { resourceId: rid, ...data } = input;
|
|
487
|
+
// API requires lineItems on PUT even for status-only changes — fetch if not provided
|
|
488
|
+
if (!data.lineItems) {
|
|
489
|
+
const current = await getInvoice(ctx.client, rid);
|
|
490
|
+
data.lineItems = current.lineItems;
|
|
491
|
+
}
|
|
496
492
|
return finalizeInvoice(ctx.client, rid, data);
|
|
497
493
|
},
|
|
498
494
|
},
|
|
@@ -534,41 +530,32 @@ export const TOOL_DEFINITIONS = [
|
|
|
534
530
|
listTool('list_bills', 'List bills (purchase invoices). Returns reference, date, status, contact, amounts. Paginated — response includes totalElements. Use limit/offset to page.', 'bills', (client, off, lim) => listBills(client, { limit: lim, offset: off })),
|
|
535
531
|
{
|
|
536
532
|
name: 'search_bills',
|
|
537
|
-
description: 'Search bills
|
|
533
|
+
description: 'Search bills with filters. Provide any combination: reference, contact, status, date range, due date range, amount range, tag, currency.',
|
|
538
534
|
params: {
|
|
539
535
|
query: { type: 'string', description: 'Search by bill reference number' },
|
|
540
536
|
contactName: { type: 'string', description: 'Search by contact name' },
|
|
537
|
+
status: { type: 'string', enum: ['DRAFT', 'UNPAID', 'PARTIALLY_PAID', 'PAID', 'OVERDUE', 'VOID'], description: 'Filter by status' },
|
|
538
|
+
tag: { type: 'string', description: 'Filter by tag name' },
|
|
541
539
|
startDate: { type: 'string', description: 'Filter bills on or after this date (YYYY-MM-DD)' },
|
|
542
540
|
endDate: { type: 'string', description: 'Filter bills on or before this date (YYYY-MM-DD)' },
|
|
541
|
+
dueDateFrom: { type: 'string', description: 'Filter by due date from (YYYY-MM-DD)' },
|
|
542
|
+
dueDateTo: { type: 'string', description: 'Filter by due date to (YYYY-MM-DD)' },
|
|
543
|
+
minAmount: { type: 'number', description: 'Minimum total amount' },
|
|
544
|
+
maxAmount: { type: 'number', description: 'Maximum total amount' },
|
|
545
|
+
currencyCode: { type: 'string', description: 'Filter by currency code (e.g. USD, SGD)' },
|
|
543
546
|
...SEARCH_PARAMS,
|
|
544
547
|
},
|
|
545
548
|
required: [],
|
|
546
549
|
group: 'bills',
|
|
547
550
|
readOnly: true,
|
|
548
551
|
execute: async (ctx, input) => {
|
|
549
|
-
const {
|
|
550
|
-
const
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
const endDate = input.endDate;
|
|
554
|
-
const filter = {};
|
|
555
|
-
if (query)
|
|
556
|
-
filter.reference = { contains: query };
|
|
557
|
-
if (contactName)
|
|
558
|
-
filter.contact = { name: { contains: contactName } };
|
|
559
|
-
if (startDate || endDate) {
|
|
560
|
-
const dateFilter = {};
|
|
561
|
-
if (startDate)
|
|
562
|
-
dateFilter.gte = startDate;
|
|
563
|
-
if (endDate)
|
|
564
|
-
dateFilter.lte = endDate;
|
|
565
|
-
filter.valueDate = dateFilter;
|
|
566
|
-
}
|
|
567
|
-
return handlePaginationMode(mode, (off, lim) => searchBills(ctx.client, {
|
|
568
|
-
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
552
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
553
|
+
const filterInput = { ...input, reference: input.reference ?? input.query };
|
|
554
|
+
return handlePagination((off, lim) => searchBills(ctx.client, {
|
|
555
|
+
filter: buildInvoiceBillFilter(filterInput),
|
|
569
556
|
limit: lim, offset: off,
|
|
570
557
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
571
|
-
}), limit, offset,
|
|
558
|
+
}), limit, offset, 20);
|
|
572
559
|
},
|
|
573
560
|
},
|
|
574
561
|
{
|
|
@@ -644,7 +631,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
644
631
|
},
|
|
645
632
|
{
|
|
646
633
|
name: 'pay_bill',
|
|
647
|
-
description: 'Record a payment against a bill.',
|
|
634
|
+
description: 'Record a payment against a bill. Bill must be APPROVED before payment — draft or voided bills cannot be paid.',
|
|
648
635
|
params: {
|
|
649
636
|
resourceId: { type: 'string' },
|
|
650
637
|
paymentAmount: { type: 'number' },
|
|
@@ -695,6 +682,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
695
682
|
readOnly: false,
|
|
696
683
|
execute: async (ctx, input) => {
|
|
697
684
|
const { resourceId: rid, ...data } = input;
|
|
685
|
+
// API requires lineItems on PUT even for status-only changes — fetch if not provided
|
|
686
|
+
if (!data.lineItems) {
|
|
687
|
+
const current = await getBill(ctx.client, rid);
|
|
688
|
+
data.lineItems = current.lineItems;
|
|
689
|
+
}
|
|
698
690
|
return finalizeBill(ctx.client, rid, data);
|
|
699
691
|
},
|
|
700
692
|
},
|
|
@@ -725,22 +717,27 @@ export const TOOL_DEFINITIONS = [
|
|
|
725
717
|
listTool('list_journals', 'List journal entries. Paginated — response includes totalElements. Use limit/offset to page.', 'journals', (client, off, lim) => listJournals(client, { limit: lim, offset: off })),
|
|
726
718
|
{
|
|
727
719
|
name: 'search_journals',
|
|
728
|
-
description: 'Search journals
|
|
729
|
-
params: {
|
|
730
|
-
query: { type: 'string' },
|
|
720
|
+
description: 'Search journals with filters. Provide any combination: reference, status, tag, type, date range.',
|
|
721
|
+
params: {
|
|
722
|
+
query: { type: 'string', description: 'Search by reference (contains)' },
|
|
723
|
+
status: { type: 'string', enum: ['DRAFT', 'FINALIZED', 'VOID'], description: 'Filter by status' },
|
|
724
|
+
tag: { type: 'string', description: 'Filter by tag name' },
|
|
725
|
+
type: { type: 'string', description: 'Filter by type (e.g. JOURNAL_MANUAL, JOURNAL_DIRECT_CASH_IN, JOURNAL_DIRECT_CASH_OUT)' },
|
|
726
|
+
startDate: { type: 'string', description: 'Filter journals on or after this date (YYYY-MM-DD)' },
|
|
727
|
+
endDate: { type: 'string', description: 'Filter journals on or before this date (YYYY-MM-DD)' },
|
|
731
728
|
...SEARCH_PARAMS,
|
|
732
729
|
},
|
|
733
|
-
required: [
|
|
730
|
+
required: [],
|
|
734
731
|
group: 'journals',
|
|
735
732
|
readOnly: true,
|
|
736
733
|
execute: async (ctx, input) => {
|
|
737
|
-
const {
|
|
738
|
-
const
|
|
739
|
-
return
|
|
740
|
-
filter:
|
|
734
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
735
|
+
const filterInput = { ...input, reference: input.reference ?? input.query };
|
|
736
|
+
return handlePagination((off, lim) => searchJournals(ctx.client, {
|
|
737
|
+
filter: buildJournalFilter(filterInput),
|
|
741
738
|
limit: lim, offset: off,
|
|
742
739
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
743
|
-
}), limit, offset,
|
|
740
|
+
}), limit, offset, 20);
|
|
744
741
|
},
|
|
745
742
|
},
|
|
746
743
|
{
|
|
@@ -802,13 +799,16 @@ export const TOOL_DEFINITIONS = [
|
|
|
802
799
|
name: 'generate_balance_sheet',
|
|
803
800
|
description: 'Generate a balance sheet report.',
|
|
804
801
|
params: {
|
|
805
|
-
snapshotDate: { type: 'string', description: 'Snapshot date (YYYY-MM-DD)' },
|
|
802
|
+
snapshotDate: { type: 'string', description: 'Snapshot date (YYYY-MM-DD). Defaults to today if omitted.' },
|
|
806
803
|
currencyCode: { type: 'string' },
|
|
807
804
|
},
|
|
808
|
-
required: [
|
|
805
|
+
required: [],
|
|
809
806
|
group: 'reports',
|
|
810
807
|
readOnly: true,
|
|
811
|
-
execute: async (ctx, input) =>
|
|
808
|
+
execute: async (ctx, input) => {
|
|
809
|
+
const date = input.snapshotDate ?? new Date().toISOString().slice(0, 10);
|
|
810
|
+
return generateBalanceSheet(ctx.client, { primarySnapshotDate: date, currencyCode: input.currencyCode });
|
|
811
|
+
},
|
|
812
812
|
},
|
|
813
813
|
{
|
|
814
814
|
name: 'generate_profit_and_loss',
|
|
@@ -913,7 +913,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
913
913
|
group: 'items',
|
|
914
914
|
readOnly: true,
|
|
915
915
|
execute: async (ctx, input) => {
|
|
916
|
-
const {
|
|
916
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
917
917
|
const filter = {};
|
|
918
918
|
if (input.appliesToSale !== undefined)
|
|
919
919
|
filter.appliesToSale = { eq: input.appliesToSale };
|
|
@@ -923,11 +923,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
923
923
|
filter.status = { eq: input.status };
|
|
924
924
|
if (input.itemCategory)
|
|
925
925
|
filter.itemCategory = { eq: input.itemCategory };
|
|
926
|
-
return
|
|
926
|
+
return handlePagination((off, lim) => searchItems(ctx.client, {
|
|
927
927
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
928
928
|
limit: lim, offset: off,
|
|
929
929
|
sort: { sortBy: [sortBy ?? 'internalName'], order: (sortOrder ?? 'ASC') },
|
|
930
|
-
}), limit, offset,
|
|
930
|
+
}), limit, offset, 20);
|
|
931
931
|
},
|
|
932
932
|
},
|
|
933
933
|
{
|
|
@@ -1006,13 +1006,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
1006
1006
|
group: 'tags',
|
|
1007
1007
|
readOnly: true,
|
|
1008
1008
|
execute: async (ctx, input) => {
|
|
1009
|
-
const {
|
|
1009
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1010
1010
|
const query = input.query;
|
|
1011
|
-
return
|
|
1011
|
+
return handlePagination((off, lim) => searchTags(ctx.client, {
|
|
1012
1012
|
filter: { tagName: { contains: query } },
|
|
1013
1013
|
limit: lim, offset: off,
|
|
1014
1014
|
sort: { sortBy: [sortBy ?? 'tagName'], order: (sortOrder ?? 'ASC') },
|
|
1015
|
-
}), limit, offset,
|
|
1015
|
+
}), limit, offset, 20);
|
|
1016
1016
|
},
|
|
1017
1017
|
},
|
|
1018
1018
|
{
|
|
@@ -1059,13 +1059,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
1059
1059
|
group: 'capsules',
|
|
1060
1060
|
readOnly: true,
|
|
1061
1061
|
execute: async (ctx, input) => {
|
|
1062
|
-
const {
|
|
1062
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1063
1063
|
const query = input.query;
|
|
1064
|
-
return
|
|
1064
|
+
return handlePagination((off, lim) => searchCapsules(ctx.client, {
|
|
1065
1065
|
filter: { title: { contains: query } },
|
|
1066
1066
|
limit: lim, offset: off,
|
|
1067
1067
|
sort: { sortBy: [sortBy ?? 'title'], order: (sortOrder ?? 'ASC') },
|
|
1068
|
-
}), limit, offset,
|
|
1068
|
+
}), limit, offset, 20);
|
|
1069
1069
|
},
|
|
1070
1070
|
},
|
|
1071
1071
|
{
|
|
@@ -1138,12 +1138,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1138
1138
|
group: 'customer_credit_notes',
|
|
1139
1139
|
readOnly: true,
|
|
1140
1140
|
execute: async (ctx, input) => {
|
|
1141
|
-
const {
|
|
1142
|
-
return
|
|
1141
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1142
|
+
return handlePagination((off, lim) => searchCustomerCreditNotes(ctx.client, {
|
|
1143
1143
|
filter: buildCnFilter(input),
|
|
1144
1144
|
limit: lim, offset: off,
|
|
1145
1145
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
1146
|
-
}), limit, offset,
|
|
1146
|
+
}), limit, offset, 20);
|
|
1147
1147
|
},
|
|
1148
1148
|
},
|
|
1149
1149
|
{
|
|
@@ -1307,12 +1307,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1307
1307
|
group: 'supplier_credit_notes',
|
|
1308
1308
|
readOnly: true,
|
|
1309
1309
|
execute: async (ctx, input) => {
|
|
1310
|
-
const {
|
|
1311
|
-
return
|
|
1310
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1311
|
+
return handlePagination((off, lim) => searchSupplierCreditNotes(ctx.client, {
|
|
1312
1312
|
filter: buildCnFilter(input),
|
|
1313
1313
|
limit: lim, offset: off,
|
|
1314
1314
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
1315
|
-
}), limit, offset,
|
|
1315
|
+
}), limit, offset, 20);
|
|
1316
1316
|
},
|
|
1317
1317
|
},
|
|
1318
1318
|
{
|
|
@@ -1493,9 +1493,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
1493
1493
|
group: 'currencies',
|
|
1494
1494
|
readOnly: true,
|
|
1495
1495
|
execute: async (ctx, input) => {
|
|
1496
|
-
const {
|
|
1496
|
+
const { limit, offset } = extractPaginationInput(input);
|
|
1497
1497
|
const cc = input.currencyCode;
|
|
1498
|
-
return
|
|
1498
|
+
return handlePagination((off, lim) => listCurrencyRates(ctx.client, cc, { limit: lim, offset: off }), limit, offset, 100);
|
|
1499
1499
|
},
|
|
1500
1500
|
},
|
|
1501
1501
|
{
|
|
@@ -1815,12 +1815,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1815
1815
|
group: 'bank',
|
|
1816
1816
|
readOnly: true,
|
|
1817
1817
|
execute: async (ctx, input) => {
|
|
1818
|
-
const {
|
|
1819
|
-
return
|
|
1818
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1819
|
+
return handlePagination((off, lim) => searchBankRecords(ctx.client, input.accountResourceId, {
|
|
1820
1820
|
filter: buildBankRecordFilter(input),
|
|
1821
1821
|
limit: lim, offset: off,
|
|
1822
1822
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
1823
|
-
}), limit, offset,
|
|
1823
|
+
}), limit, offset, 20);
|
|
1824
1824
|
},
|
|
1825
1825
|
},
|
|
1826
1826
|
// ── Cashflow Transactions ──────────────────────────────────────
|
|
@@ -1828,25 +1828,24 @@ export const TOOL_DEFINITIONS = [
|
|
|
1828
1828
|
name: 'search_cashflow_transactions',
|
|
1829
1829
|
description: 'Search cashflow transactions (unified ledger: invoices, bills, credit notes, journals, payments). Useful for reconciliation.',
|
|
1830
1830
|
params: {
|
|
1831
|
-
...SEARCH_PARAMS,
|
|
1832
1831
|
businessTransactionType: { type: 'string', description: 'Filter by type (e.g., INVOICE, BILL, JOURNAL)' },
|
|
1833
1832
|
direction: { type: 'string', enum: ['IN', 'OUT'], description: 'Filter by direction' },
|
|
1833
|
+
startDate: { type: 'string', description: 'Filter from date (YYYY-MM-DD)' },
|
|
1834
|
+
endDate: { type: 'string', description: 'Filter to date (YYYY-MM-DD)' },
|
|
1835
|
+
status: { type: 'string', description: 'Filter by transaction status' },
|
|
1836
|
+
reference: { type: 'string', description: 'Filter by reference (contains)' },
|
|
1837
|
+
...SEARCH_PARAMS,
|
|
1834
1838
|
},
|
|
1835
1839
|
required: [],
|
|
1836
1840
|
group: 'cashflow',
|
|
1837
1841
|
readOnly: true,
|
|
1838
1842
|
execute: async (ctx, input) => {
|
|
1839
|
-
const {
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
filter.businessTransactionType = { eq: input.businessTransactionType };
|
|
1843
|
-
if (input.direction)
|
|
1844
|
-
filter.direction = { eq: input.direction };
|
|
1845
|
-
return handlePaginationMode(mode, (off, lim) => searchCashflowTransactions(ctx.client, {
|
|
1846
|
-
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
1843
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1844
|
+
return handlePagination((off, lim) => searchCashflowTransactions(ctx.client, {
|
|
1845
|
+
filter: buildCashflowFilter(input),
|
|
1847
1846
|
limit: lim, offset: off,
|
|
1848
1847
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
1849
|
-
}), limit, offset,
|
|
1848
|
+
}), limit, offset, 20);
|
|
1850
1849
|
},
|
|
1851
1850
|
},
|
|
1852
1851
|
// ── Bookmarks ──────────────────────────────────────────────────
|
|
@@ -1916,13 +1915,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
1916
1915
|
group: 'org_users',
|
|
1917
1916
|
readOnly: true,
|
|
1918
1917
|
execute: async (ctx, input) => {
|
|
1919
|
-
const {
|
|
1918
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
1920
1919
|
const query = input.query;
|
|
1921
|
-
return
|
|
1920
|
+
return handlePagination((off, lim) => searchOrgUsers(ctx.client, {
|
|
1922
1921
|
filter: { or: { firstName: { contains: query }, lastName: { contains: query }, email: { contains: query } } },
|
|
1923
1922
|
limit: lim, offset: off,
|
|
1924
1923
|
sort: { sortBy: [sortBy ?? 'firstName'], order: (sortOrder ?? 'ASC') },
|
|
1925
|
-
}), limit, offset,
|
|
1924
|
+
}), limit, offset, 20);
|
|
1926
1925
|
},
|
|
1927
1926
|
},
|
|
1928
1927
|
{
|
|
@@ -2017,7 +2016,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
2017
2016
|
group: 'payments',
|
|
2018
2017
|
readOnly: true,
|
|
2019
2018
|
execute: async (ctx, input) => {
|
|
2020
|
-
const {
|
|
2019
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
2021
2020
|
const filter = {};
|
|
2022
2021
|
if (input.businessTransactionType)
|
|
2023
2022
|
filter.businessTransactionType = { eq: input.businessTransactionType };
|
|
@@ -2035,11 +2034,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
2035
2034
|
df.lte = input.toDate;
|
|
2036
2035
|
filter.valueDate = df;
|
|
2037
2036
|
}
|
|
2038
|
-
return
|
|
2037
|
+
return handlePagination((off, lim) => searchPayments(ctx.client, {
|
|
2039
2038
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
2040
2039
|
limit: lim, offset: off,
|
|
2041
2040
|
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
2042
|
-
}), limit, offset,
|
|
2041
|
+
}), limit, offset, 20);
|
|
2043
2042
|
},
|
|
2044
2043
|
},
|
|
2045
2044
|
// ── Data Exports ───────────────────────────────────────────────
|
|
@@ -2482,17 +2481,17 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2482
2481
|
group: 'magic',
|
|
2483
2482
|
readOnly: true,
|
|
2484
2483
|
execute: async (ctx, input) => {
|
|
2485
|
-
const {
|
|
2484
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
2486
2485
|
const filter = {};
|
|
2487
2486
|
if (input.status)
|
|
2488
2487
|
filter.status = input.status;
|
|
2489
2488
|
if (input.documentType)
|
|
2490
2489
|
filter.documentType = input.documentType;
|
|
2491
|
-
return
|
|
2490
|
+
return handlePagination((off, lim) => searchMagicWorkflows(ctx.client, {
|
|
2492
2491
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
2493
2492
|
limit: lim, offset: off,
|
|
2494
2493
|
sort: { sortBy: [sortBy ?? 'createdAt'], order: (sortOrder ?? 'DESC') },
|
|
2495
|
-
}), limit, offset,
|
|
2494
|
+
}), limit, offset, 20);
|
|
2496
2495
|
},
|
|
2497
2496
|
},
|
|
2498
2497
|
{
|
|
@@ -2878,13 +2877,13 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2878
2877
|
group: 'bank_rules',
|
|
2879
2878
|
readOnly: true,
|
|
2880
2879
|
execute: async (ctx, input) => {
|
|
2881
|
-
const {
|
|
2880
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
2882
2881
|
const query = input.query;
|
|
2883
|
-
return
|
|
2882
|
+
return handlePagination((off, lim) => searchBankRules(ctx.client, {
|
|
2884
2883
|
filter: { name: { contains: query } },
|
|
2885
2884
|
limit: lim, offset: off,
|
|
2886
|
-
sort:
|
|
2887
|
-
}), limit, offset,
|
|
2885
|
+
sort: { sortBy: [sortBy ?? 'name'], order: (sortOrder ?? 'ASC') },
|
|
2886
|
+
}), limit, offset, 20);
|
|
2888
2887
|
},
|
|
2889
2888
|
},
|
|
2890
2889
|
{
|
|
@@ -2981,13 +2980,14 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2981
2980
|
query: { type: 'string', description: 'Search term (name or reference)' },
|
|
2982
2981
|
status: { type: 'string', enum: ['ONGOING', 'COMPLETED', 'DISPOSED', 'DRAFT'], description: 'Filter by status' },
|
|
2983
2982
|
category: { type: 'string', enum: ['TANGIBLE', 'INTANGIBLE'], description: 'Filter by category' },
|
|
2983
|
+
tag: { type: 'string', description: 'Filter by tag name' },
|
|
2984
2984
|
...SEARCH_PARAMS,
|
|
2985
2985
|
},
|
|
2986
2986
|
required: [],
|
|
2987
2987
|
group: 'fixed_assets',
|
|
2988
2988
|
readOnly: true,
|
|
2989
2989
|
execute: async (ctx, input) => {
|
|
2990
|
-
const {
|
|
2990
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
2991
2991
|
const filter = {};
|
|
2992
2992
|
if (input.query)
|
|
2993
2993
|
filter.name = { contains: input.query };
|
|
@@ -2995,11 +2995,13 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2995
2995
|
filter.status = { eq: input.status };
|
|
2996
2996
|
if (input.category)
|
|
2997
2997
|
filter.category = { eq: input.category };
|
|
2998
|
-
|
|
2998
|
+
if (input.tag)
|
|
2999
|
+
filter.tags = { name: { eq: input.tag } };
|
|
3000
|
+
return handlePagination((off, lim) => searchFixedAssets(ctx.client, {
|
|
2999
3001
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
3000
3002
|
limit: lim, offset: off,
|
|
3001
|
-
sort:
|
|
3002
|
-
}), limit, offset,
|
|
3003
|
+
sort: { sortBy: [sortBy ?? 'name'], order: (sortOrder ?? 'DESC') },
|
|
3004
|
+
}), limit, offset, 20);
|
|
3003
3005
|
},
|
|
3004
3006
|
},
|
|
3005
3007
|
{
|
|
@@ -3172,17 +3174,17 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3172
3174
|
group: 'subscriptions',
|
|
3173
3175
|
readOnly: true,
|
|
3174
3176
|
execute: async (ctx, input) => {
|
|
3175
|
-
const {
|
|
3177
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
3176
3178
|
const filter = {};
|
|
3177
3179
|
if (input.businessTransactionType)
|
|
3178
3180
|
filter.businessTransactionType = { eq: input.businessTransactionType };
|
|
3179
3181
|
if (input.status)
|
|
3180
3182
|
filter.status = { eq: input.status };
|
|
3181
|
-
return
|
|
3183
|
+
return handlePagination((off, lim) => searchScheduledTransactions(ctx.client, {
|
|
3182
3184
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
3183
3185
|
limit: lim, offset: off,
|
|
3184
|
-
sort:
|
|
3185
|
-
}), limit, offset,
|
|
3186
|
+
sort: { sortBy: [sortBy ?? 'valueDate'], order: (sortOrder ?? 'DESC') },
|
|
3187
|
+
}), limit, offset, 20);
|
|
3186
3188
|
},
|
|
3187
3189
|
},
|
|
3188
3190
|
// ══════════════════════════════════════════════════════════════
|
|
@@ -3318,13 +3320,13 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3318
3320
|
group: 'contact_groups',
|
|
3319
3321
|
readOnly: true,
|
|
3320
3322
|
execute: async (ctx, input) => {
|
|
3321
|
-
const {
|
|
3323
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
3322
3324
|
const query = input.query;
|
|
3323
|
-
return
|
|
3325
|
+
return handlePagination((off, lim) => searchContactGroups(ctx.client, {
|
|
3324
3326
|
filter: { name: { contains: query } },
|
|
3325
3327
|
limit: lim, offset: off,
|
|
3326
|
-
sort:
|
|
3327
|
-
}), limit, offset,
|
|
3328
|
+
sort: { sortBy: [sortBy ?? 'name'], order: (sortOrder ?? 'ASC') },
|
|
3329
|
+
}), limit, offset, 20);
|
|
3328
3330
|
},
|
|
3329
3331
|
},
|
|
3330
3332
|
{
|
|
@@ -3369,17 +3371,19 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3369
3371
|
group: 'custom_fields',
|
|
3370
3372
|
readOnly: true,
|
|
3371
3373
|
execute: async (ctx, input) => {
|
|
3374
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
3372
3375
|
const filter = {};
|
|
3373
3376
|
if (input.customFieldName)
|
|
3374
3377
|
filter.customFieldName = { contains: input.customFieldName };
|
|
3375
3378
|
if (input.datatypeCode)
|
|
3376
3379
|
filter.datatypeCode = { eq: input.datatypeCode };
|
|
3377
3380
|
const searchFilter = Object.keys(filter).length > 0 ? filter : undefined;
|
|
3378
|
-
return searchCustomFields(ctx.client, {
|
|
3381
|
+
return handlePagination((off, lim) => searchCustomFields(ctx.client, {
|
|
3379
3382
|
filter: searchFilter,
|
|
3380
|
-
limit:
|
|
3381
|
-
offset:
|
|
3382
|
-
|
|
3383
|
+
limit: lim,
|
|
3384
|
+
offset: off,
|
|
3385
|
+
sort: { sortBy: [sortBy ?? 'customFieldName'], order: (sortOrder ?? 'ASC') },
|
|
3386
|
+
}), limit, offset, 20);
|
|
3383
3387
|
},
|
|
3384
3388
|
},
|
|
3385
3389
|
{
|
|
@@ -3443,18 +3447,19 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3443
3447
|
params: {
|
|
3444
3448
|
query: { type: 'string', description: 'Search term (profile name or tax type code)' },
|
|
3445
3449
|
...SEARCH_PARAMS,
|
|
3450
|
+
sortBy: { type: 'string', enum: ['name', 'taxTypeCode', 'status', 'vatValue'], description: 'Sort field for tax profiles' },
|
|
3446
3451
|
},
|
|
3447
3452
|
required: ['query'],
|
|
3448
3453
|
group: 'tax_profiles',
|
|
3449
3454
|
readOnly: true,
|
|
3450
3455
|
execute: async (ctx, input) => {
|
|
3451
|
-
const {
|
|
3456
|
+
const { limit, offset, sortBy, sortOrder } = extractPaginationInput(input);
|
|
3452
3457
|
const query = input.query;
|
|
3453
|
-
return
|
|
3458
|
+
return handlePagination((off, lim) => searchTaxProfiles(ctx.client, {
|
|
3454
3459
|
filter: { or: { name: { contains: query }, taxTypeCode: { contains: query } } },
|
|
3455
3460
|
limit: lim, offset: off,
|
|
3456
|
-
sort:
|
|
3457
|
-
}), limit, offset,
|
|
3461
|
+
sort: { sortBy: [sortBy ?? 'name'], order: (sortOrder ?? 'ASC') },
|
|
3462
|
+
}), limit, offset, 20);
|
|
3458
3463
|
},
|
|
3459
3464
|
},
|
|
3460
3465
|
getTool('get_tax_profile', 'Get full tax profile details including tax rate, type code, and status.', 'tax_profiles', (client, id) => getTaxProfile(client, id)),
|