diff --git a/austax/__init__.py b/austax/__init__.py
index fbe5315..56f422b 100644
--- a/austax/__init__.py
+++ b/austax/__init__.py
@@ -21,9 +21,7 @@ from drcr.database import db
import drcr.plugins
from drcr.webapp import app
-from .reports import tax_summary_report
-
-from datetime import datetime
+from .reports import eofy_date, tax_summary_report
def plugin_init():
drcr.plugins.advanced_reports.append(('/tax/summary', 'Tax summary'))
@@ -46,9 +44,7 @@ def make_tax_transactions():
tax_amount = report.by_id('total_tax').amount
# Get EOFY date
- dt = datetime.now().replace(month=6, day=30)
- if dt < datetime.now():
- dt = dt.replace(year=dt.year + 1)
+ dt = eofy_date()
# Estimated tax payable
transactions = [Transaction(
@@ -60,6 +56,18 @@ def make_tax_transactions():
]
)]
+ # Mandatory study loan repayment
+ loan_repayment = report.by_id('loan_repayment').amount
+ if loan_repayment.quantity != 0:
+ transactions.append(Transaction(
+ dt=dt,
+ description='Mandatory study loan repayment payable',
+ postings=[
+ Posting(account='HELP', quantity=loan_repayment.quantity, commodity='$'), # FIXME: Correct account
+ Posting(account='Income Tax Control', quantity=-loan_repayment.quantity, commodity='$')
+ ]
+ ))
+
# Get trial balance
balancer = TrialBalancer()
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
diff --git a/austax/reports.py b/austax/reports.py
index def779f..d0e1b5d 100644
--- a/austax/reports.py
+++ b/austax/reports.py
@@ -14,22 +14,34 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+from drcr import AMOUNT_DPS
from drcr.database import db
from drcr.models import AccountConfiguration, Amount, Transaction, TrialBalancer
from drcr.reports import Calculated, Report, Section, Spacer, Subtotal, entries_for_kind
-def base_income_tax(taxable_income):
- income = taxable_income.quantity
+from .tax_tables import base_tax, repayment_rates
+
+from datetime import datetime
+
+def eofy_date(dt=None):
+ """Get the datetime for the end of the financial year"""
- if income <= 1820000:
- return Amount(0, '$')
- if income <= 4500000:
- return Amount(int((income - 1820000) * 0.19), '$')
- if income <= 12000000:
- return Amount(int(509200 + (income - 4500000) * 0.325), '$')
- if income <= 18000000:
- return Amount(int(2946700 + (income - 12000000) * 0.37), '$')
- return Amount(int(5166700 + (income - 18000000) * 0.45), '$')
+ if dt is None:
+ dt = datetime.now()
+
+ dt_eofy = dt.replace(month=6, day=30)
+ if dt_eofy < dt:
+ dt_eofy = dt_eofy.replace(year=dt.year + 1)
+
+ return dt_eofy
+
+def base_income_tax(year, taxable_income):
+ """Get the amount of base income tax"""
+
+ for i, (upper_limit, flat_amount, marginal_rate) in enumerate(base_tax[year]):
+ if upper_limit is None or taxable_income.quantity <= upper_limit * (10**AMOUNT_DPS):
+ lower_limit = base_tax[year][i - 1][0]
+ return Amount(flat_amount * (10**AMOUNT_DPS) + marginal_rate * (taxable_income.quantity - lower_limit * (10**AMOUNT_DPS)), '$')
def medicare_levy(taxable_income):
if taxable_income.quantity < 2920700:
@@ -37,10 +49,16 @@ def medicare_levy(taxable_income):
return Amount(int(taxable_income.quantity * 0.02), '$')
+def study_loan_repayment(year, taxable_income):
+ """Get the amount of mandatory study loan repayment"""
+
+ for upper_limit, rate in repayment_rates[year]:
+ if upper_limit is None or taxable_income.quantity <= upper_limit * (10**AMOUNT_DPS):
+ return Amount(rate * taxable_income.quantity, '$')
+
def tax_summary_report():
# Get trial balance
balancer = TrialBalancer()
- #balancer.apply_transactions(all_transactions())
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
accounts = dict(sorted(balancer.accounts.items()))
@@ -100,7 +118,7 @@ def tax_summary_report():
entries=[
Calculated(
'Base income tax',
- lambda _: base_income_tax(report.by_id('taxable').amount)
+ lambda _: base_income_tax(eofy_date().year, report.by_id('taxable').amount)
),
Calculated(
'Medicare levy',
@@ -116,8 +134,14 @@ def tax_summary_report():
),
Spacer(),
Calculated(
- 'Income tax payable (refundable)',
- lambda _: report.by_id('total_tax').amount - report.by_id('paygw').amount,
+ 'Mandatory study loan repayment',
+ lambda _: study_loan_repayment(eofy_date().year, report.by_id('taxable').amount),
+ id='loan_repayment'
+ ),
+ Spacer(),
+ Calculated(
+ 'ATO liability payable (refundable)',
+ lambda _: report.by_id('total_tax').amount - report.by_id('paygw').amount + report.by_id('loan_repayment').amount,
heading=True,
bordered=True
)
diff --git a/austax/tax_tables.py b/austax/tax_tables.py
new file mode 100644
index 0000000..501aa6e
--- /dev/null
+++ b/austax/tax_tables.py
@@ -0,0 +1,55 @@
+# DrCr: Web-based double-entry bookkeeping framework
+# Copyright (C) 2022–2023 Lee Yingtong Li (RunasSudo)
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU Affero General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+# Base income tax
+# https://www.ato.gov.au/rates/individual-income-tax-rates/
+# Maps each financial year to list of (upper limit (INclusive), flat amount, marginal rate)
+base_tax = {
+ 2023: [
+ (18200, 0, 0),
+ (45000, 0, 0.19),
+ (120000, 5092, 0.325),
+ (180000, 29467, 0.37),
+ (None, 51667, 0.45)
+ ]
+}
+
+# Study and training loan (HELP, etc.) repayment thresholds and rates
+# https://www.ato.gov.au/Rates/HELP,-TSL-and-SFSS-repayment-thresholds-and-rates/
+# Maps each financial year to list of (upper limit (EXclusive), repayment rate)
+repayment_rates = {
+ 2023: [
+ (48361, 0),
+ (55837, 0.01),
+ (59187, 0.02),
+ (62739, 0.025),
+ (66503, 0.03),
+ (70493, 0.035),
+ (74723, 0.04),
+ (79207, 0.045),
+ (83959, 0.05),
+ (88997, 0.055),
+ (94337, 0.06),
+ (99997, 0.065),
+ (105997, 0.07),
+ (112356, 0.075),
+ (119098, 0.08),
+ (126244, 0.085),
+ (133819, 0.09),
+ (141848, 0.095),
+ (None, 0.1)
+ ]
+}