Skip to content

Commit

Permalink
Correction de l'exoneration de la CSG et CRDS pour les non residents (o…
Browse files Browse the repository at this point in the history
  • Loading branch information
Nodraak committed Jun 1, 2023
1 parent 8102b13 commit 5db03d1
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 19 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@
# Changelog

## 147.3.2 [#2106](https://github.com/openfisca/openfisca-france/pull/2106)

* Évolution du système socio-fiscal
* Périodes concernées : à partir du 2019-01-01
* Zones impactées :
* `openfisca_france/model/caracteristiques_socio_demographiques/demographie.py`
* `openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/`
* `openfisca_france/model/prestations/minima_sociaux/rsa.py`
* `tests/`
* Détails :
* Création de la variable `resident_eee_hors_france` pour gérer le cas des personnes résidentes en Europe mais hors de France.
* Après le 2019-01-01, les non résidents sont exonérés de la CSG et de la CRDS.
* Les non résidents ne sont pas éligibles au RSA.

### 147.2.2 [#2111](https://github.com/openfisca/openfisca-france/pull/2111)

* Changement mineur.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -364,11 +364,20 @@ def formula(individu, period, parameters):
return sum([nationalite == str.encode(etat_membre) for etat_membre in parameters(period).geopolitique.eee]) # TOOPTIMIZE: string encoding into bytes array should be done at load time


class resident_eee_hors_france(Variable):
value_type = bool
default_value = False
entity = Individu
label = "Individu résident dans un pays membre de l'Espace Économique Européen (EEE), hors France. Case 8SH. Voir aussi 'resident_ue'."
definition_period = YEAR
set_input = set_input_dispatch_by_period


class resident_ue(Variable):
value_type = bool
default_value = True
entity = Individu
label = "Individu résidant dans pays membre de l'Union européenne (UE)."
label = "Individu résidant dans pays membre de l'Union européenne (UE). Voir aussi 'resident_eee_hors_france'."
definition_period = MONTH
set_input = set_input_dispatch_by_period

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import logging

from openfisca_france.model.base import *
from openfisca_france.model.prelevements_obligatoires.prelevements_sociaux.contributions_sociales.base import montant_csg_crds
from openfisca_france.model.prelevements_obligatoires.prelevements_sociaux.contributions_sociales.base import montant_csg_crds, condition_csg_crds_non_residents


log = logging.getLogger(__name__)
Expand Down Expand Up @@ -106,6 +106,8 @@ class csg_deductible_salaire(Variable):
set_input = set_input_divide_by_period

def formula(individu, period, parameters):
csg_condition = condition_csg_crds_non_residents(individu, period.this_year)

assiette_csg_abattue = individu('assiette_csg_abattue', period)
assiette_csg_non_abattue = individu('assiette_csg_non_abattue', period)
plafond_securite_sociale = individu('plafond_securite_sociale', period)
Expand All @@ -117,7 +119,7 @@ def formula(individu, period, parameters):
law_node = csg.activite.deductible,
plafond_securite_sociale = plafond_securite_sociale,
)
return montant_csg
return csg_condition * montant_csg


class csg_imposable_salaire(Variable):
Expand All @@ -129,6 +131,8 @@ class csg_imposable_salaire(Variable):
set_input = set_input_divide_by_period

def formula(individu, period, parameters):
csg_condition = condition_csg_crds_non_residents(individu, period.this_year)

assiette_csg_abattue = individu('assiette_csg_abattue', period)
assiette_csg_non_abattue = individu('assiette_csg_non_abattue', period)
plafond_securite_sociale = individu('plafond_securite_sociale', period)
Expand All @@ -141,7 +145,7 @@ def formula(individu, period, parameters):
plafond_securite_sociale = plafond_securite_sociale,
)

return montant_csg
return csg_condition * montant_csg


class crds_salaire(Variable):
Expand All @@ -153,6 +157,8 @@ class crds_salaire(Variable):
set_input = set_input_divide_by_period

def formula(individu, period, parameters):
crds_condition = condition_csg_crds_non_residents(individu, period.this_year)

assiette_csg_abattue = individu('assiette_csg_abattue', period)
assiette_csg_non_abattue = individu('assiette_csg_non_abattue', period)
plafond_securite_sociale = individu('plafond_securite_sociale', period)
Expand All @@ -166,7 +172,7 @@ def formula(individu, period, parameters):
plafond_securite_sociale = plafond_securite_sociale,
)

return montant_crds
return crds_condition * montant_crds


class forfait_social(Variable):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from openfisca_core import periods, populations
from openfisca_france.model.base import *


def montant_csg_crds(base_avec_abattement = None, base_sans_abattement = None, indicatrice_taux_plein = None,
Expand All @@ -18,3 +20,31 @@ def montant_csg_crds(base_avec_abattement = None, base_sans_abattement = None, i
return -law_node.taux * base
else:
return - (law_node.taux_plein * indicatrice_taux_plein + law_node.taux_reduit * indicatrice_taux_reduit) * base


def condition_csg_crds_non_residents(individu_or_foyerfiscal, period):
'''
Depuis le 1er janvier 2019, les personnes affiliées à un régime obligatoire
de sécurité sociale autre que français au sein d'un pays de l'EEE (Union
européenne, Islande, Norvège, Liechtenstein) ou de la Suisse sont exonérées
de CSG et de CRDS.
Ces revenus demeurent soumis à un prélèvement de solidarité au taux de 7,5%.
Reference: https://www.impots.gouv.fr/international-particulier/questions/je-suis-non-resident-suis-je-redevable-des-contributions
Return:
* True: la CSG et la CRDS sont exigibles
* False: la CSG et la CRDS sont exonerees
'''

if isinstance(individu_or_foyerfiscal, populations.group_population.GroupPopulation):
# FoyerFiscal
rehf = individu_or_foyerfiscal.any(individu_or_foyerfiscal.members('resident_eee_hors_france', period))
else:
# Individu
rehf = individu_or_foyerfiscal('resident_eee_hors_france', period)

exonere = (
(periods.period('2019-01-01').start <= period.start)
* rehf
)
return not_(exonere)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from openfisca_france.model.base import *
from openfisca_france.model.prelevements_obligatoires.prelevements_sociaux.contributions_sociales.base import condition_csg_crds_non_residents

log = logging.getLogger(__name__)

Expand Down Expand Up @@ -330,13 +331,15 @@ def formula(foyer_fiscal, period, parameters):
Attention : Pour les années avant 2013, cette formule n'est pas entièrement correcte car le taux de la CSG n'était pas unique (distinction revenus du patrimoine et revenus de placement)
et il y a aussi un problème pour les années postérieures à 2017/2018
'''
csg_condition = condition_csg_crds_non_residents(foyer_fiscal, period)

assiette_csg_revenus_capital = foyer_fiscal('assiette_csg_revenus_capital', period)
csg = parameters(period).taxation_capital.prelevements_sociaux.csg

# Pour les revenus du patrimoine, le changement de CSG se fait à partir des revenus de 2017,
# mais le taux de CSG déductible se fait à partir des revenus 2018. Pour les revenus de placement le timing est différent,
# et reste à être pris en compte ici : cf. II.B de l'art. 67 de loi 2017-1837 et 3° et 4° du V.A de l'art. 8 de loi 2017-1836
return -assiette_csg_revenus_capital * csg.taux_global.produits_de_placement
return csg_condition * (-assiette_csg_revenus_capital * csg.taux_global.produits_de_placement)


class crds_revenus_capital(Variable):
Expand All @@ -346,10 +349,12 @@ class crds_revenus_capital(Variable):
definition_period = YEAR

def formula(foyer_fiscal, period, parameters):
crds_condition = condition_csg_crds_non_residents(foyer_fiscal, period)

assiette_csg_revenus_capital = foyer_fiscal('assiette_csg_revenus_capital', period)
P = parameters(period).taxation_capital.prelevements_sociaux

return -assiette_csg_revenus_capital * P.crds.produits_de_placement
return crds_condition * (-assiette_csg_revenus_capital * P.crds.produits_de_placement)


class prelevements_sociaux_revenus_capital_hors_csg_crds(Variable):
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import logging
from openfisca_france.model.base import *
from openfisca_france.model.prelevements_obligatoires.prelevements_sociaux.contributions_sociales.base import condition_csg_crds_non_residents

log = logging.getLogger(__name__)

Expand All @@ -11,6 +12,7 @@ class csg(Variable):
definition_period = YEAR

def formula(individu, period):
csg_condition = condition_csg_crds_non_residents(individu, period)
csg_imposable_salaire = individu('csg_imposable_salaire', period, options = [ADD])
csg_deductible_salaire = individu('csg_deductible_salaire', period, options = [ADD])
csg_imposable_chomage = individu('csg_imposable_chomage', period, options = [ADD])
Expand All @@ -24,15 +26,17 @@ def formula(individu, period):
csg_revenus_capital_projetee = csg_revenus_capital * individu.has_role(FoyerFiscal.DECLARANT_PRINCIPAL)

return (
csg_imposable_salaire
+ csg_deductible_salaire
+ csg_imposable_chomage
+ csg_deductible_chomage
+ csg_imposable_retraite
+ csg_deductible_retraite
+ csg_imposable_non_salarie
+ csg_deductible_non_salarie
+ csg_revenus_capital_projetee
csg_condition * (
csg_imposable_salaire
+ csg_deductible_salaire
+ csg_imposable_chomage
+ csg_deductible_chomage
+ csg_imposable_retraite
+ csg_deductible_retraite
+ csg_imposable_non_salarie
+ csg_deductible_non_salarie
+ csg_revenus_capital_projetee
)
)

# TODO: manque CSG sur IJ et pré-retraites
Expand All @@ -49,6 +53,8 @@ class crds(Variable):
definition_period = YEAR

def formula(individu, period):
crds_condition = condition_csg_crds_non_residents(individu, period)

# CRDS sur revenus individuels
crds_salaire = individu('crds_salaire', period, options = [ADD])
crds_retraite = individu('crds_retraite', period, options = [ADD])
Expand All @@ -65,7 +71,7 @@ def formula(individu, period):
crds_revenus_capital = individu.foyer_fiscal('crds_revenus_capital', period)
crds_revenus_capital_projetee = crds_revenus_capital * individu.has_role(FoyerFiscal.DECLARANT_PRINCIPAL)

return crds_individu + crds_famille_projetes + crds_revenus_capital_projetee
return crds_condition * (crds_individu + crds_famille_projetes + crds_revenus_capital_projetee)


class crds_hors_prestations(Variable):
Expand All @@ -75,6 +81,8 @@ class crds_hors_prestations(Variable):
definition_period = YEAR

def formula(individu, period):
crds_condition = condition_csg_crds_non_residents(individu, period)

# CRDS sur revenus individuels
crds_salaire = individu('crds_salaire', period, options = [ADD])
crds_retraite = individu('crds_retraite', period, options = [ADD])
Expand All @@ -85,4 +93,4 @@ def formula(individu, period):
crds_revenus_capital = individu.foyer_fiscal('crds_revenus_capital', period)
crds_revenus_capital_projetee = crds_revenus_capital * individu.has_role(FoyerFiscal.DECLARANT_PRINCIPAL)

return crds_individu + crds_revenus_capital_projetee
return crds_condition * (crds_individu + crds_revenus_capital_projetee)
4 changes: 4 additions & 0 deletions openfisca_france/model/prestations/minima_sociaux/rsa.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,9 @@ def formula(famille, period, parameters):

etudiant_i = famille.members('etudiant', period)

# les non residents ne sont pas eligibles au RSA
rsa_eligibilite_non_residents = not_(famille.any(famille.members('resident_eee_hors_france', period.this_year)))

if period.start < periods.period('2009-06').start:
# Les jeunes de moins de 25 ans ne sont pas éligibles au RMI
rsa_jeune_condition_i = False
Expand All @@ -717,6 +720,7 @@ def formula(famille, period, parameters):
famille.any((condition_age_i | rsa_jeune_condition_i) * not_(etudiant_i), role = Famille.PARENT)
* condition_nationalite
* rsa_eligibilite_tns
* rsa_eligibilite_non_residents
)


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

setup(
name = 'OpenFisca-France',
version = '147.2.2',
version = '147.3.2',
author = 'OpenFisca Team',
author_email = 'contact@openfisca.fr',
classifiers = [
Expand Down
57 changes: 57 additions & 0 deletions tests/capital/non_residents_exoneration_csg_crds.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Before 2019, everybody pay

- name: pre_2019_residents_non_exoneres
description: Avant 2019, les résidents ne sont pas exonérés
period: 2018
absolute_error_margin: 1
input:
f4ba: 1000
# resident_eee_hors_france: False
output:
csg_revenus_capital: -99
crds_revenus_capital: -5
prelevements_sociaux_revenus_capital_hors_csg_crds: -68
revenus_nets_du_capital: 828

- name: pre_2019_non_residents_non_exoneres
description: Avant 2019, les non résidents ne sont pas exonérés
period: 2018
absolute_error_margin: 1
input:
f4ba: 1000
resident_eee_hors_france: true
output:
csg_revenus_capital: -99
crds_revenus_capital: -5
prelevements_sociaux_revenus_capital_hors_csg_crds: -68
revenus_nets_du_capital: 828

# After 2019, only residents in France pay

- name: post_2019_residents_non_exoneres
description: A partir de 2019, les résidents ne sont pas exonérés
period: 2019
absolute_error_margin: 1
input:
f4ba: 1000
# resident_eee_hors_france: False
output:
csg_revenus_capital: -92
crds_revenus_capital: -5
prelevements_sociaux_revenus_capital_hors_csg_crds: -75
revenus_nets_du_capital: 828
revenu_disponible: 6837 # due to RSA

- name: post_2019_non_residents_exoneres
description: A partir de 2019, les non résidents sont exonérés
period: 2019
absolute_error_margin: 1
input:
f4ba: 1000
resident_eee_hors_france: true
output:
csg_revenus_capital: 0
crds_revenus_capital: 0
prelevements_sociaux_revenus_capital_hors_csg_crds: -75
revenus_nets_du_capital: 925
revenu_disponible: 925

0 comments on commit 5db03d1

Please sign in to comment.