jaz-clio 5.74.2 → 6.0.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.
Files changed (45) hide show
  1. package/README.md +8 -8
  2. package/assets/skills/api/SKILL.md +3 -36
  3. package/assets/skills/cli/SKILL.md +18 -28
  4. package/assets/skills/cli/references/agent-gotchas.md +2 -2
  5. package/assets/skills/cli/references/command-catalog.md +15 -31
  6. package/assets/skills/cli/references/common-workflows.md +19 -28
  7. package/assets/skills/conversion/SKILL.md +1 -1
  8. package/assets/skills/jaz-kit/SKILL.md +1 -1
  9. package/assets/skills/jaz-kit/references/flows.md +2 -2
  10. package/assets/skills/jaz-pseudo-sql/SKILL.md +1 -1
  11. package/assets/skills/jobs/SKILL.md +42 -42
  12. package/assets/skills/jobs/references/audit-prep.md +8 -8
  13. package/assets/skills/jobs/references/bank-match.md +1 -1
  14. package/assets/skills/jobs/references/bank-recon.md +6 -6
  15. package/assets/skills/jobs/references/building-blocks.md +49 -30
  16. package/assets/skills/jobs/references/credit-control.md +13 -12
  17. package/assets/skills/jobs/references/document-collection.md +6 -6
  18. package/assets/skills/jobs/references/fa-review.md +28 -29
  19. package/assets/skills/jobs/references/gst-vat-filing.md +3 -3
  20. package/assets/skills/jobs/references/month-end-close.md +37 -33
  21. package/assets/skills/jobs/references/payment-run.md +3 -4
  22. package/assets/skills/jobs/references/quarter-end-close.md +20 -18
  23. package/assets/skills/jobs/references/supplier-recon.md +5 -5
  24. package/assets/skills/jobs/references/year-end-close.md +31 -21
  25. package/assets/skills/transaction-recipes/SKILL.md +54 -117
  26. package/assets/skills/transaction-recipes/references/accrued-expenses.md +86 -61
  27. package/assets/skills/transaction-recipes/references/asset-disposal.md +135 -93
  28. package/assets/skills/transaction-recipes/references/bad-debt-provision.md +87 -65
  29. package/assets/skills/transaction-recipes/references/bank-loan.md +104 -70
  30. package/assets/skills/transaction-recipes/references/building-blocks.md +168 -72
  31. package/assets/skills/transaction-recipes/references/capital-wip.md +9 -8
  32. package/assets/skills/transaction-recipes/references/declining-balance.md +91 -64
  33. package/assets/skills/transaction-recipes/references/deferred-revenue.md +105 -68
  34. package/assets/skills/transaction-recipes/references/dividend.md +100 -64
  35. package/assets/skills/transaction-recipes/references/employee-accruals.md +111 -73
  36. package/assets/skills/transaction-recipes/references/fixed-deposit.md +116 -63
  37. package/assets/skills/transaction-recipes/references/fx-revaluation.md +28 -19
  38. package/assets/skills/transaction-recipes/references/hire-purchase.md +45 -44
  39. package/assets/skills/transaction-recipes/references/ifrs16-lease.md +108 -73
  40. package/assets/skills/transaction-recipes/references/intercompany.md +8 -6
  41. package/assets/skills/transaction-recipes/references/prepaid-amortization.md +104 -65
  42. package/assets/skills/transaction-recipes/references/provisions.md +116 -64
  43. package/assets/templates/platform-rules/jaz-agent-rules.md +10 -9
  44. package/cli.mjs +529 -607
  45. package/package.json +1 -1
@@ -1,57 +1,47 @@
1
- # Recipe: Accrued Expenses (engine name: `accrued-expense`)
1
+ # Recipe: Accrued Expenses (calculator type: `accrued-expense`)
2
2
 
3
- > Canonical recipe for month-end expense accruals (utilities, professional fees, cleaning, employee bonuses), incurred but not yet billed. The engine creates dual-entry accrual + reversal journal pairs, all dated and DRAFT, attached to one capsule.
3
+ > Canonical recipe for month-end expense accruals (utilities, professional fees, cleaning, employee bonuses), incurred but not yet billed. Each period is a pair of journals: the accrual at period-end and its reversal in the next period, all in one capsule. You calculate, create the capsule and post each journal yourself (see `building-blocks.md` § The three-step flow).
4
4
 
5
- ## Tools, recipes, calculators this recipe uses
5
+ ## Tools and calculator this recipe uses
6
6
 
7
- ### Recipe engine entry point
8
- - **`plan_recipe(recipe: 'accrued-expense', ...)`**, used in step 2: returns RecipePlan with N accrual + N reversal journals (2N total), capsule shape, required accounts, vendor metadata.
9
- - **`execute_recipe(recipe: 'accrued-expense', ...)`**, used in step 4: posts 2N future-dated DRAFT journals (one accrual at period-end, one reversal at next-period-start, repeated for `periods`) all attached to the same capsule.
7
+ ### Calculator (offline, posts nothing)
8
+ - **MCP: `calculate(type: 'accrued-expense', amount, periods, frequency, startDate, currency)`** (step 1).
9
+ - **CLI: `clio calc accrued-expense --amount <per-period> --periods <n> --start-date <YYYY-MM-DD> --currency <code> --json`** (step 1): the same result.
10
10
 
11
- ### Calculator (cross-check, no API key needed)
12
- - **`clio calc accrued-expense --amount <per-period> --periods <n> --start-date <YYYY-MM-DD> --currency <code> --json`**, used in step 1: independently produce the schedule. Returns `{ totalAccrued, schedule[n] }` where each row carries `accrualDate`, `reversalDate`, `accrualJournal`, `reversalJournal`.
11
+ ### Posting tools
12
+ - **`list_capsule_types` / `create_capsule_type(displayName: 'Accrued Expenses')` / `create_capsule(...)`** (step 3).
13
+ - **`create_journal(...)`** (step 4): one per accrual and one per reversal, each with `capsuleResourceId`.
13
14
 
14
- ### Tools (jaz-api / direct)
15
- - **`search_journals(filter: {tags: {eq: <accrual.name>}, valueDate: <prior-period>-end})`**, step 1 estimation: pull last period's posted accrual amount when `estimation_method: 'prior_month'`.
16
- - **`search_journals(filter: {tags: {eq: <accrual.name>}, valueDate: {between: [<-3 months>, <today>]}})`**, step 1 alt: trailing 3-month average when `estimation_method: 'trailing_3m_avg'`.
17
- - **`search_contacts(filter: {name: {eq: <vendor>}})`**, step 3: resolve the accrual's vendor.
18
- - **`search_accounts(filter: {name: {in: ['<expense GL>', '<accrued liability GL>']}})`**, step 3: confirm both sides of the journal exist in CoA.
19
- - **`get_journal(resourceId)` per candidate DRAFT**: step 5 monthly: find this period's pre-emitted DRAFT for finalization; journals cannot be filtered by capsule: check each candidate with `get_journal` (its `capsule.resourceId`) and confirm the set with the practitioner.
20
- - **`bulk_update_journals(items: [{resourceId: <id>, saveAsDraft: false}, ...])`**, step 5: finalize this period's accrual + reversal pair.
21
- - **`generate_trial_balance(endDate: <date>)`**, step 5 verification: confirm Accrued Expenses balance and net P&L impact.
15
+ ### Lookup and verification tools
16
+ - **`search_journals(filter: {tags: {eq: <accrual tag>}, valueDate: {between: [<-3 months>, <today>]}})`** (step 1 estimation): pull the posted accrual amounts for the prior month or the trailing three months.
17
+ - **`search_contacts(filter: {name: {eq: <vendor>}})`** (step 2): resolve the accrual's vendor for the journal narrative.
18
+ - **`search_accounts(filter: {name: {in: ['<expense GL>', '<accrued liability GL>']}})`** (step 2): confirm both sides of the journal exist in the CoA.
19
+ - **`update_journal(resourceId: <id>, saveAsDraft: false)`** (step 5): finalize a draft accrual or reversal.
20
+ - **`generate_trial_balance(endDate: <date>)`** (step 5 verification): confirm the Accrued Expenses balance and net P&L impact.
22
21
 
23
22
  ### Cross-references
24
23
  - Operational context: invoked during month-end close, once per recurring accrual whose last-posted period precedes the current period end. The close loop supplies the estimation method, GL account, vendor, and fixed/budget amount for each accrual.
25
- - Sibling recipes: `prepaid-amortization.md` (mirror: paid upfront, recognized over time vs incurred over time, billed later); `employee-accruals.md` (the same engine for leave / 13th month bonuses, but with monthly-only, no reversal needed since those accumulate).
24
+ - Sibling recipes: `prepaid-amortization.md` (mirror: paid upfront, recognized over time vs incurred over time, billed later); `employee-accruals.md` (bonus accruals use this same calculator; leave uses `leave-accrual`, monthly-only with no reversal, since leave accumulates).
26
25
  - IFRS / accounting context: matching principle (IFRS Conceptual Framework). The reversal pattern eliminates double-counting when the actual bill arrives.
27
26
 
28
27
  ---
29
28
 
30
29
  ## Step-by-step
31
30
 
32
- ### Step 1: Compute the per-period amount
31
+ ### Step 1: Compute the per-period amount and calculate
33
32
 
34
33
  Per the accrual's estimation method:
35
34
 
36
- - **`prior_month`**: pull last period's posted amount via `search_journals(filter: {tags: {eq: <accrual.name>}, valueDate: <prior-period>-end})`. Use that amount.
35
+ - **`prior_month`**: pull last period's posted amount via `search_journals` on the accrual's tag and the prior period's dates. Use that amount.
37
36
  - **`trailing_3m_avg`**: pull last 3 months' posted amounts and average.
38
37
  - **`budget`**: use the accrual's budget amount.
39
38
  - **`fixed_amount`**: use the accrual's fixed amount directly.
40
39
 
41
- Cross-check with the calculator:
40
+ Then run the calculator:
42
41
 
43
42
  ```
44
- clio calc accrued-expense --amount 3000 --periods 1 --start-date 2025-01-31 --currency SGD --json
45
- ```
46
-
47
- Returns `{ totalAccrued: 3000, schedule: [{accrualDate: '2025-01-31', reversalDate: '2025-02-01', accrualJournal: {...}, reversalJournal: {...}}] }`. For a single-period accrual, `periods: 1`. For a multi-period rolling accrual (e.g. quarterly bill arriving in month 3), use `periods: 3`.
48
-
49
- ### Step 2: Plan the recipe
50
-
51
- ```
52
- plan_recipe(
53
- // Accounts, capsule and counterparty are not plan_recipe params: execute_recipe resolves accounts from the CoA and takes bankAccountName / contactName.
54
- recipe: 'accrued-expense',
43
+ calculate(
44
+ type: 'accrued-expense',
55
45
  amount: 3000,
56
46
  periods: 1,
57
47
  startDate: '2025-01-31',
@@ -59,62 +49,97 @@ plan_recipe(
59
49
  )
60
50
  ```
61
51
 
62
- For multi-period rolling: pass `periods: 3` (engine emits 3 accrual + 3 reversal pairs across the rolling window).
52
+ ```
53
+ clio calc accrued-expense --amount 3000 --periods 1 --start-date 2025-01-31 --currency SGD --json
54
+ ```
55
+
56
+ Returns `{ totalAccruals: 3000, schedule: [{ period, accrualDate: '2025-01-31', reversalDate: '2025-02-28', amount: 3000, accrualJournal, reversalJournal }], blueprint }`. For a single-period accrual, `periods: 1`. For a multi-period rolling accrual (e.g. quarterly bill arriving in month 3), use `periods: 3`.
57
+
58
+ `blueprint.steps` (2 journals for `periods: 1`; 6 for `periods: 3`):
59
+ - Step 1, `journal`, dated `2025-01-31`: Dr Expense Account 3,000 / Cr Accrued Liability 3,000.
60
+ - Step 2, `journal`, dated `2025-02-28`: Dr Accrued Liability 3,000 / Cr Expense Account 3,000.
63
61
 
64
- Returns: `RecipePlan` with `requiredAccounts: [<expense GL>, 'Accrued Expenses']`, `needsContact: false` (vendor metadata only, no AR/AP transaction in this recipe), `steps: [{action: 'journal', date: '2025-01-31', ...}, {action: 'journal', date: '2025-02-01', ...}]` (2 journals for `periods: 1`; 6 for `periods: 3`).
62
+ The calculator dates each reversal one period after its accrual. If the entity reverses on day 1 of the next period (the usual convention), post the reversal with `valueDate: '2025-02-01'` instead: both dates fall in the next period, so the monthly P&L is the same.
65
63
 
66
- ### Step 3: Resolve dependencies
64
+ ### Step 2: Resolve accounts
67
65
 
68
- For both accounts in `requiredAccounts`:
69
- - `search_accounts(filter: {name: {eq: <accountName>}})`. If empty: halt and surface "Accrual references GL account `<accountName>` not in Jaz CoA. Create via `create_account` (suggested classifications: expense → `Operating Expense`; accrued liability → `Current Liability`) or remap the account before retry."
66
+ The blueprint's `Expense Account` and `Accrued Liability` are labels. Map each to the real account:
67
+ - `search_accounts(filter: {name: {in: ['<expense GL>', 'Accrued Expenses']}})`. If one is missing: halt and surface "Accrual needs GL account `<accountName>`, which is not in the Jaz CoA. Create via `create_account` (suggested classifications: expense → `Operating Expense`; accrued liability → `Current Liability`) or remap the account."
70
68
 
71
- Vendor: confirm via `search_contacts(filter: {name: {eq: <vendor>}})` for tagging on the journal narrative, but this recipe does NOT require a contact for posting (no AR/AP).
69
+ Vendor: confirm via `search_contacts(filter: {name: {eq: <vendor>}})` for the journal narrative. The accrual itself needs no contact (no AR/AP document).
72
70
 
73
- ### Step 4: Execute
71
+ ### Step 3: Create the capsule
74
72
 
75
73
  ```
76
- execute_recipe(recipe: 'accrued-expense', ...same args...) // accounts auto-resolved from CoA; pass `bankAccountName` / `contactName` for fuzzy resolve
74
+ list_capsule_types()
75
+ create_capsule(
76
+ capsuleTypeResourceId: <id of 'Accrued Expenses'>,
77
+ title: 'Utilities Accrual, PowerCo, FY2025',
78
+ description: <blueprint.capsuleDescription>
79
+ )
77
80
  ```
78
81
 
79
- Returns: `{ capsule: {resourceId, type, title}, steps: [{step, action, status, resourceId}, ...], summary: {total: 2N, created: 2N, ...} }`. The recipe creates **2N future-dated DRAFT journals** (for `periods: 1`, that's 2 journals: 1 accrual + 1 reversal). All attach to the same capsule.
82
+ If `Accrued Expenses` is not in the list: `create_capsule_type(displayName: 'Accrued Expenses')` first. A recurring accrual keeps ONE capsule: later periods post into the same one.
83
+
84
+ ### Step 4: Post the journals
85
+
86
+ ```
87
+ create_journal(
88
+ valueDate: '2025-01-31',
89
+ reference: 'ACCR-POWERCO-2025-01',
90
+ journalEntries: [
91
+ { accountResourceId: <expense GL>, type: 'DEBIT', amount: 3000, description: 'Accrued utilities: Jan 2025' },
92
+ { accountResourceId: <Accrued Expenses>, type: 'CREDIT', amount: 3000, description: 'Accrued utilities: Jan 2025' }
93
+ ],
94
+ saveAsDraft: false,
95
+ capsuleResourceId: <capsule id>
96
+ )
97
+ create_journal(
98
+ valueDate: '2025-02-01',
99
+ reference: 'ACCR-POWERCO-2025-01-REV',
100
+ journalEntries: [
101
+ { accountResourceId: <Accrued Expenses>, type: 'DEBIT', amount: 3000, description: 'Reverse accrued utilities: Jan 2025' },
102
+ { accountResourceId: <expense GL>, type: 'CREDIT', amount: 3000, description: 'Reverse accrued utilities: Jan 2025' }
103
+ ],
104
+ saveAsDraft: true,
105
+ capsuleResourceId: <capsule id>
106
+ )
107
+ ```
80
108
 
81
- Engine output journals:
82
- - Step 1 (date `2025-01-31`): Dr `<expense GL>` 3000 / Cr Accrued Expenses 3000. DRAFT.
83
- - Step 2 (date `2025-02-01`): Dr Accrued Expenses 3000 / Cr `<expense GL>` 3000. DRAFT; reverses step 1 in February.
109
+ The reversal stays a DRAFT until February's close.
84
110
 
85
- ### Step 5: Monthly action (finalize)
111
+ ### Step 5: Monthly action
86
112
 
87
- For the current period (Jan 2025), the accrual journal exists in DRAFT in the capsule. Monthly close action:
113
+ January close: the accrual is posted (step 4). February close: find the reversal by its reference and finalize it, then post February's accrual if the bill still has not arrived (run step 1 again for February's amount and post the new pair into the same capsule).
88
114
 
89
115
  ```
90
- **STOP: not selectable by filter.** Journals carry no capsule or fixed-asset link in either direction (`JournalFilter` declares neither; a journal row has no such field even at `view: 'full'`; `GET /capsules/{id}` returns only a `totalTransactions` count, measured 2026-09-07). A date+status search returns every matching DRAFT in the org, so it must never feed `bulk_update_journals` or `delete_journal`. Surface the capsule and its expected count to the practitioner and let them identify the journals.
91
- update_journal(resourceId: <accrual journal id>, saveAsDraft: false)
116
+ search_journals(filter: {reference: {eq: 'ACCR-POWERCO-2025-01-REV'}})
117
+ update_journal(resourceId: <reversal journal id>, saveAsDraft: false)
92
118
  ```
93
119
 
94
- The reversal journal (dated 2025-02-01) stays DRAFT until February's monthly-close. **Do NOT finalize both at once**; that defeats the period-matching purpose. February's close finalizes the reversal AND any new accrual for Feb if the bill still hasn't arrived (run `plan_recipe + execute_recipe` again for Feb's `period`).
120
+ **Do NOT finalize an accrual and its reversal in the same period**; that defeats the period-matching purpose. Journals cannot be filtered by capsule, and a date-plus-status search returns every matching DRAFT in the org: never feed one into `bulk_update_journals` or `delete_journal`.
95
121
 
96
- Verify after January finalize:
122
+ Verify after January:
97
123
  - `generate_trial_balance(endDate: '2025-01-31')`.
98
124
  - Assert: `balance['Accrued Expenses']` increased by 3000.
99
125
  - Assert: `balance['<expense GL>']` (period MTD) increased by 3000.
100
126
  - Net P&L impact for January: $3,000 expense.
101
127
 
102
- After February finalize (with reversal + new Feb accrual posted):
128
+ After February (with reversal + new Feb accrual posted):
103
129
  - `balance['Accrued Expenses']` reflects only the Feb accrual (Jan's reversed cleanly).
104
130
 
105
131
  When the actual quarterly bill arrives (typically Mar 31, $9,000 total): post a normal `create_bill(...)` for the full $9,000 against `<expense GL>`. The capsule may or may not include this bill; practitioner's call. The accrual capsule's net P&L impact is zero across the rolling window once all reversals post.
106
132
 
107
133
  ---
108
134
 
109
- ## Common error classes and recovery
135
+ ## Common problems and recovery
110
136
 
111
- | Source | Error | Recovery |
137
+ | Where | Problem | Recovery |
112
138
  |--------|-------|----------|
113
- | `plan_recipe` | 422 `unsupported_recipe` | File-name alias `accrued-expenses` was used. Use canonical engine name `accrued-expense` (singular). |
114
- | `plan_recipe` | 422 `invalid_amount` | Amount is non-positive. Computed amount may have been a credit (e.g. `prior_month` returned a credit balance). Switch `estimation_method` to `fixed_amount` for this row this period, OR investigate the credit. |
115
- | `execute_recipe` | 422 `account_not_found` | Step 3 resolution incomplete. `search_accounts`; create via `create_account` if the practitioner confirms classification. |
116
- | `bulk_update_journals` | 422 `journal_unbalanced` | Engine-emitted journals are always balanced. If you see this, the source schema changed; escalate (do not retry). |
117
- | Verification | Net P&L impact stuck after reversal posts | Likely the reversal hasn't been finalized yet. Find it (journals cannot be filtered by capsule: check each candidate with `get_journal` (its `capsule.resourceId`) and confirm the set with the practitioner); if it is still DRAFT, finalize it. |
139
+ | Calculator | "Amount must be a positive number" | The computed amount is zero or negative: `prior_month` may have returned a credit balance. Switch the estimation method to `fixed_amount` for this row this period, OR investigate the credit. |
140
+ | Step 2 | An account is missing | `search_accounts`; create via `create_account` if the practitioner confirms classification. |
141
+ | `create_journal` | "Unbalanced journal" | The lines were retyped incorrectly. Take both amounts from the calculator's step; debits must equal credits. |
142
+ | Verification | Net P&L impact stuck after the reversal should have posted | The reversal is likely still a DRAFT. Find it by its reference (journals cannot be filtered by capsule) and finalize it. |
118
143
  | Verification | Accrued Expenses balance nonzero after the actual bill posts AND all reversals run | Either the bill amount diverged from the accrual estimate (post a true-up journal: Dr/Cr `<expense GL>` for the difference) OR a reversal was missed. Audit via `generate_general_ledger(accountResourceIds: [<Accrued Expenses id>], startDate: <accrual date>, endDate: <today>)`. |
119
144
  | Actual bill posted against `Accrued Expenses` instead of `<expense GL>` | (process error) | The reversal AND the bill both touch `Accrued Expenses`: net to zero on liability, but expense gets double-recognized. Reverse the bill, re-post against `<expense GL>`. Note the risk in your working notes. |
120
145
 
@@ -122,11 +147,11 @@ When the actual quarterly bill arrives (typically Mar 31, $9,000 total): post a
122
147
 
123
148
  ## Variations
124
149
 
125
- - **Multi-period rolling accrual** (quarterly bill, monthly accrual): `periods: 3` with `frequency: 'monthly'`. Engine emits 3 accrual + 3 reversal pairs. Each month's monthly-close finalizes that month's pair.
126
- - **One-shot accrual** (year-end true-up where you don't know the per-period split): `periods: 1`, `startDate: <FY-end>`. The reversal posts on FY-end+1 day. Practitioner posts the actual bill in the next FY when it arrives.
150
+ - **Multi-period rolling accrual** (quarterly bill, monthly accrual): `periods: 3` with `frequency: 'monthly'`. The calculator returns 3 accrual + 3 reversal pairs. Each month's close posts that month's pair.
151
+ - **One-shot accrual** (year-end true-up where you don't know the per-period split): `periods: 1`, `startDate: <FY-end>`. Post the reversal on FY-end+1 day. The practitioner posts the actual bill in the next FY when it arrives.
127
152
  - **Quarterly cadence**: `frequency: 'quarterly', periods: 4` for an annual rolling accrual (rare for SMBs).
128
- - **Different estimation per period**: not supported by a single `plan_recipe` call; engine assumes constant per-period amount. For variable amounts: invoke once per period with the new amount each time.
129
- - **Vendor unknown** (estimating an accrual but supplier not yet identified): pass `vendor: null` or skip the field. Engine still posts; practitioner can update the journal narrative later via `update_journal`.
153
+ - **Different estimate per period**: the calculator assumes a constant per-period amount. For variable amounts, run it once per period with that period's amount.
154
+ - **Vendor unknown** (estimating an accrual but supplier not yet identified): leave the vendor out of the narrative. The journal still posts; update the narrative later via `update_journal`.
130
155
 
131
156
  ---
132
157
 
@@ -1,27 +1,28 @@
1
- # Recipe: Asset Disposal (engine name: `asset-disposal`)
1
+ # Recipe: Asset Disposal (calculator type: `asset-disposal`)
2
2
 
3
- > One-shot recipe for fixed-asset disposal under IAS 16.67-72 (sale, scrap, write-off). Engine emits 1 disposal journal + 1 note (manual FA-register update). The note step is engine-SKIPPED; practitioner invokes the right Jaz FA endpoint manually after `execute_recipe`.
3
+ > One-shot recipe for fixed-asset disposal under IAS 16.67-72 (sale, scrap, write-off). One disposal journal, plus the fixed-asset register update that stops depreciation. The calculator returns the accumulated depreciation, net book value, gain or loss and the journal lines; you post the journal and update the register yourself (see `building-blocks.md` § The three-step flow).
4
4
 
5
- ## Tools, recipes, calculators this recipe uses
5
+ ## Tools and calculator this recipe uses
6
6
 
7
- ### Recipe engine entry point
8
- - **`plan_recipe(recipe: 'asset-disposal', ...)`** (used in step 2): returns RecipePlan with the disposal journal (Dr Cash + Dr Accumulated Depreciation + Dr/Cr Loss/Gain on Disposal / Cr Fixed Asset Cost) and a note step.
9
- - **`execute_recipe(recipe: 'asset-disposal', ...)`** (used in step 4): posts the disposal journal. The note step is SKIPPED (engine surfaces it in `summary.notes` for the practitioner to action manually).
7
+ ### Calculator (offline, posts nothing)
8
+ - **MCP: `calculate(type: 'asset-disposal', cost, salvageValue, usefulLifeYears, acquisitionDate, disposalDate, proceeds, method, currency)`** (step 1). `method` is `sl` (the default), `ddb` or `150db`.
9
+ - **CLI: `clio calc asset-disposal --cost <c> --salvage <s> --life <years> --acquired <YYYY-MM-DD> --disposed <YYYY-MM-DD> --proceeds <p> --method <sl|ddb|150db> --currency <code> --json`** (step 1): the same result.
10
10
 
11
- ### Calculator (cross-check, no API key needed)
12
- - **`clio calc asset-disposal --cost <c> --salvage <s> --life <years> --acquired <YYYY-MM-DD> --disposed <YYYY-MM-DD> --proceeds <p> --method <sl|ddb|150db> --currency <code> --json`** (used in step 1): computes accumulated depreciation to disposal date, net book value, gain/loss. Returns `{ accumulatedDepreciation, netBookValue, proceeds, gainOrLoss, classification: 'gain' | 'loss' }`.
11
+ ### Posting tools
12
+ - **`list_capsule_types` / `create_capsule_type(displayName: 'Asset Disposal')` / `create_capsule(...)`** (step 3).
13
+ - **`create_journal(...)`** (step 4): the disposal journal.
14
+ - **`mark_fixed_asset_sold(...)`** for a sale (it links the sale transaction and records gain/loss) or **`discard_fixed_asset(...)`** for a write-off (step 5). A disposal is its own operation, never a `status` change via `update_fixed_asset`.
13
15
 
14
- ### Tools (jaz-api / direct)
15
- - **`get_fixed_asset(resourceId: <id>)`** (step 1): pull the asset's actual `cost`, `acquisitionDate`, `usefulLifeMonths`, `depreciationMethod`, `salvageValue` to feed the calculator (use the live FA-register values; auditor wants those, not a cached estimate).
16
- - **`generate_fa_summary(primarySnapshotStartDate: <FY-start>, primarySnapshotEndDate: <disposalDate>, groupBy: 'CATEGORY')`** (step 1 alt): pull NBV directly from Jaz's running FA register. If this matches your independent calc, use it as the authoritative NBV. If they diverge: investigate (likely a missing depreciation journal).
17
- - **`search_capsules(filter: {title: {eq: <capsule.name>}})`**: step 0 idempotency check. Each disposal is unique; duplicate disposal journals would corrupt the FA register reconciliation.
18
- - **`search_accounts(filter: {name: {in: ['Vehicles', 'Accumulated Depreciation (Vehicles)', 'Gain on Disposal', 'Loss on Disposal']}})`**: step 3.
19
- - **`mark_fixed_asset_sold(...)` for a sale (it links the sale transaction and records gain/loss) or `discard_fixed_asset(...)` for a write-off; a disposal is its own operation, never a `status` mutation via update_fixed_asset** OR **`POST /api/v1/mark-as-sold/fixed-assets`** OR **`POST /api/v1/discard-fixed-assets/{id}`**: step 5 manual FA-register update (the engine-skipped note step).
16
+ ### Lookup and verification tools
17
+ - **`get_fixed_asset(resourceId: <id>)`** (step 1): the asset's registered cost, purchase date and net book value. Use the live FA-register values; the auditor wants those, not a cached estimate.
18
+ - **`generate_fa_summary(primarySnapshotStartDate: <FY-start>, primarySnapshotEndDate: <disposalDate>, groupBy: 'CATEGORY')`** (step 1 alt): NBV from Jaz's running FA register. If this matches your independent calculation, use it as the authoritative NBV. If they diverge: investigate (likely a missing depreciation journal).
19
+ - **`search_capsules(filter: {title: {eq: <capsule title>}})`**: step 0 idempotency check. Each disposal is unique; duplicate disposal journals would corrupt the FA register reconciliation.
20
+ - **`search_accounts(filter: {name: {in: ['Vehicles', 'Accumulated Depreciation (Vehicles)', 'Gain on Disposal', 'Loss on Disposal']}})`**: step 2.
20
21
  - **`generate_trial_balance(endDate: <disposalDate>)`** (step 6): verify cost + accumulated depreciation cleared; gain/loss in P&L.
21
22
 
22
23
  ### Cross-references
23
24
  - Operational context: invoked during year-end close (FA disposals discovered during the year-end review) or ad-hoc during month-end close if a disposal happens mid-period.
24
- - Sibling: `declining-balance.md` (depreciation up to the disposal date; engine handles this internally via the calculator); `capital-wip.md` (the inverse: adding to FA, not disposing).
25
+ - Sibling: `declining-balance.md` (depreciation up to the disposal date; the disposal calculator recomputes it from the same inputs); `capital-wip.md` (the inverse: adding to FA, not disposing).
25
26
  - IFRS / accounting context: IAS 16.67-72 (derecognition); IAS 16.71 (gain on disposal classified as Other Income, NOT revenue); IAS 16.68 (gain/loss = net proceeds − carrying amount).
26
27
 
27
28
  ---
@@ -31,37 +32,22 @@
31
32
  ### Step 0: Idempotency check
32
33
 
33
34
  ```
34
- search_capsules(filter: {title: {eq: 'Disposal — Delivery Vehicle (Truck-001) — 2026-03-15'}})
35
+ search_capsules(filter: {title: {eq: 'Disposal, Delivery Vehicle (Truck-001), 2026-03-15'}})
35
36
  ```
36
37
 
37
38
  If a result returns: halt. Disposal capsules are unique per asset+date; duplicates would double-book the disposal.
38
39
 
39
- ### Step 1: Pull asset details + independent cross-check
40
+ ### Step 1: Pull asset details and calculate
40
41
 
41
42
  ```
42
43
  get_fixed_asset(resourceId: <FA UUID>)
43
44
  ```
44
45
 
45
- Returns: `{ resourceId, name, cost: 50000, acquisitionDate: '2023-01-01', usefulLifeMonths: 60, depreciationMethod: 'sl', salvageValue: 5000, status: 'ACTIVE', capsuleResourceId: <originating cap if any> }`. Use these as the authoritative inputs to the calculator (don't accept user-provided values blindly; auditor will tie back to the FA register).
46
+ Read the registered values off the record: `purchaseAmount` (cost), `purchaseDate` (epoch milliseconds: convert to a date), `netBookValueAmount`, `status`, and the asset's depreciation settings (life, method, residual value) as registered. Use these as the authoritative inputs to the calculator (don't accept user-provided values blindly; auditor will tie back to the FA register).
46
47
 
47
48
  ```
48
- clio calc asset-disposal --cost 50000 --salvage 5000 --life 5 --acquired 2023-01-01 --disposed 2026-03-15 --proceeds 18000 --method sl --currency SGD --json
49
- ```
50
-
51
- Returns: `{ accumulatedDepreciation: 28500, netBookValue: 21500, proceeds: 18000, gainOrLoss: -3500, classification: 'loss' }`. (`(50000 - 5000) / 60 months × 38 months elapsed = 28,500` accumulated; NBV = 50,000 − 28,500 = 21,500; loss = 18,000 − 21,500 = −3,500).
52
-
53
- Cross-check with Jaz FA register:
54
- ```
55
- get_fixed_asset(resourceId: <FA UUID>)
56
- ```
57
- Should report `accumulatedDepreciation: 28500, netBookValue: 21500` matching the independent calc within 1 cent. Variance investigation: missing monthly depreciation journals (journals cannot be filtered by capsule: check each candidate with `get_journal` (its `capsule.resourceId`) and confirm the set with the practitioner); finalize them BEFORE disposal recipe.
58
-
59
- ### Step 2: Plan the recipe
60
-
61
- ```
62
- plan_recipe(
63
- // Accounts, capsule and counterparty are not plan_recipe params: execute_recipe resolves accounts from the CoA and takes bankAccountName / contactName.
64
- recipe: 'asset-disposal',
49
+ calculate(
50
+ type: 'asset-disposal',
65
51
  cost: 50000,
66
52
  salvageValue: 5000,
67
53
  usefulLifeYears: 5,
@@ -73,67 +59,119 @@ plan_recipe(
73
59
  )
74
60
  ```
75
61
 
76
- Returns `RecipePlan` with `requiredAccounts: ['Vehicles', 'Accumulated Depreciation (Vehicles)', 'Loss on Disposal' (since classification: 'loss'), 'Cash / Bank Account']`, `needsContact: false`, `needsBankAccount: true` (proceeds receipt), `steps`:
77
- - Step 1: disposal journal, multi-line:
62
+ ```
63
+ clio calc asset-disposal --cost 50000 --salvage 5000 --life 5 --acquired 2023-01-01 --disposed 2026-03-15 --proceeds 18000 --method sl --currency SGD --json
64
+ ```
65
+
66
+ Returns `{ monthsHeld: 39, accumulatedDepreciation: 29250, netBookValue: 20750, gainOrLoss: -2750, isGain: false, disposalJournal, blueprint }`. The calculator counts a part month as a full month, so 2023-01-01 to 2026-03-15 is 39 months: `(50000 - 5000) / 60 months × 39 months = 29,250` accumulated; NBV = 50,000 − 29,250 = 20,750; loss = 18,000 − 20,750 = −2,750.
67
+
68
+ `blueprint.steps`:
69
+ - Step 1, `journal`, dated `2026-03-15`, multi-line:
78
70
  - Dr Cash / Bank Account 18,000 (proceeds received)
79
- - Dr Accumulated Depreciation (Vehicles) 28,500 (clear contra-asset)
80
- - Dr Loss on Disposal 3,500 (P&L impact)
81
- - Cr Vehicles 50,000 (clear original cost)
82
- - Step 2: NOTE step, `Update Jaz FA register: use POST /mark-as-sold/fixed-assets (if sold) or POST /discard-fixed-assets/:id (if scrapped).` Engine SKIPS this; practitioner handles manually in step 5.
71
+ - Dr Accumulated Depreciation 29,250 (clear contra-asset)
72
+ - Dr Loss on Disposal 2,750 (P&L impact)
73
+ - Cr Fixed Asset (at cost) 50,000 (clear original cost)
74
+ - Step 2, `note`: an instruction to update the FA register. For a registered asset the register books the disposal itself, so step 1 is a cross-check, not an entry to post (step 4 below).
83
75
 
84
- For scrap (no proceeds): `proceeds: 0` → `gainOrLoss: -netBookValue` → all loss.
85
- For gain (proceeds > NBV): engine debits Cash, debits Accum Dep, credits Vehicles, AND credits Gain on Disposal for the difference.
76
+ For scrap (no proceeds): `proceeds: 0` → `gainOrLoss: -netBookValue` → all loss, and the journal has no cash line.
77
+ For a gain (proceeds > NBV): the journal debits Cash, debits Accumulated Depreciation, credits Fixed Asset (at cost), AND credits Gain on Disposal for the difference.
86
78
 
87
- ### Step 3: Resolve dependencies
79
+ Cross-check with the Jaz FA register: `netBookValueAmount` from `get_fixed_asset` should match the calculator's `netBookValue` within 1 cent. The register depreciates to the posting date it has reached, so a difference of the current month's charge is timing, not error. A larger variance means depreciation is missing or was posted outside the register: resolve it BEFORE posting the disposal.
88
80
 
89
- For each account in `requiredAccounts`:
90
- - `search_accounts(filter: {name: {eq: <accountName>}})`. Suggested classifications: asset → `Non-current Asset`; accum dep → `Non-current Asset` contra; **`Gain on Disposal` → `Other Revenue`** (NOT Operating Revenue per IAS 16.71); `Loss on Disposal` → `Other Expense` (or `Operating Expense`, jurisdiction-specific).
81
+ ### Step 2: Resolve accounts and the bank account
91
82
 
92
- Bank account: resolve via `list_bank_accounts()` if the bank account resourceId isn't already known. For scrap (no proceeds): `bankAccountResourceId` is required only if there's a disposal-cost cash-out (e.g., scrap fee paid to disposal vendor); pass it for safety even if proceeds are zero.
83
+ The blueprint's account names are labels. Map each to the real account:
84
+ - `search_accounts(filter: {name: {in: ['Vehicles', 'Accumulated Depreciation (Vehicles)', 'Gain on Disposal', 'Loss on Disposal']}})`. Suggested classifications: asset → `Non-current Asset`; accum dep → `Non-current Asset` contra; **`Gain on Disposal` → `Other Revenue`** (NOT Operating Revenue per IAS 16.71); `Loss on Disposal` → `Other Expense` (or `Operating Expense`, jurisdiction-specific).
93
85
 
94
- ### Step 4: Execute
86
+ Bank account: `list_bank_accounts()` for the account the proceeds arrive in. For scrap (no proceeds) none is needed, unless there is a disposal cost (e.g., scrap fee paid to a disposal vendor), which is its own `create_cash_out`.
87
+
88
+ ### Step 3: Create the capsule
95
89
 
96
90
  ```
97
- execute_recipe(recipe: 'asset-disposal', ...same args...) // accounts auto-resolved from CoA; pass `bankAccountName` / `contactName` for fuzzy resolve
91
+ list_capsule_types()
92
+ create_capsule(
93
+ capsuleTypeResourceId: <id of 'Asset Disposal'>,
94
+ title: 'Disposal, Delivery Vehicle (Truck-001), 2026-03-15',
95
+ description: <blueprint.capsuleDescription>
96
+ )
98
97
  ```
99
98
 
100
- Returns: `{ capsule: {resourceId, type, title}, steps: [{step: 1, action: 'journal', status: 'created', resourceId: <journal id>}, {step: 2, action: 'note', status: 'skipped'}], summary: {total: 2, created: 1, skipped: 1, notes: ['Step 2 (note): Update Jaz FA register: use POST /mark-as-sold/fixed-assets (if sold) or POST /discard-fixed-assets/:id (if scrapped).'] } }`.
99
+ If `Asset Disposal` is not in the list: `create_capsule_type(displayName: 'Asset Disposal')` first.
101
100
 
102
- The disposal journal is DRAFT. Finalize when ready: `update_journal(resourceId: <journal id>, saveAsDraft: false)`.
101
+ ### Step 4: Book the disposal
103
102
 
104
- ### Step 5: Manual FA register update (CRITICAL)
103
+ Which entries you post depends on whether the asset is in the Jaz fixed-asset register. **Never do both**: the register books a disposal itself, so posting the calculator's full disposal journal and then running a register tool books the disposal twice.
105
104
 
106
- The engine cannot auto-update the FA register status; practitioner MUST do this manually. Without this step, the FA register will continue to show the asset as `ACTIVE` and Jaz will continue to auto-post depreciation journals (for SL assets), corrupting the disposal.
105
+ **A. The asset is in the register (the usual case).** Do NOT post the calculator's disposal journal. Its figures are your cross-check.
107
106
 
108
- Pick the right endpoint based on disposal type:
107
+ *Sale (proceeds received).* Jaz requires a sale entry to exist before an asset can be marked sold: an invoice line on the asset's account, or a journal whose CREDIT line is on the asset's fixed-asset account. Post the proceeds as that entry, then mark the asset sold against the credit line:
109
108
 
110
- **Sale (proceeds received):**
111
109
  ```
112
- POST /api/v1/mark-as-sold/fixed-assets
113
- {
114
- fixedAssetResourceId: <FA UUID>,
115
- disposalDate: '2026-03-15',
116
- proceeds: 18000,
117
- notes: 'Sold to <buyer>; capsule <capsuleResourceId>'
118
- }
110
+ create_journal(
111
+ valueDate: '2026-03-15',
112
+ reference: 'DISP-TRUCK-001',
113
+ journalEntries: [
114
+ { accountResourceId: <bank account>, type: 'DEBIT', amount: 18000, description: 'Proceeds, Truck-001' },
115
+ { accountResourceId: <Vehicles>, type: 'CREDIT', amount: 18000, description: 'Sale of Truck-001' }
116
+ ],
117
+ saveAsDraft: true,
118
+ capsuleResourceId: <capsule id>
119
+ )
119
120
  ```
120
- OR the MCP tool: `mark_fixed_asset_sold(resourceId: <id>, depreciationEndDate: '2026-03-15', assetDisposalGainLossAccountResourceId: <Gain/Loss on Disposal>, saleBusinessTransactionType: 'SALE', saleItemResourceId: <sale line item id>)` for a sale, or `discard_fixed_asset(resourceId: <id>, disposalDate: '2026-03-15', depreciationEndDate: '2026-03-15')` for a write-off.
121
121
 
122
- **Scrap / write-off (no proceeds, asset destroyed/donated):**
122
+ Finalize when the practitioner confirms (`update_journal(resourceId: <journal id>, saveAsDraft: false)`), read the credit line's id with `get_journal`, then:
123
+
123
124
  ```
124
- POST /api/v1/discard-fixed-assets/<FA UUID>
125
- {
126
- disposalDate: '2026-03-15',
127
- notes: 'Scrapped; capsule <capsuleResourceId>'
128
- }
125
+ mark_fixed_asset_sold(
126
+ resourceId: <FA UUID>,
127
+ depreciationEndDate: '2026-03-15',
128
+ assetDisposalGainLossAccountResourceId: <Gain / Loss on Disposal>,
129
+ saleBusinessTransactionType: 'JOURNAL_MANUAL',
130
+ saleItemResourceId: <the journal's credit line id>
131
+ )
132
+ ```
133
+
134
+ `saleBusinessTransactionType` is the kind of document that recorded the sale (`JOURNAL_MANUAL` here; `SALE` for an invoice line; `PURCHASE` for a trade-in) and `saleItemResourceId` is the LINE on that document, not the document's id. A sale entry can be linked to one asset sale only.
135
+
136
+ *Scrap / write-off (no proceeds).* No journal at all:
137
+
138
+ ```
139
+ discard_fixed_asset(resourceId: <FA UUID>, disposalDate: '2026-03-15', depreciationEndDate: '2026-03-15')
140
+ ```
141
+
142
+ Either tool records the final depreciation up to `depreciationEndDate` and the disposal entry (cost and accumulated depreciation cleared, gain or loss to the account you passed).
143
+
144
+ CLI equivalents:
145
+ ```bash
146
+ clio fixed-assets sell --id <FA UUID> --depreciation-end-date 2026-03-15 --gain-loss-account <account id> --sale-type JOURNAL_MANUAL --sale-item <journal credit line id> --json
147
+ clio fixed-assets discard <FA UUID> --disposal-date 2026-03-15 --depreciation-end-date 2026-03-15 --json
148
+ ```
149
+
150
+ After it: `get_fixed_asset(resourceId: <id>)` should return `status: 'DISPOSED'`. A disposal made in error is reversed with `undo_fixed_asset_disposal`.
151
+
152
+ **B. The asset was never in the register** (for example one depreciated by declining-balance journals, see `declining-balance.md`). There is no register to update, so the calculator's journal is the whole disposal:
153
+
129
154
  ```
130
- OR `discard_fixed_asset(resourceId: <id>, disposalDate: '2026-03-15')` for a write-off.
155
+ create_journal(
156
+ valueDate: '2026-03-15',
157
+ reference: 'DISP-TRUCK-001',
158
+ journalEntries: [
159
+ { accountResourceId: <bank account>, type: 'DEBIT', amount: 18000, description: 'Proceeds, Truck-001' },
160
+ { accountResourceId: <Accumulated Depreciation (Vehicles)>, type: 'DEBIT', amount: 29250, description: 'Clear accumulated depreciation' },
161
+ { accountResourceId: <Loss on Disposal>, type: 'DEBIT', amount: 2750, description: 'Loss on disposal' },
162
+ { accountResourceId: <Vehicles>, type: 'CREDIT', amount: 50000, description: 'Clear cost' }
163
+ ],
164
+ saveAsDraft: true,
165
+ capsuleResourceId: <capsule id>
166
+ )
167
+ ```
168
+
169
+ It is a DRAFT; finalize when the practitioner confirms.
131
170
 
132
- **Trade-in (proceeds + replacement asset):**
133
- 1. First: invoke this recipe with `proceeds: <fair value of trade-in credit>` for the disposal of the old asset.
134
- 2. Then: `create_fixed_asset(...)` for the new asset with cost = cash paid + trade-in credit (the trade-in is part-payment).
171
+ ### Step 5: Trade-in (proceeds + replacement asset)
135
172
 
136
- After the FA-register update: `get_fixed_asset(resourceId: <id>)` should return `status: 'DISPOSED'` (or `'DISCARDED'`).
173
+ 1. Dispose of the old asset as in step 4, with `proceeds: <fair value of the trade-in credit>`.
174
+ 2. `create_fixed_asset(...)` for the new asset with cost = cash paid + trade-in credit (the trade-in is part-payment).
137
175
 
138
176
  ### Step 6: Verify
139
177
 
@@ -142,15 +180,19 @@ generate_trial_balance(endDate: '2026-03-15')
142
180
  ```
143
181
 
144
182
  Assert:
145
- - `balance['Vehicles']` reduced by 50,000 (cost cleared).
146
- - `balance['Accumulated Depreciation (Vehicles)']` reduced by 28,500 (contra-asset cleared).
183
+ - `balance['Vehicles']` reduced by 50,000 (cost cleared), once.
184
+ - `balance['Accumulated Depreciation (Vehicles)']` reduced by 29,250 (contra-asset cleared), once.
147
185
  - `balance['Cash / Bank Account']` increased by 18,000.
148
- - `balance['Loss on Disposal'] (period MTD) == 3,500` (or Gain on Disposal of `gainOrLoss` if positive).
186
+ - `balance['Loss on Disposal'] (period MTD) == 2,750` (or Gain on Disposal of `gainOrLoss` if positive).
187
+
188
+ **Compare with the calculator.** For a registered asset the gain or loss the register booked should match the calculator's `gainOrLoss`; a difference of one month's depreciation is the month convention (the calculator counts a part month as a full month), anything larger needs explaining before the period closes.
189
+
190
+ **Check for a double count.** If the cost, accumulated depreciation or gain/loss moved by twice the expected amount, a full manual disposal journal was posted for an asset the register also disposed of: pull `generate_general_ledger(accountResourceIds: [<Vehicles>], startDate: <disposalDate>, endDate: <disposalDate>)`, show the practitioner both entries, and remove the manual one.
149
191
 
150
192
  ```
151
193
  get_fixed_asset(resourceId: <FA UUID>)
152
194
  ```
153
- Should now show `status: 'DISPOSED'`, NBV = 0, no further depreciation auto-posting.
195
+ Should now show `status: 'DISPOSED'`, and no further depreciation auto-posting.
154
196
 
155
197
  ```
156
198
  generate_fa_recon_summary(primarySnapshotStartDate: <FY-start>, primarySnapshotEndDate: <FY-end>)
@@ -159,28 +201,28 @@ Should reflect the disposal in the year's movement: `openingNbv − depreciation
159
201
 
160
202
  ---
161
203
 
162
- ## Common error classes and recovery
204
+ ## Common problems and recovery
163
205
 
164
- | Source | Error | Recovery |
206
+ | Where | Problem | Recovery |
165
207
  |--------|-------|----------|
166
- | `plan_recipe` | 422 `disposal_after_period_end` | `disposalDate` later than the engagement's period end. Disposal belongs to next period; halt and confirm with practitioner. |
167
- | `plan_recipe` | 422 `acquisition_after_disposal` | Inputs swapped. Verify and re-run. |
168
- | `execute_recipe` | 422 `account_not_found` for `Gain on Disposal` / `Loss on Disposal` | Step 3 incomplete. Create via `create_account(accountType: 'Other Revenue' / 'Other Expense', ...)`. |
169
- | Step 5 manual update missed | (process error: Jaz FA continues auto-depreciating) | Surface to practitioner: "Asset `<name>` (resourceId `<id>`) is still ACTIVE in FA register but disposal journal posted. Auto-depreciation will continue. Run `mark_fixed_asset_sold` (sale) or `discard_fixed_asset` (write-off) immediately." |
170
- | `mark_fixed_asset_sold` / `discard_fixed_asset` | 422 `pending_depreciation_journals` | DRAFT depreciation journals exist for periods after disposal date. Identify them (journals cannot be filtered by capsule: check each candidate with `get_journal` (its `capsule.resourceId`) and confirm the set with the practitioner), then `delete_journal` per confirmed journal. |
171
- | Cross-check | Calculator NBV ≠ FA register NBV | Investigate missing depreciation journals (recipe pre-emitted DRAFTs that weren't finalized monthly). Finalize all up to disposal date BEFORE running this recipe. |
172
- | Recipe NBV ≠ TB Vehicles − TB Accum Dep | (audit failure) | Likely a manual journal touched Vehicles or Accum Dep without going through the recipe. Audit `generate_general_ledger(accountResourceIds: [<Vehicles>], startDate: <acquisition date>, endDate: <today>)`. |
208
+ | Calculator | "Disposal date must be after acquisition date." | Inputs swapped. Verify and re-run. |
209
+ | Calculator | Disposal date later than the period being closed | The disposal belongs to the next period; halt and confirm with the practitioner. |
210
+ | Step 2 | `Gain on Disposal` / `Loss on Disposal` is missing | Create via `create_account(accountType: 'Other Revenue' / 'Other Expense', ...)`. |
211
+ | Step 5 register update missed | (process error: Jaz FA continues auto-depreciating) | Surface to practitioner: "Asset `<name>` (resourceId `<id>`) is still ACTIVE in FA register but disposal journal posted. Auto-depreciation will continue. Run `mark_fixed_asset_sold` (sale) or `discard_fixed_asset` (write-off) immediately." |
212
+ | `mark_fixed_asset_sold` / `discard_fixed_asset` | Refused because depreciation is pending after the disposal date | DRAFT depreciation journals exist for periods after the disposal date. Identify them by their references (journals cannot be filtered by capsule) and confirm the set with the practitioner, then `delete_journal` per confirmed journal. |
213
+ | Cross-check | Calculator NBV ≠ FA register NBV | Investigate missing depreciation (period journals posted as DRAFT that were never finalized). Finalize all up to the disposal date BEFORE this recipe. |
214
+ | Calculator NBV ≠ TB Vehicles − TB Accum Dep | (audit failure) | Likely a manual journal touched Vehicles or Accum Dep outside the depreciation schedule. Audit `generate_general_ledger(accountResourceIds: [<Vehicles>], startDate: <acquisition date>, endDate: <today>)`. |
173
215
 
174
216
  ---
175
217
 
176
218
  ## Variations
177
219
 
178
- - **Scrap (no proceeds)**: `proceeds: 0`. Resulting `gainOrLoss = -netBookValue` (full NBV is loss). Step 5 uses `discard-fixed-assets` not `mark-as-sold`.
220
+ - **Scrap (no proceeds)**: `proceeds: 0`. Resulting `gainOrLoss = -netBookValue` (full NBV is loss). Step 5 uses `discard_fixed_asset`, not `mark_fixed_asset_sold`.
179
221
  - **Donation**: `proceeds: 0`, but classify the loss as `Charitable Donation` (not `Loss on Disposal`) per practitioner judgment. May have tax implications (deductible donation); flag to practitioner for SG IRAS / PH BIR treatment.
180
222
  - **Insurance write-off after damage**: `proceeds: <insurance payout>`. The payout is taxable income; the loss may be deductible. Document the insurance reference in the journal narrative.
181
- - **Trade-in**: 2 recipe invocations (this one for old asset, plus `create_fixed_asset` for new) plus a manual reconciliation of the trade-in credit.
182
- - **Partial disposal** (e.g., dismantling part of a building): NOT supported by this recipe. Manual journal: pro-rate cost + accum dep based on the disposed portion.
183
- - **Disposal of a fully depreciated asset (NBV = 0)**: `proceeds > 0` → all gain. `proceeds == 0` → no entries needed in P&L; just clear the contra-asset against the cost (Dr Accum Dep / Cr Vehicles, both at full cost). Recipe still works; gain/loss = proceeds.
223
+ - **Trade-in**: this recipe for the old asset, plus `create_fixed_asset` for the new one, plus a manual reconciliation of the trade-in credit.
224
+ - **Partial disposal** (e.g., dismantling part of a building): NOT supported by the calculator. Manual journal: pro-rate cost + accum dep based on the disposed portion.
225
+ - **Disposal of a fully depreciated asset (NBV = 0)**: `proceeds > 0` → all gain. `proceeds == 0` → no entries needed in P&L; just clear the contra-asset against the cost (Dr Accum Dep / Cr Vehicles, both at full cost). The calculator still works; gain/loss = proceeds.
184
226
  - **Foreign-currency proceeds**: pass `currency: 'USD'`. Per `jaz-api/SKILL.md` rule 25, journal records via `currency: { sourceCurrency: 'USD' }`. FX gain/loss between disposal-date rate and book rate is auto-handled by Jaz on the cash side.
185
227
 
186
228
  ---
@@ -190,5 +232,5 @@ Should reflect the disposal in the year's movement: `openingNbv − depreciation
190
232
  - Year-end close: FA disposals discovered during the year-end review trigger this recipe per asset.
191
233
  - Month-end close: ad-hoc invocation when a disposal happens mid-period.
192
234
  - `audit-prep.md` step 8: supporting schedule via `search_capsules(filter: {status: {eq: 'ACTIVE'}})` then narrow by date on the rows (**do not filter capsules by date**: `startDate`/`endDate` are declared on `CapsuleFilter` and pass validation, but every form of them answers `500 Internal Server Error` (measured 2026-09-07: `{gte}`, `{between}`, and `{gte}`+`{lte}` all 500)) plus per-capsule recompute via `clio calc asset-disposal`. Auditor tests proceeds against bank statements, NBV against FA register.
193
- - `fa-review.md` job: annual FA register review identifies candidates for disposal (assets fully depreciated, assets no longer in use, assets damaged) → invoke this recipe per identified disposal.
194
- - Sibling recipe `declining-balance.md`: depreciation up to the disposal date; complete all DRAFT depreciation finalizations BEFORE running this recipe to ensure NBV is correct.
235
+ - `fa-review.md` job: annual FA register review identifies candidates for disposal (assets fully depreciated, assets no longer in use, assets damaged) → follow this recipe per identified disposal.
236
+ - Sibling recipe `declining-balance.md`: depreciation up to the disposal date; finalize all DRAFT depreciation journals BEFORE this recipe so NBV is correct.