In [1]:
from datetime import date
import json

import openfisca_france
from openfisca_france.model.base import *

#importer numpy permet de coller deux sections de courbes (nécessaire quand on fait varier le salaire de base en-dessous du SMIC mensuel)
import numpy as np
from numpy import round

# to debug / trace
from openfisca_core import tools
from openfisca_core.reforms import Reform

Système socio-fiscal


In [2]:
tax_benefit_system = openfisca_france.FranceTaxBenefitSystem()

Réforme : 2. Revenu de base financé par l'IRPP


In [3]:
from numpy import logical_not as not_, minimum as min_, maximum as max_, logical_and as and_, logical_or as or_

#- MISE EN PLACE DU RDB
#- Mise en place d'un revenu de base adulte égal à RSA socle célibataire - forfait logement célibataire,
#-                dont on déduit l'AAH jusqu'à sa hauteur
#- Mise en place d'un crédit d'impot familles monoparentales montant ??? (150€)
#- Supprimer le RSA
#- Supprimer la prime d'activité
#- Création du RdB enfant


#- CHANGEMENT DE L'ASSIETTE DE L'IRPP

#- Réforme de l'abattement pour frais professionnels :
#- le taux d'abattement pour frais professionnels est réduit à 0%
#- seuls 30% de frais réels donnent droit à un abattement
#- le taux d'abattement sur les pensions est réduit à 0%

#- La CSG déductible sur les salaires ne l'est plus
#- La CSG déductible sur le chômage ne l'est plus
#- La CSG déductible sur les pensions ne l'est plus


#- Les charges déductibles (abattements) suivantes sont supprimées : cd_deddiv, cd_doment, cd_eparet, cd_grorep, cd_percap
#- Les charges déductibles (abattements) suivantes sont maintenues : cd_acc75a, cd_penali
#- La CSG déductible sur le patrimoine ne l'est plus
#- Les réductions d'impôts suivants sont remplacés par des abattements : cotsyn, dfppce, donapd, garext 
#- Les crédits d'impôt suivants sont remplacés par des abattements : aidper, ci_garext, quaenv, saldom2

#- le revenu de base adulte et le crédit d'impot familles monoparentales sont ajoutés au revenu imposable


#- CHANGEMENT DU CALCUL DE L'IRPP
#- le barème est modifié avec les tranches 0%, 30%, 41%, 45%
#- les tranches sont modifiées : RDB adulte, XXXXX €, XXXXX €
#- la décote est annulée (ou non prise en compte)

#- les enfants sont retirés du nombre de parts (maintien du quotient conjugal et de la demi-part pour familles monoparentales)
#- Le revenu de base et le crédit familles monoparentales sont intégres au revenu imposable

#- Les réductions d'impôt suivantes sont supprimées : accult, adhcga, assvie, cappme, creaen, daepad, deffor, doment, domlog, domsoc, duflot, ecodev, ecpess, intagr, intcon, intemp, invfor, invlst, invrev, ip_net, locmeu, mecena, mohist, patnat, prcomp, reduction_impot_exceptionnelle, repsoc, resimm, rsceha, saldom, scelli, sofica, sofipe, spfcpi
#- Seules 30% des réductions d'impôt suivantes sont prise en compte : (vide)

#- Les crédits d'impôt suivants sont supprimés : accult, acqgpl, aidmob, assloy, autent, cotsyn, creimp, creimp_exc_2008, direpa, divide, drbail, inthab, jeunes, mecena, percvm, preetu, prlire

#- Intégrer le revenu de base au revenu disponible


#-Visualisation graphique en abscisse salaire brut et en ordonnée variation du revenu disponible 
# pour un célibataire sans enfant
# pour un couple sans enfant
# une famille monoparentale

In [4]:
#- Les paramètres de taux d'abattement sont annulés

#- paramètre abattement_minimum : l'abattemnt minimum est réduit à 0
#- paramètre abatpro.taux : le taux d'abattement pour frais professionnels est réduit à 0%
#- paramètre abatpen.taux : le taux d'abattement sur les pensions est réduit à 0%


def modify_legislation_json(reference_legislation_json_copy):
    for value_json in reference_legislation_json_copy['children']['ir']['children']['tspr']['children']['abatpro']['children']['taux']['values']:
        value_json['value'] = 0
    for value_json in reference_legislation_json_copy['children']['ir']['children']['tspr']['children']['abatpen']['children']['taux']['values']:
        value_json['value'] = 0
    return reference_legislation_json_copy

In [5]:
#- Variable salaire_imposable (individus)
#- La CSG déductible sur les salaires ne l'est plus

class salaire_imposable(Variable):

    def function(self, simulation, period):
        period = period.start.period(u'month').offset('first-of')
        salaire_de_base = simulation.calculate('salaire_de_base', period)
        primes_salaires = simulation.calculate('primes_salaires', period)
        primes_fonction_publique = simulation.calculate('primes_fonction_publique', period)
        indemnite_residence = simulation.calculate('indemnite_residence', period)
        supp_familial_traitement = simulation.calculate('supp_familial_traitement', period)
        csg_deductible_salaire = simulation.calculate('csg_deductible_salaire', period)
        cotisations_salariales = simulation.calculate('cotisations_salariales', period)
        remuneration_principale = simulation.calculate('remuneration_principale', period)
        hsup = simulation.calculate('hsup', period)
        rev_microsocial_declarant1 = simulation.calculate_divide('rev_microsocial_declarant1', period)
        indemnite_fin_contrat = simulation.calculate('indemnite_fin_contrat', period)

        return period, (
            salaire_de_base + primes_salaires + remuneration_principale +
            primes_fonction_publique + indemnite_residence + supp_familial_traitement +
            cotisations_salariales - hsup + rev_microsocial_declarant1 + indemnite_fin_contrat
            )

In [6]:
#- Variable chomage_imposable (individus)
#- La CSG déductible sur le chômage ne l'est plus

class chomage_imposable(Variable):
    
    def function(self, simulation, period):
        period = period
        chomage_brut = simulation.calculate('chomage_brut', period)
        csg_deductible_chomage = simulation.calculate_add('csg_deductible_chomage', period)

        return period, chomage_brut

In [7]:
#- Variable retraite_imposable (individus)
#- La CSG déductible sur les pensions de retraite ne l'est plus

class retraite_imposable(Variable):
    
    def function(self, simulation, period):
        period = period
        retraite_brute = simulation.calculate_add('retraite_brute', period)
        csg_deductible_retraite = simulation.calculate_add('csg_deductible_retraite', period)

        return period, retraite_brute

In [8]:
#- variable revenu_assimile_salaire_apres_abattements (Individu) :
#- seuls 30% de frais réels donnent droit à un abattement

class revenu_assimile_salaire_apres_abattements(Variable):

    def function(self, simulation, period):
        '''
        blabla
        '''
        period = period.this_year
        revenu_assimile_salaire = simulation.calculate('revenu_assimile_salaire', period)
        chomeur_longue_duree = simulation.calculate('chomeur_longue_duree', period)
        frais_reels = simulation.calculate('frais_reels', period)
        abatpro = simulation.legislation_at(period.start).ir.tspr.abatpro

        abattement_minimum = abatpro.min * not_(chomeur_longue_duree) + abatpro.min2 * chomeur_longue_duree
        abatfor = round(min_(max_(abatpro.taux * revenu_assimile_salaire, abattement_minimum), abatpro.max))

        return period, (frais_reels > abatfor) * (revenu_assimile_salaire - frais_reels/3) + (frais_reels <= abatfor) * max_(0, revenu_assimile_salaire - abatfor)

In [9]:
#- variable cd1 (FoyersFiscaux) : 
#- Les charges déductibles (abattements) suivantes sont supprimées : cd_deddiv, cd_doment, cd_eparet, cd_grorep, cd_percap
#- Les charges déductibles (abattements) suivantes sont maintenues : cd_acc75a, cd_penali
    
class cd1(Variable):

    def function(self, simulation, period):

        '''
        Renvoie la liste des charges déductibles avant rbg_int pour 2014
        '''
        period = period.this_year
        cd_penali = simulation.calculate('cd_penali', period)
        cd_acc75a = simulation.calculate('cd_acc75a', period)
        cd_deddiv = simulation.calculate('cd_deddiv', period)
        cd_eparet = simulation.calculate('cd_eparet', period)
        cd_grorep = simulation.calculate('cd_grorep', period)

        niches1 = cd_penali + cd_acc75a 
        # log.error("Charges déductibles to be checked because not defined for %s", 2014)
        return period, niches1

In [10]:
#- CHANGEMENT DE L'ASSIETTE DE L'IRPP 

#- variable rng (FoyersFiscaux): 
#- La CSG déductible sur le patrimoine ne l'est plus
#- Les réductions d'impôts suivantes sont remplacées par des abattements : dfppce (dons aux oeuvres), donapd (dons aide à la personne), garext (frais de garde extérieur) 
#- Les crédits d'impôt suivants sont remplacés par des abattements : aidper, ci_garext, quaenv, saldom2

#- Le revenu de base et le crédit familles monoparentales sont intégrés au revenu imposable

class rng(Variable):

    def function(self, simulation, period):
        ''' 
        Revenu net global (total 20) 
        '''
        period = period.this_year
        rbg = simulation.calculate('rbg', period)
        csg_deduc = simulation.calculate('csg_deduc', period)
        charges_deduc = simulation.calculate('charges_deduc', period)

        '''
        Renvoie la somme des réductions d'impôt à intégrer pour l'année 2014 et + (non vérifiée)
        '''
        period = period.this_year
        accult = simulation.calculate('accult', period)
        adhcga = simulation.calculate('adhcga', period)
        cappme = simulation.calculate('cappme', period)
        creaen = simulation.calculate('creaen', period)
        daepad = simulation.calculate('daepad', period)
        deffor = simulation.calculate('deffor', period)
        dfppce = simulation.calculate('dfppce', period)
        doment = simulation.calculate('doment', period)
        domlog = simulation.calculate('domlog', period)
        donapd = simulation.calculate('donapd', period)
        duflot = simulation.calculate('duflot', period)
        ecpess = simulation.calculate('ecpess', period)
        garext = simulation.calculate('garext', period)
        intagr = simulation.calculate('intagr', period)
        invfor = simulation.calculate('invfor', period)
        invlst = simulation.calculate('invlst', period)
        ip_net = simulation.calculate('ip_net', period)
        locmeu = simulation.calculate('locmeu', period)
        mecena = simulation.calculate('mecena', period)
        mohist = simulation.calculate('mohist', period)
        patnat = simulation.calculate('patnat', period)
        prcomp = simulation.calculate('prcomp', period)
        reduction_impot_exceptionnelle = simulation.calculate('reduction_impot_exceptionnelle', period)
        repsoc = simulation.calculate('repsoc', period)
        resimm = simulation.calculate('resimm', period)
        rsceha = simulation.calculate('rsceha', period)
        saldom = simulation.calculate('saldom', period)
        scelli = simulation.calculate('scelli', period)
        sofica = simulation.calculate('sofica', period)
        spfcpi = simulation.calculate('spfcpi', period)

        """ Crédits d'impôt crédités l'impôt sur les revenus de 2014 et + (non vérifié)"""
        period = period.this_year
        accult = simulation.calculate('accult', period)
        aidper = simulation.calculate('aidper', period)
        assloy = simulation.calculate('assloy', period)
        autent = simulation.calculate('autent', period)
        ci_garext = simulation.calculate('ci_garext', period)
        cotsyn = simulation.calculate('cotsyn', period)
        creimp = simulation.calculate('creimp', period)
        direpa = simulation.calculate('direpa', period)
        drbail = simulation.calculate('drbail', period)
        inthab = simulation.calculate('inthab', period)
        mecena = simulation.calculate('mecena', period)
        preetu = simulation.calculate('preetu', period)
        prlire = simulation.calculate('prlire', period)
        quaenv = simulation.calculate('quaenv', period)
        saldom2 = simulation.calculate('saldom2', period)

        """agrégation des RDB adultes + supplément famille monoparentale"""
        rdb = self.sum_by_entity(rdb_holder)
        sum_rdb_adulte = self.sum_by_entity(tspr_holder)

        credit_impot_monoparentales_holder = simulation.calculate_add('credit_impot_monoparentales', period)


        return period, max_(0, rbg + sum_rdb_adulte + credit_impot_monoparentales_holder - charges_deduc - (dfppce + donapd * garext) - (aidper, ci_garext, quaenv, saldom2))

In [11]:
#- variable ip_net -  irpp après décote (foyer fiscal):
#- la décote est évacuée du calcul de l'impôt

class ip_net(Variable):

    def function(self, simulation, period):
        '''
        irpp après décote
        '''
        period = period.this_year
        ir_plaf_qf = simulation.calculate('ir_plaf_qf', period)
        cncn_info_holder = simulation.compute('cncn_info', period)
        decote = simulation.calculate('decote', period)
        taux = simulation.legislation_at(period.start).ir.rpns.taux16

        return period, max_(0, ir_plaf_qf + self.sum_by_entity(cncn_info_holder) * taux)

In [12]:
#- variable reductions (Foyers Fiscaux) :
#- Les réductions d'impôt sont toutes supprimées : accult, adhcga, assvie, cappme, creaen, daepad, deffor, doment, domlog, domsoc, duflot, ecodev, ecpess, intagr, intcon, intemp, invfor, invlst, invrev, ip_net, locmeu, mecena, mohist, patnat, prcomp, reduction_impot_exceptionnelle, repsoc, resimm, rsceha, saldom, scelli, sofica, sofipe, spfcpi
#- Rappelons que les réductions d'impôt suivantes avaient été basculées vers des abattements :  dfppce (dons aux oeuvres), donapd (dons aide à la personne), garext (frais de garde extérieur) 

class reductions(Variable):

    @dated_function(start = date(2014, 1, 1))  # Not checked
    def function_2014__(self, simulation, period):
        '''
        Renvoie la somme des réductions d'impôt à intégrer pour l'année 2014 et + (non vérifiée)
        '''
        period = period.this_year
        accult = simulation.calculate('accult', period)
        adhcga = simulation.calculate('adhcga', period)
        cappme = simulation.calculate('cappme', period)
        creaen = simulation.calculate('creaen', period)
        daepad = simulation.calculate('daepad', period)
        deffor = simulation.calculate('deffor', period)
        dfppce = simulation.calculate('dfppce', period)
        doment = simulation.calculate('doment', period)
        domlog = simulation.calculate('domlog', period)
        donapd = simulation.calculate('donapd', period)
        duflot = simulation.calculate('duflot', period)
        ecpess = simulation.calculate('ecpess', period)
        garext = simulation.calculate('garext', period)
        intagr = simulation.calculate('intagr', period)
        invfor = simulation.calculate('invfor', period)
        invlst = simulation.calculate('invlst', period)
        ip_net = simulation.calculate('ip_net', period)
        locmeu = simulation.calculate('locmeu', period)
        mecena = simulation.calculate('mecena', period)
        mohist = simulation.calculate('mohist', period)
        patnat = simulation.calculate('patnat', period)
        prcomp = simulation.calculate('prcomp', period)
        reduction_impot_exceptionnelle = simulation.calculate('reduction_impot_exceptionnelle', period)
        repsoc = simulation.calculate('repsoc', period)
        resimm = simulation.calculate('resimm', period)
        rsceha = simulation.calculate('rsceha', period)
        saldom = simulation.calculate('saldom', period)
        scelli = simulation.calculate('scelli', period)
        sofica = simulation.calculate('sofica', period)
        spfcpi = simulation.calculate('spfcpi', period)

        total_reductions = 0
        return period, min_(ip_net, total_reductions)

In [13]:
#- variable credits_impot (Foyers Fiscaux) :
#- Les crédits d'impôt sont tous supprimés : accult, acqgpl, aidmob, assloy, autent, cotsyn, creimp, creimp_exc_2008, direpa, divide, drbail, inthab, jeunes, mecena, percvm, preetu, prlire
#- Rappelons que les crédits d'impôt suivants savaient été basculés vers des abattements : aidper, ci_garext, quaenv, saldom2

class credits_impot(Variable):

    def function(self, simulation, period):

            """ Crédits d'impôt crédités l'impôt sur les revenus de 2014 et + (non vérifié)"""
            period = period.this_year
            accult = simulation.calculate('accult', period)
            aidper = simulation.calculate('aidper', period)
            assloy = simulation.calculate('assloy', period)
            autent = simulation.calculate('autent', period)
            ci_garext = simulation.calculate('ci_garext', period)
            cotsyn = simulation.calculate('cotsyn', period)
            creimp = simulation.calculate('creimp', period)
            direpa = simulation.calculate('direpa', period)
            drbail = simulation.calculate('drbail', period)
            inthab = simulation.calculate('inthab', period)
            mecena = simulation.calculate('mecena', period)
            preetu = simulation.calculate('preetu', period)
            prlire = simulation.calculate('prlire', period)
            quaenv = simulation.calculate('quaenv', period)
            saldom2 = simulation.calculate('saldom2', period)

            return period, 0

In [14]:
#- Mise en place d'un revenu de base adulte égal à RSA socle célibataire - forfait logement célibataire
#- Pour les personnes en situation de handicap, le revenu de base est inclus dans l'Allocation Adulte Handicapé (AAH)


class rdb(Variable):
    column = FloatCol
    entity_class = Individus
    label = u"Revenu de base"

    def function(self, simulation, period):
        period = period.start.offset('first-of', 'month').period('month')
        age = simulation.calculate('age') 
        rmi = simulation.legislation_at(period.start).minim.rmi
        retraite_brute = simulation.calculate('retraite_brute',period)
        chomage_brut = simulation.calculate('chomage_brut',period)
        aah = simulation.calculate('aah',period)

        return period, max_(((age >= 18)* rmi.rmi * ( 1 -rmi.forfait_logement.taux1) + not_(age >= 18) * 0) - aah, 0)

In [15]:
# On enlève les enfants du calcul du nbptr (quotient_familial.enf*)
class nbptr(Variable):
    def function(self, simulation, period):
        '''
        Nombre de parts du foyer
        'foy'
        note 1 enfants et résidence alternée (formulaire 2041 GV page 10)

        quotient_familial.conj : nb part associées au conjoint d'un couple marié ou pacsé
        quotient_familial.enf1 : nb part 2 premiers enfants
        quotient_familial.enf2 : nb part enfants de rang 3 ou plus
        quotient_familial.inv1 : nb part supp enfants invalides (I, G)
        quotient_familial.inv2 : nb part supp adultes invalides (R)
        quotient_familial.not31 : nb part supp note 3 : cases W ou G pour veuf, celib ou div
        quotient_familial.not32 : nb part supp note 3 : personne seule ayant élevé des enfants
        quotient_familial.not41 : nb part supp adultes invalides (vous et/ou conjoint) note 4
        quotient_familial.not42 : nb part supp adultes anciens combattants (vous et/ou conjoint) note 4
        quotient_familial.not6 : nb part supp note 6
        quotient_familial.isol : demi-part parent isolé (T)
        quotient_familial.edcd : enfant issu du mariage avec conjoint décédé;
        '''
        period = period.start.offset('first-of', 'month').period('year')
        nb_pac = simulation.calculate('nb_pac', period)
        maries_ou_pacses = simulation.calculate('maries_ou_pacses', period)
        celibataire_ou_divorce = simulation.calculate('celibataire_ou_divorce', period)
        veuf = simulation.calculate('veuf', period)
        jeune_veuf = simulation.calculate('jeune_veuf', period)
        nbF = simulation.calculate('nbF', period)
        nbG = simulation.calculate('nbG', period)
        nbH = simulation.calculate('nbH', period)
        nbI = simulation.calculate('nbI', period)
        nbR = simulation.calculate('nbR', period)
        nbJ = simulation.calculate('nbJ', period)
        caseP = simulation.calculate('caseP', period)
        caseW = simulation.calculate('caseW', period)
        caseG = simulation.calculate('caseG', period)
        caseE = simulation.calculate('caseE', period)
        caseK = simulation.calculate('caseK', period)
        caseN = simulation.calculate('caseN', period)
        caseF = simulation.calculate('caseF', period)
        caseS = simulation.calculate('caseS', period)
        caseL = simulation.calculate('caseL', period)
        caseT = simulation.calculate('caseT', period)
        quotient_familial = simulation.legislation_at(period.start).ir.quotient_familial

        no_pac = nb_pac == 0  # Aucune personne à charge en garde exclusive
        has_pac = not_(no_pac)
        no_alt = nbH == 0  # Aucun enfant à charge en garde alternée
        has_alt = not_(no_alt)

        # # nombre de parts liées aux enfants à charge
        # que des enfants en résidence alternée
        enf1 = (no_pac & has_alt) * (quotient_familial.enf1 * min_(nbH, 2) * 0.5
                                     + quotient_familial.enf2 * max_(nbH - 2, 0) * 0.5)
        # pas que des enfants en résidence alternée
        enf2 = (has_pac & has_alt) * ((nb_pac == 1) * (quotient_familial.enf1 * min_(nbH, 1) * 0.5
            + quotient_familial.enf2 * max_(nbH - 1, 0) * 0.5) + (nb_pac > 1) * (quotient_familial.enf2 * nbH * 0.5))
        # pas d'enfant en résidence alternée
        enf3 = quotient_familial.enf1 * min_(nb_pac, 2) + quotient_familial.enf2 * max_((nb_pac - 2), 0)

        enf = enf1 + enf2 + enf3
        # # note 2 : nombre de parts liées aux invalides (enfant + adulte)
        n2 = quotient_familial.inv1 * (nbG + nbI / 2) + quotient_familial.inv2 * nbR

        # # note 3 : Pas de personne à charge
        # - invalide

        n31a = quotient_familial.not31a * (no_pac & no_alt & caseP)
        # - ancien combatant
        n31b = quotient_familial.not31b * (no_pac & no_alt & (caseW | caseG))
        n31 = max_(n31a, n31b)
        # - personne seule ayant élevé des enfants
        n32 = quotient_familial.not32 * (no_pac & no_alt & ((caseE | caseK) & not_(caseN)))
        n3 = max_(n31, n32)
        # # note 4 Invalidité de la personne ou du conjoint pour les mariés ou
        # # jeunes veuf(ve)s
        n4 = max_(quotient_familial.not41 * (1 * caseP + 1 * caseF), quotient_familial.not42 * (caseW | caseS))

        # # note 5
        #  - enfant du conjoint décédé
        n51 = quotient_familial.cdcd * (caseL & ((nbF + nbJ) > 0))
        #  - enfant autre et parent isolé
        n52 = quotient_familial.isol * caseT * (((no_pac & has_alt) * ((nbH == 1) * 0.5 + (nbH >= 2))) + 1 * has_pac)
        n5 = max_(n51, n52)

        # # note 6 invalide avec personne à charge
        n6 = quotient_familial.not6 * (caseP & (has_pac | has_alt))

        # # note 7 Parent isolé
        n7 = quotient_familial.isol * caseT * ((no_pac & has_alt) * ((nbH == 1) * 0.5 + (nbH >= 2)) + 1 * has_pac)

        # # Régime des mariés ou pacsés
        #m = 1 + quotient_familial.conj + enf + n2 + n4
        m = 1 + quotient_familial.conj + n2 + n4
        
        # # veufs  hors jeune_veuf
        # v = 1 + enf + n2 + n3 + n5 + n6
        v = 1 + n2 + n3 + n5 + n6
        
        # # celib div
        #c = 1 + enf + n2 + n3 + n6 + n7
        c = 1 + n2 + n3 + n6 + n7

        return period, (maries_ou_pacses | jeune_veuf) * m + (veuf & not_(jeune_veuf)) * v + celibataire_ou_divorce * c

In [16]:
# Suppression des allocations familiales

class af(Variable):

    def function(self, simulation, period):
        period = period.this_month
        af_base = simulation.calculate('af_base', period)
        af_majo = simulation.calculate('af_majo', period)
        af_forf = simulation.calculate('af_forf', period)

        #return period, af_base + af_majo + af_forf
        return period, af_base * 0

In [17]:
# Suppression du complément familial

class cf(Variable):

    def function(self, simulation, period):
        '''
        L'allocation de base de la paje n'est pas cumulable avec le complément familial
        '''
        period = period.this_month
        paje_base = simulation.calculate('paje_base', period)
        apje_temp = simulation.calculate('apje_temp', period)
        ape_temp = simulation.calculate('ape_temp', period)
        cf_montant = simulation.calculate('cf_montant', period)
        residence_mayotte = simulation.calculate('residence_mayotte', period)

        cf_brut = not_(paje_base) * (apje_temp <= cf_montant) * (ape_temp <= cf_montant) * cf_montant
        # return period, not_(residence_mayotte) * round(cf_brut, 2)
        return period, not_(residence_mayotte) * round(cf_brut, 2) * 0

In [18]:
# Suppression de l'allocation de rentrée scolaire

class ars(Variable):

    def function(self, simulation, period):
        '''
        Allocation de rentrée scolaire brute de CRDS
        '''
        period_br = period.this_year
        return period_br, self.zeros()

In [19]:
# Création d'un revenu de base enfant - Version individus au niveau des allocations familiales actuelles

class rdb_enf(Variable):
    column = FloatCol
    entity_class = Individus
    label = u"Revenu de base enfant"

    def function(self, simulation, period):
        period = period.start.offset('first-of', 'month').period('month')
        age = simulation.calculate('age')
        P = simulation.legislation_at(period.start).fam.af        
        bmaf = P.bmaf

        return period, ((age < 14) * 0.41 + not_(age < 14) * 0.57) * bmaf * (age <= 18)

In [20]:
#- Mise en place d'un crédit d'impot familles monoparentales montant (150€)
class credit_impot_monoparentales(Variable):
    column = FloatCol
    entity_class = Menages
    label = u"credit_impot_monoparentales"

    def function(self, simulation, period):
        period = period.start.offset('first-of', 'month').period('month')
        nb_enf_a_charge = simulation.calculate('nombre_enfants_a_charge_menage',period)
        caseT = simulation.calculate('caseT',period) #Egal True si le parent est isolé
      
        
        #return period, or_(and_(age_holder >= 18, nb_enf_a_charge > 0, caseT), or_(age_holder < 18, nb_enf_a_charge <= 0, not_(caseT)) * 0) * 100
        return period, (nb_enf_a_charge > 0) * (caseT) * 150
    #Si le parent est isolé, avec au moins un enfant, et qu'il est majeur il reçoit la pension

In [21]:
#- Supprimer le RSA
class rsa_socle(Variable):

    def function(self, simulation, period):
        period = period.this_month
        nb_par = simulation.calculate('nb_par', period)
        eligib = simulation.calculate('rsa_eligibilite', period)
        nb_enfant_rsa = simulation.calculate('nb_enfant_rsa', period)
        rmi = simulation.legislation_at(period.start).minim.rmi

        nbp = nb_par + nb_enfant_rsa

        taux = (
            1 +
            (nbp >= 2) * rmi.txp2 +
            (nbp >= 3) * rmi.txp3 +
            (nbp >= 4) * ((nb_par == 1) * rmi.txps + (nb_par != 1) * rmi.txp3) +
            max_(nbp - 4, 0) * rmi.txps
        )
        #on met à zéro
        return period, eligib * rmi.rmi * taux * 0

In [22]:
#- Supprimer la prime d'activité (PPA)
class ppa(DatedVariable):

    @dated_function(start = date(2016, 1, 1))
    def function(self, simulation, period):
        period = period.this_month
        seuil_non_versement = simulation.legislation_at(period.start).minim.ppa.seuil_non_versement
        # éligibilité étudiants

        ppa_eligibilite_etudiants = simulation.calculate('ppa_eligibilite_etudiants', period)
        m_1 = period.last_month
        m_2 = m_1.last_month
        m_3 = m_2.last_month
        ppa = sum(simulation.calculate('ppa_fictive', period2, extra_params = [period])
            for period2 in [m_1, m_2, m_3]) / 3
        ppa = ppa * ppa_eligibilite_etudiants * (ppa >= seuil_non_versement)

        return period, ppa*0

In [23]:
#- Intégrer le revenu de base au revenu disponible
#- (la ppe est maintenue dans cette simulation, jusqu'à sa disparition en 2016)

class revdisp(Variable):

    def function(self, simulation, period):
        '''
        Revenu disponible - ménage
        'men'
        '''
        period = period.start.period('year').offset('first-of')
        rev_trav_holder = simulation.compute('rev_trav', period)
        pen_holder = simulation.compute('pen', period)
        rev_cap_holder = simulation.compute('rev_cap', period)
        psoc_holder = simulation.compute('psoc', period)
        ppe_holder = simulation.compute('ppe', period)
        impo = simulation.calculate('impo', period)
        rdb_holder = simulation.calculate_add('rdb', period)
        credit_impot_monoparentales_holder = simulation.calculate_add('credit_impot_monoparentales', period)
        rdb_enf_holder = simulation.calculate_add('rdb_enf', period)

        pen = self.sum_by_entity(pen_holder)
        ppe = self.cast_from_entity_to_role(ppe_holder, role = VOUS)
        ppe = self.sum_by_entity(ppe)
        psoc = self.cast_from_entity_to_role(psoc_holder, role = CHEF)
        psoc = self.sum_by_entity(psoc)
        rev_cap = self.sum_by_entity(rev_cap_holder)
        rev_trav = self.sum_by_entity(rev_trav_holder)
        rdb = self.sum_by_entity(rdb_holder)
        rdb_enf = self.sum_by_entity(rdb_enf_holder)
        
        return period, rev_trav + pen + rev_cap + psoc + ppe + impo + rdb + credit_impot_monoparentales_holder + rdb_enf

In [24]:
# Legislation JSON related helpers

from openfisca_core import legislations, periods


def get_node(legislation_json, path):
    '''
    Return a node in the `legislation_json` tree at the given `path`.
    '''
    node = legislation_json
    for key in path:
        assert 'children' in node, 'Expected "children" key, got: {}'.format(legislation_json.keys())
        assert key in node['children'], 'Expected "{}" key, got: {}'.format(key, legislation_json['children'].keys())
        node = node['children'][key]
    return node


def is_scale(node):
    '''
    Returns True if the given `node` is a "Scale" (Barème in French).
    '''
    return node.get('@type') == 'Scale' and isinstance(node.get('brackets'), list)


def replace_rate(scale_node, period, new_rate, old_rate=None, bracket_index=None):
    '''
    In a given `scale_node`, for a given `period`, replace either:
        - an `old_rate` by a `new_rate`
        - a `new_rate` given a `bracket_index`
    Modifies the `scale_node`.
    '''
    assert bool(old_rate is not None) ^ bool(bracket_index is not None), 'You must provide either `old_rate` or `bracket_index`'
    assert is_scale(scale_node), 'Scale node expected'
    period = periods.period(period)
    if bracket_index is not None:
        bracket = scale_node['brackets'][bracket_index]
        for rate in bracket['rate']:
            if rate['start'] >= str(period.start) and ('stop' not in rate or rate['stop'] <= str(period.stop)):
                rate['value'] = new_rate
    elif old_rate is not None:
        for bracket in scale_node['brackets']:
            for rate in bracket['rate']:
                if rate['value'] == old_rate and rate['start'] >= str(period.start) and (
                    'stop' not in rate or rate['stop'] <= str(period.stop)):
                    rate['value'] = new_rate

                    
def at_instant(node, instant):
    instant = str(periods.instant(instant))
    dated_node = legislations.generate_dated_node_json(node, instant)
    return dated_node


def print_json(node):
    print(json.dumps(node, indent=2))

In [25]:
#- CHANGEMENT DU CALCUL DE L'IRPP
#- le barème est modifié avec les tranches 0%, 30%, 41%, 45%
#- les tranches sont modifiées : RDB adulte, XXXXX €, XXXXX €

def modify_legislation_json(reference_legislation_json):
    ir_bareme = get_node(reference_legislation_json, ['ir', 'bareme'])
    assert is_scale(ir_bareme)
    period = periods.period(2016)
    # print_json(at_instant(ir_bareme, period.start))

    replace_rate(ir_bareme, period=period, bracket_index=0, new_rate=0.3)
    replace_rate(ir_bareme, period=period, bracket_index=1, new_rate=0.3)
    replace_rate(ir_bareme, period=period, bracket_index=2, new_rate=0.3)

    # print_json(at_instant(ir_bareme, period.start))
    return reference_legislation_json

In [26]:
class ReformeRevenuDeBase_IRPP(Reform):
    name = u"Réforme Revenu de base par IRPP"
    
    # On écrit dans la parenthèse ci-dessous toutes les variables que l'on a redéfinies
    def apply(self):
        for variable in [salaire_imposable, chomage_imposable, retraite_imposable, revenu_assimile_salaire_apres_abattements, credits_impot, cd1, rng, ip_net, rdb, nbptr, af, cf, ars, rdb_enf, credit_impot_monoparentales, rsa_socle, ppa, revdisp]:
            self.update_variable(variable)
        self.modify_legislation_json(modifier_function = modify_legislation_json)

Tests


In [27]:
reform = ReformeRevenuDeBase_IRPP(tax_benefit_system)

In [28]:
parent1_salaire_de_base = 60000

In [29]:
scenario_ref_individu_seul = tax_benefit_system.new_scenario().init_single_entity(
    period = 2014,
    parent1 = dict(
        date_naissance = date(1980, 1, 1),
        salaire_de_base = parent1_salaire_de_base,
        statut_marital = u'Célibataire',
        ),
    foyer_fiscal = dict(
        caseT = True,
        ),
    enfants = [
        dict(
            date_naissance = date(2010, 1, 1),
            ),
        ],
    )

In [31]:
simulation_ref_individu_seul = scenario_ref_individu_seul.new_simulation(debug = True)

In [32]:
scenario_rdb_individu_seul = reform.new_scenario().init_single_entity(
    period = 2014,
    parent1 = dict(
        date_naissance = date(1980, 1, 1),
        salaire_de_base = parent1_salaire_de_base,
        statut_marital = u'Célibataire',
        ),
    foyer_fiscal = dict(
        caseT = True,
        ),
    enfants = [
        dict(
            date_naissance = date(2010, 1, 1),
            ),
        ],
    )

In [33]:
simulation_rdb_individu_seul = scenario_rdb_individu_seul.new_simulation(debug = True)

Calculs de référence (revenu disponible, IR, RSA, CSG imposable/deductible, impot total)


In [34]:
simulation_ref_individu_seul.calculate('irpp')


Out[34]:
array([-4019.29296875], dtype=float32)

In [40]:
simulation_rdb_individu_seul.calculate('irpp')



AssertionErrorTraceback (most recent call last)
<ipython-input-40-2c0dceb91d66> in <module>()
----> 1 simulation_rdb_individu_seul.calculate('irpp')

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in calculate(self, column_name, period, **parameters)
     72         if period is None:
     73             period = self.period
---> 74         return self.compute(column_name, period = period, **parameters).array
     75 
     76     def calculate_add(self, column_name, period = None, **parameters):

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in compute(self, column_name, period, **parameters)
    157                 caller_input_variables_infos.append(variable_infos)
    158         holder = self.get_or_new_holder(column_name)
--> 159         result = holder.compute(period = period, **parameters)
    160         if print_trace:
    161             self.print_trace(variable_name=column_name, period=period, **print_trace_kwargs)

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/holders.pyc in compute(self, period, **parameters)
    135         if (column_start_instant is None or column_start_instant <= period.start) \
    136                 and (column_stop_instant is None or period.start <= column_stop_instant):
--> 137             formula_dated_holder = self.formula.compute(period = period, **parameters)
    138             assert formula_dated_holder is not None
    139             if not column.is_permanent:

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/formulas.pyc in compute(self, period, **parameters)
    538 
    539         try:
--> 540             self.check_for_cycle(period)
    541             if debug or trace:
    542                 simulation.stack_trace.append(dict(

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/formulas.pyc in check_for_cycle(self, period)
    483             # It would be a pure circular definition.
    484             requested_periods = requested_periods_by_variable_name[variable_name]
--> 485             assert period not in requested_periods and not column.is_permanent, get_error_message()
    486             if simulation.max_nb_cycles is None or len(requested_periods) > simulation.max_nb_cycles:
    487                 message = get_error_message()

AssertionError: Circular definition detected on formula irpp<2014>. Formulas and periods involved: iai<2014>, iaidrdi<2014>, ip_net<2014>, ir_brut<2014>, ir_plaf_qf<2014>, irpp<2014>, rng<2014>, rni<2014>, taux_effectif<2014>.

In [36]:
simulation_ref_individu_seul.calculate_add('rsa')


Out[36]:
array([ 817.62524414], dtype=float32)

In [38]:
simulation_ref_individu_seul.calculate_add('csg_imposable_salaire')


Out[38]:
array([-1414.8001709,    -0.       ], dtype=float32)

In [42]:
simulation_ref_individu_seul.calculate_add('csg_deductible_salaire')


Out[42]:
array([-3006.45019531,    -0.        ], dtype=float32)

In [43]:
simulation_ref_individu_seul.calculate_add('tot_impot')


Out[43]:
array([ 8745.06445312], dtype=float32)

Calculs avec réforme RDB (revenu disponible, IR, RSA, CSG imposable/deductible, impot total)


In [45]:
simulation_rdb_individu_seul.calculate('revdisp')



AssertionErrorTraceback (most recent call last)
<ipython-input-45-b50c7d016a2d> in <module>()
----> 1 simulation_rdb_individu_seul.calculate_add('revdisp')

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in calculate_add(self, column_name, period, **parameters)
     77         if period is None:
     78             period = self.period
---> 79         return self.compute_add(column_name, period = period, **parameters).array
     80 
     81     def calculate_add_divide(self, column_name, period = None, **parameters):

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/simulations.pyc in compute_add(self, column_name, period, **parameters)
    174                 caller_input_variables_infos.append(variable_infos)
    175         holder = self.get_or_new_holder(column_name)
--> 176         return holder.compute_add(period = period, **parameters)
    177 
    178     def compute_add_divide(self, column_name, period = None, **parameters):

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/holders.pyc in compute_add(self, period, **parameters)
    162         parameters['accept_other_period'] = True
    163         while True:
--> 164             dated_holder = self.compute(period = requested_period, **parameters)
    165             requested_start = requested_period.start
    166             returned_period = dated_holder.period

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/holders.pyc in compute(self, period, **parameters)
    135         if (column_start_instant is None or column_start_instant <= period.start) \
    136                 and (column_stop_instant is None or period.start <= column_stop_instant):
--> 137             formula_dated_holder = self.formula.compute(period = period, **parameters)
    138             assert formula_dated_holder is not None
    139             if not column.is_permanent:

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/formulas.pyc in compute(self, period, **parameters)
    538 
    539         try:
--> 540             self.check_for_cycle(period)
    541             if debug or trace:
    542                 simulation.stack_trace.append(dict(

/home/notebook/virtualenvs/openfisca/local/lib/python2.7/site-packages/openfisca_core/formulas.pyc in check_for_cycle(self, period)
    483             # It would be a pure circular definition.
    484             requested_periods = requested_periods_by_variable_name[variable_name]
--> 485             assert period not in requested_periods and not column.is_permanent, get_error_message()
    486             if simulation.max_nb_cycles is None or len(requested_periods) > simulation.max_nb_cycles:
    487                 message = get_error_message()

AssertionError: Circular definition detected on formula revdisp<2014>. Formulas and periods involved: af<2014>, iai<2014>, iaidrdi<2014>, ip_net<2014>, ir_brut<2014>, ir_plaf_qf<2014>, irpp<2014>, pfam<2014>, psoc<2014>, revdisp<2014>, rng<2014>, rni<2014>, taux_effectif<2014>.

In [ ]:
simulation_rdb_individu_seul.calculate('irpp')

In [ ]:
simulation_rdb_individu_seul.calculate('csg_deductible_salaire')

In [ ]:
simulation_rdb_individu_seul.calculate('tot_impot')

In [ ]:
# trace
simulation_rdb_individu_seul.calculate('tot_impot')
#print web_tools.get_trace_tool_link(scenario, ['tot_impot'])

In [ ]: