jaz-clio 4.30.0 → 4.30.2
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/accounts.js +2 -2
- package/dist/core/api/chart-of-accounts.js +3 -0
- package/dist/core/api/client.js +7 -2
- package/dist/core/api/guards.js +33 -0
- package/dist/core/registry/namespaces.js +124 -26
- package/dist/core/registry/tools.js +247 -99
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 4.30.
|
|
3
|
+
version: 4.30.2
|
|
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.30.
|
|
3
|
+
version: 4.30.2
|
|
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.30.
|
|
3
|
+
version: 4.30.2
|
|
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.30.
|
|
3
|
+
version: 4.30.2
|
|
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.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { listAccounts, searchAccounts, createAccount, deleteAccount } from '../core/api/chart-of-accounts.js';
|
|
3
|
-
import { findExistingAccount } from '../core/api/guards.js';
|
|
3
|
+
import { findExistingAccount, normalizeAccountType } from '../core/api/guards.js';
|
|
4
4
|
import { apiAction } from './api-action.js';
|
|
5
5
|
import { outputList } from './output.js';
|
|
6
6
|
import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
|
|
@@ -93,7 +93,7 @@ export function registerAccountsCommand(program) {
|
|
|
93
93
|
const data = {
|
|
94
94
|
code: opts.code,
|
|
95
95
|
name: opts.name,
|
|
96
|
-
accountType: opts.type,
|
|
96
|
+
accountType: normalizeAccountType(opts.type),
|
|
97
97
|
};
|
|
98
98
|
if (opts.currency)
|
|
99
99
|
data.currencyCode = opts.currency;
|
|
@@ -4,6 +4,9 @@ export async function listAccounts(client, params) {
|
|
|
4
4
|
export async function searchAccounts(client, params) {
|
|
5
5
|
return client.search('/api/v1/chart-of-accounts/search', params);
|
|
6
6
|
}
|
|
7
|
+
export async function getAccount(client, resourceId) {
|
|
8
|
+
return client.get(`/api/v1/chart-of-accounts/${resourceId}`);
|
|
9
|
+
}
|
|
7
10
|
export async function createAccount(client, data) {
|
|
8
11
|
return client.post('/api/v1/chart-of-accounts', data);
|
|
9
12
|
}
|
package/dist/core/api/client.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
const DEFAULT_BASE_URL = 'https://api.getjaz.com';
|
|
2
2
|
const DEFAULT_TIMEOUT = 30_000;
|
|
3
3
|
const MAX_RETRIES = 3;
|
|
4
|
-
|
|
4
|
+
/** Status codes safe to retry. 500/502/503 only retried on idempotent methods (GET/HEAD). */
|
|
5
|
+
const ALWAYS_RETRY = [429];
|
|
6
|
+
const IDEMPOTENT_RETRY = [500, 502, 503];
|
|
5
7
|
export class JazApiError extends Error {
|
|
6
8
|
status;
|
|
7
9
|
body;
|
|
@@ -72,7 +74,10 @@ export class JazClient {
|
|
|
72
74
|
const attemptStart = Date.now();
|
|
73
75
|
try {
|
|
74
76
|
const response = await fetch(url.toString(), fetchOptions);
|
|
75
|
-
|
|
77
|
+
const isIdempotent = ['GET', 'HEAD'].includes(method);
|
|
78
|
+
const shouldRetry = ALWAYS_RETRY.includes(response.status)
|
|
79
|
+
|| (isIdempotent && IDEMPOTENT_RETRY.includes(response.status));
|
|
80
|
+
if (shouldRetry && attempt < this.maxRetries) {
|
|
76
81
|
this.onRequest?.({
|
|
77
82
|
method, path, statusCode: response.status,
|
|
78
83
|
durationMs: Date.now() - attemptStart, attempt, retried: true,
|
package/dist/core/api/guards.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { searchContacts } from './contacts.js';
|
|
2
2
|
import { searchItems } from './items.js';
|
|
3
3
|
import { searchAccounts } from './chart-of-accounts.js';
|
|
4
|
+
import { searchCapsules } from './capsules.js';
|
|
4
5
|
/** Find existing contact by name. Returns first match or undefined. */
|
|
5
6
|
export async function findExistingContact(client, name) {
|
|
6
7
|
if (!name)
|
|
@@ -15,6 +16,38 @@ export async function findExistingItem(client, itemCode) {
|
|
|
15
16
|
const result = await searchItems(client, { filter: { itemCode: { eq: itemCode } }, limit: 1 });
|
|
16
17
|
return result.totalElements > 0 ? result.data[0] : undefined;
|
|
17
18
|
}
|
|
19
|
+
// ── Account type normalization ───────────────────────────────────
|
|
20
|
+
/** Normalize common accountType variations to exact API values.
|
|
21
|
+
* Pure map lookup — no network calls, no perf impact. */
|
|
22
|
+
const ACCOUNT_TYPE_MAP = {
|
|
23
|
+
'current assets': 'Current Asset', 'current asset': 'Current Asset',
|
|
24
|
+
'fixed assets': 'Fixed Asset', 'fixed asset': 'Fixed Asset',
|
|
25
|
+
'bank account': 'Bank Accounts', 'bank accounts': 'Bank Accounts', 'bank': 'Bank Accounts',
|
|
26
|
+
'current liabilities': 'Current Liability', 'current liability': 'Current Liability',
|
|
27
|
+
'non-current liabilities': 'Non-current Liability', 'non-current liability': 'Non-current Liability',
|
|
28
|
+
'equity': 'Shareholders Equity', 'shareholders equity': 'Shareholders Equity',
|
|
29
|
+
"shareholders' equity": 'Shareholders Equity', "shareholder's equity": 'Shareholders Equity',
|
|
30
|
+
'revenue': 'Operating Revenue', 'operating revenue': 'Operating Revenue',
|
|
31
|
+
'other revenue': 'Other Revenue', 'other income': 'Other Revenue',
|
|
32
|
+
'expense': 'Operating Expense', 'operating expense': 'Operating Expense', 'expenses': 'Operating Expense',
|
|
33
|
+
'direct costs': 'Direct Costs', 'cost of goods sold': 'Direct Costs', 'cogs': 'Direct Costs',
|
|
34
|
+
'cash': 'Cash', 'inventory': 'Inventory',
|
|
35
|
+
};
|
|
36
|
+
export function normalizeAccountType(raw) {
|
|
37
|
+
if (typeof raw !== 'string')
|
|
38
|
+
return String(raw ?? '');
|
|
39
|
+
const trimmed = raw.trim();
|
|
40
|
+
return ACCOUNT_TYPE_MAP[trimmed.toLowerCase()] ?? trimmed;
|
|
41
|
+
}
|
|
42
|
+
// ── Duplicate detection ─────────────────────────────────────────
|
|
43
|
+
/** Find existing capsule by title. Normalizes whitespace/case for comparison. */
|
|
44
|
+
export async function findExistingCapsule(client, title) {
|
|
45
|
+
if (!title?.trim())
|
|
46
|
+
return undefined;
|
|
47
|
+
const normalized = title.trim().toLowerCase();
|
|
48
|
+
const result = await searchCapsules(client, { filter: { title: { contains: title.trim() } }, limit: 20 });
|
|
49
|
+
return result.data.find(c => c.title?.trim().toLowerCase() === normalized);
|
|
50
|
+
}
|
|
18
51
|
/** Find existing account by name. Returns first match or undefined. */
|
|
19
52
|
export async function findExistingAccount(client, name) {
|
|
20
53
|
if (!name)
|
|
@@ -1,47 +1,145 @@
|
|
|
1
1
|
export const TOOL_NAMESPACES = [
|
|
2
|
+
// ── Transactions ────────────────────────────────────────────
|
|
2
3
|
{
|
|
3
|
-
name: '
|
|
4
|
-
description: 'Sales invoices (INV/SI)
|
|
5
|
-
groups: ['invoices'
|
|
4
|
+
name: 'invoices',
|
|
5
|
+
description: 'Sales invoices (INV/SI). Create, search, get, update, delete, pay, finalize, apply credits, download PDF. Also: receivables, AR, billing, overdue invoices.',
|
|
6
|
+
groups: ['invoices'],
|
|
6
7
|
},
|
|
7
8
|
{
|
|
8
|
-
name: '
|
|
9
|
-
description: '
|
|
10
|
-
groups: ['
|
|
9
|
+
name: 'customer_credit_notes',
|
|
10
|
+
description: 'Customer credit notes (CN). Create, search, update, delete, finalize, refund, download PDF. Also: sales returns, customer CN.',
|
|
11
|
+
groups: ['customer_credit_notes'],
|
|
11
12
|
},
|
|
12
13
|
{
|
|
13
|
-
name: '
|
|
14
|
-
description: '
|
|
15
|
-
groups: ['
|
|
14
|
+
name: 'bills',
|
|
15
|
+
description: 'Purchase bills (PO/PI). Create, search, get, update, delete, pay, finalize, apply credits. Also: payables, AP, vendor invoices, supplier bills.',
|
|
16
|
+
groups: ['bills'],
|
|
16
17
|
},
|
|
17
18
|
{
|
|
18
|
-
name: '
|
|
19
|
-
description: '
|
|
20
|
-
groups: ['
|
|
19
|
+
name: 'supplier_credit_notes',
|
|
20
|
+
description: 'Supplier credit notes. Create, search, update, delete, finalize, refund. Also: purchase returns, debit notes, supplier CN.',
|
|
21
|
+
groups: ['supplier_credit_notes'],
|
|
21
22
|
},
|
|
22
23
|
{
|
|
23
|
-
name: '
|
|
24
|
-
description: '
|
|
25
|
-
groups: ['
|
|
24
|
+
name: 'journals',
|
|
25
|
+
description: 'Journal entries (JE). Create, search, update, delete manual journals. Also: adjusting entries, accruals, reclassifications, corrections.',
|
|
26
|
+
groups: ['journals'],
|
|
26
27
|
},
|
|
27
28
|
{
|
|
28
|
-
name: '
|
|
29
|
-
description: '
|
|
30
|
-
groups: ['
|
|
29
|
+
name: 'cashflows',
|
|
30
|
+
description: 'Cash-in receipts, cash-out disbursements, cash transfers between own accounts, petty cash. WHEN TO USE: external cash received → cash-in. External cash paid → cash-out. Moving funds between own accounts → cash transfer.',
|
|
31
|
+
groups: ['cash_entries', 'cash_transfers'],
|
|
31
32
|
},
|
|
33
|
+
// ── Banking ─────────────────────────────────────────────────
|
|
32
34
|
{
|
|
33
|
-
name: '
|
|
34
|
-
description: '
|
|
35
|
-
groups: ['
|
|
35
|
+
name: 'bank_accounts',
|
|
36
|
+
description: 'Bank accounts, bank statement imports (CSV/OFX), bank records search, auto-reconciliation. For unreconciled queries: ALWAYS search bank records with status UNRECONCILED after listing accounts. Also: bank feeds, bank balance.',
|
|
37
|
+
groups: ['bank'],
|
|
36
38
|
},
|
|
39
|
+
{
|
|
40
|
+
name: 'bank_rules',
|
|
41
|
+
description: 'Bank reconciliation rules (action shortcuts). Create, search, update, delete bank rules. Configure auto-matching rules for bank records.',
|
|
42
|
+
groups: ['bank_rules'],
|
|
43
|
+
},
|
|
44
|
+
// ── Reports ─────────────────────────────────────────────────
|
|
45
|
+
{
|
|
46
|
+
name: 'financial_reports',
|
|
47
|
+
description: 'Core financial statements: trial balance (TB), balance sheet (BS/B/S), profit & loss (PnL/P&L/income statement), cash flow, general ledger (GL), cash balance/position, equity movement, VAT/GST ledger. Also: how profitable, what is the balance.',
|
|
48
|
+
groups: ['financial_reports'],
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
name: 'operational_reports',
|
|
52
|
+
description: 'Aging and operational reports: aged receivables (AR aging), aged payables (AP aging), AR report, bank balance summary, bank reconciliation reports, fixed asset (FA) summary, FA reconciliation. Data exports (CSV/Excel). Also: overdue analysis, how much owed.',
|
|
53
|
+
groups: ['operational_reports', 'exports'],
|
|
54
|
+
},
|
|
55
|
+
// ── Master Data ─────────────────────────────────────────────
|
|
56
|
+
{
|
|
57
|
+
name: 'contacts',
|
|
58
|
+
description: 'Contacts (customers/suppliers/vendors), contact groups. Create, search, get, update, delete, bulk create contacts. List/create contact groups.',
|
|
59
|
+
groups: ['contacts', 'contact_groups'],
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
name: 'items_and_inventory',
|
|
63
|
+
description: 'Products, services, inventory items. Create, search, get, update, delete items. Check inventory balance. Also: SKU, catalog, stock.',
|
|
64
|
+
groups: ['items', 'inventory'],
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
name: 'tags_and_custom_fields',
|
|
68
|
+
description: 'Tags for categorizing transactions. Custom fields for adding metadata (text, date, dropdown). Create, search, delete tags and custom fields.',
|
|
69
|
+
groups: ['tags', 'custom_fields'],
|
|
70
|
+
},
|
|
71
|
+
// ── Accounting Setup ────────────────────────────────────────
|
|
72
|
+
{
|
|
73
|
+
name: 'chart_of_accounts',
|
|
74
|
+
description: 'Chart of accounts (COA/GL accounts). Create, search, update accounts. Bookmarks (favorites/shortcuts). Also: ledger codes, account types.',
|
|
75
|
+
groups: ['accounts', 'bookmarks'],
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
name: 'currencies',
|
|
79
|
+
description: 'Currencies, exchange rates (FX/forex). List/add org currencies. Set, update, import currency rates. Also: multi-currency, FX rates.',
|
|
80
|
+
groups: ['currencies'],
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
name: 'tax_profiles',
|
|
84
|
+
description: 'Tax profiles (GST/VAT/sales tax), withholding tax codes (WHT/ATC). Search, create, update tax profiles. List WHT codes.',
|
|
85
|
+
groups: ['tax_profiles'],
|
|
86
|
+
},
|
|
87
|
+
// ── Capsules & Recipes ──────────────────────────────────────
|
|
37
88
|
{
|
|
38
89
|
name: 'capsules_and_recipes',
|
|
39
|
-
description: 'Capsules (transaction groupings
|
|
40
|
-
groups: ['capsules', 'recipes'
|
|
90
|
+
description: 'Capsules (transaction groupings/capsule types). Financial recipes: amortization, depreciation, deferred revenue, IFRS 16 leases, hire purchase, fixed deposits, FX revaluation, loan schedules, ECL/expected credit loss, IAS 37 provisions, asset disposal. Plan and execute recipes. Keywords: calculate, provision, schedule, expected credit loss, revaluation.',
|
|
91
|
+
groups: ['capsules', 'recipes'],
|
|
92
|
+
},
|
|
93
|
+
// ── Scheduling ──────────────────────────────────────────────
|
|
94
|
+
{
|
|
95
|
+
name: 'scheduled_transactions',
|
|
96
|
+
description: 'Scheduled/recurring invoices, bills, journals. Create scheduled invoices/bills/journals, search scheduled transactions. Also: recurring, auto-generate.',
|
|
97
|
+
groups: ['schedulers'],
|
|
98
|
+
},
|
|
99
|
+
{
|
|
100
|
+
name: 'subscriptions',
|
|
101
|
+
description: 'Subscriptions (recurring billing/payment plans). Create, update, cancel, search subscriptions. Also: recurring charges, subscription schedules.',
|
|
102
|
+
groups: ['subscriptions'],
|
|
103
|
+
},
|
|
104
|
+
// ── Organization ────────────────────────────────────────────
|
|
105
|
+
{
|
|
106
|
+
name: 'organization',
|
|
107
|
+
description: 'Organization info (name, currency, country, fiscal year). User management: invite, update, remove, search org users. Bulk invite.',
|
|
108
|
+
groups: ['organization', 'org_users'],
|
|
109
|
+
},
|
|
110
|
+
// ── Documents & AI ──────────────────────────────────────────
|
|
111
|
+
{
|
|
112
|
+
name: 'document_ai',
|
|
113
|
+
description: 'File attachments, AI document extraction (magic/OCR). Upload/list attachments. Create transactions from PDFs/images (invoice scanning, bill extraction). Track extraction workflows.',
|
|
114
|
+
groups: ['attachments', 'magic'],
|
|
115
|
+
},
|
|
116
|
+
// ── Fixed Assets ────────────────────────────────────────────
|
|
117
|
+
{
|
|
118
|
+
name: 'fixed_assets',
|
|
119
|
+
description: 'Fixed assets (PP&E/property, plant, equipment). Search, create, update, discard, sell, transfer, undo disposal. Also: depreciation, asset register.',
|
|
120
|
+
groups: ['fixed_assets'],
|
|
121
|
+
},
|
|
122
|
+
// ── Payments & Search ───────────────────────────────────────
|
|
123
|
+
{
|
|
124
|
+
name: 'payments_and_search',
|
|
125
|
+
description: 'Payments search/list across all transaction types. Cashflow transaction ledger. Universal cross-entity search. Transaction summary (fetch any transaction + attachments + payment history in one call).',
|
|
126
|
+
groups: ['payments', 'cashflow', 'search'],
|
|
127
|
+
},
|
|
128
|
+
// ── Drafts ──────────────────────────────────────────────────
|
|
129
|
+
{
|
|
130
|
+
name: 'drafts',
|
|
131
|
+
description: 'Draft validation for invoices, bills, journals, credit notes. Bulk finalize multiple drafts at once. Check if drafts are ready to finalize.',
|
|
132
|
+
groups: ['drafts'],
|
|
133
|
+
},
|
|
134
|
+
// ── Job Blueprints ──────────────────────────────────────────
|
|
135
|
+
{
|
|
136
|
+
name: 'close_procedures',
|
|
137
|
+
description: 'Period-end close checklists: month-end, quarter-end, year-end close. Bank reconciliation job. GST/VAT filing job. Audit preparation. Returns structured blueprints.',
|
|
138
|
+
groups: ['close_jobs'],
|
|
41
139
|
},
|
|
42
140
|
{
|
|
43
|
-
name: '
|
|
44
|
-
description: '
|
|
45
|
-
groups: ['
|
|
141
|
+
name: 'operational_jobs',
|
|
142
|
+
description: 'Operational job checklists: payment runs, credit control/collections, supplier reconciliation, fixed asset review, document collection, statutory filing. Returns structured blueprints.',
|
|
143
|
+
groups: ['operational_jobs'],
|
|
46
144
|
},
|
|
47
145
|
];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { findExistingContact, findExistingItem, findExistingAccount } from '../api/guards.js';
|
|
1
|
+
import { findExistingContact, findExistingItem, findExistingAccount, findExistingCapsule, normalizeAccountType } from '../api/guards.js';
|
|
2
2
|
import { getOrganization } from '../api/organization.js';
|
|
3
|
-
import { listAccounts, searchAccounts, createAccount, updateAccount, deleteAccount, } from '../api/chart-of-accounts.js';
|
|
3
|
+
import { listAccounts, searchAccounts, getAccount, createAccount, updateAccount, deleteAccount, } from '../api/chart-of-accounts.js';
|
|
4
4
|
import { listContacts, searchContacts, getContact, createContact, updateContact, deleteContact, } from '../api/contacts.js';
|
|
5
5
|
import { listInvoices, searchInvoices, getInvoice, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, createScheduledInvoice, finalizeInvoice, applyCreditsToInvoice, downloadInvoicePdf, } from '../api/invoices.js';
|
|
6
6
|
import { listBills, searchBills, getBill, createBill, updateBill, deleteBill, createBillPayment, createScheduledBill, finalizeBill, applyCreditsToBill, } from '../api/bills.js';
|
|
@@ -61,8 +61,7 @@ const PAGINATION_PARAMS = {
|
|
|
61
61
|
};
|
|
62
62
|
const SEARCH_PARAMS = {
|
|
63
63
|
...PAGINATION_PARAMS,
|
|
64
|
-
sortBy
|
|
65
|
-
order: { type: 'string', enum: ['ASC', 'DESC'], description: 'Sort direction' },
|
|
64
|
+
// sortBy/order removed — hardcoded per-tool to prevent LLM sending invalid sort fields
|
|
66
65
|
};
|
|
67
66
|
const CURRENCY_PARAM = {
|
|
68
67
|
type: 'object',
|
|
@@ -124,10 +123,8 @@ const LINE_ITEM_PARAM = {
|
|
|
124
123
|
function extractPaginationInput(input) {
|
|
125
124
|
const limit = input.limit;
|
|
126
125
|
const offset = input.offset;
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
const sortOrder = rawOrder === 'ASC' || rawOrder === 'DESC' ? rawOrder : undefined;
|
|
130
|
-
return { limit, offset, sortBy, sortOrder };
|
|
126
|
+
// sortBy/order not exposed to LLM — each search tool uses its own hardcoded default
|
|
127
|
+
return { limit, offset, sortBy: undefined, sortOrder: undefined };
|
|
131
128
|
}
|
|
132
129
|
// ── List tool factory (DRY — all 10 list tools share this) ───────
|
|
133
130
|
function listTool(name, description, group, fetcher) {
|
|
@@ -171,14 +168,137 @@ function deleteTool(name, description, group, deleter) {
|
|
|
171
168
|
},
|
|
172
169
|
};
|
|
173
170
|
}
|
|
174
|
-
/**
|
|
175
|
-
|
|
171
|
+
/**
|
|
172
|
+
* Writable fields per entity type (allowlist — only these go to PUT).
|
|
173
|
+
* Derived from the finalize/update function signatures in src/core/api/.
|
|
174
|
+
*/
|
|
175
|
+
const WRITABLE_FIELDS = {
|
|
176
|
+
invoice: new Set([
|
|
177
|
+
'reference', 'valueDate', 'dueDate', 'contactResourceId',
|
|
178
|
+
'lineItems', 'notes', 'invoiceNotes', 'internalNotes', 'tag', 'tags',
|
|
179
|
+
'isTaxVatApplicable', 'isTaxVATApplicable', 'taxInclusion',
|
|
180
|
+
'terms', 'currency', 'customFields', 'capsuleResourceId',
|
|
181
|
+
'taxProfileResourceId', 'customerPaymentProfileResourceId',
|
|
182
|
+
]),
|
|
183
|
+
bill: new Set([
|
|
184
|
+
'reference', 'valueDate', 'dueDate', 'contactResourceId',
|
|
185
|
+
'lineItems', 'notes', 'internalNotes', 'tag', 'tags',
|
|
186
|
+
'isTaxVatApplicable', 'isTaxVATApplicable', 'taxInclusion',
|
|
187
|
+
'terms', 'currency', 'customFields', 'capsuleResourceId',
|
|
188
|
+
'taxProfileResourceId',
|
|
189
|
+
]),
|
|
190
|
+
customer_credit_note: new Set([
|
|
191
|
+
'reference', 'valueDate', 'contactResourceId',
|
|
192
|
+
'lineItems', 'notes', 'tag', 'tags',
|
|
193
|
+
'isTaxVatApplicable', 'isTaxVATApplicable', 'taxInclusion',
|
|
194
|
+
'currency', 'customFields', 'capsuleResourceId',
|
|
195
|
+
'taxProfileResourceId',
|
|
196
|
+
]),
|
|
197
|
+
supplier_credit_note: new Set([
|
|
198
|
+
'reference', 'valueDate', 'contactResourceId',
|
|
199
|
+
'lineItems', 'notes', 'tag', 'tags',
|
|
200
|
+
'isTaxVatApplicable', 'isTaxVATApplicable', 'taxInclusion',
|
|
201
|
+
'currency', 'customFields', 'capsuleResourceId',
|
|
202
|
+
'taxProfileResourceId',
|
|
203
|
+
]),
|
|
204
|
+
};
|
|
205
|
+
/** Normalize a date string to YYYY-MM-DD (strip time component). */
|
|
206
|
+
function toDateOnly(v) {
|
|
207
|
+
if (typeof v !== 'string')
|
|
208
|
+
return v;
|
|
209
|
+
if (/^\d{4}-\d{2}-\d{2}$/.test(v))
|
|
210
|
+
return v;
|
|
211
|
+
const m = /^(\d{4}-\d{2}-\d{2})T/.exec(v);
|
|
212
|
+
return m ? m[1] : v;
|
|
213
|
+
}
|
|
214
|
+
/** Writable line item fields for PUT (allowlist). */
|
|
215
|
+
const WRITABLE_LI_FIELDS = new Set([
|
|
216
|
+
'name', 'quantity', 'unitPrice', 'unit', 'accountResourceId',
|
|
217
|
+
'taxProfileResourceId', 'description', 'classifierConfig',
|
|
218
|
+
'itemResourceId', 'discount',
|
|
219
|
+
]);
|
|
220
|
+
/** Normalize GET line items to PUT format (field name asymmetries + allowlist). */
|
|
221
|
+
function normalizeLineItems(items) {
|
|
222
|
+
if (!Array.isArray(items))
|
|
223
|
+
return items;
|
|
224
|
+
return items.map((li) => {
|
|
225
|
+
const out = {};
|
|
226
|
+
for (const [k, v] of Object.entries(li)) {
|
|
227
|
+
if (v === null || v === undefined)
|
|
228
|
+
continue;
|
|
229
|
+
// GET returns organizationAccountResourceId, PUT wants accountResourceId
|
|
230
|
+
if (k === 'organizationAccountResourceId') {
|
|
231
|
+
out.accountResourceId = v;
|
|
232
|
+
}
|
|
233
|
+
else if (k === 'taxProfile' && typeof v === 'object' && v !== null) {
|
|
234
|
+
const rid = v.resourceId;
|
|
235
|
+
if (rid)
|
|
236
|
+
out.taxProfileResourceId = rid;
|
|
237
|
+
}
|
|
238
|
+
else if (k === 'discount' && typeof v === 'object' && v !== null) {
|
|
239
|
+
// Only include discount if it has a non-zero value (zero defaults cause 400)
|
|
240
|
+
const rv = v.rateValue;
|
|
241
|
+
if (rv && Number(rv) !== 0)
|
|
242
|
+
out.discount = v;
|
|
243
|
+
}
|
|
244
|
+
else if (WRITABLE_LI_FIELDS.has(k)) {
|
|
245
|
+
out[k] = v;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return out;
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Fetch full entity, pick only writable fields, normalize GET→PUT asymmetries,
|
|
253
|
+
* then merge LLM overrides on top. Bulletproof for finalize/update PUTs.
|
|
254
|
+
*/
|
|
255
|
+
async function fetchAndMerge(client, type, resourceId, overrides) {
|
|
176
256
|
const fetcher = type === 'invoice' ? getInvoice
|
|
177
257
|
: type === 'bill' ? getBill
|
|
178
258
|
: type === 'customer_credit_note' ? getCustomerCreditNote
|
|
179
259
|
: getSupplierCreditNote;
|
|
180
260
|
const result = await fetcher(client, resourceId);
|
|
181
|
-
|
|
261
|
+
const existing = result.data;
|
|
262
|
+
const allowed = WRITABLE_FIELDS[type];
|
|
263
|
+
// Pick only writable fields from existing entity
|
|
264
|
+
const base = {};
|
|
265
|
+
for (const [k, v] of Object.entries(existing)) {
|
|
266
|
+
if (allowed.has(k) && v !== null && v !== undefined)
|
|
267
|
+
base[k] = v;
|
|
268
|
+
}
|
|
269
|
+
// Normalize dates to YYYY-MM-DD (GET returns ISO datetime)
|
|
270
|
+
if (base.valueDate)
|
|
271
|
+
base.valueDate = toDateOnly(base.valueDate);
|
|
272
|
+
if (base.dueDate)
|
|
273
|
+
base.dueDate = toDateOnly(base.dueDate);
|
|
274
|
+
// Normalize line items (field name asymmetries)
|
|
275
|
+
if (base.lineItems)
|
|
276
|
+
base.lineItems = normalizeLineItems(base.lineItems);
|
|
277
|
+
// Overrides win (LLM may supply updated lineItems, notes, etc.)
|
|
278
|
+
for (const [k, v] of Object.entries(overrides)) {
|
|
279
|
+
if (v !== undefined)
|
|
280
|
+
base[k] = v;
|
|
281
|
+
}
|
|
282
|
+
// Auto-resolve missing accountResourceId for finalize (exact name match, auditable)
|
|
283
|
+
const items = base.lineItems;
|
|
284
|
+
if (items?.length) {
|
|
285
|
+
const missing = items.filter(li => !li.accountResourceId);
|
|
286
|
+
if (missing.length > 0) {
|
|
287
|
+
const defaultName = type === 'invoice' || type === 'customer_credit_note'
|
|
288
|
+
? 'Operating Revenue' : 'Operating Expense';
|
|
289
|
+
const acctResult = await searchAccounts(client, { filter: { name: { eq: defaultName } }, limit: 1 });
|
|
290
|
+
const defaultAcct = acctResult.data?.[0];
|
|
291
|
+
if (defaultAcct?.resourceId) {
|
|
292
|
+
for (const li of missing)
|
|
293
|
+
li.accountResourceId = defaultAcct.resourceId;
|
|
294
|
+
}
|
|
295
|
+
const stillMissing = items.filter(li => !li.accountResourceId);
|
|
296
|
+
if (stillMissing.length > 0) {
|
|
297
|
+
throw new Error(`Cannot finalize: ${stillMissing.length} line item(s) missing accountResourceId and no default account found.`);
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return base;
|
|
182
302
|
}
|
|
183
303
|
// ── Tool Definitions ─────────────────────────────────────────────
|
|
184
304
|
export const TOOL_DEFINITIONS = [
|
|
@@ -233,22 +353,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
233
353
|
if (existingAcct) {
|
|
234
354
|
return { _guard: 'duplicate_skipped', message: `Account "${acctName}" already exists.`, existing: existingAcct };
|
|
235
355
|
}
|
|
236
|
-
|
|
237
|
-
const typeMap = {
|
|
238
|
-
'current assets': 'Current Asset', 'current asset': 'Current Asset',
|
|
239
|
-
'fixed assets': 'Fixed Asset', 'fixed asset': 'Fixed Asset',
|
|
240
|
-
'bank account': 'Bank Accounts', 'bank accounts': 'Bank Accounts', 'bank': 'Bank Accounts',
|
|
241
|
-
'current liabilities': 'Current Liability', 'current liability': 'Current Liability',
|
|
242
|
-
'non-current liabilities': 'Non-current Liability', 'non-current liability': 'Non-current Liability',
|
|
243
|
-
'equity': 'Shareholders Equity', 'shareholders equity': 'Shareholders Equity', "shareholders' equity": 'Shareholders Equity', "shareholder's equity": 'Shareholders Equity',
|
|
244
|
-
'revenue': 'Operating Revenue', 'operating revenue': 'Operating Revenue',
|
|
245
|
-
'other revenue': 'Other Revenue', 'other income': 'Other Revenue',
|
|
246
|
-
'expense': 'Operating Expense', 'operating expense': 'Operating Expense', 'expenses': 'Operating Expense',
|
|
247
|
-
'direct costs': 'Direct Costs', 'cost of goods sold': 'Direct Costs', 'cogs': 'Direct Costs',
|
|
248
|
-
'cash': 'Cash', 'inventory': 'Inventory',
|
|
249
|
-
};
|
|
250
|
-
const rawType = input.accountType;
|
|
251
|
-
const accountType = typeMap[rawType.toLowerCase()] ?? rawType;
|
|
356
|
+
const accountType = normalizeAccountType(input.accountType);
|
|
252
357
|
return createAccount(ctx.client, {
|
|
253
358
|
code: input.code,
|
|
254
359
|
name: acctName,
|
|
@@ -268,10 +373,22 @@ export const TOOL_DEFINITIONS = [
|
|
|
268
373
|
required: ['resourceId'],
|
|
269
374
|
group: 'accounts',
|
|
270
375
|
readOnly: false,
|
|
271
|
-
execute: async (ctx, input) =>
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
376
|
+
execute: async (ctx, input) => {
|
|
377
|
+
// Fetch existing account — only keep writable fields (not server-managed ones)
|
|
378
|
+
const rid = input.resourceId;
|
|
379
|
+
const existing = (await getAccount(ctx.client, rid)).data;
|
|
380
|
+
const ACCOUNT_WRITABLE = ['name', 'code', 'classificationType', 'taxProfileResourceId', 'currency', 'description'];
|
|
381
|
+
const merged = Object.fromEntries(ACCOUNT_WRITABLE.filter(k => existing[k] !== undefined && existing[k] !== null).map(k => [k, existing[k]]));
|
|
382
|
+
// GET→PUT asymmetry: GET returns accountType, PUT requires classificationType (Rule 21)
|
|
383
|
+
if (!merged.classificationType && existing.accountType) {
|
|
384
|
+
merged.classificationType = existing.accountType;
|
|
385
|
+
}
|
|
386
|
+
if (input.name !== undefined)
|
|
387
|
+
merged.name = input.name;
|
|
388
|
+
if (input.code !== undefined)
|
|
389
|
+
merged.code = input.code;
|
|
390
|
+
return updateAccount(ctx.client, rid, merged);
|
|
391
|
+
},
|
|
275
392
|
},
|
|
276
393
|
// ── Contacts ───────────────────────────────────────────────────
|
|
277
394
|
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 })),
|
|
@@ -404,6 +521,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
404
521
|
description: `Create a new invoice. IMPORTANT:
|
|
405
522
|
- Line items use "name" (not "description") for the item label.
|
|
406
523
|
- saveAsDraft defaults to true. Set to false only if user says "finalize".
|
|
524
|
+
- accountResourceId is REQUIRED on each lineItem for finalized invoices. Search accounts first (e.g. Operating Revenue).
|
|
407
525
|
- Currency format: { sourceCurrency: "USD", exchangeRate: 1.35 }
|
|
408
526
|
- contactResourceId is required — search contacts first to get the ID.
|
|
409
527
|
- Dates must be YYYY-MM-DD format.`,
|
|
@@ -496,7 +614,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
496
614
|
},
|
|
497
615
|
{
|
|
498
616
|
name: 'finalize_invoice',
|
|
499
|
-
description: 'Finalize a draft invoice (set saveAsDraft=false). Can optionally update fields in the same call.',
|
|
617
|
+
description: 'Finalize a draft invoice (set saveAsDraft=false). Can optionally update fields in the same call. IMPORTANT: Every lineItem MUST have accountResourceId. If the draft was created without it, pass lineItems with accountResourceId added (search accounts first, e.g. Operating Revenue for sales).',
|
|
500
618
|
params: {
|
|
501
619
|
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
502
620
|
reference: { type: 'string' },
|
|
@@ -509,10 +627,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
509
627
|
group: 'invoices',
|
|
510
628
|
readOnly: false,
|
|
511
629
|
execute: async (ctx, input) => {
|
|
512
|
-
const { resourceId: rid, ...
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
return finalizeInvoice(ctx.client, rid, data);
|
|
630
|
+
const { resourceId: rid, ...overrides } = input;
|
|
631
|
+
const merged = await fetchAndMerge(ctx.client, 'invoice', rid, overrides);
|
|
632
|
+
return finalizeInvoice(ctx.client, rid, merged);
|
|
516
633
|
},
|
|
517
634
|
},
|
|
518
635
|
{
|
|
@@ -594,7 +711,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
594
711
|
},
|
|
595
712
|
{
|
|
596
713
|
name: 'create_bill',
|
|
597
|
-
description: 'Create a new bill. Same rules as create_invoice
|
|
714
|
+
description: 'Create a new bill. Same rules as create_invoice: name not description, saveAsDraft defaults true, accountResourceId REQUIRED on lineItems for finalized bills (search accounts first, e.g. Operating Expense).',
|
|
598
715
|
params: {
|
|
599
716
|
reference: { type: 'string' },
|
|
600
717
|
valueDate: { type: 'string' },
|
|
@@ -680,7 +797,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
680
797
|
},
|
|
681
798
|
{
|
|
682
799
|
name: 'finalize_bill',
|
|
683
|
-
description: 'Finalize a draft bill (set saveAsDraft=false). Can optionally update fields in the same call.',
|
|
800
|
+
description: 'Finalize a draft bill (set saveAsDraft=false). Can optionally update fields in the same call. IMPORTANT: Every lineItem MUST have accountResourceId. If the draft was created without it, pass lineItems with accountResourceId added (search accounts first, e.g. Operating Expense for purchases).',
|
|
684
801
|
params: {
|
|
685
802
|
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
686
803
|
reference: { type: 'string' },
|
|
@@ -693,10 +810,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
693
810
|
group: 'bills',
|
|
694
811
|
readOnly: false,
|
|
695
812
|
execute: async (ctx, input) => {
|
|
696
|
-
const { resourceId: rid, ...
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
return finalizeBill(ctx.client, rid, data);
|
|
813
|
+
const { resourceId: rid, ...overrides } = input;
|
|
814
|
+
const merged = await fetchAndMerge(ctx.client, 'bill', rid, overrides);
|
|
815
|
+
return finalizeBill(ctx.client, rid, merged);
|
|
700
816
|
},
|
|
701
817
|
},
|
|
702
818
|
{
|
|
@@ -792,7 +908,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
792
908
|
readOnly: false,
|
|
793
909
|
execute: async (ctx, input) => deleteJournal(ctx.client, input.resourceId),
|
|
794
910
|
},
|
|
795
|
-
// ── Reports
|
|
911
|
+
// ── Financial Reports (core statements) ──────────────────────
|
|
796
912
|
{
|
|
797
913
|
name: 'generate_trial_balance',
|
|
798
914
|
description: 'Generate a trial balance report.',
|
|
@@ -801,7 +917,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
801
917
|
currencyCode: { type: 'string', description: 'Currency override' },
|
|
802
918
|
},
|
|
803
919
|
required: ['endDate'],
|
|
804
|
-
group: '
|
|
920
|
+
group: 'financial_reports',
|
|
805
921
|
readOnly: true,
|
|
806
922
|
execute: async (ctx, input) => generateTrialBalance(ctx.client, input),
|
|
807
923
|
},
|
|
@@ -813,7 +929,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
813
929
|
currencyCode: { type: 'string' },
|
|
814
930
|
},
|
|
815
931
|
required: [],
|
|
816
|
-
group: '
|
|
932
|
+
group: 'financial_reports',
|
|
817
933
|
readOnly: true,
|
|
818
934
|
execute: async (ctx, input) => {
|
|
819
935
|
const date = input.snapshotDate ?? new Date().toISOString().slice(0, 10);
|
|
@@ -829,7 +945,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
829
945
|
currencyCode: { type: 'string' },
|
|
830
946
|
},
|
|
831
947
|
required: ['startDate', 'endDate'],
|
|
832
|
-
group: '
|
|
948
|
+
group: 'financial_reports',
|
|
833
949
|
readOnly: true,
|
|
834
950
|
execute: async (ctx, input) => generateProfitAndLoss(ctx.client, input),
|
|
835
951
|
},
|
|
@@ -841,7 +957,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
841
957
|
endDate: { type: 'string' },
|
|
842
958
|
},
|
|
843
959
|
required: ['startDate', 'endDate'],
|
|
844
|
-
group: '
|
|
960
|
+
group: 'financial_reports',
|
|
845
961
|
readOnly: true,
|
|
846
962
|
execute: async (ctx, input) => generateCashflow(ctx.client, input),
|
|
847
963
|
},
|
|
@@ -852,7 +968,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
852
968
|
endDate: { type: 'string' },
|
|
853
969
|
},
|
|
854
970
|
required: ['endDate'],
|
|
855
|
-
group: '
|
|
971
|
+
group: 'operational_reports',
|
|
856
972
|
readOnly: true,
|
|
857
973
|
execute: async (ctx, input) => generateArSummary(ctx.client, input),
|
|
858
974
|
},
|
|
@@ -863,7 +979,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
863
979
|
endDate: { type: 'string' },
|
|
864
980
|
},
|
|
865
981
|
required: ['endDate'],
|
|
866
|
-
group: '
|
|
982
|
+
group: 'operational_reports',
|
|
867
983
|
readOnly: true,
|
|
868
984
|
execute: async (ctx, input) => generateApSummary(ctx.client, input),
|
|
869
985
|
},
|
|
@@ -874,7 +990,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
874
990
|
endDate: { type: 'string', description: 'Snapshot date (YYYY-MM-DD)' },
|
|
875
991
|
},
|
|
876
992
|
required: ['endDate'],
|
|
877
|
-
group: '
|
|
993
|
+
group: 'financial_reports',
|
|
878
994
|
readOnly: true,
|
|
879
995
|
execute: async (ctx, input) => generateCashBalance(ctx.client, input),
|
|
880
996
|
},
|
|
@@ -887,7 +1003,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
887
1003
|
groupBy: { type: 'string', description: 'Group by: ACCOUNT (default), TRANSACTION, or CAPSULE' },
|
|
888
1004
|
},
|
|
889
1005
|
required: ['startDate', 'endDate'],
|
|
890
|
-
group: '
|
|
1006
|
+
group: 'financial_reports',
|
|
891
1007
|
readOnly: true,
|
|
892
1008
|
execute: async (ctx, input) => {
|
|
893
1009
|
const i = input;
|
|
@@ -1108,11 +1224,19 @@ export const TOOL_DEFINITIONS = [
|
|
|
1108
1224
|
required: ['capsuleTypeResourceId', 'title'],
|
|
1109
1225
|
group: 'capsules',
|
|
1110
1226
|
readOnly: false,
|
|
1111
|
-
execute: async (ctx, input) =>
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1227
|
+
execute: async (ctx, input) => {
|
|
1228
|
+
const capsuleTitle = input.title;
|
|
1229
|
+
// Guard: check for existing capsule with same title
|
|
1230
|
+
const existingCapsule = await findExistingCapsule(ctx.client, capsuleTitle);
|
|
1231
|
+
if (existingCapsule) {
|
|
1232
|
+
return { _guard: 'duplicate_skipped', message: `Capsule "${capsuleTitle}" already exists.`, existing: existingCapsule };
|
|
1233
|
+
}
|
|
1234
|
+
return createCapsule(ctx.client, {
|
|
1235
|
+
capsuleTypeResourceId: input.capsuleTypeResourceId,
|
|
1236
|
+
title: capsuleTitle,
|
|
1237
|
+
description: input.description,
|
|
1238
|
+
});
|
|
1239
|
+
},
|
|
1116
1240
|
},
|
|
1117
1241
|
{
|
|
1118
1242
|
name: 'update_capsule',
|
|
@@ -1260,8 +1384,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
1260
1384
|
group: 'customer_credit_notes',
|
|
1261
1385
|
readOnly: false,
|
|
1262
1386
|
execute: async (ctx, input) => {
|
|
1263
|
-
const { resourceId: rid, ...
|
|
1264
|
-
|
|
1387
|
+
const { resourceId: rid, ...overrides } = input;
|
|
1388
|
+
const merged = await fetchAndMerge(ctx.client, 'customer_credit_note', rid, overrides);
|
|
1389
|
+
return finalizeCustomerCreditNote(ctx.client, rid, merged);
|
|
1265
1390
|
},
|
|
1266
1391
|
},
|
|
1267
1392
|
{
|
|
@@ -1434,8 +1559,9 @@ export const TOOL_DEFINITIONS = [
|
|
|
1434
1559
|
group: 'supplier_credit_notes',
|
|
1435
1560
|
readOnly: false,
|
|
1436
1561
|
execute: async (ctx, input) => {
|
|
1437
|
-
const { resourceId: rid, ...
|
|
1438
|
-
|
|
1562
|
+
const { resourceId: rid, ...overrides } = input;
|
|
1563
|
+
const merged = await fetchAndMerge(ctx.client, 'supplier_credit_note', rid, overrides);
|
|
1564
|
+
return finalizeSupplierCreditNote(ctx.client, rid, merged);
|
|
1439
1565
|
},
|
|
1440
1566
|
},
|
|
1441
1567
|
{
|
|
@@ -1508,7 +1634,22 @@ export const TOOL_DEFINITIONS = [
|
|
|
1508
1634
|
required: ['currencies'],
|
|
1509
1635
|
group: 'currencies',
|
|
1510
1636
|
readOnly: false,
|
|
1511
|
-
execute: async (ctx, input) =>
|
|
1637
|
+
execute: async (ctx, input) => {
|
|
1638
|
+
const requested = input.currencies;
|
|
1639
|
+
// Guard: fetch all enabled currencies once, filter locally (not N API calls)
|
|
1640
|
+
const allCurrencies = await listCurrencies(ctx.client);
|
|
1641
|
+
const enabledSet = new Set(allCurrencies.data.map(c => c.currencyCode.toUpperCase()));
|
|
1642
|
+
const alreadyEnabled = requested.filter(code => enabledSet.has(code.toUpperCase()));
|
|
1643
|
+
const toAdd = requested.filter(code => !enabledSet.has(code.toUpperCase()));
|
|
1644
|
+
if (toAdd.length === 0) {
|
|
1645
|
+
return { _guard: 'duplicate_skipped', message: `All currencies already enabled: ${requested.join(', ')}.`, existing: alreadyEnabled };
|
|
1646
|
+
}
|
|
1647
|
+
const result = await addCurrency(ctx.client, toAdd);
|
|
1648
|
+
if (alreadyEnabled.length > 0) {
|
|
1649
|
+
return { ...result, _note: `Skipped already-enabled: ${alreadyEnabled.join(', ')}` };
|
|
1650
|
+
}
|
|
1651
|
+
return result;
|
|
1652
|
+
},
|
|
1512
1653
|
},
|
|
1513
1654
|
{
|
|
1514
1655
|
name: 'list_currency_rates',
|
|
@@ -1610,10 +1751,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1610
1751
|
listTool('list_cash_in', 'List cash-in entries (direct cash receipts). Paginated.', 'cash_entries', (client, off, lim) => listCashIn(client, { limit: lim, offset: off })),
|
|
1611
1752
|
{
|
|
1612
1753
|
name: 'create_cash_in',
|
|
1613
|
-
description: `Record money received INTO a bank account (customer payment, refund received, deposit
|
|
1754
|
+
description: `Record money received INTO a bank account from an EXTERNAL source (customer payment, refund received, deposit from outside).
|
|
1755
|
+
WHEN TO USE: customer payments received, refunds from suppliers, insurance payouts, external deposits.
|
|
1756
|
+
WHEN NOT TO USE: moving money between your own bank/cash accounts — use create_cash_transfer instead.
|
|
1614
1757
|
- accountResourceId is the bank/cash account receiving the money.
|
|
1615
1758
|
- journalEntries are the offsetting entries (e.g., revenue account). Each needs accountResourceId, type (DEBIT/CREDIT), and amount.
|
|
1616
|
-
- The API enforces account separation: cash-in accounts cannot be used for cash-out
|
|
1759
|
+
- The API enforces account separation: cash-in accounts cannot be used for cash-out.`,
|
|
1617
1760
|
params: {
|
|
1618
1761
|
reference: { type: 'string', description: 'Reference number' },
|
|
1619
1762
|
valueDate: { type: 'string', description: 'Date (YYYY-MM-DD)' },
|
|
@@ -1649,10 +1792,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1649
1792
|
listTool('list_cash_out', 'List cash-out entries (direct cash disbursements). Paginated.', 'cash_entries', (client, off, lim) => listCashOut(client, { limit: lim, offset: off })),
|
|
1650
1793
|
{
|
|
1651
1794
|
name: 'create_cash_out',
|
|
1652
|
-
description: `Record money paid OUT FROM a bank account (expense, supplier payment, reimbursement, withdrawal).
|
|
1795
|
+
description: `Record money paid OUT FROM a bank account to an EXTERNAL party (expense, supplier payment, reimbursement, withdrawal).
|
|
1796
|
+
WHEN TO USE: expenses paid, supplier payments, reimbursements, withdrawals to external parties.
|
|
1797
|
+
WHEN NOT TO USE: moving money between your own bank/cash accounts — use create_cash_transfer instead.
|
|
1653
1798
|
- accountResourceId is the bank/cash account disbursing the money.
|
|
1654
1799
|
- journalEntries are the offsetting entries (e.g., expense account). Each needs accountResourceId, type (DEBIT/CREDIT), and amount.
|
|
1655
|
-
- The API enforces account separation: cash-out accounts cannot be used for cash-in
|
|
1800
|
+
- The API enforces account separation: cash-out accounts cannot be used for cash-in.`,
|
|
1656
1801
|
params: {
|
|
1657
1802
|
reference: { type: 'string', description: 'Reference number' },
|
|
1658
1803
|
valueDate: { type: 'string', description: 'Date (YYYY-MM-DD)' },
|
|
@@ -1749,9 +1894,11 @@ export const TOOL_DEFINITIONS = [
|
|
|
1749
1894
|
listTool('list_cash_transfers', 'List cash transfer entries. Paginated.', 'cash_transfers', (client, off, lim) => listCashTransfers(client, { limit: lim, offset: off })),
|
|
1750
1895
|
{
|
|
1751
1896
|
name: 'create_cash_transfer',
|
|
1752
|
-
description: `Move money between two of YOUR OWN bank/cash accounts (internal transfer
|
|
1753
|
-
|
|
1754
|
-
|
|
1897
|
+
description: `Move money between two of YOUR OWN bank/cash accounts (internal transfer, e.g., main bank to petty cash, USD account to SGD account).
|
|
1898
|
+
WHEN TO USE: petty cash top-ups from bank, inter-account transfers, currency conversions between own accounts.
|
|
1899
|
+
WHEN NOT TO USE: receiving money from external parties (use create_cash_in) or paying external parties (use create_cash_out).
|
|
1900
|
+
- cashOut = source account (money leaves), cashIn = destination account (money arrives).
|
|
1901
|
+
- Each side needs accountResourceId + amount.
|
|
1755
1902
|
- reference is auto-generated if not provided.
|
|
1756
1903
|
- Do NOT send currency/exchangeRate — derived server-side from bank account currencies.`,
|
|
1757
1904
|
params: {
|
|
@@ -2077,13 +2224,14 @@ Available export types: trial-balance, balance-sheet, profit-and-loss, general-l
|
|
|
2077
2224
|
description: `Plan a transaction recipe — run a financial calculator and show what accounts, contacts, and bank accounts are needed. Read-only (no API calls).
|
|
2078
2225
|
Supported recipes: ${RECIPE_TYPES.join(', ')}
|
|
2079
2226
|
Returns: capsule type/name, required accounts, step breakdown (journal/bill/invoice/cash-in/cash-out), and full calculator results.
|
|
2080
|
-
Use this BEFORE execute_recipe to verify requirements. Parameters vary by recipe — see recipe skill docs for per-recipe params
|
|
2227
|
+
Use this BEFORE execute_recipe to verify requirements. Parameters vary by recipe — see recipe skill docs for per-recipe params.
|
|
2228
|
+
CRITICAL: ALWAYS call this tool for ANY calculation involving: depreciation, amortization, ECL/expected credit loss, FX revaluation, lease/IFRS 16, hire purchase, loan schedules, provisions, deferred revenue, fixed deposits, or asset disposal. Never compute these manually.`,
|
|
2081
2229
|
params: {
|
|
2082
2230
|
recipe: { type: 'string', enum: [...RECIPE_TYPES], description: 'Recipe type' },
|
|
2083
2231
|
// Universal params
|
|
2084
2232
|
amount: { type: 'number', description: 'Amount (for amortization, accrued-expense, dividend, ecl, provision, fx-reval)' },
|
|
2085
2233
|
principal: { type: 'number', description: 'Principal (for loan, fixed-deposit)' },
|
|
2086
|
-
startDate: { type: 'string', description: 'Start date YYYY-MM-DD (
|
|
2234
|
+
startDate: { type: 'string', description: 'Start date YYYY-MM-DD (REQUIRED for blueprint generation — default to today if user does not specify)' },
|
|
2087
2235
|
currency: { type: 'string', description: 'Currency code (e.g. SGD, USD)' },
|
|
2088
2236
|
periods: { type: 'number', description: 'Number of periods (for amortization, accrued-expense, leave-accrual)' },
|
|
2089
2237
|
frequency: { type: 'string', enum: ['monthly', 'quarterly', 'annual'], description: 'Frequency (for depreciation, amortization, accrued-expense)' },
|
|
@@ -2609,7 +2757,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2609
2757
|
}),
|
|
2610
2758
|
},
|
|
2611
2759
|
// ══════════════════════════════════════════════════════════════
|
|
2612
|
-
// ──
|
|
2760
|
+
// ── Close Procedure Jobs (offline — no API calls) ────────────
|
|
2613
2761
|
// ══════════════════════════════════════════════════════════════
|
|
2614
2762
|
{
|
|
2615
2763
|
name: 'generate_month_end_blueprint',
|
|
@@ -2619,7 +2767,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2619
2767
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2620
2768
|
},
|
|
2621
2769
|
required: ['period'],
|
|
2622
|
-
group: '
|
|
2770
|
+
group: 'close_jobs',
|
|
2623
2771
|
readOnly: true,
|
|
2624
2772
|
execute: async (_ctx, input) => generateMonthEndBlueprint({
|
|
2625
2773
|
period: input.period,
|
|
@@ -2635,7 +2783,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2635
2783
|
incremental: { type: 'boolean', description: 'Skip month-end phases (assumes months already closed)' },
|
|
2636
2784
|
},
|
|
2637
2785
|
required: ['period'],
|
|
2638
|
-
group: '
|
|
2786
|
+
group: 'close_jobs',
|
|
2639
2787
|
readOnly: true,
|
|
2640
2788
|
execute: async (_ctx, input) => generateQuarterEndBlueprint({
|
|
2641
2789
|
period: input.period,
|
|
@@ -2652,7 +2800,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2652
2800
|
incremental: { type: 'boolean', description: 'Skip quarter/month phases (assumes already closed)' },
|
|
2653
2801
|
},
|
|
2654
2802
|
required: ['period'],
|
|
2655
|
-
group: '
|
|
2803
|
+
group: 'close_jobs',
|
|
2656
2804
|
readOnly: true,
|
|
2657
2805
|
execute: async (_ctx, input) => generateYearEndBlueprint({
|
|
2658
2806
|
period: input.period,
|
|
@@ -2669,7 +2817,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2669
2817
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2670
2818
|
},
|
|
2671
2819
|
required: [],
|
|
2672
|
-
group: '
|
|
2820
|
+
group: 'close_jobs',
|
|
2673
2821
|
readOnly: true,
|
|
2674
2822
|
execute: async (_ctx, input) => generateBankReconBlueprint({
|
|
2675
2823
|
account: input.account,
|
|
@@ -2685,7 +2833,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2685
2833
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2686
2834
|
},
|
|
2687
2835
|
required: ['period'],
|
|
2688
|
-
group: '
|
|
2836
|
+
group: 'close_jobs',
|
|
2689
2837
|
readOnly: true,
|
|
2690
2838
|
execute: async (_ctx, input) => generateGstVatBlueprint({
|
|
2691
2839
|
period: input.period,
|
|
@@ -2700,7 +2848,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2700
2848
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2701
2849
|
},
|
|
2702
2850
|
required: [],
|
|
2703
|
-
group: '
|
|
2851
|
+
group: 'operational_jobs',
|
|
2704
2852
|
readOnly: true,
|
|
2705
2853
|
execute: async (_ctx, input) => generatePaymentRunBlueprint({
|
|
2706
2854
|
dueBefore: input.dueBefore,
|
|
@@ -2715,7 +2863,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2715
2863
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2716
2864
|
},
|
|
2717
2865
|
required: [],
|
|
2718
|
-
group: '
|
|
2866
|
+
group: 'operational_jobs',
|
|
2719
2867
|
readOnly: true,
|
|
2720
2868
|
execute: async (_ctx, input) => generateCreditControlBlueprint({
|
|
2721
2869
|
overdueDays: input.overdueDays,
|
|
@@ -2731,7 +2879,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2731
2879
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2732
2880
|
},
|
|
2733
2881
|
required: [],
|
|
2734
|
-
group: '
|
|
2882
|
+
group: 'operational_jobs',
|
|
2735
2883
|
readOnly: true,
|
|
2736
2884
|
execute: async (_ctx, input) => generateSupplierReconBlueprint({
|
|
2737
2885
|
supplier: input.supplier,
|
|
@@ -2747,7 +2895,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2747
2895
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2748
2896
|
},
|
|
2749
2897
|
required: ['period'],
|
|
2750
|
-
group: '
|
|
2898
|
+
group: 'close_jobs',
|
|
2751
2899
|
readOnly: true,
|
|
2752
2900
|
execute: async (_ctx, input) => generateAuditPrepBlueprint({
|
|
2753
2901
|
period: input.period,
|
|
@@ -2761,7 +2909,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2761
2909
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2762
2910
|
},
|
|
2763
2911
|
required: [],
|
|
2764
|
-
group: '
|
|
2912
|
+
group: 'operational_jobs',
|
|
2765
2913
|
readOnly: true,
|
|
2766
2914
|
execute: async (_ctx, input) => generateFaReviewBlueprint({
|
|
2767
2915
|
currency: input.currency,
|
|
@@ -2774,7 +2922,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2774
2922
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2775
2923
|
},
|
|
2776
2924
|
required: [],
|
|
2777
|
-
group: '
|
|
2925
|
+
group: 'operational_jobs',
|
|
2778
2926
|
readOnly: true,
|
|
2779
2927
|
execute: async (_ctx, input) => generateDocumentCollectionBlueprint({
|
|
2780
2928
|
currency: input.currency,
|
|
@@ -2789,7 +2937,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2789
2937
|
currency: { type: 'string', description: 'Base currency (e.g., "SGD"). Defaults to SGD.' },
|
|
2790
2938
|
},
|
|
2791
2939
|
required: [],
|
|
2792
|
-
group: '
|
|
2940
|
+
group: 'operational_jobs',
|
|
2793
2941
|
readOnly: true,
|
|
2794
2942
|
execute: async (_ctx, input) => generateStatutoryFilingBlueprint({
|
|
2795
2943
|
ya: input.ya,
|
|
@@ -3189,12 +3337,12 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3189
3337
|
return handlePagination((off, lim) => searchScheduledTransactions(ctx.client, {
|
|
3190
3338
|
filter: Object.keys(filter).length > 0 ? filter : undefined,
|
|
3191
3339
|
limit: lim, offset: off,
|
|
3192
|
-
sort: { sortBy: [sortBy ?? '
|
|
3340
|
+
sort: { sortBy: [sortBy ?? 'startDate'], order: (sortOrder ?? 'DESC') },
|
|
3193
3341
|
}), limit, offset, 20);
|
|
3194
3342
|
},
|
|
3195
3343
|
},
|
|
3196
3344
|
// ══════════════════════════════════════════════════════════════
|
|
3197
|
-
// ── Reports (
|
|
3345
|
+
// ── Financial Reports (continued) ─────────────────────────
|
|
3198
3346
|
// ══════════════════════════════════════════════════════════════
|
|
3199
3347
|
{
|
|
3200
3348
|
name: 'generate_vat_ledger',
|
|
@@ -3204,7 +3352,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3204
3352
|
endDate: { type: 'string', description: 'End date (YYYY-MM-DD)' },
|
|
3205
3353
|
},
|
|
3206
3354
|
required: ['startDate', 'endDate'],
|
|
3207
|
-
group: '
|
|
3355
|
+
group: 'financial_reports',
|
|
3208
3356
|
readOnly: true,
|
|
3209
3357
|
execute: async (ctx, input) => generateVatLedger(ctx.client, {
|
|
3210
3358
|
startDate: input.startDate,
|
|
@@ -3222,7 +3370,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3222
3370
|
compareCount: { type: 'number', description: 'Number of comparison periods' },
|
|
3223
3371
|
},
|
|
3224
3372
|
required: ['primarySnapshotStartDate', 'primarySnapshotEndDate'],
|
|
3225
|
-
group: '
|
|
3373
|
+
group: 'financial_reports',
|
|
3226
3374
|
readOnly: true,
|
|
3227
3375
|
execute: async (ctx, input) => generateEquityMovement(ctx.client, input),
|
|
3228
3376
|
},
|
|
@@ -3234,7 +3382,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3234
3382
|
currencyCode: { type: 'string', description: 'Currency code (e.g., "SGD")' },
|
|
3235
3383
|
},
|
|
3236
3384
|
required: ['primarySnapshotDate'],
|
|
3237
|
-
group: '
|
|
3385
|
+
group: 'operational_reports',
|
|
3238
3386
|
readOnly: true,
|
|
3239
3387
|
execute: async (ctx, input) => generateBankBalanceSummary(ctx.client, {
|
|
3240
3388
|
primarySnapshotDate: input.primarySnapshotDate,
|
|
@@ -3252,7 +3400,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3252
3400
|
tags: { type: 'array', items: { type: 'string' }, description: 'Filter by tags' },
|
|
3253
3401
|
},
|
|
3254
3402
|
required: ['bankAccountResourceId', 'primarySnapshotStartDate', 'primarySnapshotEndDate'],
|
|
3255
|
-
group: '
|
|
3403
|
+
group: 'operational_reports',
|
|
3256
3404
|
readOnly: true,
|
|
3257
3405
|
execute: async (ctx, input) => generateBankReconSummary(ctx.client, input),
|
|
3258
3406
|
},
|
|
@@ -3267,7 +3415,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3267
3415
|
currencyCode: { type: 'string', description: 'Currency code' },
|
|
3268
3416
|
},
|
|
3269
3417
|
required: ['bankAccountResourceId', 'primarySnapshotStartDate', 'primarySnapshotEndDate', 'filter'],
|
|
3270
|
-
group: '
|
|
3418
|
+
group: 'operational_reports',
|
|
3271
3419
|
readOnly: true,
|
|
3272
3420
|
execute: async (ctx, input) => generateBankReconDetails(ctx.client, input),
|
|
3273
3421
|
},
|
|
@@ -3281,7 +3429,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3281
3429
|
currencyCode: { type: 'string', description: 'Currency code' },
|
|
3282
3430
|
},
|
|
3283
3431
|
required: ['primarySnapshotStartDate', 'primarySnapshotEndDate', 'groupBy'],
|
|
3284
|
-
group: '
|
|
3432
|
+
group: 'operational_reports',
|
|
3285
3433
|
readOnly: true,
|
|
3286
3434
|
execute: async (ctx, input) => generateFaSummary(ctx.client, input),
|
|
3287
3435
|
},
|
|
@@ -3295,7 +3443,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3295
3443
|
currencyCode: { type: 'string', description: 'Currency code' },
|
|
3296
3444
|
},
|
|
3297
3445
|
required: ['primarySnapshotStartDate', 'primarySnapshotEndDate'],
|
|
3298
|
-
group: '
|
|
3446
|
+
group: 'operational_reports',
|
|
3299
3447
|
readOnly: true,
|
|
3300
3448
|
execute: async (ctx, input) => generateFaReconSummary(ctx.client, input),
|
|
3301
3449
|
},
|
|
@@ -3306,7 +3454,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3306
3454
|
endDate: { type: 'string', description: 'Report date (YYYY-MM-DD) — point-in-time snapshot' },
|
|
3307
3455
|
},
|
|
3308
3456
|
required: ['endDate'],
|
|
3309
|
-
group: '
|
|
3457
|
+
group: 'operational_reports',
|
|
3310
3458
|
readOnly: true,
|
|
3311
3459
|
execute: async (ctx, input) => generateArReport(ctx.client, { endDate: input.endDate }),
|
|
3312
3460
|
},
|
|
@@ -3535,7 +3683,7 @@ Works for invoices, bills, customer credit notes, supplier credit notes, and jou
|
|
|
3535
3683
|
resourceId: { type: 'string', description: 'Transaction resourceId' },
|
|
3536
3684
|
},
|
|
3537
3685
|
required: ['transactionType', 'resourceId'],
|
|
3538
|
-
group: '
|
|
3686
|
+
group: 'search',
|
|
3539
3687
|
readOnly: true,
|
|
3540
3688
|
execute: async (ctx, input) => {
|
|
3541
3689
|
const type = input.transactionType;
|
|
@@ -3602,18 +3750,18 @@ Works for invoices, bills, customer credit notes, supplier credit notes, and jou
|
|
|
3602
3750
|
const results = [];
|
|
3603
3751
|
for (const item of items) {
|
|
3604
3752
|
try {
|
|
3605
|
-
const
|
|
3753
|
+
const merged = await fetchAndMerge(ctx.client, item.type, item.resourceId, {});
|
|
3606
3754
|
if (item.type === 'invoice') {
|
|
3607
|
-
await finalizeInvoice(ctx.client, item.resourceId,
|
|
3755
|
+
await finalizeInvoice(ctx.client, item.resourceId, merged);
|
|
3608
3756
|
}
|
|
3609
3757
|
else if (item.type === 'bill') {
|
|
3610
|
-
await finalizeBill(ctx.client, item.resourceId,
|
|
3758
|
+
await finalizeBill(ctx.client, item.resourceId, merged);
|
|
3611
3759
|
}
|
|
3612
3760
|
else if (item.type === 'customer_credit_note') {
|
|
3613
|
-
await finalizeCustomerCreditNote(ctx.client, item.resourceId,
|
|
3761
|
+
await finalizeCustomerCreditNote(ctx.client, item.resourceId, merged);
|
|
3614
3762
|
}
|
|
3615
3763
|
else if (item.type === 'supplier_credit_note') {
|
|
3616
|
-
await finalizeSupplierCreditNote(ctx.client, item.resourceId,
|
|
3764
|
+
await finalizeSupplierCreditNote(ctx.client, item.resourceId, merged);
|
|
3617
3765
|
}
|
|
3618
3766
|
results.push({ ...item, success: true });
|
|
3619
3767
|
}
|