diff --git a/CHANGELOG.md b/CHANGELOG.md index a0b52fd..e8aa793 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +# 3.0.0 [#14](https://github.com/openfisca/openfisca-tunisia-pension/pull/14) + +* Évolution du système socio-fiscal. +* Périodes concernées : toutes. +* Zones impactées : `variables/regimes`. +* Détails : + - Sépare les régimes dans différents répertoires + # 3.0.0 [#13](https://github.com/openfisca/openfisca-tunisia-pension/pull/13) * Amélioration technique. diff --git a/openfisca_tunisia_pension/variables/helpers.py b/openfisca_tunisia_pension/variables/helpers.py new file mode 100644 index 0000000..dcf14c2 --- /dev/null +++ b/openfisca_tunisia_pension/variables/helpers.py @@ -0,0 +1,30 @@ +'''Helper functions''' + + +import bottleneck +from numpy import minimum as min_ + + +def pension_generique(trimestres_valides, sal_ref, age, taux_annuite_base, taux_annuite_supplemetaire, duree_stage, + age_elig, periode_remplacement_base, plaf_taux_pension, smig): + taux_pension = ( + (trimestres_valides < 4 * periode_remplacement_base) * (trimestres_valides / 4) * taux_annuite_base + + (trimestres_valides >= 4 * periode_remplacement_base) * ( + taux_annuite_base * periode_remplacement_base + + (trimestres_valides / 4 - periode_remplacement_base) * taux_annuite_supplemetaire + ) + ) + montant = min_(taux_pension, plaf_taux_pension) * sal_ref + return montant + + +def mean_over_k_largest(vector, k): + '''Return the mean over the k largest values of a vector''' + if k == 0: + return 0 + + if k <= len(vector): + return vector.sum() / len(vector) + + z = -bottleneck.partition(-vector, kth = k) + return z.sum() / k diff --git a/openfisca_tunisia_pension/variables/pension.py b/openfisca_tunisia_pension/variables/pension.py deleted file mode 100644 index f3adde7..0000000 --- a/openfisca_tunisia_pension/variables/pension.py +++ /dev/null @@ -1,163 +0,0 @@ -from __future__ import division - - -import bottleneck -import functools -from numpy import ( - apply_along_axis, - logical_not as not_, - maximum as max_, - minimum as min_, - vstack, - ) - -from openfisca_core import periods -from openfisca_core.model_api import * -from openfisca_tunisia_pension.entities import Individu - - -class salaire_reference_rsa(Variable): - value_type = float - entity = Individu - label = 'Salaires de référence du régime des salariés agricoles' - definition_period = YEAR - - def formula(individu, period): - # TODO: gérer le nombre d'année - # TODO: plafonner les salaires à 2 fois le smag de l'année d'encaissement - # period = period.start.offset('first-of', 'month').period('year') - base_declaration_rsa = 180 - base_liquidation_rsa = 300 - - n = 3 - mean_over_largest = functools.partial(mean_over_k_largest, k = n) - salaire = apply_along_axis( - mean_over_largest, - axis = 0, - arr = vstack([ - individu('salaire', period = periods.period('year', year)) - for year in range(period.start.year, period.start.year - n, -1) - ]), - ) - salaire_refererence = salaire * base_liquidation_rsa / base_declaration_rsa - return salaire_refererence - - -class salaire_reference_rsna(Variable): - value_type = float - entity = Individu - label = 'Salaires de référence du régime des salariés non agricoles' - definition_period = YEAR - - def formula(individu, period): - # TODO: gérer le nombre d'année n - # TODO: plafonner les salaires à 6 fois le smig de l'année d'encaissement - n = 10 - mean_over_largest = functools.partial(mean_over_k_largest, k = n) - salaire_refererence = apply_along_axis( - mean_over_largest, - axis = 0, - arr = vstack([ - individu('salaire', period = year) - for year in range(period.start.year, period.start.year - n, -1) - ]), - ) - return salaire_refererence - - -class pension_rsna(Variable): - value_type = float - entity = Individu - label = 'Pension des affiliés au régime des salariés non agricoles' - definition_period = YEAR - - def formula(individu, period, parameters): - trimestres_valides = individu('trimestres_valides', period = period) - salaire_reference = individu('salaire_reference_rsna', period = period) - age = individu('age', period = period) - - taux_annuite_base = parameters(period).retraite.rsna.taux_annuite_base - taux_annuite_supplemetaire = parameters(period).retraite.rsna.taux_annuite_supplemetaire - duree_stage = parameters(period).retraite.rsna.stage_derog - age_eligible = parameters(period).retraite.rsna.age_dep_anticip - periode_remplacement_base = parameters(period).retraite.rsna.periode_remplacement_base - plaf_taux_pension = parameters(period).retraite.rsna.plaf_taux_pension - smig = parameters(period).marche_travail.smig_48h - - pension_min_sup = parameters(period).retraite.rsna.pension_minimale.sup - pension_min_inf = parameters(period).retraite.rsna.pension_minimale.inf - - stage = trimestres_valides > 4 * duree_stage - pension_minimale = ( - stage * pension_min_sup + not_(stage) * pension_min_inf - ) - montant = pension_generique( - trimestres_valides, - salaire_reference, - age, - taux_annuite_base, - taux_annuite_supplemetaire, - duree_stage, - age_eligible, - periode_remplacement_base, - plaf_taux_pension, - smig, - ) - # eligibilite - eligibilite_age = age > age_eligible - eligibilite = stage * eligibilite_age * (salaire_reference > 0) - # plafonnement - montant_pension_percu = max_(montant, pension_minimale * smig) - return eligibilite * montant_pension_percu - - -def _pension_rsa(trimestres_valides, sal_ref_rsa, regime, age, parameters): - ''' - Pension du régime des salariés agricoles - ''' - taux_annuite_base = parameters.retraite.rsa.taux_annuite_base - taux_annuite_supplemetaire = parameters.retraite.rsa.taux_annuite_supplemetaire - duree_stage = parameters.retraite.rsa.stage_requis - age_elig = parameters.retraite.rsa.age_legal - periode_remplacement_base = parameters.retraite.rsa.periode_remplacement_base - plaf_taux_pension = parameters.retraite.rsa.plaf_taux_pension - smag = parameters.marche_travail.smag * 25 - stage = trimestres_valides > 4 * duree_stage - pension_min = parameters.retraite.rsa.pension_min - sal_ref = sal_ref_rsa - - montant = pension_generique(trimestres_valides, sal_ref, age, taux_annuite_base, taux_annuite_supplemetaire, - duree_stage, age_elig, periode_remplacement_base, plaf_taux_pension, smag) - - elig_age = age > age_elig - elig = stage * elig_age * (sal_ref > 0) - montant_percu = max_(montant, pension_min * smag) - pension = elig * montant_percu - return pension - - -# Helper function - -def pension_generique(trimestres_valides, sal_ref, age, taux_annuite_base, taux_annuite_supplemetaire, duree_stage, - age_elig, periode_remplacement_base, plaf_taux_pension, smig): - taux_pension = ( - (trimestres_valides < 4 * periode_remplacement_base) * (trimestres_valides / 4) * taux_annuite_base - + (trimestres_valides >= 4 * periode_remplacement_base) * ( - taux_annuite_base * periode_remplacement_base - + (trimestres_valides / 4 - periode_remplacement_base) * taux_annuite_supplemetaire - ) - ) - montant = min_(taux_pension, plaf_taux_pension) * sal_ref - return montant - - -def mean_over_k_largest(vector, k): - '''Return the mean over the k largest values of a vector''' - if k == 0: - return 0 - - if k <= len(vector): - return vector.sum() / len(vector) - - z = -bottleneck.partition(-vector, kth = k) - return z.sum() / k diff --git a/openfisca_tunisia_pension/variables/rsa.py b/openfisca_tunisia_pension/variables/rsa.py new file mode 100644 index 0000000..df81f07 --- /dev/null +++ b/openfisca_tunisia_pension/variables/rsa.py @@ -0,0 +1,65 @@ +import functools +from numpy import ( + apply_along_axis, + maximum as max_, + vstack, + ) + +from openfisca_core import periods +from openfisca_core.model_api import * +from openfisca_tunisia_pension.entities import Individu + + +class salaire_reference_rsa(Variable): + value_type = float + entity = Individu + label = 'Salaires de référence du régime des salariés agricoles' + definition_period = YEAR + + def formula(individu, period): + # TODO: gérer le nombre d'année + # TODO: plafonner les salaires à 2 fois le smag de l'année d'encaissement + base_declaration_rsa = 180 + base_liquidation_rsa = 300 + + n = 3 + mean_over_largest = functools.partial(mean_over_k_largest, k = n) + salaire = apply_along_axis( + mean_over_largest, + axis = 0, + arr = vstack([ + individu('salaire', period = periods.period('year', year)) + for year in range(period.start.year, period.start.year - n, -1) + ]), + ) + salaire_refererence = salaire * base_liquidation_rsa / base_declaration_rsa + return salaire_refererence + + +class pension_rsa(Variable): + value_type = float + entity = Individu + label = 'Salaires de référence du régime des salariés agricoles' + definition_period = YEAR + + def formula(individu, period, parameters): + rsa = parameters.retraite.rsa(period) + taux_annuite_base = rsa.taux_annuite_base + taux_annuite_supplemetaire = rsa.taux_annuite_supplemetaire + duree_stage = rsa.stage_requis + age_elig = rsa.age_legal + periode_remplacement_base = rsa.periode_remplacement_base + plaf_taux_pension = rsa.plaf_taux_pension + smag = parameters(period).marche_travail.smag * 25 + duree_stage_validee = trimestres_valides > 4 * duree_stage + pension_min = rsa.pension_min + salaire_reference = individu('salaire_reference_rsa', period) + + montant = pension_generique(trimestres_valides, sal_ref, age, taux_annuite_base, taux_annuite_supplemetaire, + duree_stage, age_elig, periode_remplacement_base, plaf_taux_pension, smag) + + elig_age = age > age_elig + elig = duree_stage_validee * elig_age * (salaire_reference > 0) + montant_percu = max_(montant, pension_min * smag) + pension = elig * montant_percu + return pension diff --git a/openfisca_tunisia_pension/variables/rsna.py b/openfisca_tunisia_pension/variables/rsna.py new file mode 100644 index 0000000..48c7b35 --- /dev/null +++ b/openfisca_tunisia_pension/variables/rsna.py @@ -0,0 +1,80 @@ +import functools +from numpy import ( + apply_along_axis, + logical_not as not_, + maximum as max_, + vstack, + ) + +from openfisca_core.model_api import * +from openfisca_tunisia_pension.entities import Individu +from openfisca_tunisia_pension.variables.helpers import mean_over_k_largest, pension_generique + + +class salaire_reference_rsna(Variable): + value_type = float + entity = Individu + label = 'Salaires de référence du régime des salariés non agricoles' + definition_period = YEAR + + def formula(individu, period): + # TODO: gérer le nombre d'année n + # TODO: plafonner les salaires à 6 fois le smig de l'année d'encaissement + n = 10 + mean_over_largest = functools.partial(mean_over_k_largest, k = n) + salaire_refererence = apply_along_axis( + mean_over_largest, + axis = 0, + arr = vstack([ + individu('salaire', period = year) + for year in range(period.start.year, period.start.year - n, -1) + ]), + ) + return salaire_refererence + + +class pension_rsna(Variable): + value_type = float + entity = Individu + label = 'Pension des affiliés au régime des salariés non agricoles' + definition_period = YEAR + + def formula(individu, period, parameters): + trimestres_valides = individu('trimestres_valides', period = period) + salaire_reference = individu('salaire_reference_rsna', period = period) + age = individu('age', period = period) + + rsna = parameters(period).retraite.rsna + taux_annuite_base = rsna.taux_annuite_base + taux_annuite_supplemetaire = rsna.taux_annuite_supplemetaire + duree_stage = rsna.stage_derog + age_eligible = rsna.age_dep_anticip + periode_remplacement_base = rsna.periode_remplacement_base + plaf_taux_pension = rsna.plaf_taux_pension + smig = parameters(period).marche_travail.smig_48h + + pension_min_sup = rsna.pension_minimale.sup + pension_min_inf = rsna.pension_minimale.inf + + stage = trimestres_valides > 4 * duree_stage + pension_minimale = ( + stage * pension_min_sup + not_(stage) * pension_min_inf + ) + montant = pension_generique( + trimestres_valides, + salaire_reference, + age, + taux_annuite_base, + taux_annuite_supplemetaire, + duree_stage, + age_eligible, + periode_remplacement_base, + plaf_taux_pension, + smig, + ) + # eligibilite + eligibilite_age = age > age_eligible + eligibilite = stage * eligibilite_age * (salaire_reference > 0) + # plafonnement + montant_pension_percu = max_(montant, pension_minimale * smig) + return eligibilite * montant_pension_percu diff --git a/pyproject.toml b/pyproject.toml index b3e43a0..4448bf4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "OpenFisca-Tunisia-Pension" -version = "3.0.0" +version = "4.0.0" description = "OpenFisca Rules as Code model for Tunisia pensions." readme = "README.md" keywords = ["microsimulation", "tax", "benefit", "pension", "rac", "rules-as-code", "tunisia"]