# import
import os
import json
import pandas as pd
import numpy as np
import copy
import plotly.graph_objects as go
from datetime import datetime
from openfisca_core.simulation_builder import SimulationBuilder
from leximpact_survey_scenario.leximpact_tax_and_benefit_system import leximpact_tbsPARAMETRES
# niveaux de revenus (en pss ou en €)
x_min_enpss = 0
x_max_enpss = 10
x_pas_enpss = 0.25
pss_mensuel = 3864
x_min = x_min_enpss * pss_mensuel
x_max = x_max_enpss * pss_mensuel
x_pas = x_pas_enpss * pss_mensuel
x_range = np.arange(x_min, x_max * 12 + 12, x_pas * 12)
x_range_length = len(x_range)# liste des cotisations
liste_cotisations = [
# revenus
"salaire_de_base",
"traitement_indiciaire_brut",
# cotisations salariales (contributives)
# prive
"agirc_arrco_salarie",
"apec_salarie",
"contribution_equilibre_general_salarie",
"contribution_equilibre_technique_salarie",
"vieillesse_deplafonnee_salarie",
"vieillesse_plafonnee_salarie",
# public
"ircantec_salarie",
"pension_salarie",
"rafp_salarie",
# cotisations salariales (non contributives)
"mmid_salarie",
# cotisations patronales (contributives)
"ags",
"agirc_arrco_employeur",
"apec_employeur",
"chomage_employeur",
"contribution_equilibre_general_employeur",
"contribution_equilibre_technique_employeur",
"vieillesse_deplafonnee_employeur",
"vieillesse_plafonnee_employeur",
"fonds_emploi_hospitalier",
"ircantec_employeur",
"pension_employeur",
"rafp_employeur",
# cotisations patronales (non contributives)
"ati_atiacl",
"penibilite",
"accident_du_travail",
"contribution_solidarite_autonomie",
"famille",
"mmid_employeur",
"taxe_salaires",
"forfait_social",
# autres
"csg_deductible_salaire",
"csg_imposable_salaire",
"crds_salaire",
"fnal_contribution",
"versement_transport",
"taxe_apprentissage",
"contribution_formation_professionnelle",
"financement_organisations_syndicales",
]# chemin vers les cas-types
path_cas_types = os.path.join(os.getcwd(), "cas_types")# liste des cas-types
# prive
liste_cas_types_salaire = [
"cot_01_salarie_prive_non_cadre.json",
"cot_02_salarie_prive_cadre.json",
"cot_07_salarie_public_non_titulaire.json",
]
# public
liste_cas_types_traitement = [
"cot_03_fonctionnaire_etat.json",
"cot_04_fonctionnaire_militaire.json",
"cot_05_fonctionnaire_territoriale.json",
"cot_06_fonctionnaire_hospitaliere.json",
]FONCTIONS
def calculer_cotisations(liste_cas_types, liste_cotisations, revenu):
for i_cas_type in range(len(liste_cas_types)):
# importer le cas-type
cas_types = os.path.join(path_cas_types, liste_cas_types[i_cas_type])
with open(cas_types) as f:
temp = json.load(f)
if temp.get("sliders"):
del temp["sliders"]
del temp["description"], temp["linked_variables"], temp["title"]
# duplication du cas type en fonction du revenus
list_cas_types = list()
# boucle revenus
for i in x_range:
# print(i)
temp2 = copy.deepcopy(temp)
temp2["individus"]["Adulte 1"][revenu] = {
"2023": i,
"2024": i,
"2025": i,
}
list_cas_types.append(temp2)
data_frame = pd.DataFrame(list_cas_types)
# cas-type
cas_type = dict(
data_frame.iloc[2][["familles", "foyers_fiscaux", "individus", "menages"]]
)
# pour chaque niveau de revenus on calcul le détail
donnees = pd.DataFrame()
for i in range(x_range_length):
cas_type = dict(
data_frame.iloc[i][
["familles", "foyers_fiscaux", "individus", "menages"]
]
)
simulation = SimulationBuilder()
simulation = simulation.build_from_entities(leximpact_tbs, cas_type)
indiv = dict()
for variable in liste_cotisations:
indiv[variable] = [simulation.calculate(variable, "2025-01")[0]]
donnees = pd.concat([donnees, pd.DataFrame(indiv)], axis=0)
# cotisations en positif
donnees = donnees.abs()
# colonne pss
donnees["pss"] = donnees.loc[:, revenu] / pss_mensuel
# total des cotisations sariales et employeur
donnees["total_cotis_salarie"] = 0
for j in np.arange(0, len(donnees.columns) - 1, 1):
if "salarie" in donnees.columns[j]:
if donnees.columns[j] in donnees.columns:
donnees["total_cotis_salarie"] = (
donnees["total_cotis_salarie"] + donnees[donnees.columns[j]]
)
autres_cotis_salariales = [
"csg_deductible_salaire",
"csg_imposable_salaire",
"crds_salaire",
]
for i_cotis_sal in np.arange(0, len(autres_cotis_salariales) - 1, 1):
if autres_cotis_salariales[i_cotis_sal] in donnees.columns:
donnees["total_cotis_salarie"] = (
donnees["total_cotis_salarie"]
+ donnees[autres_cotis_salariales[i_cotis_sal]]
)
# employeur
donnees["total_cotis_employeur"] = 0
for j in np.arange(0, len(donnees.columns) - 1, 1):
if "employeur" in donnees.columns[j]:
if donnees.columns[j] in donnees.columns:
donnees["total_cotis_employeur"] = (
donnees["total_cotis_employeur"]
+ donnees[donnees.columns[j]]
)
autres_cotis_patronales = [
"forfait_social",
"ags",
"contribution_solidarite_autonomie",
"famille",
"accident_du_travail",
"fnal_contribution",
"taxe_apprentissage",
"contribution_formation_professionnelle",
"financement_organisations_syndicales",
]
for i_cotis_pat in np.arange(0, len(autres_cotis_patronales) - 1, 1):
if autres_cotis_patronales[i_cotis_pat] in donnees.columns:
donnees["total_cotis_employeur"] = (
donnees["total_cotis_employeur"]
+ donnees[autres_cotis_patronales[i_cotis_pat]]
)
# cotisations totales
donnees["total_cotis"] = (
donnees["total_cotis_salarie"] + donnees["total_cotis_employeur"]
)
# ordre des colonnes
col = donnees.pop("total_cotis_salarie")
donnees.insert(0, col.name, col)
col = donnees.pop("total_cotis_employeur")
donnees.insert(0, col.name, col)
col = donnees.pop("total_cotis")
donnees.insert(0, col.name, col)
col = donnees.pop(revenu)
donnees.insert(0, col.name, col)
col = donnees.pop("pss")
donnees.insert(0, col.name, col)
# supprimer les colonnes de zéros
# donnees = donnees.loc[:, donnees.any()]
# arrondir
# donnees = round(donnees, 4)
# enregistrer donnees cas-type
# print('enregistrer donnees')
globals()["donnees_" + liste_cas_types[i_cas_type][4:6]] = donnees
print("donnees_" + liste_cas_types[i_cas_type][4:6])def dessiner_graphique(donnees, revenu, affichage_en_taux):
donnees_graph = copy.deepcopy(donnees)
# montants (False) ou taux (True) :
if affichage_en_taux:
for col in donnees_graph.columns:
if col != ("pss"):
if col != (revenu):
donnees_graph[col] = donnees_graph[col] / donnees_graph[revenu]
# globals()["donnees_taux"] = donnees_graph
# graph
# colonnes hors de la boucle add.trace
col_graph_loop = donnees_graph.columns.difference(
[
revenu,
"pss",
"total_cotis_salarie",
"total_cotis_employeur",
"total_cotis",
],
sort=False,
)
fig = go.Figure(
layout=go.Layout(
template="plotly_white",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis,
line=dict(color="black", dash="dashdot"),
name="total_cotis",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis_salarie,
line=dict(color="blue", dash="dashdot"),
name="total_cotis_salarie",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis_employeur,
line=dict(color="red", dash="dashdot"),
name="total_cotis_employeur",
)
)
# colonne dans la boucle add.trace
print(col_graph_loop)
for col in col_graph_loop:
# print(col)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss, y=donnees_graph[col], mode="lines", name=col
)
)
fig.update_layout(
showlegend=True,
title={
"text": "Cotisations (mensuelles)",
"y": 0.9, # new
"x": 0.5,
"xanchor": "center",
"yanchor": "top", # new
},
xaxis_title="PSS",
)
fig.show()def comparer_donnees(donnees_A, donnees_B, revenu, affichage_en_taux):
donnees_A_graph = copy.deepcopy(donnees_A)
donnees_B_graph = copy.deepcopy(donnees_B)
# montants (False) ou taux (True) :
if affichage_en_taux:
for col in donnees_A_graph.columns:
if col != ("pss"):
if col != (revenu):
donnees_A_graph[col] = (
donnees_A_graph[col] / donnees_A_graph[revenu]
)
for col in donnees_B_graph.columns:
if col != ("pss"):
if col != (revenu):
donnees_B_graph[col] = (
donnees_B_graph[col] / donnees_B_graph[revenu]
)
# globals()["donnees_taux"] = donnees_graph
# soustraire donnees A et donnees B
donnees_C = donnees_B_graph.subtract(donnees_A_graph, fill_value=0)
donnees_graph = copy.deepcopy(donnees_C)
donnees_graph[revenu] = donnees_graph[revenu]
donnees_graph["pss"] = donnees_A_graph["pss"]
# supprimer les colonnes avec uniquement zéros
donnees_graph = donnees_graph.loc[:, (donnees_graph**2).sum() != 0]
# graph
# colonnes hors de la boucle add.trace
col_graph_loop = donnees_graph.columns.difference(
[
revenu,
"pss",
"total_cotis_salarie",
"total_cotis_employeur",
"total_cotis",
],
sort=False,
)
# creer colonnes totaux zero si inexistantes
if "total_cotis" not in donnees_graph.columns:
donnees_graph["total_cotis"] = 0
if "total_cotis_salarie" not in donnees_graph.columns:
donnees_graph["total_cotis_salarie"] = 0
if "total_cotis_employeur" not in donnees_graph.columns:
donnees_graph["total_cotis_employeur"] = 0
fig = go.Figure(
layout=go.Layout(
template="plotly_white",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis,
line=dict(color="black", dash="dashdot"),
name="total_cotis",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis_salarie,
line=dict(color="blue", dash="dashdot"),
name="total_cotis_salarie",
)
)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss,
y=donnees_graph.total_cotis_employeur,
line=dict(color="red", dash="dashdot"),
name="total_cotis_employeur",
)
)
# colonne dans la boucle add.trace
print(col_graph_loop)
for col in col_graph_loop:
# print(col)
fig.add_trace(
go.Scatter(
x=donnees_graph.pss, y=donnees_graph[col], mode="lines", name=col
)
)
fig.update_layout(
showlegend=True,
title={
"text": "Cotisations (mensuelles)",
"y": 0.9, # new
"x": 0.5,
"xanchor": "center",
"yanchor": "top", # new
},
xaxis_title="PSS",
)
# enregistrer donnees
globals()["donnees_A"] = donnees_A
globals()["donnees_B"] = donnees_B
globals()["donnees_graph"] = donnees_graph
fig.show()def exporter(donnees):
# créer le nom du fichier avec date et heure
exportfile_name = datetime.today().strftime("%Y-%m-%d %H:%M:%S")
exportfile_name = exportfile_name.replace(" ", "-")
exportfile_name = exportfile_name.replace(":", "-")
exportfile_name = exportfile_name + "-output"
exportfile_name = exportfile_name + ".xlsx"
exportfile_name = "output" + "/" + exportfile_name
# print(exportfile_name)
# exporter
donnees.to_excel(exportfile_name)CALCULS DES COTISATIONS
# calculer les cas-type (donnees)
calculer_cotisations(
liste_cas_types=liste_cas_types_salaire,
liste_cotisations=liste_cotisations,
revenu="salaire_de_base",
)
calculer_cotisations(
liste_cas_types=liste_cas_types_traitement,
liste_cotisations=liste_cotisations,
revenu="traitement_indiciaire_brut",
)# GRAPHIQUES
# cotisations non nulles
selection = copy.deepcopy(donnees_01)
donnees_check = selection.loc[:, selection.any()]
donnees_check.columnsdessiner_graphique(donnees=donnees_07, revenu="salaire_de_base", affichage_en_taux=True)comparer_donnees(
donnees_A=donnees_01,
donnees_B=donnees_02,
revenu="salaire_de_base",
affichage_en_taux=False,
)EXPORTER
exporter(donnees_C)BROUILLONS
# arbre de parametres
# simulation.tax_benefit_system.parameters.children.keys
# for k in simulation.tax_benefit_system.parameters.cotsoc.cotisations_salarie.children.keys():
# print(k)# retrouver les parametres
# simulation.tax_benefit_system.parameters.chomage.allocations_assurance_chomage.afd