loan_creator 0.10.0 → 0.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 838d0d280c585125b013b8ea6d7e8b58e94c2234fffe3d49d1c97f39a67b254f
4
- data.tar.gz: 7a607035130b53af6678bdec2a91b9d99fcace0a1710db1ef83333a4b15133b0
3
+ metadata.gz: 3f7d2dcd74aa8a01bb25577eaf1c6ea05daba9eef071d76119d7ad00773fe0f7
4
+ data.tar.gz: ed40fe1ff5610cbef9bc5c5c92f31095a0eaa6ad745a8f194861e65d5b8e316b
5
5
  SHA512:
6
- metadata.gz: 06c8e0dfa135c8a80f9969f60db83a8bc3630161fea4f208dff6480ab9142635d196aaa729fdb32fd89e38e87804cbc76fcc1f10ec464c680c10219ef7aec23c
7
- data.tar.gz: c710e04d68debd3d7f63569586aed46ac1426aabf9ed995fa762a18745916d1f31339f93b173159ddd61dcf23b145527471dbc7ff873a1bdfc46169e310f4322
6
+ metadata.gz: 16256ac04047a1b505e37dddec9299d5409d1940dfe2d7271ebc0cc5207bfb5301f3375fbf3f793cb5a70dbacab3e5a7c3c6bd87d07c6449faeba2dd73368f71
7
+ data.tar.gz: 4fe77879f1dfa61adecaf5d30dcf2dd5d6c501c1d82b75232cd106ffa827c440d52326a37c25bc939f445010ab7c4d256527e92edef2124d3c6ed8369337c0eb
@@ -24,7 +24,7 @@ module LoanCreator
24
24
  def compute_last_term(timetable)
25
25
  @crd_end_of_period = bigd('0')
26
26
  @due_interests_beginning_of_period = @due_interests_end_of_period
27
- @period_interests = @due_interests_end_of_period + compute_capitalized_interests(@due_on, timetable)
27
+ @period_interests = @due_interests_end_of_period + compute_capitalized_interests(timetable)
28
28
  @due_interests_end_of_period = 0
29
29
  @period_capital = @crd_beginning_of_period
30
30
  @total_paid_capital_end_of_period += @period_capital
@@ -32,14 +32,22 @@ module LoanCreator
32
32
  @period_amount_to_pay = @period_capital + @period_interests
33
33
  end
34
34
 
35
- def compute_capitalized_interests(due_date, timetable)
36
- computed_periodic_interests_rate = periodic_interests_rate(due_date, relative_to_date: timetable_term_dates[timetable.next_index - 1])
37
- (amount + @due_interests_beginning_of_period).mult(computed_periodic_interests_rate, BIG_DECIMAL_DIGITS)
35
+ def compute_capitalized_interests(timetable)
36
+ if multi_part_interests_calculation && term_dates? && (timetable_term_dates[timetable.current_index] + 1.year) < @due_on
37
+ multi_part_interests(
38
+ timetable_term_dates[timetable.current_index],
39
+ @due_on,
40
+ annual_interests_rate,
41
+ amount_to_capitalize
42
+ )
43
+ else
44
+ compute_period_generated_interests(periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on))
45
+ end
38
46
  end
39
47
 
40
48
  def compute_term(timetable)
41
49
  @due_interests_beginning_of_period = @due_interests_end_of_period
42
- @due_interests_end_of_period += compute_capitalized_interests(@due_on, timetable)
50
+ @due_interests_end_of_period += compute_capitalized_interests(timetable)
43
51
  end
44
52
  end
45
53
  end
@@ -1,6 +1,7 @@
1
1
  module LoanCreator
2
2
  class Common
3
3
  extend BorrowerTimetable
4
+ include TimeHelper
4
5
 
5
6
  PERIODS_IN_MONTHS = {
6
7
  month: 1,
@@ -30,9 +31,11 @@ module LoanCreator
30
31
  deferred_in_periods: 0,
31
32
  interests_start_date: nil,
32
33
  initial_values: {},
33
- realistic_durations: false
34
+ realistic_durations: false,
35
+ multi_part_interests_calculation: true
34
36
  }.freeze
35
37
 
38
+
36
39
  attr_reader *REQUIRED_ATTRIBUTES
37
40
  attr_reader *OPTIONAL_ATTRIBUTES.keys
38
41
 
@@ -47,9 +50,9 @@ module LoanCreator
47
50
  prepare_custom_term_dates if term_dates?
48
51
  end
49
52
 
50
- def periodic_interests_rate(date = nil, relative_to_date: nil)
53
+ def periodic_interests_rate(start_date, end_date)
51
54
  if realistic_durations?
52
- compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date: relative_to_date).div(100, BIG_DECIMAL_DIGITS)
55
+ compute_realistic_periodic_interests_rate(start_date, end_date, annual_interests_rate)
53
56
  else
54
57
  @periodic_interests_rate ||=
55
58
  annual_interests_rate.div(12 / PERIODS_IN_MONTHS[period], BIG_DECIMAL_DIGITS).div(100, BIG_DECIMAL_DIGITS)
@@ -101,7 +104,7 @@ module LoanCreator
101
104
 
102
105
  def set_attributes
103
106
  required_attributes.each { |k| instance_variable_set(:"@#{k}", @options.fetch(k)) }
104
- OPTIONAL_ATTRIBUTES.each { |k,v| instance_variable_set(:"@#{k}", @options.fetch(k, v)) }
107
+ OPTIONAL_ATTRIBUTES.each { |k, v| instance_variable_set(:"@#{k}", @options.fetch(k, v)) }
105
108
  end
106
109
 
107
110
  def validate(key, &block)
@@ -236,43 +239,6 @@ module LoanCreator
236
239
  (interests_start_date && interests_start_date < term_zero_date) && !term_dates?
237
240
  end
238
241
 
239
- def leap_days_count(date, relative_to_date:)
240
- start_year = relative_to_date.year
241
- end_year = date.year
242
-
243
- (start_year..end_year).sum do |year|
244
- next 0 unless Date.gregorian_leap?(year)
245
-
246
- start_date =
247
- if start_year == year
248
- relative_to_date
249
- else
250
- Date.new(year - 1, 12, 31)
251
- end
252
-
253
- end_date =
254
- if end_year == year
255
- date
256
- else
257
- Date.new(year, 12, 31)
258
- end
259
-
260
- end_date - start_date
261
- end
262
- end
263
-
264
- def compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date:)
265
- total_days = date - relative_to_date
266
- leap_days = bigd(leap_days_count(date, relative_to_date: relative_to_date))
267
- non_leap_days = bigd(total_days - leap_days)
268
-
269
- annual_interests_rate.mult(
270
- leap_days.div(366, BIG_DECIMAL_DIGITS) +
271
- non_leap_days.div(365, BIG_DECIMAL_DIGITS),
272
- BIG_DECIMAL_DIGITS
273
- )
274
- end
275
-
276
242
  def realistic_durations?
277
243
  term_dates? || @realistic_durations.present?
278
244
  end
@@ -301,7 +267,11 @@ module LoanCreator
301
267
  end
302
268
 
303
269
  def compute_period_generated_interests(interests_rate)
304
- (@crd_beginning_of_period + @due_interests_beginning_of_period).mult(interests_rate, BIG_DECIMAL_DIGITS)
270
+ amount_to_capitalize.mult(interests_rate, BIG_DECIMAL_DIGITS)
271
+ end
272
+
273
+ def amount_to_capitalize
274
+ @crd_beginning_of_period + @due_interests_beginning_of_period
305
275
  end
306
276
  end
307
277
  end
@@ -23,12 +23,25 @@ module LoanCreator
23
23
  @last_period = last_period?(idx)
24
24
  @deferred_period = @index <= deferred_in_periods
25
25
  @due_on = timetable_term_dates[timetable.next_index]
26
- computed_periodic_interests_rate = periodic_interests_rate(@due_on, relative_to_date: timetable_term_dates[timetable.next_index - 1])
27
26
 
28
27
  # Reminder: CRD beginning of period = CRD end of period **of previous period**
29
28
  @crd_beginning_of_period = @crd_end_of_period
30
29
  @due_interests_beginning_of_period = @due_interests_end_of_period
31
- @period_theoric_interests = period_theoric_interests(computed_periodic_interests_rate)
30
+
31
+ @period_theoric_interests = (
32
+ # if period is more than a year
33
+ if multi_part_interests_calculation && term_dates? && (timetable_term_dates[timetable.current_index] + 1.year) < @due_on
34
+ multi_part_interests(
35
+ timetable_term_dates[timetable.current_index],
36
+ @due_on,
37
+ annual_interests_rate,
38
+ amount_to_capitalize
39
+ )
40
+ else
41
+ period_theoric_interests(periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on))
42
+ end
43
+ )
44
+
32
45
  @delta_interests = @period_theoric_interests - @period_theoric_interests.round(2)
33
46
  @accrued_delta_interests += @delta_interests
34
47
  @amount_to_add = bigd(
@@ -24,7 +24,7 @@ module LoanCreator
24
24
  @last_period = last_period?(idx)
25
25
  @deferred_period = @index <= deferred_in_periods
26
26
  @due_on = timetable_term_dates[timetable.next_index]
27
- computed_periodic_interests_rate = periodic_interests_rate(@due_on, relative_to_date: timetable_term_dates[timetable.next_index - 1])
27
+ computed_periodic_interests_rate = periodic_interests_rate(timetable_term_dates[timetable.current_index], @due_on)
28
28
 
29
29
  @crd_beginning_of_period = @crd_end_of_period
30
30
  @period_theoric_interests = period_theoric_interests(@index, computed_periodic_interests_rate)
@@ -0,0 +1,62 @@
1
+ module LoanCreator
2
+ module TimeHelper
3
+ extend ActiveSupport::Concern
4
+
5
+ included do
6
+ def leap_days_count(start_date, end_date)
7
+ start_year = start_date.year
8
+ # mostly no op but allows to skip one iteration if end date is january 1st
9
+ end_year = (end_date - 1.day).year
10
+
11
+ (start_year..end_year).sum do |year|
12
+ next 0 unless Date.gregorian_leap?(year)
13
+
14
+ current_start_date =
15
+ if start_year == year
16
+ start_date
17
+ else
18
+ Date.new(year, 1, 1)
19
+ end
20
+
21
+ current_end_date =
22
+ if end_year == year
23
+ end_date
24
+ else
25
+ Date.new(year + 1, 1, 1)
26
+ end
27
+
28
+ current_end_date - current_start_date
29
+ end
30
+ end
31
+
32
+ def compute_realistic_periodic_interests_rate(start_date, end_date, annual_interests_rate)
33
+ total_days = end_date - start_date
34
+ leap_days = bigd(leap_days_count(start_date, end_date))
35
+ non_leap_days = bigd(total_days - leap_days)
36
+
37
+ annual_interests_rate.mult(
38
+ leap_days.div(366, BIG_DECIMAL_DIGITS) +
39
+ non_leap_days.div(365, BIG_DECIMAL_DIGITS),
40
+ BIG_DECIMAL_DIGITS
41
+ ).div(100, BIG_DECIMAL_DIGITS)
42
+ end
43
+
44
+ # for terms spanning more than a year,
45
+ # we capitalize each years until the last one which behaves normally
46
+ def multi_part_interests(start_date, end_date, annual_interests_rate, amount_to_capitalize)
47
+ duration_in_days = end_date - start_date
48
+ leap_days = bigd(leap_days_count(start_date, end_date))
49
+ non_leap_days = bigd(duration_in_days - leap_days)
50
+
51
+ ratio = non_leap_days.div(365, BIG_DECIMAL_DIGITS) + leap_days.div(366, BIG_DECIMAL_DIGITS)
52
+ full_years, year_part = ratio.divmod(1)
53
+ rate = annual_interests_rate.div(100, BIG_DECIMAL_DIGITS)
54
+
55
+ total = amount_to_capitalize.mult((1 + rate)**full_years, BIG_DECIMAL_DIGITS)
56
+ .mult(1 + rate * year_part, BIG_DECIMAL_DIGITS)
57
+
58
+ total - amount_to_capitalize
59
+ end
60
+ end
61
+ end
62
+ end
@@ -40,5 +40,9 @@ module LoanCreator
40
40
  def next_index
41
41
  @current_index.nil? ? @starting_index : @current_index + 1
42
42
  end
43
+
44
+ def current_index
45
+ @current_index.nil? ? @starting_index - 1 : @current_index
46
+ end
43
47
  end
44
48
  end
@@ -33,7 +33,7 @@ module LoanCreator
33
33
  end
34
34
 
35
35
  def compute_interests(due_date, timetable)
36
- computed_periodic_interests_rate = periodic_interests_rate(due_date, relative_to_date: timetable_term_dates[timetable.next_index - 1])
36
+ computed_periodic_interests_rate = periodic_interests_rate(timetable_term_dates[timetable.current_index], due_date)
37
37
  amount.mult(bigd(computed_periodic_interests_rate), BIG_DECIMAL_DIGITS)
38
38
  end
39
39
 
@@ -1,3 +1,3 @@
1
1
  module LoanCreator
2
- VERSION = '0.10.0'.freeze
2
+ VERSION = '0.11.0'.freeze
3
3
  end
data/lib/loan_creator.rb CHANGED
@@ -8,6 +8,7 @@ module LoanCreator
8
8
  BIG_DECIMAL_DIGITS = 14
9
9
 
10
10
  autoload :ExcelFormulas, 'loan_creator/excel_formulas'
11
+ autoload :TimeHelper, 'loan_creator/time_helper'
11
12
  autoload :BorrowerTimetable, 'loan_creator/borrower_timetable'
12
13
  autoload :Common, 'loan_creator/common'
13
14
  autoload :Standard, 'loan_creator/standard'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: loan_creator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - thibaulth
@@ -12,7 +12,7 @@ authors:
12
12
  autorequire:
13
13
  bindir: exe
14
14
  cert_chain: []
15
- date: 2021-05-26 00:00:00.000000000 Z
15
+ date: 2021-11-03 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bundler
@@ -178,6 +178,7 @@ files:
178
178
  - lib/loan_creator/standard.rb
179
179
  - lib/loan_creator/term.rb
180
180
  - lib/loan_creator/term_dates_validator.rb
181
+ - lib/loan_creator/time_helper.rb
181
182
  - lib/loan_creator/timetable.rb
182
183
  - lib/loan_creator/uncapitalized_bullet.rb
183
184
  - lib/loan_creator/version.rb