refactor(tests): udpates tests based on the english lexer

This commit is contained in:
EmileRolley 2021-03-09 20:48:35 +01:00
parent 304f6a27ae
commit a42d0c7728
6 changed files with 471 additions and 419 deletions

View File

@ -1,14 +1,14 @@
@@Include: ../tutorial_en.catala_en@@
> Include: ../tutorial_en.catala_en
@Test@
## [Test]
/*
```catala
declaration scope UnitTest1:
context tax_computation scope NewIncomeTaxComputation
scope UnitTest1:
definition
tax_computation.individual
tax_computation.individual
equals
Individual {
-- income: $230,000
@ -26,4 +26,4 @@ scope UnitTest2:
}
assertion tax_computation.income_tax = $0.00
*/
```

View File

@ -1,206 +1,222 @@
@@The Catala language tutorial@@
## The Catala language tutorial
Welcome to this tutorial, whose objective is to guide you through the features
of the Catala language and teach you how to annotate a legislative text using
the language. This document is addressed primarily to developers or people that
have a programming background, though tech-savvy lawyers will probably figure
Welcome to this tutorial, whose objective is to guide you through the features
of the Catala language and teach you how to annotate a legislative text using
the language. This document is addressed primarily to developers or people that
have a programming background, though tech-savvy lawyers will probably figure
things out.
@@Literate programming@@+
### Literate programming
To begin writing a Catala program, you must start from the text of the
legislative source that will justify the code that you will write. Concretely,
that means copy-pasting the text of the law into a Catala source file and
formatting it according so that Catala can understand it. Catala source files
have the ".catala_en" extension. If you were to write a Catala program for a
To begin writing a Catala program, you must start from the text of the
legislative source that will justify the code that you will write. Concretely,
that means copy-pasting the text of the law into a Catala source file and
formatting it according so that Catala can understand it. Catala source files
have the ".catala_en" extension. If you were to write a Catala program for a
French law, you would use the ".catala_fr" extension.
You can write any kind of plain text in Catala, and it will be printed as is
in PDF or HTML output. You can split your text into short lines, those
will appear as a single paragraph in the output. If you want to create a
new paragrah, you have to leave a blank line in the source.
You can write any kind of plain text in Catala, and it will be printed as is
in PDF or HTML output. You can split your text into short lines, those
will appear as a single paragraph in the output. If you want to create a
new paragrah, you have to leave a blank line in the source.
Catala allows you to declare section or subsection headers as it is done
here, with the "at" symbol repeated twice. You can define heading of lower
importance by adding increasing numbers of "+" after the title of the heading.
here, with the "at" symbol repeated twice. You can define heading of lower
importance by adding increasing numbers of "+" after the title of the heading.
The fundamental division unit is the article, introduced by a single "at".
Let's analyse a fictional example that defines an income tax.
@Article 1@
The income tax for an individual is defined as a fixed percentage of the
#### [Article 1]
The income tax for an individual is defined as a fixed percentage of the
individual's income over a year.
/*
# Welcome to the code mode of Catala. This is a comment, because the line is
```catala
# Welcome to the code mode of Catala. This is a comment, because the line is
# prefixed by #.
# We will soon learn what to write here in order to translate the meaning
# We will soon learn what to write here in order to translate the meaning
# of the article into Catala code.
*/
```
To do that, we will intertwine short snippets of code between the sentences of
the legislative text. Each snippet of code should be as short as possible and
as close as possible to the actual sentence that justifies the code. This style
is called literate programming, a programming paradigm invented by the famous
To do that, we will intertwine short snippets of code between the sentences of
the legislative text. Each snippet of code should be as short as possible and
as close as possible to the actual sentence that justifies the code. This style
is called literate programming, a programming paradigm invented by the famous
computer scientist Donald Knuth in the 70s.
@@Defining a fictional income tax@@+
### Defining a fictional income tax
The content of article 1 uses a lot of implicit context: there exists an
individual with an income, as well as an income tax that the individual has
to pay each year. Even if this implicit context is not verbatim in the law,
we have to explicit it for programming purposes. Concretely, we need a
"metadata" section that defines the shape and types of the data used
The content of article 1 uses a lot of implicit context: there exists an
individual with an income, as well as an income tax that the individual has
to pay each year. Even if this implicit context is not verbatim in the law,
we have to explicit it for programming purposes. Concretely, we need a
"metadata" section that defines the shape and types of the data used
inside the law.
Let's start our metadata section by declaring the type information for the
Let's start our metadata section by declaring the type information for the
individual:
@@Begin metadata@@
/*
> Begin metadata
```catala
declaration structure Individual:
# The name of the structure "Individual", must start with an
# uppercase letter: this is the CamelCase convention.
data income content money
# In this line, "income" is the name of the structure field and
# In this line, "income" is the name of the structure field and
# "money" is the type of what is stored in that field.
# Available types include: integer, decimal, money, date, duration,
# Available types include: integer, decimal, money, date, duration,
# and any other structure or enumeration that you declare
data number_of_children content integer
# "income" and "number_of_children" start by a lowercase letter,
# "income" and "number_of_children" start by a lowercase letter,
# they follow the snake_case convention
*/
@@End metadata@@
```
This structure contains two data fields, "income" and "number_of_children". Structures are
useful to group together data that goes together. Usually, you
get one structure per concrete object on which the law applies (like the
individual). It is up to you to decide how to group the data together,
> End metadata
This structure contains two data fields, "income" and "number_of_children". Structures are
useful to group together data that goes together. Usually, you
get one structure per concrete object on which the law applies (like the
individual). It is up to you to decide how to group the data together,
but you should aim to optimize code readability.
Sometimes, the law gives an enumeration of different situations. These
Sometimes, the law gives an enumeration of different situations. These
enumerations are modeled in Catala using an enumeration type, like:
@@Begin metadata@@
/*
> Begin metadata
```catala
declaration enumeration TaxCredit:
# The name "TaxCredit" is also written in CamelCase
# The name "TaxCredit" is also written in CamelCase
-- NoTaxCredit
# This line says that "TaxCredit" can be a "NoTaxCredit" situation
-- ChildrenTaxCredit content integer
# This line says that alternatively, "TaxCredit" can be a
-- ChildrenTaxCredit content integer
# This line says that alternatively, "TaxCredit" can be a
# "ChildrenTaxCredit" situation. This situation carries a content
# of type integer corresponding to the number of children concerned
# of type integer corresponding to the number of children concerned
# by the tax credit. This means that if you're in the "ChildrenTaxCredit"
# situation, you will also have access to this number of children
*/
@@End metadata@@
# situation, you will also have access to this number of children
```
In computer science terms, such an enumeration is called a "sum type" or simply
an enum. The combination of structures and enumerations allow the Catala
programmer to declare all possible shapes of data, as they are equivalent to
the powerful notion of "algebraic datatypes".
> End metadata
We've defined and typed the data that the program will manipulate. Now we have
to define the logical context in which these data will evolve. This is done in
Catala using "scopes". Scopes are close to functions in terms of traditional
In computer science terms, such an enumeration is called a "sum type" or simply
an enum. The combination of structures and enumerations allow the Catala
programmer to declare all possible shapes of data, as they are equivalent to
the powerful notion of "algebraic datatypes".
We've defined and typed the data that the program will manipulate. Now we have
to define the logical context in which these data will evolve. This is done in
Catala using "scopes". Scopes are close to functions in terms of traditional
programming. Scopes also have to be declared in metadata, so here we go:
@@Begin metadata@@
/*
> Begin metadata
```catala
declaration scope IncomeTaxComputation:
# Scope names use CamelCase
context individual content Individual
# This line declares a context element of the scope, which is akin to
# a function parameter in computer science term. This is the piece of
# This line declares a context element of the scope, which is akin to
# a function parameter in computer science term. This is the piece of
# data on which the scope will operate
context fixed_percentage content decimal
context income_tax content money
*/
@@End metadata@@
```
We now have everything to annotate the contents of article 1, which is copied
> End metadata
We now have everything to annotate the contents of article 1, which is copied
over below.
@Article 1@
The income tax for an individual is defined as a fixed percentage of the
#### [Article 1]
The income tax for an individual is defined as a fixed percentage of the
individual's income over a year.
/*
```catala
scope IncomeTaxComputation:
definition income_tax equals
individual.income *$ fixed_percentage
*/
```
In the code, we are defining inside our scope the amount of the income tax
according to the formula described in the article. When defining formulae,
you have access to all the usual arithmetic operators: addition "+",
substraction "-", multiplication "*" and division (slash).
In the code, we are defining inside our scope the amount of the income tax
according to the formula described in the article. When defining formulae,
you have access to all the usual arithmetic operators: addition "+",
substraction "-", multiplication "*" and division (slash).
However, in the Catala code, you can see that we use "*$" to multiply the
individual income by the fixed percentage. The $ suffix indicates that we
are performing a multiplication on an amount of money. Indeed, in Catala,
you have to keep track of what you are dealing with: is it money ? Is it
an integer? Using just "+" or "*" can be ambiguous in terms of rounding,
since money is usually rounded at the cent. So to disambiguate, we suffix these
operations with something that indicates the type of what we manipulate.
The suffixes are "$" for money "." for decimals, "at" (like in email adresses)
for dates and the hat symbol for durations. If you forget the suffix, the Catala type
checker will display an error message that will help you put it where it
belongs.
However, in the Catala code, you can see that we use "*$" to multiply the
individual income by the fixed percentage. The $ suffix indicates that we
are performing a multiplication on an amount of money. Indeed, in Catala,
you have to keep track of what you are dealing with: is it money ? Is it
an integer? Using just "+" or "*" can be ambiguous in terms of rounding,
since money is usually rounded at the cent. So to disambiguate, we suffix these
operations with something that indicates the type of what we manipulate.
The suffixes are "$" for money "." for decimals, "at" (like in email adresses)
for dates and the hat symbol for durations. If you forget the suffix, the Catala type
checker will display an error message that will help you put it where it
belongs.
But inside article 1, one question remains unknown: what is the value of
But inside article 1, one question remains unknown: what is the value of
of the fixed percentage? Often, precise values are defined elsewhere in the
legislative source. Here, let's suppose we have:
@Article 2@
#### [Article 2]
The fixed percentage mentionned at article 1 is equal to 20 %.
/*
```catala
scope IncomeTaxComputation:
definition fixed_percentage equals 20 %
# Writing 20% is just an abbreviation for 0.20
*/
```
You can see here that Catala allows definitions to be scattered throughout
You can see here that Catala allows definitions to be scattered throughout
the annotation of the legislative text, so that each
definition is as close as possible to its location in the text.
@@Conditional definitions@@+
### Conditional definitions
So far so good, but now the legislative text introduces some trickiness. Let us
So far so good, but now the legislative text introduces some trickiness. Let us
suppose the third article says:
@Article 3@ If the individual is in charge of 2 or more children, then the fixed
#### [Article 3]
If the individual is in charge of 2 or more children, then the fixed
percentage mentionned at article 1 is equal to 15 %.
/*
```catala
# How to redefine fixed_percentage?
*/
```
This article actually gives another definition for the fixed percentage, which
was already defined in article 2. However, article 3 defines the percentage
conditionnally to the individual having more than 2 children. Catala allows
was already defined in article 2. However, article 3 defines the percentage
conditionnally to the individual having more than 2 children. Catala allows
you precisely to redefine a variable under a condition:
/*
```catala
scope IncomeTaxComputation:
definition fixed_percentage under condition
individual.number_of_children >= 2
consequence equals 15 %
# Writing 15% is just an abbreviation for 0.15
*/
```
When the Catala program will execute, the right definition will be dynamically
chosen by looking at which condition is true. A correctly drafted legislative
source should always ensure that at most one condition is true at all times.
However, if it is not the case, Catala will let you define a precedence on the
chosen by looking at which condition is true. A correctly drafted legislative
source should always ensure that at most one condition is true at all times.
However, if it is not the case, Catala will let you define a precedence on the
conditions, which has to be justified by the law.
### Functions
@@Functions@@+
Catala lets you define functions anywhere in your data. Here's what it looks
like in the metadata definition when we want to define a two-brackets tax
Catala lets you define functions anywhere in your data. Here's what it looks
like in the metadata definition when we want to define a two-brackets tax
computation:
@@Begin metadata@@
/*
> Begin metadata
```catala
declaration structure TwoBrackets:
data breakpoint content money
data rate1 content decimal
@ -209,38 +225,44 @@ declaration structure TwoBrackets:
declaration scope TwoBracketsTaxComputation :
context brackets content TwoBrackets
context tax_formula content money depends on money
*/
@@End metadata@@
```
> End metadata
And in the code:
@Article4@ The tax amount for a two-brackets computation is equal to the amount
#### [Article4]
The tax amount for a two-brackets computation is equal to the amount
of income in each bracket multiplied by the rate of each bracket.
/*
```catala
scope TwoBracketsTaxComputation :
definition tax_formula of income equals
if income <=$ brackets.breakpoint then
income *$ brackets.rate1
else (
brackets.breakpoint *$ brackets.rate1 +$
brackets.breakpoint *$ brackets.rate1 +$
(income -$ brackets.breakpoint) *$ brackets.rate2
)
*/
```
@@Scope inclusion@@+
### Scope inclusion
Now that we've defined our helper scope for computing a two-brackets tax, we
Now that we've defined our helper scope for computing a two-brackets tax, we
want to use it in our main tax computation scope.
@Article 5@ For individuals whose income is greater than $100,000, the income
tax of article 1 is 40% of the income above $100,000. Below $100,000, the
#### [Article 5]
For individuals whose income is greater than $100,000, the income
tax of article 1 is 40% of the income above $100,000. Below $100,000, the
income tax is 20% of the income.
/*
```catala
declaration scope NewIncomeTaxComputation:
context two_brackets scope TwoBracketsTaxComputation
# This line says that we add the item two_brackets to the context.
# However, the "scope" keyword tells that this item is not a piece of data
# However, the "scope" keyword tells that this item is not a piece of data
# but rather a subscope that we can use to compute things.
context individual content Individual
context income_tax content money
@ -251,62 +273,66 @@ scope NewIncomeTaxComputation :
-- rate1: 20%
-- rate2: 40%
}
definition income_tax equals two_brackets.tax_formula of individual.income
*/
definition income_tax equals two_brackets.tax_formula of individual.income
```
@Article 6@
Individuals earning less than $10,000 are exempted of the income tax mentionned
#### [Article 6]
Individuals earning less than $10,000 are exempted of the income tax mentionned
at article 1.
/*
```catala
scope NewIncomeTaxComputation:
definition income_tax under condition
definition income_tax under condition
individual.income <=$ $10,000
consequence equals $0
*/
```
That's it! We've defined a two-brackets tax computation simply by annotating
legislative article by snippets of Catala code. However, attentive readers
may have caught something weird in articles 5 and 6. What happens when the
income of the individual is between $10,000 and $100,000 ?
That's it! We've defined a two-brackets tax computation simply by annotating
legislative article by snippets of Catala code. However, attentive readers
may have caught something weird in articles 5 and 6. What happens when the
income of the individual is between $10,000 and $100,000 ?
The law leaves it unspecified ; our dummy articles are clearly badly drafted.
But Catala can help you find this sort of errors via simple testing or
The law leaves it unspecified ; our dummy articles are clearly badly drafted.
But Catala can help you find this sort of errors via simple testing or
even formal verification. Let's start with the testing.
@@Testing Catala programs@@+
### Testing Catala programs
Testing Catala programs can be done directly into Catala. Indeed, writing test
cases for each Catala scope that you define is a good practice called
"unit testing" in the software engineering community. A test case is defined
cases for each Catala scope that you define is a good practice called
"unit testing" in the software engineering community. A test case is defined
as another scope:
@Testing NewIncomeTaxComputation@
/*
#### [Testing NewIncomeTaxComputation]
```catala
declaration scope Test1:
context tax_computation scope NewIncomeTaxComputation
context income_tax content money
scope Test1:
definition
tax_computation.individual
tax_computation.individual
# We define the argument to the subscope
equals
# The four lines below define a whole structure by giving a value to
# The four lines below define a whole structure by giving a value to
# each of its fields
Individual {
-- income: $230,000
-- number_of_children: 0
}
definition income_tax equals tax_computation.income_tax
# Next, we retrieve the income tax value compute it by the subscope and
# Next, we retrieve the income tax value compute it by the subscope and
# assert that it is equal to the expected value :
# ($230,000-$100,00)*40%+$100,000*20% = $72,000
assertion income_tax = $72,000
*/
```
This test should pass. Let us now consider a failing test case:
/*
```catala
declaration scope Test2:
context tax_computation scope NewIncomeTaxComputation
context income_tax content money
@ -319,24 +345,24 @@ scope Test2:
definition income_tax equals tax_computation.income_tax
assertion income_tax = $0
*/
```
This test case should compute a $0 income tax because of Article 6. But instead,
execution will yield an error saying that there is a conflict between rules.
execution will yield an error saying that there is a conflict between rules.
@@Defining exceptions to rules@@+
### Defining exceptions to rules
Indeed, the definition of the income tax in article 6 conflicts with the
definition of income tax in article 5. But actually, article 6 is just an
exception of article 5. In the law, it is implicit that if article 6 is
Indeed, the definition of the income tax in article 6 conflicts with the
definition of income tax in article 5. But actually, article 6 is just an
exception of article 5. In the law, it is implicit that if article 6 is
applicable, then it takes precedence over article 5.
@Fixing the computation@
#### [Fixing the computation]
This implicit precedence has to be explicitely declared in Catala. Here is a
fixed version of the NewIncomeTaxComputation scope:
/*
```catala
declaration scope NewIncomeTaxComputationFixed:
context two_brackets scope TwoBracketsTaxComputation
context individual content Individual
@ -349,22 +375,22 @@ scope NewIncomeTaxComputationFixed :
-- rate2: 40%
}
# To define an exception to a rule, you have to first label the rule that
# To define an exception to a rule, you have to first label the rule that
# you want to attach the exception to. You can put any snake_case identifier
# for the label
label article_5
definition income_tax equals two_brackets.tax_formula of individual.income
label article_5
definition income_tax equals two_brackets.tax_formula of individual.income
# Then, you can declare the exception by referring back to the label
exception article_5
definition income_tax under condition
definition income_tax under condition
individual.income <=$ $10,000
consequence equals $0
*/
```
And the test that should now work:
/*
```catala
declaration scope Test3:
context tax_computation scope NewIncomeTaxComputationFixed
context income_tax content money
@ -374,17 +400,17 @@ scope Test3:
-- income: $4,000
-- number_of_children: 0
}
definition income_tax equals tax_computation.income_tax
definition income_tax equals tax_computation.income_tax
assertion income_tax = $0
*/
```
@@Conclusion@@+
### Conclusion
This tutorial presents the basic concepts and syntax of the Catala language
features. It is then up to you use them to annotate legislative texts
This tutorial presents the basic concepts and syntax of the Catala language
features. It is then up to you use them to annotate legislative texts
with their algorithmic translation.
There is no single way to write Catala programs, as the program style should be
adapted to the legislation it annotates. However, Catala is a functional
language at heart, so following standard functional programming design patterns
There is no single way to write Catala programs, as the program style should be
adapted to the legislation it annotates. However, Catala is a functional
language at heart, so following standard functional programming design patterns
should help achieve concise and readable code.

View File

@ -1,14 +1,15 @@
@@Section 121@@
## Section 121
@@Begin metadata@@
/*
> Begin metadata
```catala
declaration structure Period:
data begin content date
data begin content date
data end content date
declaration scope PeriodMerge:
context periods1 content collection Period
context periods2 content collection Period
context periods1 content collection Period
context periods2 content collection Period
context output content collection Period
scope PeriodMerge:
@ -17,7 +18,7 @@ scope PeriodMerge:
definition periods2 equals []
# TODO: find a way to implement the merging of two collections of date
# periods into a single non-overlapping collection of date periods such
# periods into a single non-overlapping collection of date periods such
# that the output covers both input date ranges.
definition output equals []
@ -26,43 +27,43 @@ declaration structure PreviousSaleWhereSection121aApplied:
declaration enumeration OtherSection121aSale:
-- NoOtherSaleWhereSection121aApplied
-- MostRecentSaleWhereSection121aApplied content
-- NoOtherSaleWhereSection121aApplied
-- MostRecentSaleWhereSection121aApplied content
PreviousSaleWhereSection121aApplied
declaration scope Section121SinglePerson:
context requirements_met condition
context requirements_ownership_met condition
context requirements_met condition
context requirements_ownership_met condition
context requirements_usage_met condition
context date_of_sale_or_exchange content date
context property_ownage content collection Period
# Invariant: the periods in the collection are disjoint
context property_usage_as_principal_residence
context property_usage_as_principal_residence
content collection Period
# Invariant: the periods in the collection are disjoint
context aggregate_periods_from_last_five_years content duration
context aggregate_periods_from_last_five_years content duration
depends on collection Period
context gain_cap content money
context gain_from_sale_or_exchange_of_property content money
context income_excluded_from_gross_income_uncapped content money
context income_excluded_from_gross_income content money
context section_121_b_3_applies condition
context section_121_b_3_applies condition
context other_section_121a_sale content OtherSection121aSale
declaration structure PersonalData:
data property_ownage content collection Period
data property_usage_as_principal_residence
data property_usage_as_principal_residence
content collection Period
data other_section_121a_sale content OtherSection121aSale
declaration structure JointReturn:
data person1 content PersonalData
data person2 content PersonalData
declaration structure JointReturn:
data person1 content PersonalData
data person2 content PersonalData
declaration structure DeadSpouseInfo:
data return content PersonalData
data date_of_spouse_death content date
data death_spouse_info_at_time_of_death content PersonalData
data death_spouse_info_at_time_of_death content PersonalData
declaration enumeration ReturnType:
-- SingleReturn content PersonalData
@ -76,10 +77,10 @@ declaration scope Section121TwoPersons:
context section121Person2 scope Section121SinglePerson
context section121a_requirements_met condition
context section_121_b_2_A_condition condition
context gain_cap_person_1 content money
context gain_cap_person_1 content money
context gain_cap_person_2 content money
context gain_cap content money
context return_type content ReturnType
context return_type content ReturnType
context return_date content date
context date_of_sale_or_exchange content date
context gain_from_sale_or_exchange_of_property content money
@ -89,49 +90,49 @@ declaration scope Section121TwoPersons:
# Defining sub-scopes arguments
scope Section121TwoPersons:
definition section121Person2.date_of_sale_or_exchange equals
definition section121Person2.date_of_sale_or_exchange equals
date_of_sale_or_exchange
definition section121Person1.date_of_sale_or_exchange equals
definition section121Person1.date_of_sale_or_exchange equals
date_of_sale_or_exchange
definition person1 equals match return_type with pattern
definition person1 equals match return_type with pattern
-- SingleReturn of data_person1 : data_person1
-- JointReturn of data_couple : data_couple.person1
-- SingleReturnSurvivingSpouse of data_single: data_single.return
definition person2 equals match return_type with pattern
definition person2 equals match return_type with pattern
-- SingleReturn of data_person2 : data_person2
-- JointReturn of data_couple : data_couple.person2
-- SingleReturnSurvivingSpouse of data_single: data_single.return
definition section121Person1.property_ownage equals person1.property_ownage
definition section121Person1.property_usage_as_principal_residence equals
definition section121Person1.property_usage_as_principal_residence equals
person1.property_usage_as_principal_residence
definition section121Person2.property_ownage equals person2.property_ownage
definition section121Person2.property_usage_as_principal_residence equals
definition section121Person2.property_usage_as_principal_residence equals
person1.property_usage_as_principal_residence
definition section121Person1.gain_from_sale_or_exchange_of_property equals
definition section121Person1.gain_from_sale_or_exchange_of_property equals
gain_from_sale_or_exchange_of_property
definition section121Person2.gain_from_sale_or_exchange_of_property equals
definition section121Person2.gain_from_sale_or_exchange_of_property equals
gain_from_sale_or_exchange_of_property
definition section121Person1.other_section_121a_sale equals
definition section121Person1.other_section_121a_sale equals
person1.other_section_121a_sale
definition section121Person2.other_section_121a_sale equals
person2.other_section_121a_sale
definition gain_cap_person_1 equals section121Person1.gain_cap
definition gain_cap_person_1 equals section121Person1.gain_cap
definition gain_cap_person_2 equals section121Person2.gain_cap
declaration scope Section121TwoPasses:
context first_pass scope Section121TwoPersons
context second_pass scope Section121TwoPersons
context return_type content ReturnType
context return_type content ReturnType
context return_date content date
context date_of_sale_or_exchange content date
context gain_from_sale_or_exchange_of_property content money
@ -140,308 +141,316 @@ declaration scope Section121TwoPasses:
# Defining sub-scopes arguments
scope Section121TwoPasses:
definition first_pass.return_type equals return_type
definition second_pass.return_type equals return_type
definition first_pass.return_date equals return_date
definition second_pass.return_date equals return_date
definition first_pass.return_type equals return_type
definition second_pass.return_type equals return_type
definition first_pass.gain_from_sale_or_exchange_of_property equals
definition first_pass.return_date equals return_date
definition second_pass.return_date equals return_date
definition first_pass.gain_from_sale_or_exchange_of_property equals
gain_from_sale_or_exchange_of_property
definition second_pass.gain_from_sale_or_exchange_of_property equals
definition second_pass.gain_from_sale_or_exchange_of_property equals
gain_from_sale_or_exchange_of_property
definition first_pass.date_of_sale_or_exchange equals date_of_sale_or_exchange
definition second_pass.date_of_sale_or_exchange equals date_of_sale_or_exchange
definition income_excluded_from_gross_income equals
definition income_excluded_from_gross_income equals
second_pass.income_excluded_from_gross_income
*/
@@End metadata@@
```
> End metadata
@(a) Exclusion@
## [(a) Exclusion]
Gross income shall not include gain from the sale or exchange of property if,
during the 5-year period ending on the date of the sale or exchange, such
property has been owned and used by the taxpayer as the taxpayers principal
residence for periods aggregating 2 years or more.
/*
Gross income shall not include gain from the sale or exchange of property if,
during the 5-year period ending on the date of the sale or exchange, such
property has been owned and used by the taxpayer as the taxpayers principal
residence for periods aggregating 2 years or more.
```catala
scope Section121SinglePerson:
# Here we aggregate over all the periods of the collection. For
# Here we aggregate over all the periods of the collection. For
# each period, three cases:
# - either the period began less that 5 years before the
# - either the period began less that 5 years before the
# date_of_sale_or_exchange in which case we count if full
# - either the period ended more that 5 years before the
# date_of_sale_or_exchange in which case we don't count it
# - either the 5 years mark is inside the period and we only
# - either the period ended more that 5 years before the
# date_of_sale_or_exchange in which case we don't count it
# - either the 5 years mark is inside the period and we only
# cound the half after 5 years
definition aggregate_periods_from_last_five_years of periods equals
definition aggregate_periods_from_last_five_years of periods equals
sum duration for period in periods of (
if date_of_sale_or_exchange <=@ period.begin +@ 5 year then
period.end -@ period.begin
else (if date_of_sale_or_exchange >=@ period.end +@ 5 year then
0 day
else (if date_of_sale_or_exchange >=@ period.end +@ 5 year then
0 day
else ((period.end +@ 5 year) -@ date_of_sale_or_exchange))
)
# Regulation 1.121-1(c)(1): 2 years = 730 days
# Regulation 1.121-1(c)(1): the periods of ownage and usage
# Regulation 1.121-1(c)(1): the periods of ownage and usage
# don't have to overlap
rule requirements_ownership_met under condition
rule requirements_ownership_met under condition
aggregate_periods_from_last_five_years of property_ownage >=^ 730 day
consequence fulfilled
consequence fulfilled
rule requirements_usage_met under condition
rule requirements_usage_met under condition
aggregate_periods_from_last_five_years of
property_usage_as_principal_residence >=^ 730 day
consequence fulfilled
rule requirements_met under condition
rule requirements_met under condition
requirements_ownership_met and requirements_usage_met
consequence fulfilled
definition income_excluded_from_gross_income_uncapped equals
if requirements_met then gain_from_sale_or_exchange_of_property
else $0
scope Section121TwoPersons:
definition section121a_requirements_met equals section121Person1.requirements_met
definition income_excluded_from_gross_income_uncapped equals
definition income_excluded_from_gross_income_uncapped equals
section121Person1.income_excluded_from_gross_income_uncapped
*/
```
@@(b) Limitations@@+
### (b) Limitations
@(1) In general@
#### [(1) In general]
The amount of gain excluded from gross income under subsection (a) with
The amount of gain excluded from gross income under subsection (a) with
respect to any sale or exchange shall not exceed $250,000.
/*
```catala
scope Section121SinglePerson:
definition gain_cap equals $250,000
# Big semantics insight for Catala. Here we could want to get rid of
# the "_uncapped" version of the variable. But in the current
# Big semantics insight for Catala. Here we could want to get rid of
# the "_uncapped" version of the variable. But in the current
# semantics we can't do that because we don't allow for recursion.
definition income_excluded_from_gross_income equals
if income_excluded_from_gross_income_uncapped >=$ gain_cap then
definition income_excluded_from_gross_income equals
if income_excluded_from_gross_income_uncapped >=$ gain_cap then
gain_cap
else
else
income_excluded_from_gross_income_uncapped
scope Section121TwoPersons:
definition gain_cap equals section121Person1.gain_cap
scope Section121TwoPersons:
definition gain_cap equals section121Person1.gain_cap
definition income_excluded_from_gross_income equals
if income_excluded_from_gross_income_uncapped >=$ gain_cap then
definition income_excluded_from_gross_income equals
if income_excluded_from_gross_income_uncapped >=$ gain_cap then
gain_cap
else
else
income_excluded_from_gross_income_uncapped
*/
```
@(2) Special rules for joint returns@
#### [(2) Special rules for joint returns]
In the case of a husband and wife who make a joint return for the taxable year
In the case of a husband and wife who make a joint return for the taxable year
of the sale or exchange of the property—
/*
```catala
# Taxable year of the sale or exchange ?=? year when the income is taxed
# Imagine a couple selling the house in 2020 and getting the payment in
# 2021 where they file a joint return. Does (A) apply or not ?
# Imagine a couple selling the house in 2020 and getting the payment in
# 2021 where they file a joint return. Does (A) apply or not ?
# Reasonably it should.
*/
```
@(A) $500,000 Limitation for certain joint returns@
#### [(A) $500,000 Limitation for certain joint returns]
Paragraph (1) shall be applied by substituting “$500,000” for “$250,000” if—
(i) either spouse meets the ownership requirements of subsection (a) with
respect to such property;
(ii) both spouses meet the use requirements of subsection (a) with respect to
(ii) both spouses meet the use requirements of subsection (a) with respect to
such property; and
(iii) neither spouse is ineligible for the benefits of subsection (a) with
(iii) neither spouse is ineligible for the benefits of subsection (a) with
respect to such property by reason of paragraph (3).
/*
```catala
scope Section121TwoPersons:
rule section_121_b_2_A_condition under condition
rule section_121_b_2_A_condition under condition
(return_type with pattern JointReturn of data_couple)
and
# i)
(section121Person1.requirements_ownership_met or
section121Person2.requirements_ownership_met)
and
# ii)
(section121Person1.requirements_usage_met and
# i)
(section121Person1.requirements_ownership_met or
section121Person2.requirements_ownership_met)
and
# ii)
(section121Person1.requirements_usage_met and
section121Person2.requirements_usage_met)
# iii)
and
(not (section121Person1.section_121_b_3_applies))
and
(not (section121Person2.section_121_b_3_applies))
and
(not (section121Person2.section_121_b_3_applies))
consequence fulfilled
exception
rule section121a_requirements_met under condition
section_121_b_2_A_condition
rule section121a_requirements_met under condition
section_121_b_2_A_condition
consequence fulfilled
exception
definition gain_cap under condition
exception
definition gain_cap under condition
section_121_b_2_A_condition
consequence equals $500,000
*/
```
@(B) Other joint returns@
If such spouses do not meet the requirements of subparagraph (A), the limitation
under paragraph (1) shall be the sum of the limitations under paragraph (1) to
which each spouse would be entitled if such spouses had not been married. For
purposes of the preceding sentence, each spouse shall be treated as owning the
#### [(B) Other joint returns]
If such spouses do not meet the requirements of subparagraph (A), the limitation
under paragraph (1) shall be the sum of the limitations under paragraph (1) to
which each spouse would be entitled if such spouses had not been married. For
purposes of the preceding sentence, each spouse shall be treated as owning the
property during the period that either spouse owned the property.
/*
scope Section121TwoPasses under condition
(return_type with pattern JointReturn) and
```catala
scope Section121TwoPasses under condition
(return_type with pattern JointReturn) and
not (first_pass.section_121_b_2_A_condition):
definition second_pass.gain_cap equals
first_pass.gain_cap_person_1 +$
definition second_pass.gain_cap equals
first_pass.gain_cap_person_1 +$
first_pass.gain_cap_person_2
definition period_merge.periods1 equals match return_type with pattern
definition period_merge.periods1 equals match return_type with pattern
-- JointReturn of joint_return: joint_return.person1.property_ownage
-- SingleReturnSurvivingSpouse of dead_spouse_info : [] # does not happen
-- SingleReturn of return : [] # does not happen
definition period_merge.periods2 equals match return_type with pattern
definition period_merge.periods2 equals match return_type with pattern
-- JointReturn of joint_return: joint_return.person2.property_ownage
-- SingleReturnSurvivingSpouse of dead_spouse_info : [] # does not happen
-- SingleReturn of return : [] # does not happen
definition second_pass.person1 equals PersonalData {
-- property_ownage: period_merge.output
-- property_usage_as_principal_residence:
-- property_usage_as_principal_residence:
first_pass.person1.property_usage_as_principal_residence
-- other_section_121a_sale: first_pass.person1.other_section_121a_sale
}
definition second_pass.person2 equals PersonalData {
-- property_ownage: period_merge.output
-- property_usage_as_principal_residence:
-- property_usage_as_principal_residence:
first_pass.person2.property_usage_as_principal_residence
-- other_section_121a_sale: first_pass.person2.other_section_121a_sale
}
*/
@(3) Application to only 1 sale or exchange every 2 years@
```
#### [(3) Application to only 1 sale or exchange every 2 years]
Subsection (a) shall not apply to any sale or exchange by the taxpayer if,
during the 2-year period ending on the date of such sale or exchange, there
Subsection (a) shall not apply to any sale or exchange by the taxpayer if,
during the 2-year period ending on the date of such sale or exchange, there
was any other sale or exchange by the taxpayer to which subsection (a) applied.
/*
scope Section121SinglePerson:
rule section_121_b_3_applies under condition
(other_section_121a_sale with pattern
```catala
scope Section121SinglePerson:
rule section_121_b_3_applies under condition
(other_section_121a_sale with pattern
MostRecentSaleWhereSection121aApplied of other_sale) and
date_of_sale_or_exchange -@ other_sale.date_of_sale_or_exchange <=^ 2 year
consequence fulfilled
exception
definition income_excluded_from_gross_income_uncapped under condition
exception
definition income_excluded_from_gross_income_uncapped under condition
section_121_b_3_applies
consequence equals $0
*/
consequence equals $0
```
@(4) Special rule for certain sales by surviving spouses@
#### [(4) Special rule for certain sales by surviving spouses]
/*
# Sarah: the year when your spouse dies, do you file a joint return or
```catala
# Sarah: the year when your spouse dies, do you file a joint return or
# separate returns?
*/
In the case of a sale or exchange of property by an unmarried individual whose
spouse is deceased on the date of such sale, paragraph (1) shall be applied by
substituting “$500,000” for “$250,000” if such sale occurs not later than 2
years after the date of death of such spouse and the requirements of paragraph
```
In the case of a sale or exchange of property by an unmarried individual whose
spouse is deceased on the date of such sale, paragraph (1) shall be applied by
substituting “$500,000” for “$250,000” if such sale occurs not later than 2
years after the date of death of such spouse and the requirements of paragraph
(2)(A) were met immediately before such date of death.
/*
scope Section121TwoPasses under condition
return_type with pattern SingleReturnSurvivingSpouse of single_data and
```catala
scope Section121TwoPasses under condition
return_type with pattern SingleReturnSurvivingSpouse of single_data and
single_data.date_of_spouse_death <@ date_of_sale_or_exchange and
date_of_sale_or_exchange <=@ single_data.date_of_spouse_death +@ 2 year
date_of_sale_or_exchange <=@ single_data.date_of_spouse_death +@ 2 year
:
definition first_pass.date_of_sale_or_exchange equals
definition first_pass.date_of_sale_or_exchange equals
match return_type with pattern
-- SingleReturnSurvivingSpouse of single_data: single_data.date_of_spouse_death
-- SingleReturn of return: date_of_sale_or_exchange # does not happen
-- JointReturn of return: date_of_sale_or_exchange # does not happen
definition first_pass.return_type equals
match return_type with pattern
-- SingleReturnSurvivingSpouse of single_data:
match return_type with pattern
-- SingleReturnSurvivingSpouse of single_data:
JointReturn content (JointReturn {
-- person1: single_data.return
-- person2: single_data.death_spouse_info_at_time_of_death
})
-- SingleReturn of return: SingleReturn content return # does not happen
-- JointReturn of return: JointReturn content return # does not happen
definition second_pass.gain_cap under condition
definition second_pass.gain_cap under condition
first_pass.section_121_b_2_A_condition
consequence equals $500,000
*/
```
@@(5) Exclusion of gain allocated to nonqualified use@@++
#### (5) Exclusion of gain allocated to nonqualified use
@(A) In general@
##### [(A) In general]
Subsection (a) shall not apply to so much of the gain from the sale or exchange
Subsection (a) shall not apply to so much of the gain from the sale or exchange
of property as is allocated to periods of nonqualified use.
@(B) Gain allocated to periods of nonqualified use@
##### [(B) Gain allocated to periods of nonqualified use]
For purposes of subparagraph (A), gain shall be allocated to periods of
For purposes of subparagraph (A), gain shall be allocated to periods of
nonqualified use based on the ratio which—
(i) the aggregate periods of nonqualified use during the period such property
(i) the aggregate periods of nonqualified use during the period such property
was owned by the taxpayer, bears to
(ii) the period such property was owned by the taxpayer.
@@(C) Period of nonqualified use@@+++
##### (C) Period of nonqualified use
For purposes of this paragraph—
@(i) In general@
###### [(i) In general]
The term “period of nonqualified use” means any period (other than the portion
of any period preceding January 1, 2009) during which the property is not used
as the principal residence of the taxpayer or the taxpayers spouse or former
The term “period of nonqualified use” means any period (other than the portion
of any period preceding January 1, 2009) during which the property is not used
as the principal residence of the taxpayer or the taxpayers spouse or former
spouse.
@(ii) Exceptions@
###### [(ii) Exceptions]
The term “period of nonqualified use” does not include—
(I) any portion of the 5-year period described in subsection (a) which is after
the last date that such property is used as the principal residence of the
(I) any portion of the 5-year period described in subsection (a) which is after
the last date that such property is used as the principal residence of the
taxpayer or the taxpayers spouse,
(II) any period (not to exceed an aggregate period of 10 years) during which the
taxpayer or the taxpayers spouse is serving on qualified official extended duty
(as defined in subsection (d)(9)(C)) described in clause (i), (ii), or (iii) of
(II) any period (not to exceed an aggregate period of 10 years) during which the
taxpayer or the taxpayers spouse is serving on qualified official extended duty
(as defined in subsection (d)(9)(C)) described in clause (i), (ii), or (iii) of
subsection (d)(9)(A), and
(III) any other period of temporary absence (not to exceed an aggregate period
of 2 years) due to change of employment, health conditions, or such other
(III) any other period of temporary absence (not to exceed an aggregate period
of 2 years) due to change of employment, health conditions, or such other
unforeseen circumstances as may be specified by the Secretary.
@(D) Coordination with recognition of gain attributable to depreciation@
###### [(D) Coordination with recognition of gain attributable to depreciation]
For purposes of this paragraph—
(i) subparagraph (A) shall be applied after the application of subsection
(i) subparagraph (A) shall be applied after the application of subsection
(d)(6), and
(ii) subparagraph (B) shall be applied without regard to any gain to which
(ii) subparagraph (B) shall be applied without regard to any gain to which
subsection (d)(6) applies.

View File

@ -1,7 +1,7 @@
@@Section 132@@
## Section 132
@@Begin metadata@@
/*
> Begin metadata
```catala
# We only formalize part (c) here
declaration enumeration DiscountType:
-- Property
@ -19,25 +19,27 @@ declaration scope QualifiedEmployeeDiscount:
context is_services content boolean
scope QualifiedEmployeeDiscount:
definition is_property equals match discount_type with pattern
-- Property: true
definition is_property equals match discount_type with pattern
-- Property: true
-- Services: false
definition is_services equals match discount_type with pattern
-- Property: false
-- Property: false
-- Services: true
*/
@@End metadata@@
```
> End metadata
@@(c) Qualified employee discount defined@@+
### (c) Qualified employee discount defined
For purposes of this section—
@(1) Qualified employee discount@
The term “qualified employee discount” means any employee discount with respect
#### [(1) Qualified employee discount]
The term “qualified employee discount” means any employee discount with respect
to qualified property or services to the extent such discount does not exceed—
(A) in the case of property, the gross profit percentage of the price at which
(A) in the case of property, the gross profit percentage of the price at which
the property is being offered by the employer to customers, or
/*
```catala
scope QualifiedEmployeeDiscount :
definition qualified_employee_discount
under condition is_property consequence
@ -46,10 +48,12 @@ scope QualifiedEmployeeDiscount :
customer_price *$ gross_profit_percentage
then customer_price *$ gross_profit_percentage
else employee_discount
*/
(B) in the case of services, 20 percent of the price at which the services are
```
(B) in the case of services, 20 percent of the price at which the services are
being offered by the employer to customers.
/*
```catala
scope QualifiedEmployeeDiscount :
definition qualified_employee_discount
under condition is_services consequence
@ -64,57 +68,69 @@ scope QualifiedEmployeeDiscount under condition is_services:
# We provide a default value here so that the computations run smooth.
definition aggregate_cost equals $0
definition gross_profit_percentage equals 0%
*/
@@(2) Gross profit percentage@@++
```
#### (2) Gross profit percentage
##### [(A) In general]
@(A) In general@
The term “gross profit percentage” means the percent which—
(i) the excess of the aggregate sales price of property sold by the employer
(i) the excess of the aggregate sales price of property sold by the employer
to customers over the aggregate cost of such property to the employer, is of
(ii) the aggregate sale price of such property.
/*
```catala
scope QualifiedEmployeeDiscount under condition is_property:
assertion customer_price >=$ aggregate_cost
definition gross_profit_percentage equals
(customer_price -$ aggregate_cost) /$ customer_price
*/
@(B) Determination of gross profit percentage@
```
##### [(B) Determination of gross profit percentage]
Gross profit percentage shall be determined on the basis of—
(i) all property offered to customers in the ordinary course of the line of
business of the employer in which the employee is performing services (or a
(i) all property offered to customers in the ordinary course of the line of
business of the employer in which the employee is performing services (or a
reasonable classification of property selected by the employer), and
(ii) the employers experience during a representative period.
/*
```catala
# (i) and (ii) are subjective criteria for determining the gross profit
# percentage ; we do not formalize them
*/
@(3) Employee discount defined@
```
##### [(3) Employee discount defined]
The term “employee discount” means the amount by which—
(A) the price at which the property or services are provided by the employer to
(A) the price at which the property or services are provided by the employer to
an employee for use by such employee, is less than
(B) the price at which such property or services are being offered by the
(B) the price at which such property or services are being offered by the
employer to customers.
/*
```catala
scope QualifiedEmployeeDiscount:
assertion customer_price >=$ employee_price
definition employee_discount equals
customer_price -$ employee_price
*/
@(4) Qualified property or services@
The term “qualified property or services” means any property (other than real
property and other than personal property of a kind held for investment) or
services which are offered for sale to customers in the ordinary course of
the line of business of the employer in which the employee is performing
```
##### [(4) Qualified property or services]
The term “qualified property or services” means any property (other than real
property and other than personal property of a kind held for investment) or
services which are offered for sale to customers in the ordinary course of
the line of business of the employer in which the employee is performing
services.
/*
```catala
# Again, this is for subjectively determining what item qualifies for a
# discount, not formalizing
*/
```

View File

@ -1,17 +1,17 @@
@@Include: ../section_121.catala_en@@
> Include: ../section_121.catala_en
@Testing paragraph (a)@
## [Testing paragraph (a)]
/*
```catala
declaration scope Data:
context period_four_years_recent content Period
context period_one_year_recent content Period
context period_four_years_recent content Period
context period_one_year_recent content Period
context period_two_years_middle content Period
context date_of_sale_or_exchange content date
context gain_from_sale_or_exchange_of_property content money
context return_date content date
context person_ok_1 content PersonalData
context person_ok_2 content PersonalData
context person_ok_1 content PersonalData
context person_ok_2 content PersonalData
context person_ko_1 content PersonalData
context person_ko_2 content PersonalData
@ -38,7 +38,7 @@ scope Data:
}
definition person_ok_2 equals PersonalData {
-- property_ownage: [period_four_years_recent]
-- property_usage_as_principal_residence:
-- property_usage_as_principal_residence:
[period_two_years_middle; period_one_year_recent]
-- other_section_121a_sale: NoOtherSaleWhereSection121aApplied
}
@ -53,9 +53,9 @@ scope Data:
-- property_usage_as_principal_residence: [period_two_years_middle]
-- other_section_121a_sale: NoOtherSaleWhereSection121aApplied
}
####################### Testing Section121SinglePerson #########################
@ -65,7 +65,7 @@ declaration scope Test1:
scope Test1:
definition scope121a.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121a.gain_from_sale_or_exchange_of_property equals
definition scope121a.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121a.property_ownage equals [data_.period_four_years_recent]
definition scope121a.property_usage_as_principal_residence equals
@ -79,10 +79,10 @@ declaration scope Test2:
scope Test2:
definition scope121a.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121a.gain_from_sale_or_exchange_of_property equals
definition scope121a.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121a.property_ownage equals [data_.period_four_years_recent]
definition scope121a.property_usage_as_principal_residence equals
definition scope121a.property_usage_as_principal_residence equals
[data_.period_one_year_recent]
definition scope121a.other_section_121a_sale equals NoOtherSaleWhereSection121aApplied
assertion not scope121a.requirements_met
@ -93,10 +93,10 @@ declaration scope Test3:
scope Test3:
definition scope121a.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121a.gain_from_sale_or_exchange_of_property equals
definition scope121a.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121a.property_ownage equals [data_.period_four_years_recent]
definition scope121a.property_usage_as_principal_residence equals
definition scope121a.property_usage_as_principal_residence equals
[data_.period_two_years_middle]
definition scope121a.other_section_121a_sale equals NoOtherSaleWhereSection121aApplied
assertion not scope121a.requirements_met
@ -107,10 +107,10 @@ declaration scope Test4:
scope Test4:
definition scope121a.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121a.gain_from_sale_or_exchange_of_property equals
definition scope121a.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121a.property_ownage equals [data_.period_four_years_recent]
definition scope121a.property_usage_as_principal_residence equals
definition scope121a.property_usage_as_principal_residence equals
[data_.period_two_years_middle; data_.period_one_year_recent]
definition scope121a.other_section_121a_sale equals NoOtherSaleWhereSection121aApplied
assertion scope121a.requirements_met
@ -121,10 +121,10 @@ declaration scope Test5:
context scope121 scope Section121TwoPersons
context data_ scope Data
scope Test5:
scope Test5:
definition scope121.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121.return_date equals data_.return_date
definition scope121.gain_from_sale_or_exchange_of_property equals
definition scope121.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121.return_type equals SingleReturn content data_.person_ok_1
assertion scope121.income_excluded_from_gross_income = $250,000
@ -133,14 +133,14 @@ declaration scope Test6:
context scope121 scope Section121TwoPersons
context data_ scope Data
scope Test6:
scope Test6:
definition scope121.date_of_sale_or_exchange equals data_.date_of_sale_or_exchange
definition scope121.return_date equals data_.return_date
definition scope121.gain_from_sale_or_exchange_of_property equals
definition scope121.gain_from_sale_or_exchange_of_property equals
data_.gain_from_sale_or_exchange_of_property
definition scope121.return_type equals JointReturn content (JointReturn {
-- person1: data_.person_ok_1
-- person2: data_.person_ok_2
})
assertion scope121.income_excluded_from_gross_income = $350,000
*/
```

View File

@ -1,9 +1,10 @@
@@Include: ../section_132.catala_en@@
> Include: ../section_132.catala_en
@Test@
/*
### [Test]
```catala
declaration scope TestSection132_1:
context section_132 scope QualifiedEmployeeDiscount
context section_132 scope QualifiedEmployeeDiscount
scope TestSection132_1:
definition section_132.customer_price equals $1500
@ -13,11 +14,11 @@ scope TestSection132_1:
assertion section_132.employee_discount = $500
assertion section_132.gross_profit_percentage = 0.4
assertion section_132.qualified_employee_discount = $500
*/
```
/*
```catala
declaration scope TestSection132_2:
context section_132 scope QualifiedEmployeeDiscount
context section_132 scope QualifiedEmployeeDiscount
scope TestSection132_2:
definition section_132.customer_price equals $1500
@ -27,11 +28,11 @@ scope TestSection132_2:
assertion section_132.employee_discount = $500
assertion section_132.gross_profit_percentage = 0.2
assertion section_132.qualified_employee_discount = $300.00
*/
```
/*
```catala
declaration scope TestSection132_3:
context section_132 scope QualifiedEmployeeDiscount
context section_132 scope QualifiedEmployeeDiscount
scope TestSection132_3:
definition section_132.customer_price equals $1500
@ -39,4 +40,4 @@ scope TestSection132_3:
definition section_132.discount_type equals Services
assertion section_132.employee_discount = $500
assertion section_132.qualified_employee_discount = $300
*/
```