class LoanCreator::Common

Constants

OPTIONAL_ATTRIBUTES
PERIODS_IN_MONTHS
REQUIRED_ATTRIBUTES
REQUIRED_ATTRIBUTES_TERM_DATES

Public Class Methods

bigd(value) click to toggle source
# File lib/loan_creator/common.rb, line 76
def self.bigd(value)
  BigDecimal(value, BIG_DECIMAL_DIGITS)
end
new(**options) click to toggle source
# File lib/loan_creator/common.rb, line 39
def initialize(**options)
  @options = options
  require_attributes
  reinterpret_attributes
  set_attributes
  validate_attributes
  set_initial_values
  validate_initial_values
  prepare_custom_term_dates if term_dates?
end

Public Instance Methods

bigd(value) click to toggle source
# File lib/loan_creator/common.rb, line 80
def bigd(value)
  self.class.bigd(value)
end
lender_timetable() click to toggle source
# File lib/loan_creator/common.rb, line 72
def lender_timetable
  raise NotImplementedError
end
periodic_interests_rate(date = nil, relative_to_date: nil) click to toggle source
# File lib/loan_creator/common.rb, line 50
def periodic_interests_rate(date = nil, relative_to_date: nil)
  if realistic_durations?
    compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date: relative_to_date).div(100, BIG_DECIMAL_DIGITS)
  else
    @periodic_interests_rate ||=
      annual_interests_rate.div(12 / PERIODS_IN_MONTHS[period], BIG_DECIMAL_DIGITS).div(100, BIG_DECIMAL_DIGITS)
  end
end
timetable_term_dates() click to toggle source
# File lib/loan_creator/common.rb, line 59
def timetable_term_dates
  @_timetable_term_dates ||= Hash.new do |dates, index|
    dates[index] =
      if index < 1
        dates[index + 1].advance(months: -PERIODS_IN_MONTHS.fetch(period))
      elsif index == 1
        starts_on
      else
        starts_on.advance(months: PERIODS_IN_MONTHS.fetch(period) * (index - 1))
      end
  end
end

Private Instance Methods

compute_index() click to toggle source
# File lib/loan_creator/common.rb, line 198
def compute_index
  @index ? (@starting_index + @index - 1) : nil
end
compute_period_generated_interests(interests_rate) click to toggle source
# File lib/loan_creator/common.rb, line 303
def compute_period_generated_interests(interests_rate)
  (@crd_beginning_of_period + @due_interests_beginning_of_period).mult(interests_rate, BIG_DECIMAL_DIGITS)
end
compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date:) click to toggle source
# File lib/loan_creator/common.rb, line 264
def compute_realistic_periodic_interests_rate_percentage_for(date, relative_to_date:)
  total_days = date - relative_to_date
  leap_days = bigd(leap_days_count(date, relative_to_date: relative_to_date))
  non_leap_days = bigd(total_days - leap_days)

  annual_interests_rate.mult(
    leap_days.div(366, BIG_DECIMAL_DIGITS) +
    non_leap_days.div(365, BIG_DECIMAL_DIGITS),
    BIG_DECIMAL_DIGITS
  )
end
compute_term_zero() click to toggle source
# File lib/loan_creator/common.rb, line 206
def compute_term_zero
  @crd_beginning_of_period              = @crd_end_of_period
  @period_theoric_interests             = term_zero_interests
  @delta_interests                      = @period_theoric_interests - @period_theoric_interests.round(2)
  @accrued_delta_interests             += @delta_interests
  @period_interests                     = @period_theoric_interests.round(2)
  @total_paid_interests_end_of_period  += @period_interests
  @period_amount_to_pay                 = @period_interests
  @index                                = 0
  @due_on                               = timetable_term_dates[0]
end
current_term() click to toggle source
# File lib/loan_creator/common.rb, line 170
def current_term
  LoanCreator::Term.new(
    crd_beginning_of_period:            @crd_beginning_of_period,
    crd_end_of_period:                  @crd_end_of_period,
    period_theoric_interests:           @period_theoric_interests,
    delta_interests:                    @delta_interests,
    accrued_delta_interests:            @accrued_delta_interests,
    due_interests_beginning_of_period:  @due_interests_beginning_of_period,
    due_interests_end_of_period:        @due_interests_end_of_period,
    amount_to_add:                      @amount_to_add,
    period_interests:                   @period_interests,
    period_capital:                     @period_capital,
    total_paid_capital_end_of_period:   @total_paid_capital_end_of_period,
    total_paid_interests_end_of_period: @total_paid_interests_end_of_period,
    period_amount_to_pay:               @period_amount_to_pay,
    due_on:                             @due_on,
    index:                              compute_index
  )
end
last_period?(idx) click to toggle source
# File lib/loan_creator/common.rb, line 202
def last_period?(idx)
  idx == (duration_in_periods - 1)
end
leap_days_count(date, relative_to_date:) click to toggle source
# File lib/loan_creator/common.rb, line 239
def leap_days_count(date, relative_to_date:)
  start_year = relative_to_date.year
  end_year = date.year

  (start_year..end_year).sum do |year|
    next 0 unless Date.gregorian_leap?(year)

    start_date =
      if start_year == year
        relative_to_date
      else
        Date.new(year - 1, 12, 31)
      end

    end_date =
      if end_year == year
        date
      else
        Date.new(year, 12, 31)
      end

    end_date - start_date
  end
end
new_timetable() click to toggle source
# File lib/loan_creator/common.rb, line 190
def new_timetable
  LoanCreator::Timetable.new(
    loan: self,
    interests_start_date: interests_start_date,
    starting_index: @starting_index
  )
end
prepare_custom_term_dates() click to toggle source
# File lib/loan_creator/common.rb, line 292
def prepare_custom_term_dates
  term_dates = @options[:term_dates].each_with_index.with_object({}) do |(term_date, index), obj|
    obj[index + @starting_index - 1] = term_date
  end

  # if starting_index > 1 term_dates[0] is not set
  term_dates[0] ||= starts_on
  @_timetable_term_dates = term_dates
  @realistic_durations = true
end
realistic_durations?() click to toggle source
# File lib/loan_creator/common.rb, line 276
def realistic_durations?
  term_dates? || @realistic_durations.present?
end
reinterpret_attributes() click to toggle source
# File lib/loan_creator/common.rb, line 90
def reinterpret_attributes
  @options[:period] = @options[:period].to_sym unless term_dates?
  @options[:amount] = bigd(@options[:amount])
  @options[:annual_interests_rate] = bigd(@options[:annual_interests_rate])
  @options[:starts_on] = Date.parse(@options[:starts_on]) if @options[:starts_on].is_a?(String)
  @options[:interests_start_date] = Date.parse(@options[:interests_start_date]) if @options[:interests_start_date].is_a?(String)

  if term_dates?
    @options[:term_dates].map!{ |term_date| Date.parse(term_date.to_s) }
  end
end
require_attributes() click to toggle source
# File lib/loan_creator/common.rb, line 86
def require_attributes
  required_attributes.each { |k| raise ArgumentError.new(k) unless @options.fetch(k, nil) }
end
required_attributes() click to toggle source
# File lib/loan_creator/common.rb, line 280
def required_attributes
  if term_dates?
    REQUIRED_ATTRIBUTES_TERM_DATES
  else
    REQUIRED_ATTRIBUTES
  end
end
reset_current_term() click to toggle source
# File lib/loan_creator/common.rb, line 153
def reset_current_term
  @accrued_delta_interests            ||= bigd('0')
  @total_paid_capital_end_of_period   ||= bigd('0')
  @total_paid_interests_end_of_period ||= bigd('0')
  @due_interests_beginning_of_period  ||= bigd('0')
  @crd_beginning_of_period            =   bigd('0')
  @crd_end_of_period                  =   bigd('0')
  @period_theoric_interests           =   bigd('0')
  @due_interests_end_of_period        =   @due_interests_beginning_of_period
  @delta_interests                    =   bigd('0')
  @amount_to_add                      =   bigd('0')
  @period_interests                   =   bigd('0')
  @period_capital                     =   bigd('0')
  @period_amount_to_pay               =   bigd('0')
  @due_on                             =   nil
end
set_attributes() click to toggle source
# File lib/loan_creator/common.rb, line 102
def set_attributes
  required_attributes.each { |k| instance_variable_set(:"@#{k}", @options.fetch(k)) }
  OPTIONAL_ATTRIBUTES.each { |k,v| instance_variable_set(:"@#{k}", @options.fetch(k, v)) }
end
set_initial_values() click to toggle source
# File lib/loan_creator/common.rb, line 141
def set_initial_values
  @starting_index         = initial_values[:starting_index] || 1
  @initial_due_interests  = bigd(initial_values[:due_interests] || 0)

  return if initial_values.blank?

  (@total_paid_capital_end_of_period   = bigd(initial_values[:paid_capital]))
  (@total_paid_interests_end_of_period = bigd(initial_values[:paid_interests]))
  (@accrued_delta_interests            = bigd(initial_values[:accrued_delta_interests]))
  (@due_interests_beginning_of_period  = bigd(initial_values[:due_interests] || 0))
end
term_dates?() click to toggle source
# File lib/loan_creator/common.rb, line 288
def term_dates?
  @options[:term_dates].present?
end
term_zero?() click to toggle source
# File lib/loan_creator/common.rb, line 235
def term_zero?
  (interests_start_date && interests_start_date < term_zero_date) && !term_dates?
end
term_zero_date() click to toggle source
# File lib/loan_creator/common.rb, line 231
def term_zero_date
  starts_on.advance(months: -PERIODS_IN_MONTHS.fetch(@period))
end
term_zero_duration() click to toggle source
# File lib/loan_creator/common.rb, line 227
def term_zero_duration
  (term_zero_date - interests_start_date).to_i
end
term_zero_interests() click to toggle source
# File lib/loan_creator/common.rb, line 218
def term_zero_interests
  @crd_beginning_of_period * term_zero_interests_rate
end
term_zero_interests_rate() click to toggle source
# File lib/loan_creator/common.rb, line 222
def term_zero_interests_rate
  term_zero_interests_rate_percentage = (annual_interests_rate * term_zero_duration).div(365, BIG_DECIMAL_DIGITS)
  term_zero_interests_rate_percentage.div(100, BIG_DECIMAL_DIGITS)
end
validate(key, &block) click to toggle source
# File lib/loan_creator/common.rb, line 107
def validate(key, &block)
  raise unless block.call(instance_variable_get(:"@#{key}"))
rescue => e
  raise ArgumentError.new([key, e.message].join(': '))
end
validate_attributes() click to toggle source
# File lib/loan_creator/common.rb, line 113
def validate_attributes
  validate(:period) { |v| PERIODS_IN_MONTHS.keys.include?(v) } unless term_dates?
  validate(:amount) { |v| v.is_a?(BigDecimal) && v > 0 }
  validate(:annual_interests_rate) { |v| v.is_a?(BigDecimal) && v >= 0 }
  validate(:starts_on) { |v| v.is_a?(Date) }
  validate(:duration_in_periods) { |v| v.is_a?(Integer) && v > 0 }
  validate(:deferred_in_periods) { |v| v.is_a?(Integer) && v >= 0 && v < duration_in_periods }
  validate_term_dates if term_dates?
end
validate_initial_values() click to toggle source
# File lib/loan_creator/common.rb, line 132
def validate_initial_values
  return if initial_values.blank?

  validate(:total_paid_capital_end_of_period) { |v| v.is_a?(BigDecimal) && v >= 0 }
  validate(:total_paid_interests_end_of_period) { |v| v.is_a?(BigDecimal) && v >= 0 }
  validate(:accrued_delta_interests) { |v| v.is_a?(BigDecimal) }
  validate(:starting_index) { |v| v.is_a?(Integer) && v >= 0 }
end
validate_term_dates() click to toggle source
# File lib/loan_creator/common.rb, line 123
def validate_term_dates
  TermDatesValidator.call(
    term_dates: @options[:term_dates],
    duration_in_periods: @options[:duration_in_periods],
    interests_start_date: @options[:interests_start_date],
    loan_class: self.class.name
  )
end