jaz-cli 2.5.0 → 2.7.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/assets/skills/api/SKILL.md +12 -2
- package/assets/skills/api/references/dependencies.md +3 -2
- package/assets/skills/api/references/endpoints.md +78 -0
- package/assets/skills/api/references/feature-glossary.md +5 -5
- package/assets/skills/api/references/field-map.md +17 -0
- package/assets/skills/api/references/full-api-surface.md +1 -1
- package/assets/skills/conversion/SKILL.md +1 -1
- package/assets/skills/transaction-recipes/SKILL.md +53 -19
- package/assets/skills/transaction-recipes/references/asset-disposal.md +174 -0
- package/assets/skills/transaction-recipes/references/fixed-deposit.md +164 -0
- package/assets/skills/transaction-recipes/references/hire-purchase.md +190 -0
- package/dist/__tests__/amortization.test.js +101 -0
- package/dist/__tests__/asset-disposal.test.js +249 -0
- package/dist/__tests__/blueprint.test.js +72 -0
- package/dist/__tests__/depreciation.test.js +125 -0
- package/dist/__tests__/ecl.test.js +134 -0
- package/dist/__tests__/fixed-deposit.test.js +214 -0
- package/dist/__tests__/fx-reval.test.js +115 -0
- package/dist/__tests__/lease.test.js +96 -0
- package/dist/__tests__/loan.test.js +80 -0
- package/dist/__tests__/provision.test.js +141 -0
- package/dist/__tests__/validate.test.js +81 -0
- package/dist/calc/amortization.js +21 -3
- package/dist/calc/asset-disposal.js +155 -0
- package/dist/calc/blueprint.js +26 -1
- package/dist/calc/depreciation.js +24 -1
- package/dist/calc/ecl.js +13 -1
- package/dist/calc/fixed-deposit.js +178 -0
- package/dist/calc/format.js +107 -2
- package/dist/calc/fx-reval.js +11 -1
- package/dist/calc/lease.js +42 -9
- package/dist/calc/loan.js +12 -2
- package/dist/calc/provision.js +17 -1
- package/dist/commands/calc.js +54 -2
- package/package.json +5 -2
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
# Recipe: Fixed Deposit
|
|
2
|
+
|
|
3
|
+
## Scenario
|
|
4
|
+
|
|
5
|
+
Your company places $100,000 in a 12-month fixed deposit at 3.5% annual interest. Under IFRS 9, this is classified as a financial asset at amortized cost (hold-to-collect business model, passes the SPPI test — solely payments of principal and interest). Interest is accrued monthly using the effective interest rate method and recognized in profit or loss. At maturity, the bank returns the principal plus accumulated interest.
|
|
6
|
+
|
|
7
|
+
**Pattern:** Manual journals + capsule (monthly accrual, single maturity settlement)
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Accounts Involved
|
|
12
|
+
|
|
13
|
+
| Account | Type | Subtype | Role |
|
|
14
|
+
|---|---|---|---|
|
|
15
|
+
| Fixed Deposit | Asset | Current Asset | Carries the principal for the deposit term |
|
|
16
|
+
| Accrued Interest Receivable | Asset | Current Asset | Accumulates monthly interest accruals |
|
|
17
|
+
| Interest Income | Revenue | Other Income | P&L recognition of interest earned |
|
|
18
|
+
| Cash / Bank Account | Asset | Bank | Outflow on placement, inflow at maturity |
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Journal Entries
|
|
23
|
+
|
|
24
|
+
### Step 1: Placement (Day 1) — cash-out
|
|
25
|
+
|
|
26
|
+
Record the transfer from operating account to the fixed deposit. This is a **cash-out** entry in Jaz (cash leaves the bank account):
|
|
27
|
+
|
|
28
|
+
| Line | Account | Debit | Credit |
|
|
29
|
+
|---|---|---|---|
|
|
30
|
+
| 1 | Fixed Deposit | $100,000 | |
|
|
31
|
+
| 2 | Cash / Bank Account | | $100,000 |
|
|
32
|
+
|
|
33
|
+
> In Jaz, record this via the bank module (match the outgoing bank feed entry) or as a manual journal.
|
|
34
|
+
|
|
35
|
+
### Step 2: Monthly Interest Accrual (each month)
|
|
36
|
+
|
|
37
|
+
**Simple interest (default):** Equal monthly accrual amount.
|
|
38
|
+
|
|
39
|
+
| Line | Account | Debit | Credit |
|
|
40
|
+
|---|---|---|---|
|
|
41
|
+
| 1 | Accrued Interest Receivable | $291.67 | |
|
|
42
|
+
| 2 | Interest Income | | $291.67 |
|
|
43
|
+
|
|
44
|
+
**Calculation:**
|
|
45
|
+
- Annual interest = $100,000 × 3.5% = $3,500
|
|
46
|
+
- Monthly accrual = $3,500 / 12 = $291.67
|
|
47
|
+
|
|
48
|
+
**Compound interest:** Interest is calculated on the carrying amount (principal + previously accrued interest) using the effective interest rate. Each month's accrual is slightly higher than the last. Use `jaz calc fixed-deposit --principal 100000 --rate 3.5 --term 12 --compound monthly` to generate the exact schedule.
|
|
49
|
+
|
|
50
|
+
### Step 3: Maturity (final day) — cash-in
|
|
51
|
+
|
|
52
|
+
Principal plus accrued interest returns to the bank account. This is a **cash-in** entry in Jaz (cash arrives in the bank account):
|
|
53
|
+
|
|
54
|
+
| Line | Account | Debit | Credit |
|
|
55
|
+
|---|---|---|---|
|
|
56
|
+
| 1 | Cash / Bank Account | $103,500 | |
|
|
57
|
+
| 2 | Fixed Deposit | | $100,000 |
|
|
58
|
+
| 3 | Accrued Interest Receivable | | $3,500 |
|
|
59
|
+
|
|
60
|
+
> After this entry, Fixed Deposit and Accrued Interest Receivable both zero out. The net P&L impact is $3,500 of Interest Income recognized over the 12 months. In Jaz, record via the bank module (match the incoming bank feed entry) or as a manual journal.
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Capsule Structure
|
|
65
|
+
|
|
66
|
+
**Capsule:** "Fixed Deposit — DBS — 2025"
|
|
67
|
+
**Capsule Type:** "Fixed Deposit"
|
|
68
|
+
|
|
69
|
+
Contents:
|
|
70
|
+
- 1 placement journal
|
|
71
|
+
- 12 monthly interest accrual journals
|
|
72
|
+
- 1 maturity journal
|
|
73
|
+
- **Total entries:** 14
|
|
74
|
+
|
|
75
|
+
The CLI generates a `capsuleDescription` field with full workings — including the interest calculation method, per-period accrual amounts, and maturity value — so the capsule is self-documenting.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## Worked Example
|
|
80
|
+
|
|
81
|
+
**Deposit terms:**
|
|
82
|
+
- Principal: $100,000
|
|
83
|
+
- Annual rate: 3.5%
|
|
84
|
+
- Term: 12 months (simple interest)
|
|
85
|
+
- Monthly accrual: $100,000 × 3.5% / 12 = $291.67
|
|
86
|
+
- Total interest: $3,500.00
|
|
87
|
+
- Maturity value: $103,500.00
|
|
88
|
+
|
|
89
|
+
**CLI command:**
|
|
90
|
+
```
|
|
91
|
+
jaz calc fixed-deposit --principal 100000 --rate 3.5 --term 12 --currency SGD
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Accrual Schedule (first 3 months + last month)
|
|
95
|
+
|
|
96
|
+
| Month | Accrual Amount | Cumulative Interest | Accrued Interest Receivable |
|
|
97
|
+
|---|---|---|---|
|
|
98
|
+
| 1 | $291.67 | $291.67 | $291.67 |
|
|
99
|
+
| 2 | $291.67 | $583.34 | $583.34 |
|
|
100
|
+
| 3 | $291.67 | $875.01 | $875.01 |
|
|
101
|
+
| ... | ... | ... | ... |
|
|
102
|
+
| 12 | $291.63* | $3,500.00 | $3,500.00 |
|
|
103
|
+
|
|
104
|
+
> *Final month adjusted by $0.04 to close the balance exactly to $3,500.00 (rounding correction).
|
|
105
|
+
|
|
106
|
+
**Month 1 journal entry:**
|
|
107
|
+
- Dr Accrued Interest Receivable $291.67
|
|
108
|
+
- Cr Interest Income $291.67
|
|
109
|
+
- Description: "FD interest accrual — Month 1 of 12 (DBS)"
|
|
110
|
+
- Assign to capsule
|
|
111
|
+
|
|
112
|
+
**Month 3 journal entry:**
|
|
113
|
+
- Dr Accrued Interest Receivable $291.67
|
|
114
|
+
- Cr Interest Income $291.67
|
|
115
|
+
- Description: "FD interest accrual — Month 3 of 12 (DBS)"
|
|
116
|
+
- Assign to capsule
|
|
117
|
+
|
|
118
|
+
**Maturity journal entry:**
|
|
119
|
+
- Dr Cash $103,500.00
|
|
120
|
+
- Cr Fixed Deposit $100,000.00
|
|
121
|
+
- Cr Accrued Interest Receivable $3,500.00
|
|
122
|
+
- Description: "FD maturity — principal + interest received (DBS)"
|
|
123
|
+
- Assign to capsule
|
|
124
|
+
|
|
125
|
+
**Totals over 12 months:**
|
|
126
|
+
- Total interest income: $3,500.00
|
|
127
|
+
- Net cash impact: −$100,000 at placement, +$103,500 at maturity = +$3,500
|
|
128
|
+
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
## Enrichment Suggestions
|
|
132
|
+
|
|
133
|
+
| Enrichment | Value | Why |
|
|
134
|
+
|---|---|---|
|
|
135
|
+
| Tracking Tag | "Fixed Deposit" | Filter all FD-related transactions in reports |
|
|
136
|
+
| Nano Classifier | Instrument → "Term Deposit" | Distinguish from savings accounts or money market funds |
|
|
137
|
+
| Custom Field | "Deposit Reference" → "TD-2025-001" | Record the bank's deposit certificate number |
|
|
138
|
+
| Custom Field | "Bank Name" → "DBS" | Identify the issuing bank for multi-bank portfolios |
|
|
139
|
+
|
|
140
|
+
Set the tag and nano classifier on each accrual journal. Custom fields go on the placement journal (the capsule groups everything together).
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Verification
|
|
145
|
+
|
|
146
|
+
1. **Accrued Interest Receivable should accumulate monthly** → Trial Balance at month 6 should show $1,750.02 (6 × $291.67). The balance grows each month until maturity.
|
|
147
|
+
2. **Interest Income P&L line = total accrual to date** → P&L at any date should equal the sum of accrual entries posted up to that date.
|
|
148
|
+
3. **At maturity, Fixed Deposit account zeros out** → After the maturity journal, Fixed Deposit balance = $0 and Accrued Interest Receivable balance = $0.
|
|
149
|
+
4. **Cash flow: Cash decreased by principal on placement, increased by maturity value at end** → Net cash movement over the 12 months = +$3,500 (the interest earned).
|
|
150
|
+
5. **Group General Ledger by Capsule** → "Fixed Deposit — DBS — 2025" should show all 14 entries. Fixed Deposit nets to $0 and Interest Income totals $3,500.
|
|
151
|
+
|
|
152
|
+
---
|
|
153
|
+
|
|
154
|
+
## Variations
|
|
155
|
+
|
|
156
|
+
**Compound interest:** Use `jaz calc fixed-deposit --principal 100000 --rate 3.5 --term 12 --compound monthly` to generate a schedule where each month's interest is calculated on the carrying amount (principal + accrued interest to date). Total interest will be slightly higher than simple interest ($3,556.46 vs $3,500.00 for monthly compounding). The effective interest rate method applies the same logic — each period's income = carrying amount × periodic rate.
|
|
157
|
+
|
|
158
|
+
**FX-denominated deposit:** If the deposit is in USD but your base currency is SGD, record the placement journal with `currency: { sourceCurrency: "USD" }`. Monthly accruals should use the spot rate at each month-end. At maturity, any difference between the cumulative translated amounts and the actual SGD received is an FX gain/loss. Cross-reference the fx-revaluation recipe for month-end revaluation of the outstanding balance.
|
|
159
|
+
|
|
160
|
+
**Premature withdrawal:** The bank may impose a penalty (reduced interest rate or flat fee). Record the maturity journal with the reduced interest amount. If interest already accrued exceeds the actual payout, reverse the excess: Dr Interest Income / Cr Accrued Interest Receivable. Note the penalty in the journal description.
|
|
161
|
+
|
|
162
|
+
**Auto-rollover:** When the bank automatically rolls over the deposit at maturity, close the original capsule with a maturity journal (Dr Fixed Deposit New / Cr Fixed Deposit Old + Cr Accrued Interest Receivable), then create a new capsule for the rolled deposit. The new principal may include the prior interest if the bank compounds on rollover.
|
|
163
|
+
|
|
164
|
+
**PH withholding tax (20% final WHT on interest income):** In jurisdictions that withhold tax on interest at source, the maturity cash received is net of WHT. Record: Dr Cash (net) + Dr WHT Receivable or Tax Expense / Cr Fixed Deposit + Cr Accrued Interest Receivable. This variation is deferred to a future recipe update.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
# Recipe: Hire Purchase
|
|
2
|
+
|
|
3
|
+
## Scenario
|
|
4
|
+
|
|
5
|
+
Your company acquires a motor vehicle under a hire purchase (HP) agreement: $5,000/month for 36 months at 5% incremental borrowing rate. Ownership transfers to you at the end of the term. Under IFRS 16, this is accounted for the same way as any other lease — right-of-use asset, lease liability, effective interest unwinding — with **one critical difference**: the ROU asset is depreciated over its **useful life** (60 months), not the lease term (36 months), because ownership transfers at the end.
|
|
6
|
+
|
|
7
|
+
**Pattern:** Hybrid — native fixed asset (straight-line ROU depreciation over useful life) + manual journals (liability unwinding) + capsule
|
|
8
|
+
|
|
9
|
+
**Cross-reference:** This recipe is a variant of the [IFRS 16 Lease recipe](./ifrs16-lease.md). Everything is identical except the depreciation period. Read the IFRS 16 recipe first for the full foundational treatment, then return here for the hire purchase-specific difference.
|
|
10
|
+
|
|
11
|
+
**Why the difference:** IFRS 16.32 requires that when ownership transfers (or a purchase option is reasonably certain to be exercised), the ROU asset is depreciated over the asset's useful life rather than the lease term. Hire purchase agreements always transfer ownership, so this rule always applies.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## Accounts Involved
|
|
16
|
+
|
|
17
|
+
| Account | Type | Subtype | Role |
|
|
18
|
+
|---|---|---|---|
|
|
19
|
+
| Right-of-Use Asset | Asset | Non-Current Asset | The HP'd asset (e.g., motor vehicle) |
|
|
20
|
+
| Accumulated Depreciation — ROU | Asset | Non-Current Asset | Contra-asset reducing ROU net book value |
|
|
21
|
+
| Lease Liability | Liability | Non-Current Liability | PV of future HP payments |
|
|
22
|
+
| Interest Expense — Leases | Expense | Finance Cost | Effective interest on lease liability |
|
|
23
|
+
| Depreciation Expense — ROU | Expense | Depreciation | Straight-line over useful life (NOT lease term) |
|
|
24
|
+
| Cash / Bank Account | Asset | Bank | Makes monthly HP payments |
|
|
25
|
+
|
|
26
|
+
> **Same accounts as IFRS 16 lease.** The only change is the depreciation calculation — the accounts, journal structure, and liability unwinding are all identical.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## Journal Entries
|
|
31
|
+
|
|
32
|
+
### Step 1: Initial Recognition (same as IFRS 16)
|
|
33
|
+
|
|
34
|
+
| Line | Account | Debit | Credit |
|
|
35
|
+
|---|---|---|---|
|
|
36
|
+
| 1 | Right-of-Use Asset | $166,828.51 | |
|
|
37
|
+
| 2 | Lease Liability | | $166,828.51 |
|
|
38
|
+
|
|
39
|
+
PV of 36 payments of $5,000 at 5% annual (0.4167% monthly).
|
|
40
|
+
|
|
41
|
+
### Step 2: Monthly HP Payment (same as IFRS 16)
|
|
42
|
+
|
|
43
|
+
Each $5,000 payment splits between interest (expense) and principal (liability reduction):
|
|
44
|
+
|
|
45
|
+
| Line | Account | Debit | Credit |
|
|
46
|
+
|---|---|---|---|
|
|
47
|
+
| 1 | Lease Liability | *principal portion* | |
|
|
48
|
+
| 2 | Interest Expense — Leases | *interest portion* | |
|
|
49
|
+
| 3 | Cash / Bank Account | | $5,000 |
|
|
50
|
+
|
|
51
|
+
**Calculation per month:**
|
|
52
|
+
- Interest = Outstanding liability x 0.4167%
|
|
53
|
+
- Principal = $5,000 - Interest
|
|
54
|
+
- New liability = Outstanding liability - Principal
|
|
55
|
+
|
|
56
|
+
### Step 3: Monthly Depreciation — THE KEY DIFFERENCE
|
|
57
|
+
|
|
58
|
+
| Line | Account | Debit | Credit |
|
|
59
|
+
|---|---|---|---|
|
|
60
|
+
| 1 | Depreciation Expense — ROU | $2,780.48 | |
|
|
61
|
+
| 2 | Accumulated Depreciation — ROU | | $2,780.48 |
|
|
62
|
+
|
|
63
|
+
**Standard IFRS 16 lease:** $166,828.51 / 36 months = **$4,634.13/month**
|
|
64
|
+
**Hire purchase:** $166,828.51 / 60 months = **$2,780.48/month**
|
|
65
|
+
|
|
66
|
+
> The hire purchase depreciation rate is **$1,853.65/month LESS** than a standard lease. This is because the asset's economic life (60 months) extends well beyond the payment term (36 months). Ownership transfers, so the asset continues generating value after the last payment.
|
|
67
|
+
|
|
68
|
+
---
|
|
69
|
+
|
|
70
|
+
## Capsule Structure
|
|
71
|
+
|
|
72
|
+
**Capsule:** "Hire Purchase — Motor Vehicle — 2025"
|
|
73
|
+
**Capsule Type:** "Hire Purchase"
|
|
74
|
+
|
|
75
|
+
Contents:
|
|
76
|
+
- 1 initial recognition journal (ROU asset + lease liability)
|
|
77
|
+
- 36 monthly payment journals (manual — interest changes each month)
|
|
78
|
+
- 60 monthly depreciation entries (auto-generated by fixed asset register — these appear in the ledger but aren't manually created)
|
|
79
|
+
- **Manually created entries:** 37
|
|
80
|
+
|
|
81
|
+
> The fixed asset depreciation entries are not assigned to the capsule automatically. Group the General Ledger by the ROU asset account alongside the capsule view for a complete picture. Depreciation continues for 24 months after the last HP payment.
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Worked Example
|
|
86
|
+
|
|
87
|
+
**HP terms:**
|
|
88
|
+
- Monthly payment: $5,000
|
|
89
|
+
- Lease term: 36 months
|
|
90
|
+
- Incremental borrowing rate: 5% annual (0.4167% monthly)
|
|
91
|
+
- Asset useful life: 60 months
|
|
92
|
+
- PV of lease payments: $166,828.51
|
|
93
|
+
|
|
94
|
+
**CLI command:**
|
|
95
|
+
```
|
|
96
|
+
jaz calc lease --payment 5000 --term 36 --rate 5 --useful-life 60 --start-date 2025-01-01
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
The `--useful-life 60` flag tells the calculator this is a hire purchase — depreciation will use 60 months instead of the 36-month lease term. The CLI generates a `capsuleDescription` with full workings so the capsule is self-documenting.
|
|
100
|
+
|
|
101
|
+
### Liability Unwinding Table (first 3 months + month 36)
|
|
102
|
+
|
|
103
|
+
| Month | Opening Liability | Interest (0.4167%) | Principal | Closing Liability |
|
|
104
|
+
|---|---|---|---|---|
|
|
105
|
+
| 1 | $166,828.51 | $695.12 | $4,304.88 | $162,523.63 |
|
|
106
|
+
| 2 | $162,523.63 | $677.18 | $4,322.82 | $158,200.81 |
|
|
107
|
+
| 3 | $158,200.81 | $659.17 | $4,340.83 | $153,859.98 |
|
|
108
|
+
| ... | ... | ... | ... | ... |
|
|
109
|
+
| 36 | $4,979.24 | $20.75 | $4,979.24 | $0.00* |
|
|
110
|
+
|
|
111
|
+
> *Final payment adjusted to close the liability exactly to zero (payment = $4,999.99).
|
|
112
|
+
|
|
113
|
+
**Month 1 journal entry (manual — same as IFRS 16):**
|
|
114
|
+
- Dr Lease Liability $4,304.88
|
|
115
|
+
- Dr Interest Expense — Leases $695.12
|
|
116
|
+
- Cr Cash $5,000.00
|
|
117
|
+
- Description: "HP payment — Month 1 of 36 (Motor Vehicle)"
|
|
118
|
+
- Assign to capsule
|
|
119
|
+
|
|
120
|
+
**Month 1 depreciation (automatic — posted by Jaz FA register):**
|
|
121
|
+
- Dr Depreciation Expense — ROU $2,780.48
|
|
122
|
+
- Cr Accumulated Depreciation — ROU $2,780.48
|
|
123
|
+
|
|
124
|
+
### Depreciation Comparison: Hire Purchase vs Standard Lease
|
|
125
|
+
|
|
126
|
+
| Item | Standard IFRS 16 Lease | Hire Purchase |
|
|
127
|
+
|---|---|---|
|
|
128
|
+
| Depreciation period | 36 months (lease term) | 60 months (useful life) |
|
|
129
|
+
| Monthly depreciation | $4,634.13 | $2,780.48 |
|
|
130
|
+
| **Difference** | — | **$1,853.65/month LESS** |
|
|
131
|
+
|
|
132
|
+
### What Happens After Month 36 (Lease Payments End)
|
|
133
|
+
|
|
134
|
+
| Item | Value |
|
|
135
|
+
|---|---|
|
|
136
|
+
| Lease Liability | $0 (fully unwound) |
|
|
137
|
+
| ROU Asset (gross) | $166,828.51 |
|
|
138
|
+
| Accumulated Depreciation (36 months) | $100,097.28 |
|
|
139
|
+
| **ROU Net Book Value** | **$66,731.23** |
|
|
140
|
+
| Remaining depreciation period | 24 more months |
|
|
141
|
+
|
|
142
|
+
After the last HP payment, the lease liability is zero and no more cash leaves the account. But the ROU asset still has a book value of $66,731.23 because it was depreciated over 60 months, not 36. Depreciation of $2,780.48/month continues for another 24 months until the asset is fully depreciated.
|
|
143
|
+
|
|
144
|
+
### Summary Over Full Useful Life (60 Months)
|
|
145
|
+
|
|
146
|
+
| Item | Total |
|
|
147
|
+
|---|---|
|
|
148
|
+
| Total cash payments | $180,000 (36 x $5,000) |
|
|
149
|
+
| Total interest expense | $13,171.48 |
|
|
150
|
+
| Total depreciation expense | $166,828.51 |
|
|
151
|
+
| **Total P&L impact** | **$180,000** |
|
|
152
|
+
| Lease liability at month 36 | $0 |
|
|
153
|
+
| ROU asset net book value at month 36 | $66,731.23 |
|
|
154
|
+
| ROU asset net book value at month 60 | $0 |
|
|
155
|
+
|
|
156
|
+
> **P&L timing differs from a standard lease.** With hire purchase, the P&L charge is spread over 60 months instead of 36. In months 1-36, both interest and depreciation hit the P&L. In months 37-60, only depreciation remains — at a lower rate than the original cash payment.
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Enrichment Suggestions
|
|
161
|
+
|
|
162
|
+
| Enrichment | Value | Why |
|
|
163
|
+
|---|---|---|
|
|
164
|
+
| Tracking Tag | "Hire Purchase" | Filter all HP-related transactions |
|
|
165
|
+
| Nano Classifier | Asset Type → "Motor Vehicle" | Break down by asset class if multiple HPs |
|
|
166
|
+
| Custom Field | "HP Agreement #" → "HP-2025-0089" | Record the hire purchase agreement reference |
|
|
167
|
+
|
|
168
|
+
---
|
|
169
|
+
|
|
170
|
+
## Verification
|
|
171
|
+
|
|
172
|
+
1. **Lease Liability should reduce to $0 at end of lease term (month 36)** → Group General Ledger by Capsule. Liability starts at $166,828.51 and closes at $0 after 36 payments.
|
|
173
|
+
2. **Interest Expense total = total cash payments - PV** → $180,000 − $166,828.51 = $13,171.48 (rounding may cause ±$0.01 variance).
|
|
174
|
+
3. **ROU Asset continues depreciating after lease payments end** → At month 36, ROU net book value = $66,731.23. Depreciation continues at $2,780.48/month for 24 more months.
|
|
175
|
+
4. **At end of useful life (month 60), ROU asset is fully depreciated** → ROU Asset $166,828.51 − Accumulated Depreciation $166,828.51 = $0 net book value.
|
|
176
|
+
5. **Balance Sheet check** → Between months 37-60, no lease liability exists but the ROU asset (net of accumulated depreciation) continues appearing on the balance sheet. This is the distinguishing feature of hire purchase vs operating lease.
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## Variations
|
|
181
|
+
|
|
182
|
+
**Standard operating lease (no ownership transfer):** Use `jaz calc lease --payment 5000 --term 36 --rate 5` without the `--useful-life` flag. The ROU asset is depreciated over the lease term (36 months), not useful life. See the [IFRS 16 Lease recipe](./ifrs16-lease.md).
|
|
183
|
+
|
|
184
|
+
**Guaranteed residual value:** If the HP agreement includes a guaranteed residual value (e.g., $10,000 balloon payment at end of term), add the PV of the residual to the initial ROU asset and lease liability. The liability schedule must include the balloon as the final period payment.
|
|
185
|
+
|
|
186
|
+
**Variable payments:** Only the fixed portion of HP payments is included in the PV calculation. Any variable component (e.g., mileage-based charges) is recognized as expense when incurred, not capitalized in the ROU asset.
|
|
187
|
+
|
|
188
|
+
**SG motor vehicle specifics:** The COE (Certificate of Entitlement) is typically treated as a separate intangible asset with its own amortization schedule (10 years for Category A/B), not bundled into the HP's ROU asset. Create a separate capsule for the COE if applicable.
|
|
189
|
+
|
|
190
|
+
**SG tax depreciation (IRAS):** Capital allowances for motor vehicles under Section 19/19A may differ from IFRS depreciation. The S-car cap ($35,000 for private cars) and the 3-year write-off period for commercial vehicles don't align with IFRS useful life estimates. Businesses may need to track both IFRS depreciation (for financial reporting) and tax depreciation (for S68 deductions) separately. This recipe covers IFRS only — tax depreciation adjustments are out of scope.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import { calculatePrepaidExpense, calculateDeferredRevenue } from '../calc/amortization.js';
|
|
3
|
+
import { CalcValidationError } from '../calc/validate.js';
|
|
4
|
+
describe('calculatePrepaidExpense', () => {
|
|
5
|
+
const base = { amount: 12000, periods: 12 };
|
|
6
|
+
it('per period = amount / periods', () => {
|
|
7
|
+
const r = calculatePrepaidExpense(base);
|
|
8
|
+
expect(r.perPeriodAmount).toBe(1000);
|
|
9
|
+
});
|
|
10
|
+
it('schedule has correct length', () => {
|
|
11
|
+
const r = calculatePrepaidExpense(base);
|
|
12
|
+
expect(r.schedule).toHaveLength(12);
|
|
13
|
+
});
|
|
14
|
+
it('total amortized = original amount', () => {
|
|
15
|
+
const r = calculatePrepaidExpense(base);
|
|
16
|
+
const total = r.schedule.reduce((s, row) => s + row.amortized, 0);
|
|
17
|
+
expect(Math.round(total * 100) / 100).toBe(12000);
|
|
18
|
+
});
|
|
19
|
+
it('remaining balance reaches zero at final period', () => {
|
|
20
|
+
const r = calculatePrepaidExpense(base);
|
|
21
|
+
expect(r.schedule[r.schedule.length - 1].remainingBalance).toBe(0);
|
|
22
|
+
});
|
|
23
|
+
it('handles rounding — 10000 / 3 periods', () => {
|
|
24
|
+
const r = calculatePrepaidExpense({ amount: 10000, periods: 3 });
|
|
25
|
+
expect(r.perPeriodAmount).toBe(3333.33);
|
|
26
|
+
const total = r.schedule.reduce((s, row) => s + row.amortized, 0);
|
|
27
|
+
expect(Math.round(total * 100) / 100).toBe(10000);
|
|
28
|
+
// Final period absorbs rounding
|
|
29
|
+
expect(r.schedule[2].amortized).toBe(3333.34);
|
|
30
|
+
});
|
|
31
|
+
it('every journal entry balanced', () => {
|
|
32
|
+
const r = calculatePrepaidExpense(base);
|
|
33
|
+
for (const row of r.schedule) {
|
|
34
|
+
const debits = row.journal.lines.reduce((s, l) => s + l.debit, 0);
|
|
35
|
+
const credits = row.journal.lines.reduce((s, l) => s + l.credit, 0);
|
|
36
|
+
expect(debits).toBe(credits);
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
it('journal entries use Expense / Prepaid Asset accounts', () => {
|
|
40
|
+
const r = calculatePrepaidExpense(base);
|
|
41
|
+
expect(r.schedule[0].journal.lines[0].account).toBe('Expense');
|
|
42
|
+
expect(r.schedule[0].journal.lines[1].account).toBe('Prepaid Asset');
|
|
43
|
+
});
|
|
44
|
+
// Blueprint
|
|
45
|
+
it('blueprint step 1 = bill', () => {
|
|
46
|
+
const r = calculatePrepaidExpense({ ...base, startDate: '2025-01-01' });
|
|
47
|
+
expect(r.blueprint.steps[0].action).toBe('bill');
|
|
48
|
+
});
|
|
49
|
+
it('blueprint step count = 1 + periods', () => {
|
|
50
|
+
const r = calculatePrepaidExpense({ ...base, startDate: '2025-01-01' });
|
|
51
|
+
expect(r.blueprint.steps).toHaveLength(13);
|
|
52
|
+
});
|
|
53
|
+
it('blueprint null without startDate', () => {
|
|
54
|
+
const r = calculatePrepaidExpense(base);
|
|
55
|
+
expect(r.blueprint).toBeNull();
|
|
56
|
+
});
|
|
57
|
+
// Quarterly
|
|
58
|
+
it('quarterly frequency works', () => {
|
|
59
|
+
const r = calculatePrepaidExpense({ amount: 12000, periods: 4, frequency: 'quarterly', startDate: '2025-01-01' });
|
|
60
|
+
expect(r.schedule).toHaveLength(4);
|
|
61
|
+
// Date present and properly formatted (exact value depends on TZ)
|
|
62
|
+
expect(r.schedule[0].date).toMatch(/^\d{4}-\d{2}-\d{2}$/);
|
|
63
|
+
});
|
|
64
|
+
// Validation
|
|
65
|
+
it('rejects zero amount', () => {
|
|
66
|
+
expect(() => calculatePrepaidExpense({ amount: 0, periods: 12 })).toThrow(CalcValidationError);
|
|
67
|
+
});
|
|
68
|
+
it('rejects zero periods', () => {
|
|
69
|
+
expect(() => calculatePrepaidExpense({ amount: 12000, periods: 0 })).toThrow(CalcValidationError);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
describe('calculateDeferredRevenue', () => {
|
|
73
|
+
const base = { amount: 36000, periods: 12 };
|
|
74
|
+
it('per period = amount / periods', () => {
|
|
75
|
+
const r = calculateDeferredRevenue(base);
|
|
76
|
+
expect(r.perPeriodAmount).toBe(3000);
|
|
77
|
+
});
|
|
78
|
+
it('total recognized = original amount', () => {
|
|
79
|
+
const r = calculateDeferredRevenue(base);
|
|
80
|
+
const total = r.schedule.reduce((s, row) => s + row.amortized, 0);
|
|
81
|
+
expect(Math.round(total * 100) / 100).toBe(36000);
|
|
82
|
+
});
|
|
83
|
+
it('journal entries use Deferred Revenue / Revenue accounts', () => {
|
|
84
|
+
const r = calculateDeferredRevenue(base);
|
|
85
|
+
expect(r.schedule[0].journal.lines[0].account).toBe('Deferred Revenue');
|
|
86
|
+
expect(r.schedule[0].journal.lines[1].account).toBe('Revenue');
|
|
87
|
+
});
|
|
88
|
+
// Blueprint
|
|
89
|
+
it('blueprint step 1 = invoice', () => {
|
|
90
|
+
const r = calculateDeferredRevenue({ ...base, startDate: '2025-01-01' });
|
|
91
|
+
expect(r.blueprint.steps[0].action).toBe('invoice');
|
|
92
|
+
});
|
|
93
|
+
it('capsuleType is Deferred Revenue', () => {
|
|
94
|
+
const r = calculateDeferredRevenue({ ...base, startDate: '2025-01-01' });
|
|
95
|
+
expect(r.blueprint.capsuleType).toBe('Deferred Revenue');
|
|
96
|
+
});
|
|
97
|
+
it('capsuleDescription present', () => {
|
|
98
|
+
const r = calculateDeferredRevenue({ ...base, startDate: '2025-01-01' });
|
|
99
|
+
expect(r.blueprint.capsuleDescription).toBeTruthy();
|
|
100
|
+
});
|
|
101
|
+
});
|