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

Correction de l'exonération de la CSG et CRDS pour les non-résidents #2106

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Member

Choose a reason for hiding this comment

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

L'import n'est pas nécessaire.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Si il l'est sinon j'ai une erreur NameError: name 'csg_crds_condition_residence' is not defined ... J'ai raté quelque chose ?



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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

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
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

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
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

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
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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)
Copy link
Member

Choose a reason for hiding this comment

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

Il vaut mieux éviter de manipuler des GroupPopulation et passer par des variables. Elles permettent la gestion des changements de législation dans le temps de façon plus naturelle.

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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved
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 * (
Nodraak marked this conversation as resolved.
Show resolved Hide resolved
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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

# 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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved


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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved

# 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)
Nodraak marked this conversation as resolved.
Show resolved Hide resolved
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
Nodraak marked this conversation as resolved.
Show resolved Hide resolved
)


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 = '[email protected]',
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