From a0936fd8ad9eee7bd055b983e2a25e54ecf53bdc Mon Sep 17 00:00:00 2001 From: tim Date: Thu, 5 Nov 2020 12:32:31 +0200 Subject: [PATCH] Script to apply taxes to an amount. Updated for 2021 tax year rates. --- taxcalc.py | 93 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 93 insertions(+) create mode 100755 taxcalc.py diff --git a/taxcalc.py b/taxcalc.py new file mode 100755 index 0000000..183b288 --- /dev/null +++ b/taxcalc.py @@ -0,0 +1,93 @@ +#!/usr/bin/python3 +''' Calculate SARS tax on payments ''' + +import locale +import math +import re +import sys + +tax_rates = [ + dict( + rate = 18, + low = 1, + ), dict( + rate = 26, + low = 205_901, + ), dict( + rate = 31, + low = 321_601, + ), dict( + rate = 36, + low = 445_101, + ), dict( + rate = 39, + low = 584_201, + ), dict( + rate = 41, + low = 744_801, + ), dict( + rate = 45, + low = 1_577_301, + ), +] +for r in tax_rates: + idx = tax_rates.index(r) + if idx < len(tax_rates) - 1: + high = tax_rates[idx+1].get('low') + r['high'] = high - 1 + else: + r['high'] = False + +def error(e): + if e: + print(e); + print("{0}: Please enter the annual or monthly amount before tax".format(sys.argv[0])) + sys.exit() + +locale.setlocale( locale.LC_MONETARY, 'en_ZA.UTF-8' ) +locale._override_localeconv = {'mon_thousands_sep': ' ', 'mon_decimal_point': ','} +currency = locale.localeconv() + +if len(sys.argv) < 2: + error() +elif isinstance(sys.argv[1], str): + try: + amount = "".join(sys.argv[1:]).strip(currency.get('currency_symbol')) + amount = re.sub('[,.](\d{2})$','.\g<1>', amount) # convert decimal separator to . + amount = re.sub('[ ,](\d{3})','\g<1>', amount) # convert thousands separator to '' + amount = locale.atof(amount) + amount = int(amount) + except Exception as e: + error(e) +else: + error() + +''' Are we dealing with a monthly amount or annual amount? + Assume anything over R100k is annual. + If earning more than 100k per month, convert to --month/--year commandline option +''' +if amount > 100_000: + is_annual = True +else: + is_annual = False + print("Notice: value {} determined to be per month, dividing tax rates by 12".format(amount)) + +total_tax = 0 +for r in tax_rates: + low = (r.get('low') - 1) + high = r.get('high') + rate = r.get('rate') + if not is_annual: + low = math.ceil(low/12) + high = math.ceil(high/12) + if amount >= low: + bracket = amount + if high and amount > high: + bracket = high + current = math.ceil(bracket - low) + total_tax += math.ceil(current * rate / 100) + print(" {:>6} - {:>6} = {:>6} / {} = {:>6}".format(bracket, low, current, rate, total_tax)) + if amount < high: + break + +print("\nTotal tax on {} is {}".format(locale.currency(amount, grouping=True), locale.currency(total_tax, grouping=True)))