loan_creator 0.6.3.1 → 0.8.2

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: c906065bac296df07426a567f4798d7202fa88a8bdc5015f9424bf86cd808f28
4
- data.tar.gz: a4303b1997d07d8a064b2c6b8406760d6b506e21cdb9048a9a0d774fc69fca09
3
+ metadata.gz: f175586a927e2dd609903b79ed629c1ab5bc3aecde02b1cc5809629d33c5b852
4
+ data.tar.gz: fd695b7fba80c64efa7b5c0cefc9a1092ed6c74aa4ac070da3cb25771740e3bf
5
5
  SHA512:
6
- metadata.gz: a15bd8c5abb50eff1b6fe4313ca945ab5f9928b8cd7b93cd7b2649b37095b71dfb00fea7058883019bf951ecc9034b15b66c44bb7366c78dd78d52758927014a
7
- data.tar.gz: d4fe3bf469606cd8f0287a673ff09e7ba7f6b29177adb3dc3cca7cdd2680aa092adfb35ba4daa34300c9c7c2fa7779a4c79ca968e197158533cc0d1c85453159
6
+ metadata.gz: 6de1a02fc201dd985c0c255ee7b96989d477723607015647f9608228174f9bfdd4aa7f667ea9d74425a251b4912dae0430e1edd84623b7a8c1953e28ce1797f9
7
+ data.tar.gz: b3b3e8d4a1d007b1f8481238df1d021b5f597d2a46b887495f4099320dcd20711ccc2fb9e2ed2fa34775a99c33826c9de2d4a5b7ede12d7eb8e978d45df017f1
data/CHANGELOG.md CHANGED
@@ -1,3 +1,40 @@
1
+ v0.8.2
2
+ -------------------------
3
+
4
+ - manage initial value `due_interests` for Linear and InFine loan types
5
+
6
+ v0.8.1
7
+ -------------------------
8
+ - add `realistic_durations` option
9
+
10
+ v0.8.0
11
+ -------------------------
12
+
13
+ #### Feature
14
+ - due interests to date are now store into `due_interests_` columns for `LoanCreator::UncapitalizedBullet`
15
+
16
+ #### Bugfix
17
+ - fix `initial_values` not taking `paid_capital` and `paid_interests` into account for `LoanCreator::UncapitalizedBullet`
18
+ and `LoanCreator::Bullet`
19
+
20
+ #### Breaking changes
21
+ - rename columns `capitalized_interests_beginning_of_period` and `capitalized_interests_end_of_period` for
22
+ `due_interests_beginning_of_period` and `due_interests_end_of_period`
23
+ - rename `capitalized_interests` option in `:inital_values` into `due_interests`
24
+
25
+
26
+ v0.7.1
27
+ -------------------------
28
+
29
+ - fix `index` for `borrower_timetable` method that did not take into account starting index of `lender_timetables`
30
+
31
+ v0.7.0
32
+ -------------------------
33
+
34
+ - change `capitalized_interests` for `capitalized_interests_beginning_of_period`
35
+ and `capitalized_interests_end_of_period` in `LoanCreator::Term`
36
+ - add `capitalized_interests` in `:inital_values` for `LoanCreator::Bullet` loans
37
+
1
38
  v0.6.2
2
39
  -------------------------
3
40
 
data/README.md CHANGED
@@ -44,9 +44,10 @@ Each instance of one of the previous classes has the following attributes:
44
44
  :annual_interests_rate
45
45
  :starts_at
46
46
  :duration_in_periods
47
- :deferred_in_periods (default to zero)
47
+ :deferred_in_periods (defaults to zero)
48
48
  :interests_start_date (optional)
49
49
  :initial_values (to generate a timetable from a previous term or at a given state)
50
+ :realistic_durations (optional, defaults to false)
50
51
  ```
51
52
 
52
53
  Initial values must be a hash with specific keys, like so:
@@ -57,7 +58,7 @@ Initial values must be a hash with specific keys, like so:
57
58
  paid_interests: 11000.0,
58
59
  accrued_delta_interests: 0,
59
60
  starting_index: 2,
60
- capitalized_interests: 0
61
+ due_interests: 0
61
62
  }
62
63
  ```
63
64
 
@@ -102,6 +103,9 @@ There is also a `LoanCreator::Timetable` class dedicated to record the data of t
102
103
 
103
104
  # Amount to pay this term
104
105
  :period_amount_to_pay
106
+
107
+ # Whether or not to use the real number of days in each month
108
+ :realistic_durations
105
109
  ```
106
110
 
107
111
  `#periodic_interests_rate` renders a precise calculation of the loan's periodic interests rate based on two inputs: `#annual_interests_rate` and `#period`.
@@ -148,9 +152,11 @@ Capital share shall be repaid in full at loan's end.
148
152
  `Bullet` loan generates terms where terms' payments are zero. \
149
153
  Interests are capitalized, i.e. added to the borrowed capital on each term.\
150
154
  Capital share shall be repaid in full and all interests paid at loan's end.
155
+ N.b.: Due capitalized interests to date are stored into terms under `due_intesrests_` columns
151
156
 
152
157
  `UncapitalizedBullet` same as bullet, the only difference is the interests\
153
158
  are NOT capitalized.
159
+ N.b.: Due interests to date are stored into terms under `due_intesrests_` columns
154
160
 
155
161
  There is no deferred time for `InFine` and `Bullet` loans as it would be equivalent to increasing loan's duration.
156
162
 
@@ -174,6 +180,9 @@ additional term with only interests for the time difference.
174
180
  For example, with a `start_at` in january 2020 and a `interests_start_date` in october 2019, the timetable will include a
175
181
  first term corresponding to 3 months of interests.
176
182
 
183
+ `realistic_durations`: Optional. A boolean tha specifies whether or not to use the real number of days in each month when calculating the periodic interests rate. Note that leap years are taken into account. Default: `false`.
184
+ The default behaviour is to use the `period` in relation to the number of months in a year (ie: 12)
185
+
177
186
  ## Calculation
178
187
 
179
188
  An excel simulator for standard case can be found [here](CapSens_Loan.xlsx).
@@ -7,6 +7,8 @@ module LoanCreator
7
7
  :period_capital,
8
8
  :total_paid_capital_end_of_period,
9
9
  :total_paid_interests_end_of_period,
10
+ :due_interests_beginning_of_period,
11
+ :due_interests_end_of_period,
10
12
  :period_amount_to_pay
11
13
  ].freeze
12
14
 
@@ -18,8 +20,8 @@ module LoanCreator
18
20
  end
19
21
 
20
22
  borrower_timetable = LoanCreator::Timetable.new(
21
- starts_on: lenders_timetables.first.starts_on,
22
- period: lenders_timetables.first.period
23
+ starting_index: lenders_timetables.first.starting_index,
24
+ loan: lenders_timetables.first.loan
23
25
  )
24
26
 
25
27
  # Borrower timetable is not concerned with computation-related value (delta, etc.),
@@ -7,31 +7,39 @@ module LoanCreator
7
7
  reset_current_term
8
8
  @crd_beginning_of_period = amount
9
9
  @crd_end_of_period = amount
10
- (duration_in_periods - 1).times { |period| compute_term(timetable, period + 1) }
11
- compute_last_term
12
- timetable << current_term
10
+
11
+ duration_in_periods.times { |idx| timetable << compute_current_term(idx, timetable) }
12
+
13
13
  timetable
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def compute_last_term
18
+ def compute_current_term(idx, timetable)
19
+ @due_on = timetable_term_dates[timetable.next_index]
20
+ last_period?(idx) ? compute_last_term(timetable) : compute_term(timetable)
21
+ current_term
22
+ end
23
+
24
+ def compute_last_term(timetable)
19
25
  @crd_end_of_period = bigd('0')
20
- @period_interests = compute_capitalized_interests(duration_in_periods)
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)
28
+ @due_interests_end_of_period = 0
21
29
  @period_capital = @crd_beginning_of_period
22
- @total_paid_capital_end_of_period = @period_capital
23
- @total_paid_interests_end_of_period = @period_interests
30
+ @total_paid_capital_end_of_period += @period_capital
31
+ @total_paid_interests_end_of_period += @period_interests
24
32
  @period_amount_to_pay = @period_capital + @period_interests
25
- @capitalized_interests = compute_capitalized_interests(duration_in_periods)
26
33
  end
27
34
 
28
- def compute_capitalized_interests(period)
29
- amount.mult((bigd(1) + periodic_interests_rate) ** period, BIG_DECIMAL_DIGITS) - amount
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)
30
38
  end
31
39
 
32
- def compute_term(timetable, period)
33
- @capitalized_interests = compute_capitalized_interests(period)
34
- timetable << current_term
40
+ def compute_term(timetable)
41
+ @due_interests_beginning_of_period = @due_interests_end_of_period
42
+ @due_interests_end_of_period += compute_capitalized_interests(@due_on, timetable)
35
43
  end
36
44
  end
37
45
  end
@@ -21,7 +21,8 @@ module LoanCreator
21
21
  # attribute: default_value
22
22
  deferred_in_periods: 0,
23
23
  interests_start_date: nil,
24
- initial_values: {}
24
+ initial_values: {},
25
+ realistic_durations: false
25
26
  }.freeze
26
27
 
27
28
  attr_reader *REQUIRED_ATTRIBUTES
@@ -37,14 +38,26 @@ module LoanCreator
37
38
  validate_initial_values
38
39
  end
39
40
 
40
- def periodic_interests_rate_percentage
41
- @periodic_interests_rate_percentage ||=
42
- annual_interests_rate.div(12 / PERIODS_IN_MONTHS[period], BIG_DECIMAL_DIGITS)
41
+ def periodic_interests_rate(date = nil, relative_to_date: nil)
42
+ if realistic_durations?
43
+ compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date: relative_to_date).div(100, BIG_DECIMAL_DIGITS)
44
+ else
45
+ @periodic_interests_rate ||=
46
+ annual_interests_rate.div(12 / PERIODS_IN_MONTHS[period], BIG_DECIMAL_DIGITS).div(100, BIG_DECIMAL_DIGITS)
47
+ end
43
48
  end
44
49
 
45
- def periodic_interests_rate
46
- @periodic_interests_rate ||=
47
- periodic_interests_rate_percentage.div(100, BIG_DECIMAL_DIGITS)
50
+ def timetable_term_dates
51
+ @_timetable_term_dates ||= Hash.new do |dates, index|
52
+ dates[index] =
53
+ if index < 1
54
+ dates[index + 1].advance(months: -PERIODS_IN_MONTHS.fetch(period))
55
+ elsif index == 1
56
+ starts_on
57
+ else
58
+ starts_on.advance(months: PERIODS_IN_MONTHS.fetch(period) * (index - 1))
59
+ end
60
+ end
48
61
  end
49
62
 
50
63
  def lender_timetable
@@ -103,29 +116,32 @@ module LoanCreator
103
116
  end
104
117
 
105
118
  def set_initial_values
106
- @starting_index = initial_values[:starting_index] || 1
119
+ @starting_index = initial_values[:starting_index] || 1
120
+ @initial_due_interests = bigd(initial_values[:due_interests] || 0)
107
121
 
108
122
  return if initial_values.blank?
109
123
 
110
124
  (@total_paid_capital_end_of_period = bigd(initial_values[:paid_capital]))
111
125
  (@total_paid_interests_end_of_period = bigd(initial_values[:paid_interests]))
112
126
  (@accrued_delta_interests = bigd(initial_values[:accrued_delta_interests]))
127
+ (@due_interests_beginning_of_period = bigd(initial_values[:due_interests] || 0))
113
128
  end
114
129
 
115
130
  def reset_current_term
116
- @crd_beginning_of_period = bigd('0')
117
- @crd_end_of_period = bigd('0')
118
- @period_theoric_interests = bigd('0')
119
- @capitalized_interests = bigd('0')
120
- @delta_interests = bigd('0')
121
- @accrued_delta_interests = @accrued_delta_interests || bigd('0')
122
- @amount_to_add = bigd('0')
123
- @period_interests = bigd('0')
124
- @period_capital = bigd('0')
125
- @total_paid_capital_end_of_period = @total_paid_capital_end_of_period || bigd('0')
126
- @total_paid_interests_end_of_period = @total_paid_interests_end_of_period || bigd('0')
127
- @period_amount_to_pay = bigd('0')
128
- @due_on = nil
131
+ @accrued_delta_interests ||= bigd('0')
132
+ @total_paid_capital_end_of_period ||= bigd('0')
133
+ @total_paid_interests_end_of_period ||= bigd('0')
134
+ @due_interests_beginning_of_period ||= bigd('0')
135
+ @crd_beginning_of_period = bigd('0')
136
+ @crd_end_of_period = bigd('0')
137
+ @period_theoric_interests = bigd('0')
138
+ @due_interests_end_of_period = @due_interests_beginning_of_period
139
+ @delta_interests = bigd('0')
140
+ @amount_to_add = bigd('0')
141
+ @period_interests = bigd('0')
142
+ @period_capital = bigd('0')
143
+ @period_amount_to_pay = bigd('0')
144
+ @due_on = nil
129
145
  end
130
146
 
131
147
  def current_term
@@ -135,7 +151,8 @@ module LoanCreator
135
151
  period_theoric_interests: @period_theoric_interests,
136
152
  delta_interests: @delta_interests,
137
153
  accrued_delta_interests: @accrued_delta_interests,
138
- capitalized_interests: @capitalized_interests,
154
+ due_interests_beginning_of_period: @due_interests_beginning_of_period,
155
+ due_interests_end_of_period: @due_interests_end_of_period,
139
156
  amount_to_add: @amount_to_add,
140
157
  period_interests: @period_interests,
141
158
  period_capital: @period_capital,
@@ -149,8 +166,7 @@ module LoanCreator
149
166
 
150
167
  def new_timetable
151
168
  LoanCreator::Timetable.new(
152
- starts_on: starts_on,
153
- period: period,
169
+ loan: self,
154
170
  interests_start_date: interests_start_date,
155
171
  starting_index: @starting_index
156
172
  )
@@ -160,6 +176,10 @@ module LoanCreator
160
176
  @index ? (@starting_index + @index - 1) : nil
161
177
  end
162
178
 
179
+ def last_period?(idx)
180
+ idx == (duration_in_periods - 1)
181
+ end
182
+
163
183
  def compute_term_zero
164
184
  @crd_beginning_of_period = @crd_end_of_period
165
185
  @period_theoric_interests = term_zero_interests
@@ -169,6 +189,7 @@ module LoanCreator
169
189
  @total_paid_interests_end_of_period += @period_interests
170
190
  @period_amount_to_pay = @period_interests
171
191
  @index = 0
192
+ @due_on = timetable_term_dates[0]
172
193
  end
173
194
 
174
195
  def term_zero_interests
@@ -191,5 +212,21 @@ module LoanCreator
191
212
  def term_zero?
192
213
  interests_start_date && interests_start_date < term_zero_date
193
214
  end
215
+
216
+ def compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date:)
217
+ realistic_days = 365
218
+ realistic_days += 1 if date.leap?
219
+ realistic_days_in_period = (date - relative_to_date).to_i
220
+
221
+ annual_interests_rate.div(bigd(realistic_days) / bigd(realistic_days_in_period), BIG_DECIMAL_DIGITS)
222
+ end
223
+
224
+ def realistic_durations?
225
+ !!@realistic_durations
226
+ end
227
+
228
+ def compute_period_generated_interests(interests_rate)
229
+ (@crd_beginning_of_period + @due_interests_beginning_of_period).mult(interests_rate, BIG_DECIMAL_DIGITS)
230
+ end
194
231
  end
195
232
  end
@@ -11,26 +11,24 @@ module LoanCreator
11
11
  timetable << current_term
12
12
  end
13
13
 
14
- duration_in_periods.times do |idx|
15
- @last_period = last_period?(idx)
16
- @deferred_period = idx < deferred_in_periods
17
- compute_current_term(idx)
18
- timetable << current_term
19
- end
14
+ duration_in_periods.times { |idx| timetable << compute_current_term(idx, timetable) }
20
15
 
21
16
  timetable
22
17
  end
23
18
 
24
19
  private
25
20
 
26
- def last_period?(idx)
27
- idx == (duration_in_periods - 1)
28
- end
21
+ def compute_current_term(idx, timetable)
22
+ @index = idx + 1
23
+ @last_period = last_period?(idx)
24
+ @deferred_period = @index <= deferred_in_periods
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])
29
27
 
30
- def compute_current_term(idx)
31
28
  # Reminder: CRD beginning of period = CRD end of period **of previous period**
32
29
  @crd_beginning_of_period = @crd_end_of_period
33
- @period_theoric_interests = @crd_beginning_of_period * periodic_interests_rate
30
+ @due_interests_beginning_of_period = @due_interests_end_of_period
31
+ @period_theoric_interests = period_theoric_interests(computed_periodic_interests_rate)
34
32
  @delta_interests = @period_theoric_interests - @period_theoric_interests.round(2)
35
33
  @accrued_delta_interests += @delta_interests
36
34
  @amount_to_add = bigd(
@@ -49,8 +47,17 @@ module LoanCreator
49
47
  @total_paid_interests_end_of_period += @period_interests
50
48
  @period_amount_to_pay = @period_interests + @period_capital
51
49
  @crd_end_of_period -= @period_capital
52
- @due_on = nil
53
- @index = idx + 1
50
+ @due_interests_end_of_period -= reimbursed_due_interests
51
+
52
+ current_term
53
+ end
54
+
55
+ def period_theoric_interests(computed_periodic_interests_rate)
56
+ if @due_interests_beginning_of_period > 0
57
+ reimbursed_due_interests + compute_period_generated_interests(computed_periodic_interests_rate)
58
+ else
59
+ compute_period_generated_interests(computed_periodic_interests_rate)
60
+ end
54
61
  end
55
62
 
56
63
  def period_capital
@@ -58,9 +65,26 @@ module LoanCreator
58
65
  @crd_beginning_of_period
59
66
  elsif @deferred_period
60
67
  bigd(0)
68
+ elsif @due_interests_beginning_of_period > 0
69
+ compute_period_capital - reimbursed_due_interests
70
+ else
71
+ compute_period_capital
72
+ end
73
+ end
74
+
75
+ def reimbursed_due_interests
76
+ if @deferred_period
77
+ bigd(0)
61
78
  else
62
- (amount / (duration_in_periods - deferred_in_periods)).round(2)
79
+ [
80
+ @due_interests_beginning_of_period,
81
+ compute_period_capital
82
+ ].min
63
83
  end
64
84
  end
85
+
86
+ def compute_period_capital
87
+ ((amount + @initial_due_interests) / (duration_in_periods - deferred_in_periods)).round(2)
88
+ end
65
89
  end
66
90
  end
@@ -12,24 +12,22 @@ module LoanCreator
12
12
  timetable << current_term
13
13
  end
14
14
 
15
- duration_in_periods.times do |idx|
16
- @last_period = last_period?(idx)
17
- @deferred_period = idx < deferred_in_periods
18
- compute_current_term(idx)
19
- timetable << current_term
20
- end
15
+ duration_in_periods.times { |idx| timetable << compute_current_term(idx, timetable) }
16
+
21
17
  timetable
22
18
  end
23
19
 
24
20
  private
25
21
 
26
- def last_period?(idx)
27
- idx == (duration_in_periods - 1)
28
- end
22
+ def compute_current_term(idx, timetable)
23
+ @index = idx + 1
24
+ @last_period = last_period?(idx)
25
+ @deferred_period = @index <= deferred_in_periods
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])
29
28
 
30
- def compute_current_term(idx)
31
29
  @crd_beginning_of_period = @crd_end_of_period
32
- @period_theoric_interests = period_theoric_interests(idx)
30
+ @period_theoric_interests = period_theoric_interests(@index, computed_periodic_interests_rate)
33
31
  @delta_interests = @period_theoric_interests - @period_theoric_interests.round(2)
34
32
  @accrued_delta_interests += @delta_interests
35
33
  @amount_to_add = bigd(
@@ -43,37 +41,37 @@ module LoanCreator
43
41
  )
44
42
  @accrued_delta_interests -= @amount_to_add
45
43
  @period_interests = @period_theoric_interests.round(2) + @amount_to_add
46
- @period_capital = period_capital(idx)
44
+ @period_capital = period_capital(@index, computed_periodic_interests_rate)
47
45
  @total_paid_capital_end_of_period += @period_capital
48
46
  @total_paid_interests_end_of_period += @period_interests
49
47
  @period_amount_to_pay = @period_interests + @period_capital
50
48
  @crd_end_of_period -= @period_capital
51
- @due_on = nil
52
- @index = idx + 1
49
+
50
+ current_term
53
51
  end
54
52
 
55
- def period_theoric_interests(idx)
53
+ def period_theoric_interests(idx, computed_periodic_interests_rate)
56
54
  if @deferred_period
57
- @crd_beginning_of_period * periodic_interests_rate
55
+ @crd_beginning_of_period * computed_periodic_interests_rate
58
56
  else
59
57
  -ipmt(
60
- periodic_interests_rate,
61
- (idx + 1) - deferred_in_periods,
58
+ computed_periodic_interests_rate,
59
+ idx - deferred_in_periods,
62
60
  duration_in_periods - deferred_in_periods,
63
61
  amount
64
62
  )
65
63
  end
66
64
  end
67
65
 
68
- def period_capital(idx)
66
+ def period_capital(idx, computed_periodic_interests_rate)
69
67
  if @last_period
70
68
  @crd_beginning_of_period
71
69
  elsif @deferred_period
72
70
  bigd(0)
73
71
  else
74
72
  -ppmt(
75
- periodic_interests_rate,
76
- (idx + 1) - deferred_in_periods,
73
+ computed_periodic_interests_rate,
74
+ idx - deferred_in_periods,
77
75
  duration_in_periods - deferred_in_periods,
78
76
  amount
79
77
  ).round(2)
@@ -17,8 +17,11 @@ module LoanCreator
17
17
  # Accrued interests' delta
18
18
  :accrued_delta_interests,
19
19
 
20
- # Capitalized interests (Bullet only)
21
- :capitalized_interests,
20
+ # Due interests at the beginning of the term (Bullet and UncapitalizedBullet only)
21
+ :due_interests_beginning_of_period,
22
+
23
+ # Due interests at the end of the term (Bullet and UncapitalizedBullet only)
24
+ :due_interests_end_of_period,
22
25
 
23
26
  # Adjustment of -0.01, 0 or +0.01 cent depending on accrued_delta_interests
24
27
  :amount_to_add,
@@ -1,22 +1,13 @@
1
1
  # coding: utf-8
2
2
  module LoanCreator
3
3
  class Timetable
4
- # Used to calculate next term's date (see ActiveSupport#advance)
5
- PERIODS = {
6
- month: {months: 1},
7
- quarter: {months: 3},
8
- semester: {months: 6},
9
- year: {years: 1}
10
- }
4
+ attr_reader :loan, :terms, :starting_index #, :interests_start_date
11
5
 
12
- attr_reader :terms, :starts_on, :period #, :interests_start_date
13
-
14
- def initialize(starts_on:, period:, interests_start_date: nil, starting_index: 1)
15
- raise ArgumentError.new(:period) unless PERIODS.keys.include?(period)
6
+ delegate :starts_on, :period, to: :loan
16
7
 
8
+ def initialize(loan:, interests_start_date: nil, starting_index: 1)
17
9
  @terms = []
18
- @starts_on = (starts_on.is_a?(Date) ? starts_on : Date.parse(starts_on))
19
- @period = period
10
+ @loan = loan
20
11
  @starting_index = starting_index
21
12
 
22
13
  if interests_start_date
@@ -26,8 +17,11 @@ module LoanCreator
26
17
 
27
18
  def <<(term)
28
19
  raise ArgumentError.new('LoanCreator::Term expected') unless term.is_a?(LoanCreator::Term)
29
- term.index ||= autoincrement_index
30
- term.due_on ||= date_for(term.index)
20
+
21
+ @current_index = term.index || next_index
22
+
23
+ term.index = @current_index
24
+ term.due_on ||= loan.timetable_term_dates[term.index]
31
25
  @terms << term
32
26
  self
33
27
  end
@@ -43,29 +37,8 @@ module LoanCreator
43
37
  @terms.find { |term| term.index == index }
44
38
  end
45
39
 
46
- private
47
-
48
- def autoincrement_index
49
- @current_index = (@current_index.nil? ? @starting_index : @current_index + 1)
50
- end
51
-
52
- def date_for(index)
53
- @_dates ||= Hash.new do |dates, index|
54
- dates[index] =
55
- if index < 1
56
- dates[index + 1].advance(PERIODS.fetch(period).transform_values {|n| -n})
57
- elsif index == 1
58
- starts_on
59
- else
60
- dates[index - 1].advance(PERIODS.fetch(period))
61
- end
62
- end
63
-
64
- @_dates[index]
65
- end
66
-
67
- def reset_dates
68
- @_dates = nil
40
+ def next_index
41
+ @current_index.nil? ? @starting_index : @current_index + 1
69
42
  end
70
43
  end
71
44
  end
@@ -7,28 +7,39 @@ module LoanCreator
7
7
  reset_current_term
8
8
  @crd_beginning_of_period = amount
9
9
  @crd_end_of_period = amount
10
- (duration_in_periods - 1).times { timetable << current_term }
11
- compute_last_term
12
- timetable << current_term
10
+
11
+ duration_in_periods.times { |idx| timetable << compute_current_term(idx, timetable) }
12
+
13
13
  timetable
14
14
  end
15
15
 
16
16
  private
17
17
 
18
- def compute_last_term
19
- @crd_end_of_period = bigd('0')
20
- @period_interests = total_interests
21
- @period_capital = @crd_beginning_of_period
22
- @total_paid_capital_end_of_period = @period_capital
23
- @total_paid_interests_end_of_period = @period_interests
24
- @period_amount_to_pay = @period_capital + @period_interests
18
+ def compute_current_term(idx, timetable)
19
+ @due_on = timetable_term_dates[timetable.next_index]
20
+ last_period?(idx) ? compute_last_term(timetable) : compute_term(timetable)
21
+ current_term
22
+ end
23
+
24
+ def compute_last_term(timetable)
25
+ @crd_end_of_period = bigd('0')
26
+ @due_interests_beginning_of_period = @due_interests_end_of_period
27
+ @period_interests = @due_interests_end_of_period + compute_interests(@due_on, timetable)
28
+ @due_interests_end_of_period = 0
29
+ @period_capital = @crd_beginning_of_period
30
+ @total_paid_capital_end_of_period += @period_capital
31
+ @total_paid_interests_end_of_period += @period_interests
32
+ @period_amount_to_pay = @period_capital + @period_interests
33
+ end
34
+
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])
37
+ amount.mult(bigd(computed_periodic_interests_rate), BIG_DECIMAL_DIGITS)
25
38
  end
26
39
 
27
- def total_interests
28
- amount.mult(
29
- bigd(periodic_interests_rate) * bigd(duration_in_periods),
30
- BIG_DECIMAL_DIGITS
31
- )
40
+ def compute_term(timetable)
41
+ @due_interests_beginning_of_period = @due_interests_end_of_period
42
+ @due_interests_end_of_period += compute_interests(@due_on, timetable)
32
43
  end
33
44
  end
34
45
  end
@@ -1,3 +1,3 @@
1
1
  module LoanCreator
2
- VERSION = '0.6.3.1'.freeze
2
+ VERSION = '0.8.2'.freeze
3
3
  end
data/loan_creator.gemspec CHANGED
@@ -24,6 +24,8 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency 'rspec', '~> 3.0'
25
25
  spec.add_development_dependency 'simplecov', '~> 0.16'
26
26
  spec.add_development_dependency 'byebug', '~> 11.0'
27
+ spec.add_development_dependency 'pry', '~> 0.13.1'
28
+ spec.add_development_dependency 'table_print', '~> 1.5'
27
29
 
28
30
  spec.add_runtime_dependency 'bigdecimal'
29
31
  spec.add_runtime_dependency 'activesupport'
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.6.3.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - thibaulth
@@ -9,10 +9,10 @@ authors:
9
9
  - younes.serraj
10
10
  - Antoine Becquet
11
11
  - Jerome Drevet
12
- autorequire:
12
+ autorequire:
13
13
  bindir: exe
14
14
  cert_chain: []
15
- date: 2021-01-05 00:00:00.000000000 Z
15
+ date: 2021-05-05 00:00:00.000000000 Z
16
16
  dependencies:
17
17
  - !ruby/object:Gem::Dependency
18
18
  name: bundler
@@ -84,6 +84,34 @@ dependencies:
84
84
  - - "~>"
85
85
  - !ruby/object:Gem::Version
86
86
  version: '11.0'
87
+ - !ruby/object:Gem::Dependency
88
+ name: pry
89
+ requirement: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - "~>"
92
+ - !ruby/object:Gem::Version
93
+ version: 0.13.1
94
+ type: :development
95
+ prerelease: false
96
+ version_requirements: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - "~>"
99
+ - !ruby/object:Gem::Version
100
+ version: 0.13.1
101
+ - !ruby/object:Gem::Dependency
102
+ name: table_print
103
+ requirement: !ruby/object:Gem::Requirement
104
+ requirements:
105
+ - - "~>"
106
+ - !ruby/object:Gem::Version
107
+ version: '1.5'
108
+ type: :development
109
+ prerelease: false
110
+ version_requirements: !ruby/object:Gem::Requirement
111
+ requirements:
112
+ - - "~>"
113
+ - !ruby/object:Gem::Version
114
+ version: '1.5'
87
115
  - !ruby/object:Gem::Dependency
88
116
  name: bigdecimal
89
117
  requirement: !ruby/object:Gem::Requirement
@@ -112,7 +140,7 @@ dependencies:
112
140
  - - ">="
113
141
  - !ruby/object:Gem::Version
114
142
  version: '0'
115
- description:
143
+ description:
116
144
  email:
117
145
  - thibault@capsens.eu
118
146
  - nicolas.besnard@capsens.eu
@@ -157,7 +185,7 @@ homepage: https://github.com/CapSens/loan-creator
157
185
  licenses:
158
186
  - MIT
159
187
  metadata: {}
160
- post_install_message:
188
+ post_install_message:
161
189
  rdoc_options: []
162
190
  require_paths:
163
191
  - lib
@@ -173,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
173
201
  version: '0'
174
202
  requirements: []
175
203
  rubygems_version: 3.1.2
176
- signing_key:
204
+ signing_key:
177
205
  specification_version: 4
178
206
  summary: Create and update timetables from input data
179
207
  test_files: []