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.