I'm currently writing a tax calculator, and I noticed that if I input an invalid tax code it looks like it gets rejected at first, but then the program seems to retain the initial inputs and loops through them in reverse order (like so):
[User Input] Gross Income: £32,000
[User Input] Tax Code: 32,000
Tax Code after input: 32,000
Failed letter check
Invalid input. Please enter your tax code.
[User Input] Tax Code: y
Tax Code after input: y
Passed letter check
Tax Code after letter check: y
Tax Code during dictionary match check (false): y
Failed dictionary match check
Invalid input. Please enter your tax code.
[User Input] Tax Code: 1257L
Tax Code after input: 1257L
Passed letter check
Tax Code after letter check: 1257L
Tax Code during dictionary match check (true): 1257L
Passed dictionary match check
Tax Code after dictionary match check: 1257L
Personal Allowance: 12570
Tax Letter: L
Tax Code after dictionary match check: y
Personal Allowance: 0
Tax Letter: Y
Tax Code after letter check: 32,000
Traceback (most recent call last):
File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 336, in <module>
salarycalc()
File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 243, in salarycalc
personal_allowance, tax_letter = get_tax_code()
File "C:\Users\Joshua.Riberio\Git\taxcalc\taxcalc.py", line 78, in get_tax_code
tax_letter = tax_code[tax_letter_index:].upper()
TypeError: slice indices must be integers or None or have an __index__ method
Here is my code:
def get_tax_code():
tax_code = input('Tax Code: ')
print('Tax Code after input:', tax_code)
tax_letter_index = ''
# Checking input contains a letter
for char in tax_code:
if char.upper() in alpha:
tax_letter_index = tax_code.index(char)
print('Passed letter check')
break
if tax_letter_index == '':
print('Failed letter check')
print('Invalid input. Please enter your tax code.')
get_tax_code()
print('Tax Code after letter check:', tax_code)
tax_letter = tax_code[tax_letter_index:].upper()
# Checking input has a key match in the tax_letters dictionary
if tax_letter not in tax_letters.keys():
print('Tax Code during dictionary match check (false):', tax_code)
print('Failed dictionary match check')
print('Invalid input. Please enter your tax code.')
get_tax_code()
elif tax_letter in tax_letters.keys():
print('Tax Code during dictionary match check (true):', tax_code)
print('Passed dictionary match check')
print('Tax Code after dictionary match check:', tax_code)
# Getting personal allowance from Tax Code
personal_allowance = tax_code[:tax_letter_index]
if personal_allowance == '':
personal_allowance = 0
else:
personal_allowance = int(personal_allowance) * 10
# Setting personal allowance exceptions for gross income over £100,000
if gross_income > 100000:
personal_allowance = set_personal_allowance - ((gross_income - 100000) / 2)
if personal_allowance < 0:
personal_allowance = 0
print('Personal Allowance:', personal_allowance)
print('Tax Letter:', tax_letter)
return personal_allowance, tax_letter
The excessive prints are just so I could see where the input was being changed. It seems as though after a successful run through, the code loops back up to the top using the previous invalid inputs, resulting in the code failing to run.
Can anybody see where I'm going wrong?
Notes
tax_letters
is a defined dictionary, will be updating this to pull from a selected CSV:
tax_letters = {
"L": "You’re entitled to the standard tax-free Personal Allowance",
"M": "Marriage Allowance: you’ve received a transfer of 10% of your partner’s Personal Allowance",
"N": "Marriage Allowance: you’ve transferred 10% of your Personal Allowance to your partner",
"T": "Your tax code includes other calculations to work out your Personal Allowance",
"0T": "Your Personal Allowance has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
"BR": "All your income from this job or pension is taxed at the basic rate (usually used if you’ve got more than one job or pension)",
"D0": "All your income from this job or pension is taxed at the higher rate (usually used if you’ve got more than one job or pension)",
"D1": "All your income from this job or pension is taxed at the additional rate (usually used if you’ve got more than one job or pension)",
"NT": "You’re not paying any tax on this income",
"S": "Your income or pension is taxed using the rates in Scotland",
"S0T": "Your Personal Allowance (Scotland) has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
"SBR": "All your income from this job or pension is taxed at the basic rate in Scotland (usually used if you’ve got more than one job or pension)",
"SD0": "All your income from this job or pension is taxed at the intermediate rate in Scotland (usually used if you’ve got more than one job or pension)",
"SD1": "All your income from this job or pension is taxed at the higher rate in Scotland (usually used if you’ve got more than one job or pension)",
"SD2": "All your income from this job or pension is taxed at the top rate in Scotland (usually used if you’ve got more than one job or pension)",
"C": "Your income or pension is taxed using the rates in Wales",
"C0T": "Your Personal Allowance (Wales) has been used up, or you’ve started a new job and your employer does not have the details they need to give you a tax code",
"CBR": "All your income from this job or pension is taxed at the basic rate in Wales (usually used if you’ve got more than one job or pension)",
"CD0": "All your income from this job or pension is taxed at the higher rate in Wales (usually used if you’ve got more than one job or pension)",
"CD1": "All your income from this job or pension is taxed at the additional rate in Wales (usually used if you’ve got more than one job or pension)"
}
gross_income
is defined in the main function:
def salarycalc():
screen_clear()
global gross_income
gross_income = input('Gross Income: £')
if ',' in gross_income:
gross_income = gross_income.replace(',', '')
if '£' in gross_income:
gross_income = gross_income.replace('£', '')
gross_income = float(gross_income)
set_personal_allowance
is a variable defined by a value in the 'tax_brackets' dictionary, again will be updating this later to pull from a selection of CSV's:
tax_brackets = {
'Personal Allowance': 12570,
'Basic': [0, 0.2],
'Higher': [50270, 0.4],
'Additional': [150000, 0.45]
}
set_personal_allowance = tax_brackets['Personal Allowance']
CodePudding user response:
So the main reason why your code probably didn't work was because of the repeated recursive calls. To make the approach simpler, I broke down the functions required into 2 verification checks. Please note that I've included a few sample data to check the program, you can ignore that accordingly.
The driver function get_tax_code
runs as long as two checks are True, this means that it will only stop asking for the input once the tax code is verified by both letter check and dictionary match.
def get_tax_code(tax_letters):
gross_income = 32000
set_personal_allowance = 12570
check1 = True
check2 = True
while check1 or check2:
tax_code = input('Tax Code: ')
print('Tax Code after input:', tax_code)
tax_letter_index = verify1(tax_code)
if tax_letter_index != -1:
check1 = False
tax_letter = tax_code[tax_letter_index:].upper()
print('Tax Code after letter check:', tax_code)
x = verify2(tax_letter, tax_letters)
if x:
print('Tax Code during dictionary match check (true):', tax_code)
print('Passed dictionary match check')
print('Tax Code after dictionary match check:', tax_code)
check2 = False
else:
print('Tax Code during dictionary match check (false):', tax_code)
print('Failed dictionary match check')
print('Invalid input. Please enter your tax code.')
else:
print('Failed letter check')
print('Invalid input. Please enter your tax code.')
# Getting personal allowance from Tax Code
personal_allowance = tax_code[:tax_letter_index]
if personal_allowance == '':
personal_allowance = 0
else:
personal_allowance = int(personal_allowance) * 10
# Setting personal allowance exceptions for gross income over £100,000
if gross_income > 100000:
personal_allowance = set_personal_allowance - ((gross_income - 100000) / 2)
if personal_allowance < 0:
personal_allowance = 0
print('Personal Allowance:', personal_allowance)
print('Tax Letter:', tax_letter)
These are the two helper functions which were earlier included in get_tax_code
.
The first function verify1()
takes care of the letter check while the second verify2()
handles dictionary match. The values returned from both these functions determines if the check values are updated which inturn decides if the user has to provide the tax_code
input again.
def verify1(tax_code):
tax_letter_index = ''
# Checking input contains a letter
for char in tax_code:
if char.upper().isalpha():
tax_letter_index = tax_code.index(char)
print('Passed letter check')
break
if tax_letter_index == '':
return -1
else:
return tax_letter_index
def verify2(tax_letter, tax_letters):
if tax_letter not in tax_letters.keys():
return False
elif tax_letter in tax_letters.keys():
return True
Output
Tax Code: 3200
Tax Code after input: 3200
Failed letter check
Invalid input. Please enter your tax code.
Tax Code: y
Tax Code after input: y
Passed letter check
Tax Code during dictionary match check (false): y
Failed dictionary match check
Invalid input. Please enter your tax code.
Tax Code: 1257L
Tax Code after input: 1257L
Passed letter check
Tax Code during dictionary match check (true): 1257L
Passed dictionary match check
Tax Code after dictionary match check: 1257L
Personal Allowance: 12570
Tax Letter: L