austax: Implement calculation of mandatory study loan repayment

This commit is contained in:
RunasSudo 2023-01-05 22:11:55 +11:00
parent 83a43a1bef
commit bf86d72175
Signed by: RunasSudo
GPG Key ID: 7234E476BF21C61A
3 changed files with 108 additions and 21 deletions

View File

@ -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())

View File

@ -14,22 +14,34 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
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
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), '$')
from datetime import datetime
def eofy_date(dt=None):
"""Get the datetime for the end of the financial year"""
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
)

55
austax/tax_tables.py Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
# 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)
]
}