4. Application Exercise – [Tax Calculation]
4.1. The problem
We aim to write a program to calculate a taxpayer’s tax. We consider the simplified case of a taxpayer who has only their salary to report:
- we calculate the number of tax brackets for the employee nbParts = nbChildren / 2 + 1 if they are unmarried, nbChildren / 2 + 2 if they are married, where nbChildren is the number of children;
- we calculate their taxable income R = 0.72 * S, where S is their annual salary;
- we calculate their family coefficient Q = R/N;
- we calculate their tax I based on the following data.
12620.0 0 0
13190 0.05 631
15640 0.1 1290.5
24740 0.15 2072.5
31810 0.2 3309.5
39970 0.25 4900
48360 0.3 6898.5
55790 0.35 9316.5
92970 0.4 12106
127860 0.45 16754.5
151250 0.50 23147.5
172040 0.55 30710
195000 0.60 39312
0 0.65 49062
Each row has 3 fields. To calculate tax I, find the first row where QF <= field1. For example, if QF = 30,000, the row found will be:
Tax I is then equal to 0.15*R - 2072.5*nbParts. If QF is such that the condition QF<=field1 is never met, then the coefficients from the last row are used. Here:
which gives tax I = 0.65*R - 49062*nbParts.
4.2. Version with lists
| # -*- coding=utf-8 -*-
import math, sys
def cutNewLineChar(ligne):
# delete the end-of-line mark if it exists
l=len(ligne);
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l]);
# --------------------------------------------------------------------------
def calculImpots(marie,enfants,salaire,limites,coeffR,coeffN):
# married: yes, no
# children: number of children
# salary: annual salary
# number of shares
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# an additional 1/2 share if at least 3 children
if enfants>=3:
nbParts+=0.5
# taxable income
revenuImposable=0.72*salaire
# family quotient
quotient=revenuImposable/nbParts
# is set at the end of the limit table to stop the following loop
limites[len(limites)-1]=quotient
# tAX CALCULATION
i=0
while(quotient>limites[i]):
i=i+1
# because we've placed quotient at the end of the limit array, the previous loop
# cannot go beyond the limit table
# now we can calculate the tax
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definition of constants
DATA="data.txt"
RESULTATS="resultats.txt"
limites=[12620,13190,15640,24740,31810,39970,48360,55790,92970,127860,151250,172040,195000,0]
coeffR=[0,0.05,0.1,0.15,0.2,0.25,0.3,0.35,0.4,0.45,0.5,0.55,0.6,0.65]
coeffN=[0,631,1290.5,2072.5,3309.5,4900,6898.5,9316.5,12106,16754.5,23147.5,30710,39312,49062]
# reading data
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# open results file
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# use the current line of the data file
ligne=data.readline()
while(ligne != ''):
# remove any end-of-line marker
ligne=cutNewLineChar(ligne)
# we retrieve the 3 fields married:children:salary which form the line
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# tax calculation
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# enter the result
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# a new line is read
ligne=data.readline()
# close files
data.close()
resultats.close()
|
Results
The data file data.txt:
oui,2,200000
non,2,200000
oui,3,200000
non,3,200000
oui,5,50000
non,0,3000000
The results.txt file containing the results:
oui:2:200000:22504.0
non:2:200000:33388.0
oui:3:200000:16400.0
non:3:200000:22504.0
oui:5:50000:0.0
non:0:3000000:1354938.0
4.3. Version with text files
In the previous example, the data needed to calculate the tax was found in three lists. From now on, it will be retrieved from a text file:
| 12620:13190:15640:24740:31810:39970:48360:55790:92970:127860:151250:172040:195000:0
0:0.05:0.1:0.15:0.2:0.25:0.3:0.35:0.4:0.45:0.5:0.55:0.6:0.65
0:631:1290.5:2072.5:3309.5:4900:6898.5:9316.5:12106:16754.5:23147.5:30710:39312:49062
|
| # -*- coding=utf-8 -*-
import math,sys
# --------------------------------------------------------------------------
def getTables(IMPOTS):
# IMPOTS: the name of the file containing data from the limit tables, coeffR, coeffN
# does the IMPOTS file exist?
try:
data=open(IMPOTS,"r")
except:
return ("Le fichier IMPOTS n'existe pas",0,0,0)
# create the 3 lists - assume the lines are syntactically correct
# -- line 1
ligne=data.readline()
if ligne== '':
return ("La premiere ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
limites=cutNewLineChar(ligne).split(":")
for i in range(len(limites)):
limites[i]=int(limites[i])
# -- line 2
ligne=data.readline()
if ligne== '':
return ("La deuxieme ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
coeffR=cutNewLineChar(ligne).split(":")
for i in range(len(coeffR)):
coeffR[i]=float(coeffR[i])
# -- line 3
ligne=data.readline()
if ligne== '':
return ("La troisieme ligne du fichier {0} est absente".format(IMPOTS),0,0,0)
coeffN=cutNewLineChar(ligne).split(":")
for i in range(len(coeffN)):
coeffN[i]=float(coeffN[i])
# end
return ("",limites,coeffR,coeffN)
# --------------------------------------------------------------------------
def cutNewLineChar(ligne):
# delete the end-of-line mark if it exists
l=len(ligne);
while(ligne[l-1]=="\n" or ligne[l-1]=="\r"):
l-=1
return(ligne[0:l]);
# --------------------------------------------------------------------------
def calculImpots(marie,enfants,salaire,limites,coeffR,coeffN):
# married: yes, no
# children: number of children
# salary: annual salary
# number of shares
marie=marie.lower()
if(marie=="oui"):
nbParts=float(enfants)/2+2
else:
nbParts=float(enfants)/2+1
# an additional 1/2 share if at least 3 children
if enfants>=3:
nbParts+=0.5
# taxable income
revenuImposable=0.72*salaire
# family quotient
quotient=revenuImposable/nbParts
# is set at the end of the limit array to stop the loop that follows
limites[len(limites)-1]=quotient
# tAX CALCULATION
i=0
while(quotient>limites[i]):
i=i+1
# because we've placed quotient at the end of the limit array, the previous loop
# cannot exceed the limits of the board
# now we can calculate the tax
return math.floor(revenuImposable*coeffR[i]-nbParts*coeffN[i])
# ------------------------------------------------ main
# definition of constants
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# the data required to calculate the tax has been placed in the IMPOTS file
# one line per table in the form
# val1:val2:val3,...
(erreur,limites,coeffR,coeffN)=getTables(IMPOTS)
# was there a mistake?
if(erreur):
print "{0}\n".format(erreur)
sys.exit()
# reading data
try:
data=open(DATA,"r")
except:
print "Impossible d'ouvrir en lecture le fichier des donnees [DATA]"
sys.exit()
# open results file
try:
resultats=open(RESULTATS,"w")
except:
print "Impossible de creer le fichier des résultats [RESULTATS]"
sys.exit()
# use the current line of the data file
ligne=data.readline()
while(ligne != ''):
# remove any end-of-line marker
ligne=cutNewLineChar(ligne)
# we retrieve the 3 fields married:children:salary which form the line
(marie,enfants,salaire)=ligne.split(",")
enfants=int(enfants)
salaire=int(salaire)
# tax calculation
impot=calculImpots(marie,enfants,salaire,limites,coeffR,coeffN)
# enter the result
resultats.write("{0}:{1}:{2}:{3}\n".format(marie,enfants,salaire,impot))
# a new line is read
ligne=data.readline()
# close files
data.close()
resultats.close()
|
Results
Same as before.
Program (taxes_02b)
The getImpots method above (lines 6–36) returns a tuple (error, limits, coeffR, coeffN), where error is an error message that may be empty. You may want to handle these error cases using exceptions. In that case:
- if an error occurs, the getImpots method throws an exception;
- otherwise, it returns the tuple (limits, coeffR, coeffN).
The code for the getImpots method then becomes the following:
| def getTables(IMPOTS):
# IMPOTS: the name of the file containing data from the limit tables, coeffR, coeffN
# does file IMPOTS exist? if not, exception IOError is thrown in this case
data=open(IMPOTS,"r")
# create the 3 lists - assume the lines are syntactically correct
# -- line 1
ligne=data.readline()
if ligne== '':
raise RuntimeError ("La premiere ligne du fichier {0} est absente".format(IMPOTS))
limites=cutNewLineChar(ligne).split(":")
for i in range(len(limites)):
limites[i]=int(limites[i])
# -- line 2
ligne=data.readline()
if ligne== '':
raise RuntimeError ("La deuxieme ligne du fichier {0} est absente".format(IMPOTS))
coeffR=cutNewLineChar(ligne).split(":")
for i in range(len(coeffR)):
coeffR[i]=float(coeffR[i])
# -- line 3
ligne=data.readline()
if ligne== '':
raise RuntimeError ("La troisieme ligne du fichier {0} est absente".format(IMPOTS))
coeffN=cutNewLineChar(ligne).split(":")
for i in range(len(coeffN)):
coeffN[i]=float(coeffN[i])
# end
return (limites,coeffR,coeffN)
|
- line 4: we do not check if opening the file fails. If it fails, an IOError exception is raised. We let this propagate to the calling program;
- lines 9–10: We raise an exception to indicate that the expected first line is missing. We use a predefined RuntimeError exception. We can also create our own exception classes. We’ll do that a little later;
- lines 16–17 and 23–24: We do the same thing for lines 2 and 3.
The main program code then becomes the following:
# ------------------------------------------------ main
# définition des constantes
DATA="data.txt"
RESULTATS="resultats.txt"
IMPOTS="impots.txt"
# les données nécessaires au calcul de l'impôt ont été placées dans le fichier IMPOTS
# à raison d'une ligne par tableau sous la forme
# val1:val2:val3,...
erreur=False
try:
(limites,coeffR,coeffN)=getTables(IMPOTS)
except IOError, message:
erreur=True
except RuntimeError, message:
erreur=True
# y-a-t-il eu une erreur ?
if(erreur):
print "L'erreur suivante s'est produite : {0}\n".format(message)
sys.exit()
# lecture des données
...
- lines 12-17: we call the getImpots method and handle the two exceptions it may raise: IOError and RuntimeError. In this handling, we simply note that an error occurred;
- lines 20–22: we display the error message of the intercepted exception and terminate the program.