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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 011b928965029163173e68112ae51080948046dbe3d403bb4fa1a2748ad1ecc8
4
- data.tar.gz: 77a452106b4936fb835999bcd715b56829b2c0c3fcbcc50f7991543ded67799f
3
+ metadata.gz: c8fae1b53a7ecf7549facdc39a0c66457b7dfca65307a160d3b1fe517e31a47f
4
+ data.tar.gz: 76d46ad120ab3796290b224e98c4c48d2739b629a7cbb6fd1308a3a82bd8fcdd
5
5
  SHA512:
6
- metadata.gz: 33cd93886945b241a613c41d6b744be37a950c1677e3cb1a4bd800a140747abb22cdad38b9ff69a76712a82afef05b3513ab692edf1748b11c5f8ca15f35a2b3
7
- data.tar.gz: 29ae86383f5c6d7a19d6a671f62a71d8fdb919b17aea7406c68af5b7268e01b2ae0f742e4c0f0c84de425b22c18f34348377f2fd751765fdcc5776e9fe2ae666
6
+ metadata.gz: cf7229ac603b71b2f6a48c2a51664b2a09d5b27e301f39470f5a5b949f7d83f8e4a770caad432a23db3da6e346551647853fa9382fbfa55ea8c7a26858af1cec
7
+ data.tar.gz: c290b721a56d0c96c4ca40d63a3b3617f0ffa07fd70114337b80f2a4a57aa6c4b883230e8d0d0f6dc33a2c2b9188e00268958fc21dc62ae3a62bd18dce129d42
@@ -15,4 +15,4 @@ jobs:
15
15
  run: |
16
16
  gem install bundler -v 2.2.15
17
17
  bundle install
18
- bundle exec rake
18
+ bundle exec rspec
data/.rubocop.yml CHANGED
@@ -1,5 +1,6 @@
1
1
  AllCops:
2
2
  TargetRubyVersion: 2.7
3
+ NewCops: enable
3
4
 
4
5
  Style/StringLiterals:
5
6
  Enabled: true
@@ -10,4 +11,4 @@ Style/StringLiteralsInInterpolation:
10
11
  EnforcedStyle: double_quotes
11
12
 
12
13
  Layout/LineLength:
13
- Max: 120
14
+ Max: 240
data/Gemfile CHANGED
@@ -8,4 +8,4 @@ gemspec
8
8
  gem "rake", "~> 13.0"
9
9
 
10
10
  gem "rspec", "~> 3.0"
11
-
11
+ gem "rubocop", "~> 1.17"
data/Gemfile.lock CHANGED
@@ -1,13 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- french_tax_system (0.1.2)
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
@@ -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: 1570
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
- REVENUES_STANDARD_ALLOWANCE = 0.1
28
-
29
- REAL_REGIMEN_DEDUCTIBLE_EXPENSES_FOR_YEAR_TWO = %w[house_landlord_charges_amount_per_year
30
- 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].freeze
31
-
32
- # Methods
33
- ## Main method
34
- def test
35
- "test me"
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
- family_quotient_amount = calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
57
- current_year = Date.today.year
51
+ REVENUES_STANDARD_ALLOWANCE = 0.1
58
52
 
59
- aggregated_tax_amount = calc_aggregated_tax_amount(family_quotient_amount, current_year)
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
- (aggregated_tax_amount * fiscal_nb_parts).floor
62
- end
60
+ SOCIAL_CONTRIBUTIONS_PERCENTAGE = 0.172
63
61
 
64
- def calc_capped_income_tax_amount_per_year_with_property_income_of(net_taxable_property_income_amount)
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
- fiscal_nb_parts = case fiscal_marital_status
68
- when "Célibataire" then 1
69
- when "Marié / Pacsé" then 2
70
- end
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
- family_quotient_amount = calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
73
- current_year = Date.today.year
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
- aggregated_tax_amount = calc_aggregated_tax_amount(family_quotient_amount, current_year)
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
- (aggregated_tax_amount * fiscal_nb_parts).floor
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
- def calc_net_taxable_property_income_amount
81
- case fiscal_status
82
- when "Vide"
83
- NueFormulas.calc_net_taxable_property_income_amount(self)
84
- when "LMNP"
85
- LmnpFormulas.calc_net_taxable_property_income_amount(self)
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
- def calc_income_taxes_scale_with_property_income_of(net_taxable_property_income_amount)
90
- global_net_taxable_income_amount = calc_global_net_taxable_amount_with_property_income_of(net_taxable_property_income_amount)
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
- fiscal_nb_parts = calc_fiscal_nb_parts
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
- family_quotient_amount = calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
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
- income_taxes_scale = INCOME_TAXES_SCALE["year#{current_year}".to_sym]
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
- income_taxes_scale.find { |scale| family_quotient_amount <= scale[:family_quotient_amount][:end_scale] }
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
- def calc_global_net_taxable_amount_with_property_income_of(net_taxable_property_income_amount)
103
- ((fiscal_revenues_p1 + fiscal_revenues_p2) * (1 - REVENUES_STANDARD_ALLOWANCE)) + net_taxable_property_income_amount
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
- def calc_fiscal_nb_parts
107
- case fiscal_marital_status
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
- FISCAL_NB_PARTS_FOR_SINGLE_PERSON + calc_fiscal_nb_parts_incurred_from_children
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
- def calc_fiscal_nb_parts_incurred_from_children
116
- # Detail of calculation method in GitHub wiki: https://github.com/Mth0158/mini-keyz/wiki/French-tax-system
117
- total_nb_children = fiscal_nb_dependent_children + fiscal_nb_alternate_custody_children
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
- 0.5 * fiscal_nb_dependent_children + 0.25 * fiscal_nb_alternate_custody_children
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
- 0.5 + (fiscal_nb_alternate_custody_children - 2) * 0.5
126
-
127
- elsif fiscal_nb_dependent_children == 1
128
- if fiscal_nb_alternate_custody_children == 2
129
- 1.25
130
- elsif fiscal_nb_alternate_custody_children >= 3
131
- 1.25 + (fiscal_nb_alternate_custody_children - 2) * 0.5
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
- 1 + fiscal_nb_alternate_custody_children * 0.5
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
- def calc_family_quotient_capped_amount(_e)
148
- "ee"
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FrenchTaxSystem
4
- VERSION = "0.1.2"
4
+ VERSION = "1.0.0"
5
5
  end
data/lib/lmnp_formulas.rb CHANGED
@@ -1,9 +1,176 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module FrenchTaxSystem
4
- class LmnpFormulas
5
- def test3
6
- "ee3"
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
- class NueFormulas
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
- # def self.calc_income_tax_amount_for_year_two(args = {})
8
- # net_taxable_property_income_amount = calc_net_taxable_property_income_amount_for_year_two(args)
9
- # IncomeTaxesBaseFormulas.calc_income_tax_amount_per_year(args, net_taxable_property_income_amount)
10
- # end
11
-
12
- def self.calc_net_taxable_property_income_amount_for_year_two(simulation_instance)
13
- case simulation_instance.fiscal_regimen
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
- deductible_expenses = IncomeTaxesFormulas::REAL_REGIMEN_DEDUCTIBLE_EXPENSES_FOR_YEAR_TWO.map do |expense|
16
- simulation_instance.send(expense) || 0
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.1.2
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-07 00:00:00.000000000 Z
11
+ date: 2021-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec