jaz-clio 4.30.9 → 4.30.11

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 CHANGED
@@ -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 205 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.
238
+ Expose all 207 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.30.9
3
+ version: 4.30.11
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.
@@ -243,7 +243,7 @@ Bills, invoices, and credit notes share identical mandatory field specs. Adding
243
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.
244
244
 
245
245
  ### Subscriptions & Scheduled Transactions
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). **Subscriptions are invoices only** (SALE) — no bills. Different from scheduled invoices: subscriptions auto-prorate partial periods (generate credit notes for mid-period changes), but currency/tax/account are immutable after creation. Use scheduled invoices for fixed-amount recurring invoices where you need per-occurrence flexibility.
246
+ 93. **Subscription endpoints are under `/scheduled/subscriptions`** — List, GET, POST, PUT, DELETE all at `/api/v1/scheduled/subscriptions[/:id]`. Cancel is **PUT** (not POST) at `/api/v1/scheduled/cancel-subscriptions/:id` (different path pattern). **Subscriptions are invoices only** (SALE) — no bills. Different from scheduled invoices: subscriptions auto-prorate partial periods (generate credit notes for mid-period changes), but currency/tax/account are immutable after creation. Use scheduled invoices for fixed-amount recurring invoices where you need per-occurrence flexibility. **All subscription CRUD requires `proratedConfig: { proratedAdjustmentLineText: string }`**. Cancel requires `cancelDateType` (`END_OF_CURRENT_PERIOD`, `END_OF_LAST_PERIOD`, `CUSTOM_DATE`) + `proratedAdjustmentLineText` + `resourceId` in body. Must cancel before delete. `businessTransactionType` is NOT in the OAS — the API ignores it.
247
247
  94. **Scheduled transaction search does NOT support `createdAt` sort** — `POST /scheduled-transaction/search` sort fields: `startDate`, `nextScheduleDate`, etc. Default to `startDate` DESC. This is a cross-entity search across all scheduled types (invoices, bills, journals, subscriptions).
248
248
 
249
249
  ### Universal Search
@@ -278,6 +278,9 @@ Bills, invoices, and credit notes share identical mandatory field specs. Adding
278
278
  105. **`add_currency_rate` for new rates, `update_currency_rate` only for editing existing records** — When a user says "update the rate" or "set the rate", use `add_currency_rate` (POST — creates a new rate entry for a date). Only use `update_currency_rate` (PUT) when explicitly modifying an existing rate record by its resourceId.
279
279
  106. **Contact PUT uses `email` (string), not `emails` (array)** — GET returns `emails: [{email, label}]` (array) but PUT accepts `email: "user@example.com"` (string). Sending the `emails` array in PUT body causes 400 "Invalid request body". The CLI and tool executor handle this automatically via read-modify-write with the correct field.
280
280
 
281
+ ### Quick Fix (Bulk Update)
282
+ 107. **20 Quick Fix endpoints for bulk-updating transactions and line items** — `POST /api/v1/quick-fix/{entity}` with `{ resourceIds: [...], attributes: {...} }`. Response: `{ updated: [...], failed: [{ resourceId, error, errorCode }] }`. Entities grouped by domain: **ARAP**: invoices, bills, customer-credit-notes, supplier-credit-notes. **Accounting**: journals, cash-entries. **Schedulers**: sale-schedules, purchase-schedules, subscription-schedules, journal-schedules. Line-item endpoints add `/line-items` suffix. ARAP line items use `{ lineItemResourceIds, attributes }`. Scheduler line items use `{ schedulerUpdates: [{ schedulerResourceId, lineItemUpdates }] }`. **Known gotcha**: `valueDate` (and `dueDate` for ARAP) must currently be included in `attributes` even when unchanged — absent fields are treated as "remove" rather than "keep existing". This will be fixed server-side.
283
+
281
284
  ## Supporting Files
282
285
 
283
286
  For detailed reference, read these files in this skill directory:
@@ -1320,6 +1320,75 @@ Same but with `"bill"` wrapper instead of `"invoice"`.
1320
1320
 
1321
1321
  ---
1322
1322
 
1323
+ ## 16b. Subscriptions (Recurring Invoices with Auto-Proration)
1324
+
1325
+ Subscriptions auto-generate invoices on schedule with proration support. **Different from scheduled invoices**: subscriptions auto-prorate partial periods (generate credit notes for mid-period changes), but currency/tax/account are immutable after creation. Invoices only — no bills.
1326
+
1327
+ ### POST /api/v1/scheduled/subscriptions
1328
+
1329
+ ```json
1330
+ // Request:
1331
+ {
1332
+ "repeat": "MONTHLY",
1333
+ "startDate": "2026-04-01",
1334
+ "status": "ACTIVE",
1335
+ "proratedConfig": {
1336
+ "proratedAdjustmentLineText": "Prorated adjustment"
1337
+ },
1338
+ "invoice": {
1339
+ "contactResourceId": "uuid-customer",
1340
+ "reference": "SUB-001",
1341
+ "valueDate": "2026-04-01",
1342
+ "dueDate": "2026-04-30",
1343
+ "lineItems": [
1344
+ { "name": "Monthly Retainer", "unitPrice": 3000, "quantity": 1, "accountResourceId": "uuid-revenue" }
1345
+ ],
1346
+ "saveAsDraft": false
1347
+ }
1348
+ }
1349
+
1350
+ // Response:
1351
+ { "data": { "resourceId": "uuid" } }
1352
+ ```
1353
+
1354
+ **CRITICAL notes**:
1355
+ - `proratedConfig` is **REQUIRED** on create, update, and cancel. Omitting it causes 500 (server null pointer).
1356
+ - `businessTransactionType` is NOT in the OAS — the API ignores it. Don't send it.
1357
+ - Uses `repeat` + `invoice` wrapper — same structure as scheduled invoices (`POST /scheduled/invoices`).
1358
+ - `repeat`: `"WEEKLY"`, `"MONTHLY"`, `"QUARTERLY"`, `"YEARLY"`.
1359
+ - `saveAsDraft: false` is REQUIRED inside the `invoice` wrapper.
1360
+ - Currency, tax, and account details are the SAME for all items and CANNOT be changed after creation.
1361
+ - Mid-period cancellations or amount changes auto-generate prorated credit notes.
1362
+
1363
+ ### PUT /api/v1/scheduled/cancel-subscriptions/:id
1364
+
1365
+ Cancel is **PUT** (not POST). Requires body fields — empty `{}` returns 422.
1366
+
1367
+ ```json
1368
+ // Request:
1369
+ {
1370
+ "cancelDateType": "END_OF_CURRENT_PERIOD",
1371
+ "proratedAdjustmentLineText": "Prorated adjustment",
1372
+ "resourceId": "uuid-subscription"
1373
+ }
1374
+
1375
+ // Response:
1376
+ { "data": { "resourceId": "uuid", "status": "SUCCEEDED" } }
1377
+ ```
1378
+
1379
+ `cancelDateType` values: `END_OF_CURRENT_PERIOD` (default), `END_OF_LAST_PERIOD`, `CUSTOM_DATE` (requires `endDate: "YYYY-MM-DD"`).
1380
+
1381
+ Note the different path pattern from CRUD: cancel is at `/scheduled/cancel-subscriptions/:id`, not `/scheduled/subscriptions/:id/cancel`. Must cancel before delete — cannot delete ACTIVE subscriptions.
1382
+
1383
+ ### Other subscription endpoints
1384
+
1385
+ - `GET /api/v1/scheduled/subscriptions` — List all subscriptions
1386
+ - `GET /api/v1/scheduled/subscriptions/:id` — Get subscription details
1387
+ - `PUT /api/v1/scheduled/subscriptions/:id` — Update subscription (requires `proratedConfig`, `repeat`, `resourceId`)
1388
+ - `DELETE /api/v1/scheduled/subscriptions/:id` — Delete subscription (must be cancelled first)
1389
+
1390
+ ---
1391
+
1323
1392
  ## 17. Reports
1324
1393
 
1325
1394
  ### POST /api/v1/generate-reports/trial-balance
@@ -1745,4 +1814,93 @@ Middleware on payment, credit, and refund endpoints automatically wraps a flat J
1745
1814
 
1746
1815
  ---
1747
1816
 
1748
- *Last updated: 2026-02-23 — Added: Account single create (POST /chart-of-accounts) and delete (DELETE /chart-of-accounts/:id). Currency list: added customRateCount field, field name gotcha (currencyCode NOT code). Currency enable: 400 on duplicate, one-way operation. Currency rate POST: string response gotcha (no resourceId returned).*
1817
+ ---
1818
+
1819
+ ## 17. Quick Fix (Bulk Update)
1820
+
1821
+ 20 endpoints for bulk-updating transactions and line items in a single API call.
1822
+
1823
+ ### Pattern
1824
+
1825
+ ```
1826
+ POST /api/v1/quick-fix/{entity}
1827
+ POST /api/v1/quick-fix/{entity}/line-items
1828
+ ```
1829
+
1830
+ ### Entities (grouped by domain)
1831
+
1832
+ **ARAP**: `invoices`, `bills`, `customer-credit-notes`, `supplier-credit-notes`
1833
+ **Accounting**: `journals`, `cash-entries`
1834
+ **Schedulers**: `sale-schedules`, `purchase-schedules`, `subscription-schedules`, `journal-schedules`
1835
+
1836
+ ### Transaction-Level Request
1837
+
1838
+ ```json
1839
+ POST /api/v1/quick-fix/bills
1840
+ {
1841
+ "resourceIds": ["uuid1", "uuid2"],
1842
+ "attributes": {
1843
+ "valueDate": "2026-03-01",
1844
+ "dueDate": "2026-03-31",
1845
+ "tags": ["Q1-2026"],
1846
+ "contactResourceId": "uuid"
1847
+ }
1848
+ }
1849
+ ```
1850
+
1851
+ ### Line-Item-Level Request (ARAP + Accounting)
1852
+
1853
+ ```json
1854
+ POST /api/v1/quick-fix/invoices/line-items
1855
+ {
1856
+ "lineItemResourceIds": ["li-uuid1", "li-uuid2"],
1857
+ "attributes": {
1858
+ "name": "Updated Item",
1859
+ "quantity": 2,
1860
+ "unitPrice": 150,
1861
+ "organizationAccountResourceId": "acct-uuid",
1862
+ "taxProfileResourceId": "tax-uuid"
1863
+ }
1864
+ }
1865
+ ```
1866
+
1867
+ ### Line-Item-Level Request (Schedulers)
1868
+
1869
+ ```json
1870
+ POST /api/v1/quick-fix/sale-schedules/line-items
1871
+ {
1872
+ "schedulerUpdates": [
1873
+ {
1874
+ "schedulerResourceId": "sched-uuid",
1875
+ "lineItemUpdates": [
1876
+ { "arrayIndex": 0, "unitPrice": 200 }
1877
+ ]
1878
+ }
1879
+ ]
1880
+ }
1881
+ ```
1882
+
1883
+ ### Response (all 20 endpoints)
1884
+
1885
+ ```json
1886
+ {
1887
+ "updated": ["uuid1", "uuid2"],
1888
+ "failed": [
1889
+ { "resourceId": "uuid3", "error": "Transaction is locked", "errorCode": "TRANSACTION_LOCKED" }
1890
+ ]
1891
+ }
1892
+ ```
1893
+
1894
+ ### Updatable Fields
1895
+
1896
+ **Transaction-level**: valueDate, dueDate (ARAP), contactResourceId, tags, capsuleResourceId, customFields, currencySettings, taxCurrencySettings, invoiceNotes (invoices/CNs), templateResourceId (invoices/CNs), billFrom/billTo (invoices/CNs), endDate/interval (schedulers).
1897
+
1898
+ **ARAP line items**: name, quantity, unit, unitPrice, discount, itemResourceId, organizationAccountResourceId, taxProfileResourceId, classifierConfig, withholdingTax (bills/supplier-CNs only).
1899
+
1900
+ **Journal/cash-entry line items**: organizationAccountResourceId, amount, description, taxProfileResourceId, classifierConfig.
1901
+
1902
+ **Known gotcha**: `valueDate` (and `dueDate` for ARAP) must currently be included in `attributes` even when unchanged — absent fields are treated as "remove". Server-side fix pending.
1903
+
1904
+ ---
1905
+
1906
+ *Last updated: 2026-03-09 — Added: Quick Fix (20 bulk-update endpoints), subscription CRUD (proratedConfig, PUT cancel).*
@@ -574,6 +574,22 @@ if (acct.code) ctx.coaIds[acct.code] = acct.resourceId;
574
574
 
575
575
  ---
576
576
 
577
+ ## Subscriptions Errors
578
+
579
+ ### 500 Internal Server Error — missing `proratedConfig`
580
+ **Cause**: `POST /scheduled/subscriptions` and `PUT /scheduled/subscriptions/:id` return 500 when `proratedConfig` is omitted. The server null-pointer dereferences on the missing field. OAS marks it required but the 500 is misleading.
581
+ **Fix**: Always include `proratedConfig: { proratedAdjustmentLineText: "Prorated adjustment" }` in create and update bodies.
582
+
583
+ ### 422 on cancel — wrong method or missing fields
584
+ **Cause**: Cancel endpoint is **PUT** `/scheduled/cancel-subscriptions/:id` (not POST). Requires body: `{ cancelDateType, proratedAdjustmentLineText, resourceId }`. Empty `{}` returns 422.
585
+ **Fix**: Use PUT with required fields. `cancelDateType`: `END_OF_CURRENT_PERIOD` (default), `END_OF_LAST_PERIOD`, `CUSTOM_DATE`.
586
+
587
+ ### Cannot delete ACTIVE subscription
588
+ **Cause**: `DELETE /scheduled/subscriptions/:id` fails if subscription status is ACTIVE.
589
+ **Fix**: Cancel first, then delete.
590
+
591
+ ---
592
+
577
593
  ## Deposits Errors
578
594
 
579
595
  ### 404 — Endpoint does not exist
@@ -803,4 +819,18 @@ Journals support a top-level `currency` object to create entries in a foreign cu
803
819
 
804
820
  ---
805
821
 
806
- *Last updated: 2026-03-09 — Added cash entry PUT 500 (missing accountResourceId), contact PUT email/emails asymmetry, tax profile dedup/scoping, journal balance check, duplicate reference errors.*
822
+ ### Quick Fix Errors
823
+
824
+ **"Not allowed to remove valueDate of a sale/purchase/journal"** — `valueDate` absent from `attributes`. Currently must be included even if unchanged. Server-side fix pending.
825
+
826
+ **"Not allowed to remove dueDate of a sale"** — Same pattern for `dueDate` on invoices.
827
+
828
+ **"Not allowed to remove pdfTemplate of a sale"** — Same pattern for `templateResourceId` on invoices that have a template.
829
+
830
+ **"Item name is required to bulk update purchase item details"** — Line-item quick-fix requires `name` in attributes. Same "absent = remove" pattern.
831
+
832
+ **"TRANSACTION_LOCKED"** — Transaction is in a locked period. Cannot update.
833
+
834
+ ---
835
+
836
+ *Last updated: 2026-03-09 — Added Quick Fix errors (absent = remove gotcha), cash entry PUT 500 (missing accountResourceId), contact PUT email/emails asymmetry.*
@@ -18,6 +18,20 @@ If payment date equals invoice date, it's recorded as a cash transaction (not AR
18
18
 
19
19
  ---
20
20
 
21
+ ## Subscriptions (Recurring Invoices with Proration)
22
+
23
+ Auto-generates invoices on a recurring schedule (weekly, monthly, quarterly, yearly) with automatic proration for mid-period adjustments. When dates or amounts change, prorated invoices or credit notes are generated for partial periods.
24
+
25
+ **Key differences from scheduled invoices**: Subscriptions auto-prorate (scheduled invoices don't). But currency, tax, and account details must be the same for all line items and cannot be changed after creation (scheduled invoices allow per-occurrence flexibility).
26
+
27
+ Use subscriptions for: software licenses, retainer services, SaaS billing — any recurring revenue where proration matters. Use scheduled invoices for fixed-amount recurring invoices where you need to change line items, tax, or currency per occurrence.
28
+
29
+ **Invoices only** — no bills. `businessTransactionType` must be `"SALE"`.
30
+
31
+ **API**: CRUD `GET/POST/PUT/DELETE /scheduled/subscriptions`, cancel `POST /scheduled/cancel-subscriptions/:id`, search `POST /scheduled-transaction/search` (cross-entity).
32
+
33
+ ---
34
+
21
35
  ## Bills
22
36
 
23
37
  Purchase documents from suppliers for goods/services received. Track Accounts Payable with multi-currency FX. Bills follow the same lifecycle as invoices but with reversed RGL logic and admin-only approval workflows. Supports withholding tax (WHT) on line items.
@@ -223,6 +237,12 @@ Bank statement import supports CSV and PDF with configurable column mapping. Sta
223
237
 
224
238
  **API**: `POST /magic/importBankStatementFromAttachment` (multipart: `sourceFile`, `accountResourceId`, `businessTransactionType: "BANK_STATEMENT"`, `sourceType: "FILE"`)
225
239
 
240
+ ### Quick Fix (Bulk Update)
241
+
242
+ Mass-edit transactions and line items in a single operation. Replaces the need to open and update each transaction individually. Supports all transaction types (invoices, bills, credit notes, journals, cash entries) and their scheduled/subscription variants. Common use cases: batch date corrections, reassigning contacts, bulk-tagging for reporting, changing accounts/tax profiles across multiple line items.
243
+
244
+ **API**: `POST /api/v1/quick-fix/{entity}` (transaction-level) + `POST /api/v1/quick-fix/{entity}/line-items` (line-item-level). 20 endpoints total.
245
+
226
246
  ---
227
247
 
228
248
  *Maintained alongside [help.jaz.ai](https://help.jaz.ai).*
@@ -565,4 +565,15 @@ Battle-tested patterns from production Jaz API clients:
565
565
 
566
566
  ---
567
567
 
568
- *Last updated: 2026-02-23 — Added: Currency response field mapping (currencyCode NOT code, currencyName NOT name, currencySymbol NOT symbol, customRateCount). CurrencyRate response field mapping (sourceCurrencyCode, functionalCurrencyCode, notes). Account create field mapping (classificationType, currency, foreign-currency accounts). Response shape quirks: rate POST/PUT return plain strings.*
568
+ ### Quick Fix (Bulk Update) Field Mapping
569
+
570
+ | What you'd guess | Actual field | Notes |
571
+ |-------------------|-------------|-------|
572
+ | `accountResourceId` (line items) | `organizationAccountResourceId` | Quick-fix line items use GET-side field name, NOT the POST alias |
573
+ | `repeat` (schedulers) | `interval` | Quick-fix scheduler attributes use `interval`, NOT `repeat` |
574
+ | `lineItems` | `lineItemResourceIds` | Pass individual line item IDs, not the transaction resourceId |
575
+ | `schedulerResourceId` | `schedulerResourceId` | For scheduler line-item updates (in `schedulerUpdates` array) |
576
+
577
+ ---
578
+
579
+ *Last updated: 2026-03-09 — Added Quick Fix field mapping. Previous: Currency response fields, account create fields.*
@@ -694,6 +694,17 @@ These features exist in the Jaz platform and may affect API responses or cause u
694
694
  - Move transactions between capsules via `POST /api/v1/moveTransactionCapsules`
695
695
  - Search transactions within capsules
696
696
 
697
+ ### Quick Fix (Bulk Update) — 20 Endpoints
698
+
699
+ Bulk-update transactions or line items in a single call. Pattern: `POST /api/v1/quick-fix/{entity}` + `POST /api/v1/quick-fix/{entity}/line-items`.
700
+
701
+ **ARAP**: `invoices`, `bills`, `customer-credit-notes`, `supplier-credit-notes` (× 2 = 8)
702
+ **Accounting**: `journals`, `cash-entries` (× 2 = 4)
703
+ **Schedulers**: `sale-schedules`, `purchase-schedules`, `subscription-schedules`, `journal-schedules` (× 2 = 8)
704
+
705
+ Request: `{ resourceIds: [...], attributes: {...} }` (transactions) or `{ lineItemResourceIds: [...], attributes: {...} }` (line items) or `{ schedulerUpdates: [...] }` (scheduler line items).
706
+ Response: `{ updated: [...], failed: [{ resourceId, error, errorCode }] }`.
707
+
697
708
  ---
698
709
 
699
- *Last updated: 2026-02-14 — All search/list responses standardized to flat shape (no outer data wrapper). Kebab-case aliases for capsule-types and move-transaction-capsules. Sort now array on all endpoints (no exceptions). Major overhaul: Added 20+ missing endpoints (bank rules, capsule types, nano classifiers, catalogs, reference data, auto-reconciliation). Expanded search filter syntax with all 7 operator types, nested filters, logical operators, andGroup/orGroup, date format asymmetry. Added cross-reference to search-reference.md. All data sourced from Go structs.*
710
+ *Last updated: 2026-03-09Added 20 Quick Fix endpoints (bulk update). Previous: All search/list responses standardized to flat shape, kebab-case aliases, sort array on all endpoints.*
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: jaz-conversion
3
- version: 4.30.9
3
+ version: 4.30.11
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.9
3
+ version: 4.30.11
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.9
3
+ version: 4.30.11
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.
@@ -0,0 +1,139 @@
1
+ import chalk from 'chalk';
2
+ import { readFileSync } from 'node:fs';
3
+ import { quickFix, quickFixLineItems, QUICK_FIX_ENTITIES } from '../core/api/quick-fix.js';
4
+ import { QUICK_FIX_ARAP } from '../core/api/quick-fix.js';
5
+ import { apiAction } from './api-action.js';
6
+ function parseJson(source, label) {
7
+ try {
8
+ return JSON.parse(source);
9
+ }
10
+ catch (err) {
11
+ console.error(chalk.red(`Invalid JSON in ${label}: ${err.message}`));
12
+ process.exitCode = 1;
13
+ return null;
14
+ }
15
+ }
16
+ export function registerQuickFixCommand(program) {
17
+ const cmd = program
18
+ .command('quick-fix <entity>')
19
+ .description('Bulk-update transactions or line items in one call')
20
+ .addHelpText('after', `
21
+ Entities (grouped by domain):
22
+ ARAP: invoices, bills, customer-credit-notes, supplier-credit-notes
23
+ Accounting: journals, cash-entries
24
+ Schedulers: sale-schedules, purchase-schedules, subscription-schedules, journal-schedules
25
+
26
+ Examples:
27
+ clio quick-fix invoices --ids id1,id2 --attributes '{"valueDate":"2026-01-15","tags":["Q1"]}'
28
+ clio quick-fix bills --ids id1 --date 2026-02-01 --due 2026-03-01 --tag Marketing
29
+ clio quick-fix journals --line-items --ids li1,li2 --attributes '{"organizationAccountResourceId":"<uuid>"}'
30
+ clio quick-fix sale-schedules --line-items --input scheduler-updates.json`)
31
+ .option('--ids <csv>', 'Comma-separated resourceIds (transaction or line item)')
32
+ .option('--line-items', 'Target line items instead of transactions')
33
+ .option('--attributes <json>', 'Attributes JSON object')
34
+ .option('--input <file>', 'Read attributes/body from JSON file')
35
+ .option('--date <date>', 'Shorthand: set valueDate (YYYY-MM-DD)')
36
+ .option('--due <date>', 'Shorthand: set dueDate (YYYY-MM-DD)')
37
+ .option('--tag <name>', 'Shorthand: set tags to [name]')
38
+ .option('--contact <id>', 'Shorthand: set contactResourceId')
39
+ .option('--account <id>', 'Shorthand: set organizationAccountResourceId (line items)')
40
+ .option('--tax-profile <id>', 'Shorthand: set taxProfileResourceId (line items)')
41
+ .option('--api-key <key>', 'API key')
42
+ .option('--json', 'JSON output')
43
+ .action((entity, opts) => apiAction(async (client) => {
44
+ // ── Validate entity ──────────────────────────────────────
45
+ if (!QUICK_FIX_ENTITIES.includes(entity)) {
46
+ console.error(chalk.red(`Unknown entity: ${entity}`));
47
+ console.error(`Valid entities: ${QUICK_FIX_ENTITIES.join(', ')}`);
48
+ process.exitCode = 1;
49
+ return;
50
+ }
51
+ // ── Build body ───────────────────────────────────────────
52
+ if (opts.input) {
53
+ const raw = parseJson(readFileSync(opts.input, 'utf-8'), '--input');
54
+ if (!raw)
55
+ return;
56
+ const result = opts.lineItems
57
+ ? await quickFixLineItems(client, entity, raw)
58
+ : await quickFix(client, entity, raw);
59
+ formatResult(result, opts.json);
60
+ return;
61
+ }
62
+ const ids = opts.ids ? opts.ids.split(',').map((s) => s.trim()).filter(Boolean) : [];
63
+ if (ids.length === 0) {
64
+ console.error(chalk.red('--ids is required (comma-separated resourceIds)'));
65
+ process.exitCode = 1;
66
+ return;
67
+ }
68
+ // Merge --attributes JSON with shorthand flags
69
+ let attrs = {};
70
+ if (opts.attributes) {
71
+ const parsed = parseJson(opts.attributes, '--attributes');
72
+ if (!parsed)
73
+ return;
74
+ attrs = parsed;
75
+ }
76
+ if (opts.date)
77
+ attrs.valueDate = opts.date;
78
+ if (opts.due)
79
+ attrs.dueDate = opts.due;
80
+ if (opts.tag)
81
+ attrs.tags = [opts.tag];
82
+ if (opts.contact)
83
+ attrs.contactResourceId = opts.contact;
84
+ if (opts.account)
85
+ attrs.organizationAccountResourceId = opts.account;
86
+ if (opts.taxProfile)
87
+ attrs.taxProfileResourceId = opts.taxProfile;
88
+ // ── Pre-flight: require valueDate/dueDate (API treats absent as remove) ──
89
+ if (!opts.lineItems) {
90
+ if (!('valueDate' in attrs)) {
91
+ console.error(chalk.red('valueDate is required in attributes (use --date or include in --attributes)'));
92
+ console.error(chalk.dim('The API currently treats absent fields as "remove". Server-side fix pending.'));
93
+ process.exitCode = 1;
94
+ return;
95
+ }
96
+ if (QUICK_FIX_ARAP.includes(entity) && !('dueDate' in attrs)) {
97
+ console.error(chalk.red('dueDate is required for ARAP entities (use --due or include in --attributes)'));
98
+ console.error(chalk.dim('The API currently treats absent fields as "remove". Server-side fix pending.'));
99
+ process.exitCode = 1;
100
+ return;
101
+ }
102
+ }
103
+ if (opts.lineItems) {
104
+ const result = await quickFixLineItems(client, entity, {
105
+ lineItemResourceIds: ids,
106
+ attributes: attrs,
107
+ });
108
+ formatResult(result, opts.json);
109
+ }
110
+ else {
111
+ const result = await quickFix(client, entity, {
112
+ resourceIds: ids,
113
+ attributes: attrs,
114
+ });
115
+ formatResult(result, opts.json);
116
+ }
117
+ })(opts));
118
+ }
119
+ function formatResult(result, json) {
120
+ if (json) {
121
+ console.log(JSON.stringify(result, null, 2));
122
+ return;
123
+ }
124
+ if (result.updated.length > 0) {
125
+ console.log(chalk.green(`Updated ${result.updated.length} item(s):`));
126
+ for (const id of result.updated)
127
+ console.log(` ${chalk.cyan(id)}`);
128
+ }
129
+ if (result.failed.length > 0) {
130
+ console.log(chalk.red(`Failed ${result.failed.length} item(s):`));
131
+ for (const f of result.failed) {
132
+ console.log(` ${chalk.cyan(f.resourceId)} ${chalk.red(f.error)}${f.errorCode ? ` (${f.errorCode})` : ''}`);
133
+ }
134
+ process.exitCode = 1;
135
+ }
136
+ if (result.updated.length === 0 && result.failed.length === 0) {
137
+ console.log('No items processed.');
138
+ }
139
+ }
@@ -103,17 +103,20 @@ export function registerSubscriptionsCommand(program) {
103
103
  ...(opts.taxProfile && !li.taxProfileResourceId ? { taxProfileResourceId: opts.taxProfile } : {}),
104
104
  }));
105
105
  }
106
+ // Subscriptions use repeat + invoice wrapper + proratedConfig (required)
106
107
  body = {
107
- businessTransactionType: 'SALE',
108
- interval: opts.interval,
108
+ repeat: opts.interval,
109
109
  startDate: opts.startDate,
110
110
  status: 'ACTIVE',
111
- contactResourceId: opts.contact,
112
- reference: opts.ref,
113
- valueDate: opts.date,
114
- dueDate: opts.due,
115
- lineItems,
116
- saveAsDraft: false,
111
+ proratedConfig: { proratedAdjustmentLineText: 'Prorated adjustment' },
112
+ invoice: {
113
+ contactResourceId: opts.contact,
114
+ reference: opts.ref,
115
+ valueDate: opts.date,
116
+ dueDate: opts.due,
117
+ lineItems,
118
+ saveAsDraft: false,
119
+ },
117
120
  };
118
121
  if (opts.endDate)
119
122
  body.endDate = opts.endDate;
@@ -163,12 +166,19 @@ export function registerSubscriptionsCommand(program) {
163
166
  })(opts));
164
167
  cmd
165
168
  .command('cancel <resourceId>')
166
- .description('Cancel an active subscription')
169
+ .description('Cancel an active subscription (prorates remaining period)')
170
+ .option('--cancel-date-type <type>', 'END_OF_CURRENT_PERIOD, END_OF_LAST_PERIOD, or CUSTOM_DATE', 'END_OF_CURRENT_PERIOD')
171
+ .option('--end-date <YYYY-MM-DD>', 'Custom cancel date (only with CUSTOM_DATE)')
172
+ .option('--prorated-text <text>', 'Prorated adjustment line text', 'Prorated adjustment')
167
173
  .option('--format <type>', 'Output format: table, json, csv, yaml')
168
174
  .option('--api-key <key>', 'API key')
169
175
  .option('--json', 'JSON output')
170
176
  .action((resourceId, opts) => apiAction(async (client) => {
171
- await cancelSubscription(client, resourceId);
177
+ await cancelSubscription(client, resourceId, {
178
+ cancelDateType: opts.cancelDateType,
179
+ proratedAdjustmentLineText: opts.proratedText,
180
+ endDate: opts.endDate,
181
+ });
172
182
  if (opts.json) {
173
183
  console.log(JSON.stringify({ cancelled: true, resourceId }));
174
184
  return;
@@ -33,5 +33,6 @@ export * as subscriptions from './subscriptions.js';
33
33
  export * as contactGroups from './contact-groups.js';
34
34
  export * as inventory from './inventory.js';
35
35
  export * as search from './search.js';
36
+ export * as quickFix from './quick-fix.js';
36
37
  // ── Guards (pre-flight checks for create operations) ────────────
37
38
  export * from './guards.js';
@@ -0,0 +1,16 @@
1
+ // ── Entity Groups ────────────────────────────────────────────────
2
+ // Grouped by domain — values are API path segments for /api/v1/quick-fix/{entity}
3
+ export const QUICK_FIX_ARAP = ['invoices', 'bills', 'customer-credit-notes', 'supplier-credit-notes'];
4
+ const QUICK_FIX_ACCOUNTING = ['journals', 'cash-entries'];
5
+ const QUICK_FIX_SCHEDULERS = ['sale-schedules', 'purchase-schedules', 'subscription-schedules', 'journal-schedules'];
6
+ export const QUICK_FIX_ENTITIES = [...QUICK_FIX_ARAP, ...QUICK_FIX_ACCOUNTING, ...QUICK_FIX_SCHEDULERS];
7
+ // ── Transaction-Level Quick Fix ──────────────────────────────────
8
+ export async function quickFix(client, entity, body) {
9
+ return client.post(`/api/v1/quick-fix/${entity}`, body);
10
+ }
11
+ // ── Line-Item-Level Quick Fix ────────────────────────────────────
12
+ // ARAP: { lineItemResourceIds: string[], attributes: {...} }
13
+ // Schedulers: { schedulerUpdates: [{ schedulerResourceId, lineItemUpdates: [...] }] }
14
+ export async function quickFixLineItems(client, entity, body) {
15
+ return client.post(`/api/v1/quick-fix/${entity}/line-items`, body);
16
+ }
@@ -13,8 +13,13 @@ export async function updateSubscription(client, resourceId, data) {
13
13
  export async function deleteSubscription(client, resourceId) {
14
14
  await client.delete(`/api/v1/scheduled/subscriptions/${resourceId}`);
15
15
  }
16
- export async function cancelSubscription(client, resourceId) {
17
- return client.post(`/api/v1/scheduled/cancel-subscriptions/${resourceId}`, {});
16
+ export async function cancelSubscription(client, resourceId, opts) {
17
+ return client.put(`/api/v1/scheduled/cancel-subscriptions/${resourceId}`, {
18
+ resourceId,
19
+ cancelDateType: opts?.cancelDateType ?? 'END_OF_CURRENT_PERIOD',
20
+ proratedAdjustmentLineText: opts?.proratedAdjustmentLineText ?? 'Prorated adjustment',
21
+ ...(opts?.endDate ? { endDate: opts.endDate } : {}),
22
+ });
18
23
  }
19
24
  export async function searchScheduledTransactions(client, params) {
20
25
  // Scheduled transactions sort fields: startDate, nextScheduleDate, etc. (no createdAt)
@@ -125,6 +125,12 @@ export const TOOL_NAMESPACES = [
125
125
  description: 'Payments search/list across all transaction types. Cashflow transaction ledger. Universal cross-entity search. Transaction summary (fetch any transaction + attachments + payment history in one call).',
126
126
  groups: ['payments', 'cashflow', 'search'],
127
127
  },
128
+ // ── Quick Fix (Bulk Update) ────────────────────────────────
129
+ {
130
+ name: 'quick_fix',
131
+ description: 'Quick Fix: bulk-update multiple transactions or line items in one call. Change dates, contacts, tags, accounts, tax profiles, custom fields across many invoices/bills/journals/credit-notes/cash-entries/schedulers at once. Also: batch update, mass edit.',
132
+ groups: ['quick_fix'],
133
+ },
128
134
  // ── Drafts ──────────────────────────────────────────────────
129
135
  {
130
136
  name: 'drafts',
@@ -39,6 +39,7 @@ import { listContactGroups, getContactGroup, searchContactGroups, createContactG
39
39
  import { listInventoryItems, getInventoryBalance } from '../api/inventory.js';
40
40
  import { universalSearch } from '../api/search.js';
41
41
  import { listCustomFields, getCustomField, searchCustomFields, createCustomField, updateCustomField, deleteCustomField } from '../api/custom-fields.js';
42
+ import { quickFix, quickFixLineItems, QUICK_FIX_ENTITIES } from '../api/quick-fix.js';
42
43
  // Job blueprints (offline — no API calls)
43
44
  import { generateMonthEndBlueprint } from '../jobs/month-end/blueprint.js';
44
45
  import { generateQuarterEndBlueprint } from '../jobs/quarter-end/blueprint.js';
@@ -3330,10 +3331,14 @@ Use for: software licenses, retainer services, recurring SaaS billing. Invoices
3330
3331
  accountResourceId,
3331
3332
  ...(taxProfileResourceId ? { taxProfileResourceId } : {}),
3332
3333
  }));
3334
+ // Subscriptions use repeat + invoice wrapper + proratedConfig (required)
3333
3335
  const body = {
3334
- businessTransactionType: 'SALE',
3335
- interval, startDate, contactResourceId, reference, valueDate, dueDate,
3336
- lineItems: items, saveAsDraft: false, status: 'ACTIVE',
3336
+ repeat: interval, startDate, status: 'ACTIVE',
3337
+ proratedConfig: { proratedAdjustmentLineText: 'Prorated adjustment' },
3338
+ invoice: {
3339
+ contactResourceId, reference, valueDate, dueDate,
3340
+ lineItems: items, saveAsDraft: false,
3341
+ },
3337
3342
  };
3338
3343
  if (endDate)
3339
3344
  body.endDate = endDate;
@@ -3360,14 +3365,19 @@ Use for: software licenses, retainer services, recurring SaaS billing. Invoices
3360
3365
  deleteTool('delete_subscription', 'Delete a subscription. Removes the recurring schedule.', 'subscriptions', (client, id) => deleteSubscription(client, id)),
3361
3366
  {
3362
3367
  name: 'cancel_subscription',
3363
- description: 'Cancel an active subscription. Stops future billing but preserves history.',
3368
+ description: 'Cancel an active subscription. Prorates remaining period and stops future billing. Must cancel before delete.',
3364
3369
  params: {
3365
3370
  resourceId: { type: 'string', description: 'Subscription resourceId' },
3371
+ cancelDateType: { type: 'string', enum: ['END_OF_CURRENT_PERIOD', 'END_OF_LAST_PERIOD', 'CUSTOM_DATE'], description: 'When to end (default: END_OF_CURRENT_PERIOD)' },
3372
+ endDate: { type: 'string', description: 'Custom cancel date YYYY-MM-DD (only with CUSTOM_DATE)' },
3366
3373
  },
3367
3374
  required: ['resourceId'],
3368
3375
  group: 'subscriptions',
3369
3376
  readOnly: false,
3370
- execute: async (ctx, input) => cancelSubscription(ctx.client, input.resourceId),
3377
+ execute: async (ctx, input) => cancelSubscription(ctx.client, input.resourceId, {
3378
+ cancelDateType: input.cancelDateType,
3379
+ endDate: input.endDate,
3380
+ }),
3371
3381
  },
3372
3382
  {
3373
3383
  name: 'search_scheduled_transactions',
@@ -3957,4 +3967,53 @@ Returns per-contact result: created, skipped (duplicate), or failed.`,
3957
3967
  };
3958
3968
  },
3959
3969
  },
3970
+ // ══════════════════════════════════════════════════════════════
3971
+ // ── Quick Fix (Bulk Update) ─────────────────────────────────
3972
+ // ══════════════════════════════════════════════════════════════
3973
+ {
3974
+ name: 'quick_fix_transactions',
3975
+ description: 'Bulk-update multiple transactions of the same type in one call. Pass resourceIds + attributes — only present fields are changed. ARAP: invoices, bills, customer-credit-notes, supplier-credit-notes. Accounting: journals, cash-entries. Schedulers: sale-schedules, purchase-schedules, subscription-schedules, journal-schedules. Common attributes: valueDate, contactResourceId, tags, capsuleResourceId. Entity-specific: dueDate (invoices/bills/CNs), invoiceNotes, templateResourceId, currencySettings, taxCurrencySettings, customFields. NOTE: valueDate (and dueDate for invoices/bills) must currently be included even if unchanged.',
3976
+ params: {
3977
+ entity: { type: 'string', enum: [...QUICK_FIX_ENTITIES], description: 'Transaction type to update' },
3978
+ resourceIds: { type: 'array', items: { type: 'string' }, description: 'Array of transaction resourceIds to update' },
3979
+ attributes: { type: 'object', description: 'Fields to update — only present fields are changed. Common: valueDate, contactResourceId, tags, capsuleResourceId. Invoices/bills: dueDate, currencySettings, taxCurrencySettings, customFields. Invoices/CNs: invoiceNotes, templateResourceId, billFrom, billTo. Schedulers: endDate, interval.' },
3980
+ },
3981
+ required: ['entity', 'resourceIds', 'attributes'],
3982
+ group: 'quick_fix',
3983
+ readOnly: false,
3984
+ execute: async (ctx, input) => quickFix(ctx.client, input.entity, {
3985
+ resourceIds: input.resourceIds,
3986
+ attributes: input.attributes,
3987
+ }),
3988
+ },
3989
+ {
3990
+ name: 'quick_fix_line_items',
3991
+ description: 'Bulk-update line items across multiple transactions. For ARAP (invoices, bills, credit notes) and accounting (journals, cash-entries): pass lineItemResourceIds + attributes. For schedulers: pass schedulerUpdates array with per-scheduler lineItemUpdates. ARAP line item attributes: name, quantity, unit, unitPrice, discount, itemResourceId, organizationAccountResourceId, taxProfileResourceId, classifierConfig. Bill/supplier-CN also: withholdingTax. Journal/cash-entry line items: organizationAccountResourceId, amount, description, taxProfileResourceId, classifierConfig.',
3992
+ params: {
3993
+ entity: { type: 'string', enum: [...QUICK_FIX_ENTITIES], description: 'Transaction type' },
3994
+ lineItemResourceIds: { type: 'array', items: { type: 'string' }, description: 'Line item resourceIds to update (ARAP + accounting entities)' },
3995
+ attributes: { type: 'object', description: 'Fields to update on all specified line items' },
3996
+ schedulerUpdates: { type: 'array', items: { type: 'object' }, description: 'Per-scheduler updates (scheduler entities only): [{ schedulerResourceId, lineItemUpdates: [{ arrayIndex, ...fields }] }]' },
3997
+ },
3998
+ required: ['entity'],
3999
+ group: 'quick_fix',
4000
+ readOnly: false,
4001
+ execute: async (ctx, input) => {
4002
+ const entity = input.entity;
4003
+ const isScheduler = ['sale-schedules', 'purchase-schedules', 'subscription-schedules', 'journal-schedules'].includes(entity);
4004
+ const body = {};
4005
+ if (isScheduler) {
4006
+ if (!input.schedulerUpdates)
4007
+ throw new Error('schedulerUpdates is required for scheduler entities (sale-schedules, purchase-schedules, subscription-schedules, journal-schedules)');
4008
+ body.schedulerUpdates = input.schedulerUpdates;
4009
+ }
4010
+ else {
4011
+ if (!input.lineItemResourceIds)
4012
+ throw new Error('lineItemResourceIds is required for non-scheduler entities');
4013
+ body.lineItemResourceIds = input.lineItemResourceIds;
4014
+ body.attributes = input.attributes ?? {};
4015
+ }
4016
+ return quickFixLineItems(ctx.client, entity, body);
4017
+ },
4018
+ },
3960
4019
  ];
package/dist/index.js CHANGED
@@ -49,6 +49,7 @@ import { registerContactGroupsCommand } from './commands/contact-groups.js';
49
49
  import { registerInventoryCommand } from './commands/inventory.js';
50
50
  import { registerSearchCommand } from './commands/search.js';
51
51
  import { registerCustomFieldsCommand } from './commands/custom-fields.js';
52
+ import { registerQuickFixCommand } from './commands/quick-fix.js';
52
53
  import { registerSchemaCommand } from './commands/schema.js';
53
54
  import { applyAllExamples } from './commands/help-examples.js';
54
55
  import { shouldShowPicker, showCommandPicker, attachSubcommandPickers } from './commands/picker.js';
@@ -161,6 +162,7 @@ registerContactGroupsCommand(program);
161
162
  registerInventoryCommand(program);
162
163
  registerSearchCommand(program);
163
164
  registerCustomFieldsCommand(program);
165
+ registerQuickFixCommand(program);
164
166
  registerSchemaCommand(program);
165
167
  applyAllExamples(program);
166
168
  // Add --org to every command that has --api-key (DRY: zero changes to command files)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jaz-clio",
3
- "version": "4.30.9",
3
+ "version": "4.30.11",
4
4
  "description": "Clio — Command Line Interface Orchestrator for Jaz AI.",
5
5
  "type": "module",
6
6
  "bin": {