jaz-clio 4.32.5 → 4.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/assets/skills/api/SKILL.md +1 -1
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +1 -1
- package/assets/skills/transaction-recipes/SKILL.md +1 -1
- package/dist/commands/bills.js +62 -1
- package/dist/commands/invoices.js +62 -1
- package/dist/commands/nano-classifiers.js +173 -0
- package/dist/commands/payments.js +66 -1
- package/dist/commands/schedulers.js +209 -2
- package/dist/core/api/bills.js +20 -0
- package/dist/core/api/index.js +1 -0
- package/dist/core/api/invoices.js +20 -0
- package/dist/core/api/nano-classifiers.js +37 -0
- package/dist/core/api/payments.js +9 -0
- package/dist/core/api/schedulers.js +45 -0
- package/dist/core/registry/namespaces.js +6 -1
- package/dist/core/registry/tools.js +310 -10
- package/dist/index.js +2 -0
- package/dist/types/index.js +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<p align="center">
|
|
4
4
|
<a href="https://www.npmjs.com/package/jaz-clio"><img src="https://img.shields.io/npm/v/jaz-clio?style=for-the-badge&logo=npm" alt="npm"></a>
|
|
5
5
|
<a href="https://www.npmjs.com/package/jaz-clio"><img src="https://img.shields.io/npm/dm/jaz-clio?style=for-the-badge&label=downloads" alt="npm downloads"></a>
|
|
6
|
-
<img src="https://img.shields.io/badge/tools-
|
|
6
|
+
<img src="https://img.shields.io/badge/tools-211-blue?style=for-the-badge" alt="211 Tools">
|
|
7
7
|
<img src="https://img.shields.io/badge/calculators-13-red?style=for-the-badge" alt="13 Calculators">
|
|
8
8
|
<img src="https://img.shields.io/badge/jobs-12-teal?style=for-the-badge" alt="12 Jobs">
|
|
9
9
|
<a href="https://github.com/teamtinvio/jaz-ai/blob/main/LICENSE"><img src="https://img.shields.io/github/license/teamtinvio/jaz-ai?style=for-the-badge&color=green" alt="License"></a>
|
|
@@ -235,7 +235,7 @@ Every command supports `--json` for structured output — ideal for piping to ot
|
|
|
235
235
|
|
|
236
236
|
## MCP Server
|
|
237
237
|
|
|
238
|
-
Expose all
|
|
238
|
+
Expose all 235 CLI tools to AI coding and coworking agents via the Model Context Protocol (MCP). The server runs locally on your machine — no cloud, no ports. API calls go directly from your machine to the Jaz API.
|
|
239
239
|
|
|
240
240
|
**Claude Code:**
|
|
241
241
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-api
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.33.0
|
|
4
4
|
description: Complete reference for the Jaz REST API — the accounting platform backend. Use this skill whenever building, modifying, debugging, or extending any code that calls the API — including API clients, integrations, data seeding, test data, or new endpoint work. Contains every field name, response shape, error, gotcha, and edge case discovered through live production testing.
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Requires Jaz API key (x-jk-api-key header). Works with Claude Code, Google Antigravity, OpenAI Codex, GitHub Copilot, Cursor, and any agent that reads markdown.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-conversion
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.33.0
|
|
4
4
|
description: Accounting data conversion skill — migrates customer data from Xero, QuickBooks, Sage, MYOB, and Excel exports to Jaz. Covers config, quick, and full conversion workflows, Excel parsing, CoA/contact/tax/items mapping, clearing accounts, TTB, and TB verification.
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-jobs
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.33.0
|
|
4
4
|
description: 12 accounting jobs for SMB bookkeepers and accountants — month-end, quarter-end, and year-end close playbooks plus 9 ad-hoc operational jobs (bank recon, document collection, GST/VAT filing, payment runs, credit control, supplier recon, audit prep, fixed asset review, statutory filing). Jobs can have paired tools as nested subcommands (e.g., `clio jobs bank-recon match`, `clio jobs document-collection ingest`, `clio jobs statutory-filing sg-cs`). Paired with an interactive CLI blueprint generator (clio jobs).
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill. For individual transaction patterns, load the jaz-recipes skill.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: jaz-recipes
|
|
3
|
-
version: 4.
|
|
3
|
+
version: 4.33.0
|
|
4
4
|
description: 16 IFRS-compliant recipes for complex multi-step accounting in Jaz — prepaid amortization, deferred revenue, loan schedules, IFRS 16 leases, hire purchase, fixed deposits, asset disposal, FX revaluation, ECL provisioning, IAS 37 provisions, dividends, intercompany, and capital WIP. Each recipe includes journal entries, capsule structure, and verification steps. Paired with 13 financial calculators that produce execution-ready blueprints with workings.
|
|
5
5
|
license: MIT
|
|
6
6
|
compatibility: Works with Claude Code, Claude Cowork, Claude.ai, and any agent that reads markdown. For API payloads, load the jaz-api skill alongside this one.
|
package/dist/commands/bills.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
|
-
import { listBills, getBill, searchBills, createBill, updateBill, deleteBill, createBillPayment, applyCreditsToBill, finalizeBill, } from '../core/api/bills.js';
|
|
3
|
+
import { listBills, getBill, searchBills, createBill, updateBill, deleteBill, createBillPayment, applyCreditsToBill, finalizeBill, listBillPayments, listBillCredits, reverseBillCredit, } from '../core/api/bills.js';
|
|
4
4
|
import { listAttachments } from '../core/api/attachments.js';
|
|
5
5
|
import { apiAction } from './api-action.js';
|
|
6
6
|
import { resolveContactFlag, resolveAccountFlag, resolveTaxProfileFlag } from './resolve.js';
|
|
@@ -579,4 +579,65 @@ export function registerBillsCommand(program) {
|
|
|
579
579
|
}
|
|
580
580
|
}
|
|
581
581
|
})(opts));
|
|
582
|
+
// ── clio bills list-payments ────────────────────────────────────
|
|
583
|
+
bills
|
|
584
|
+
.command('list-payments <resourceId>')
|
|
585
|
+
.description('List payments recorded against a bill')
|
|
586
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
587
|
+
.option('--json', 'Output as JSON')
|
|
588
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
589
|
+
const res = await listBillPayments(client, resourceId);
|
|
590
|
+
if (opts.json) {
|
|
591
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
const payments = res.data;
|
|
595
|
+
if (payments.length === 0) {
|
|
596
|
+
console.log('No payments recorded for this bill.');
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
console.log(chalk.bold(`Payments for bill ${resourceId}:\n`));
|
|
600
|
+
for (const p of payments) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
601
|
+
console.log(` ${chalk.cyan(p.resourceId)} ${p.paymentAmount ?? p.amount ?? ''} on ${p.valueDate ?? ''}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
})(opts));
|
|
605
|
+
// ── clio bills list-credits ─────────────────────────────────────
|
|
606
|
+
bills
|
|
607
|
+
.command('list-credits <resourceId>')
|
|
608
|
+
.description('List credit notes applied to a bill')
|
|
609
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
610
|
+
.option('--json', 'Output as JSON')
|
|
611
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
612
|
+
const res = await listBillCredits(client, resourceId);
|
|
613
|
+
if (opts.json) {
|
|
614
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
const credits = res.data;
|
|
618
|
+
if (credits.length === 0) {
|
|
619
|
+
console.log('No credits applied to this bill.');
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
console.log(chalk.bold(`Credits for bill ${resourceId}:\n`));
|
|
623
|
+
for (const c of credits) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
624
|
+
console.log(` ${chalk.cyan(c.resourceId)} ${c.amount ?? ''}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
})(opts));
|
|
628
|
+
// ── clio bills reverse-credit ───────────────────────────────────
|
|
629
|
+
bills
|
|
630
|
+
.command('reverse-credit <resourceId> <creditResourceId>')
|
|
631
|
+
.description('Reverse (unapply) a supplier credit note from a bill')
|
|
632
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
633
|
+
.option('--json', 'Output as JSON')
|
|
634
|
+
.action((resourceId, creditResourceId, opts) => apiAction(async (client) => {
|
|
635
|
+
await reverseBillCredit(client, resourceId, creditResourceId);
|
|
636
|
+
if (opts.json) {
|
|
637
|
+
console.log(JSON.stringify({ reversed: true, resourceId, creditResourceId }));
|
|
638
|
+
}
|
|
639
|
+
else {
|
|
640
|
+
console.log(chalk.green(`Credit ${creditResourceId} reversed from bill ${resourceId}.`));
|
|
641
|
+
}
|
|
642
|
+
})(opts));
|
|
582
643
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
2
|
import { formatStatus, formatId, formatReference, formatCurrency } from './format-helpers.js';
|
|
3
|
-
import { listInvoices, getInvoice, searchInvoices, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, applyCreditsToInvoice, downloadInvoicePdf, finalizeInvoice, } from '../core/api/invoices.js';
|
|
3
|
+
import { listInvoices, getInvoice, searchInvoices, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, applyCreditsToInvoice, downloadInvoicePdf, finalizeInvoice, listInvoicePayments, listInvoiceCredits, reverseInvoiceCredit, } from '../core/api/invoices.js';
|
|
4
4
|
import { listAttachments } from '../core/api/attachments.js';
|
|
5
5
|
import { apiAction } from './api-action.js';
|
|
6
6
|
import { resolveContactFlag, resolveAccountFlag, resolveTaxProfileFlag } from './resolve.js';
|
|
@@ -556,4 +556,65 @@ export function registerInvoicesCommand(program) {
|
|
|
556
556
|
}
|
|
557
557
|
}
|
|
558
558
|
})(opts));
|
|
559
|
+
// ── clio invoices list-payments ─────────────────────────────────
|
|
560
|
+
invoices
|
|
561
|
+
.command('list-payments <resourceId>')
|
|
562
|
+
.description('List payments recorded against an invoice')
|
|
563
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
564
|
+
.option('--json', 'Output as JSON')
|
|
565
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
566
|
+
const res = await listInvoicePayments(client, resourceId);
|
|
567
|
+
if (opts.json) {
|
|
568
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
const payments = res.data;
|
|
572
|
+
if (payments.length === 0) {
|
|
573
|
+
console.log('No payments recorded for this invoice.');
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
console.log(chalk.bold(`Payments for invoice ${resourceId}:\n`));
|
|
577
|
+
for (const p of payments) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
578
|
+
console.log(` ${chalk.cyan(p.resourceId)} ${p.paymentAmount ?? p.amount ?? ''} on ${p.valueDate ?? ''}`);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
})(opts));
|
|
582
|
+
// ── clio invoices list-credits ──────────────────────────────────
|
|
583
|
+
invoices
|
|
584
|
+
.command('list-credits <resourceId>')
|
|
585
|
+
.description('List credit notes applied to an invoice')
|
|
586
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
587
|
+
.option('--json', 'Output as JSON')
|
|
588
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
589
|
+
const res = await listInvoiceCredits(client, resourceId);
|
|
590
|
+
if (opts.json) {
|
|
591
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
592
|
+
}
|
|
593
|
+
else {
|
|
594
|
+
const credits = res.data;
|
|
595
|
+
if (credits.length === 0) {
|
|
596
|
+
console.log('No credits applied to this invoice.');
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
console.log(chalk.bold(`Credits for invoice ${resourceId}:\n`));
|
|
600
|
+
for (const c of credits) { // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
601
|
+
console.log(` ${chalk.cyan(c.resourceId)} ${c.amount ?? ''}`);
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
})(opts));
|
|
605
|
+
// ── clio invoices reverse-credit ────────────────────────────────
|
|
606
|
+
invoices
|
|
607
|
+
.command('reverse-credit <resourceId> <creditResourceId>')
|
|
608
|
+
.description('Reverse (unapply) a credit note from an invoice')
|
|
609
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
610
|
+
.option('--json', 'Output as JSON')
|
|
611
|
+
.action((resourceId, creditResourceId, opts) => apiAction(async (client) => {
|
|
612
|
+
await reverseInvoiceCredit(client, resourceId, creditResourceId);
|
|
613
|
+
if (opts.json) {
|
|
614
|
+
console.log(JSON.stringify({ reversed: true, resourceId, creditResourceId }));
|
|
615
|
+
}
|
|
616
|
+
else {
|
|
617
|
+
console.log(chalk.green(`Credit ${creditResourceId} reversed from invoice ${resourceId}.`));
|
|
618
|
+
}
|
|
619
|
+
})(opts));
|
|
559
620
|
}
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import chalk from 'chalk';
|
|
2
|
+
import { listNanoClassifiers, getNanoClassifier, searchNanoClassifiers, createNanoClassifier, updateNanoClassifier, deleteNanoClassifier, } from '../core/api/nano-classifiers.js';
|
|
3
|
+
import { apiAction } from './api-action.js';
|
|
4
|
+
import { outputList } from './output.js';
|
|
5
|
+
import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields } from './parsers.js';
|
|
6
|
+
import { paginatedFetch } from './pagination.js';
|
|
7
|
+
import { formatId } from './format-helpers.js';
|
|
8
|
+
const NC_COLUMNS = [
|
|
9
|
+
{ key: 'resourceId', header: 'ID', format: formatId },
|
|
10
|
+
{ key: 'type', header: 'Type' },
|
|
11
|
+
{ key: 'classes', header: 'Classes', format: (v) => Array.isArray(v) ? v.map((c) => c.className).join(', ') : '' },
|
|
12
|
+
];
|
|
13
|
+
export function registerNanoClassifiersCommand(program) {
|
|
14
|
+
const cmd = program
|
|
15
|
+
.command('nano-classifiers')
|
|
16
|
+
.description('Manage nano classifiers (tracking categories)');
|
|
17
|
+
// ── clio nano-classifiers list ──────────────────────────────────
|
|
18
|
+
cmd
|
|
19
|
+
.command('list')
|
|
20
|
+
.description('List nano classifiers')
|
|
21
|
+
.option('--limit <n>', 'Max results (default 100)', parsePositiveInt)
|
|
22
|
+
.option('--offset <n>', 'Page number offset (0-indexed)', parseNonNegativeInt)
|
|
23
|
+
.option('--all', 'Fetch all pages')
|
|
24
|
+
.option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
|
|
25
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
26
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
27
|
+
.option('--json', 'Output as JSON')
|
|
28
|
+
.action(apiAction(async (client, opts) => {
|
|
29
|
+
const result = await paginatedFetch(opts, (p) => listNanoClassifiers(client, p), { label: 'Fetching nano classifiers' });
|
|
30
|
+
outputList(result, NC_COLUMNS, opts, 'Nano Classifiers'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
31
|
+
}));
|
|
32
|
+
// ── clio nano-classifiers get ───────────────────────────────────
|
|
33
|
+
cmd
|
|
34
|
+
.command('get <resourceId>')
|
|
35
|
+
.description('Get a nano classifier by resourceId')
|
|
36
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
37
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
38
|
+
.option('--json', 'Output as JSON')
|
|
39
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
40
|
+
const res = await getNanoClassifier(client, resourceId);
|
|
41
|
+
const nc = res.data;
|
|
42
|
+
if (opts.json) {
|
|
43
|
+
console.log(JSON.stringify(nc, null, 2));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
console.log(chalk.bold('Type:'), nc.type);
|
|
47
|
+
console.log(chalk.bold('ID:'), nc.resourceId);
|
|
48
|
+
console.log(chalk.bold('Classes:'));
|
|
49
|
+
for (const cls of nc.classes) {
|
|
50
|
+
console.log(` ${cls.className} (${cls.resourceId})`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
})(opts));
|
|
54
|
+
// ── clio nano-classifiers search ────────────────────────────────
|
|
55
|
+
cmd
|
|
56
|
+
.command('search <query>')
|
|
57
|
+
.description('Search nano classifiers by type')
|
|
58
|
+
.option('--limit <n>', 'Max results (default 20)', parsePositiveInt)
|
|
59
|
+
.option('--offset <n>', 'Page number offset (0-indexed)', parseNonNegativeInt)
|
|
60
|
+
.option('--all', 'Fetch all pages')
|
|
61
|
+
.option('--max-rows <n>', 'Max rows for --all (default 10000)', parsePositiveInt)
|
|
62
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
63
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
64
|
+
.option('--json', 'Output as JSON')
|
|
65
|
+
.action((query, opts) => apiAction(async (client) => {
|
|
66
|
+
const filter = { type: { contains: query } };
|
|
67
|
+
const sort = { sortBy: ['type'], order: 'ASC' };
|
|
68
|
+
const result = await paginatedFetch(opts, ({ limit, offset }) => searchNanoClassifiers(client, { filter, limit, offset, sort }), { label: 'Searching nano classifiers', defaultLimit: 20 });
|
|
69
|
+
outputList(result, NC_COLUMNS, opts, 'Nano Classifiers'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
70
|
+
})(opts));
|
|
71
|
+
// ── clio nano-classifiers create ────────────────────────────────
|
|
72
|
+
cmd
|
|
73
|
+
.command('create')
|
|
74
|
+
.description('Create a nano classifier')
|
|
75
|
+
.option('--type <type>', 'Classifier type name')
|
|
76
|
+
.option('--classes <json>', 'Class names as JSON array: ["Sales","Marketing"] or comma-separated: Sales,Marketing')
|
|
77
|
+
.option('--printable', 'Show on printed documents (default: false)')
|
|
78
|
+
.option('--input <file>', 'Read full request body from JSON file (or pipe via stdin)')
|
|
79
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
80
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
81
|
+
.option('--json', 'Output as JSON')
|
|
82
|
+
.action(apiAction(async (client, opts) => {
|
|
83
|
+
const body = readBodyInput(opts);
|
|
84
|
+
let res;
|
|
85
|
+
if (body) {
|
|
86
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- user-provided JSON, API validates
|
|
87
|
+
res = await createNanoClassifier(client, body);
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
requireFields(opts, [
|
|
91
|
+
{ flag: '--type', key: 'type' },
|
|
92
|
+
{ flag: '--classes', key: 'classes' },
|
|
93
|
+
]);
|
|
94
|
+
let classes;
|
|
95
|
+
try {
|
|
96
|
+
classes = JSON.parse(opts.classes);
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
classes = opts.classes.split(',').map((s) => s.trim());
|
|
100
|
+
}
|
|
101
|
+
res = await createNanoClassifier(client, {
|
|
102
|
+
type: opts.type,
|
|
103
|
+
classes,
|
|
104
|
+
printable: opts.printable === true,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
if (opts.json) {
|
|
108
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
console.log(chalk.green(`Nano classifier created: ${res.data.type ?? opts.type}`));
|
|
112
|
+
console.log(chalk.bold('ID:'), res.data.resourceId);
|
|
113
|
+
}
|
|
114
|
+
}));
|
|
115
|
+
// ── clio nano-classifiers update ────────────────────────────────
|
|
116
|
+
cmd
|
|
117
|
+
.command('update <resourceId>')
|
|
118
|
+
.description('Update a nano classifier')
|
|
119
|
+
.option('--type <type>', 'New classifier type name')
|
|
120
|
+
.option('--classes <json>', 'Updated class names as JSON array or comma-separated')
|
|
121
|
+
.option('--printable', 'Show on printed documents')
|
|
122
|
+
.option('--input <file>', 'Read full request body from JSON file (or pipe via stdin)')
|
|
123
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
124
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
125
|
+
.option('--json', 'Output as JSON')
|
|
126
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
127
|
+
const body = readBodyInput(opts);
|
|
128
|
+
let res;
|
|
129
|
+
if (body) {
|
|
130
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- user-provided JSON, API validates
|
|
131
|
+
res = await updateNanoClassifier(client, resourceId, body);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
const data = {};
|
|
135
|
+
if (opts.type)
|
|
136
|
+
data.type = opts.type;
|
|
137
|
+
if (opts.classes) {
|
|
138
|
+
try {
|
|
139
|
+
data.classes = JSON.parse(opts.classes);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
data.classes = opts.classes.split(',').map((s) => s.trim());
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
if (opts.printable !== undefined)
|
|
146
|
+
data.printable = opts.printable;
|
|
147
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
148
|
+
res = await updateNanoClassifier(client, resourceId, data);
|
|
149
|
+
}
|
|
150
|
+
if (opts.json) {
|
|
151
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(chalk.green(`Nano classifier ${resourceId} updated.`));
|
|
155
|
+
}
|
|
156
|
+
})(opts));
|
|
157
|
+
// ── clio nano-classifiers delete ────────────────────────────────
|
|
158
|
+
cmd
|
|
159
|
+
.command('delete <resourceId>')
|
|
160
|
+
.description('Delete a nano classifier')
|
|
161
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
162
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
163
|
+
.option('--json', 'Output as JSON')
|
|
164
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
165
|
+
await deleteNanoClassifier(client, resourceId);
|
|
166
|
+
if (opts.json) {
|
|
167
|
+
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
console.log(chalk.green(`Nano classifier ${resourceId} deleted.`));
|
|
171
|
+
}
|
|
172
|
+
})(opts));
|
|
173
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { listPayments, searchPayments, } from '../core/api/payments.js';
|
|
2
|
+
import { listPayments, searchPayments, getPayment, updatePayment, deletePayment, } from '../core/api/payments.js';
|
|
3
3
|
import { apiAction } from './api-action.js';
|
|
4
4
|
import { parsePositiveInt, parseNonNegativeInt } from './parsers.js';
|
|
5
5
|
import { paginatedFetch } from './pagination.js';
|
|
@@ -75,4 +75,69 @@ export function registerPaymentsCommand(program) {
|
|
|
75
75
|
const result = await paginatedFetch(opts, ({ limit, offset }) => searchPayments(client, { filter: searchFilter, limit, offset, sort }), { label: 'Searching transactions', defaultLimit: 20 });
|
|
76
76
|
outputList(result, PAYMENT_COLUMNS, opts, 'Payments'); // eslint-disable-line @typescript-eslint/no-explicit-any
|
|
77
77
|
}));
|
|
78
|
+
// ── clio payments get ───────────────────────────────────────────
|
|
79
|
+
payments
|
|
80
|
+
.command('get <resourceId>')
|
|
81
|
+
.description('Get a specific payment record')
|
|
82
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
83
|
+
.option('--format <type>', 'Output format: table, json, csv, yaml')
|
|
84
|
+
.option('--json', 'Output as JSON')
|
|
85
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
86
|
+
const res = await getPayment(client, resourceId);
|
|
87
|
+
const p = res.data;
|
|
88
|
+
if (opts.json) {
|
|
89
|
+
console.log(JSON.stringify(p, null, 2));
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
console.log(chalk.bold('ID:'), p.resourceId);
|
|
93
|
+
console.log(chalk.bold('Amount:'), p.paymentAmount);
|
|
94
|
+
console.log(chalk.bold('Date:'), p.valueDate);
|
|
95
|
+
console.log(chalk.bold('Method:'), p.paymentMethod);
|
|
96
|
+
console.log(chalk.bold('Reference:'), p.reference);
|
|
97
|
+
}
|
|
98
|
+
})(opts));
|
|
99
|
+
// ── clio payments update ────────────────────────────────────────
|
|
100
|
+
payments
|
|
101
|
+
.command('update <resourceId>')
|
|
102
|
+
.description('Update a payment record')
|
|
103
|
+
.option('--amount <n>', 'Corrected payment amount')
|
|
104
|
+
.option('--reference <ref>', 'Payment reference')
|
|
105
|
+
.option('--date <YYYY-MM-DD>', 'Payment date')
|
|
106
|
+
.option('--method <method>', 'Payment method (BANK_TRANSFER, CASH, CHEQUE, etc.)')
|
|
107
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
108
|
+
.option('--json', 'Output as JSON')
|
|
109
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
110
|
+
const data = {};
|
|
111
|
+
if (opts.amount)
|
|
112
|
+
data.paymentAmount = parseFloat(opts.amount);
|
|
113
|
+
if (opts.reference)
|
|
114
|
+
data.reference = opts.reference;
|
|
115
|
+
if (opts.date)
|
|
116
|
+
data.valueDate = opts.date;
|
|
117
|
+
if (opts.method)
|
|
118
|
+
data.paymentMethod = opts.method;
|
|
119
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
120
|
+
const res = await updatePayment(client, resourceId, data);
|
|
121
|
+
if (opts.json) {
|
|
122
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
console.log(chalk.green(`Payment ${resourceId} updated.`));
|
|
126
|
+
}
|
|
127
|
+
})(opts));
|
|
128
|
+
// ── clio payments delete ────────────────────────────────────────
|
|
129
|
+
payments
|
|
130
|
+
.command('delete <resourceId>')
|
|
131
|
+
.description('Delete (void) a payment record')
|
|
132
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
133
|
+
.option('--json', 'Output as JSON')
|
|
134
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
135
|
+
await deletePayment(client, resourceId);
|
|
136
|
+
if (opts.json) {
|
|
137
|
+
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
138
|
+
}
|
|
139
|
+
else {
|
|
140
|
+
console.log(chalk.green(`Payment ${resourceId} deleted.`));
|
|
141
|
+
}
|
|
142
|
+
})(opts));
|
|
78
143
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import chalk from 'chalk';
|
|
2
|
-
import { listScheduledInvoices, listScheduledBills, listScheduledJournals, } from '../core/api/schedulers.js';
|
|
2
|
+
import { listScheduledInvoices, listScheduledBills, listScheduledJournals, getScheduledInvoice, updateScheduledInvoice, deleteScheduledInvoice, getScheduledBill, updateScheduledBill, deleteScheduledBill, getScheduledJournal, updateScheduledJournal, deleteScheduledJournal, } from '../core/api/schedulers.js';
|
|
3
3
|
import { createScheduledInvoice } from '../core/api/invoices.js';
|
|
4
4
|
import { createScheduledBill } from '../core/api/bills.js';
|
|
5
5
|
import { createScheduledJournal } from '../core/api/journals.js';
|
|
@@ -10,7 +10,7 @@ import { parsePositiveInt, parseNonNegativeInt, readBodyInput, requireFields, pa
|
|
|
10
10
|
import { paginatedFetch } from './pagination.js';
|
|
11
11
|
const SCHEDULER_COLUMNS = [
|
|
12
12
|
{ key: 'resourceId', header: 'ID', format: formatId },
|
|
13
|
-
{ key: '
|
|
13
|
+
{ key: 'interval', header: 'Repeat' },
|
|
14
14
|
{ key: 'startDate', header: 'Start' },
|
|
15
15
|
{ key: 'status', header: 'Status' },
|
|
16
16
|
];
|
|
@@ -239,4 +239,211 @@ Strings are replaced with values relative to the transaction date.`);
|
|
|
239
239
|
console.log(chalk.green('Scheduled journal created'));
|
|
240
240
|
}
|
|
241
241
|
}));
|
|
242
|
+
// ── clio schedulers get-invoice ─────────────────────────────────
|
|
243
|
+
cmd
|
|
244
|
+
.command('get-invoice <resourceId>')
|
|
245
|
+
.description('Get a scheduled invoice by resourceId')
|
|
246
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
247
|
+
.option('--json', 'Output as JSON')
|
|
248
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
249
|
+
const res = await getScheduledInvoice(client, resourceId);
|
|
250
|
+
if (opts.json) {
|
|
251
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
const s = res.data;
|
|
255
|
+
console.log(chalk.bold('ID:'), s.resourceId);
|
|
256
|
+
console.log(chalk.bold('Status:'), s.status);
|
|
257
|
+
console.log(chalk.bold('Repeat:'), s.interval ?? s.repeat);
|
|
258
|
+
console.log(chalk.bold('Start:'), s.startDate);
|
|
259
|
+
if (s.endDate)
|
|
260
|
+
console.log(chalk.bold('End:'), s.endDate);
|
|
261
|
+
}
|
|
262
|
+
})(opts));
|
|
263
|
+
// ── clio schedulers update-invoice ──────────────────────────────
|
|
264
|
+
cmd
|
|
265
|
+
.command('update-invoice <resourceId>')
|
|
266
|
+
.description('Update a scheduled invoice')
|
|
267
|
+
.option('--repeat <interval>', 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY')
|
|
268
|
+
.option('--start-date <YYYY-MM-DD>', 'First occurrence date')
|
|
269
|
+
.option('--end-date <YYYY-MM-DD>', 'Last occurrence date')
|
|
270
|
+
.option('--status <status>', 'Status: ACTIVE or PAUSED')
|
|
271
|
+
.option('--input <file>', 'Read full request body from JSON file (or pipe via stdin)')
|
|
272
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
273
|
+
.option('--json', 'Output as JSON')
|
|
274
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
275
|
+
const body = readBodyInput(opts);
|
|
276
|
+
const data = body ?? {};
|
|
277
|
+
if (!body) {
|
|
278
|
+
if (opts.repeat)
|
|
279
|
+
data.repeat = opts.repeat;
|
|
280
|
+
if (opts.startDate)
|
|
281
|
+
data.startDate = opts.startDate;
|
|
282
|
+
if (opts.endDate)
|
|
283
|
+
data.endDate = opts.endDate;
|
|
284
|
+
if (opts.status)
|
|
285
|
+
data.status = opts.status;
|
|
286
|
+
}
|
|
287
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
288
|
+
const res = await updateScheduledInvoice(client, resourceId, data);
|
|
289
|
+
if (opts.json) {
|
|
290
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
console.log(chalk.green(`Scheduled invoice ${resourceId} updated.`));
|
|
294
|
+
}
|
|
295
|
+
})(opts));
|
|
296
|
+
// ── clio schedulers delete-invoice ──────────────────────────────
|
|
297
|
+
cmd
|
|
298
|
+
.command('delete-invoice <resourceId>')
|
|
299
|
+
.description('Delete a scheduled invoice')
|
|
300
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
301
|
+
.option('--json', 'Output as JSON')
|
|
302
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
303
|
+
await deleteScheduledInvoice(client, resourceId);
|
|
304
|
+
if (opts.json) {
|
|
305
|
+
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
306
|
+
}
|
|
307
|
+
else {
|
|
308
|
+
console.log(chalk.green(`Scheduled invoice ${resourceId} deleted.`));
|
|
309
|
+
}
|
|
310
|
+
})(opts));
|
|
311
|
+
// ── clio schedulers get-bill ────────────────────────────────────
|
|
312
|
+
cmd
|
|
313
|
+
.command('get-bill <resourceId>')
|
|
314
|
+
.description('Get a scheduled bill by resourceId')
|
|
315
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
316
|
+
.option('--json', 'Output as JSON')
|
|
317
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
318
|
+
const res = await getScheduledBill(client, resourceId);
|
|
319
|
+
if (opts.json) {
|
|
320
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
321
|
+
}
|
|
322
|
+
else {
|
|
323
|
+
const s = res.data;
|
|
324
|
+
console.log(chalk.bold('ID:'), s.resourceId);
|
|
325
|
+
console.log(chalk.bold('Status:'), s.status);
|
|
326
|
+
console.log(chalk.bold('Repeat:'), s.interval ?? s.repeat);
|
|
327
|
+
console.log(chalk.bold('Start:'), s.startDate);
|
|
328
|
+
if (s.endDate)
|
|
329
|
+
console.log(chalk.bold('End:'), s.endDate);
|
|
330
|
+
}
|
|
331
|
+
})(opts));
|
|
332
|
+
// ── clio schedulers update-bill ─────────────────────────────────
|
|
333
|
+
cmd
|
|
334
|
+
.command('update-bill <resourceId>')
|
|
335
|
+
.description('Update a scheduled bill')
|
|
336
|
+
.option('--repeat <interval>', 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY')
|
|
337
|
+
.option('--start-date <YYYY-MM-DD>', 'First occurrence date')
|
|
338
|
+
.option('--end-date <YYYY-MM-DD>', 'Last occurrence date')
|
|
339
|
+
.option('--status <status>', 'Status: ACTIVE or PAUSED')
|
|
340
|
+
.option('--input <file>', 'Read full request body from JSON file (or pipe via stdin)')
|
|
341
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
342
|
+
.option('--json', 'Output as JSON')
|
|
343
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
344
|
+
const body = readBodyInput(opts);
|
|
345
|
+
const data = body ?? {};
|
|
346
|
+
if (!body) {
|
|
347
|
+
if (opts.repeat)
|
|
348
|
+
data.repeat = opts.repeat;
|
|
349
|
+
if (opts.startDate)
|
|
350
|
+
data.startDate = opts.startDate;
|
|
351
|
+
if (opts.endDate)
|
|
352
|
+
data.endDate = opts.endDate;
|
|
353
|
+
if (opts.status)
|
|
354
|
+
data.status = opts.status;
|
|
355
|
+
}
|
|
356
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
357
|
+
const res = await updateScheduledBill(client, resourceId, data);
|
|
358
|
+
if (opts.json) {
|
|
359
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
360
|
+
}
|
|
361
|
+
else {
|
|
362
|
+
console.log(chalk.green(`Scheduled bill ${resourceId} updated.`));
|
|
363
|
+
}
|
|
364
|
+
})(opts));
|
|
365
|
+
// ── clio schedulers delete-bill ─────────────────────────────────
|
|
366
|
+
cmd
|
|
367
|
+
.command('delete-bill <resourceId>')
|
|
368
|
+
.description('Delete a scheduled bill')
|
|
369
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
370
|
+
.option('--json', 'Output as JSON')
|
|
371
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
372
|
+
await deleteScheduledBill(client, resourceId);
|
|
373
|
+
if (opts.json) {
|
|
374
|
+
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
375
|
+
}
|
|
376
|
+
else {
|
|
377
|
+
console.log(chalk.green(`Scheduled bill ${resourceId} deleted.`));
|
|
378
|
+
}
|
|
379
|
+
})(opts));
|
|
380
|
+
// ── clio schedulers get-journal ─────────────────────────────────
|
|
381
|
+
cmd
|
|
382
|
+
.command('get-journal <resourceId>')
|
|
383
|
+
.description('Get a scheduled journal by resourceId')
|
|
384
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
385
|
+
.option('--json', 'Output as JSON')
|
|
386
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
387
|
+
const res = await getScheduledJournal(client, resourceId);
|
|
388
|
+
if (opts.json) {
|
|
389
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
const s = res.data;
|
|
393
|
+
console.log(chalk.bold('ID:'), s.resourceId);
|
|
394
|
+
console.log(chalk.bold('Status:'), s.status);
|
|
395
|
+
console.log(chalk.bold('Repeat:'), s.interval ?? s.repeat);
|
|
396
|
+
console.log(chalk.bold('Start:'), s.startDate);
|
|
397
|
+
if (s.endDate)
|
|
398
|
+
console.log(chalk.bold('End:'), s.endDate);
|
|
399
|
+
}
|
|
400
|
+
})(opts));
|
|
401
|
+
// ── clio schedulers update-journal ──────────────────────────────
|
|
402
|
+
cmd
|
|
403
|
+
.command('update-journal <resourceId>')
|
|
404
|
+
.description('Update a scheduled journal')
|
|
405
|
+
.option('--repeat <interval>', 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY')
|
|
406
|
+
.option('--start-date <YYYY-MM-DD>', 'First occurrence date')
|
|
407
|
+
.option('--end-date <YYYY-MM-DD>', 'Last occurrence date')
|
|
408
|
+
.option('--status <status>', 'Status: ACTIVE or PAUSED')
|
|
409
|
+
.option('--input <file>', 'Read full request body from JSON file (or pipe via stdin)')
|
|
410
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
411
|
+
.option('--json', 'Output as JSON')
|
|
412
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
413
|
+
const body = readBodyInput(opts);
|
|
414
|
+
const data = body ?? {};
|
|
415
|
+
if (!body) {
|
|
416
|
+
if (opts.repeat)
|
|
417
|
+
data.repeat = opts.repeat;
|
|
418
|
+
if (opts.startDate)
|
|
419
|
+
data.startDate = opts.startDate;
|
|
420
|
+
if (opts.endDate)
|
|
421
|
+
data.endDate = opts.endDate;
|
|
422
|
+
if (opts.status)
|
|
423
|
+
data.status = opts.status;
|
|
424
|
+
}
|
|
425
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
426
|
+
const res = await updateScheduledJournal(client, resourceId, data);
|
|
427
|
+
if (opts.json) {
|
|
428
|
+
console.log(JSON.stringify(res.data, null, 2));
|
|
429
|
+
}
|
|
430
|
+
else {
|
|
431
|
+
console.log(chalk.green(`Scheduled journal ${resourceId} updated.`));
|
|
432
|
+
}
|
|
433
|
+
})(opts));
|
|
434
|
+
// ── clio schedulers delete-journal ──────────────────────────────
|
|
435
|
+
cmd
|
|
436
|
+
.command('delete-journal <resourceId>')
|
|
437
|
+
.description('Delete a scheduled journal')
|
|
438
|
+
.option('--api-key <key>', 'API key (overrides stored/env)')
|
|
439
|
+
.option('--json', 'Output as JSON')
|
|
440
|
+
.action((resourceId, opts) => apiAction(async (client) => {
|
|
441
|
+
await deleteScheduledJournal(client, resourceId);
|
|
442
|
+
if (opts.json) {
|
|
443
|
+
console.log(JSON.stringify({ deleted: true, resourceId }));
|
|
444
|
+
}
|
|
445
|
+
else {
|
|
446
|
+
console.log(chalk.green(`Scheduled journal ${resourceId} deleted.`));
|
|
447
|
+
}
|
|
448
|
+
})(opts));
|
|
242
449
|
}
|
package/dist/core/api/bills.js
CHANGED
|
@@ -39,3 +39,23 @@ export async function applyCreditsToBill(client, billResourceId, credits) {
|
|
|
39
39
|
export async function createScheduledBill(client, data) {
|
|
40
40
|
return client.post('/api/v1/scheduled/bills', data);
|
|
41
41
|
}
|
|
42
|
+
// ── Payment & Credit Sub-resources ───────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* List payments recorded against a bill.
|
|
45
|
+
* API returns raw array (not wrapped in {data: [...]}).
|
|
46
|
+
*/
|
|
47
|
+
export async function listBillPayments(client, billId) {
|
|
48
|
+
const res = await client.get(`/api/v1/bills/${billId}/payments`);
|
|
49
|
+
return { data: Array.isArray(res) ? res : res.data ?? [] };
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* List credit notes applied to a bill.
|
|
53
|
+
* API returns raw array (not wrapped in {data: [...]}).
|
|
54
|
+
*/
|
|
55
|
+
export async function listBillCredits(client, billId) {
|
|
56
|
+
const res = await client.get(`/api/v1/bills/${billId}/credits`);
|
|
57
|
+
return { data: Array.isArray(res) ? res : res.data ?? [] };
|
|
58
|
+
}
|
|
59
|
+
export async function reverseBillCredit(client, billId, creditId) {
|
|
60
|
+
await client.delete(`/api/v1/bills/${billId}/credits/${creditId}`);
|
|
61
|
+
}
|
package/dist/core/api/index.js
CHANGED
|
@@ -34,5 +34,6 @@ export * as contactGroups from './contact-groups.js';
|
|
|
34
34
|
export * as inventory from './inventory.js';
|
|
35
35
|
export * as search from './search.js';
|
|
36
36
|
export * as quickFix from './quick-fix.js';
|
|
37
|
+
export * as nanoClassifiers from './nano-classifiers.js';
|
|
37
38
|
// ── Guards (pre-flight checks for create operations) ────────────
|
|
38
39
|
export * from './guards.js';
|
|
@@ -50,3 +50,23 @@ export async function finalizeInvoice(client, resourceId, data) {
|
|
|
50
50
|
export async function downloadInvoicePdf(client, resourceId) {
|
|
51
51
|
return client.get(`/api/v1/invoices/${resourceId}/download`);
|
|
52
52
|
}
|
|
53
|
+
// ── Payment & Credit Sub-resources ───────────────────────────────
|
|
54
|
+
/**
|
|
55
|
+
* List payments recorded against an invoice.
|
|
56
|
+
* API returns raw array (not wrapped in {data: [...]}).
|
|
57
|
+
*/
|
|
58
|
+
export async function listInvoicePayments(client, invoiceId) {
|
|
59
|
+
const res = await client.get(`/api/v1/invoices/${invoiceId}/payments`);
|
|
60
|
+
return { data: Array.isArray(res) ? res : res.data ?? [] };
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* List credit notes applied to an invoice.
|
|
64
|
+
* API returns raw array (not wrapped in {data: [...]}).
|
|
65
|
+
*/
|
|
66
|
+
export async function listInvoiceCredits(client, invoiceId) {
|
|
67
|
+
const res = await client.get(`/api/v1/invoices/${invoiceId}/credits`);
|
|
68
|
+
return { data: Array.isArray(res) ? res : res.data ?? [] };
|
|
69
|
+
}
|
|
70
|
+
export async function reverseInvoiceCredit(client, invoiceId, creditId) {
|
|
71
|
+
await client.delete(`/api/v1/invoices/${invoiceId}/credits/${creditId}`);
|
|
72
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
export async function listNanoClassifiers(client, params) {
|
|
2
|
+
return client.list('/api/v1/nano-classifiers', params);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Get a nano classifier by resourceId.
|
|
6
|
+
* API returns double-wrapped: `{ data: { data: [...], totalElements, totalPages } }`.
|
|
7
|
+
* Extract the first element from the inner paginated response.
|
|
8
|
+
*/
|
|
9
|
+
export async function getNanoClassifier(client, resourceId) {
|
|
10
|
+
const res = await client.get(`/api/v1/nano-classifiers/${resourceId}`);
|
|
11
|
+
return { data: res.data.data[0] };
|
|
12
|
+
}
|
|
13
|
+
export async function searchNanoClassifiers(client, params) {
|
|
14
|
+
return client.search('/api/v1/nano-classifiers/search', params);
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Create a nano classifier.
|
|
18
|
+
* API expects `classes: string[]` (simple string array, not `[{className}]`)
|
|
19
|
+
* and `printable: boolean` is required.
|
|
20
|
+
*/
|
|
21
|
+
export async function createNanoClassifier(client, data) {
|
|
22
|
+
return client.post('/api/v1/nano-classifiers', {
|
|
23
|
+
...data,
|
|
24
|
+
printable: data.printable ?? false,
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Update a nano classifier.
|
|
29
|
+
* `classes` can be either string[] (new class names) or
|
|
30
|
+
* NanoClassifierClass[] (existing classes with resourceIds).
|
|
31
|
+
*/
|
|
32
|
+
export async function updateNanoClassifier(client, resourceId, data) {
|
|
33
|
+
return client.put(`/api/v1/nano-classifiers/${resourceId}`, data);
|
|
34
|
+
}
|
|
35
|
+
export async function deleteNanoClassifier(client, resourceId) {
|
|
36
|
+
await client.delete(`/api/v1/nano-classifiers/${resourceId}`);
|
|
37
|
+
}
|
|
@@ -16,3 +16,12 @@ export async function listPayments(client, params) {
|
|
|
16
16
|
export async function searchPayments(client, params) {
|
|
17
17
|
return client.search('/api/v1/cashflow-transactions/search', params);
|
|
18
18
|
}
|
|
19
|
+
export async function getPayment(client, resourceId) {
|
|
20
|
+
return client.get(`/api/v1/payments/${resourceId}`);
|
|
21
|
+
}
|
|
22
|
+
export async function updatePayment(client, resourceId, data) {
|
|
23
|
+
return client.put(`/api/v1/payments/${resourceId}`, data);
|
|
24
|
+
}
|
|
25
|
+
export async function deletePayment(client, resourceId) {
|
|
26
|
+
await client.delete(`/api/v1/payments/${resourceId}`);
|
|
27
|
+
}
|
|
@@ -7,3 +7,48 @@ export async function listScheduledBills(client, params) {
|
|
|
7
7
|
export async function listScheduledJournals(client, params) {
|
|
8
8
|
return client.list('/api/v1/scheduled/journals', params);
|
|
9
9
|
}
|
|
10
|
+
// ── Scheduled Invoice CRUD ───────────────────────────────────────
|
|
11
|
+
export async function getScheduledInvoice(client, resourceId) {
|
|
12
|
+
return client.get(`/api/v1/scheduled/invoices/${resourceId}`);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Update a scheduled invoice.
|
|
16
|
+
* Accepts scheduling fields (repeat, startDate, endDate, status) AND the full
|
|
17
|
+
* invoice template (invoice: { reference, valueDate, dueDate, contactResourceId, lineItems, ... }).
|
|
18
|
+
*/
|
|
19
|
+
export async function updateScheduledInvoice(client, resourceId, data) {
|
|
20
|
+
return client.put(`/api/v1/scheduled/invoices/${resourceId}`, data);
|
|
21
|
+
}
|
|
22
|
+
export async function deleteScheduledInvoice(client, resourceId) {
|
|
23
|
+
await client.delete(`/api/v1/scheduled/invoices/${resourceId}`);
|
|
24
|
+
}
|
|
25
|
+
// ── Scheduled Bill CRUD ──────────────────────────────────────────
|
|
26
|
+
export async function getScheduledBill(client, resourceId) {
|
|
27
|
+
return client.get(`/api/v1/scheduled/bills/${resourceId}`);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Update a scheduled bill.
|
|
31
|
+
* Accepts scheduling fields (repeat, startDate, endDate, status) AND the full
|
|
32
|
+
* bill template (bill: { reference, valueDate, dueDate, contactResourceId, lineItems, ... }).
|
|
33
|
+
*/
|
|
34
|
+
export async function updateScheduledBill(client, resourceId, data) {
|
|
35
|
+
return client.put(`/api/v1/scheduled/bills/${resourceId}`, data);
|
|
36
|
+
}
|
|
37
|
+
export async function deleteScheduledBill(client, resourceId) {
|
|
38
|
+
await client.delete(`/api/v1/scheduled/bills/${resourceId}`);
|
|
39
|
+
}
|
|
40
|
+
// ── Scheduled Journal CRUD ───────────────────────────────────────
|
|
41
|
+
export async function getScheduledJournal(client, resourceId) {
|
|
42
|
+
return client.get(`/api/v1/scheduled/journals/${resourceId}`);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Update a scheduled journal.
|
|
46
|
+
* Accepts scheduling fields (repeat, startDate, endDate, status) AND the full
|
|
47
|
+
* journal template (valueDate, schedulerEntries, reference, notes).
|
|
48
|
+
*/
|
|
49
|
+
export async function updateScheduledJournal(client, resourceId, data) {
|
|
50
|
+
return client.put(`/api/v1/scheduled/journals/${resourceId}`, data);
|
|
51
|
+
}
|
|
52
|
+
export async function deleteScheduledJournal(client, resourceId) {
|
|
53
|
+
await client.delete(`/api/v1/scheduled/journals/${resourceId}`);
|
|
54
|
+
}
|
|
@@ -73,6 +73,11 @@ export const TOOL_NAMESPACES = [
|
|
|
73
73
|
description: 'Tags for categorizing transactions. Custom fields for adding metadata (text, date, dropdown). Create, search, delete tags and custom fields.',
|
|
74
74
|
groups: ['tags', 'custom_fields'],
|
|
75
75
|
},
|
|
76
|
+
{
|
|
77
|
+
name: 'nano_classifiers',
|
|
78
|
+
description: 'Nano classifiers (tracking categories/dimensions). List, search, create, update, delete classifiers and their classes. Used for line-item tagging and dimensional reporting. Also: tracking categories, cost centers, departments, projects.',
|
|
79
|
+
groups: ['nano_classifiers'],
|
|
80
|
+
},
|
|
76
81
|
// ── Accounting Setup ────────────────────────────────────────
|
|
77
82
|
{
|
|
78
83
|
name: 'chart_of_accounts',
|
|
@@ -127,7 +132,7 @@ export const TOOL_NAMESPACES = [
|
|
|
127
132
|
// ── Payments & Search ───────────────────────────────────────
|
|
128
133
|
{
|
|
129
134
|
name: 'payments_and_search',
|
|
130
|
-
description: '
|
|
135
|
+
description: 'Payment records: get, update, delete individual payments. List payments/credits on invoices and bills. Reverse credit applications. Cashflow transaction search. Universal cross-entity search. Also: void payment, payment history, credit note applications.',
|
|
131
136
|
groups: ['payments', 'cashflow', 'search'],
|
|
132
137
|
},
|
|
133
138
|
// ── Quick Fix (Bulk Update) ────────────────────────────────
|
|
@@ -2,8 +2,8 @@ import { findExistingContact, findExistingItem, findExistingAccount, findExistin
|
|
|
2
2
|
import { getOrganization } from '../api/organization.js';
|
|
3
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
|
-
import { listInvoices, searchInvoices, getInvoice, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, createScheduledInvoice, finalizeInvoice, applyCreditsToInvoice, downloadInvoicePdf, } from '../api/invoices.js';
|
|
6
|
-
import { listBills, searchBills, getBill, createBill, updateBill, deleteBill, createBillPayment, createScheduledBill, finalizeBill, applyCreditsToBill, } from '../api/bills.js';
|
|
5
|
+
import { listInvoices, searchInvoices, getInvoice, createInvoice, updateInvoice, deleteInvoice, createInvoicePayment, createScheduledInvoice, finalizeInvoice, applyCreditsToInvoice, downloadInvoicePdf, listInvoicePayments, listInvoiceCredits, reverseInvoiceCredit, } from '../api/invoices.js';
|
|
6
|
+
import { listBills, searchBills, getBill, createBill, updateBill, deleteBill, createBillPayment, createScheduledBill, finalizeBill, applyCreditsToBill, listBillPayments, listBillCredits, reverseBillCredit, } from '../api/bills.js';
|
|
7
7
|
import { listJournals, searchJournals, getJournal, createJournal, deleteJournal, updateJournal, createScheduledJournal, } from '../api/journals.js';
|
|
8
8
|
import { generateTrialBalance, generateBalanceSheet, generateProfitAndLoss, generateCashflow, generateArSummary, generateApSummary, generateCashBalance, generateGeneralLedger, generateVatLedger, generateEquityMovement, generateBankBalanceSummary, generateBankReconSummary, generateBankReconDetails, generateFaSummary, generateFaReconSummary, generateArReport, } from '../api/reports.js';
|
|
9
9
|
import { listBankAccounts, addBankRecords, importBankStatement } from '../api/bank.js';
|
|
@@ -16,7 +16,7 @@ import { listCurrencies, addCurrency, listCurrencyRates, addCurrencyRate, update
|
|
|
16
16
|
import { listTaxProfiles, listTaxTypes, createTaxProfile, searchTaxProfiles, getTaxProfile, updateTaxProfile, listWithholdingTaxCodes, } from '../api/tax-profiles.js';
|
|
17
17
|
import { createCashIn, listCashIn, getCashIn, updateCashIn, createCashOut, listCashOut, getCashOut, updateCashOut, } from '../api/cash-entries.js';
|
|
18
18
|
import { createCashTransfer, listCashTransfers, getCashTransfer } from '../api/cash-transfers.js';
|
|
19
|
-
import { listScheduledInvoices, listScheduledBills, listScheduledJournals } from '../api/schedulers.js';
|
|
19
|
+
import { listScheduledInvoices, listScheduledBills, listScheduledJournals, getScheduledInvoice, updateScheduledInvoice, deleteScheduledInvoice, getScheduledBill, updateScheduledBill, deleteScheduledBill, getScheduledJournal, updateScheduledJournal, deleteScheduledJournal, } from '../api/schedulers.js';
|
|
20
20
|
import { runCalculator, RECIPE_TYPES } from '../recipe/dispatch.js';
|
|
21
21
|
import { planRecipe, extractBlueprint } from '../recipe/plan.js';
|
|
22
22
|
import { executeRecipe } from '../recipe/engine.js';
|
|
@@ -26,7 +26,7 @@ import { deleteCashEntry, searchCashflowTransactions } from '../api/cashflow.js'
|
|
|
26
26
|
import { createTransferTrialBalance } from '../api/transfer-trial-balance.js';
|
|
27
27
|
import { listBookmarks, getBookmark, createBookmarks, updateBookmark } from '../api/bookmarks.js';
|
|
28
28
|
import { listOrgUsers, searchOrgUsers, inviteOrgUser, updateOrgUser, removeOrgUser } from '../api/org-users.js';
|
|
29
|
-
import { listPayments, searchPayments } from '../api/payments.js';
|
|
29
|
+
import { listPayments, searchPayments, getPayment, updatePayment, deletePayment } from '../api/payments.js';
|
|
30
30
|
import { downloadExport } from '../api/data-exports.js';
|
|
31
31
|
import { listAttachments, addAttachment, deleteAttachment, fetchAttachmentTable } from '../api/attachments.js';
|
|
32
32
|
import { createFromAttachment, searchMagicWorkflows } from '../api/magic.js';
|
|
@@ -41,6 +41,7 @@ import { listInventoryItems, getInventoryBalance } from '../api/inventory.js';
|
|
|
41
41
|
import { universalSearch } from '../api/search.js';
|
|
42
42
|
import { listCustomFields, getCustomField, searchCustomFields, createCustomField, updateCustomField, deleteCustomField } from '../api/custom-fields.js';
|
|
43
43
|
import { quickFix, quickFixLineItems, QUICK_FIX_ENTITIES } from '../api/quick-fix.js';
|
|
44
|
+
import { listNanoClassifiers, getNanoClassifier, searchNanoClassifiers, createNanoClassifier, updateNanoClassifier, deleteNanoClassifier, } from '../api/nano-classifiers.js';
|
|
44
45
|
// Job blueprints (offline — no API calls)
|
|
45
46
|
import { generateMonthEndBlueprint } from '../jobs/month-end/blueprint.js';
|
|
46
47
|
import { generateQuarterEndBlueprint } from '../jobs/quarter-end/blueprint.js';
|
|
@@ -4053,15 +4054,25 @@ Response: { updated: string[], failed: [{ resourceId, error, errorCode }] }. On
|
|
|
4053
4054
|
params: {
|
|
4054
4055
|
entity: { type: 'string', enum: [...QUICK_FIX_ENTITIES], description: 'Transaction type to update' },
|
|
4055
4056
|
resourceIds: { type: 'array', items: { type: 'string' }, description: 'Array of transaction resourceIds to update' },
|
|
4056
|
-
attributes: { type: 'object', description: '
|
|
4057
|
+
attributes: { type: 'object', description: 'REQUIRED object with fields to update. Only included fields are changed, omitted fields left unchanged. Example: { "valueDate": "2026-03-15", "tags": ["Q1"] }. See tool description for per-entity attribute list.' },
|
|
4057
4058
|
},
|
|
4058
|
-
required: ['entity', 'resourceIds'
|
|
4059
|
+
required: ['entity', 'resourceIds'],
|
|
4059
4060
|
group: 'quick_fix',
|
|
4060
4061
|
readOnly: false,
|
|
4061
|
-
execute: async (ctx, input) =>
|
|
4062
|
-
|
|
4063
|
-
attributes
|
|
4064
|
-
|
|
4062
|
+
execute: async (ctx, input) => {
|
|
4063
|
+
// Agents sometimes pass attribute fields at top level instead of nesting in `attributes`.
|
|
4064
|
+
// Auto-sweep unrecognized keys into attributes for resilience.
|
|
4065
|
+
const KNOWN_KEYS = new Set(['entity', 'resourceIds', 'attributes']);
|
|
4066
|
+
let attrs = input.attributes ?? {};
|
|
4067
|
+
for (const [k, v] of Object.entries(input)) {
|
|
4068
|
+
if (!KNOWN_KEYS.has(k))
|
|
4069
|
+
attrs = { ...attrs, [k]: v };
|
|
4070
|
+
}
|
|
4071
|
+
return quickFix(ctx.client, input.entity, {
|
|
4072
|
+
resourceIds: input.resourceIds,
|
|
4073
|
+
attributes: attrs,
|
|
4074
|
+
});
|
|
4075
|
+
},
|
|
4065
4076
|
},
|
|
4066
4077
|
{
|
|
4067
4078
|
name: 'quick_fix_line_items',
|
|
@@ -4104,4 +4115,293 @@ Response: { updated: string[], failed: [{ resourceId, error, errorCode }] }. On
|
|
|
4104
4115
|
return quickFixLineItems(ctx.client, entity, body);
|
|
4105
4116
|
},
|
|
4106
4117
|
},
|
|
4118
|
+
// ── Nano Classifiers (Tracking Categories) ─────────────────────
|
|
4119
|
+
listTool('list_nano_classifiers', 'List nano classifiers (tracking categories). Paginated. Nano classifiers tag line items with structured categories.', 'nano_classifiers', (client, off, lim) => listNanoClassifiers(client, { limit: lim, offset: off })),
|
|
4120
|
+
getTool('get_nano_classifier', 'Get a nano classifier by resourceId. Returns type and all classes.', 'nano_classifiers', (client, id) => getNanoClassifier(client, id)),
|
|
4121
|
+
{
|
|
4122
|
+
name: 'search_nano_classifiers',
|
|
4123
|
+
description: 'Search nano classifiers by type name. Returns up to 100 by default.',
|
|
4124
|
+
params: {
|
|
4125
|
+
query: { type: 'string', description: 'Search term (classifier type)' },
|
|
4126
|
+
...SEARCH_PARAMS,
|
|
4127
|
+
},
|
|
4128
|
+
required: ['query'],
|
|
4129
|
+
group: 'nano_classifiers',
|
|
4130
|
+
readOnly: true,
|
|
4131
|
+
execute: async (ctx, input) => {
|
|
4132
|
+
const { limit, offset } = extractPaginationInput(input);
|
|
4133
|
+
const query = input.query;
|
|
4134
|
+
return handlePagination((off, lim) => searchNanoClassifiers(ctx.client, {
|
|
4135
|
+
filter: { type: { contains: query } },
|
|
4136
|
+
limit: lim, offset: off,
|
|
4137
|
+
sort: { sortBy: ['type'], order: 'ASC' },
|
|
4138
|
+
}), limit, offset, 20);
|
|
4139
|
+
},
|
|
4140
|
+
},
|
|
4141
|
+
{
|
|
4142
|
+
name: 'create_nano_classifier',
|
|
4143
|
+
description: 'Create a nano classifier (tracking category). Provide a type name and list of class names. printable defaults to true (NOTE: printable: false is currently rejected by a server bug — always use true).',
|
|
4144
|
+
params: {
|
|
4145
|
+
type: { type: 'string', description: 'Classifier type name (e.g., "Department", "Project")' },
|
|
4146
|
+
classes: {
|
|
4147
|
+
type: 'array',
|
|
4148
|
+
items: { type: 'string' },
|
|
4149
|
+
description: 'Class names: ["Sales", "Marketing", "Engineering"]',
|
|
4150
|
+
},
|
|
4151
|
+
printable: { type: 'boolean', description: 'Show on printed documents (default: true). NOTE: false is rejected by a server bug.' },
|
|
4152
|
+
},
|
|
4153
|
+
required: ['type', 'classes'],
|
|
4154
|
+
group: 'nano_classifiers',
|
|
4155
|
+
readOnly: false,
|
|
4156
|
+
execute: async (ctx, input) => {
|
|
4157
|
+
return createNanoClassifier(ctx.client, {
|
|
4158
|
+
type: input.type,
|
|
4159
|
+
classes: input.classes,
|
|
4160
|
+
printable: input.printable ?? true,
|
|
4161
|
+
});
|
|
4162
|
+
},
|
|
4163
|
+
},
|
|
4164
|
+
{
|
|
4165
|
+
name: 'update_nano_classifier',
|
|
4166
|
+
description: 'Update a nano classifier — change type name, modify classes, or toggle printable.',
|
|
4167
|
+
params: {
|
|
4168
|
+
resourceId: { type: 'string', description: 'Nano classifier resourceId' },
|
|
4169
|
+
type: { type: 'string', description: 'New type name' },
|
|
4170
|
+
classes: {
|
|
4171
|
+
type: 'array',
|
|
4172
|
+
items: { type: 'string' },
|
|
4173
|
+
description: 'Updated class names: ["Sales", "Marketing"]',
|
|
4174
|
+
},
|
|
4175
|
+
printable: { type: 'boolean', description: 'Show on printed documents' },
|
|
4176
|
+
},
|
|
4177
|
+
required: ['resourceId'],
|
|
4178
|
+
group: 'nano_classifiers',
|
|
4179
|
+
readOnly: false,
|
|
4180
|
+
execute: async (ctx, input) => {
|
|
4181
|
+
const data = {};
|
|
4182
|
+
if (input.type)
|
|
4183
|
+
data.type = input.type;
|
|
4184
|
+
if (input.classes)
|
|
4185
|
+
data.classes = input.classes;
|
|
4186
|
+
if (input.printable !== undefined)
|
|
4187
|
+
data.printable = input.printable;
|
|
4188
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4189
|
+
return updateNanoClassifier(ctx.client, input.resourceId, data);
|
|
4190
|
+
},
|
|
4191
|
+
},
|
|
4192
|
+
deleteTool('delete_nano_classifier', 'Delete a nano classifier.', 'nano_classifiers', (client, id) => deleteNanoClassifier(client, id)),
|
|
4193
|
+
// ── Invoice/Bill Payment & Credit Sub-resources ────────────────
|
|
4194
|
+
{
|
|
4195
|
+
name: 'list_invoice_payments',
|
|
4196
|
+
description: 'List all payments recorded against an invoice. Returns payment records with amounts, dates, and methods.',
|
|
4197
|
+
params: {
|
|
4198
|
+
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
4199
|
+
},
|
|
4200
|
+
required: ['resourceId'],
|
|
4201
|
+
group: 'payments',
|
|
4202
|
+
readOnly: true,
|
|
4203
|
+
execute: async (ctx, input) => listInvoicePayments(ctx.client, input.resourceId),
|
|
4204
|
+
},
|
|
4205
|
+
{
|
|
4206
|
+
name: 'list_invoice_credits',
|
|
4207
|
+
description: 'List all credit notes applied to an invoice.',
|
|
4208
|
+
params: {
|
|
4209
|
+
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
4210
|
+
},
|
|
4211
|
+
required: ['resourceId'],
|
|
4212
|
+
group: 'payments',
|
|
4213
|
+
readOnly: true,
|
|
4214
|
+
execute: async (ctx, input) => listInvoiceCredits(ctx.client, input.resourceId),
|
|
4215
|
+
},
|
|
4216
|
+
{
|
|
4217
|
+
name: 'reverse_invoice_credit',
|
|
4218
|
+
description: 'Reverse (unapply) a credit note from an invoice. The credit note becomes UNAPPLIED again.',
|
|
4219
|
+
params: {
|
|
4220
|
+
resourceId: { type: 'string', description: 'Invoice resourceId' },
|
|
4221
|
+
creditResourceId: { type: 'string', description: 'Credit application resourceId to reverse' },
|
|
4222
|
+
},
|
|
4223
|
+
required: ['resourceId', 'creditResourceId'],
|
|
4224
|
+
group: 'payments',
|
|
4225
|
+
readOnly: false,
|
|
4226
|
+
execute: async (ctx, input) => {
|
|
4227
|
+
await reverseInvoiceCredit(ctx.client, input.resourceId, input.creditResourceId);
|
|
4228
|
+
return { reversed: true, invoiceId: input.resourceId, creditId: input.creditResourceId };
|
|
4229
|
+
},
|
|
4230
|
+
},
|
|
4231
|
+
{
|
|
4232
|
+
name: 'list_bill_payments',
|
|
4233
|
+
description: 'List all payments recorded against a bill. Returns payment records with amounts, dates, and methods.',
|
|
4234
|
+
params: {
|
|
4235
|
+
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
4236
|
+
},
|
|
4237
|
+
required: ['resourceId'],
|
|
4238
|
+
group: 'payments',
|
|
4239
|
+
readOnly: true,
|
|
4240
|
+
execute: async (ctx, input) => listBillPayments(ctx.client, input.resourceId),
|
|
4241
|
+
},
|
|
4242
|
+
{
|
|
4243
|
+
name: 'list_bill_credits',
|
|
4244
|
+
description: 'List all credit notes applied to a bill.',
|
|
4245
|
+
params: {
|
|
4246
|
+
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
4247
|
+
},
|
|
4248
|
+
required: ['resourceId'],
|
|
4249
|
+
group: 'payments',
|
|
4250
|
+
readOnly: true,
|
|
4251
|
+
execute: async (ctx, input) => listBillCredits(ctx.client, input.resourceId),
|
|
4252
|
+
},
|
|
4253
|
+
{
|
|
4254
|
+
name: 'reverse_bill_credit',
|
|
4255
|
+
description: 'Reverse (unapply) a supplier credit note from a bill. The credit note becomes UNAPPLIED again.',
|
|
4256
|
+
params: {
|
|
4257
|
+
resourceId: { type: 'string', description: 'Bill resourceId' },
|
|
4258
|
+
creditResourceId: { type: 'string', description: 'Credit application resourceId to reverse' },
|
|
4259
|
+
},
|
|
4260
|
+
required: ['resourceId', 'creditResourceId'],
|
|
4261
|
+
group: 'payments',
|
|
4262
|
+
readOnly: false,
|
|
4263
|
+
execute: async (ctx, input) => {
|
|
4264
|
+
await reverseBillCredit(ctx.client, input.resourceId, input.creditResourceId);
|
|
4265
|
+
return { reversed: true, billId: input.resourceId, creditId: input.creditResourceId };
|
|
4266
|
+
},
|
|
4267
|
+
},
|
|
4268
|
+
// ── Scheduled Transaction CRUD (Get, Update, Delete) ───────────
|
|
4269
|
+
getTool('get_scheduled_invoice', 'Get a scheduled (recurring) invoice by resourceId.', 'schedulers', (client, id) => getScheduledInvoice(client, id)),
|
|
4270
|
+
{
|
|
4271
|
+
name: 'update_scheduled_invoice',
|
|
4272
|
+
description: 'Update a scheduled invoice — change schedule settings and/or the invoice template.',
|
|
4273
|
+
params: {
|
|
4274
|
+
resourceId: { type: 'string', description: 'Scheduled invoice resourceId' },
|
|
4275
|
+
repeat: { type: 'string', description: 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY' },
|
|
4276
|
+
startDate: { type: 'string', description: 'First occurrence date (YYYY-MM-DD)' },
|
|
4277
|
+
endDate: { type: 'string', description: 'Last occurrence date (YYYY-MM-DD)' },
|
|
4278
|
+
status: { type: 'string', description: 'Status: ACTIVE or PAUSED' },
|
|
4279
|
+
invoice: { type: 'object', description: 'Invoice template: { reference, valueDate, dueDate, contactResourceId, lineItems, currency, tag, saveAsDraft }' },
|
|
4280
|
+
},
|
|
4281
|
+
required: ['resourceId'],
|
|
4282
|
+
group: 'schedulers',
|
|
4283
|
+
readOnly: false,
|
|
4284
|
+
execute: async (ctx, input) => {
|
|
4285
|
+
const data = {};
|
|
4286
|
+
if (input.repeat)
|
|
4287
|
+
data.repeat = input.repeat;
|
|
4288
|
+
if (input.startDate)
|
|
4289
|
+
data.startDate = input.startDate;
|
|
4290
|
+
if (input.endDate)
|
|
4291
|
+
data.endDate = input.endDate;
|
|
4292
|
+
if (input.status)
|
|
4293
|
+
data.status = input.status;
|
|
4294
|
+
if (input.invoice)
|
|
4295
|
+
data.invoice = input.invoice;
|
|
4296
|
+
return updateScheduledInvoice(ctx.client, input.resourceId, data);
|
|
4297
|
+
},
|
|
4298
|
+
},
|
|
4299
|
+
deleteTool('delete_scheduled_invoice', 'Delete a scheduled (recurring) invoice.', 'schedulers', (client, id) => deleteScheduledInvoice(client, id)),
|
|
4300
|
+
getTool('get_scheduled_bill', 'Get a scheduled (recurring) bill by resourceId.', 'schedulers', (client, id) => getScheduledBill(client, id)),
|
|
4301
|
+
{
|
|
4302
|
+
name: 'update_scheduled_bill',
|
|
4303
|
+
description: 'Update a scheduled bill — change schedule settings and/or the bill template.',
|
|
4304
|
+
params: {
|
|
4305
|
+
resourceId: { type: 'string', description: 'Scheduled bill resourceId' },
|
|
4306
|
+
repeat: { type: 'string', description: 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY' },
|
|
4307
|
+
startDate: { type: 'string', description: 'First occurrence date (YYYY-MM-DD)' },
|
|
4308
|
+
endDate: { type: 'string', description: 'Last occurrence date (YYYY-MM-DD)' },
|
|
4309
|
+
status: { type: 'string', description: 'Status: ACTIVE or PAUSED' },
|
|
4310
|
+
bill: { type: 'object', description: 'Bill template: { reference, valueDate, dueDate, contactResourceId, lineItems, currency, tag, saveAsDraft }' },
|
|
4311
|
+
},
|
|
4312
|
+
required: ['resourceId'],
|
|
4313
|
+
group: 'schedulers',
|
|
4314
|
+
readOnly: false,
|
|
4315
|
+
execute: async (ctx, input) => {
|
|
4316
|
+
const data = {};
|
|
4317
|
+
if (input.repeat)
|
|
4318
|
+
data.repeat = input.repeat;
|
|
4319
|
+
if (input.startDate)
|
|
4320
|
+
data.startDate = input.startDate;
|
|
4321
|
+
if (input.endDate)
|
|
4322
|
+
data.endDate = input.endDate;
|
|
4323
|
+
if (input.status)
|
|
4324
|
+
data.status = input.status;
|
|
4325
|
+
if (input.bill)
|
|
4326
|
+
data.bill = input.bill;
|
|
4327
|
+
return updateScheduledBill(ctx.client, input.resourceId, data);
|
|
4328
|
+
},
|
|
4329
|
+
},
|
|
4330
|
+
deleteTool('delete_scheduled_bill', 'Delete a scheduled (recurring) bill.', 'schedulers', (client, id) => deleteScheduledBill(client, id)),
|
|
4331
|
+
getTool('get_scheduled_journal', 'Get a scheduled (recurring) journal by resourceId.', 'schedulers', (client, id) => getScheduledJournal(client, id)),
|
|
4332
|
+
{
|
|
4333
|
+
name: 'update_scheduled_journal',
|
|
4334
|
+
description: 'Update a scheduled journal — change schedule settings and/or the journal template.',
|
|
4335
|
+
params: {
|
|
4336
|
+
resourceId: { type: 'string', description: 'Scheduled journal resourceId' },
|
|
4337
|
+
repeat: { type: 'string', description: 'Recurrence: WEEKLY, MONTHLY, QUARTERLY, YEARLY' },
|
|
4338
|
+
startDate: { type: 'string', description: 'First occurrence date (YYYY-MM-DD)' },
|
|
4339
|
+
endDate: { type: 'string', description: 'Last occurrence date (YYYY-MM-DD)' },
|
|
4340
|
+
status: { type: 'string', description: 'Status: ACTIVE or PAUSED' },
|
|
4341
|
+
valueDate: { type: 'string', description: 'Journal date (YYYY-MM-DD)' },
|
|
4342
|
+
schedulerEntries: { type: 'array', description: 'Journal entries: [{ accountResourceId, type: CREDIT|DEBIT, amount, ... }]' },
|
|
4343
|
+
reference: { type: 'string', description: 'Journal reference' },
|
|
4344
|
+
notes: { type: 'string', description: 'Journal notes' },
|
|
4345
|
+
},
|
|
4346
|
+
required: ['resourceId'],
|
|
4347
|
+
group: 'schedulers',
|
|
4348
|
+
readOnly: false,
|
|
4349
|
+
execute: async (ctx, input) => {
|
|
4350
|
+
const data = {};
|
|
4351
|
+
if (input.repeat)
|
|
4352
|
+
data.repeat = input.repeat;
|
|
4353
|
+
if (input.startDate)
|
|
4354
|
+
data.startDate = input.startDate;
|
|
4355
|
+
if (input.endDate)
|
|
4356
|
+
data.endDate = input.endDate;
|
|
4357
|
+
if (input.status)
|
|
4358
|
+
data.status = input.status;
|
|
4359
|
+
if (input.valueDate)
|
|
4360
|
+
data.valueDate = input.valueDate;
|
|
4361
|
+
if (input.schedulerEntries)
|
|
4362
|
+
data.schedulerEntries = input.schedulerEntries;
|
|
4363
|
+
if (input.reference)
|
|
4364
|
+
data.reference = input.reference;
|
|
4365
|
+
if (input.notes)
|
|
4366
|
+
data.notes = input.notes;
|
|
4367
|
+
return updateScheduledJournal(ctx.client, input.resourceId, data);
|
|
4368
|
+
},
|
|
4369
|
+
},
|
|
4370
|
+
deleteTool('delete_scheduled_journal', 'Delete a scheduled (recurring) journal.', 'schedulers', (client, id) => deleteScheduledJournal(client, id)),
|
|
4371
|
+
// ── Generic Payment CRUD ───────────────────────────────────────
|
|
4372
|
+
getTool('get_payment', 'Get a specific payment record by resourceId. Returns payment amount, method, date, and reference.', 'payments', (client, id) => getPayment(client, id)),
|
|
4373
|
+
{
|
|
4374
|
+
name: 'update_payment',
|
|
4375
|
+
description: 'Update a payment record — correct amount, reference, date, method, or account.',
|
|
4376
|
+
params: {
|
|
4377
|
+
resourceId: { type: 'string', description: 'Payment resourceId (from invoice/bill paymentRecords)' },
|
|
4378
|
+
paymentAmount: { type: 'number', description: 'Corrected payment amount (bank currency)' },
|
|
4379
|
+
reference: { type: 'string', description: 'Payment reference' },
|
|
4380
|
+
valueDate: { type: 'string', description: 'Payment date (YYYY-MM-DD)' },
|
|
4381
|
+
paymentMethod: { type: 'string', description: 'Payment method (BANK_TRANSFER, CASH, CHEQUE, etc.)' },
|
|
4382
|
+
accountResourceId: { type: 'string', description: 'Bank/cash account resourceId' },
|
|
4383
|
+
transactionFee: { type: 'number', description: 'Transaction fee amount' },
|
|
4384
|
+
},
|
|
4385
|
+
required: ['resourceId'],
|
|
4386
|
+
group: 'payments',
|
|
4387
|
+
readOnly: false,
|
|
4388
|
+
execute: async (ctx, input) => {
|
|
4389
|
+
const data = {};
|
|
4390
|
+
if (input.paymentAmount !== undefined)
|
|
4391
|
+
data.paymentAmount = input.paymentAmount;
|
|
4392
|
+
if (input.reference)
|
|
4393
|
+
data.reference = input.reference;
|
|
4394
|
+
if (input.valueDate)
|
|
4395
|
+
data.valueDate = input.valueDate;
|
|
4396
|
+
if (input.paymentMethod)
|
|
4397
|
+
data.paymentMethod = input.paymentMethod;
|
|
4398
|
+
if (input.accountResourceId)
|
|
4399
|
+
data.accountResourceId = input.accountResourceId;
|
|
4400
|
+
if (input.transactionFee !== undefined)
|
|
4401
|
+
data.transactionFee = input.transactionFee;
|
|
4402
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
4403
|
+
return updatePayment(ctx.client, input.resourceId, data);
|
|
4404
|
+
},
|
|
4405
|
+
},
|
|
4406
|
+
deleteTool('delete_payment', 'Delete (void) a payment record. The associated invoice/bill balance is restored.', 'payments', (client, id) => deletePayment(client, id)),
|
|
4107
4407
|
];
|
package/dist/index.js
CHANGED
|
@@ -50,6 +50,7 @@ import { registerInventoryCommand } from './commands/inventory.js';
|
|
|
50
50
|
import { registerSearchCommand } from './commands/search.js';
|
|
51
51
|
import { registerCustomFieldsCommand } from './commands/custom-fields.js';
|
|
52
52
|
import { registerQuickFixCommand } from './commands/quick-fix.js';
|
|
53
|
+
import { registerNanoClassifiersCommand } from './commands/nano-classifiers.js';
|
|
53
54
|
import { registerSchemaCommand } from './commands/schema.js';
|
|
54
55
|
import { applyAllExamples } from './commands/help-examples.js';
|
|
55
56
|
import { shouldShowPicker, showCommandPicker, attachSubcommandPickers } from './commands/picker.js';
|
|
@@ -163,6 +164,7 @@ registerInventoryCommand(program);
|
|
|
163
164
|
registerSearchCommand(program);
|
|
164
165
|
registerCustomFieldsCommand(program);
|
|
165
166
|
registerQuickFixCommand(program);
|
|
167
|
+
registerNanoClassifiersCommand(program);
|
|
166
168
|
registerSchemaCommand(program);
|
|
167
169
|
applyAllExamples(program);
|
|
168
170
|
// Add --org to every command that has --api-key (DRY: zero changes to command files)
|
package/dist/types/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const SKILL_TYPES = ['jaz-api', 'jaz-conversion', 'jaz-recipes', 'jaz-jobs', 'all'];
|
|
2
2
|
export const SKILL_DESCRIPTIONS = {
|
|
3
|
-
'jaz-api': 'Jaz REST API reference —
|
|
3
|
+
'jaz-api': 'Jaz REST API reference — 111 rules, endpoint catalog, error catalog, field mapping',
|
|
4
4
|
'jaz-conversion': 'Data conversion pipeline — Xero, QuickBooks, Sage, Excel migration to Jaz',
|
|
5
5
|
'jaz-recipes': 'Complex accounting recipes — prepaid, deferred revenue, loans, IFRS 16, depreciation',
|
|
6
6
|
'jaz-jobs': 'Accounting job blueprints — month/quarter/year-end close + 7 ad-hoc operational workflows',
|