OpenFisca handles the fact that the legislation changes over time.
Many legislation parameters are regularly re-evaluated while the variables using them stay the same.
Example: the
taxes
parameter can change without altering the code of theflat_tax_on_salary
variable that uses that parameter.
In that case, add the new parameter values and their start dates in the appropriate parameter files.
taxes:
salary:
rate:
description: Rate for the flat tax on salaries
values:
2016-01-01:
value: 0.25
reference: https://www.legislation-source.com/2016
2015-01-01:
value: 0.20
reference: https://www.legislation-source.com/2015
taxes:
salary:
rate:
description: Rate for the flat tax on salaries
values:
2017-01-01:
value: 0.3
reference: https://www.legislation-source.com/2017
2016-01-01:
value: 0.25
reference: https://www.legislation-source.com/2016
2015-01-01:
value: 0.2
reference: https://www.legislation-source.com/2015
After this change, in a formula:
parameters('2016-04').taxes.salary.rate
is 0.25
parameters('2017-01').taxes.salary.rate
is 0.3
parameters('2022-01').taxes.salary.rate
is 0.3
Some fiscal or benefit mechanism significantly evolve over time and call for a change in the formula that computes them. In this case, a simple parameter adjustement is not enough.
For instance, let’s assume that from the 1st of Jan. 2017, the flat_tax_on_salary
is not applied anymore on the first 1000
earned by a person.
We implement this rule by adding a new formula to our variable, and dating it:
class flat_tax_on_salary(Variable):
value_type = float
entity = Person
label = u"Individualized and monthly paid tax on salaries"
definition_period = MONTH
def formula_2017(person, period, parameters):
salary = person('salary', period)
salary_above_1000 = min_(salary - 1000, 0)
return salary_above_1000 * parameters(period).taxes.salary.rate
def formula(person, period, parameters):
salary = person('salary', period)
return salary * parameters(period).taxes.salary.rate
If the flat_tax_on_salary
is calculated for a person before the 31st of Dec. 2016 (included), formula
is used. If it is called after the 1st of Jan 2017 (included), formula_2017
is used.
Formula naming rules:
A formula name must always start with formula
.
To define a starting date for a formula, we add to its name a suffix made of an underscore followed by a date.
For instance, formula_2017_01_01
is active from the 1st of Jan. 2017.
When defining a date, the month is given before the day.
When no month or day is specified, OpenFisca uses ‘01’ as default value.
For instance, formula_2017
is equivalent to formula_2017_01_01
.
If no date is specified for a formula, OpenFisca will consider that this formula has been active since the dawn of time (or more precisely, since 0001-01-01
, as Python does not handle B.C. dates).
For instance, formula
is active on 2010
.
A formula is active until another formula, starting later, becomes active and replaces it (or until the variable end
date is reached).
For instance, formula
is active until 2016-12-31
(included). On the day after, 2017-01-01
, formula_2017
becomes active, and formula
becomes inactive.
In our previous example, we assumed that flat_tax_on_salary
had always had a formula, since the dawn of time. This is a reasonable hypothesis if we are only interested in running computations for recent years.
But most fiscal and benefit mechanisms have been introduced at some point. Let’s for instance assume that our flat_tax_on_salary
only appeared in our legislation on the 1st of June 2005.
This is easily implemented by dating the two formulas:
class flat_tax_on_salary(Variable):
value_type = float
entity = Person
label = u"Individualized and monthly paid tax on salaries"
definition_period = MONTH
def formula_2017(person, period, parameters):
salary = person('salary', period)
salary_above_1000 = min_(salary - 1000, 0)
return salary_above_1000 * parameters(period).taxes.salary.rate
def formula_2005_06(person, period, parameters):
salary = person('salary', period)
return salary * parameters(period).taxes.salary.rate
Only a few characters changed in comparison with the last example: the suffix _2005_06
has been added to the second formula name.
Note that if flat_tax_on_salary
is calculated before 2005-05-31
(included), none of the two formulas is used, as they are both inactive at this time. Instead, the variable default value is returned.
As legislation evolves, some fiscal or benefit mechanisms disappear.
Let’s for instance assume that a progressive_income_tax
used to exist before the flat_tax_on_salary
was introduced. This progressive tax then disappeared on the 1st of June 2005.
This is implemented with an end
attribute that defines the last day a variable can be calculated:
class progressive_income_tax(Variable):
value_type = float
entity = Person
label = u"Former tax replaced by the flat tax on the 1st of June 2005"
definition_period = MONTH
end = '2005-05-31'
def formula(person, period, legislation):
# Apply a marginal scale to the person's income
...
If progressive_income_tax
is called before 2005-05-31
(included), formula
will be used.
However, if progressive_income_tax
is calculated after 2005-06-01
(included), formula
is not used, as it is not active anymore at this time. Instead, the variable default value is returned.
Note that:
The end
day is inclusive: it is the last day a variable and its formulas are active (and not the first day it is not active anymore).
The end
value is a string of format YYYY-MM-DD
where YYYY
, MM
and DD
are respectively a year, month and day.
When defining a date, the month is given before the day.
Like variables, parameters tend to disappear as legislation evolves.
For instance, let’s assume that the housing_allowance
parameter is only defined from 2010 (1st of January) to the end of 2016 (31st of December).
We can end housing_allowance
at a specific date by simply assigning null
as a value for that date (2017-01-01
in this case).
description: Housing allowance amount (as a fraction of the rent)
# in parameters/housing_allowance.yaml
metadata:
unit: /1
values:
# This parameter is only defined from the 1st of Jan 2010 to the 1st of Jan 2017 (excluded).
2010-01-01:
value: 0.25
2017-01-01:
value: null
If housing_allowance
is called before 2016-12-31
(included) and after 2010-01-01
(included), the 0.25
value will be used.
However, trying to obtain the parameter after 2017-01-01
(included) will raise an error:
Example:
tax_benefit_system.parameters(2017).benefits.housing_allowanceOutput:
ParameterNotFoundError: The parameter 'benefits[housing_allowance]' was not found in the 2017-01-01 tax and benefit system.
The same thing can be done for a scale by assigning a null
value for the end date across all brackets.
The following example of social_security_contribution
ends as of the 1st of January 2017.
description: Social security contribution tax scale
metadata:
threshold_unit: currency-EUR
rate_unit: /1
brackets:
- rate:
2013-01-01:
value: 0.03
2017-01-01:
value: null
threshold:
2013-01-01:
value: 0.0
2017-01-01:
value: null
- rate:
2013-01-01:
value: 0.1
2017-01-01:
value: null
threshold:
2013-01-01:
value: 12000.0
2017-01-01:
value: null
It is also possible to only end one bracket. We use the same social_security_contribution
example to illustrate, by ending only the second bracket as of the 1st of January 2017.
description: Social security contribution tax scale
metadata:
threshold_unit: currency-EUR
rate_unit: /1
brackets:
- rate: # 1st bracket
2013-01-01:
value: 0.03
2017-01-01:
value: 0.04
threshold:
2013-01-01:
value: 0.0
- rate: # 2nd bracket
2013-01-01:
value: 0.1
2017-01-01:
value: null
threshold:
2013-01-01:
value: 12000.0
2017-01-01:
value: null