Skip to content

Commit

Permalink
Merge pull request #252 from openfisca/mensualisation_inversion
Browse files Browse the repository at this point in the history
Permet inversion au niveau mensuel
  • Loading branch information
sylvainipp authored May 30, 2024
2 parents b2d0485 + 74d51fc commit 0028e7d
Show file tree
Hide file tree
Showing 5 changed files with 60 additions and 25 deletions.
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
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

0 comments on commit 0028e7d

Please sign in to comment.