Module: FrenchTaxSystem

Extended by:
FrenchTaxSystem
Included in:
FrenchTaxSystem
Defined in:
lib/nue_formulas.rb,
lib/lmnp_formulas.rb,
lib/french_tax_system.rb,
lib/french_tax_system/version.rb

Defined Under Namespace

Modules: LmnpFormulas, NueFormulas Classes: Error

Constant Summary collapse

INCOME_TAXES_SCALE =

Constants

{
  year2021: [
    { family_quotient_amount: { start_scale: 0, end_scale: 10_084 }, tax: 0 },
    { family_quotient_amount: { start_scale: 10_085, end_scale: 25_710 }, tax: 0.11 },
    { family_quotient_amount: { start_scale: 25_711, end_scale: 73_516 }, tax: 0.3 },
    { family_quotient_amount: { start_scale: 73_517, end_scale: 158_122 }, tax: 0.41 },
    { family_quotient_amount: { start_scale: 158_123, end_scale: Float::INFINITY }, tax: 0.45 }
  ]
}.freeze
FISCAL_NB_PARTS_FOR_MARRIED_COUPLE =
2
FISCAL_NB_PARTS_FOR_SINGLE_PERSON =
1
FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN =
0.5
FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN =
0.25
FAMILY_QUOTIENT_CAPPING_AMOUNT =
{
  ## Per half fiscal parts for children
  year2021: {
    married_couple_household: {
      half_part: 1570
    },
    single_person_household: {
      marked_up_half_part: 1852,
      half_part: 1570
    }
  }
}.freeze
DISCOUNT_ON_LOW_INCOME_TAX =
{
  year2021: {
    discount_percentage: 0.4525,
    threshold_single_person_household: 1722,
    lump_sum_single_person_household: 779,
    threshold_married_couple_household: 2849,
    lump_sum_married_couple_household: 1289
  }
}.freeze
REVENUES_STANDARD_ALLOWANCE =
0.1
REAL_REGIMEN_DEDUCTIBLE_EXPENSES =
{
  fiscal_year1: %w[house_first_works_amount house_landlord_charges_amount_per_year
                   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],
  fiscal_year2: %w[house_landlord_charges_amount_per_year
                   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
SOCIAL_CONTRIBUTIONS_PERCENTAGE =
0.172
VERSION =
"1.0.1"

Instance Method Summary collapse

Instance Method Details

#apply_discount_on_low_income_tax(simulation, almost_final_income_tax, current_year) ⇒ Integer

Apply on final tax amount the discount for low incomes

Returns:

  • (Integer)

    the final tax income with the reduced income tax for low incomes (euros)



395
396
397
398
399
400
401
402
403
404
405
406
407
# File 'lib/french_tax_system.rb', line 395

def apply_discount_on_low_income_tax(simulation, almost_final_income_tax, current_year)
  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]
    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])
    (almost_final_income_tax - discount_to_apply).negative? ? 0 : almost_final_income_tax - discount_to_apply

  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]
    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])
    (almost_final_income_tax - discount_to_apply).negative? ? 0 : almost_final_income_tax - discount_to_apply

  else
    almost_final_income_tax
  end
end

#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) ⇒ Float

Apply fiscal part capping

Returns:

  • (Float)

    the previsional income tax with fiscal part capping effect if necessary (euros)



380
381
382
383
384
385
# File 'lib/french_tax_system.rb', line 380

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)
  not_capped_income_tax = aggregated_tax_amount_for_real_fiscal_parts * fiscal_nb_parts
  capped_income_tax = (aggregated_tax_amount_for_fiscal_parts_capping * fiscal_nb_parts_for_capping) - capping_due_to_fiscal_parts

  [not_capped_income_tax, capped_income_tax].max
end

#calc_aggregated_tax_amount(family_quotient_amount, current_year) ⇒ Integer

Calculate the aggregated tax amount

Returns:

  • (Integer)

    the aggregated tax amount (euros)



357
358
359
360
361
362
363
364
365
366
367
368
369
# File 'lib/french_tax_system.rb', line 357

def calc_aggregated_tax_amount(family_quotient_amount, current_year)
  income_taxes_scale = INCOME_TAXES_SCALE["year#{current_year}".to_sym]

  income_taxes_scale.map do |scale|
    if family_quotient_amount < scale[:family_quotient_amount][:start_scale]
      0
    elsif family_quotient_amount >= scale[:family_quotient_amount][:start_scale] && family_quotient_amount < scale[:family_quotient_amount][:end_scale]
      (family_quotient_amount - scale[:family_quotient_amount][:start_scale]) * scale[:tax]
    elsif family_quotient_amount >= scale[:family_quotient_amount][:end_scale]
      (scale[:family_quotient_amount][:end_scale] - scale[:family_quotient_amount][:start_scale]) * scale[:tax]
    end
  end.sum
end

#calc_capping_due_to_fiscal_parts(simulation, fiscal_nb_parts, current_year) ⇒ Integer

Calculate the capping income tax deduction from fiscal parts

Returns:

  • (Integer)

    the capping income tax deduction from fiscal parts (euros)



323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
# File 'lib/french_tax_system.rb', line 323

def calc_capping_due_to_fiscal_parts(simulation, fiscal_nb_parts, current_year)
  case simulation[:fiscal_marital_status]
  when "Marié / Pacsé"
    (fiscal_nb_parts - FISCAL_NB_PARTS_FOR_MARRIED_COUPLE) * FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:married_couple_household][:half_part] * 2
  when "Célibataire"
    # 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
    # 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
    if simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 0
      0

    elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 1
      marked_up_half_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part]
      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
      marked_up_half_part + next_parts

    elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] >= 2
      marked_up_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part] * 2
      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
      marked_up_part + next_parts

    elsif simulation[:fiscal_nb_dependent_children] >= 1
      marked_up_part = FAMILY_QUOTIENT_CAPPING_AMOUNT["year#{current_year}".to_sym][:single_person_household][:marked_up_half_part] * 2
      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
      marked_up_part + next_parts
    end
  end
end

#calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts) ⇒ Integer

Calculate the family quotient amount

Returns:

  • (Integer)

    the family quotient amount (euros)



310
311
312
# File 'lib/french_tax_system.rb', line 310

def calc_family_quotient_amount(global_net_taxable_income_amount, fiscal_nb_parts)
  global_net_taxable_income_amount / fiscal_nb_parts
end

#calc_fiscal_nb_parts(simulation) ⇒ Integer

Calculate the household’s number of fiscal parts

Returns:

  • (Integer)

    the number of fiscal parts (nb)



237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
# File 'lib/french_tax_system.rb', line 237

def calc_fiscal_nb_parts(simulation)
  case simulation[:fiscal_marital_status]
  when "Marié / Pacsé"
    FISCAL_NB_PARTS_FOR_MARRIED_COUPLE + calc_fiscal_nb_parts_incurred_from_children(simulation)
  when "Célibataire"
    # 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
    if simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 0
      FISCAL_NB_PARTS_FOR_SINGLE_PERSON

    elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] == 1
      FISCAL_NB_PARTS_FOR_SINGLE_PERSON + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)

    elsif simulation[:fiscal_nb_dependent_children] == 0 && simulation[:fiscal_nb_alternate_custody_children] >= 2
      FISCAL_NB_PARTS_FOR_SINGLE_PERSON + 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)

    elsif simulation[:fiscal_nb_dependent_children] >= 1
      FISCAL_NB_PARTS_FOR_SINGLE_PERSON + FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + calc_fiscal_nb_parts_incurred_from_children(simulation)
    end
  end
end

#calc_fiscal_nb_parts_incurred_from_children(simulation) ⇒ Integer

Calculate the number of fiscal parts incurred from children

Returns:

  • (Integer)

    the number of fiscal parts incurred from children (nb)



265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/french_tax_system.rb', line 265

def calc_fiscal_nb_parts_incurred_from_children(simulation)
  total_nb_children = simulation[:fiscal_nb_dependent_children] + simulation[:fiscal_nb_alternate_custody_children]

  if total_nb_children <= 2
    FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN * simulation[:fiscal_nb_dependent_children] + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * simulation[:fiscal_nb_alternate_custody_children]

  elsif total_nb_children >= 3
    # Above 3 children, fiscal nb parts for added children get multiplied by 2
    if simulation[:fiscal_nb_dependent_children] == 0
      first_two_children = 2 * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
      next_children = (simulation[:fiscal_nb_alternate_custody_children] - 2) * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
      first_two_children + next_children

    elsif simulation[:fiscal_nb_dependent_children] == 1
      if simulation[:fiscal_nb_alternate_custody_children] == 2
        first_two_children = FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
        next_children = FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
        first_two_children + next_children

      elsif simulation[:fiscal_nb_alternate_custody_children] >= 3
        first_two_children = FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN + FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN
        next_children = (simulation[:fiscal_nb_alternate_custody_children] - 1) * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
        first_two_children + next_children
      end

    elsif simulation[:fiscal_nb_dependent_children] == 2
      first_two_children = 2 * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN
      next_children = simulation[:fiscal_nb_alternate_custody_children] * FISCAL_NB_PARTS_FOR_ALTERNATE_CUSTODY_CHILDREN * 2
      first_two_children + next_children

    elsif simulation[:fiscal_nb_dependent_children] >= 3
      first_two_children = 2 * FISCAL_NB_PARTS_FOR_DEPENDENT_CHILDREN
      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
      first_two_children + next_children

    end
  end
end

#calc_global_net_taxable_amount(simulation, net_taxable_property_income_amount) ⇒ Float

Calculate the global net taxable amount with or without the generated income from the property investment

Returns:

  • (Float)

    the global net taxable amount (euros)



225
226
227
# File 'lib/french_tax_system.rb', line 225

def calc_global_net_taxable_amount(simulation, net_taxable_property_income_amount)
  ((simulation[:fiscal_revenues_p1] + (simulation[:fiscal_revenues_p2].nil? ? 0 : simulation[:fiscal_revenues_p2])) * (1 - REVENUES_STANDARD_ALLOWANCE)) + net_taxable_property_income_amount
end

#calc_income_tax_amount_for_year(simulation, calculation_method, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year) ⇒ Hash

Calculate the income tax to pay with or without the generated income from the property investment

Returns:

  • (Hash)

    a hash made of the final income tax to pay (euros) and other values for the fiscal year inputed



151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/french_tax_system.rb', line 151

def calc_income_tax_amount_for_year(simulation, calculation_method, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
  # Calculate net taxable property income and global net taxable income
  case calculation_method
  when "with_property_income"
    net_taxable_property_income_amount = calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
    global_net_taxable_income_amount = calc_global_net_taxable_amount(simulation,
                                                                      net_taxable_property_income_amount[:net_taxable_property_income_amount])
  when "without_property_income"
    global_net_taxable_income_amount = calc_global_net_taxable_amount(simulation,
                                                                      0)
  else
    raise ArgumentError, "Not a valid argument, it should be 'with_property_income' or 'without_property_income'"
  end

  # Calculate the number of fiscal parts
  fiscal_nb_parts = calc_fiscal_nb_parts(simulation)
  fiscal_nb_parts_for_capping = simulation[:fiscal_marital_status] == "Célibataire" ? FISCAL_NB_PARTS_FOR_SINGLE_PERSON : FISCAL_NB_PARTS_FOR_MARRIED_COUPLE

  # Calculate the family quotient amount
  family_quotient_amount_for_real_fiscal_parts = calc_family_quotient_amount(global_net_taxable_income_amount,
                                                                             fiscal_nb_parts)
  family_quotient_amount_for_fiscal_parts_capping = calc_family_quotient_amount(global_net_taxable_income_amount,
                                                                                fiscal_nb_parts_for_capping)

  # Calculcate the aggregated tax amount
  current_year = Date.today.year
  aggregated_tax_amount_for_real_fiscal_parts = calc_aggregated_tax_amount(family_quotient_amount_for_real_fiscal_parts, current_year)
  aggregated_tax_amount_for_fiscal_parts_capping = calc_aggregated_tax_amount(family_quotient_amount_for_fiscal_parts_capping, current_year)

  # Apply fiscal part capping if necessary
  capping_due_to_fiscal_parts = calc_capping_due_to_fiscal_parts(simulation, fiscal_nb_parts, current_year)

  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)

  # Apply discount on low income tax if necessary
  final_income_tax = apply_discount_on_low_income_tax(simulation, almost_final_income_tax, current_year)

  # If the income tax is less than 61 euros, it is not collected
  final_income_tax = final_income_tax <= 61 ? 0 : final_income_tax

  # Return a hash of values
  {
    income_tax_amount: final_income_tax,
    average_tax_rate: final_income_tax / global_net_taxable_income_amount,
    global_net_taxable_income_amount: global_net_taxable_income_amount,
    net_taxable_property_income_amount: calculation_method == "with_property_income" ? net_taxable_property_income_amount[:net_taxable_property_income_amount] : 0,
    negative_taxable_property_income?: calculation_method == "with_property_income" ? net_taxable_property_income_amount[:negative_taxable_property_income?] : false,
    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,
    discount_on_low_income_tax_amount: (almost_final_income_tax - final_income_tax).positive? ? almost_final_income_tax - final_income_tax : 0,
    fiscal_nb_parts: fiscal_nb_parts
  }
end

#calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year) ⇒ Object



409
410
411
412
413
414
415
416
417
418
419
420
# File 'lib/french_tax_system.rb', line 409

def calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
  case simulation[:fiscal_status]
  when "Vide"
    NueFormulas.calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
  when "LMNP"
    LmnpFormulas.calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
  when nil
    raise ArgumentError, "fiscal_status can't be nil"
  else
    raise ArgumentError, "Not a valid fiscal status, it should be one available"
  end
end

#calc_social_contributions_amount_for_year(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
# File 'lib/french_tax_system.rb', line 204

def calc_social_contributions_amount_for_year(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)
  # Is only triggered from main formula so it assumes that this is a "with_property_income" case
  # Calculate net taxable property income that will be reported to French taxes
  net_taxable_property_income_amount = calc_net_taxable_property_income_amount(simulation, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)

  # Return the social contributions to pay in addition to income taxes (it really never ends...)
  if net_taxable_property_income_amount[:net_taxable_property_income_amount] <= 0
    0
  else
    net_taxable_property_income_amount[:net_taxable_property_income_amount] * SOCIAL_CONTRIBUTIONS_PERCENTAGE
  end
end

#calc_taxes_amount_per_year(simulation, calculation_method, investment_top_fiscal_year) ⇒ Object

Calculate the income tax to pay over the years (from first to investment_top_fscail year) with or without the generated income from the property investment



90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/french_tax_system.rb', line 90

def calc_taxes_amount_per_year(simulation, calculation_method, investment_top_fiscal_year)
  # Iterate over investment first to top fiscal year and return an array which concatenates all hashes generated per fiscal year
  income_tax_array = []
  (1..investment_top_fiscal_year).map.with_index do |investment_fiscal_year, index|
    ## Set postponed neg tax p income to 0 for the first year and to previous year result for other years
    if investment_fiscal_year == 1
      postponed_negative_taxable_property_income_from_previous_fiscal_year = 0
    elsif investment_fiscal_year >= 2
      postponed_negative_taxable_property_income_from_previous_fiscal_year = income_tax_array[index - 1][:income_tax][:negative_taxable_property_income_amount_to_postpone]
    end

    ## Calculate income tax params for this fiscal_year
    income_tax_params = calc_income_tax_amount_for_year(simulation, calculation_method, postponed_negative_taxable_property_income_from_previous_fiscal_year, investment_fiscal_year)

    ## Calculate social contributions for this fiscal year if property
    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

    ## Fill array with a nice big chunky hash
    income_tax_array << {
      income_tax: income_tax_params,
      social_contributions_amount: social_contributions_amount
    }
  end
  income_tax_array
end