Class: Dscf::Credit::LoanAccrualGeneratorService

Inherits:
Object
  • Object
show all
Defined in:
app/services/dscf/credit/loan_accrual_generator_service.rb

Overview

LoanAccrualGeneratorService generates daily interest and penalty accruals for active loans. This service is designed to be run daily by a background job/cron job.

Examples:

Run for all active loans

service = Dscf::Credit::LoanAccrualGeneratorService.new
result = service.generate_daily_accruals

Run for specific loans

loan_ids = [1, 2, 3]
service = Dscf::Credit::LoanAccrualGeneratorService.new(loan_ids: loan_ids)
result = service.generate_daily_accruals

Run for specific date (useful for backfilling)

service = Dscf::Credit::LoanAccrualGeneratorService.new(accrual_date: Date.yesterday)
result = service.generate_daily_accruals

See Also:

  • for detailed documentation

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(loan_ids: nil, accrual_date: Date.current, force_regenerate: false) ⇒ LoanAccrualGeneratorService

Initialize the loan accrual generator service

Parameters:

  • loan_ids (Array<Integer>, nil) (defaults to: nil)

    Optional array of specific loan IDs to process. If nil, processes all eligible loans.

  • accrual_date (Date) (defaults to: Date.current)

    The date for which to generate accruals. Defaults to today.

  • force_regenerate (Boolean) (defaults to: false)

    If true, regenerates accruals even if they already exist for the given date. Defaults to false.



29
30
31
32
33
# File 'app/services/dscf/credit/loan_accrual_generator_service.rb', line 29

def initialize(loan_ids: nil, accrual_date: Date.current, force_regenerate: false)
  @loan_ids = loan_ids
  @accrual_date = accrual_date
  @force_regenerate = force_regenerate
end

Instance Attribute Details

#accrual_dateObject (readonly)

Returns the value of attribute accrual_date.



20
21
22
# File 'app/services/dscf/credit/loan_accrual_generator_service.rb', line 20

def accrual_date
  @accrual_date
end

#force_regenerateObject (readonly)

Returns the value of attribute force_regenerate.



20
21
22
# File 'app/services/dscf/credit/loan_accrual_generator_service.rb', line 20

def force_regenerate
  @force_regenerate
end

#loan_idsObject (readonly)

Returns the value of attribute loan_ids.



20
21
22
# File 'app/services/dscf/credit/loan_accrual_generator_service.rb', line 20

def loan_ids
  @loan_ids
end

Instance Method Details

#generate_daily_accrualsHash

Generate daily accruals for eligible loans

Eligible loans are those with:

  • Status: active, overdue, or disbursed

  • Have outstanding balance (remaining_amount > 0 or pending accruals exist)

For each eligible loan:

  1. Calculate daily interest based on principal and interest rate

  2. Calculate penalty if loan is overdue

  3. Create accrual records with status=‘pending’

Examples:

Success response

{
  success: true,
  accruals_created: 45,
  loans_processed: 30,
  accrual_details: [
    { loan_id: 1, accrual_type: 'interest', amount: 50.00 },
    { loan_id: 1, accrual_type: 'penalty', amount: 25.00 },
    ...
  ],
  errors: [],
  message: "Successfully generated 45 accruals for 30 loans on 2025-10-03"
}

Returns:

  • (Hash)

    Result hash with the following structure:

    • :success [Boolean] Whether the operation succeeded

    • :accruals_created [Integer] Number of accruals created

    • :loans_processed [Integer] Number of loans processed

    • :accrual_details [Array<Hash>] Details of each accrual created

    • :errors [Array<Hash>] Any errors encountered (loan_id, error message)

    • :message [String] Summary message



67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# File 'app/services/dscf/credit/loan_accrual_generator_service.rb', line 67

def generate_daily_accruals
  loans = fetch_eligible_loans

  return success_result([], []) if loans.empty?

  accrual_details = []
  errors = []

  loans.each do |loan|
    begin
      loan_accruals = generate_accruals_for_loan(loan)
      accrual_details.concat(loan_accruals)
    rescue StandardError => e
      errors << {
        loan_id: loan.id,
        error: e.message
      }
    end
  end

  success_result(accrual_details, errors)
rescue StandardError => e
  error_result("Accrual generation failed: #{e.message}")
end