BusinessMath Quarterly Series
13 min read
Part 15 of 12-Week BusinessMath Series
In 2019, new lease accounting standards (IFRS 16 and ASC 842) fundamentally changed how companies report leases. Most leases must now be capitalized on the balance sheet, creating:
This affects nearly every business with operating leases (office space, equipment, vehicles). CFOs need to:
Manual lease accounting in spreadsheets is error-prone and doesn’t scale when you have dozens or hundreds of leases.
BusinessMath provides the Lease type with comprehensive tools for lease liability calculation, ROU asset modeling, amortization schedules, and expense tracking.
Calculate the initial lease liability and ROU asset:
import BusinessMath
// Office lease: quarterly payments for 1 year
let q1 = Period.quarter(year: 2025, quarter: 1)
let periods = [q1, q1 + 1, q1 + 2, q1 + 3]
let payments = TimeSeries(
periods: periods,
values: [25_000.0, 25_000.0, 25_000.0, 25_000.0]
)
// Create lease with 6% annual discount rate (incremental borrowing rate)
let lease = Lease(
payments: payments,
discountRate: 0.06
)
// Calculate present value (lease liability)
let liability = lease.presentValue()
print("Initial lease liability: \(liability.currency())") // ~$96,360
// Calculate right-of-use asset (initially equals liability)
let rouAsset = lease.rightOfUseAsset()
print("ROU asset: \(rouAsset.currency())") // $96,360
Output:
Initial lease liability: $96,360
ROU asset: $96,360
The calculation: Four $25,000 payments discounted at 6% annual (1.5% quarterly) = $96,360 present value.
Generate a complete amortization schedule showing how the liability decreases each period:
let schedule = lease.liabilitySchedule()
print("=== Lease Liability Schedule ===")
print("Period\t\tBeginning\tPayment\t\tInterest\tPrincipal\tEnding")
print("------\t\t---------\t-------\t\t--------\t---------\t------")
for (i, period) in periods.enumerated() {
// Beginning balance
let beginning = i == 0 ? liability : schedule[periods[i-1]]!
// Payment
let payment = payments[period]!
// Interest expense (Beginning × quarterly rate)
let interest = lease.interestExpense(period: period)
// Principal reduction
let principal = lease.principalReduction(period: period)
// Ending balance
let ending = schedule[period]!
print("\(period.label)\(beginning.currency(0).paddingLeft(toLength: 14))\(payment.currency(0).paddingLeft(toLength: 10))\(interest.currency(0).paddingLeft(toLength: 13))\(principal.currency(0).paddingLeft(toLength: 13))\(ending.currency(0).paddingLeft(toLength: 9))")
}
print("\nTotal payments: \((payments.reduce(0, +)).currency(0))")
print("Total interest: \((lease.totalInterest()).currency(0))")
Output:
=== Lease Liability Schedule ===
Period Beginning Payment Interest Principal Ending
------ --------- ------- -------- --------- ------
2025-Q1 $96,360 $25,000 $1,445 $23,555 $96,360
2025-Q2 $96,360 $25,000 $1,092 $23,908 $48,897
2025-Q3 $48,897 $25,000 $733 $24,267 $24,631
2025-Q4 $24,631 $25,000 $369 $24,631 $0
Total payments: $100,000
Total interest: $3,640
The insight: Interest expense decreases each period as the liability balance declines (front-loaded interest).
Many leases include upfront costs that increase the ROU asset:
let leaseWithCosts = Lease(
payments: payments,
discountRate: 0.06,
initialDirectCosts: 5_000.0, // Legal fees, broker commissions
prepaidAmount: 10_000.0 // First month rent + security deposit
)
let liability = leaseWithCosts.presentValue() // PV of payments only
let rouAsset = leaseWithCosts.rightOfUseAsset() // PV + costs + prepayments
print("=== Initial Recognition with Costs ===")
print("Lease liability: \(liability.currency())") // $96,454
print("ROU asset: \(rouAsset.currency())") // $111,454
print("\nDifference: \((rouAsset - liability).currency())") // $15,000 (costs + prepayment)
Output:
=== Initial Recognition with Costs ===
Lease liability: $96,360
ROU asset: $111,360
Difference: $15,000
The accounting: Liability = PV of future payments. Asset = Liability + upfront costs + prepayments.
ROU assets are depreciated straight-line over the lease term:
print("\n=== ROU Asset Depreciation ===")
// Quarterly depreciation (straight-line over 4 quarters)
let depreciation = leaseWithCosts.depreciation(period: q1)
print("Quarterly depreciation: \(depreciation.currency())") // $111,454 ÷ 4 = $27,864
// Track carrying value each quarter
for (i, period) in periods.enumerated() {
let carryingValue = leaseWithCosts.carryingValue(period: period)
let quarterNum = i + 1
print("Q\(quarterNum) carrying value: \(carryingValue.currency())")
}
Output:
=== ROU Asset Depreciation ===
Quarterly depreciation: $27,840
Q1 carrying value: $83,520
Q2 carrying value: $55,680
Q3 carrying value: $27,840
Q4 carrying value: $0
The pattern: ROU asset decreases linearly by $27,864 each quarter until fully depreciated.
Each period has two expenses: interest and depreciation:
print("\n=== Total P&L Impact by Quarter ===")
print("Quarter\tInterest\tDepreciation\tTotal Expense")
print("-------\t--------\t------------\t-------------")
var totalInterest = 0.0
var totalDepreciation = 0.0
for (i, period) in periods.enumerated() {
let interest = leaseWithCosts.interestExpense(period: period)
let depreciation = leaseWithCosts.depreciation(period: period)
let total = interest + depreciation
totalInterest += interest
totalDepreciation += depreciation
let quarterNum = i + 1
print("Q\(quarterNum)\t\(interest.currency())\t\(depreciation.currency())\t\(total.currency())")
}
print("\nTotal:\t\(totalInterest.currency())\t\(totalDepreciation.currency())\t\((totalInterest + totalDepreciation).currency())")
print("\n** Note: Expense is front-loaded due to higher interest in early periods")
Output:
=== Total P&L Impact by Quarter ===
Quarter Interest Depreciation Total Expense
------- -------- ------------ -------------
2025-Q1 $1,445 $27,840 $29,285
2025-Q2 $1,092 $27,840 $28,932
2025-Q3 $733 $27,840 $28,573
2025-Q4 $369 $27,840 $28,209
Total: $3,640 $111,360 $115,000
** Note: Expense is front-loaded due to higher interest in early periods
The insight: Total expense ($115k) exceeds cash payments ($100k) because we’re expensing the upfront costs ($15k) over the lease term.
Leases of 12 months or less can be expensed instead of capitalized:
let shortTermLease = Lease(
payments: payments, // 4 quarterly payments = 12 months
discountRate: 0.06,
leaseTerm: .months(12)
)
if shortTermLease.isShortTerm {
print("\n✓ Qualifies for short-term exemption")
print("Can expense payments as incurred without capitalizing")
// No balance sheet impact
let rouAsset = shortTermLease.rightOfUseAsset() // Returns 0
print("ROU asset: \(rouAsset.currency())")
} else {
print("Must capitalize lease")
}
Output:
✓ Qualifies for short-term exemption
Can expense payments as incurred without capitalizing
ROU asset: $0.00
The rule: Leases ≤ 12 months can be treated as operating expenses (no capitalization required).
Leases of assets valued under $5,000 can also be expensed:
// Small equipment lease
let lowValueLease = Lease(
payments: payments,
discountRate: 0.06,
underlyingAssetValue: 4_500.0 // Below $5K threshold
)
if lowValueLease.isLowValue {
print("\n✓ Qualifies for low-value exemption")
print("Underlying asset value: \(lowValueLease.underlyingAssetValue!.currency())")
print("Can expense payments as incurred")
}
Output:
✓ Qualifies for low-value exemption
Underlying asset value: $4,500.00
Can expense payments as incurred
The rule: Assets with fair value < $5,000 when new (e.g., laptops, small office equipment) can be expensed.
The discount rate significantly impacts lease valuation:
print("\n=== Impact of Discount Rate ===")
// Conservative rate (lower discount = higher PV)
let lowRate = Lease(payments: payments, discountRate: 0.04)
// Market rate
let marketRate = Lease(payments: payments, discountRate: 0.06)
// Riskier rate (higher discount = lower PV)
let highRate = Lease(payments: payments, discountRate: 0.10)
print("At 4% rate: \(lowRate.presentValue().currency())")
print("At 6% rate: \(marketRate.presentValue().currency())")
print("At 10% rate: \(highRate.presentValue().currency())")
let difference = lowRate.presentValue() - highRate.presentValue()
print("\nDifference between 4% and 10%: \(difference.currency())")
Output:
=== Impact of Discount Rate ===
At 4% rate: $97,549.14
At 6% rate: $96,359.62
At 10% rate: $94,049.36
Difference between 4% and 10%: $3,499.78
The insight: Higher discount rates reduce the present value (and thus the balance sheet liability). Companies often use their incremental borrowing rate (IBR).
Real-world leases often have annual rent increases:
// 5-year office lease with 3% annual escalation
let startDate = Period.quarter(year: 2025, quarter: 1)
let fiveYearPeriods = (0..<20).map { startDate + $0 } // 20 quarters
// Generate escalating payments
var escalatingPayments: [Double] = []
let baseRent = 30_000.0
for i in 0..<20 {
let yearIndex = i / 4 // Which year (0-4)
let escalatedRent = baseRent * pow(1.03, Double(yearIndex))
escalatingPayments.append(escalatedRent)
}
let paymentSeries = TimeSeries(periods: fiveYearPeriods, values: escalatingPayments)
let longTermLease = Lease(
payments: paymentSeries,
discountRate: 0.068, // 6.8% IBR
initialDirectCosts: 15_000.0,
prepaidAmount: 30_000.0
)
let liability = longTermLease.presentValue()
let rouAsset = longTermLease.rightOfUseAsset()
print("\n=== 5-Year Office Lease ===")
print("Base quarterly rent: \(baseRent.currency())")
print("Total payments (nominal): \(paymentSeries.reduce(0, +).currency())")
print("Present value: \(liability.currency())")
print("ROU asset: \(rouAsset.currency())")
print("\nDiscount: \((paymentSeries.reduce(0, +) - liability).currency()) (\((1 - liability / paymentSeries.reduce(0, +)).percent(1)))")
Output:
=== 5-Year Office Lease ===
Base quarterly rent: $30,000.00
Total payments (nominal): $637,096.30
Present value: $534,140.43
ROU asset: $579,140.43
Discount: $102,955.86 (16.2%)
The reality: Over 5 years, the present value is ~24% less than nominal payments due to time value of money.
import BusinessMath
// Office lease: quarterly payments for 1 year
let q1 = Period.quarter(year: 2025, quarter: 1)
let periods = [q1, q1 + 1, q1 + 2, q1 + 3]
let payments = TimeSeries(
periods: periods,
values: [25_000.0, 25_000.0, 25_000.0, 25_000.0]
)
// Create lease with 6% annual discount rate (incremental borrowing rate)
let lease = Lease(
payments: payments,
discountRate: 0.06
)
// Calculate present value (lease liability)
let liability = lease.presentValue()
print("Initial lease liability: \(liability.currency(0))") // ~$96,360
// Calculate right-of-use asset (initially equals liability)
let rouAsset = lease.rightOfUseAsset()
print("ROU asset: \(rouAsset.currency(0))") // $96,360
let schedule = lease.liabilitySchedule()
print("=== Lease Liability Schedule ===")
print("Period\t\tBeginning\tPayment\t\tInterest\tPrincipal\tEnding")
print("------\t\t---------\t-------\t\t--------\t---------\t------")
for (i, period) in periods.enumerated() {
// Beginning balance
let beginning = i == 0 ? liability : schedule[periods[i-1]]!
// Payment
let payment = payments[period]!
// Interest expense (Beginning × quarterly rate)
let interest = lease.interestExpense(period: period)
// Principal reduction
let principal = lease.principalReduction(period: period)
// Ending balance
let ending = schedule[period]!
print("\(period.label)\(beginning.currency(0).paddingLeft(toLength: 14))\(payment.currency(0).paddingLeft(toLength: 10))\(interest.currency(0).paddingLeft(toLength: 13))\(principal.currency(0).paddingLeft(toLength: 13))\(ending.currency(0).paddingLeft(toLength: 9))")
}
print("\nTotal payments: \((payments.reduce(0, +)).currency(0))")
print("Total interest: \((lease.totalInterest()).currency(0))")
let leaseWithCosts = Lease(
payments: payments,
discountRate: 0.06,
initialDirectCosts: 5_000.0, // Legal fees, broker commissions
prepaidAmount: 10_000.0 // First month rent + security deposit
)
let liability_wc = leaseWithCosts.presentValue() // PV of payments only
let rouAsset_wc = leaseWithCosts.rightOfUseAsset() // PV + costs + prepayments
print("=== Initial Recognition with Costs ===")
print("Lease liability: \(liability_wc.currency(0))") // $96,360
print("ROU asset: \(rouAsset_wc.currency(0))") // $111,360
print("\nDifference: \((rouAsset_wc - liability_wc).currency(0))") // $15,000 (costs + prepayment)
print("\n=== ROU Asset Depreciation ===")
// Quarterly depreciation (straight-line over 4 quarters)
let depreciation = leaseWithCosts.depreciation(period: q1)
print("Quarterly depreciation: \(depreciation.currency(0))") // $111,454 ÷ 4 = $27,864
// Track carrying value each quarter
for (i, period) in periods.enumerated() {
let carryingValue = leaseWithCosts.carryingValue(period: period)
let quarterNum = i + 1
print("Q\(quarterNum) carrying value: \(carryingValue.currency(0))")
}
print("\n=== Total P&L Impact by Quarter ===")
print("Quarter\tInterest\tDepreciation\tTotal Expense")
print("-------\t--------\t------------\t-------------")
var totalInterest = 0.0
var totalDepreciation = 0.0
for (i, period) in periods.enumerated() {
let interest = leaseWithCosts.interestExpense(period: period)
let depreciation = leaseWithCosts.depreciation(period: period)
let total = interest + depreciation
totalInterest += interest
totalDepreciation += depreciation
let quarterNum = i + 1
print("\(period.label)\(interest.currency(0).paddingLeft(toLength: 9))\(depreciation.currency(0).paddingLeft(toLength: 16))\(total.currency(0).paddingLeft(toLength: 17))")
}
print("\n Total:\(totalInterest.currency(0).paddingLeft(toLength: 9))\(totalDepreciation.currency(0).paddingLeft(toLength: 16))\((totalInterest + totalDepreciation).currency(0).paddingLeft(toLength: 17))")
print("\n** Note: Expense is front-loaded due to higher interest in early periods")
let shortTermLease = Lease(
payments: payments, // 4 quarterly payments = 12 months
discountRate: 0.06,
leaseTerm: .months(12)
)
if shortTermLease.isShortTerm {
print("\n✓ Qualifies for short-term exemption")
print("Can expense payments as incurred without capitalizing")
// No balance sheet impact
let rouAsset = shortTermLease.rightOfUseAsset() // Returns 0
print("ROU asset: \(rouAsset.currency())")
} else {
print("Must capitalize lease")
}
// Small equipment lease
let lowValueLease = Lease(
payments: payments,
discountRate: 0.06,
underlyingAssetValue: 4_500.0 // Below $5K threshold
)
if lowValueLease.isLowValue {
print("\n✓ Qualifies for low-value exemption")
print("Underlying asset value: \(lowValueLease.underlyingAssetValue!.currency())")
print("Can expense payments as incurred")
}
print("\n=== Impact of Discount Rate ===")
// Conservative rate (lower discount = higher PV)
let lowRate = Lease(payments: payments, discountRate: 0.04)
// Market rate
let marketRate = Lease(payments: payments, discountRate: 0.06)
// Riskier rate (higher discount = lower PV)
let highRate = Lease(payments: payments, discountRate: 0.10)
print("At 4% rate: \(lowRate.presentValue().currency())")
print("At 6% rate: \(marketRate.presentValue().currency())")
print("At 10% rate: \(highRate.presentValue().currency())")
let difference = lowRate.presentValue() - highRate.presentValue()
print("\nDifference between 4% and 10%: \(difference.currency())")
// 5-year office lease with 3% annual escalation
let startDate = Period.quarter(year: 2025, quarter: 1)
let fiveYearPeriods = (0..<20).map { startDate + $0 } // 20 quarters
// Generate escalating payments
var escalatingPayments: [Double] = []
let baseRent = 30_000.0
for i in 0..<20 {
let yearIndex = i / 4 // Which year (0-4)
let escalatedRent = baseRent * pow(1.03, Double(yearIndex))
escalatingPayments.append(escalatedRent)
}
let paymentSeries = TimeSeries(periods: fiveYearPeriods, values: escalatingPayments)
let longTermLease = Lease(
payments: paymentSeries,
discountRate: 0.068, // 6.8% IBR
initialDirectCosts: 15_000.0,
prepaidAmount: 30_000.0
)
let liability_ep = longTermLease.presentValue()
let rouAsset_ep = longTermLease.rightOfUseAsset()
print("\n=== 5-Year Office Lease ===")
print("Base quarterly rent: \(baseRent.currency())")
print("Total payments (nominal): \(paymentSeries.reduce(0, +).currency())")
print("Present value: \(liability_ep.currency())")
print("ROU asset: \(rouAsset_ep.currency())")
print("\nDiscount: \((paymentSeries.reduce(0, +) - liability_ep).currency()) (\((1 - liability_ep / paymentSeries.reduce(0, +)).percent(1)))")
→ Full API Reference: BusinessMath Docs – 3.6 Lease Accounting
Modifications to try:
Every public company with leases must comply with IFRS 16 / ASC 842:
Example - Delta Air Lines: Adopted ASC 842 and added $8.5 billion in lease liabilities to the balance sheet. Their debt-to-equity ratio instantly increased from 1.5x to 2.8x.
CFO use case: “We have 250 office leases across 30 countries. I need to calculate the total lease liability and ROU asset for our quarterly 10-Q filing, broken down by currency and region.”
BusinessMath makes this programmatic, auditable, and reproducible.
★ Insight ─────────────────────────────────────
Why the New Lease Accounting Standards?
Under old rules (IAS 17 / FAS 13), operating leases were off-balance-sheet.
This meant:
IFRS 16 / ASC 842 solved this by requiring capitalization of virtually all leases. Now the balance sheet reflects the economic reality: if you have the right to use an asset and an obligation to pay, that’s an asset and liability.
Trade-off: More complexity, but greater transparency.
─────────────────────────────────────────────────
The hardest design decision for lease accounting was: How much to embed accounting rules in the API vs. leaving flexibility?
Example dilemma: Should Lease.rightOfUseAsset() automatically include initial direct costs? Or require the user to add them separately?
We chose automatic inclusion because:
But this means the API embeds accounting assumptions. If standards change (e.g., IFRS 17 for insurance), the API must evolve.
The lesson: For domain-specific APIs (accounting, tax, legal), embedding rules improves correctness but reduces flexibility. Choose based on your users’ expertise—CPAs benefit from enforced rules; accountants building custom models need flexibility.
Related Methodology: Test-First Development (Week 1) - We wrote failing tests for IFRS 16 requirements first, ensuring compliance.
Coming up next week: Week 5 explores loans, investments, and valuation techniques.
Monday: Loan Amortization - Building schedules for mortgages, car loans, and term loans.
Series Progress:
Tagged with: businessmath, swift, leases, ifrs16, asc842, accounting, right-of-use-assets