11. Exercice d’application : version 3▲
Cette nouvelle version introduit deux changements :
-
les données nécessaires au calcul de l'impôt et fournies par l'administration fiscale sont placées dans un fichier jSON [admindata.json] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
{
"limites": [9964, 27519, 73779, 156244, 0],
"coeffR": [0, 0.14, 0.3, 0.41, 0.45],
"coeffN": [0, 1394.96, 5798, 13913.69, 20163.45],
"PLAFOND_QF_DEMI_PART": 1551,
"PLAFOND_REVENUS_CELIBATAIRE_POUR_REDUCTION": 21037,
"PLAFOND_REVENUS_COUPLE_POUR_REDUCTION": 42074,
"VALEUR_REDUC_DEMI_PART": 3797,
"PLAFOND_DECOTE_CELIBATAIRE": 1196,
"PLAFOND_DECOTE_COUPLE": 1970,
"PLAFOND_IMPOT_COUPLE_POUR_DECOTE": 2627,
"PLAFOND_IMPOT_CELIBATAIRE_POUR_DECOTE": 1595,
"ABATTEMENT_DIXPOURCENT_MAX": 12502,
"ABATTEMENT_DIXPOURCENT_MIN": 437
}
-
les résultats du calcul de l'impôt seront eux également placés dans un fichier jSON [résultats.json] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
[
{
"marié": "oui",
"enfants": 2,
"salaire": 55555,
"impôt": 2814,
"surcôte": 0,
"décôte": 0,
"réduction": 0,
"taux": 0.14
},
{
"marié": "oui",
"enfants": 2,
"salaire": 50000,
"impôt": 1384,
"surcôte": 0,
"décôte": 384,
"réduction": 347,
"taux": 0.14
},
…
{
"marié": "oui",
"enfants": 3,
"salaire": 200000,
"impôt": 42842,
"surcôte": 17283,
"décôte": 0,
"réduction": 0,
"taux": 0.41
}
]
11-1. Le script de configuration [config.py]▲
Le script de configuration sera le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
def configure():
import os
# chemin absolu du dossier de ce script
script_dir = os.path.dirname(os.path.abspath(__file__))
# dépendances de l'application
absolute_dependencies = [
f"{script_dir}/../shared",
]
# configuration de l'application
config = {
# chemin absolu du fichier des contribuables
"taxpayersFilename": f"{script_dir}/../data/taxpayersdata.txt",
# chemin absolu du fichier des résultats
"resultsFilename": f"{script_dir}/../data/résultats.json",
# chemin absolu du fichier des données de l'administration fiscale
"admindataFilename": f"{script_dir}/../data/admindata.json"
}
# mise à jour du syspath
from myutils import set_syspath
set_syspath(absolute_dependencies)
# on rend la config
return config
-
ligne 8 : on met le dossier [shared] dans le Python Path. Ce dossier contient le module [impôts_module_02] utilisé par le script principal ;
11-2. Script principal [main.py]▲
Le script principal de la version 3 est le suivant :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
# on configure l'application
import config
config = config.configure()
# le syspath est configuré - on peut faire les imports
from impôts_module_02 import calcul_impôt, get_admindata, get_taxpayers_data, record_results_in_json_file
# fichier des contribuables
taxpayers_filename = config['taxpayersFilename']
# fichier des résultats
results_filename = config['resultsFilename']
# fichier des données de l'administration fiscale
admindata_filename = config['admindataFilename']
# code
try:
# lecture des données de l'administration fiscale
admindata = get_admindata(admindata_filename)
# lecture des données contribuables
taxpayers = get_taxpayers_data(taxpayers_filename)
# liste des résultats
results = []
# on calcule l'impôt des contribuables
for taxpayer in taxpayers:
# le calcul de l'impôt renvoie un dictionnaire de clés
# ['marié', 'enfants', 'salaire', 'impôt', 'surcôte', 'décôte', 'réduction', 'taux']
result = calcul_impôt(admindata, taxpayer['marié'], taxpayer['enfants'], taxpayer['salaire'])
# le dictionnaire est ajouté à la liste des résultats
results.append(result)
# on enregistre les résultats
record_results_in_json_file(results_filename, results)
except BaseException as erreur:
# il peut y avoir différentes erreurs : absence de fichier, contenu de fichier incorrect
# on affiche l'erreur et on quitte l'application
print(f"L'erreur suivante s'est produite : {erreur}]\n")
finally:
print("Travail terminé...")
Notes
-
lignes 2-4, on configure l’application notamment son Python Path ;
-
ligne 7 : on importe les fonctions dont on a besoin dans [main.py] ;
-
lignes 9-14 : les noms des fichiers exploités par l’application sont récupérés dans la configuration ;
-
le script principal de la version 3 présente trois différences vis à vis de celui des versions 1 et 2 :
-
ligne 21 : les données de l'administration fiscale sont prises dans le fichier jSON [./data/admindata.json] ;
-
ligne 32 : les résultats du calcul de l'impôt sont placés dans le fichier jSON [./data/résultats.json] ;
-
ligne 7 : les fonctions de la version 3 sont trouvées dans le module [impots.modules.impôts_module_02] ;
-
11-3. Le module [impots.v02.modules.impôts_module_02]▲
Le module [impots.v02.modules.impôts_module_02] a la structure suivante :
-
on retrouve dans le module des fonctions déjà présentes dans le module utilisé par la version 1 avec cependant une différence. Lorsque le module de la version 2 reprend une fonction présente dans le module de la version 1, elle le fait avec un paramètre supplémentaire : [adminData] (lignes 29, 51, 77, 127). Ce paramètre représente le dictionnaire des données fiscales issues du fichier jSON [adminData.json]. Dans le module de la version 1, ces données n'avaient pas besoin d'être passées aux fonctions car elles étaient définies globalement à celles-ci ce qui faisait que les fonctions les connaissaient ;
11-4. Lecture des données de l'administration fiscale▲
La fonction [get_admindata] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
# lecture des données de l'administration fiscale dans un fichier jSON
# ----------------------------------------
def get_admindata(admindata_filename: str) -> dict:
# lecture des données de l'administration fiscale
# on laisse remonter les éventuelles exceptions : absence du fichier, contenu jSON incorrect
file = None
try:
# ouverture du fichier jSON en lecture
file = codecs.open(admindata_filename, "r", "utf8")
# transfert du contenu dans un dictionnaire
admin_data = json.load(file)
# on rend le résultat
return admin_data
finally:
# fermeture du fichier s'il a été ouvert
if file:
file.close()
-
ligne 9 : on récupère le dictionnaire image du fichier jSON lu ;
11-5. Enregistrement des résultats▲
La fonction [record_results_in_json_file] est la suivante :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
# écriture des résultats dans un fichier jSON
# ----------------------------------------
def record_results_in_json_file(results_filename: str, results: list):
file = None
try:
# ouverture du fichier des résultats
file = codecs.open(results_filename, "w", "utf8")
# écriture en bloc
json.dump(results, file, ensure_ascii=False)
finally:
# on ferme le fichier s'il a été ouvert
if file:
file.close()
-
ligne 7 : on crée un fichier encodé en UTF-8 ;
-
ligne 9 : on écrit la liste [results] dans le fichier jSON. Les caractères UTF-8 ne sont pas échappés (ensure_ascii=False) ;
11-6. Modification des fonctions▲
Certaines fonctions reçoivent désormais un paramètre [admin_data] supplémentaire. Cela modifie un peu leur écriture. Prenons par exemple la fonction [calcul_impôt] :
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
# calcul de l'impôt - étape 1
# ----------------------------------------
def calcul_impôt(admin_data: dict, marié: str, enfants: int, salaire: int) -> dict:
# marié : oui, non
# enfants : nombre d'enfants
# salaire : salaire annuel
# limites, coeffr, coeffn : les tableaux des données permettant le calcul de l'impôt
#
# calcul de l'impôt avec enfants
result1 = calcul_impôt_2(admin_data, marié, enfants, salaire)
impot1 = result1["impôt"]
# calcul de l'impôt sans les enfants
if enfants != 0:
result2 = calcul_impôt_2(admin_data, marié, 0, salaire)
impot2 = result2["impôt"]
# application du plafonnement du quotient familial
if enfants < 3:
# PLAFOND_QF_DEMI_PART euros pour les 2 premiers enfants
impot2 = impot2 - enfants * admin_data['plafond_qf_demi_part']
else:
# PLAFOND_QF_DEMI_PART euros pour les 2 premiers enfants, le double pour les suivants
impot2 = impot2 - 2 * admin_data['plafond_qf_demi_part'] - (enfants - 2) * 2 * admin_data[
'plafond_qf_demi_part']
else:
impot2 = impot1
result2 = result1
# on prend l'impôt le plus fort avec le taux et la surcôte qui vont avec
if impot1 > impot2:
impot = impot1
taux = result1["taux"]
surcôte = result1["surcôte"]
else:
surcôte = impot2 - impot1 + result2["surcôte"]
impot = impot2
taux = result2["taux"]
# calcul d'une éventuelle décôte
décôte = get_décôte(admin_data, marié, salaire, impot)
impot -= décôte
# calcul d'une éventuelle réduction d'impôts
réduction = get_réduction(admin_data, marié, salaire, enfants, impot)
impot -= réduction
# résultat
return {"marié": marié, "enfants": enfants, "salaire": salaire, "impôt": math.floor(impot), "surcôte": surcôte,
"décôte": décôte, "réduction": réduction, "taux": taux}
Notes
-
là où [calcul_impôt] appelle d'autres fonctions, elle passe [admin_data] en 1er paramètre (lignes 10, 14, 39, 42) ;
-
là où [calcul_impôt] utilise des constantes fiscales, elle passe désormais par le dictionnaire [admin_data] (lignes 19, 22) ;
Toutes les fonctions recevant [admin_data] comme paramètre, subissent ces mêmes types de modifications.
11-7. Résultats▲
Les résultats obtenus sont ceux présentés au début du paragraphe 8.3.




