french_tax_system 0.1.2 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +1 -1
- data/.rubocop.yml +2 -1
- data/Gemfile +1 -1
- data/Gemfile.lock +22 -1
- data/lib/french_tax_system.rb +314 -82
- data/lib/french_tax_system/version.rb +1 -1
- data/lib/lmnp_formulas.rb +170 -3
- data/lib/nue_formulas.rb +186 -13
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8fae1b53a7ecf7549facdc39a0c66457b7dfca65307a160d3b1fe517e31a47f
|
4
|
+
data.tar.gz: 76d46ad120ab3796290b224e98c4c48d2739b629a7cbb6fd1308a3a82bd8fcdd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cf7229ac603b71b2f6a48c2a51664b2a09d5b27e301f39470f5a5b949f7d83f8e4a770caad432a23db3da6e346551647853fa9382fbfa55ea8c7a26858af1cec
|
7
|
+
data.tar.gz: c290b721a56d0c96c4ca40d63a3b3617f0ffa07fd70114337b80f2a4a57aa6c4b883230e8d0d0f6dc33a2c2b9188e00268958fc21dc62ae3a62bd18dce129d42
|
data/.github/workflows/main.yml
CHANGED
data/.rubocop.yml
CHANGED
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,13 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
french_tax_system (0.
|
4
|
+
french_tax_system (1.0.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
|
+
ast (2.4.2)
|
9
10
|
diff-lcs (1.4.4)
|
11
|
+
parallel (1.20.1)
|
12
|
+
parser (3.0.1.1)
|
13
|
+
ast (~> 2.4.1)
|
14
|
+
rainbow (3.0.0)
|
10
15
|
rake (13.0.3)
|
16
|
+
regexp_parser (2.1.1)
|
17
|
+
rexml (3.2.5)
|
11
18
|
rspec (3.10.0)
|
12
19
|
rspec-core (~> 3.10.0)
|
13
20
|
rspec-expectations (~> 3.10.0)
|
@@ -21,6 +28,19 @@ GEM
|
|
21
28
|
diff-lcs (>= 1.2.0, < 2.0)
|
22
29
|
rspec-support (~> 3.10.0)
|
23
30
|
rspec-support (3.10.2)
|
31
|
+
rubocop (1.17.0)
|
32
|
+
parallel (~> 1.10)
|
33
|
+
parser (>= 3.0.0.0)
|
34
|
+
rainbow (>= 2.2.2, < 4.0)
|
35
|
+
regexp_parser (>= 1.8, < 3.0)
|
36
|
+
rexml
|
37
|
+
rubocop-ast (>= 1.7.0, < 2.0)
|
38
|
+
ruby-progressbar (~> 1.7)
|
39
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
40
|
+
rubocop-ast (1.7.0)
|
41
|
+
parser (>= 3.0.1.1)
|
42
|
+
ruby-progressbar (1.11.0)
|
43
|
+
unicode-display_width (2.0.0)
|
24
44
|
|
25
45
|
PLATFORMS
|
26
46
|
x86_64-linux
|
@@ -29,6 +49,7 @@ DEPENDENCIES
|
|
29
49
|
french_tax_system!
|
30
50
|
rake (~> 13.0)
|
31
51
|
rspec (~> 3.0)
|
52
|
+
rubocop (~> 1.17)
|
32
53
|
|
33
54
|
BUNDLED WITH
|
34
55
|
2.2.19
|
data/lib/french_tax_system.rb
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require "date"
|
3
4
|
require_relative "french_tax_system/version"
|
5
|
+
require_relative "nue_formulas"
|
6
|
+
require_relative "lmnp_formulas"
|
4
7
|
|
5
8
|
module FrenchTaxSystem
|
9
|
+
extend self
|
6
10
|
class Error < StandardError; end
|
7
11
|
|
8
12
|
# Constants
|
@@ -18,136 +22,313 @@ module FrenchTaxSystem
|
|
18
22
|
|
19
23
|
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE = 2
|
20
24
|
FISCAL_NB_PARTS_FOR_SINGLE_PERSON = 1
|
25
|
+
FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN = 0.5
|
26
|
+
FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN = 0.25
|
21
27
|
|
22
28
|
FAMILY_QUOTIENT_CAPPING_AMOUNT = {
|
23
29
|
## Per half fiscal parts for children
|
24
|
-
year2021:
|
30
|
+
year2021: {
|
31
|
+
married_couple_household: {
|
32
|
+
half_part: 1570
|
33
|
+
},
|
34
|
+
single_person_household: {
|
35
|
+
marked_up_half_part: 1852,
|
36
|
+
half_part: 1570
|
37
|
+
}
|
38
|
+
}
|
25
39
|
}.freeze
|
26
40
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
end
|
37
|
-
|
38
|
-
def calc_income_tax_amount_per_year_with_property_income_of(net_taxable_property_income_amount)
|
39
|
-
global_net_taxable_income_amount = calc_global_net_taxable_amount_with_property_income_of(net_taxable_property_income_amount)
|
40
|
-
|
41
|
-
fiscal_nb_parts = calc_fiscal_nb_parts
|
42
|
-
|
43
|
-
family_quotient_amount = calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
|
44
|
-
current_year = Date.today.year
|
45
|
-
|
46
|
-
aggregated_tax_amount = calc_aggregated_tax_amount(family_quotient_amount, current_year)
|
47
|
-
|
48
|
-
(aggregated_tax_amount * fiscal_nb_parts).floor
|
49
|
-
end
|
50
|
-
|
51
|
-
def calc_not_capped_income_tax_amount_per_year_with_property_income_of(net_taxable_property_income_amount)
|
52
|
-
global_net_taxable_income_amount = calc_global_net_taxable_amount_with_property_income_of(net_taxable_property_income_amount)
|
53
|
-
|
54
|
-
fiscal_nb_parts = calc_fiscal_nb_parts
|
41
|
+
DISCOUNT_ON_LOW_INCOME_TAX = {
|
42
|
+
year2021: {
|
43
|
+
discount_percentage: 0.4525,
|
44
|
+
threshold_single_person_household: 1722,
|
45
|
+
lump_sum_single_person_household: 779,
|
46
|
+
threshold_married_couple_household: 2849,
|
47
|
+
lump_sum_married_couple_household: 1289
|
48
|
+
}
|
49
|
+
}.freeze
|
55
50
|
|
56
|
-
|
57
|
-
current_year = Date.today.year
|
51
|
+
REVENUES_STANDARD_ALLOWANCE = 0.1
|
58
52
|
|
59
|
-
|
53
|
+
REAL_REGIMEN_DEDUCTIBLE_EXPENSES = {
|
54
|
+
fiscal_year1: %w[house_first_works_amount house_landlord_charges_amount_per_year
|
55
|
+
house_property_management_amount_per_year house_insurance_gli_amount_per_year house_insurance_pno_amount_per_year house_property_tax_amount_per_year credit_loan_cumulative_interests_paid_for_year_two credit_loan_insurance_amount_per_year],
|
56
|
+
fiscal_year2: %w[house_landlord_charges_amount_per_year
|
57
|
+
house_property_management_amount_per_year house_insurance_gli_amount_per_year house_insurance_pno_amount_per_year house_property_tax_amount_per_year credit_loan_cumulative_interests_paid_for_year_two credit_loan_insurance_amount_per_year]
|
58
|
+
}.freeze
|
60
59
|
|
61
|
-
|
62
|
-
end
|
60
|
+
SOCIAL_CONTRIBUTIONS_PERCENTAGE = 0.172
|
63
61
|
|
64
|
-
|
65
|
-
global_net_taxable_income_amount = calc_global_net_taxable_amount_with_property_income_of(net_taxable_property_income_amount)
|
62
|
+
# Methods
|
66
63
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
64
|
+
# xxx
|
65
|
+
def calc_taxes_amount_per_year(simulation, calculation_method, investment_top_fiscal_year)
|
66
|
+
# Iterate over investment first to top fiscal year and return an array which concatenates all hashes generated per fiscal year
|
67
|
+
income_tax_array = []
|
68
|
+
(1..investment_top_fiscal_year).map.with_index do |investment_fiscal_year, index|
|
69
|
+
## Set postponed neg tax p income to 0 for the first year and to previous year result for other years
|
70
|
+
if investment_fiscal_year == 1
|
71
|
+
postponed_negative_taxable_property_income_from_previous_fiscal_year = 0
|
72
|
+
elsif investment_fiscal_year >= 2
|
73
|
+
postponed_negative_taxable_property_income_from_previous_fiscal_year = income_tax_array[index - 1][:income_tax][:negative_taxable_property_income_amount_to_postpone]
|
74
|
+
end
|
71
75
|
|
72
|
-
|
73
|
-
|
76
|
+
## Calculate income tax params for this fiscal_year
|
77
|
+
income_tax_params = calc_income_tax_amount_for_year(simulation, calculation_method, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
74
78
|
|
75
|
-
|
79
|
+
## Calculate social contributions for this fiscal year if property
|
80
|
+
social_contributions_amount = calculation_method == "with_property_income" ? calc_social_contributions_amount_for_year(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year) : 0
|
76
81
|
|
77
|
-
|
82
|
+
## Fill array with a nice big chunky hash
|
83
|
+
income_tax_array << {
|
84
|
+
income_tax: income_tax_params,
|
85
|
+
social_contributions_amount: social_contributions_amount
|
86
|
+
}
|
87
|
+
end
|
88
|
+
income_tax_array
|
78
89
|
end
|
79
90
|
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
91
|
+
# Calculate the income tax to pay with or without the generated income from the property investment
|
92
|
+
#
|
93
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
94
|
+
# @options simulation [Integer] :house_rent_amount_per_year how much is the rent paid by the tenant (euros/year)
|
95
|
+
# @options simulation [Integer] :house_price_bought_amount how much was the house bought (euros)
|
96
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
97
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
98
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
99
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
100
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
101
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
102
|
+
# @options simulation [Integer] :credit_loan_amount how much is credit loan amount (euros)
|
103
|
+
# @options simulation [Integer] :credit_loan_duration how long is the credit (years)
|
104
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
105
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
106
|
+
# @options simulation [String] :fiscal_status what fiscal status has been chosen
|
107
|
+
# @options simulation [String] :fiscal_regimen what fiscal regimen has been chosen
|
108
|
+
# @options simulation [Integer] :fiscal_revenues_p1 salary from person 1 of the fiscal household (euros)
|
109
|
+
# @options simulation [Integer] :fiscal_revenues_p2 salary from person 2 of the fiscal household (euros)
|
110
|
+
# @options simulation [String] :fiscal_marital_status fiscal relation between the 'parents' of the household
|
111
|
+
# @options simulation [Integer] :fiscal_nb_dependent_children number of dependent children of fiscal household (nb)
|
112
|
+
# @options simulation [Integer] :fiscal_nb_alternate_custody_children number of alternate custody children of fiscal household (nb)
|
113
|
+
# @params [String] calculation_method indicates if the calculation is made with or without the property income
|
114
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
115
|
+
# @params [Integer] investment_fiscal_year the fisal year of the calculation
|
116
|
+
#
|
117
|
+
# @return [Hash] a hash made of the final income tax to pay (euros) and other values for the fiscal year inputed
|
118
|
+
# @options hash [Float] :income_tax_amount the income tax amount to pay for this fiscal year (euros)
|
119
|
+
# @options hash [Float] :average_tax_rate the average tax rate (percentage)
|
120
|
+
# @options hash [Float] :global_net_taxable_income_amount the net taxable income from the household (euros)
|
121
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
122
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
123
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
124
|
+
# @options hash [Float] :discount_on_low_income_tax_amount the amount of income tax reduction for low incomes (euros)
|
125
|
+
# @options hash [Integer] :fiscal_nb_parts the household's number of fiscal parts (nb)
|
126
|
+
def calc_income_tax_amount_for_year(simulation, calculation_method, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
127
|
+
# Calculate net taxable property income and global net taxable income
|
128
|
+
case calculation_method
|
129
|
+
when "with_property_income"
|
130
|
+
net_taxable_property_income_amount = calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
131
|
+
global_net_taxable_income_amount = calc_global_net_taxable_amount(simulation,
|
132
|
+
net_taxable_property_income_amount[:net_taxable_property_income_amount])
|
133
|
+
when "without_property_income"
|
134
|
+
global_net_taxable_income_amount = calc_global_net_taxable_amount(simulation,
|
135
|
+
0)
|
136
|
+
else
|
137
|
+
raise ArgumentError, "Not a valid argument, it should be 'with_property_income' or 'without_property_income'"
|
86
138
|
end
|
87
|
-
end
|
88
139
|
|
89
|
-
|
90
|
-
|
140
|
+
# Calculate the number of fiscal parts
|
141
|
+
fiscal_nb_parts = calc_fiscal_nb_parts(simulation)
|
142
|
+
fiscal_nb_parts_for_capping = simulation[:fiscal_marital_status] == "Célibataire" ? FISCAL_NB_PARTS_FOR_SINGLE_PERSON : FISCAL_NB_PARTS_FOR_MARRIED_COUPLE
|
91
143
|
|
92
|
-
|
144
|
+
# Calculate the family quotient amount
|
145
|
+
family_quotient_amount_for_real_fiscal_parts = calc_family_quotient_amount(global_net_taxable_income_amount,
|
146
|
+
fiscal_nb_parts)
|
147
|
+
family_quotient_amount_for_fiscal_parts_capping = calc_family_quotient_amount(global_net_taxable_income_amount,
|
148
|
+
fiscal_nb_parts_for_capping)
|
93
149
|
|
94
|
-
|
150
|
+
# Calculcate the aggregated tax amount
|
95
151
|
current_year = Date.today.year
|
152
|
+
aggregated_tax_amount_for_real_fiscal_parts = calc_aggregated_tax_amount(family_quotient_amount_for_real_fiscal_parts, current_year)
|
153
|
+
aggregated_tax_amount_for_fiscal_parts_capping = calc_aggregated_tax_amount(family_quotient_amount_for_fiscal_parts_capping, current_year)
|
154
|
+
|
155
|
+
# Apply fiscal part capping if necessary
|
156
|
+
capping_due_to_fiscal_parts = calc_capping_due_to_fiscal_parts(simulation, fiscal_nb_parts, current_year)
|
157
|
+
|
158
|
+
almost_final_income_tax = apply_fiscal_parts_capping(aggregated_tax_amount_for_real_fiscal_parts, fiscal_nb_parts, aggregated_tax_amount_for_fiscal_parts_capping, fiscal_nb_parts_for_capping, capping_due_to_fiscal_parts)
|
159
|
+
|
160
|
+
# Apply discount on low income tax if necessary
|
161
|
+
final_income_tax = apply_discount_on_low_income_tax(simulation, almost_final_income_tax, current_year)
|
162
|
+
|
163
|
+
# If the income tax is less than 61 euros, it is not collected
|
164
|
+
final_income_tax = final_income_tax <= 61 ? 0 : final_income_tax
|
165
|
+
|
166
|
+
# Return a hash of values
|
167
|
+
{
|
168
|
+
income_tax_amount: final_income_tax,
|
169
|
+
average_tax_rate: final_income_tax / global_net_taxable_income_amount,
|
170
|
+
global_net_taxable_income_amount: global_net_taxable_income_amount,
|
171
|
+
net_taxable_property_income_amount: calculation_method == "with_property_income" ? net_taxable_property_income_amount[:net_taxable_property_income_amount] : 0,
|
172
|
+
negative_taxable_property_income?: calculation_method == "with_property_income" ? net_taxable_property_income_amount[:negative_taxable_property_income?] : false,
|
173
|
+
negative_taxable_property_income_amount_to_postpone: calculation_method == "with_property_income" ? net_taxable_property_income_amount[:negative_taxable_property_income_amount_to_postpone] : 0,
|
174
|
+
discount_on_low_income_tax_amount: (almost_final_income_tax - final_income_tax).positive? ? almost_final_income_tax - final_income_tax : 0,
|
175
|
+
fiscal_nb_parts: fiscal_nb_parts
|
176
|
+
}
|
177
|
+
end
|
96
178
|
|
97
|
-
|
179
|
+
def calc_social_contributions_amount_for_year(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
180
|
+
# Is only triggered from main formula so it assumes that this is a "with_property_income" case
|
181
|
+
# Calculate net taxable property income that will be reported to French taxes
|
182
|
+
net_taxable_property_income_amount = calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
98
183
|
|
99
|
-
|
184
|
+
# Return the social contributions to pay in addition to income taxes (it really never ends...)
|
185
|
+
if net_taxable_property_income_amount[:net_taxable_property_income_amount] <= 0
|
186
|
+
0
|
187
|
+
else
|
188
|
+
net_taxable_property_income_amount[:net_taxable_property_income_amount] * SOCIAL_CONTRIBUTIONS_PERCENTAGE
|
189
|
+
end
|
100
190
|
end
|
101
191
|
|
102
|
-
|
103
|
-
|
192
|
+
# Calculate the global net taxable amount with or without the generated income from the property investment
|
193
|
+
#
|
194
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
195
|
+
# @options simulation [Integer] :fiscal_revenues_p1 salary from person 1 of the fiscal household (euros)
|
196
|
+
# @options simulation [Integer] :fiscal_revenues_p2 salary from person 2 of the fiscal household (euros)
|
197
|
+
# @params [Float] net_taxable_property_income_amount the taxable amount generated from the property income, can be positive or negative (euros)
|
198
|
+
#
|
199
|
+
# @return [Float] the global net taxable amount (euros)
|
200
|
+
def calc_global_net_taxable_amount(simulation, net_taxable_property_income_amount)
|
201
|
+
((simulation[:fiscal_revenues_p1] + (simulation.key?(:fiscal_revenues_p2) ? simulation[:fiscal_revenues_p2] : 0)) * (1 - REVENUES_STANDARD_ALLOWANCE)) + net_taxable_property_income_amount
|
104
202
|
end
|
105
203
|
|
106
|
-
|
107
|
-
|
204
|
+
# Calculate the household's number of fiscal parts
|
205
|
+
#
|
206
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
207
|
+
# @options simulation [String] :fiscal_marital_status fiscal relation between the 'parents' of the household
|
208
|
+
# @options simulation [Integer] :fiscal_nb_dependent_children number of dependent children of fiscal household (nb)
|
209
|
+
# @options simulation [Integer] :fiscal_nb_alternate_custody_children number of alternate custody children of fiscal household (nb)
|
210
|
+
#
|
211
|
+
# @return [Integer] the number of fiscal parts (nb)
|
212
|
+
def calc_fiscal_nb_parts(simulation)
|
213
|
+
case simulation[:fiscal_marital_status]
|
108
214
|
when "Marié / Pacsé"
|
109
|
-
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE + calc_fiscal_nb_parts_incurred_from_children
|
215
|
+
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE + calc_fiscal_nb_parts_incurred_from_children(simulation)
|
110
216
|
when "Célibataire"
|
111
|
-
|
217
|
+
# We make the assumption that when 'Celibataire' the parent lives alone, so the fiscal part incurred from the first children (ie the biggest one in term of fiscal part) is double
|
218
|
+
if simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 0
|
219
|
+
FISCAL_NB_PARTS_FOR_SINGLE_PERSON
|
220
|
+
|
221
|
+
elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 1
|
222
|
+
FISCAL_NB_PARTS_FOR_SINGLE_PERSON + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)
|
223
|
+
|
224
|
+
elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] >= 2
|
225
|
+
FISCAL_NB_PARTS_FOR_SINGLE_PERSON + 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)
|
226
|
+
|
227
|
+
elsif simulation[:fiscal_nb_dependent_children] >= 1
|
228
|
+
FISCAL_NB_PARTS_FOR_SINGLE_PERSON + FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)
|
229
|
+
end
|
112
230
|
end
|
113
231
|
end
|
114
232
|
|
115
|
-
|
116
|
-
|
117
|
-
|
233
|
+
# Calculate the number of fiscal parts incurred from children
|
234
|
+
#
|
235
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
236
|
+
# @options simulation [Integer] :fiscal_nb_dependent_children number of dependent children of fiscal household (nb)
|
237
|
+
# @options simulation [Integer] :fiscal_nb_alternate_custody_children number of alternate custody children of fiscal household (nb)
|
238
|
+
#
|
239
|
+
# @return [Integer] the number of fiscal parts incurred from children (nb)
|
240
|
+
def calc_fiscal_nb_parts_incurred_from_children(simulation)
|
241
|
+
total_nb_children = simulation[:fiscal_nb_dependent_children] + simulation[:fiscal_nb_alternate_custody_children]
|
118
242
|
|
119
243
|
if total_nb_children <= 2
|
120
|
-
|
244
|
+
FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN * simulation[:fiscal_nb_dependent_children] + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * simulation[:fiscal_nb_alternate_custody_children]
|
121
245
|
|
122
246
|
elsif total_nb_children >= 3
|
123
|
-
|
124
|
-
if fiscal_nb_dependent_children == 0
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
247
|
+
# Above 3 children, fiscal nb parts for added children get multiplied by 2
|
248
|
+
if simulation[:fiscal_nb_dependent_children] == 0
|
249
|
+
first_two_children = 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
|
250
|
+
next_children = (simulation[:fiscal_nb_alternate_custody_children] - 2) * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
|
251
|
+
first_two_children + next_children
|
252
|
+
|
253
|
+
elsif simulation[:fiscal_nb_dependent_children] == 1
|
254
|
+
if simulation[:fiscal_nb_alternate_custody_children] == 2
|
255
|
+
first_two_children = FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
|
256
|
+
next_children = FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
|
257
|
+
first_two_children + next_children
|
258
|
+
|
259
|
+
elsif simulation[:fiscal_nb_alternate_custody_children] >= 3
|
260
|
+
first_two_children = FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
|
261
|
+
next_children = (simulation[:fiscal_nb_alternate_custody_children] - 1) * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
|
262
|
+
first_two_children + next_children
|
132
263
|
end
|
133
264
|
|
134
|
-
elsif fiscal_nb_dependent_children == 2
|
135
|
-
|
265
|
+
elsif simulation[:fiscal_nb_dependent_children] == 2
|
266
|
+
first_two_children = 2 * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN
|
267
|
+
next_children = simulation[:fiscal_nb_alternate_custody_children] * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
|
268
|
+
first_two_children + next_children
|
269
|
+
|
270
|
+
elsif simulation[:fiscal_nb_dependent_children] >= 3
|
271
|
+
first_two_children = 2 * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN
|
272
|
+
next_children = (simulation[:fiscal_nb_dependent_children] - 2) * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN * 2 + simulation[:fiscal_nb_alternate_custody_children] * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
|
273
|
+
first_two_children + next_children
|
136
274
|
|
137
|
-
elsif fiscal_nb_dependent_children >= 3
|
138
|
-
2 + fiscal_nb_alternate_custody_children * 0.5
|
139
275
|
end
|
140
276
|
end
|
141
277
|
end
|
142
278
|
|
279
|
+
# Calculate the family quotient amount
|
280
|
+
#
|
281
|
+
# @params [Float] :global_net_taxable_income_amount the net taxable income from the household (euros)
|
282
|
+
# @params [Integer] fiscal_nb_parts the household's number of fiscal parts (nb)
|
283
|
+
#
|
284
|
+
# @return [Integer] the family quotient amount (euros)
|
143
285
|
def calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
|
144
286
|
global_net_taxable_income_amount / fiscal_nb_parts
|
145
287
|
end
|
146
288
|
|
147
|
-
|
148
|
-
|
289
|
+
# Calculate the capping income tax deduction from fiscal parts
|
290
|
+
#
|
291
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
292
|
+
# @options simulation [Integer] :fiscal_nb_dependent_children number of dependent children of fiscal household (nb)
|
293
|
+
# @options simulation [Integer] :fiscal_nb_alternate_custody_children number of alternate custody children of fiscal household (nb)
|
294
|
+
# @params [Integer] fiscal_nb_parts the household's number of fiscal parts (nb)
|
295
|
+
# @params [Integer] current_year the current_year of the calculation (nb)
|
296
|
+
#
|
297
|
+
# @return [Integer] the capping income tax deduction from fiscal parts (euros)
|
298
|
+
def calc_capping_due_to_fiscal_parts(simulation, fiscal_nb_parts, current_year)
|
299
|
+
case simulation[:fiscal_marital_status]
|
300
|
+
when "Marié / Pacsé"
|
301
|
+
(fiscal_nb_parts - FISCAL_NB_PARTS_FOR_MARRIED_COUPLE) * FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:married_couple_household][:half_part] * 2
|
302
|
+
when "Célibataire"
|
303
|
+
# When single parent household, sicne we consider it as 'parent isole', the first half part (if only alt custody children) or the first part (if at least one dependent child) is marked up
|
304
|
+
# It is linked to the doubled fiscal part for the first child (ie the biggest one in term of fiscal part) that we use in calc_fiscal_nb_parts
|
305
|
+
if simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 0
|
306
|
+
0
|
307
|
+
|
308
|
+
elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 1
|
309
|
+
marked_up_half_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part]
|
310
|
+
next_parts = (fiscal_nb_parts - 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN - FISCAL_NB_PARTS_FOR_SINGLE_PERSON) * FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:half_part] * 2
|
311
|
+
marked_up_half_part + next_parts
|
312
|
+
|
313
|
+
elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] >= 2
|
314
|
+
marked_up_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part] * 2
|
315
|
+
next_parts = (fiscal_nb_parts - 2 * 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN - FISCAL_NB_PARTS_FOR_SINGLE_PERSON) * FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:half_part] * 2
|
316
|
+
marked_up_part + next_parts
|
317
|
+
|
318
|
+
elsif simulation[:fiscal_nb_dependent_children] >= 1
|
319
|
+
marked_up_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part] * 2
|
320
|
+
next_parts = (fiscal_nb_parts - 2 * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN - FISCAL_NB_PARTS_FOR_SINGLE_PERSON) * FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:half_part] * 2
|
321
|
+
marked_up_part + next_parts
|
322
|
+
end
|
323
|
+
end
|
149
324
|
end
|
150
325
|
|
326
|
+
# Calculate the aggregated tax amount
|
327
|
+
#
|
328
|
+
# @params [Integer] family_quotient_amount the household's quotient amount (euros)
|
329
|
+
# @params [Integer] current_year the current_year of the calculation (nb)
|
330
|
+
#
|
331
|
+
# @return [Integer] the aggregated tax amount (euros)
|
151
332
|
def calc_aggregated_tax_amount(family_quotient_amount, current_year)
|
152
333
|
income_taxes_scale = INCOME_TAXES_SCALE["year#{current_year}".to_sym]
|
153
334
|
|
@@ -161,4 +342,55 @@ module FrenchTaxSystem
|
|
161
342
|
end
|
162
343
|
end.sum
|
163
344
|
end
|
345
|
+
|
346
|
+
# Apply fiscal part capping
|
347
|
+
#
|
348
|
+
# @params [Float] aggregated_tax_amount_for_real_fiscal_parts the aggregated tax amount from the fiscal parts with children (euros)
|
349
|
+
# @params [Integer] fiscal_nb_parts the household's number of fiscal parts (nb)
|
350
|
+
# @params [Float] aggregated_tax_amount_for_fiscal_parts_capping the aggregated tax amount from the fiscal parts of the parent(s) (euros)
|
351
|
+
# @params [Integer] fiscal_nb_parts_for_capping the parent(s) number of fiscal parts (nb)
|
352
|
+
# @params [Float] capping_due_to_fiscal_parts the capping to income tax deduction from fiscal parts to apply (euros)
|
353
|
+
#
|
354
|
+
# @return [Float] the previsional income tax with fiscal part capping effect if necessary (euros)
|
355
|
+
def apply_fiscal_parts_capping(aggregated_tax_amount_for_real_fiscal_parts, fiscal_nb_parts, aggregated_tax_amount_for_fiscal_parts_capping, fiscal_nb_parts_for_capping, capping_due_to_fiscal_parts)
|
356
|
+
not_capped_income_tax = aggregated_tax_amount_for_real_fiscal_parts * fiscal_nb_parts
|
357
|
+
capped_income_tax = (aggregated_tax_amount_for_fiscal_parts_capping * fiscal_nb_parts_for_capping) - capping_due_to_fiscal_parts
|
358
|
+
|
359
|
+
[not_capped_income_tax, capped_income_tax].max
|
360
|
+
end
|
361
|
+
|
362
|
+
# Apply on final tax amount the discount for low incomes
|
363
|
+
#
|
364
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
365
|
+
# @options simulation [String] :fiscal_marital_status fiscal relation between the 'parents' of the household
|
366
|
+
# @params [Float] almost_final_income_tax the highest amount between the aggregated tax amounts from capped and not capped fiscal parts (euros)
|
367
|
+
# @params [Integer] current_year the current_year of the calculation (nb)
|
368
|
+
#
|
369
|
+
# @return [Integer] the final tax income with the reduced income tax for low incomes (euros)
|
370
|
+
def apply_discount_on_low_income_tax(simulation, almost_final_income_tax, current_year)
|
371
|
+
if almost_final_income_tax.positive? && simulation[:fiscal_marital_status] == "Célibataire" && almost_final_income_tax <= DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:threshold_single_person_household]
|
372
|
+
discount_to_apply = DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:lump_sum_single_person_household] - (almost_final_income_tax * DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:discount_percentage])
|
373
|
+
(almost_final_income_tax - discount_to_apply).negative? ? 0 : almost_final_income_tax - discount_to_apply
|
374
|
+
|
375
|
+
elsif almost_final_income_tax.positive? && simulation[:fiscal_marital_status] == "Marié / Pacsé" && almost_final_income_tax <= DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:threshold_married_couple_household]
|
376
|
+
discount_to_apply = DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:lump_sum_married_couple_household] - (almost_final_income_tax * DISCOUNT_ON_LOW_INCOME_TAX["year#{current_year}".to_sym][:discount_percentage])
|
377
|
+
(almost_final_income_tax - discount_to_apply).negative? ? 0 : almost_final_income_tax - discount_to_apply
|
378
|
+
|
379
|
+
else
|
380
|
+
almost_final_income_tax
|
381
|
+
end
|
382
|
+
end
|
383
|
+
|
384
|
+
def calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
385
|
+
case simulation[:fiscal_status]
|
386
|
+
when "Vide"
|
387
|
+
NueFormulas.calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
388
|
+
when "LMNP"
|
389
|
+
LmnpFormulas.calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
390
|
+
when nil
|
391
|
+
raise ArgumentError, "fiscal_status can't be nil"
|
392
|
+
else
|
393
|
+
raise ArgumentError, "Not a valid fiscal status, it should be one available"
|
394
|
+
end
|
395
|
+
end
|
164
396
|
end
|
data/lib/lmnp_formulas.rb
CHANGED
@@ -1,9 +1,176 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FrenchTaxSystem
|
4
|
-
|
5
|
-
|
6
|
-
|
4
|
+
module LmnpFormulas
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Constants
|
8
|
+
PROPERTY_INCOME_STANDARD_ALLOWANCE = 0.5
|
9
|
+
AVERAGE_AMORTIZATION_PROPERTY_DURATION = 33.00
|
10
|
+
AVERAGE_AMORTIZATION_FIRST_WORKS_DURATION = 20.00
|
11
|
+
|
12
|
+
# Methods
|
13
|
+
# Calculate the net taxable income generated from the property investment
|
14
|
+
#
|
15
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
16
|
+
# @options simulation [Integer] :house_rent_amount_per_year how much is the rent paid by the tenant (euros/year)
|
17
|
+
# @options simulation [Integer] :house_price_bought_amount how much was the house bought (euros)
|
18
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
19
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
20
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
21
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
22
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
23
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
24
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
25
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
26
|
+
# @options simulation [String] :fiscal_regimen what fiscal regimen has been chosen
|
27
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
28
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
29
|
+
#
|
30
|
+
# @return [Hash] fiscal_year* the corresponding year * as requested in @params
|
31
|
+
# @options fiscal_year* [Float] :net_taxable_property_income_amount the taxable income generated from the property investment to add to the global taxable income
|
32
|
+
# @options fiscal_year* [Boolean] :negative_taxable_property_income? are we doing a taxable income reduction thks to our property investment
|
33
|
+
# @options fiscal_year* [Integer] :negative_taxable_property_income_amount_to_postpone negative taxable property income amount to postpone to the next fiscal year
|
34
|
+
def calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
35
|
+
# Calculate net taxable property income amount thks to fiscal regimen
|
36
|
+
case simulation[:fiscal_regimen]
|
37
|
+
when "Forfait"
|
38
|
+
calc_flat_rate_regimen_net_taxable_property_income_amount(simulation)
|
39
|
+
when "Réel"
|
40
|
+
calc_deductible_expenses_regimen_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Calculate net taxable property income for 'Forfait' fiscal_regimen
|
45
|
+
#
|
46
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
47
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
48
|
+
#
|
49
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
50
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
51
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
52
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
53
|
+
def calc_flat_rate_regimen_net_taxable_property_income_amount(simulation)
|
54
|
+
net_taxable_property_income_amount = simulation[:house_rent_amount_per_year] * (1 - PROPERTY_INCOME_STANDARD_ALLOWANCE)
|
55
|
+
{
|
56
|
+
net_taxable_property_income_amount: net_taxable_property_income_amount,
|
57
|
+
negative_taxable_property_income?: false,
|
58
|
+
negative_taxable_property_income_amount_to_postpone: 0
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
# Calculate net taxable property income for 'Reel' fiscal_regimen
|
63
|
+
#
|
64
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
65
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
66
|
+
# @options simulation [Integer] :house_price_bought_amount how much was the house bought (euros)
|
67
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
68
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
69
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
70
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
71
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
72
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
73
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
74
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
75
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
76
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
77
|
+
#
|
78
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
79
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
80
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
81
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
82
|
+
def calc_deductible_expenses_regimen_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
83
|
+
# Calculate deductible expenses from this fiscal year
|
84
|
+
deductible_expenses = calc_deductible_expenses_sum(simulation, investment_fiscal_year)
|
85
|
+
|
86
|
+
# Calculate amortization for average property
|
87
|
+
amortization_property = calc_amortization(simulation[:house_price_bought_amount], AVERAGE_AMORTIZATION_PROPERTY_DURATION)
|
88
|
+
|
89
|
+
# Calculate amortization for first works
|
90
|
+
amortization_first_works = calc_amortization(simulation[:house_first_works_amount], AVERAGE_AMORTIZATION_FIRST_WORKS_DURATION)
|
91
|
+
|
92
|
+
# Calculate gross taxable property income amount depending on fiscal year and with postponed negative taxable property income from previous fiscal year
|
93
|
+
gross_taxable_property_income_amount = calc_gross_taxable_property_income_amount(simulation, deductible_expenses, amortization_property, amortization_first_works, postponed_negative_taxable_property_income_from_previous_fiscal_year)
|
94
|
+
|
95
|
+
if gross_taxable_property_income_amount >= 0
|
96
|
+
# Return a hash with corresponding values
|
97
|
+
{
|
98
|
+
net_taxable_property_income_amount: gross_taxable_property_income_amount,
|
99
|
+
negative_taxable_property_income?: false,
|
100
|
+
negative_taxable_property_income_amount_to_postpone: 0
|
101
|
+
}
|
102
|
+
elsif gross_taxable_property_income_amount.negative?
|
103
|
+
# Cap negativity of net taxable amount and postpone negative taxable if remaining
|
104
|
+
calc_net_taxable_property_income_repartition(simulation, gross_taxable_property_income_amount)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# Calculate the gross taxable property income amount
|
109
|
+
#
|
110
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
111
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
112
|
+
# @parans [Float] :deductible_expenses the sum of deductible expenses (euros)
|
113
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
114
|
+
#
|
115
|
+
# @return [Float] the gross taxable property income amount
|
116
|
+
def calc_gross_taxable_property_income_amount(simulation, deductible_expenses, amortization_property, amortization_first_works, postponed_negative_taxable_property_income_from_previous_fiscal_year)
|
117
|
+
simulation[:house_rent_amount_per_year] - deductible_expenses - amortization_property - amortization_first_works - postponed_negative_taxable_property_income_from_previous_fiscal_year
|
118
|
+
end
|
119
|
+
|
120
|
+
# Calculate the sum of deductible expenses for this fiscal year
|
121
|
+
#
|
122
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
123
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
124
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
125
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
126
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
127
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
128
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
129
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
130
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
131
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
132
|
+
#
|
133
|
+
# @return [Float] the sum of deductible expenses for this fiscal year (euros)
|
134
|
+
def calc_deductible_expenses_sum(simulation, investment_fiscal_year)
|
135
|
+
if investment_fiscal_year == 1
|
136
|
+
FrenchTaxSystem::REAL_REGIMEN_DEDUCTIBLE_EXPENSES[:fiscal_year1].map do |expense|
|
137
|
+
simulation.key?(expense.to_sym) ? simulation[expense.to_sym] : 0
|
138
|
+
end.sum
|
139
|
+
elsif investment_fiscal_year >= 2
|
140
|
+
FrenchTaxSystem::REAL_REGIMEN_DEDUCTIBLE_EXPENSES[:fiscal_year2].map do |expense|
|
141
|
+
simulation.key?(expense.to_sym) ? simulation[expense.to_sym] : 0
|
142
|
+
end.sum
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# Calculate the amortization for this fiscal year
|
147
|
+
#
|
148
|
+
# @params [Float] expense an expense to be amortized
|
149
|
+
# @params [Integer] amortization_duration indicates the amortization duration which is being used (years)
|
150
|
+
#
|
151
|
+
# @return [Float] the amortization for this fiscal year (euros)
|
152
|
+
def calc_amortization(expense, amortization_duration)
|
153
|
+
expense / amortization_duration
|
154
|
+
end
|
155
|
+
|
156
|
+
# Calculate and cap if necessary the negative taxable income and postpone negative taxable if remaining
|
157
|
+
#
|
158
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
159
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
160
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
161
|
+
# @params [Float] :gross_taxable_property_income_amount the taxable income generated from the property investment to add to the global taxable income
|
162
|
+
#
|
163
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
164
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
165
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
166
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
167
|
+
def calc_net_taxable_property_income_repartition(_simulation, gross_taxable_property_income_amount)
|
168
|
+
# gross_taxable_property_income_amount is necessarily negative because of conditional execution of this method
|
169
|
+
{
|
170
|
+
net_taxable_property_income_amount: 0,
|
171
|
+
negative_taxable_property_income?: true,
|
172
|
+
negative_taxable_property_income_amount_to_postpone: gross_taxable_property_income_amount.abs
|
173
|
+
}
|
7
174
|
end
|
8
175
|
end
|
9
176
|
end
|
data/lib/nue_formulas.rb
CHANGED
@@ -1,24 +1,197 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module FrenchTaxSystem
|
4
|
-
|
4
|
+
module NueFormulas
|
5
|
+
extend self
|
6
|
+
|
7
|
+
# Constants
|
5
8
|
PROPERTY_INCOME_STANDARD_ALLOWANCE = 0.3
|
9
|
+
CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT = 10_700
|
6
10
|
|
7
|
-
#
|
8
|
-
#
|
9
|
-
#
|
10
|
-
#
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
# Methods
|
12
|
+
# Calculate the net taxable income generated from the property investment
|
13
|
+
#
|
14
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
15
|
+
# @options simulation [Integer] :house_rent_amount_per_year how much is the rent paid by the tenant (euros/year)
|
16
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
17
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
18
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
19
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
20
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
21
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
22
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
23
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
24
|
+
# @options simulation [String] :fiscal_regimen what fiscal regimen has been chosen
|
25
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
26
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
27
|
+
#
|
28
|
+
# @return [Hash] fiscal_year* the corresponding year * as requested in @params
|
29
|
+
# @options fiscal_year* [Float] :net_taxable_property_income_amount the taxable income generated from the property investment to add to the global taxable income
|
30
|
+
# @options fiscal_year* [Boolean] :negative_taxable_property_income? are we doing a taxable income reduction thks to our property investment
|
31
|
+
# @options fiscal_year* [Integer] :negative_taxable_property_income_amount_to_postpone negative taxable property income amount to postpone to the next fiscal year
|
32
|
+
def calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
33
|
+
# Calculate net taxable property income amount thks to fiscal regimen
|
34
|
+
case simulation[:fiscal_regimen]
|
35
|
+
when "Forfait"
|
36
|
+
calc_flat_rate_regimen_net_taxable_property_income_amount(simulation)
|
14
37
|
when "Réel"
|
15
|
-
|
16
|
-
|
38
|
+
calc_deductible_expenses_regimen_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Calculate net taxable property income for 'Forfait' fiscal_regimen
|
43
|
+
#
|
44
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
45
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
46
|
+
#
|
47
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
48
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
49
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
50
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
51
|
+
def calc_flat_rate_regimen_net_taxable_property_income_amount(simulation)
|
52
|
+
net_taxable_property_income_amount = simulation[:house_rent_amount_per_year] * (1 - PROPERTY_INCOME_STANDARD_ALLOWANCE)
|
53
|
+
{
|
54
|
+
net_taxable_property_income_amount: net_taxable_property_income_amount,
|
55
|
+
negative_taxable_property_income?: false,
|
56
|
+
negative_taxable_property_income_amount_to_postpone: 0
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
# Calculate net taxable property income for 'Reel' fiscal_regimen
|
61
|
+
#
|
62
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
63
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
64
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
65
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
66
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
67
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
68
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
69
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
70
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
71
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
72
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
73
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
74
|
+
#
|
75
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
76
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
77
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
78
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
79
|
+
def calc_deductible_expenses_regimen_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
|
80
|
+
# Calculate deductible expenses from this fiscal year
|
81
|
+
deductible_expenses = calc_deductible_expenses_sum(simulation, investment_fiscal_year)
|
82
|
+
|
83
|
+
# Calculate gross taxable property income amount depending on fiscal year and with postponed negative taxable property income from previous fiscal year
|
84
|
+
gross_taxable_property_income_amount = calc_gross_taxable_property_income_amount(simulation, deductible_expenses, postponed_negative_taxable_property_income_from_previous_fiscal_year)
|
85
|
+
|
86
|
+
if gross_taxable_property_income_amount >= 0
|
87
|
+
# Return a hash with corresponding values
|
88
|
+
{
|
89
|
+
net_taxable_property_income_amount: gross_taxable_property_income_amount,
|
90
|
+
negative_taxable_property_income?: false,
|
91
|
+
negative_taxable_property_income_amount_to_postpone: 0
|
92
|
+
}
|
93
|
+
elsif gross_taxable_property_income_amount.negative?
|
94
|
+
# Cap negativity of net taxable amount and postpone negative taxable if remaining
|
95
|
+
calc_net_taxable_property_income_repartition(simulation, gross_taxable_property_income_amount)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Calculate the gross taxable property income amount
|
100
|
+
#
|
101
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
102
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
103
|
+
# @parans [Float] :deductible_expenses the sum of deductible expenses (euros)
|
104
|
+
# @params [Integer] postponed_negative_taxable_property_income_from_previous_fiscal_year the potentiel negative taxable income from the previous fiscal year
|
105
|
+
#
|
106
|
+
# @return [Float] the gross taxable property income amount
|
107
|
+
def calc_gross_taxable_property_income_amount(simulation, deductible_expenses, postponed_negative_taxable_property_income_from_previous_fiscal_year)
|
108
|
+
simulation[:house_rent_amount_per_year] - deductible_expenses - postponed_negative_taxable_property_income_from_previous_fiscal_year
|
109
|
+
end
|
110
|
+
|
111
|
+
# Calculate the sum of deductible expenses for this fiscal year
|
112
|
+
#
|
113
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
114
|
+
# @options simulation [Integer] :house_first_works_amount how much were the first works realized (euros)
|
115
|
+
# @options simulation [Integer] :house_landlord_charges_amount_per_year how much are the landlord charges (euros/year)
|
116
|
+
# @options simulation [Float] :house_property_management_amount_per_year how much is property management cost (euros/year)
|
117
|
+
# @options simulation [Integer] :house_insurance_gli_amount_per_year how much is gli insurance cost (euros/year)
|
118
|
+
# @options simulation [Integer] :house_insurance_pno_amount_per_year how much is pno insurance cost (euros/year)
|
119
|
+
# @options simulation [Integer] :house_property_tax_amount_per_year how much is the property tax (euros/year)
|
120
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
121
|
+
# @options simulation [Float] :credit_loan_insurance_amount_per_year how much is the credit insurance cost (euros/year)
|
122
|
+
# @params [Integer] investment_fiscal_year indicates the investment fiscal year on which the calculation is made
|
123
|
+
#
|
124
|
+
# @return [Float] the sum of deductible expenses for this fiscal year (euros)
|
125
|
+
def calc_deductible_expenses_sum(simulation, investment_fiscal_year)
|
126
|
+
if investment_fiscal_year == 1
|
127
|
+
FrenchTaxSystem::REAL_REGIMEN_DEDUCTIBLE_EXPENSES[:fiscal_year1].map do |expense|
|
128
|
+
simulation.key?(expense.to_sym) ? simulation[expense.to_sym] : 0
|
129
|
+
end.sum
|
130
|
+
elsif investment_fiscal_year >= 2
|
131
|
+
FrenchTaxSystem::REAL_REGIMEN_DEDUCTIBLE_EXPENSES[:fiscal_year2].map do |expense|
|
132
|
+
simulation.key?(expense.to_sym) ? simulation[expense.to_sym] : 0
|
17
133
|
end.sum
|
18
|
-
simulation_instance.house_rent_amount_per_year - deductible_expenses
|
19
|
-
when "Forfait"
|
20
|
-
simulation_instance.house_rent_amount_per_year * (1 - PROPERTY_INCOME_STANDARD_ALLOWANCE)
|
21
134
|
end
|
22
135
|
end
|
136
|
+
|
137
|
+
# Calculate and cap if necessary the negative taxable income and postpone negative taxable if remaining
|
138
|
+
#
|
139
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
140
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
141
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
142
|
+
# @params [Float] :gross_taxable_property_income_amount the taxable income generated from the property investment to add to the global taxable income
|
143
|
+
#
|
144
|
+
# @return [Hash] a hash made of the net taxable property income (euros) and other values
|
145
|
+
# @options hash [Float] :net_taxable_property_income_amount the net taxable property income generated from the investment (euros)
|
146
|
+
# @options hash [Boolean] :negative_taxable_property_income? returns true or false if there is a negative taxable property income for this fiscal year
|
147
|
+
# @options hash [Float] :negative_taxable_property_income_amount_to_postpone the potential negative taxable property income to postpone to the next fiscal year (euros)
|
148
|
+
def calc_net_taxable_property_income_repartition(simulation, gross_taxable_property_income_amount)
|
149
|
+
property_income_minus_loan_interet_cost = calc_property_income_minus_loan_interet_cost(simulation)
|
150
|
+
|
151
|
+
# If property_income_minus_loan_interet_cost is positive, we deduct all expenses from this fiscal year gross_taxable_property_income_amount and report what's left to next fiscal years
|
152
|
+
if property_income_minus_loan_interet_cost.positive? && gross_taxable_property_income_amount.abs <= CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT
|
153
|
+
{
|
154
|
+
net_taxable_property_income_amount: gross_taxable_property_income_amount,
|
155
|
+
negative_taxable_property_income?: true,
|
156
|
+
negative_taxable_property_income_amount_to_postpone: 0
|
157
|
+
}
|
158
|
+
elsif property_income_minus_loan_interet_cost.positive? && gross_taxable_property_income_amount.abs > CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT
|
159
|
+
{
|
160
|
+
net_taxable_property_income_amount: - CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT,
|
161
|
+
negative_taxable_property_income?: true,
|
162
|
+
negative_taxable_property_income_amount_to_postpone: (gross_taxable_property_income_amount + CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT).abs
|
163
|
+
}
|
164
|
+
# If property_income_minus_loan_interet_cost is negative, we deduct all expenses EXCEPT credit interest costs from this fiscal year gross_taxable_property_income_amount and report what's left + credit interest cost to next fiscal years
|
165
|
+
elsif property_income_minus_loan_interet_cost.negative? && gross_taxable_property_income_amount.abs <= CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT
|
166
|
+
{
|
167
|
+
net_taxable_property_income_amount: gross_taxable_property_income_amount + simulation[:credit_loan_cumulative_interests_paid_for_year_two],
|
168
|
+
negative_taxable_property_income?: true,
|
169
|
+
negative_taxable_property_income_amount_to_postpone: simulation[:credit_loan_cumulative_interests_paid_for_year_two]
|
170
|
+
}
|
171
|
+
elsif property_income_minus_loan_interet_cost.negative? && gross_taxable_property_income_amount.abs > CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT && (gross_taxable_property_income_amount.abs - simulation[:credit_loan_cumulative_interests_paid_for_year_two]) > CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT
|
172
|
+
{
|
173
|
+
net_taxable_property_income_amount: - CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT,
|
174
|
+
negative_taxable_property_income?: true,
|
175
|
+
negative_taxable_property_income_amount_to_postpone: (gross_taxable_property_income_amount + CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT).abs
|
176
|
+
}
|
177
|
+
elsif property_income_minus_loan_interet_cost.negative? && gross_taxable_property_income_amount.abs > CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT && (gross_taxable_property_income_amount.abs - simulation[:credit_loan_cumulative_interests_paid_for_year_two]) <= CAPPED_NEGATIVE_NET_TAXABLE_INCOME_AMOUNT
|
178
|
+
{
|
179
|
+
net_taxable_property_income_amount: gross_taxable_property_income_amount + simulation[:credit_loan_cumulative_interests_paid_for_year_two],
|
180
|
+
negative_taxable_property_income?: true,
|
181
|
+
negative_taxable_property_income_amount_to_postpone: simulation[:credit_loan_cumulative_interests_paid_for_year_two]
|
182
|
+
}
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
# Calculate the property income amount minus loan interest cost
|
187
|
+
#
|
188
|
+
# @params [Hash] simulation a simulation created by Mini-Keyz app
|
189
|
+
# @options simulation [Float] :house_rent_amount_per_year the income generated per year (euros)
|
190
|
+
# @options simulation [Float] :credit_loan_cumulative_interests_paid_for_year_two how much is the credit interest cost for year 2 (euros/year)
|
191
|
+
#
|
192
|
+
# @return [Float] the property income amount minus loan interest cost that is used to determine negative net taxable income repartition
|
193
|
+
def calc_property_income_minus_loan_interet_cost(simulation)
|
194
|
+
simulation[:house_rent_amount_per_year] - simulation[:credit_loan_cumulative_interests_paid_for_year_two]
|
195
|
+
end
|
23
196
|
end
|
24
197
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: french_tax_system
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 1.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mathieu EUSTACHY
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06-
|
11
|
+
date: 2021-06-17 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|