Class: HrLite::Loan

Inherits:
ApplicationRecord show all
Includes:
Audited, EncryptedMoney
Defined in:
app/models/hr_lite/loan.rb

Overview

A salary advance or loan, repaid by a fixed monthly deduction.

The outstanding balance is DERIVED from the repayments actually taken, never stored. A stored balance and a recomputed payroll run disagree the first time somebody deletes a draft.

Constant Summary collapse

STATUSES =
%w[active closed cancelled].freeze

Constants included from Audited

Audited::REDACTED, Audited::SKIPPED_ATTRIBUTES

Instance Method Summary collapse

Instance Method Details

#instalment_for(period_month) ⇒ Object

What to deduct this month: the instalment, or whatever is left if that is less — the last instalment is almost never a round one.



34
35
36
37
38
39
40
# File 'app/models/hr_lite/loan.rb', line 34

def instalment_for(period_month)
  return BigDecimal(0) unless active?
  return BigDecimal(0) if period_month < starts_on.beginning_of_month
  return BigDecimal(0) if loan_repayments.exists?(period_month: period_month)

  [ monthly_instalment, outstanding ].min
end

#outstandingObject



30
# File 'app/models/hr_lite/loan.rb', line 30

def outstanding = [ principal - repaid, BigDecimal(0) ].max

#record_repayment!(period_month, amount) ⇒ Object

Called after a run is FINALIZED, not at compute: a draft is recomputed freely and each pass would otherwise book another repayment.



44
45
46
47
48
49
# File 'app/models/hr_lite/loan.rb', line 44

def record_repayment!(period_month, amount)
  return if amount.nil? || amount <= 0

  loan_repayments.create!(period_month: period_month, amount: amount)
  update!(status: "closed") if outstanding.zero?
end

#repaidObject



28
# File 'app/models/hr_lite/loan.rb', line 28

def repaid = loan_repayments.sum(BigDecimal(0)) { |r| r.amount || BigDecimal(0) }