dscf-credit 0.3.7 → 0.3.8
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 +4 -4
- data/app/controllers/dscf/credit/loan_transactions_controller.rb +34 -0
- data/app/controllers/dscf/credit/loans_controller.rb +1 -1
- data/app/models/dscf/credit/loan_transaction.rb +11 -1
- data/app/services/dscf/credit/disbursement_service.rb +15 -3
- data/app/services/dscf/credit/loan_transaction_creator_service.rb +71 -0
- data/app/services/dscf/credit/repayment_service.rb +21 -3
- data/config/locales/en.yml +12 -0
- data/config/routes.rb +1 -0
- data/lib/dscf/credit/version.rb +1 -1
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 04cc64832d4b85ab0ca609c056fb8b92282b6231cd996144e85721b1ecfd05dd
|
|
4
|
+
data.tar.gz: 6f90fadd4bf75cfdb003b2eca35d17bd95916c75c2be77105a2296e4720b7645
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f2ca07ab61fb05247a399992d06b0692b6e0ac020b28b3cee9fd39ea4eea9a52dad7b8ade4303344616649e24c9d51dab515cd0b5ee93ec4d1f6d8abb5c55349
|
|
7
|
+
data.tar.gz: ff113b79a241223bf3834715c0f8abd8bd4f7267a0fabebc8e46438852d0a56724fe6a3f8ae74012139e1590561fceb5fe1e247fa5544769832559e71ce8d464
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Dscf::Credit
|
|
2
|
+
class LoanTransactionsController < ApplicationController
|
|
3
|
+
include Dscf::Core::Common
|
|
4
|
+
|
|
5
|
+
private
|
|
6
|
+
|
|
7
|
+
def model_params
|
|
8
|
+
params.require(:loan_transaction).permit(
|
|
9
|
+
:loan_id,
|
|
10
|
+
:transaction_type,
|
|
11
|
+
:amount,
|
|
12
|
+
:transaction_reference,
|
|
13
|
+
:status
|
|
14
|
+
)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def eager_loaded_associations
|
|
18
|
+
[ :loan ]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def allowed_order_columns
|
|
22
|
+
%w[id transaction_type amount transaction_reference status created_at updated_at]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def default_serializer_includes
|
|
26
|
+
{
|
|
27
|
+
index: [ :loan ],
|
|
28
|
+
show: [ :loan ],
|
|
29
|
+
create: [ :loan ],
|
|
30
|
+
update: [ :loan ]
|
|
31
|
+
}
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
@@ -28,7 +28,7 @@ module Dscf::Credit
|
|
|
28
28
|
def default_serializer_includes
|
|
29
29
|
{
|
|
30
30
|
index: [ :loan_profile, :credit_line ],
|
|
31
|
-
show: [ :loan_profile, :credit_line, :loan_accruals ],
|
|
31
|
+
show: [ :loan_profile, :credit_line, :loan_accruals, :loan_transactions ],
|
|
32
32
|
create: [ :loan_profile, :credit_line ],
|
|
33
33
|
update: [ :loan_profile, :credit_line ]
|
|
34
34
|
}
|
|
@@ -6,12 +6,22 @@ module Dscf::Credit
|
|
|
6
6
|
|
|
7
7
|
validates :transaction_type, :amount, :transaction_reference, :status, presence: true
|
|
8
8
|
validates :amount, numericality: { greater_than: 0 }
|
|
9
|
-
validates :transaction_type, inclusion: { in: %w[disbursement repayment interest_accrual penalty fee_charge] }
|
|
9
|
+
validates :transaction_type, inclusion: { in: %w[disbursement repayment interest_accrual penalty fee_charge reversal adjustment] }
|
|
10
10
|
validates :status, inclusion: { in: %w[pending processing completed failed reversed] }
|
|
11
11
|
validates :transaction_reference, uniqueness: true
|
|
12
12
|
|
|
13
13
|
scope :by_status, ->(status) { where(status: status) }
|
|
14
|
+
scope :completed, -> { where(status: "completed") }
|
|
15
|
+
scope :pending, -> { where(status: "pending") }
|
|
14
16
|
scope :disbursements, -> { where(transaction_type: "disbursement") }
|
|
15
17
|
scope :repayments, -> { where(transaction_type: "repayment") }
|
|
18
|
+
|
|
19
|
+
def self.ransackable_attributes(auth_object = nil)
|
|
20
|
+
%w[id transaction_type amount transaction_reference status created_at updated_at]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.ransackable_associations(auth_object = nil)
|
|
24
|
+
%w[loan]
|
|
25
|
+
end
|
|
16
26
|
end
|
|
17
27
|
end
|
|
@@ -31,8 +31,9 @@ module Dscf::Credit
|
|
|
31
31
|
loan = create_loan_record(credit_line)
|
|
32
32
|
update_credit_line_limits(loan)
|
|
33
33
|
lock_other_credit_lines(loan_profile, credit_line)
|
|
34
|
+
loan_transaction = create_disbursement_transaction(loan)
|
|
34
35
|
|
|
35
|
-
success_result(loan)
|
|
36
|
+
success_result(loan, loan_transaction)
|
|
36
37
|
end
|
|
37
38
|
rescue StandardError => e
|
|
38
39
|
error_result("Disbursement processing failed: #{e.message}")
|
|
@@ -132,7 +133,16 @@ module Dscf::Credit
|
|
|
132
133
|
eligible_credit_line.update!(available_limit: [ new_available_limit, 0 ].max)
|
|
133
134
|
end
|
|
134
135
|
|
|
135
|
-
def
|
|
136
|
+
def create_disbursement_transaction(loan)
|
|
137
|
+
transaction_service = LoanTransactionCreatorService.new(loan)
|
|
138
|
+
transaction_service.create_transaction(
|
|
139
|
+
transaction_type: "disbursement",
|
|
140
|
+
amount: loan.principal_amount,
|
|
141
|
+
status: "completed"
|
|
142
|
+
)
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def success_result(loan, loan_transaction)
|
|
136
146
|
# Reload to get associated accruals
|
|
137
147
|
loan.reload
|
|
138
148
|
|
|
@@ -144,12 +154,14 @@ module Dscf::Credit
|
|
|
144
154
|
{
|
|
145
155
|
success: true,
|
|
146
156
|
loan: loan,
|
|
157
|
+
loan_transaction: loan_transaction,
|
|
147
158
|
disbursement_details: {
|
|
148
159
|
principal_amount: loan.principal_amount.to_f,
|
|
149
160
|
facilitation_fee: facilitation_fee,
|
|
150
161
|
total_loan_amount: total_amount,
|
|
151
162
|
due_date: loan.due_date,
|
|
152
|
-
disbursed_at: loan.disbursed_at
|
|
163
|
+
disbursed_at: loan.disbursed_at,
|
|
164
|
+
transaction_reference: loan_transaction.transaction_reference
|
|
153
165
|
},
|
|
154
166
|
message: "Disbursement processed successfully"
|
|
155
167
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
module Dscf::Credit
|
|
2
|
+
# Service for creating loan transaction records
|
|
3
|
+
# Handles transaction creation with automatic reference generation
|
|
4
|
+
#
|
|
5
|
+
# @example Disbursement transaction
|
|
6
|
+
# service = LoanTransactionCreatorService.new(loan)
|
|
7
|
+
# transaction = service.create_transaction(
|
|
8
|
+
# transaction_type: "disbursement",
|
|
9
|
+
# amount: 50000.00,
|
|
10
|
+
# status: "completed"
|
|
11
|
+
# )
|
|
12
|
+
#
|
|
13
|
+
# @example Repayment transaction
|
|
14
|
+
# service = LoanTransactionCreatorService.new(loan)
|
|
15
|
+
# transaction = service.create_transaction(
|
|
16
|
+
# transaction_type: "repayment",
|
|
17
|
+
# amount: 5000.00,
|
|
18
|
+
# status: "completed"
|
|
19
|
+
# )
|
|
20
|
+
class LoanTransactionCreatorService
|
|
21
|
+
attr_reader :loan
|
|
22
|
+
|
|
23
|
+
# Transaction type prefixes for reference generation
|
|
24
|
+
TRANSACTION_PREFIXES = {
|
|
25
|
+
"disbursement" => "DSB",
|
|
26
|
+
"repayment" => "RPY",
|
|
27
|
+
"interest_accrual" => "INT",
|
|
28
|
+
"penalty" => "PEN",
|
|
29
|
+
"fee_charge" => "FEE",
|
|
30
|
+
"reversal" => "REV",
|
|
31
|
+
"adjustment" => "ADJ"
|
|
32
|
+
}.freeze
|
|
33
|
+
|
|
34
|
+
def initialize(loan)
|
|
35
|
+
@loan = loan
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Create a loan transaction record
|
|
39
|
+
#
|
|
40
|
+
# @param transaction_type [String] Type of transaction (disbursement, repayment, etc.)
|
|
41
|
+
# @param amount [Numeric] Transaction amount
|
|
42
|
+
# @param status [String] Transaction status (pending, completed, failed, etc.)
|
|
43
|
+
# @return [Dscf::Credit::LoanTransaction] Created transaction record
|
|
44
|
+
def create_transaction(transaction_type:, amount:, status: "completed")
|
|
45
|
+
transaction_reference = generate_transaction_reference(transaction_type)
|
|
46
|
+
|
|
47
|
+
Dscf::Credit::LoanTransaction.create!(
|
|
48
|
+
loan: loan,
|
|
49
|
+
transaction_type: transaction_type,
|
|
50
|
+
amount: amount,
|
|
51
|
+
transaction_reference: transaction_reference,
|
|
52
|
+
status: status
|
|
53
|
+
)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
private
|
|
57
|
+
|
|
58
|
+
# Generate unique transaction reference
|
|
59
|
+
# Format: PREFIX-LOAN_ID-TIMESTAMP-RANDOM
|
|
60
|
+
#
|
|
61
|
+
# @param transaction_type [String] Type of transaction
|
|
62
|
+
# @return [String] Generated reference
|
|
63
|
+
def generate_transaction_reference(transaction_type)
|
|
64
|
+
prefix = TRANSACTION_PREFIXES[transaction_type]
|
|
65
|
+
timestamp = Time.current.to_i
|
|
66
|
+
random = SecureRandom.hex(4)
|
|
67
|
+
|
|
68
|
+
"#{prefix}-#{loan.id}-#{timestamp}-#{random}"
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
end
|
|
@@ -85,7 +85,9 @@ module Dscf::Credit
|
|
|
85
85
|
|
|
86
86
|
reactivate_facilities_if_paid_off
|
|
87
87
|
|
|
88
|
-
|
|
88
|
+
loan_transaction = create_repayment_transaction(payment_allocation)
|
|
89
|
+
|
|
90
|
+
success_result(payment_allocation, loan_transaction)
|
|
89
91
|
end
|
|
90
92
|
rescue StandardError => e
|
|
91
93
|
error_result("Repayment processing failed: #{e.message}")
|
|
@@ -345,11 +347,25 @@ module Dscf::Credit
|
|
|
345
347
|
Rails.logger.info "Updated loan profile #{loan_profile.id} total limit to #{total_available_limit}"
|
|
346
348
|
end
|
|
347
349
|
|
|
350
|
+
# Create a loan transaction record for the repayment
|
|
351
|
+
#
|
|
352
|
+
# @param allocation [Hash] The allocation hash with payment details
|
|
353
|
+
# @return [Dscf::Credit::LoanTransaction] The created transaction record
|
|
354
|
+
def create_repayment_transaction(allocation)
|
|
355
|
+
transaction_service = LoanTransactionCreatorService.new(loan)
|
|
356
|
+
transaction_service.create_transaction(
|
|
357
|
+
transaction_type: "repayment",
|
|
358
|
+
amount: payment_amount,
|
|
359
|
+
status: "completed"
|
|
360
|
+
)
|
|
361
|
+
end
|
|
362
|
+
|
|
348
363
|
# Build success result hash
|
|
349
364
|
#
|
|
350
365
|
# @param allocation [Hash] The allocation hash with payment details
|
|
366
|
+
# @param loan_transaction [Dscf::Credit::LoanTransaction] The created transaction record
|
|
351
367
|
# @return [Hash] Success response with loan, payment details, and message
|
|
352
|
-
def success_result(allocation)
|
|
368
|
+
def success_result(allocation, loan_transaction)
|
|
353
369
|
loan.reload
|
|
354
370
|
pending_facilitation_fee = loan.loan_accruals.pending.by_type("facilitation_fee").sum(:amount)
|
|
355
371
|
pending_penalty = loan.loan_accruals.pending.by_type("penalty").sum(:amount)
|
|
@@ -358,6 +374,7 @@ module Dscf::Credit
|
|
|
358
374
|
{
|
|
359
375
|
success: true,
|
|
360
376
|
loan: loan,
|
|
377
|
+
loan_transaction: loan_transaction,
|
|
361
378
|
payment_details: {
|
|
362
379
|
payment_amount: payment_amount,
|
|
363
380
|
allocation: allocation,
|
|
@@ -366,7 +383,8 @@ module Dscf::Credit
|
|
|
366
383
|
remaining_balance: pending_facilitation_fee +
|
|
367
384
|
pending_penalty +
|
|
368
385
|
pending_interest +
|
|
369
|
-
(loan.remaining_amount || 0)
|
|
386
|
+
(loan.remaining_amount || 0),
|
|
387
|
+
transaction_reference: loan_transaction.transaction_reference
|
|
370
388
|
},
|
|
371
389
|
message: loan.status == "paid" ? "Loan fully paid off" : "Payment processed successfully"
|
|
372
390
|
}
|
data/config/locales/en.yml
CHANGED
|
@@ -124,6 +124,18 @@ en:
|
|
|
124
124
|
generate: "Failed to generate loan accruals"
|
|
125
125
|
statistics: "Failed to retrieve loan accrual statistics"
|
|
126
126
|
|
|
127
|
+
loan_transaction:
|
|
128
|
+
success:
|
|
129
|
+
index: "Loan transactions retrieved successfully"
|
|
130
|
+
show: "Loan transaction details retrieved successfully"
|
|
131
|
+
create: "Loan transaction created successfully"
|
|
132
|
+
update: "Loan transaction updated successfully"
|
|
133
|
+
errors:
|
|
134
|
+
index: "Failed to retrieve loan transactions"
|
|
135
|
+
show: "Failed to retrieve loan transaction details"
|
|
136
|
+
create: "Failed to create loan transaction"
|
|
137
|
+
update: "Failed to update loan transaction"
|
|
138
|
+
|
|
127
139
|
loan:
|
|
128
140
|
success:
|
|
129
141
|
index: "Loans retrieved successfully"
|
data/config/routes.rb
CHANGED
|
@@ -73,6 +73,7 @@ Dscf::Credit::Engine.routes.draw do
|
|
|
73
73
|
resources :credit_limit_calculations, only: [ :create ]
|
|
74
74
|
resources :disbursements, only: [ :create ]
|
|
75
75
|
resources :repayments, only: [ :create ]
|
|
76
|
+
resources :loan_transactions
|
|
76
77
|
resources :loan_applications do
|
|
77
78
|
member do
|
|
78
79
|
patch "approve"
|
data/lib/dscf/credit/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dscf-credit
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.8
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Adoniyas
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-10-
|
|
10
|
+
date: 2025-10-30 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: dscf-core
|
|
@@ -328,6 +328,7 @@ files:
|
|
|
328
328
|
- app/controllers/dscf/credit/loan_accruals_controller.rb
|
|
329
329
|
- app/controllers/dscf/credit/loan_applications_controller.rb
|
|
330
330
|
- app/controllers/dscf/credit/loan_profiles_controller.rb
|
|
331
|
+
- app/controllers/dscf/credit/loan_transactions_controller.rb
|
|
331
332
|
- app/controllers/dscf/credit/loans_controller.rb
|
|
332
333
|
- app/controllers/dscf/credit/parameter_normalizers_controller.rb
|
|
333
334
|
- app/controllers/dscf/credit/repayments_controller.rb
|
|
@@ -398,6 +399,7 @@ files:
|
|
|
398
399
|
- app/services/dscf/credit/facility_limit_calculation_engine.rb
|
|
399
400
|
- app/services/dscf/credit/loan_accrual_generator_service.rb
|
|
400
401
|
- app/services/dscf/credit/loan_profile_creation_service.rb
|
|
402
|
+
- app/services/dscf/credit/loan_transaction_creator_service.rb
|
|
401
403
|
- app/services/dscf/credit/repayment_service.rb
|
|
402
404
|
- app/services/dscf/credit/risk_application_service.rb
|
|
403
405
|
- app/services/dscf/credit/scoring_service.rb
|