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.
- package/README.md +8 -8
- package/assets/skills/api/SKILL.md +3 -36
- package/assets/skills/cli/SKILL.md +18 -28
- package/assets/skills/cli/references/agent-gotchas.md +2 -2
- package/assets/skills/cli/references/command-catalog.md +15 -31
- package/assets/skills/cli/references/common-workflows.md +19 -28
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/jaz-kit/SKILL.md +1 -1
- package/assets/skills/jaz-kit/references/flows.md +2 -2
- package/assets/skills/jaz-pseudo-sql/SKILL.md +1 -1
- package/assets/skills/jobs/SKILL.md +42 -42
- package/assets/skills/jobs/references/audit-prep.md +8 -8
- package/assets/skills/jobs/references/bank-match.md +1 -1
- package/assets/skills/jobs/references/bank-recon.md +6 -6
- package/assets/skills/jobs/references/building-blocks.md +49 -30
- package/assets/skills/jobs/references/credit-control.md +13 -12
- package/assets/skills/jobs/references/document-collection.md +6 -6
- package/assets/skills/jobs/references/fa-review.md +28 -29
- package/assets/skills/jobs/references/gst-vat-filing.md +3 -3
- package/assets/skills/jobs/references/month-end-close.md +37 -33
- package/assets/skills/jobs/references/payment-run.md +3 -4
- package/assets/skills/jobs/references/quarter-end-close.md +20 -18
- package/assets/skills/jobs/references/supplier-recon.md +5 -5
- package/assets/skills/jobs/references/year-end-close.md +31 -21
- package/assets/skills/transaction-recipes/SKILL.md +54 -117
- package/assets/skills/transaction-recipes/references/accrued-expenses.md +86 -61
- package/assets/skills/transaction-recipes/references/asset-disposal.md +135 -93
- package/assets/skills/transaction-recipes/references/bad-debt-provision.md +87 -65
- package/assets/skills/transaction-recipes/references/bank-loan.md +104 -70
- package/assets/skills/transaction-recipes/references/building-blocks.md +168 -72
- package/assets/skills/transaction-recipes/references/capital-wip.md +9 -8
- package/assets/skills/transaction-recipes/references/declining-balance.md +91 -64
- package/assets/skills/transaction-recipes/references/deferred-revenue.md +105 -68
- package/assets/skills/transaction-recipes/references/dividend.md +100 -64
- package/assets/skills/transaction-recipes/references/employee-accruals.md +111 -73
- package/assets/skills/transaction-recipes/references/fixed-deposit.md +116 -63
- package/assets/skills/transaction-recipes/references/fx-revaluation.md +28 -19
- package/assets/skills/transaction-recipes/references/hire-purchase.md +45 -44
- package/assets/skills/transaction-recipes/references/ifrs16-lease.md +108 -73
- package/assets/skills/transaction-recipes/references/intercompany.md +8 -6
- package/assets/skills/transaction-recipes/references/prepaid-amortization.md +104 -65
- package/assets/skills/transaction-recipes/references/provisions.md +116 -64
- package/assets/templates/platform-rules/jaz-agent-rules.md +10 -9
- package/cli.mjs +529 -607
- package/package.json +1 -1
|
@@ -1,26 +1,27 @@
|
|
|
1
|
-
# Recipe: Bad Debt / ECL Provision (
|
|
1
|
+
# Recipe: Bad Debt / ECL Provision (calculator type: `ecl`)
|
|
2
2
|
|
|
3
|
-
> One-shot recipe for IFRS 9 simplified-approach ECL on trade receivables.
|
|
3
|
+
> One-shot recipe for IFRS 9 simplified-approach ECL on trade receivables. One journal: the top-up (or release) of the existing Allowance for Doubtful Debts to match the per-bucket × loss-rate calculation. Run quarterly or annually (most SMBs); not monthly. You calculate, create the capsule and post the journal yourself (see `building-blocks.md` § The three-step flow).
|
|
4
4
|
|
|
5
|
-
## Tools
|
|
5
|
+
## Tools and calculator this recipe uses
|
|
6
6
|
|
|
7
|
-
###
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
### Calculator (offline, posts nothing)
|
|
8
|
+
- **MCP: `calculate(type: 'ecl', buckets, existingProvision, startDate, currency)`** (step 1). `buckets` is a list of `{ name, balance, rate }` with `rate` in percent; `startDate` is the provision date and dates the journal step.
|
|
9
|
+
- **CLI: `clio calc ecl --current <c> --30d <30> --60d <60> --90d <90> --120d <120> --rates <r1>,<r2>,<r3>,<r4>,<r5> --existing-provision <ep> --currency <code> --json`** (step 1): the same calculation over five fixed buckets. The CLI has no date flag, so its journal step is undated.
|
|
10
10
|
|
|
11
|
-
###
|
|
12
|
-
- **`
|
|
11
|
+
### Posting tools
|
|
12
|
+
- **`list_capsule_types` / `create_capsule_type(displayName: 'ECL Provision')` / `create_capsule(...)`** (step 3).
|
|
13
|
+
- **`create_journal(...)`** (step 4): the single ECL adjustment. ONE-SHOT: no schedule, no future-dated entries.
|
|
13
14
|
|
|
14
|
-
###
|
|
15
|
-
- **`generate_aged_ar(endDate: <date>)
|
|
16
|
-
- **`search_accounts(filter: {name: {in: ['Allowance for Doubtful Debts', 'Bad Debt Expense']}})`**: step
|
|
17
|
-
- **`generate_trial_balance(endDate: <date>)
|
|
18
|
-
- **`search_capsules(filter: {title: {eq: <capsule
|
|
19
|
-
- **`apply_credits_to_invoice(...)` / `create_customer_credit_note(...)
|
|
15
|
+
### Lookup and verification tools
|
|
16
|
+
- **`generate_aged_ar(endDate: <date>)`** (step 1 input): AR aged into the buckets the calculator expects.
|
|
17
|
+
- **`search_accounts(filter: {name: {in: ['Allowance for Doubtful Debts', 'Bad Debt Expense']}})`**: step 2.
|
|
18
|
+
- **`generate_trial_balance(endDate: <date>)`** (step 1 input): pull `existingProvision` from the current `Allowance for Doubtful Debts` balance; step 5 verify the post-journal balance matches the calculated ECL.
|
|
19
|
+
- **`search_capsules(filter: {title: {eq: <capsule title>}})`**: step 0 idempotency check (one ECL capsule per period; quarterly = 4 per FY).
|
|
20
|
+
- **`apply_credits_to_invoice(...)` / `create_customer_credit_note(...)`** (step 6, specific write-off pattern): when individual invoices are deemed unrecoverable, write them off via credit note OR direct payment with `paymentMethod: 'DEBT_WRITE_OFF'`.
|
|
20
21
|
|
|
21
22
|
### Cross-references
|
|
22
23
|
- Operational context: invoked during year-end close (Y4 in `year-end-close.md`) for FY-end ECL; during the GST/VAT filing cycle if quarterly cadence is set; rarely during month-end close (mental check during variance review only).
|
|
23
|
-
- Sibling: `provisions.md` (
|
|
24
|
+
- Sibling: `provisions.md` (calculator `provision`): IAS 37 provisions with PV unwinding pattern, more complex than this recipe.
|
|
24
25
|
- IFRS / accounting context: IFRS 9.5.5.15 (simplified approach mandatory for trade receivables); IFRS 9.B5.5.35 (provision matrix). For specific large customers in stage-3 (objective evidence of impairment): supplement this recipe with specific impairment via `create_customer_credit_note` per customer.
|
|
25
26
|
|
|
26
27
|
---
|
|
@@ -35,18 +36,18 @@ search_capsules(filter: {title: {eq: 'FY2025 Year-End ECL True-Up'}})
|
|
|
35
36
|
|
|
36
37
|
If a result returns: halt. ECL is one-shot per period; duplicate would double-recognize.
|
|
37
38
|
|
|
38
|
-
### Step 1: Pull AR aging + existing provision
|
|
39
|
+
### Step 1: Pull AR aging + existing provision, then calculate
|
|
39
40
|
|
|
40
41
|
```
|
|
41
42
|
generate_aged_ar(endDate: '2025-12-31')
|
|
42
43
|
```
|
|
43
44
|
|
|
44
45
|
Returns aging buckets. Map to calculator inputs:
|
|
45
|
-
- `current` (
|
|
46
|
-
- `30d` (
|
|
47
|
-
- `60d` (
|
|
48
|
-
- `90d` (
|
|
49
|
-
- `120d
|
|
46
|
+
- `current` (not yet overdue)
|
|
47
|
+
- `30d` (1-30 days overdue)
|
|
48
|
+
- `60d` (31-60 days overdue)
|
|
49
|
+
- `90d` (61-90 days overdue)
|
|
50
|
+
- `120d` (91+ days overdue)
|
|
50
51
|
|
|
51
52
|
```
|
|
52
53
|
generate_trial_balance(endDate: '2025-12-31')
|
|
@@ -54,6 +55,22 @@ generate_trial_balance(endDate: '2025-12-31')
|
|
|
54
55
|
|
|
55
56
|
Pull `balance['Allowance for Doubtful Debts']` (sign-flipped; it's a contra-asset, naturally credit balance). This is `existingProvision`.
|
|
56
57
|
|
|
58
|
+
```
|
|
59
|
+
calculate(
|
|
60
|
+
type: 'ecl',
|
|
61
|
+
buckets: [
|
|
62
|
+
{name: 'Current', balance: 100000, rate: 0.5},
|
|
63
|
+
{name: '1-30 days', balance: 50000, rate: 2},
|
|
64
|
+
{name: '31-60 days', balance: 20000, rate: 5},
|
|
65
|
+
{name: '61-90 days', balance: 10000, rate: 10},
|
|
66
|
+
{name: '91+ days', balance: 5000, rate: 50}
|
|
67
|
+
],
|
|
68
|
+
existingProvision: 5000,
|
|
69
|
+
currency: 'SGD',
|
|
70
|
+
startDate: '2025-12-31' // provision date: the aged AR report date. The ECL journal step is dated on it.
|
|
71
|
+
)
|
|
72
|
+
```
|
|
73
|
+
|
|
57
74
|
```
|
|
58
75
|
clio calc ecl \
|
|
59
76
|
--current 100000 \
|
|
@@ -67,52 +84,58 @@ clio calc ecl \
|
|
|
67
84
|
--json
|
|
68
85
|
```
|
|
69
86
|
|
|
70
|
-
Returns
|
|
87
|
+
Returns `{ totalReceivables: 185000, totalEcl: 6000, weightedRate: 3.2432, adjustmentRequired: 1000, isIncrease: true, bucketDetails: [{ bucket: 'Current', balance: 100000, lossRate: 0.5, ecl: 500 }, ...], journal, blueprint }`. The bucket ECLs are 500 + 1,000 + 1,000 + 1,000 + 2,500 = 6,000; against the existing 5,000 a top-up of $1,000 is needed.
|
|
71
88
|
|
|
72
|
-
|
|
89
|
+
Rates are PERCENT: `0.5,2,5,10,50` means 0.5%, 2%, 5%, 10%, 50% (NOT 50%, 200%, 500%...). Tune them to the entity's historical loss rate. Common starting point for SMBs: `0.5,2,5,10,50` for the 5 buckets. Auditor will sample-test the rates against actual historical losses; keep documentation of how rates were derived.
|
|
73
90
|
|
|
74
|
-
|
|
91
|
+
`blueprint.steps` (single `journal`): Dr Bad Debt Expense 1,000 / Cr Allowance for Doubtful Debts 1,000.
|
|
75
92
|
|
|
76
|
-
|
|
93
|
+
If `adjustmentRequired` is negative (calculated ECL < existing provision; `isIncrease: false`): the journal direction reverses (Dr Allowance for Doubtful Debts / Cr Bad Debt Expense for the release).
|
|
77
94
|
|
|
78
|
-
|
|
79
|
-
plan_recipe(
|
|
80
|
-
recipe: 'ecl',
|
|
81
|
-
buckets: [
|
|
82
|
-
{name: 'Current', balance: 100000, rate: 0.5},
|
|
83
|
-
{name: '1-30 days', balance: 50000, rate: 2},
|
|
84
|
-
{name: '31-60 days', balance: 20000, rate: 5},
|
|
85
|
-
{name: '61-90 days', balance: 10000, rate: 10},
|
|
86
|
-
{name: '91+ days', balance: 5000, rate: 50}
|
|
87
|
-
],
|
|
88
|
-
existingProvision: 5000,
|
|
89
|
-
currency: 'SGD',
|
|
90
|
-
startDate: '2025-12-31' // provision date: the aged AR report date. The ECL journal is dated on it.
|
|
91
|
-
)
|
|
92
|
-
```
|
|
95
|
+
If `adjustmentRequired` is 0 there is nothing to post: `blueprint.steps` is empty. Stop here and record that the provision was reviewed and unchanged.
|
|
93
96
|
|
|
94
|
-
|
|
97
|
+
If the adjustment is below the entity's materiality threshold: skip the posting entirely; document the decision in your working notes ("ECL change immaterial: $X below threshold $Y").
|
|
95
98
|
|
|
96
|
-
|
|
99
|
+
### Step 2: Resolve accounts
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
For each account in `requiredAccounts`:
|
|
101
|
-
- `search_accounts(filter: {name: {eq: <accountName>}})`. Suggested classifications: `Allowance for Doubtful Debts` → `Current Asset` (contra-AR; sometimes set up as separate account, sometimes as a sub-account of `Accounts Receivable`); `Bad Debt Expense` → `Operating Expense`.
|
|
101
|
+
The blueprint's `Bad Debt Expense` and `Allowance for Doubtful Debts` are labels. Map each to the real account:
|
|
102
|
+
- `search_accounts(filter: {name: {in: ['Allowance for Doubtful Debts', 'Bad Debt Expense']}})`. Suggested classifications: `Allowance for Doubtful Debts` → `Current Asset` (contra-AR; sometimes set up as separate account, sometimes as a sub-account of `Accounts Receivable`); `Bad Debt Expense` → `Operating Expense`.
|
|
102
103
|
|
|
103
104
|
If `Allowance for Doubtful Debts` doesn't exist in the CoA: `create_account(name: 'Allowance for Doubtful Debts', code: <unused account code>, accountType: 'Current Asset')` first. Common gap in CoAs that haven't run formal ECL.
|
|
104
105
|
|
|
105
|
-
|
|
106
|
+
No contact and no bank account are involved.
|
|
107
|
+
|
|
108
|
+
### Step 3: Create the capsule
|
|
106
109
|
|
|
107
110
|
```
|
|
108
|
-
|
|
111
|
+
list_capsule_types()
|
|
112
|
+
create_capsule(
|
|
113
|
+
capsuleTypeResourceId: <id of 'ECL Provision'>,
|
|
114
|
+
title: 'FY2025 Year-End ECL True-Up',
|
|
115
|
+
description: <blueprint.capsuleDescription>
|
|
116
|
+
)
|
|
109
117
|
```
|
|
110
118
|
|
|
111
|
-
`
|
|
119
|
+
If `ECL Provision` is not in the list: `create_capsule_type(displayName: 'ECL Provision')` first.
|
|
120
|
+
|
|
121
|
+
### Step 4: Post the journal
|
|
112
122
|
|
|
113
|
-
|
|
123
|
+
Date it on the aged AR report date:
|
|
124
|
+
|
|
125
|
+
```
|
|
126
|
+
create_journal(
|
|
127
|
+
valueDate: '2025-12-31',
|
|
128
|
+
reference: 'ECL-FY2025',
|
|
129
|
+
journalEntries: [
|
|
130
|
+
{ accountResourceId: <Bad Debt Expense>, type: 'DEBIT', amount: 1000, description: 'ECL provision increase, IFRS 9 simplified approach' },
|
|
131
|
+
{ accountResourceId: <Allowance for Doubtful Debts>, type: 'CREDIT', amount: 1000, description: 'ECL provision increase, IFRS 9 simplified approach' }
|
|
132
|
+
],
|
|
133
|
+
saveAsDraft: true,
|
|
134
|
+
capsuleResourceId: <capsule id>
|
|
135
|
+
)
|
|
136
|
+
```
|
|
114
137
|
|
|
115
|
-
|
|
138
|
+
The journal is a DRAFT; finalize via `update_journal(resourceId: <id>, saveAsDraft: false)` once the practitioner confirms the inputs.
|
|
116
139
|
|
|
117
140
|
### Step 5: Verify
|
|
118
141
|
|
|
@@ -121,8 +144,8 @@ generate_trial_balance(endDate: '2025-12-31')
|
|
|
121
144
|
```
|
|
122
145
|
|
|
123
146
|
Assert:
|
|
124
|
-
- `balance['Allowance for Doubtful Debts'] == -
|
|
125
|
-
- `balance['Bad Debt Expense'] (period MTD) increased by
|
|
147
|
+
- `balance['Allowance for Doubtful Debts'] == -totalEcl` (within 1 cent). Updated to match the new computed ECL.
|
|
148
|
+
- `balance['Bad Debt Expense'] (period MTD) increased by adjustmentRequired` (or reduced if it is a release).
|
|
126
149
|
- `(balance['Accounts Receivable'] - |balance['Allowance for Doubtful Debts']|)` is the net receivables presented on the balance sheet (BS line: `Trade Receivables, net`).
|
|
127
150
|
|
|
128
151
|
### Step 6: Specific impairment (stage 3, separate from this recipe)
|
|
@@ -159,30 +182,29 @@ pay_invoice(
|
|
|
159
182
|
)
|
|
160
183
|
```
|
|
161
184
|
|
|
162
|
-
|
|
185
|
+
Use the appropriate jurisdiction-specific account if your CoA distinguishes write-offs from generic bad debt expense.
|
|
163
186
|
|
|
164
187
|
Write-offs reduce both the gross AR balance AND offset against the existing Allowance (since the customer is now provisioned for). Re-run step 1 `generate_aged_ar` afterwards; the written-off customer should no longer appear, and the corresponding portion of the Allowance should reduce.
|
|
165
188
|
|
|
166
189
|
---
|
|
167
190
|
|
|
168
|
-
## Common
|
|
191
|
+
## Common problems and recovery
|
|
169
192
|
|
|
170
|
-
|
|
|
193
|
+
| Where | Problem | Recovery |
|
|
171
194
|
|--------|-------|----------|
|
|
172
|
-
|
|
|
173
|
-
|
|
|
174
|
-
|
|
|
175
|
-
|
|
|
176
|
-
|
|
|
177
|
-
| Specific write-off changes ECL inputs | (process) | After Path A or Path B write-off, re-run step 1 `generate_aged_ar` and re-execute the ECL recipe with updated buckets; the calculated ECL likely reduces because the worst customer is now off the books. |
|
|
195
|
+
| Calculator | "... balance must be zero or positive" / "... loss rate must be zero or positive" | A bucket balance or rate is negative. A credit balance in an aging bucket is an unapplied receipt or credit note: resolve it in AR first, do not net it into the ECL. |
|
|
196
|
+
| Calculator | ECL far larger than expected | Rates were passed as percentages of 100 by mistake, or as decimals where percent is expected. `2` is 2%; `0.02` is 0.02%. |
|
|
197
|
+
| Step 2 | `Allowance for Doubtful Debts` is missing | Most-commonly-missing account. Create via `create_account(name: 'Allowance for Doubtful Debts', code: <unused account code>, accountType: 'Current Asset')`. |
|
|
198
|
+
| Verification | TB Allowance ≠ calculated ECL after journal posts | Investigate: likely an interim period posted its own ECL adjustment after `existingProvision` was read (cumulative). Audit via `generate_general_ledger(accountResourceIds: [<Allowance>], startDate: <FY-start>, endDate: <today>)`. |
|
|
199
|
+
| Specific write-off changes ECL inputs | (process) | After Path A or Path B write-off, re-run step 1 `generate_aged_ar` and recalculate with updated buckets; the calculated ECL likely reduces because the worst customer is now off the books. |
|
|
178
200
|
|
|
179
201
|
---
|
|
180
202
|
|
|
181
203
|
## Variations
|
|
182
204
|
|
|
183
|
-
- **Quarterly cadence**: same recipe, run quarterly with
|
|
205
|
+
- **Quarterly cadence**: same recipe, run quarterly with the journal dated `<quarter-end>`. The review cadence (`monthly` | `quarterly` | `annual`) depends on the entity's policy. Most SMBs run annual (FY-end only) or quarterly.
|
|
184
206
|
- **Specific high-risk customer with stage-3 impairment**: combine simplified-approach ECL recipe (collective) + Path A or B specific write-off (individual). Run the collective AFTER the specific write-offs so the buckets reflect post-write-off balances.
|
|
185
|
-
- **Multi-currency AR**: ECL is per-currency. Run the recipe per currency (each with its own `Allowance for Doubtful Debts (<currency>)` if you want segregation, or aggregate into one base-currency Allowance). Jaz auto-handles FX revaluation of the AR + Allowance balances per IAS 21.23 (do NOT
|
|
207
|
+
- **Multi-currency AR**: ECL is per-currency. Run the recipe per currency (each with its own `Allowance for Doubtful Debts (<currency>)` if you want segregation, or aggregate into one base-currency Allowance). Jaz auto-handles FX revaluation of the AR + Allowance balances per IAS 21.23 (do NOT post an `fx-reval` result).
|
|
186
208
|
- **Forward-looking macroeconomic adjustments** (IFRS 9 paragraphs B5.5.51-54): apply a multiplier to the `--rates` to reflect current/expected economic conditions. E.g., recession overlay: `--rates 1.0,3,7,15,60` instead of `0.5,2,5,10,50`. Document the rationale in your working notes.
|
|
187
209
|
- **POCI assets** (purchased or originated credit-impaired): NOT supported by this simplified-approach recipe. Use stage-3 specific impairment via Path A/B for each.
|
|
188
210
|
|
|
@@ -194,4 +216,4 @@ Write-offs reduce both the gross AR balance AND offset against the existing Allo
|
|
|
194
216
|
- GST/VAT filing cycle (where applicable): quarterly ECL review for entities on a quarterly cadence.
|
|
195
217
|
- Month-end close: mental ECL cross-check during variance analysis only; formal recipe runs annually/quarterly.
|
|
196
218
|
- `audit-prep.md` step 8: supporting schedule via the most recent `ECL Provision` capsule + the underlying `clio calc ecl` JSON. Auditor tests rate appropriateness against actual historical loss data.
|
|
197
|
-
- Sibling `provisions.md` (
|
|
219
|
+
- Sibling `provisions.md` (calculator `provision`): IAS 37 provisions with PV unwinding (more complex pattern).
|
|
@@ -1,27 +1,29 @@
|
|
|
1
|
-
# Recipe: Bank Loan (
|
|
1
|
+
# Recipe: Bank Loan (calculator type: `loan`)
|
|
2
2
|
|
|
3
|
-
> Canonical recipe for term loans with fixed monthly installments.
|
|
3
|
+
> Canonical recipe for term loans with fixed monthly installments. Never hand-build the amortization table: the calculator returns it, with the journal lines for every installment. You create the capsule and post the disbursement and each repayment yourself (see `building-blocks.md` § The three-step flow).
|
|
4
4
|
|
|
5
|
-
## Tools
|
|
5
|
+
## Tools and calculator this recipe uses
|
|
6
6
|
|
|
7
|
-
###
|
|
8
|
-
-
|
|
9
|
-
-
|
|
7
|
+
### Calculator (offline, posts nothing)
|
|
8
|
+
- **MCP: `calculate(type: 'loan', principal, annualRate, termMonths, startDate, currency)`** (step 1).
|
|
9
|
+
- **CLI: `clio calc loan --principal <p> --rate <annual %> --term <months> --start-date <YYYY-MM-DD> --currency <code> --json`** (step 1): the same result.
|
|
10
10
|
|
|
11
|
-
###
|
|
12
|
-
- **`
|
|
11
|
+
### Posting tools
|
|
12
|
+
- **`list_capsule_types` / `create_capsule_type(displayName: 'Loan Repayment')` / `create_capsule(...)`** (step 3).
|
|
13
|
+
- **`create_cash_in(...)`** (step 4): the disbursement.
|
|
14
|
+
- **`create_journal(...)`** (step 4): one per repayment. The interest portion changes every month, so a fixed-amount schedule does not fit.
|
|
13
15
|
|
|
14
|
-
###
|
|
15
|
-
- **`
|
|
16
|
-
- **`
|
|
17
|
-
- **`
|
|
18
|
-
- **`
|
|
19
|
-
- **`
|
|
16
|
+
### Lookup and verification tools
|
|
17
|
+
- **`search_capsules(filter: {title: {eq: <capsule title>}})`** (step 0): detect duplicate setup. Loan capsules are unique per facility; duplicate creation is almost always an agent error.
|
|
18
|
+
- **`list_bank_accounts()`** (step 2): resolve the disbursement bank account by `name + currency`.
|
|
19
|
+
- **`search_accounts(filter: {name: {in: ['Loan Payable', 'Interest Expense']}})`** (step 2): confirm liability + expense GL accounts exist.
|
|
20
|
+
- **`update_journal(resourceId: <id>, saveAsDraft: false)`** (step 5): lift a draft repayment journal to ACTIVE once the bank payment is confirmed.
|
|
21
|
+
- **`generate_trial_balance(endDate: <date>)`** (step 5): verify the loan liability balance matches the schedule's `closingBalance` column.
|
|
20
22
|
|
|
21
23
|
### Cross-references
|
|
22
|
-
- Operational context: invoked during data migration (when the prior system's loan transfers in via the conversion clearing account, then forward-recognition starts here) and during month-end close (monthly accruals do NOT include loan interest;
|
|
24
|
+
- Operational context: invoked during data migration (when the prior system's loan transfers in via the conversion clearing account, then forward-recognition starts here) and during month-end close (monthly accruals do NOT include loan interest; each repayment journal from this recipe carries it).
|
|
23
25
|
- Sibling recipes: `fixed-deposit.md` (mirror: placement instead of disbursement, interest income instead of expense), `hire-purchase.md` (loan + depreciation combo for asset purchases).
|
|
24
|
-
- IFRS / accounting context: IFRS 9 amortized cost measurement. The
|
|
26
|
+
- IFRS / accounting context: IFRS 9 amortized cost measurement. The calculator uses the effective interest method (constant rate × outstanding balance), not straight-line.
|
|
25
27
|
|
|
26
28
|
---
|
|
27
29
|
|
|
@@ -30,25 +32,16 @@
|
|
|
30
32
|
### Step 0: Idempotency check
|
|
31
33
|
|
|
32
34
|
```
|
|
33
|
-
search_capsules(filter: {title: {eq: 'Bank Loan
|
|
35
|
+
search_capsules(filter: {title: {eq: 'Bank Loan, ABC Bank, LN-2025-0042'}})
|
|
34
36
|
```
|
|
35
37
|
|
|
36
|
-
If a result returns: halt and surface "Loan capsule `<name>` already exists (resourceId `<id>`). Re-
|
|
38
|
+
If a result returns: halt and surface "Loan capsule `<name>` already exists (resourceId `<id>`). Re-posting would create a duplicate disbursement. Confirm the practitioner intent: if this is the same facility, continue posting its remaining repayments into the existing capsule."
|
|
37
39
|
|
|
38
|
-
### Step 1:
|
|
40
|
+
### Step 1: Calculate
|
|
39
41
|
|
|
40
42
|
```
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
Returns: `{ perPeriodAmount: 1933.28, totalInterest: 15996.80, schedule: [{period, openingBalance, interest, principal, closingBalance}, ...] }`. Save to `workpapers/<period>/loan-amortization.json` for the workpaper record.
|
|
45
|
-
|
|
46
|
-
### Step 2: Plan the recipe
|
|
47
|
-
|
|
48
|
-
```
|
|
49
|
-
plan_recipe(
|
|
50
|
-
// Accounts, capsule and counterparty are not plan_recipe params: execute_recipe resolves accounts from the CoA and takes bankAccountName / contactName.
|
|
51
|
-
recipe: 'loan',
|
|
43
|
+
calculate(
|
|
44
|
+
type: 'loan',
|
|
52
45
|
principal: 100000,
|
|
53
46
|
annualRate: 6,
|
|
54
47
|
termMonths: 60,
|
|
@@ -57,47 +50,89 @@ plan_recipe(
|
|
|
57
50
|
)
|
|
58
51
|
```
|
|
59
52
|
|
|
60
|
-
|
|
53
|
+
```
|
|
54
|
+
clio calc loan --principal 100000 --rate 6 --term 60 --start-date 2025-01-01 --currency SGD --json
|
|
55
|
+
```
|
|
61
56
|
|
|
62
|
-
|
|
57
|
+
Returns `{ monthlyPayment: 1933.28, totalPayments: 115996.84, totalInterest: 15996.84, totalPrincipal: 100000, schedule: [{ period, date, openingBalance, payment, interest, principal, closingBalance, journal }, ...60], blueprint }`. Save it to `workpapers/<period>/loan-amortization.json` for the workpaper record.
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
-
|
|
59
|
+
`blueprint.steps` (61):
|
|
60
|
+
- Step 1, `cash-in`, dated `2025-01-01`: Dr Cash / Bank Account 100,000 / Cr Loan Payable 100,000.
|
|
61
|
+
- Steps 2 to 61, `journal`, dated `2025-02-01` through `2030-01-01`: Dr Loan Payable (principal portion), Dr Interest Expense (interest portion), Cr Cash / Bank Account (the installment). Month 1 is 1,433.28 principal + 500.00 interest = 1,933.28. The final installment is 1,933.32: it absorbs the rounding so the balance closes at exactly 0.
|
|
66
62
|
|
|
67
|
-
|
|
68
|
-
- `list_bank_accounts()`, match by `name + currency`. If still no match: halt and surface it.
|
|
63
|
+
Without `startDate` the calculator returns the schedule only (`blueprint: null`). Set it to the drawdown date so the repayment dates match the bank's.
|
|
69
64
|
|
|
70
|
-
|
|
71
|
-
- `search_contacts(filter: {name: {eq: <vendor>}})`. If empty: halt and surface "Lender `<vendor>` not in Jaz contacts. Create as `supplier: true` via `create_contact` before retry."
|
|
65
|
+
### Step 2: Resolve accounts and the bank account
|
|
72
66
|
|
|
73
|
-
|
|
67
|
+
The blueprint's `Loan Payable`, `Interest Expense` and `Cash / Bank Account` are labels. Map each to the real account:
|
|
68
|
+
- `search_accounts(filter: {name: {in: ['Loan Payable', 'Interest Expense']}})`. If one is missing, halt: "Loan recipe needs GL account `<accountName>`, which is not in the CoA. Create via `create_account` (suggested classifications: `Loan Payable` → `Non-current Liability`; `Interest Expense` → `Operating Expense`) or remap the account."
|
|
69
|
+
- `list_bank_accounts()`, match by `name + currency`. If no match: halt and surface it.
|
|
70
|
+
|
|
71
|
+
For the lender contact (optional, for the cash-in's `contactResourceId`):
|
|
72
|
+
- `search_contacts(filter: {name: {eq: <lender>}})`. If empty and the practitioner wants the lender recorded: create as `supplier: true` via `create_contact`.
|
|
73
|
+
|
|
74
|
+
### Step 3: Create the capsule
|
|
74
75
|
|
|
75
76
|
```
|
|
76
|
-
|
|
77
|
+
list_capsule_types()
|
|
78
|
+
create_capsule(
|
|
79
|
+
capsuleTypeResourceId: <id of 'Loan Repayment'>,
|
|
80
|
+
title: 'Bank Loan, ABC Bank, LN-2025-0042',
|
|
81
|
+
description: <blueprint.capsuleDescription>
|
|
82
|
+
)
|
|
77
83
|
```
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
- Step 1: 1 cash-in for the loan disbursement (per `jaz-api/SKILL.md` rule 26: `accountResourceId` at top level for the bank account, `lines: [{accountResourceId: <Loan Payable>, amount: 100000}]` for the offset). Posted ACTIVE immediately, whatever `finalize` says: cash entries have no draft state.
|
|
81
|
-
- Steps 2..termMonths+1: **N future-dated DRAFT journals** (one per repayment period, dated end-of-month for each month from `<startDate>+1 month` through `<startDate>+termMonths`). Each is a 3-line entry: debit Loan Payable (principal portion per amortization schedule), debit Interest Expense (interest portion), credit Cash.
|
|
85
|
+
If `Loan Repayment` is not in the list: `create_capsule_type(displayName: 'Loan Repayment')` first.
|
|
82
86
|
|
|
83
|
-
|
|
87
|
+
### Step 4: Post the entries
|
|
84
88
|
|
|
85
|
-
|
|
89
|
+
**4a. Disbursement** (blueprint step 1). Per `jaz-api/SKILL.md` rule 26: `accountResourceId` at top level is the bank account; `lines` carries the offset.
|
|
86
90
|
|
|
87
|
-
|
|
91
|
+
```
|
|
92
|
+
create_cash_in(
|
|
93
|
+
valueDate: '2025-01-01',
|
|
94
|
+
accountResourceId: <bank account>,
|
|
95
|
+
reference: 'LN-2025-0042-DISB',
|
|
96
|
+
lines: [{ accountResourceId: <Loan Payable>, amount: 100000, description: 'Loan proceeds, ABC Bank LN-2025-0042' }],
|
|
97
|
+
capsuleResourceId: <capsule id>
|
|
98
|
+
)
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
It posts ACTIVE immediately: cash entries have no draft state. Post it once the funds have actually arrived. If the disbursement is already in the books (created from the bank feed), do not post it again: move it into the capsule with `move_transaction_capsules` if it sits in another capsule, or leave it and note its reference in the capsule description.
|
|
88
102
|
|
|
89
|
-
|
|
103
|
+
**4b. Repayments** (blueprint steps 2 to 61). One `create_journal` per step, using that step's amounts:
|
|
90
104
|
|
|
91
105
|
```
|
|
92
|
-
|
|
106
|
+
create_journal(
|
|
107
|
+
valueDate: '2025-02-01',
|
|
108
|
+
reference: 'LN-2025-0042-01',
|
|
109
|
+
journalEntries: [
|
|
110
|
+
{ accountResourceId: <Loan Payable>, type: 'DEBIT', amount: 1433.28, description: 'Loan payment, month 1 of 60' },
|
|
111
|
+
{ accountResourceId: <Interest Expense>, type: 'DEBIT', amount: 500.00, description: 'Loan payment, month 1 of 60' },
|
|
112
|
+
{ accountResourceId: <bank account>, type: 'CREDIT', amount: 1933.28, description: 'Loan payment, month 1 of 60' }
|
|
113
|
+
],
|
|
114
|
+
saveAsDraft: true,
|
|
115
|
+
capsuleResourceId: <capsule id>
|
|
116
|
+
)
|
|
93
117
|
```
|
|
94
118
|
|
|
95
|
-
|
|
119
|
+
Two workable cadences; agree one with the practitioner:
|
|
120
|
+
- **Post each month's journal at that month's close**, after the bank payment clears, from the saved schedule (`saveAsDraft: false`).
|
|
121
|
+
- **Post all 60 now as drafts** so the full amortization is visible in the capsule, then finalize one per month.
|
|
122
|
+
|
|
123
|
+
Either way the reference pattern (`LN-2025-0042-01` to `-60`) is what lets you find a specific month's journal later.
|
|
124
|
+
|
|
125
|
+
### Step 5: Monthly action (during monthly-close)
|
|
126
|
+
|
|
127
|
+
After the actual bank payment posts each month, find that month's journal by its reference and finalize it (or post it now if you are posting month by month):
|
|
96
128
|
|
|
97
129
|
```
|
|
130
|
+
search_journals(filter: {reference: {eq: 'LN-2025-0042-03'}})
|
|
98
131
|
update_journal(resourceId: <journal id>, saveAsDraft: false)
|
|
99
132
|
```
|
|
100
133
|
|
|
134
|
+
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`.
|
|
135
|
+
|
|
101
136
|
After disbursement (period 0):
|
|
102
137
|
- `generate_trial_balance(endDate: <startDate>)`.
|
|
103
138
|
- Assert: `balance['Cash / Bank Account']` increased by `principal`.
|
|
@@ -110,39 +145,38 @@ After each monthly finalize:
|
|
|
110
145
|
|
|
111
146
|
After the FINAL period (60th repayment) is finalized:
|
|
112
147
|
- Assert: `balance['Loan Payable'] == 0` exactly.
|
|
113
|
-
- Assert: `balance['Interest Expense'] (life-to-date)` matches `totalInterest` from the calculator
|
|
114
|
-
- Close the capsule via a manual `update_capsule(title: '<original> [CLOSED]')` (the API has no `status` field for capsules; closure is informational only) if the org tracks capsule status.
|
|
148
|
+
- Assert: `balance['Interest Expense'] (life-to-date)` matches `totalInterest` from the calculator (the final period carries the rounding adjustment).
|
|
149
|
+
- Close the capsule via a manual `update_capsule(resourceId: <id>, title: '<original> [CLOSED]')` (the API has no `status` field for capsules; closure is informational only) if the org tracks capsule status.
|
|
115
150
|
|
|
116
151
|
---
|
|
117
152
|
|
|
118
|
-
## Common
|
|
153
|
+
## Common problems and recovery
|
|
119
154
|
|
|
120
|
-
|
|
|
155
|
+
| Where | Problem | Recovery |
|
|
121
156
|
|--------|-------|----------|
|
|
122
|
-
|
|
|
123
|
-
|
|
|
124
|
-
| `
|
|
125
|
-
| `
|
|
126
|
-
|
|
|
127
|
-
|
|
|
128
|
-
|
|
|
129
|
-
| `update_capsule` | 422 `capsule_locked` | The capsule is in a closed period (lock date passed). Lift the lock first via `update_account` lock_date OR add the new entry in the next open period. |
|
|
157
|
+
| Calculator | "Principal must be a positive number" / "Term (months) must be a positive integer" | Fix the input. For a single-shot principal-plus-interest loan (one period), a schedule adds nothing: post the interest as an accrual and the principal repayment as a manual journal. |
|
|
158
|
+
| Step 2 | Bank account not found | Re-run `list_bank_accounts` and re-resolve the bank account resourceId. |
|
|
159
|
+
| `create_cash_in` | Loan currency ≠ bank account currency | Pick a bank account in the loan's currency, or record the FX on disbursement (per `jaz-api/SKILL.md` rule 24). |
|
|
160
|
+
| `create_capsule` | Returns the existing capsule instead of a new one | Duplicate setup. Step 0 should have caught this; go back to step 0. |
|
|
161
|
+
| Verification | Interest amount off by cents against the bank's statement | The calculator uses the effective interest method per period, rounded to 2dp. If the bank's own schedule differs by cents, post the bank's figures: the bank's statement is the record. |
|
|
162
|
+
| Monthly finalize | Journal falls in a locked period | The lock date has passed. Lift the lock first, or post the entry in the next open period, with the practitioner's agreement. |
|
|
163
|
+
| A step failed midway | Disbursement posted, some repayments not | Do not start again: `search_journals` by reference to see which months exist, then continue from the first missing one. |
|
|
130
164
|
|
|
131
165
|
---
|
|
132
166
|
|
|
133
167
|
## Variations
|
|
134
168
|
|
|
135
|
-
- **Variable-rate loan:**
|
|
136
|
-
- **Lump-sum principal repayment:** Post a manual journal (`create_journal`) Dr Loan Payable / Cr Cash. Then
|
|
137
|
-
- **Interest-only period:** Not supported by the loan calculator. Workaround: post N manual interest-only journals via `create_journal` (Dr Interest Expense / Cr Cash) for the interest-only window, then
|
|
138
|
-
- **Multi-currency loan (USD loan with SGD base):** Pass `currency: 'USD'`. Disbursement records via `currency: { sourceCurrency: 'USD' }` per `jaz-api/SKILL.md` rule 25. Monthly repayments stay in USD. Period-end FX revaluation against base currency is auto-handled by Jaz (Loan Payable is a monetary item per IAS 21.23; Jaz auto-translates at closing rate). Verify via the month-end close FX verification flow; do NOT
|
|
139
|
-
- **Loan origination fees:** Out of scope for
|
|
140
|
-
- **Year-end current/non-current reclassification:**
|
|
169
|
+
- **Variable-rate loan:** One calculation covers one fixed rate. When the rate changes, delete the remaining DRAFT repayment journals (each found by its reference, then `delete_journal(resourceId: <id>)`), recalculate from the current outstanding balance with the new rate (`principal: <outstanding>`, `termMonths: <remaining>`), and post the new repayment journals into the SAME capsule. Skip the new blueprint's `cash-in` step: the disbursement is already in the books.
|
|
170
|
+
- **Lump-sum principal repayment:** Post a manual journal (`create_journal`) Dr Loan Payable / Cr Cash. Then replace the remaining repayments as for a rate change, from the new outstanding balance.
|
|
171
|
+
- **Interest-only period:** Not supported by the loan calculator. Workaround: post N manual interest-only journals via `create_journal` (Dr Interest Expense / Cr Cash) for the interest-only window, then calculate from the start of the amortizing window with the full outstanding principal.
|
|
172
|
+
- **Multi-currency loan (USD loan with SGD base):** Pass `currency: 'USD'`. Disbursement records via `currency: { sourceCurrency: 'USD' }` per `jaz-api/SKILL.md` rule 25. Monthly repayments stay in USD. Period-end FX revaluation against base currency is auto-handled by Jaz (Loan Payable is a monetary item per IAS 21.23; Jaz auto-translates at closing rate). Verify via the month-end close FX verification flow; do NOT post an `fx-reval` result.
|
|
173
|
+
- **Loan origination fees:** Out of scope for the calculator (its effective-interest schedule does not amortize fees into the EIR). Post fees as a separate manual journal: Dr `Operating Expense > Loan Origination Fee` / Cr Cash. For IFRS 9 EIR-amortized fees, model the fee as `prepaid-expense` over the loan term.
|
|
174
|
+
- **Year-end current/non-current reclassification:** Not part of the schedule; manual annual journal: Dr Loan Payable Non-current / Cr Loan Payable Current for the next 12 months' principal portion. Job playbook `jobs/references/year-end-close.md` Y6 covers this.
|
|
141
175
|
|
|
142
176
|
---
|
|
143
177
|
|
|
144
178
|
## Cross-references
|
|
145
179
|
|
|
146
|
-
- Month-end close: explicitly excludes loan interest from monthly accruals because
|
|
147
|
-
- `jobs/references/year-end-close.md` Y6: current/non-current reclassification of the next 12 months' principal portion (manual journal pattern
|
|
148
|
-
- Data migration: when the prior system carried a loan, the opening trial balance includes the outstanding balance. Conversion (`jaz-conversion/SKILL.md § Option 2`) loads it via the `Conversion Clearing > Loan` account; this recipe then runs from the migration date forward only (do NOT model historical periods retroactively).
|
|
180
|
+
- Month-end close: explicitly excludes loan interest from monthly accruals because each month's repayment journal from this recipe already carries the interest. Never post a separate loan-interest accrual on top of it.
|
|
181
|
+
- `jobs/references/year-end-close.md` Y6: current/non-current reclassification of the next 12 months' principal portion (manual journal pattern).
|
|
182
|
+
- Data migration: when the prior system carried a loan, the opening trial balance includes the outstanding balance. Conversion (`jaz-conversion/SKILL.md § Option 2`) loads it via the `Conversion Clearing > Loan` account; this recipe then runs from the migration date forward only (do NOT model historical periods retroactively), and the blueprint's `cash-in` step is skipped.
|