Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Permet inversion au niveau mensuel #252

Merged
merged 11 commits into from
May 30, 2024
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Changelog

### 3.4.0 [#252](https://github.com/openfisca/openfisca-france-data/pull/252)

* New features
- Rend l'inversion plus robuste pour permettre de la faire de manière mois par mois
- En particulier, donne un seuil d'exonération sur le chômage net dépendant de la période.
- Ajoute dans l'inversion la prise en compte de l'indemnité compensatrice de csg

### 3.3.1 [#256](https://github.com/openfisca/openfisca-france-data/pull/256)

* Technical changes
Expand Down
72 changes: 50 additions & 22 deletions openfisca_france_data/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
import logging

from openfisca_core import periods
from openfisca_core.periods.date_unit import DateUnit
from openfisca_core.taxscales import MarginalRateTaxScale, combine_tax_scales
from openfisca_core.formula_helpers import switch
from openfisca_france.model.base import TypesCategorieSalarie, TAUX_DE_PRIME
from openfisca_france.model.prelevements_obligatoires.prelevements_sociaux.cotisations_sociales.base import (
cotisations_salarie_by_categorie_salarie,
)
from openfisca_france_data.smic import smic_horaire_brut
from openfisca_france_data import openfisca_france_tax_benefit_system


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -58,7 +60,7 @@ def create_salaire_de_base(individus, period = None, revenu_type = 'imposable',
taux_abattement = parameters_csg_deductible.abattement.rates[0]
try:
seuil_abattement = parameters_csg_deductible.abattement.thresholds[1]
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours était limité à 4 PSS
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours été limité à 4 PSS
seuil_abattement = None
csg_deductible = MarginalRateTaxScale(name = 'csg_deductible')
csg_deductible.add_bracket(0, taux_csg * (1 - taux_abattement))
Expand All @@ -72,7 +74,7 @@ def create_salaire_de_base(individus, period = None, revenu_type = 'imposable',
taux_abattement = parameters_csg_imposable.abattement.rates[0]
try:
seuil_abattement = parameters_csg_imposable.abattement.thresholds[1]
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours était limité à 4 PSS
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours été limité à 4 PSS
seuil_abattement = None
csg_imposable = MarginalRateTaxScale(name = 'csg_imposable')
csg_imposable.add_bracket(0, taux_csg * (1 - taux_abattement))
Expand All @@ -84,7 +86,7 @@ def create_salaire_de_base(individus, period = None, revenu_type = 'imposable',
taux_abattement = parameters_crds.abattement.rates[0]
try:
seuil_abattement = parameters_crds.abattement.thresholds[1]
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours était limité à 4 PSS
except IndexError: # Pour gérer le fait que l'abattement n'a pas toujours été limité à 4 PSS
seuil_abattement = None
crds = MarginalRateTaxScale(name = 'crds')
crds.add_bracket(0, taux_csg * (1 - taux_abattement))
Expand Down Expand Up @@ -121,13 +123,13 @@ def create_salaire_de_base(individus, period = None, revenu_type = 'imposable',
whours = parameters.marche_travail.salaire_minimum.smic.nb_heures_travail_mensuel

if period.unit == 'year':
plafond_securite_sociale = plafond_securite_sociale_mensuel * 12
heures_temps_plein = whours * 12
nb_mois = 12
elif period.unit == 'month':
plafond_securite_sociale = plafond_securite_sociale_mensuel * period.size
heures_temps_plein = whours * period.size
nb_mois = period.size
else:
raise
plafond_securite_sociale = plafond_securite_sociale_mensuel * nb_mois
heures_temps_plein = whours * nb_mois

if revenu_type == 'imposable':
salaire_pour_inversion = individus.salaire_imposable
Expand All @@ -151,20 +153,26 @@ def create_salaire_de_base(individus, period = None, revenu_type = 'imposable',
}
)

def add_agirc_gmp_to_agirc(agirc, parameters):
plafond_securite_sociale_annuel = parameters.prelevements_sociaux.pss.plafond_securite_sociale_mensuel * 12
salaire_charniere = parameters.prelevements_sociaux.regimes_complementaires_retraite_secteur_prive.gmp.salaire_charniere_annuel / plafond_securite_sociale_annuel
cotisation = parameters.prelevements_sociaux.regimes_complementaires_retraite_secteur_prive.gmp.cotisation_forfaitaire_mensuelle.part_salariale * 12
n = (cotisation + 1) * 12
agirc.add_bracket(n / plafond_securite_sociale_annuel, 0)
def add_agirc_gmp_to_agirc(agirc, parameters, period):
if period.unit == 'year':
nb_mois = 12
elif period.unit == 'month':
nb_mois = period.size
else:
raise
plafond_securite_sociale = plafond_securite_sociale_mensuel * nb_mois
salaire_charniere = parameters.prelevements_sociaux.regimes_complementaires_retraite_secteur_prive.gmp.salaire_charniere_annuel * (nb_mois / 12) / plafond_securite_sociale
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Le paramètre plafond_securite_sociale n'est donc plus défini dans la fonction ce qui va poser problème si on appelle la fonction. Idem pour nb_mois. Je les mettrai a minima en paramètre de la fonction

cotisation = parameters.prelevements_sociaux.regimes_complementaires_retraite_secteur_prive.gmp.cotisation_forfaitaire_mensuelle.part_salariale * nb_mois
n = (cotisation + 1) * 12 # pour permettre la mensualisation en cas d'inversion, en évitant un taux 12 fois plus élevé sur une tranche 12 fois plus étroite
agirc.add_bracket(n / plafond_securite_sociale, 0)
agirc.rates[0] = cotisation / n
agirc.thresholds[2] = salaire_charniere

salaire_de_base = 0.0
for categorie in ['prive_non_cadre', 'prive_cadre', 'public_non_titulaire']:
if categorie == 'prive_cadre' and "agirc" in salarie[categorie]._children:
print("adding GMP")
add_agirc_gmp_to_agirc(salarie[categorie].agirc, parameters)
add_agirc_gmp_to_agirc(salarie[categorie].agirc, parameters, period)

bareme = combine_tax_scales(salarie[categorie])
bareme.add_tax_scale(csg_deductible)
Expand Down Expand Up @@ -209,7 +217,7 @@ def add_agirc_gmp_to_agirc(agirc, parameters):
def create_traitement_indiciaire_brut(individus, period = None, revenu_type = 'imposable',
tax_benefit_system = None):
"""
Calcule le tratement indiciaire brut à partir du salaire imposable ou du salaire net.
Calcule le traitement indiciaire brut à partir du salaire imposable ou du salaire net.
Note : le supplément familial de traitement est imposable. Pas géré
"""
assert period is not None
Expand Down Expand Up @@ -384,9 +392,14 @@ def create_traitement_indiciaire_brut(individus, period = None, revenu_type = 'i
1: brut_proratise * (heures_remunerees_volume / (heures_temps_plein)),
}
)
traitement_indiciaire_brut += (
(categorie_salarie == TypesCategorieSalarie[categorie].index) * brut
)

if period.start.year>2017:traitement_indiciaire_brut += (
(categorie_salarie == TypesCategorieSalarie[categorie].index) * brut / (1 + 0.0076)
) # Prise en compte de l'indemnité compensatrice de csg, qui sera recalculée. Non prise en compte des exonérations de cotisation sur cette indemnité
else:
traitement_indiciaire_brut += (
(categorie_salarie == TypesCategorieSalarie[categorie].index) * brut
)
if (categorie_salarie == TypesCategorieSalarie[categorie].index).any():
log.debug("Pour {} : brut = {}".format(TypesCategorieSalarie[categorie].index, brut))
log.debug('bareme direct: {}'.format(bareme))
Expand All @@ -395,12 +408,20 @@ def create_traitement_indiciaire_brut(individus, period = None, revenu_type = 'i
individus['primes_fonction_publique'] = TAUX_DE_PRIME * traitement_indiciaire_brut


def create_revenus_remplacement_bruts(individus, period, tax_benefit_system):
def create_revenus_remplacement_bruts(individus, period, tax_benefit_system, revenu_type = 'net'):
assert 'taux_csg_remplacement' in individus

individus.chomage_imposable.fillna(0, inplace = True)
individus.retraite_imposable.fillna(0, inplace = True)
individus.salaire_net.fillna(0, inplace = True)
if revenu_type == 'imposable':
assert 'salaire_imposable' in individus.columns
salaire_pour_inversion = individus.salaire_imposable
elif revenu_type == 'net':
assert 'salaire_net' in individus.columns
salaire_pour_inversion = individus.salaire_net
else :
raise Exception("revenu_type not implemented")
salaire_pour_inversion.fillna(0, inplace = True)

parameters = tax_benefit_system.get_parameters_at_instant(period.start)
csg = parameters.prelevements_sociaux.contributions_sociales.csg
Expand All @@ -410,13 +431,20 @@ def create_revenus_remplacement_bruts(individus, period, tax_benefit_system):
seuil_abattement_csg_chomage = parameters.prelevements_sociaux.contributions_sociales.csg.remplacement.allocations_chomage.deductible.abattement.thresholds[1]
taux_plein = csg_deductible_chomage.taux_plein
taux_reduit = csg_deductible_chomage.taux_reduit
liste_smic_mensuel = []
for month in period.get_subperiods(DateUnit.MONTH):
smic_horaire_mois = openfisca_france_tax_benefit_system.parameters.marche_travail.salaire_minimum.smic.smic_b_horaire(month)
nb_heures_mois = openfisca_france_tax_benefit_system.parameters.marche_travail.salaire_minimum.smic.nb_heures_travail_mensuel(month)
smic_mensuel = smic_horaire_mois * nb_heures_mois
liste_smic_mensuel.append(smic_mensuel)

seuil_chomage_net_exoneration = (
(35 * 52) * smic_horaire_brut[period.start.year]
sum(liste_smic_mensuel)
* (
(individus.taux_csg_remplacement == 2) / (1 - taux_reduit)
+ (individus.taux_csg_remplacement >= 3) / (1 - taux_plein)
)
) - individus.salaire_net
) - salaire_pour_inversion # théoriquement, il s'agit du net pour salaire et rpns, mais il n'est pas toujours disponible en pratique
exonere_csg_chomage = (
(individus.taux_csg_remplacement < 2)
| (individus.chomage_imposable <= seuil_chomage_net_exoneration)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def create_individu_variables_brutes(
create_taux_csg_remplacement(individus, period, tax_benefit_system)
created_variables.append('taux_csg_remplacement')

create_revenus_remplacement_bruts(individus, period, tax_benefit_system)
create_revenus_remplacement_bruts(individus, period, tax_benefit_system, revenu_type = revenu_type)
created_variables.append('chomage_brut')
created_variables.append('retraite_brute')

Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

setup(
name = "OpenFisca-France-Data",
version = "3.3.1",
version = "3.4.0",
description = "OpenFisca-France-Data module to work with French survey data",
long_description = long_description,
long_description_content_type="text/markdown",
Expand Down
2 changes: 1 addition & 1 deletion tests/test_inversion.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
# Inverse incomes from net to gross : the tested functions

create_taux_csg_remplacement(individus, period(year), tax_benefit_system)
create_revenus_remplacement_bruts(individus, period(year), tax_benefit_system)
create_revenus_remplacement_bruts(individus, period(year), tax_benefit_system, revenu_type = 'net')

# Test against chomage_brut_test

Expand Down
Loading