dscf-credit 0.2.9 → 0.3.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 390fae684bcd0b01e25c3fa6d41b4fe735170cdd4ca5c3186bb534266db76489
|
4
|
+
data.tar.gz: 4cc9d0d6ffd10236ba5886a88cc2aa5b19844b064937df699ca46f578e530c29
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 28f5c78621fd09dff37a4d412a080cc0171a3ab44c191d681091f535e2ae65b5830ade9bd65fde02f98610835945eb311d6b8c257caa4930d7696fb75ea34dc1
|
7
|
+
data.tar.gz: 244304566b483837dcb91b300fff555de7fc8e27a583899c16a363db454777e1467b5b2fe8c744ceb4bdc23b14bc183ad12e69744f472f4d4054780e6b1d985e
|
@@ -119,31 +119,45 @@ module Dscf::Credit
|
|
119
119
|
# @param loan [Dscf::Credit::Loan] The loan to generate accruals for
|
120
120
|
# @return [Array<Hash>] Array of accrual details created
|
121
121
|
def generate_accruals_for_loan(loan)
|
122
|
-
return [] if accrual_exists_for_date?(loan) && !force_regenerate
|
123
|
-
|
124
122
|
accruals = []
|
125
123
|
|
126
124
|
# Generate interest accrual
|
127
125
|
if loan.remaining_amount > 0
|
128
|
-
|
129
|
-
|
126
|
+
unless accrual_exists_for_date?(loan, "interest") && !force_regenerate
|
127
|
+
interest_accrual = generate_interest_accrual(loan)
|
128
|
+
accruals << interest_accrual if interest_accrual
|
129
|
+
end
|
130
130
|
end
|
131
131
|
|
132
132
|
# Generate penalty accrual if overdue
|
133
133
|
if loan_is_overdue?(loan)
|
134
|
-
|
135
|
-
|
134
|
+
unless accrual_exists_for_date?(loan, "penalty") && !force_regenerate
|
135
|
+
penalty_accrual = generate_penalty_accrual(loan)
|
136
|
+
accruals << penalty_accrual if penalty_accrual
|
137
|
+
end
|
136
138
|
end
|
137
139
|
|
138
140
|
accruals
|
139
141
|
end
|
140
142
|
|
141
|
-
# Check if
|
143
|
+
# Check if a specific type of accrual already exists for the loan on the given date
|
144
|
+
#
|
145
|
+
# This method checks per accrual type (interest or penalty) rather than both together.
|
146
|
+
# This ensures that an existing penalty accrual doesn't block an interest accrual,
|
147
|
+
# and vice versa, making the method more precise and flexible.
|
148
|
+
#
|
149
|
+
# Note: Only checks for interest and penalty accruals, not facilitation fees,
|
150
|
+
# since facilitation fees are generated during disbursement and should not
|
151
|
+
# prevent daily interest/penalty generation.
|
142
152
|
#
|
143
153
|
# @param loan [Dscf::Credit::Loan] The loan to check
|
144
|
-
# @
|
145
|
-
|
146
|
-
|
154
|
+
# @param accrual_type [String] The type of accrual to check ('interest' or 'penalty')
|
155
|
+
# @return [Boolean] True if the specified accrual type exists for the date
|
156
|
+
def accrual_exists_for_date?(loan, accrual_type)
|
157
|
+
loan.loan_accruals
|
158
|
+
.where(applied_on: accrual_date)
|
159
|
+
.where(accrual_type: accrual_type)
|
160
|
+
.exists?
|
147
161
|
end
|
148
162
|
|
149
163
|
# Generate interest accrual for a loan
|
@@ -261,25 +261,32 @@ module Dscf::Credit
|
|
261
261
|
# Only runs if loan.status == "paid"
|
262
262
|
#
|
263
263
|
# Process:
|
264
|
-
# 1. Find
|
265
|
-
# 2.
|
266
|
-
#
|
267
|
-
#
|
268
|
-
# 3. Unlock the credit line by setting locked: false
|
264
|
+
# 1. Find the eligible credit line used for this loan
|
265
|
+
# 2. Reset available_limit to credit_limit, applying risk if present
|
266
|
+
# 3. Find all other locked eligible credit lines (locked during disbursement)
|
267
|
+
# 4. Unlock them by setting locked: false
|
269
268
|
#
|
270
269
|
# This allows the borrower to access credit again after repayment.
|
271
270
|
#
|
272
271
|
# @return [void]
|
273
272
|
#
|
274
273
|
# @example
|
275
|
-
# # Before: eligible_line.locked = true
|
276
|
-
# # After payment: eligible_line.locked = false
|
274
|
+
# # Before: eligible_line.locked = true, available_limit = 50000
|
275
|
+
# # After payment: eligible_line.locked = false, available_limit = 100000 (or adjusted for risk)
|
277
276
|
def reactivate_facilities_if_paid_off
|
278
277
|
return unless loan.status == "paid"
|
279
278
|
|
280
279
|
loan_profile = loan.loan_profile
|
281
280
|
credit_line = loan.credit_line
|
282
281
|
|
282
|
+
# Reset available limit for the eligible credit line used for this loan
|
283
|
+
eligible_credit_line = loan_profile.eligible_credit_lines
|
284
|
+
.find_by(credit_line: credit_line)
|
285
|
+
|
286
|
+
if eligible_credit_line
|
287
|
+
reset_available_limit(eligible_credit_line)
|
288
|
+
end
|
289
|
+
|
283
290
|
# Unlock other eligible credit lines that were locked during disbursement
|
284
291
|
loan_profile.eligible_credit_lines
|
285
292
|
.where.not(credit_line: credit_line)
|
@@ -287,6 +294,57 @@ module Dscf::Credit
|
|
287
294
|
.update_all(locked: false)
|
288
295
|
end
|
289
296
|
|
297
|
+
# Reset available limit for an eligible credit line, applying risk if present
|
298
|
+
#
|
299
|
+
# If risk > 0, the available limit is reduced by the risk percentage
|
300
|
+
# Formula: available_limit = credit_limit * (1 - risk)
|
301
|
+
#
|
302
|
+
# Also recalculates and updates the loan profile's total_limit to reflect
|
303
|
+
# the current state of all eligible credit lines.
|
304
|
+
#
|
305
|
+
# @param eligible_credit_line [Dscf::Credit::EligibleCreditLine]
|
306
|
+
# @return [void]
|
307
|
+
#
|
308
|
+
# @example Without risk
|
309
|
+
# # risk = 0 or nil
|
310
|
+
# # available_limit = credit_limit (100000)
|
311
|
+
#
|
312
|
+
# @example With risk
|
313
|
+
# # risk = 0.1 (10%)
|
314
|
+
# # available_limit = credit_limit * (1 - 0.1) = 100000 * 0.9 = 90000
|
315
|
+
def reset_available_limit(eligible_credit_line)
|
316
|
+
credit_limit = eligible_credit_line.credit_limit
|
317
|
+
risk = eligible_credit_line.risk || 0
|
318
|
+
|
319
|
+
if risk > 0
|
320
|
+
available_limit = credit_limit * (1 - risk)
|
321
|
+
else
|
322
|
+
available_limit = credit_limit
|
323
|
+
end
|
324
|
+
|
325
|
+
eligible_credit_line.update!(available_limit: available_limit)
|
326
|
+
|
327
|
+
# Recalculate loan_profile total_limit after updating available_limit
|
328
|
+
update_loan_profile_total_limit(eligible_credit_line.loan_profile)
|
329
|
+
end
|
330
|
+
|
331
|
+
# Recalculate and update loan profile's total_limit
|
332
|
+
#
|
333
|
+
# Sums all available_limit values from the loan profile's eligible credit lines
|
334
|
+
# and updates the loan_profile.total_limit to reflect the current state.
|
335
|
+
#
|
336
|
+
# @param loan_profile [Dscf::Credit::LoanProfile]
|
337
|
+
# @return [void]
|
338
|
+
def update_loan_profile_total_limit(loan_profile)
|
339
|
+
return unless loan_profile
|
340
|
+
|
341
|
+
total_available_limit = loan_profile.eligible_credit_lines.sum(:available_limit)
|
342
|
+
|
343
|
+
loan_profile.update!(total_limit: total_available_limit)
|
344
|
+
|
345
|
+
Rails.logger.info "Updated loan profile #{loan_profile.id} total limit to #{total_available_limit}"
|
346
|
+
end
|
347
|
+
|
290
348
|
# Build success result hash
|
291
349
|
#
|
292
350
|
# @param allocation [Hash] The allocation hash with payment details
|
data/lib/dscf/credit/version.rb
CHANGED