jaz-clio 4.30.2 → 4.30.6
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 +4 -1
- package/assets/skills/api/references/endpoints.md +16 -11
- package/assets/skills/api/references/full-api-surface.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/bank-rules.js +37 -6
- package/dist/commands/bills.js +2 -2
- package/dist/commands/customer-credit-notes.js +2 -2
- package/dist/commands/fixed-assets.js +93 -14
- package/dist/commands/invoices.js +2 -2
- package/dist/commands/journals.js +2 -2
- package/dist/commands/subscriptions.js +49 -6
- package/dist/commands/supplier-credit-notes.js +2 -2
- package/dist/core/api/attachments.js +4 -1
- package/dist/core/registry/tools.js +88 -110
- package/package.json +1 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 4.30.
|
|
3
|
+
version: 4.30.6
|
|
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.
|
|
@@ -238,6 +238,9 @@ Bills, invoices, and credit notes share identical mandatory field specs. Adding
|
|
|
238
238
|
### Fixed Assets
|
|
239
239
|
91. **Fixed asset search does NOT support `createdAt` sort** — Valid sort fields: `resourceId`, `name`, `purchaseDate`, `typeName`, `purchaseAmount`, `bookValueNetBookValueAmount`, `depreciationMethod`, `status`. Using `createdAt` returns 422. Default to `purchaseDate` DESC.
|
|
240
240
|
92. **Fixed asset disposal/sale/transfer use different endpoint patterns** — Discard: `POST /discard-fixed-assets/:id` (body includes `resourceId` + dates). Mark sold: `POST /mark-as-sold/fixed-assets` (body-only, no path param). Transfer: `POST /transfer-fixed-assets` (body-only). Undo: `POST /undo-disposal/fixed-assets/:id`.
|
|
241
|
+
92a. **Two ways to create fixed assets** — (1) **Register** (`POST /fixed-assets`): links to an existing purchase bill or journal. ACTIVE assets require `purchaseBusinessTransactionType` (`PURCHASE` or `JOURNAL_MANUAL`) and `purchaseBusinessTransactionResourceId`. Draft assets skip this validation. (2) **Transfer** (`POST /transfer-fixed-assets`): standalone asset entry, no linked transaction needed.
|
|
242
|
+
92b. **`saveAsDraft` defaults to `true`** — To create an ACTIVE fixed asset, pass `saveAsDraft: false` with ALL required fields: `name`, `category`, `typeCode`, `purchaseAmount`, `purchaseDate`, `purchaseAssetAccountResourceId`, `depreciationMethod`, `effectiveLife`, and for `STRAIGHT_LINE`: `depreciationStartDate`, `accumulatedDepreciationAccountResourceId`, `depreciationExpenseAccountResourceId`. Omitting any returns 422.
|
|
243
|
+
92c. **Valid enums** — `depreciationMethod`: `STRAIGHT_LINE`, `NO_DEPRECIATION`. `category`: `TANGIBLE`, `INTANGIBLE`. Optional string fields (`purchaseBusinessTransactionResourceId`, `accumulatedDepreciationAccountResourceId`, `capsuleResourceId`) can be safely omitted — the API ignores empty values.
|
|
241
244
|
|
|
242
245
|
### Subscriptions & Scheduled Transactions
|
|
243
246
|
93. **Subscription endpoints are under `/scheduled/subscriptions`** — List, GET, POST, PUT, DELETE all at `/api/v1/scheduled/subscriptions[/:id]`. Cancel is at `/api/v1/scheduled/cancel-subscriptions/:id` (different path pattern).
|
|
@@ -1571,22 +1571,27 @@ POST /api/v1/fixed-assets
|
|
|
1571
1571
|
```json
|
|
1572
1572
|
{
|
|
1573
1573
|
"name": "Office Laptop - MacBook Pro",
|
|
1574
|
+
"purchaseAmount": 3500.00,
|
|
1574
1575
|
"purchaseDate": "2026-01-15",
|
|
1575
|
-
"purchaseCost": 3500.00,
|
|
1576
|
-
"depreciationMethod": "STRAIGHT_LINE",
|
|
1577
|
-
"usefulLifeMonths": 36,
|
|
1578
1576
|
"depreciationStartDate": "2026-01-15",
|
|
1579
|
-
"
|
|
1580
|
-
"
|
|
1581
|
-
"
|
|
1582
|
-
"
|
|
1577
|
+
"purchaseAssetAccountResourceId": "fixed-asset-coa-uuid",
|
|
1578
|
+
"depreciationMethod": "STRAIGHT_LINE",
|
|
1579
|
+
"effectiveLife": 36,
|
|
1580
|
+
"depreciableValueResidualAmount": 0,
|
|
1581
|
+
"depreciationExpenseAccountResourceId": "depreciation-expense-coa-uuid",
|
|
1582
|
+
"accumulatedDepreciationAccountResourceId": "accumulated-depreciation-coa-uuid",
|
|
1583
|
+
"saveAsDraft": true
|
|
1583
1584
|
}
|
|
1584
1585
|
```
|
|
1585
1586
|
|
|
1586
|
-
- `
|
|
1587
|
-
- `
|
|
1588
|
-
- `
|
|
1589
|
-
-
|
|
1587
|
+
- `purchaseDate` and `depreciationStartDate`: YYYY-MM-DD (both required — omitting returns 422)
|
|
1588
|
+
- `purchaseAmount`: Purchase cost (required)
|
|
1589
|
+
- `purchaseAssetAccountResourceId`: Asset account (required)
|
|
1590
|
+
- `depreciationMethod`: `"STRAIGHT_LINE"` or `"NO_DEPRECIATION"`
|
|
1591
|
+
- `effectiveLife`: Integer (months)
|
|
1592
|
+
- `category`: `"TANGIBLE"` or `"INTANGIBLE"`
|
|
1593
|
+
- `saveAsDraft`: Defaults to `true`. Set `false` to activate — requires `purchaseBusinessTransactionType` (`PURCHASE`/`JOURNAL_MANUAL`) + `purchaseBusinessTransactionResourceId`
|
|
1594
|
+
- Optional string fields (`purchaseBusinessTransactionResourceId`, `capsuleResourceId`) can be safely omitted for drafts
|
|
1590
1595
|
|
|
1591
1596
|
### Response
|
|
1592
1597
|
```json
|
|
@@ -426,7 +426,7 @@
|
|
|
426
426
|
### Fixed Assets
|
|
427
427
|
| Method | Path | Description |
|
|
428
428
|
|--------|------|-------------|
|
|
429
|
-
| POST | `/fixed-assets` | Create (
|
|
429
|
+
| POST | `/fixed-assets` | Create (requires `purchaseDate` + `depreciationStartDate`) |
|
|
430
430
|
| GET | `/fixed-assets` | List |
|
|
431
431
|
| GET | `/fixed-assets/:resourceId` | Get by ID |
|
|
432
432
|
| POST | `/fixed-assets/search` | Search |
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-conversion
|
|
3
|
-
version: 4.30.
|
|
3
|
+
version: 4.30.6
|
|
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.6
|
|
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.6
|
|
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.
|
|
@@ -63,16 +63,37 @@ export function registerBankRulesCommand(program) {
|
|
|
63
63
|
})(opts));
|
|
64
64
|
cmd
|
|
65
65
|
.command('create')
|
|
66
|
-
.description('Create a bank rule')
|
|
67
|
-
.option('--
|
|
66
|
+
.description('Create a bank reconciliation rule')
|
|
67
|
+
.option('--name <name>', 'Rule name (required)')
|
|
68
|
+
.option('--action-type <type>', 'Action type, e.g. RECONCILE_WITH_DIRECT_CASH_ENTRY (required)')
|
|
69
|
+
.option('--account <resourceId>', 'Bank account resourceId this rule applies to (required)')
|
|
70
|
+
.option('--config <json>', 'Rule configuration as JSON (allocation type, accounts, percentages, tax)')
|
|
71
|
+
.option('--input <file>', 'Read full request body from JSON file (overrides flags)')
|
|
68
72
|
.option('--api-key <key>', 'API key')
|
|
69
73
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
70
74
|
.option('--json', 'JSON output')
|
|
71
75
|
.action(apiAction(async (client, opts) => {
|
|
72
|
-
|
|
76
|
+
let body = readBodyInput(opts);
|
|
73
77
|
if (!body) {
|
|
74
|
-
|
|
75
|
-
|
|
78
|
+
if (!opts.name || !opts.actionType || !opts.account) {
|
|
79
|
+
console.error(chalk.red('Required: --name, --action-type, --account'));
|
|
80
|
+
console.error(chalk.dim('Or use --input <file> to provide full JSON body.'));
|
|
81
|
+
process.exit(1);
|
|
82
|
+
}
|
|
83
|
+
body = {
|
|
84
|
+
name: opts.name,
|
|
85
|
+
actionType: opts.actionType,
|
|
86
|
+
appliesToReconciliationAccountResourceId: opts.account,
|
|
87
|
+
};
|
|
88
|
+
if (opts.config) {
|
|
89
|
+
try {
|
|
90
|
+
body.configuration = JSON.parse(opts.config);
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
console.error(chalk.red('Invalid --config JSON'));
|
|
94
|
+
process.exit(1);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
76
97
|
}
|
|
77
98
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
78
99
|
const res = await createBankRule(client, body);
|
|
@@ -87,7 +108,8 @@ export function registerBankRulesCommand(program) {
|
|
|
87
108
|
.command('update <resourceId>')
|
|
88
109
|
.description('Update a bank rule')
|
|
89
110
|
.option('--name <name>', 'New name')
|
|
90
|
-
.option('--
|
|
111
|
+
.option('--config <json>', 'Updated configuration as JSON')
|
|
112
|
+
.option('--input <file>', 'Read full update body from JSON file (overrides flags)')
|
|
91
113
|
.option('--api-key <key>', 'API key')
|
|
92
114
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
93
115
|
.option('--json', 'JSON output')
|
|
@@ -101,6 +123,15 @@ export function registerBankRulesCommand(program) {
|
|
|
101
123
|
data = {};
|
|
102
124
|
if (opts.name !== undefined)
|
|
103
125
|
data.name = opts.name;
|
|
126
|
+
if (opts.config) {
|
|
127
|
+
try {
|
|
128
|
+
data.configuration = JSON.parse(opts.config);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
console.error(chalk.red('Invalid --config JSON'));
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
104
135
|
}
|
|
105
136
|
const res = await updateBankRule(client, resourceId, data);
|
|
106
137
|
if (opts.json) {
|
package/dist/commands/bills.js
CHANGED
|
@@ -374,7 +374,7 @@ export function registerBillsCommand(program) {
|
|
|
374
374
|
let attachmentCount = 0;
|
|
375
375
|
try {
|
|
376
376
|
const attRes = await listAttachments(client, 'bills', b.resourceId);
|
|
377
|
-
attachmentCount =
|
|
377
|
+
attachmentCount = attRes.data.length;
|
|
378
378
|
}
|
|
379
379
|
catch {
|
|
380
380
|
// Attachment listing may fail for some bills — don't block the report
|
|
@@ -558,7 +558,7 @@ export function registerBillsCommand(program) {
|
|
|
558
558
|
const billRes = await getBill(client, resourceId);
|
|
559
559
|
const bill = billRes.data;
|
|
560
560
|
const attRes = await listAttachments(client, 'bills', resourceId);
|
|
561
|
-
const attachments =
|
|
561
|
+
const attachments = attRes.data;
|
|
562
562
|
if (opts.json) {
|
|
563
563
|
console.log(JSON.stringify({
|
|
564
564
|
billResourceId: resourceId,
|
|
@@ -345,7 +345,7 @@ export function registerCustomerCreditNotesCommand(program) {
|
|
|
345
345
|
let attachmentCount = 0;
|
|
346
346
|
try {
|
|
347
347
|
const attRes = await listAttachments(client, 'customer-credit-notes', cn.resourceId);
|
|
348
|
-
attachmentCount =
|
|
348
|
+
attachmentCount = attRes.data.length;
|
|
349
349
|
}
|
|
350
350
|
catch { /* Attachment listing may fail — don't block the report */ }
|
|
351
351
|
return buildDraftReport(cn, CREDIT_NOTE_REQUIRED_FIELDS, attachmentCount);
|
|
@@ -496,7 +496,7 @@ export function registerCustomerCreditNotesCommand(program) {
|
|
|
496
496
|
const cnRes = await getCustomerCreditNote(client, resourceId);
|
|
497
497
|
const cn = cnRes.data;
|
|
498
498
|
const attRes = await listAttachments(client, 'customer-credit-notes', resourceId);
|
|
499
|
-
const attachments =
|
|
499
|
+
const attachments = attRes.data;
|
|
500
500
|
if (opts.json) {
|
|
501
501
|
console.log(JSON.stringify({ creditNoteResourceId: resourceId, creditNoteReference: cn.reference || null, attachments }, null, 2));
|
|
502
502
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { listFixedAssets, getFixedAsset, searchFixedAssets, createFixedAsset, updateFixedAsset, deleteFixedAsset, discardFixedAsset, markFixedAssetSold, transferFixedAsset, undoFixedAssetDisposal, } from '../core/api/fixed-assets.js';
|
|
3
3
|
import { apiAction } from './api-action.js';
|
|
4
|
-
import { parsePositiveInt, parseNonNegativeInt, readBodyInput, parseCustomFields } from './parsers.js';
|
|
4
|
+
import { parsePositiveInt, parseNonNegativeInt, parseMoney, readBodyInput, parseCustomFields } from './parsers.js';
|
|
5
5
|
import { paginatedFetch } from './pagination.js';
|
|
6
6
|
import { outputList } from './output.js';
|
|
7
7
|
import { formatId, formatCurrency } from './format-helpers.js';
|
|
@@ -84,16 +84,69 @@ export function registerFixedAssetsCommand(program) {
|
|
|
84
84
|
cmd
|
|
85
85
|
.command('create')
|
|
86
86
|
.description('Register a new fixed asset')
|
|
87
|
-
.option('--
|
|
88
|
-
.option('--
|
|
87
|
+
.option('--name <name>', 'Asset name (required)')
|
|
88
|
+
.option('--type <typeName>', 'Asset type (e.g., "Buildings", "Vehicles", "Furniture")')
|
|
89
|
+
.option('--category <cat>', 'TANGIBLE or INTANGIBLE')
|
|
90
|
+
.option('--amount <n>', 'Purchase cost (required)', parseMoney)
|
|
91
|
+
.option('--date <YYYY-MM-DD>', 'Purchase date (required)')
|
|
92
|
+
.option('--depreciation-start <YYYY-MM-DD>', 'Depreciation start date (required)')
|
|
93
|
+
.option('--asset-account <id>', 'Asset account resourceId (required)')
|
|
94
|
+
.option('--depreciation-method <method>', 'STRAIGHT_LINE or NO_DEPRECIATION')
|
|
95
|
+
.option('--effective-life <months>', 'Effective life in months', parsePositiveInt)
|
|
96
|
+
.option('--residual <n>', 'Residual/salvage value', parseMoney)
|
|
97
|
+
.option('--depreciation-expense-account <id>', 'Depreciation expense account resourceId')
|
|
98
|
+
.option('--accumulated-depreciation-account <id>', 'Accumulated depreciation account resourceId')
|
|
99
|
+
.option('--purchase-bt-type <type>', 'Linked purchase BT type: PURCHASE (bill) or JOURNAL_MANUAL')
|
|
100
|
+
.option('--purchase-bt-id <id>', 'Linked purchase BT resourceId (required for ACTIVE assets)')
|
|
101
|
+
.option('--notes <text>', 'Internal notes')
|
|
102
|
+
.option('--draft', 'Save as draft (default: true)')
|
|
103
|
+
.option('--finalize', 'Activate immediately (saveAsDraft: false)')
|
|
104
|
+
.option('--tag <name>', 'Tag name')
|
|
105
|
+
.option('--custom-fields <json>', 'Custom field values as JSON array')
|
|
106
|
+
.option('--input <file>', 'Read full request body from JSON file (overrides flags)')
|
|
89
107
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
90
108
|
.option('--api-key <key>', 'API key')
|
|
91
109
|
.option('--json', 'JSON output')
|
|
92
110
|
.action(apiAction(async (client, opts) => {
|
|
93
|
-
|
|
111
|
+
let body = readBodyInput(opts);
|
|
94
112
|
if (!body) {
|
|
95
|
-
|
|
96
|
-
|
|
113
|
+
// Build body from flags
|
|
114
|
+
if (!opts.name || opts.amount === undefined || !opts.date || !opts.assetAccount || !opts.depreciationStart) {
|
|
115
|
+
console.error(chalk.red('Required: --name, --amount, --date, --depreciation-start, --asset-account'));
|
|
116
|
+
console.error(chalk.dim('Or use --input <file> to provide full JSON body.'));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
}
|
|
119
|
+
body = {
|
|
120
|
+
name: opts.name,
|
|
121
|
+
purchaseAmount: opts.amount,
|
|
122
|
+
purchaseDate: opts.date,
|
|
123
|
+
depreciationStartDate: opts.depreciationStart,
|
|
124
|
+
purchaseAssetAccountResourceId: opts.assetAccount,
|
|
125
|
+
};
|
|
126
|
+
if (opts.type)
|
|
127
|
+
body.typeName = opts.type;
|
|
128
|
+
if (opts.category)
|
|
129
|
+
body.category = opts.category;
|
|
130
|
+
if (opts.depreciationMethod)
|
|
131
|
+
body.depreciationMethod = opts.depreciationMethod;
|
|
132
|
+
if (opts.effectiveLife)
|
|
133
|
+
body.effectiveLife = opts.effectiveLife;
|
|
134
|
+
if (opts.residual !== undefined)
|
|
135
|
+
body.depreciableValueResidualAmount = opts.residual;
|
|
136
|
+
if (opts.depreciationExpenseAccount)
|
|
137
|
+
body.depreciationExpenseAccountResourceId = opts.depreciationExpenseAccount;
|
|
138
|
+
if (opts.accumulatedDepreciationAccount)
|
|
139
|
+
body.accumulatedDepreciationAccountResourceId = opts.accumulatedDepreciationAccount;
|
|
140
|
+
if (opts.purchaseBtType)
|
|
141
|
+
body.purchaseBusinessTransactionType = opts.purchaseBtType;
|
|
142
|
+
if (opts.purchaseBtId)
|
|
143
|
+
body.purchaseBusinessTransactionResourceId = opts.purchaseBtId;
|
|
144
|
+
if (opts.notes)
|
|
145
|
+
body.internalNotes = opts.notes;
|
|
146
|
+
if (opts.tag)
|
|
147
|
+
body.tags = [opts.tag];
|
|
148
|
+
if (opts.finalize)
|
|
149
|
+
body.saveAsDraft = false;
|
|
97
150
|
}
|
|
98
151
|
if (opts.customFields)
|
|
99
152
|
body.customFields = parseCustomFields(opts.customFields);
|
|
@@ -124,8 +177,12 @@ export function registerFixedAssetsCommand(program) {
|
|
|
124
177
|
.command('update <resourceId>')
|
|
125
178
|
.description('Update a fixed asset')
|
|
126
179
|
.option('--name <name>', 'New name')
|
|
180
|
+
.option('--notes <text>', 'Internal notes')
|
|
181
|
+
.option('--depreciation-method <method>', 'STRAIGHT_LINE or NO_DEPRECIATION')
|
|
182
|
+
.option('--effective-life <months>', 'Effective life in months', parsePositiveInt)
|
|
183
|
+
.option('--tag <name>', 'Tag name')
|
|
127
184
|
.option('--custom-fields <json>', 'Custom field values as JSON array')
|
|
128
|
-
.option('--input <file>', 'Read full update body from JSON file')
|
|
185
|
+
.option('--input <file>', 'Read full update body from JSON file (overrides flags)')
|
|
129
186
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
130
187
|
.option('--api-key <key>', 'API key')
|
|
131
188
|
.option('--json', 'JSON output')
|
|
@@ -139,9 +196,17 @@ export function registerFixedAssetsCommand(program) {
|
|
|
139
196
|
data = {};
|
|
140
197
|
if (opts.name !== undefined)
|
|
141
198
|
data.name = opts.name;
|
|
142
|
-
if (opts.
|
|
143
|
-
data.
|
|
199
|
+
if (opts.notes !== undefined)
|
|
200
|
+
data.internalNotes = opts.notes;
|
|
201
|
+
if (opts.depreciationMethod)
|
|
202
|
+
data.depreciationMethod = opts.depreciationMethod;
|
|
203
|
+
if (opts.effectiveLife)
|
|
204
|
+
data.effectiveLife = opts.effectiveLife;
|
|
205
|
+
if (opts.tag)
|
|
206
|
+
data.tags = [opts.tag];
|
|
144
207
|
}
|
|
208
|
+
if (opts.customFields)
|
|
209
|
+
data.customFields = parseCustomFields(opts.customFields);
|
|
145
210
|
const res = await updateFixedAsset(client, resourceId, data);
|
|
146
211
|
if (opts.json) {
|
|
147
212
|
console.log(JSON.stringify(res.data, null, 2));
|
|
@@ -191,16 +256,30 @@ export function registerFixedAssetsCommand(program) {
|
|
|
191
256
|
}));
|
|
192
257
|
cmd
|
|
193
258
|
.command('transfer')
|
|
194
|
-
.description('Transfer a fixed asset')
|
|
195
|
-
.option('--
|
|
259
|
+
.description('Transfer a fixed asset to a different type/account')
|
|
260
|
+
.option('--id <resourceId>', 'Source fixed asset resourceId (required)')
|
|
261
|
+
.option('--name <name>', 'New asset name')
|
|
262
|
+
.option('--type <typeName>', 'New asset type')
|
|
263
|
+
.option('--asset-account <id>', 'New asset account resourceId')
|
|
264
|
+
.option('--input <file>', 'Read full transfer body from JSON file (overrides flags)')
|
|
196
265
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
197
266
|
.option('--api-key <key>', 'API key')
|
|
198
267
|
.option('--json', 'JSON output')
|
|
199
268
|
.action(apiAction(async (client, opts) => {
|
|
200
|
-
|
|
269
|
+
let body = readBodyInput(opts);
|
|
201
270
|
if (!body) {
|
|
202
|
-
|
|
203
|
-
|
|
271
|
+
if (!opts.id) {
|
|
272
|
+
console.error(chalk.red('Required: --id <resourceId>'));
|
|
273
|
+
console.error(chalk.dim('Or use --input <file> to provide full JSON body.'));
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
body = { resourceId: opts.id };
|
|
277
|
+
if (opts.name)
|
|
278
|
+
body.name = opts.name;
|
|
279
|
+
if (opts.type)
|
|
280
|
+
body.typeName = opts.type;
|
|
281
|
+
if (opts.assetAccount)
|
|
282
|
+
body.purchaseAssetAccountResourceId = opts.assetAccount;
|
|
204
283
|
}
|
|
205
284
|
const res = await transferFixedAsset(client, body);
|
|
206
285
|
if (opts.json) {
|
|
@@ -387,7 +387,7 @@ export function registerInvoicesCommand(program) {
|
|
|
387
387
|
let attachmentCount = 0;
|
|
388
388
|
try {
|
|
389
389
|
const attRes = await listAttachments(client, 'invoices', inv.resourceId);
|
|
390
|
-
attachmentCount =
|
|
390
|
+
attachmentCount = attRes.data.length;
|
|
391
391
|
}
|
|
392
392
|
catch { /* Attachment listing may fail — don't block the report */ }
|
|
393
393
|
return buildDraftReport(inv, INVOICE_REQUIRED_FIELDS, attachmentCount);
|
|
@@ -539,7 +539,7 @@ export function registerInvoicesCommand(program) {
|
|
|
539
539
|
const invRes = await getInvoice(client, resourceId);
|
|
540
540
|
const inv = invRes.data;
|
|
541
541
|
const attRes = await listAttachments(client, 'invoices', resourceId);
|
|
542
|
-
const attachments =
|
|
542
|
+
const attachments = attRes.data;
|
|
543
543
|
if (opts.json) {
|
|
544
544
|
console.log(JSON.stringify({ invoiceResourceId: resourceId, invoiceReference: inv.reference || null, attachments }, null, 2));
|
|
545
545
|
}
|
|
@@ -242,7 +242,7 @@ export function registerJournalsCommand(program) {
|
|
|
242
242
|
let attachmentCount = 0;
|
|
243
243
|
try {
|
|
244
244
|
const attRes = await listAttachments(client, 'journals', j.resourceId);
|
|
245
|
-
attachmentCount =
|
|
245
|
+
attachmentCount = attRes.data.length;
|
|
246
246
|
}
|
|
247
247
|
catch { /* Attachment listing may fail — don't block the report */ }
|
|
248
248
|
return buildDraftReport(j, JOURNAL_REQUIRED_FIELDS, attachmentCount, 'journalEntries');
|
|
@@ -377,7 +377,7 @@ export function registerJournalsCommand(program) {
|
|
|
377
377
|
const journalRes = await getJournal(client, resourceId);
|
|
378
378
|
const journal = journalRes.data;
|
|
379
379
|
const attRes = await listAttachments(client, 'journals', resourceId);
|
|
380
|
-
const attachments =
|
|
380
|
+
const attachments = attRes.data;
|
|
381
381
|
if (opts.json) {
|
|
382
382
|
console.log(JSON.stringify({ journalResourceId: resourceId, journalReference: journal.reference || null, attachments }, null, 2));
|
|
383
383
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { listSubscriptions, getSubscription, createSubscription, updateSubscription, deleteSubscription, cancelSubscription, searchScheduledTransactions, } from '../core/api/subscriptions.js';
|
|
3
3
|
import { apiAction } from './api-action.js';
|
|
4
|
-
import { parsePositiveInt, parseNonNegativeInt, readBodyInput } from './parsers.js';
|
|
4
|
+
import { parsePositiveInt, parseNonNegativeInt, parseMoney, readBodyInput } from './parsers.js';
|
|
5
5
|
import { paginatedFetch } from './pagination.js';
|
|
6
6
|
import { outputList } from './output.js';
|
|
7
7
|
import { formatId } from './format-helpers.js';
|
|
@@ -56,16 +56,59 @@ export function registerSubscriptionsCommand(program) {
|
|
|
56
56
|
})(opts));
|
|
57
57
|
cmd
|
|
58
58
|
.command('create')
|
|
59
|
-
.description('Create a subscription')
|
|
60
|
-
.option('--
|
|
59
|
+
.description('Create a recurring subscription')
|
|
60
|
+
.option('--type <type>', 'SALE (invoice) or PURCHASE (bill) (required)')
|
|
61
|
+
.option('--interval <interval>', 'WEEKLY, MONTHLY, or YEARLY (required)')
|
|
62
|
+
.option('--start-date <YYYY-MM-DD>', 'Start date (required)')
|
|
63
|
+
.option('--end-date <YYYY-MM-DD>', 'End date (optional, omit for ongoing)')
|
|
64
|
+
.option('--contact <resourceId>', 'Contact resourceId (required)')
|
|
65
|
+
.option('--ref <reference>', 'Reference / invoice number')
|
|
66
|
+
.option('--date <YYYY-MM-DD>', 'Transaction value date')
|
|
67
|
+
.option('--due <YYYY-MM-DD>', 'Due date')
|
|
68
|
+
.option('--lines <json>', 'Line items as JSON array')
|
|
69
|
+
.option('--amount <n>', 'Single line item amount (shorthand)', parseMoney)
|
|
70
|
+
.option('--account <resourceId>', 'Line item account resourceId (used with --amount)')
|
|
71
|
+
.option('--line-name <name>', 'Line item name (used with --amount)')
|
|
72
|
+
.option('--input <file>', 'Read full request body from JSON file (overrides flags)')
|
|
61
73
|
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
62
74
|
.option('--api-key <key>', 'API key')
|
|
63
75
|
.option('--json', 'JSON output')
|
|
64
76
|
.action(apiAction(async (client, opts) => {
|
|
65
|
-
|
|
77
|
+
let body = readBodyInput(opts);
|
|
66
78
|
if (!body) {
|
|
67
|
-
|
|
68
|
-
|
|
79
|
+
if (!opts.type || !opts.interval || !opts.startDate) {
|
|
80
|
+
console.error(chalk.red('Required: --type, --interval, --start-date'));
|
|
81
|
+
console.error(chalk.dim('Or use --input <file> to provide full JSON body.'));
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
// Build line items from --lines or --amount shorthand
|
|
85
|
+
let lineItems;
|
|
86
|
+
if (opts.lines) {
|
|
87
|
+
try {
|
|
88
|
+
lineItems = JSON.parse(opts.lines);
|
|
89
|
+
}
|
|
90
|
+
catch {
|
|
91
|
+
console.error(chalk.red('Invalid --lines JSON'));
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
else if (opts.amount !== undefined) {
|
|
96
|
+
lineItems = [{ name: opts.lineName ?? 'Subscription', unitPrice: opts.amount, quantity: 1, accountResourceId: opts.account }];
|
|
97
|
+
}
|
|
98
|
+
body = {
|
|
99
|
+
businessTransactionType: opts.type,
|
|
100
|
+
interval: opts.interval,
|
|
101
|
+
startDate: opts.startDate,
|
|
102
|
+
status: 'ACTIVE',
|
|
103
|
+
contactResourceId: opts.contact,
|
|
104
|
+
reference: opts.ref,
|
|
105
|
+
valueDate: opts.date,
|
|
106
|
+
dueDate: opts.due,
|
|
107
|
+
lineItems,
|
|
108
|
+
saveAsDraft: false,
|
|
109
|
+
};
|
|
110
|
+
if (opts.endDate)
|
|
111
|
+
body.endDate = opts.endDate;
|
|
69
112
|
}
|
|
70
113
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
71
114
|
const res = await createSubscription(client, body);
|
|
@@ -329,7 +329,7 @@ export function registerSupplierCreditNotesCommand(program) {
|
|
|
329
329
|
let attachmentCount = 0;
|
|
330
330
|
try {
|
|
331
331
|
const attRes = await listAttachments(client, 'supplier-credit-notes', cn.resourceId);
|
|
332
|
-
attachmentCount =
|
|
332
|
+
attachmentCount = attRes.data.length;
|
|
333
333
|
}
|
|
334
334
|
catch { /* Attachment listing may fail — don't block the report */ }
|
|
335
335
|
return buildDraftReport(cn, CREDIT_NOTE_REQUIRED_FIELDS, attachmentCount);
|
|
@@ -480,7 +480,7 @@ export function registerSupplierCreditNotesCommand(program) {
|
|
|
480
480
|
const cnRes = await getSupplierCreditNote(client, resourceId);
|
|
481
481
|
const cn = cnRes.data;
|
|
482
482
|
const attRes = await listAttachments(client, 'supplier-credit-notes', resourceId);
|
|
483
|
-
const attachments =
|
|
483
|
+
const attachments = attRes.data;
|
|
484
484
|
if (opts.json) {
|
|
485
485
|
console.log(JSON.stringify({ creditNoteResourceId: resourceId, creditNoteReference: cn.reference || null, attachments }, null, 2));
|
|
486
486
|
}
|
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* Works for bills, invoices, journals, and credit notes.
|
|
4
4
|
*/
|
|
5
5
|
export async function listAttachments(client, btType, btResourceId) {
|
|
6
|
-
|
|
6
|
+
const result = await client.get(`/api/v1/${btType}/${btResourceId}/attachments`);
|
|
7
|
+
// Guard all consumers (CLI, MCP, daemon): client.get() returns undefined on 204,
|
|
8
|
+
// and some endpoints return {} or { data: null } — always ensure .data is an array.
|
|
9
|
+
return { data: Array.isArray(result?.data) ? result.data : [] };
|
|
7
10
|
}
|
|
8
11
|
export async function addAttachment(client, data) {
|
|
9
12
|
const { businessTransactionType: btType, businessTransactionResourceId: btId, ...rest } = data;
|
|
@@ -53,7 +53,7 @@ import { generateFaReviewBlueprint } from '../jobs/fa-review/blueprint.js';
|
|
|
53
53
|
import { generateDocumentCollectionBlueprint } from '../jobs/document-collection/blueprint.js';
|
|
54
54
|
import { generateStatutoryFilingBlueprint } from '../jobs/statutory-filing/blueprint.js';
|
|
55
55
|
// Draft validation (pure logic from core/drafts/)
|
|
56
|
-
import { buildDraftReport, INVOICE_REQUIRED_FIELDS, BILL_REQUIRED_FIELDS, CREDIT_NOTE_REQUIRED_FIELDS, JOURNAL_REQUIRED_FIELDS, } from '../drafts/index.js';
|
|
56
|
+
import { validateDraft, buildDraftReport, INVOICE_REQUIRED_FIELDS, BILL_REQUIRED_FIELDS, CREDIT_NOTE_REQUIRED_FIELDS, JOURNAL_REQUIRED_FIELDS, } from '../drafts/index.js';
|
|
57
57
|
// ── Shared param snippets (DRY) ─────────────────────────────────
|
|
58
58
|
const PAGINATION_PARAMS = {
|
|
59
59
|
limit: { type: 'number', description: 'Max results per page (default 20, max 1000).' },
|
|
@@ -103,6 +103,20 @@ const CLASSIFIER_CONFIG_PARAM = {
|
|
|
103
103
|
},
|
|
104
104
|
description: 'Nano classifier config for line items. Each entry links a capsule type with selected classes.',
|
|
105
105
|
};
|
|
106
|
+
const JOURNAL_ENTRY_PARAM = {
|
|
107
|
+
type: 'array',
|
|
108
|
+
items: {
|
|
109
|
+
type: 'object',
|
|
110
|
+
properties: {
|
|
111
|
+
accountResourceId: { type: 'string', description: 'Account resourceId' },
|
|
112
|
+
type: { type: 'string', enum: ['DEBIT', 'CREDIT'], description: 'Debit or credit' },
|
|
113
|
+
amount: { type: 'number', description: 'Amount' },
|
|
114
|
+
description: { type: 'string', description: 'Line description' },
|
|
115
|
+
},
|
|
116
|
+
required: ['accountResourceId', 'type', 'amount'],
|
|
117
|
+
},
|
|
118
|
+
description: 'Journal entries (debit/credit lines with accountResourceId, type, amount)',
|
|
119
|
+
};
|
|
106
120
|
const LINE_ITEM_PARAM = {
|
|
107
121
|
type: 'array',
|
|
108
122
|
items: {
|
|
@@ -279,27 +293,26 @@ async function fetchAndMerge(client, type, resourceId, overrides) {
|
|
|
279
293
|
if (v !== undefined)
|
|
280
294
|
base[k] = v;
|
|
281
295
|
}
|
|
282
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
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
|
-
}
|
|
296
|
+
// Validate draft readiness — reuses shared draft validation (DRY with CLI).
|
|
297
|
+
// fetchAndMerge is only called by finalize tools, so always validate.
|
|
298
|
+
const specs = type === 'invoice' ? INVOICE_REQUIRED_FIELDS
|
|
299
|
+
: type === 'bill' ? BILL_REQUIRED_FIELDS
|
|
300
|
+
: CREDIT_NOTE_REQUIRED_FIELDS;
|
|
301
|
+
const { missingFields, ready } = validateDraft(base, specs);
|
|
302
|
+
if (!ready) {
|
|
303
|
+
throw new Error(`Cannot finalize: missing ${missingFields.join(', ')}. ` +
|
|
304
|
+
`Use search_accounts (filter by accountType) and search_contacts to resolve, ` +
|
|
305
|
+
`then pass the missing fields to this tool.`);
|
|
300
306
|
}
|
|
301
307
|
return base;
|
|
302
308
|
}
|
|
309
|
+
/** Advisory pre-flight: reject payment/refund against DRAFT documents (API is authoritative). */
|
|
310
|
+
async function assertNotDraft(getter, client, resourceId, kind) {
|
|
311
|
+
const res = await getter(client, resourceId);
|
|
312
|
+
if (res.data.status === 'DRAFT') {
|
|
313
|
+
throw new Error(`Cannot pay a DRAFT ${kind}. Finalize it first with finalize_${kind}.`);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
303
316
|
// ── Tool Definitions ─────────────────────────────────────────────
|
|
304
317
|
export const TOOL_DEFINITIONS = [
|
|
305
318
|
// ── Organization ───────────────────────────────────────────────
|
|
@@ -470,7 +483,14 @@ export const TOOL_DEFINITIONS = [
|
|
|
470
483
|
execute: async (ctx, input) => {
|
|
471
484
|
const { resourceId: rid, ...updates } = input;
|
|
472
485
|
const { data: existing } = await getContact(ctx.client, rid);
|
|
473
|
-
|
|
486
|
+
// Filter to writable fields only — GET returns read-only fields (resourceId, createdAt, etc.)
|
|
487
|
+
// that cause 400 "Invalid request body" if included in PUT. Same pattern as update_account.
|
|
488
|
+
const CONTACT_WRITABLE = ['billingName', 'name', 'emails', 'customer', 'supplier',
|
|
489
|
+
'taxRegistrationNumber', 'address', 'phone', 'status'];
|
|
490
|
+
const ex = existing;
|
|
491
|
+
const base = Object.fromEntries(CONTACT_WRITABLE.filter(k => ex[k] !== undefined && ex[k] !== null).map(k => [k, ex[k]]));
|
|
492
|
+
Object.assign(base, updates);
|
|
493
|
+
return updateContact(ctx.client, rid, base);
|
|
474
494
|
},
|
|
475
495
|
},
|
|
476
496
|
// ── Invoices ───────────────────────────────────────────────────
|
|
@@ -544,13 +564,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
544
564
|
},
|
|
545
565
|
{
|
|
546
566
|
name: 'update_invoice',
|
|
547
|
-
description: 'Update an existing draft invoice.
|
|
567
|
+
description: 'Update an existing draft invoice (change reference, dates, line items, notes, custom fields). Use when the user says "update", "change", "fix", or "correct" a draft invoice. Line items CAN be fully replaced — pass the complete updated lineItems array.',
|
|
548
568
|
params: {
|
|
549
569
|
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
550
570
|
reference: { type: 'string' },
|
|
551
571
|
valueDate: { type: 'string' },
|
|
552
572
|
dueDate: { type: 'string' },
|
|
553
|
-
lineItems:
|
|
573
|
+
lineItems: LINE_ITEM_PARAM,
|
|
554
574
|
notes: { type: 'string' },
|
|
555
575
|
customFields: CUSTOM_FIELDS_PARAM,
|
|
556
576
|
},
|
|
@@ -591,6 +611,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
591
611
|
group: 'invoices',
|
|
592
612
|
readOnly: false,
|
|
593
613
|
execute: async (ctx, input) => {
|
|
614
|
+
const resourceId = input.resourceId;
|
|
594
615
|
const payAmt = Number(input.paymentAmount);
|
|
595
616
|
if (!Number.isFinite(payAmt) || payAmt <= 0) {
|
|
596
617
|
throw new Error('paymentAmount must be a positive number');
|
|
@@ -599,7 +620,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
599
620
|
if (!Number.isFinite(txnAmt) || txnAmt <= 0) {
|
|
600
621
|
throw new Error('transactionAmount must be a positive number');
|
|
601
622
|
}
|
|
602
|
-
|
|
623
|
+
await assertNotDraft(getInvoice, ctx.client, resourceId, 'invoice');
|
|
624
|
+
return createInvoicePayment(ctx.client, resourceId, {
|
|
603
625
|
paymentAmount: payAmt,
|
|
604
626
|
transactionAmount: txnAmt,
|
|
605
627
|
accountResourceId: input.accountResourceId,
|
|
@@ -620,7 +642,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
620
642
|
reference: { type: 'string' },
|
|
621
643
|
valueDate: { type: 'string' },
|
|
622
644
|
dueDate: { type: 'string' },
|
|
623
|
-
lineItems:
|
|
645
|
+
lineItems: LINE_ITEM_PARAM,
|
|
624
646
|
notes: { type: 'string' },
|
|
625
647
|
},
|
|
626
648
|
required: ['resourceId'],
|
|
@@ -634,7 +656,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
634
656
|
},
|
|
635
657
|
{
|
|
636
658
|
name: 'apply_credits_to_invoice',
|
|
637
|
-
description: 'Apply customer credit note(s) to an invoice.
|
|
659
|
+
description: 'Apply customer credit note(s) to an invoice. IMPORTANT: The credit note must be FINALIZED first (status UNAPPLIED, not DRAFT). If it is still a draft, call finalize_customer_credit_note first. Then search_customer_credit_notes with status UNAPPLIED to find available credits for the contact, and pass creditNoteResourceId and amountApplied for each.',
|
|
638
660
|
params: {
|
|
639
661
|
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
640
662
|
credits: {
|
|
@@ -731,13 +753,13 @@ export const TOOL_DEFINITIONS = [
|
|
|
731
753
|
},
|
|
732
754
|
{
|
|
733
755
|
name: 'update_bill',
|
|
734
|
-
description: 'Update an existing draft bill.
|
|
756
|
+
description: 'Update an existing draft bill (change reference, dates, line items, notes, custom fields). Use when the user says "update", "change", "fix", or "correct" a draft bill. Line items CAN be fully replaced — pass the complete updated lineItems array.',
|
|
735
757
|
params: {
|
|
736
758
|
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
737
759
|
reference: { type: 'string' },
|
|
738
760
|
valueDate: { type: 'string' },
|
|
739
761
|
dueDate: { type: 'string' },
|
|
740
|
-
lineItems:
|
|
762
|
+
lineItems: LINE_ITEM_PARAM,
|
|
741
763
|
notes: { type: 'string' },
|
|
742
764
|
customFields: CUSTOM_FIELDS_PARAM,
|
|
743
765
|
},
|
|
@@ -774,6 +796,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
774
796
|
group: 'bills',
|
|
775
797
|
readOnly: false,
|
|
776
798
|
execute: async (ctx, input) => {
|
|
799
|
+
const billResourceId = input.resourceId;
|
|
777
800
|
const billPayAmt = Number(input.paymentAmount);
|
|
778
801
|
if (!Number.isFinite(billPayAmt) || billPayAmt <= 0) {
|
|
779
802
|
throw new Error('paymentAmount must be a positive number');
|
|
@@ -782,7 +805,8 @@ export const TOOL_DEFINITIONS = [
|
|
|
782
805
|
if (!Number.isFinite(billTxnAmt) || billTxnAmt <= 0) {
|
|
783
806
|
throw new Error('transactionAmount must be a positive number');
|
|
784
807
|
}
|
|
785
|
-
|
|
808
|
+
await assertNotDraft(getBill, ctx.client, billResourceId, 'bill');
|
|
809
|
+
return createBillPayment(ctx.client, billResourceId, {
|
|
786
810
|
paymentAmount: billPayAmt,
|
|
787
811
|
transactionAmount: billTxnAmt,
|
|
788
812
|
accountResourceId: input.accountResourceId,
|
|
@@ -803,7 +827,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
803
827
|
reference: { type: 'string' },
|
|
804
828
|
valueDate: { type: 'string' },
|
|
805
829
|
dueDate: { type: 'string' },
|
|
806
|
-
lineItems:
|
|
830
|
+
lineItems: LINE_ITEM_PARAM,
|
|
807
831
|
notes: { type: 'string' },
|
|
808
832
|
},
|
|
809
833
|
required: ['resourceId'],
|
|
@@ -817,7 +841,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
817
841
|
},
|
|
818
842
|
{
|
|
819
843
|
name: 'apply_credits_to_bill',
|
|
820
|
-
description: 'Apply supplier credit note(s) to a bill.
|
|
844
|
+
description: 'Apply supplier credit note(s) to a bill. IMPORTANT: The credit note must be FINALIZED first (status UNAPPLIED, not DRAFT). If it is still a draft, call finalize_supplier_credit_note first. Then search_supplier_credit_notes with status UNAPPLIED to find available credits for the contact, and pass creditNoteResourceId and amountApplied for each.',
|
|
821
845
|
params: {
|
|
822
846
|
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
823
847
|
credits: {
|
|
@@ -875,19 +899,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
875
899
|
params: {
|
|
876
900
|
reference: { type: 'string', description: 'Journal reference' },
|
|
877
901
|
valueDate: { type: 'string', description: 'Journal date (YYYY-MM-DD)' },
|
|
878
|
-
journalEntries:
|
|
879
|
-
type: 'array',
|
|
880
|
-
items: {
|
|
881
|
-
type: 'object',
|
|
882
|
-
properties: {
|
|
883
|
-
accountResourceId: { type: 'string' },
|
|
884
|
-
type: { type: 'string', enum: ['DEBIT', 'CREDIT'] },
|
|
885
|
-
amount: { type: 'number' },
|
|
886
|
-
description: { type: 'string' },
|
|
887
|
-
},
|
|
888
|
-
required: ['accountResourceId', 'type', 'amount'],
|
|
889
|
-
},
|
|
890
|
-
},
|
|
902
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
891
903
|
saveAsDraft: { type: 'boolean' },
|
|
892
904
|
notes: { type: 'string' },
|
|
893
905
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
@@ -1352,12 +1364,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1352
1364
|
},
|
|
1353
1365
|
{
|
|
1354
1366
|
name: 'update_customer_credit_note',
|
|
1355
|
-
description: 'Update a draft customer credit note.',
|
|
1367
|
+
description: 'Update a draft customer credit note (change amount, line items, contact, date, notes). Use when the user says "update", "change", "fix", or "correct" a credit note.',
|
|
1356
1368
|
params: {
|
|
1357
1369
|
resourceId: { type: 'string', description: 'Customer credit note resourceId' },
|
|
1358
1370
|
reference: { type: 'string' },
|
|
1359
1371
|
valueDate: { type: 'string' },
|
|
1360
|
-
lineItems:
|
|
1372
|
+
lineItems: LINE_ITEM_PARAM,
|
|
1361
1373
|
notes: { type: 'string' },
|
|
1362
1374
|
tag: { type: 'string' },
|
|
1363
1375
|
customFields: CUSTOM_FIELDS_PARAM,
|
|
@@ -1377,7 +1389,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1377
1389
|
resourceId: { type: 'string', description: 'Customer credit note resourceId' },
|
|
1378
1390
|
reference: { type: 'string' },
|
|
1379
1391
|
valueDate: { type: 'string' },
|
|
1380
|
-
lineItems:
|
|
1392
|
+
lineItems: LINE_ITEM_PARAM,
|
|
1381
1393
|
notes: { type: 'string' },
|
|
1382
1394
|
},
|
|
1383
1395
|
required: ['resourceId'],
|
|
@@ -1527,12 +1539,12 @@ export const TOOL_DEFINITIONS = [
|
|
|
1527
1539
|
},
|
|
1528
1540
|
{
|
|
1529
1541
|
name: 'update_supplier_credit_note',
|
|
1530
|
-
description: 'Update a draft supplier credit note.',
|
|
1542
|
+
description: 'Update a draft supplier credit note (change amount, line items, contact, date, notes). Use when the user says "update", "change", "fix", or "correct" a credit note.',
|
|
1531
1543
|
params: {
|
|
1532
1544
|
resourceId: { type: 'string', description: 'Supplier credit note resourceId' },
|
|
1533
1545
|
reference: { type: 'string' },
|
|
1534
1546
|
valueDate: { type: 'string' },
|
|
1535
|
-
lineItems:
|
|
1547
|
+
lineItems: LINE_ITEM_PARAM,
|
|
1536
1548
|
notes: { type: 'string' },
|
|
1537
1549
|
tag: { type: 'string' },
|
|
1538
1550
|
customFields: CUSTOM_FIELDS_PARAM,
|
|
@@ -1552,7 +1564,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1552
1564
|
resourceId: { type: 'string', description: 'Supplier credit note resourceId' },
|
|
1553
1565
|
reference: { type: 'string' },
|
|
1554
1566
|
valueDate: { type: 'string' },
|
|
1555
|
-
lineItems:
|
|
1567
|
+
lineItems: LINE_ITEM_PARAM,
|
|
1556
1568
|
notes: { type: 'string' },
|
|
1557
1569
|
},
|
|
1558
1570
|
required: ['resourceId'],
|
|
@@ -1653,7 +1665,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1653
1665
|
},
|
|
1654
1666
|
{
|
|
1655
1667
|
name: 'list_currency_rates',
|
|
1656
|
-
description: 'List exchange rates for a specific currency.',
|
|
1668
|
+
description: 'List exchange rates for a specific currency. IMPORTANT: You MUST call list_currencies first to discover which currencies the org has enabled — never guess or assume currency codes.',
|
|
1657
1669
|
params: {
|
|
1658
1670
|
currencyCode: { type: 'string', description: 'Currency code (e.g., "USD")' },
|
|
1659
1671
|
...PAGINATION_PARAMS,
|
|
@@ -1669,7 +1681,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1669
1681
|
},
|
|
1670
1682
|
{
|
|
1671
1683
|
name: 'add_currency_rate',
|
|
1672
|
-
description: 'Add an exchange rate for a currency. Rate is relative to the base currency.',
|
|
1684
|
+
description: 'Add or set an exchange rate for a currency. ALWAYS use this tool even when the user says "update rate" — it handles both new and existing rates. Rate is relative to the base currency. Call list_currencies first to get valid currency codes.',
|
|
1673
1685
|
params: {
|
|
1674
1686
|
currencyCode: { type: 'string', description: 'Currency code' },
|
|
1675
1687
|
rate: { type: 'number', description: 'Exchange rate (e.g., 1.35 for 1 USD = 1.35 SGD)' },
|
|
@@ -1687,7 +1699,7 @@ export const TOOL_DEFINITIONS = [
|
|
|
1687
1699
|
},
|
|
1688
1700
|
{
|
|
1689
1701
|
name: 'update_currency_rate',
|
|
1690
|
-
description: 'Update an existing exchange rate.',
|
|
1702
|
+
description: 'Update an existing exchange rate for a currency. Call list_currency_rates first to find the rate resourceId. Use add_currency_rate for new rates.',
|
|
1691
1703
|
params: {
|
|
1692
1704
|
currencyCode: { type: 'string', description: 'Currency code' },
|
|
1693
1705
|
resourceId: { type: 'string', description: 'Rate resourceId' },
|
|
@@ -1761,19 +1773,7 @@ WHEN NOT TO USE: moving money between your own bank/cash accounts — use create
|
|
|
1761
1773
|
reference: { type: 'string', description: 'Reference number' },
|
|
1762
1774
|
valueDate: { type: 'string', description: 'Date (YYYY-MM-DD)' },
|
|
1763
1775
|
accountResourceId: { type: 'string', description: 'Bank/cash account resourceId' },
|
|
1764
|
-
journalEntries:
|
|
1765
|
-
type: 'array',
|
|
1766
|
-
items: {
|
|
1767
|
-
type: 'object',
|
|
1768
|
-
properties: {
|
|
1769
|
-
accountResourceId: { type: 'string' },
|
|
1770
|
-
type: { type: 'string', enum: ['DEBIT', 'CREDIT'] },
|
|
1771
|
-
amount: { type: 'number' },
|
|
1772
|
-
description: { type: 'string' },
|
|
1773
|
-
},
|
|
1774
|
-
required: ['accountResourceId', 'type', 'amount'],
|
|
1775
|
-
},
|
|
1776
|
-
},
|
|
1776
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
1777
1777
|
notes: { type: 'string' },
|
|
1778
1778
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
1779
1779
|
currency: CURRENCY_PARAM,
|
|
@@ -1802,19 +1802,7 @@ WHEN NOT TO USE: moving money between your own bank/cash accounts — use create
|
|
|
1802
1802
|
reference: { type: 'string', description: 'Reference number' },
|
|
1803
1803
|
valueDate: { type: 'string', description: 'Date (YYYY-MM-DD)' },
|
|
1804
1804
|
accountResourceId: { type: 'string', description: 'Bank/cash account resourceId' },
|
|
1805
|
-
journalEntries:
|
|
1806
|
-
type: 'array',
|
|
1807
|
-
items: {
|
|
1808
|
-
type: 'object',
|
|
1809
|
-
properties: {
|
|
1810
|
-
accountResourceId: { type: 'string' },
|
|
1811
|
-
type: { type: 'string', enum: ['DEBIT', 'CREDIT'] },
|
|
1812
|
-
amount: { type: 'number' },
|
|
1813
|
-
description: { type: 'string' },
|
|
1814
|
-
},
|
|
1815
|
-
required: ['accountResourceId', 'type', 'amount'],
|
|
1816
|
-
},
|
|
1817
|
-
},
|
|
1805
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
1818
1806
|
notes: { type: 'string' },
|
|
1819
1807
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
1820
1808
|
currency: CURRENCY_PARAM,
|
|
@@ -1843,12 +1831,12 @@ WHEN NOT TO USE: moving money between your own bank/cash accounts — use create
|
|
|
1843
1831
|
},
|
|
1844
1832
|
{
|
|
1845
1833
|
name: 'update_cash_in',
|
|
1846
|
-
description: 'Update an existing cash-in entry.',
|
|
1834
|
+
description: 'Update an existing cash-in entry (change amount, date, reference, notes, tags, or journal entries). Use when the user says "update", "change", "fix", or "adjust" a cash-in that was just created or found. Do NOT create a new entry — use this tool instead.',
|
|
1847
1835
|
params: {
|
|
1848
1836
|
resourceId: { type: 'string', description: 'Cash-in resourceId' },
|
|
1849
1837
|
reference: { type: 'string' },
|
|
1850
1838
|
valueDate: { type: 'string' },
|
|
1851
|
-
journalEntries:
|
|
1839
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
1852
1840
|
notes: { type: 'string' },
|
|
1853
1841
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
1854
1842
|
},
|
|
@@ -1873,12 +1861,12 @@ WHEN NOT TO USE: moving money between your own bank/cash accounts — use create
|
|
|
1873
1861
|
},
|
|
1874
1862
|
{
|
|
1875
1863
|
name: 'update_cash_out',
|
|
1876
|
-
description: 'Update an existing cash-out entry.',
|
|
1864
|
+
description: 'Update an existing cash-out entry (change amount, date, reference, notes, tags, or journal entries). Use when the user says "update", "change", "fix", or "adjust" a cash-out that was just created or found. Do NOT create a new entry — use this tool instead.',
|
|
1877
1865
|
params: {
|
|
1878
1866
|
resourceId: { type: 'string', description: 'Cash-out resourceId' },
|
|
1879
1867
|
reference: { type: 'string' },
|
|
1880
1868
|
valueDate: { type: 'string' },
|
|
1881
|
-
journalEntries:
|
|
1869
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
1882
1870
|
notes: { type: 'string' },
|
|
1883
1871
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
1884
1872
|
},
|
|
@@ -1951,7 +1939,7 @@ WHEN NOT TO USE: receiving money from external parties (use create_cash_in) or p
|
|
|
1951
1939
|
},
|
|
1952
1940
|
{
|
|
1953
1941
|
name: 'search_bank_records',
|
|
1954
|
-
description: 'Search bank records (imported bank transactions) for a specific account.
|
|
1942
|
+
description: 'Search bank records (imported bank transactions) for a specific account. Use this to find unreconciled items, match deposits to invoices, or identify bank charges. Filter by status (UNRECONCILED/RECONCILED), date range, description, payer/payee, reference, and amount range. Call list_bank_accounts first to get the accountResourceId.',
|
|
1955
1943
|
params: {
|
|
1956
1944
|
accountResourceId: { type: 'string', description: 'Bank account resourceId' },
|
|
1957
1945
|
status: { type: 'string', enum: ['UNRECONCILED', 'RECONCILED', 'ARCHIVED', 'POSSIBLE_DUPLICATE'], description: 'Filter by reconciliation status' },
|
|
@@ -2040,7 +2028,7 @@ WHEN NOT TO USE: receiving money from external parties (use create_cash_in) or p
|
|
|
2040
2028
|
},
|
|
2041
2029
|
{
|
|
2042
2030
|
name: 'update_bookmark',
|
|
2043
|
-
description: 'Update an existing bookmark.',
|
|
2031
|
+
description: 'Update an existing bookmark (name, value, or category). Use when modifying a previously created bookmark.',
|
|
2044
2032
|
params: {
|
|
2045
2033
|
resourceId: { type: 'string', description: 'Bookmark resourceId' },
|
|
2046
2034
|
name: { type: 'string', description: 'New name' },
|
|
@@ -2512,20 +2500,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
2512
2500
|
resourceId: { type: 'string', description: 'Journal resourceId' },
|
|
2513
2501
|
reference: { type: 'string', description: 'Updated reference' },
|
|
2514
2502
|
valueDate: { type: 'string', description: 'Updated date (YYYY-MM-DD)' },
|
|
2515
|
-
journalEntries:
|
|
2516
|
-
type: 'array',
|
|
2517
|
-
items: {
|
|
2518
|
-
type: 'object',
|
|
2519
|
-
properties: {
|
|
2520
|
-
accountResourceId: { type: 'string' },
|
|
2521
|
-
amount: { type: 'number' },
|
|
2522
|
-
type: { type: 'string', enum: ['DEBIT', 'CREDIT'] },
|
|
2523
|
-
description: { type: 'string' },
|
|
2524
|
-
},
|
|
2525
|
-
required: ['accountResourceId', 'amount', 'type'],
|
|
2526
|
-
},
|
|
2527
|
-
description: 'Updated journal entries (debit/credit lines)',
|
|
2528
|
-
},
|
|
2503
|
+
journalEntries: JOURNAL_ENTRY_PARAM,
|
|
2529
2504
|
notes: { type: 'string' },
|
|
2530
2505
|
tags: { type: 'array', items: { type: 'string' }, description: 'Tags (string array)' },
|
|
2531
2506
|
saveAsDraft: { type: 'boolean', description: 'Keep as draft (default true)' },
|
|
@@ -3059,7 +3034,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3059
3034
|
},
|
|
3060
3035
|
{
|
|
3061
3036
|
name: 'update_bank_rule',
|
|
3062
|
-
description: 'Update an existing bank reconciliation rule.',
|
|
3037
|
+
description: 'Update an existing bank reconciliation rule (conditions, actions, or name). Fetches current rule first — only send fields to change.',
|
|
3063
3038
|
params: {
|
|
3064
3039
|
resourceId: { type: 'string', description: 'Bank rule resourceId' },
|
|
3065
3040
|
name: { type: 'string', description: 'New rule name' },
|
|
@@ -3160,10 +3135,11 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3160
3135
|
},
|
|
3161
3136
|
{
|
|
3162
3137
|
name: 'create_fixed_asset',
|
|
3163
|
-
description: `Register a
|
|
3164
|
-
-
|
|
3165
|
-
-
|
|
3166
|
-
|
|
3138
|
+
description: `Register a fixed asset linked to an existing purchase transaction (bill or journal).
|
|
3139
|
+
- saveAsDraft defaults to true. Set to false to activate — requires ALL fields below.
|
|
3140
|
+
- ACTIVE assets require: purchaseBusinessTransactionType + purchaseBusinessTransactionResourceId
|
|
3141
|
+
(links to the bill/journal that recorded the purchase).
|
|
3142
|
+
- For standalone asset entry (no linked transaction), use transfer_fixed_asset instead.`,
|
|
3167
3143
|
params: {
|
|
3168
3144
|
name: { type: 'string', description: 'Asset name' },
|
|
3169
3145
|
typeName: { type: 'string', description: 'Asset type (e.g., "Buildings", "Vehicles", "Furniture")' },
|
|
@@ -3171,17 +3147,19 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3171
3147
|
purchaseAmount: { type: 'number', description: 'Purchase cost' },
|
|
3172
3148
|
purchaseDate: { type: 'string', description: 'Purchase date (YYYY-MM-DD)' },
|
|
3173
3149
|
purchaseAssetAccountResourceId: { type: 'string', description: 'Asset account resourceId' },
|
|
3174
|
-
depreciationStartDate: { type: 'string', description: 'Depreciation start date (YYYY-MM-DD)' },
|
|
3175
|
-
depreciationMethod: { type: 'string', description: 'Depreciation method
|
|
3150
|
+
depreciationStartDate: { type: 'string', description: 'Depreciation start date (YYYY-MM-DD, required for STRAIGHT_LINE)' },
|
|
3151
|
+
depreciationMethod: { type: 'string', enum: ['STRAIGHT_LINE', 'NO_DEPRECIATION'], description: 'Depreciation method' },
|
|
3176
3152
|
effectiveLife: { type: 'number', description: 'Effective life in months' },
|
|
3177
3153
|
depreciableValueResidualAmount: { type: 'number', description: 'Residual/salvage value' },
|
|
3178
|
-
depreciationExpenseAccountResourceId: { type: 'string', description: 'Depreciation expense account resourceId' },
|
|
3179
|
-
accumulatedDepreciationAccountResourceId: { type: 'string', description: 'Accumulated depreciation account resourceId' },
|
|
3154
|
+
depreciationExpenseAccountResourceId: { type: 'string', description: 'Depreciation expense account resourceId (required for STRAIGHT_LINE)' },
|
|
3155
|
+
accumulatedDepreciationAccountResourceId: { type: 'string', description: 'Accumulated depreciation account resourceId (required for STRAIGHT_LINE)' },
|
|
3156
|
+
purchaseBusinessTransactionType: { type: 'string', enum: ['PURCHASE', 'JOURNAL_MANUAL'], description: 'Type of purchase transaction this asset is linked to' },
|
|
3157
|
+
purchaseBusinessTransactionResourceId: { type: 'string', description: 'ResourceId of the purchase bill or journal' },
|
|
3180
3158
|
internalNotes: { type: 'string', description: 'Internal notes' },
|
|
3181
|
-
saveAsDraft: { type: 'boolean', description: 'Save as draft (default true)' },
|
|
3159
|
+
saveAsDraft: { type: 'boolean', description: 'Save as draft (default true). False = activate immediately.' },
|
|
3182
3160
|
customFields: CUSTOM_FIELDS_PARAM,
|
|
3183
3161
|
},
|
|
3184
|
-
required: ['name', 'purchaseAmount', 'purchaseDate', 'purchaseAssetAccountResourceId'],
|
|
3162
|
+
required: ['name', 'purchaseAmount', 'purchaseDate', 'purchaseAssetAccountResourceId', 'depreciationStartDate'],
|
|
3185
3163
|
group: 'fixed_assets',
|
|
3186
3164
|
readOnly: false,
|
|
3187
3165
|
execute: async (ctx, input) => createFixedAsset(ctx.client, input),
|
|
@@ -3241,7 +3219,7 @@ Auto-resolves accounts from chart of accounts. Provide bankAccountName for recip
|
|
|
3241
3219
|
},
|
|
3242
3220
|
{
|
|
3243
3221
|
name: 'transfer_fixed_asset',
|
|
3244
|
-
description: 'Transfer a fixed asset to a different asset type/account. Creates a new asset registration from an existing one.',
|
|
3222
|
+
description: 'Transfer a fixed asset to a different asset type/account, OR create a standalone asset entry (no linked purchase transaction needed). Creates a new asset registration from an existing one.',
|
|
3245
3223
|
params: {
|
|
3246
3224
|
resourceId: { type: 'string', description: 'Source fixed asset resourceId' },
|
|
3247
3225
|
name: { type: 'string', description: 'New asset name' },
|