diff --git a/CHANGELOG.md b/CHANGELOG.md index 96dbb3ea0a..687df8d0f0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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. diff --git a/openfisca_france/model/caracteristiques_socio_demographiques/demographie.py b/openfisca_france/model/caracteristiques_socio_demographiques/demographie.py index c632575d82..09375af929 100644 --- a/openfisca_france/model/caracteristiques_socio_demographiques/demographie.py +++ b/openfisca_france/model/caracteristiques_socio_demographiques/demographie.py @@ -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 diff --git a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/activite.py b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/activite.py index e6b34885d3..b0bda8cb38 100644 --- a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/activite.py +++ b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/activite.py @@ -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__) @@ -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) @@ -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): @@ -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) @@ -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): @@ -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) @@ -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): diff --git a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/base.py b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/base.py index d5b8b42b8a..f0a011bd4d 100644 --- a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/base.py +++ b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/base.py @@ -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, @@ -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) diff --git a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/capital.py b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/capital.py index 8d8e334425..f5f2981d48 100644 --- a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/capital.py +++ b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/capital.py @@ -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__) @@ -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): @@ -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): diff --git a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/csg_crds.py b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/csg_crds.py index a45d5bd94b..c3da2394ea 100644 --- a/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/csg_crds.py +++ b/openfisca_france/model/prelevements_obligatoires/prelevements_sociaux/contributions_sociales/csg_crds.py @@ -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__) @@ -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]) @@ -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 @@ -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]) @@ -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): @@ -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]) @@ -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) diff --git a/openfisca_france/model/prestations/minima_sociaux/rsa.py b/openfisca_france/model/prestations/minima_sociaux/rsa.py index 75f09d3590..8403e5d8b2 100644 --- a/openfisca_france/model/prestations/minima_sociaux/rsa.py +++ b/openfisca_france/model/prestations/minima_sociaux/rsa.py @@ -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 @@ -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 ) diff --git a/setup.py b/setup.py index e954f5c4b4..25ce4075d7 100644 --- a/setup.py +++ b/setup.py @@ -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 = [ diff --git a/tests/capital/non_residents_exoneration_csg_crds.yaml b/tests/capital/non_residents_exoneration_csg_crds.yaml new file mode 100644 index 0000000000..cc028eb8c4 --- /dev/null +++ b/tests/capital/non_residents_exoneration_csg_crds.yaml @@ -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