austax: Implement calculation of mandatory study loan repayment
This commit is contained in:
parent
83a43a1bef
commit
bf86d72175
@ -21,9 +21,7 @@ from drcr.database import db
|
|||||||
import drcr.plugins
|
import drcr.plugins
|
||||||
from drcr.webapp import app
|
from drcr.webapp import app
|
||||||
|
|
||||||
from .reports import tax_summary_report
|
from .reports import eofy_date, tax_summary_report
|
||||||
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
def plugin_init():
|
def plugin_init():
|
||||||
drcr.plugins.advanced_reports.append(('/tax/summary', 'Tax summary'))
|
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
|
tax_amount = report.by_id('total_tax').amount
|
||||||
|
|
||||||
# Get EOFY date
|
# Get EOFY date
|
||||||
dt = datetime.now().replace(month=6, day=30)
|
dt = eofy_date()
|
||||||
if dt < datetime.now():
|
|
||||||
dt = dt.replace(year=dt.year + 1)
|
|
||||||
|
|
||||||
# Estimated tax payable
|
# Estimated tax payable
|
||||||
transactions = [Transaction(
|
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
|
# Get trial balance
|
||||||
balancer = TrialBalancer()
|
balancer = TrialBalancer()
|
||||||
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
|
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
|
||||||
|
@ -14,22 +14,34 @@
|
|||||||
# You should have received a copy of the GNU Affero General Public License
|
# 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/>.
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
from drcr import AMOUNT_DPS
|
||||||
from drcr.database import db
|
from drcr.database import db
|
||||||
from drcr.models import AccountConfiguration, Amount, Transaction, TrialBalancer
|
from drcr.models import AccountConfiguration, Amount, Transaction, TrialBalancer
|
||||||
from drcr.reports import Calculated, Report, Section, Spacer, Subtotal, entries_for_kind
|
from drcr.reports import Calculated, Report, Section, Spacer, Subtotal, entries_for_kind
|
||||||
|
|
||||||
def base_income_tax(taxable_income):
|
from .tax_tables import base_tax, repayment_rates
|
||||||
income = taxable_income.quantity
|
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
def eofy_date(dt=None):
|
||||||
|
"""Get the datetime for the end of the financial year"""
|
||||||
|
|
||||||
if income <= 1820000:
|
if dt is None:
|
||||||
return Amount(0, '$')
|
dt = datetime.now()
|
||||||
if income <= 4500000:
|
|
||||||
return Amount(int((income - 1820000) * 0.19), '$')
|
dt_eofy = dt.replace(month=6, day=30)
|
||||||
if income <= 12000000:
|
if dt_eofy < dt:
|
||||||
return Amount(int(509200 + (income - 4500000) * 0.325), '$')
|
dt_eofy = dt_eofy.replace(year=dt.year + 1)
|
||||||
if income <= 18000000:
|
|
||||||
return Amount(int(2946700 + (income - 12000000) * 0.37), '$')
|
return dt_eofy
|
||||||
return Amount(int(5166700 + (income - 18000000) * 0.45), '$')
|
|
||||||
|
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):
|
def medicare_levy(taxable_income):
|
||||||
if taxable_income.quantity < 2920700:
|
if taxable_income.quantity < 2920700:
|
||||||
@ -37,10 +49,16 @@ def medicare_levy(taxable_income):
|
|||||||
|
|
||||||
return Amount(int(taxable_income.quantity * 0.02), '$')
|
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():
|
def tax_summary_report():
|
||||||
# Get trial balance
|
# Get trial balance
|
||||||
balancer = TrialBalancer()
|
balancer = TrialBalancer()
|
||||||
#balancer.apply_transactions(all_transactions())
|
|
||||||
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
|
balancer.apply_transactions(db.session.scalars(db.select(Transaction).options(db.selectinload(Transaction.postings))).all())
|
||||||
|
|
||||||
accounts = dict(sorted(balancer.accounts.items()))
|
accounts = dict(sorted(balancer.accounts.items()))
|
||||||
@ -100,7 +118,7 @@ def tax_summary_report():
|
|||||||
entries=[
|
entries=[
|
||||||
Calculated(
|
Calculated(
|
||||||
'Base income tax',
|
'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(
|
Calculated(
|
||||||
'Medicare levy',
|
'Medicare levy',
|
||||||
@ -116,8 +134,14 @@ def tax_summary_report():
|
|||||||
),
|
),
|
||||||
Spacer(),
|
Spacer(),
|
||||||
Calculated(
|
Calculated(
|
||||||
'Income tax payable (refundable)',
|
'Mandatory study loan repayment',
|
||||||
lambda _: report.by_id('total_tax').amount - report.by_id('paygw').amount,
|
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,
|
heading=True,
|
||||||
bordered=True
|
bordered=True
|
||||||
)
|
)
|
||||||
|
55
austax/tax_tables.py
Normal file
55
austax/tax_tables.py
Normal 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)
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user