dscf-credit 0.1.2 → 0.1.3
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/concerns/dscf/core/authenticatable.rb +81 -0
- data/app/controllers/concerns/dscf/core/common.rb +200 -0
- data/app/controllers/concerns/dscf/core/filterable.rb +12 -0
- data/app/controllers/concerns/dscf/core/json_response.rb +77 -0
- data/app/controllers/concerns/dscf/core/pagination.rb +71 -0
- data/app/controllers/concerns/dscf/core/token_authenticatable.rb +53 -0
- data/app/controllers/dscf/credit/categories_controller.rb +3 -3
- data/app/controllers/dscf/credit/credit_limit_calculations_controller.rb +50 -0
- data/app/controllers/dscf/credit/credit_line_specs_controller.rb +2 -1
- data/app/controllers/dscf/credit/credit_lines_controller.rb +3 -3
- data/app/controllers/dscf/credit/disbursements_controller.rb +55 -0
- data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -0
- data/app/controllers/dscf/credit/loan_profiles_controller.rb +138 -0
- data/app/controllers/dscf/credit/payment_requests_controller.rb +54 -5
- data/app/controllers/dscf/credit/repayments_controller.rb +53 -0
- data/app/controllers/dscf/credit/scoring_parameters_controller.rb +3 -3
- data/app/controllers/dscf/credit/scoring_tables_controller.rb +8 -8
- data/app/models/dscf/credit/bank_branch.rb +2 -1
- data/app/models/dscf/credit/category.rb +3 -1
- data/app/models/dscf/credit/credit_line.rb +3 -3
- data/app/models/dscf/credit/credit_line_spec.rb +3 -3
- data/app/models/dscf/credit/eligible_credit_line.rb +28 -0
- data/app/models/dscf/credit/loan_profile.rb +8 -5
- data/app/models/dscf/credit/loan_profile_scoring_spec.rb +4 -5
- data/app/models/dscf/credit/scoring_parameter.rb +2 -2
- data/app/models/dscf/credit/scoring_table.rb +4 -4
- data/app/serializers/dscf/credit/bank_branch_serializer.rb +1 -0
- data/app/serializers/dscf/credit/category_serializer.rb +2 -0
- data/app/serializers/dscf/credit/credit_line_serializer.rb +2 -0
- data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +1 -1
- data/app/serializers/dscf/credit/daily_routine_transaction_serializer.rb +8 -0
- data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +8 -0
- data/app/serializers/dscf/credit/loan_profile_scoring_spec_serializer.rb +8 -0
- data/app/serializers/dscf/credit/loan_profile_serializer.rb +15 -0
- data/app/serializers/dscf/credit/loan_serializer.rb +12 -0
- data/app/serializers/dscf/credit/loan_transaction_serializer.rb +8 -0
- data/app/serializers/dscf/credit/payment_request_serializer.rb +10 -0
- data/app/serializers/dscf/credit/payment_serializer.rb +8 -0
- data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +2 -0
- data/app/serializers/dscf/credit/scoring_table_serializer.rb +1 -1
- data/app/services/dscf/credit/credit_limit_calculation_service.rb +153 -0
- data/app/services/dscf/credit/disbursement_service.rb +180 -0
- data/app/services/dscf/credit/repayment_service.rb +216 -0
- data/app/services/dscf/credit/risk_application_service.rb +27 -0
- data/app/services/dscf/credit/scoring_service.rb +297 -0
- data/config/locales/en.yml +79 -0
- data/config/routes.rb +19 -5
- data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +1 -0
- data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +2 -1
- data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +5 -8
- data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +2 -2
- data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +18 -0
- data/db/seeds.rb +88 -22
- data/lib/dscf/credit/version.rb +1 -1
- data/spec/factories/dscf/credit/credit_line_specs.rb +1 -0
- data/spec/factories/dscf/credit/credit_lines.rb +3 -3
- data/spec/factories/dscf/credit/eligible_credit_lines.rb +33 -0
- data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +1 -4
- data/spec/factories/dscf/credit/loan_profiles.rb +1 -0
- data/spec/factories/dscf/credit/scoring_tables.rb +1 -1
- metadata +29 -2
@@ -0,0 +1,297 @@
|
|
1
|
+
module Dscf::Credit
|
2
|
+
class ScoringService
|
3
|
+
attr_reader :loan_profile
|
4
|
+
|
5
|
+
def initialize(loan_profile)
|
6
|
+
@loan_profile = loan_profile
|
7
|
+
end
|
8
|
+
|
9
|
+
# Calculate credit score for a loan profile using the eligibility category scoring table
|
10
|
+
# @param external_scoring_data [Hash] Optional external scoring input data
|
11
|
+
# @return [Hash] Result containing score, total_limit, and success status
|
12
|
+
def calculate_credit_score(external_scoring_data = nil)
|
13
|
+
eligibility_category = find_eligibility_category
|
14
|
+
return error_result("Eligibility category not found") unless eligibility_category
|
15
|
+
|
16
|
+
scoring_tables = eligibility_category.scoring_tables.active.includes(:scoring_parameter)
|
17
|
+
return error_result("No active scoring parameters found for eligibility category") if scoring_tables.empty?
|
18
|
+
|
19
|
+
scoring_data = get_scoring_data(external_scoring_data)
|
20
|
+
score = calculate_weighted_score(scoring_tables, scoring_data)
|
21
|
+
facility_limit = calculate_facility_limit(score, scoring_data)
|
22
|
+
|
23
|
+
success_result(score, facility_limit)
|
24
|
+
rescue StandardError => e
|
25
|
+
error_result("Score calculation failed: #{e.message}")
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def get_scoring_data(external_scoring_data)
|
31
|
+
if external_scoring_data.present?
|
32
|
+
external_scoring_data
|
33
|
+
else
|
34
|
+
extract_scoring_data_from_profile
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def extract_scoring_data_from_profile
|
39
|
+
latest_spec = loan_profile.loan_profile_scoring_specs.order(created_at: :desc).first
|
40
|
+
|
41
|
+
if latest_spec&.scoring_input_data.present? && !latest_spec.scoring_input_data.empty?
|
42
|
+
default_data = prepare_scoring_data
|
43
|
+
default_data.merge(latest_spec.scoring_input_data.symbolize_keys)
|
44
|
+
else
|
45
|
+
prepare_scoring_data
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def find_eligibility_category
|
50
|
+
Dscf::Credit::Category.find_by(type: "eligibility", name: "default") ||
|
51
|
+
Dscf::Credit::Category.find_by(type: "eligibility")
|
52
|
+
end
|
53
|
+
|
54
|
+
def prepare_scoring_data
|
55
|
+
{
|
56
|
+
average_daily_purchase: extract_average_daily_purchase,
|
57
|
+
years_at_location: extract_years_at_location,
|
58
|
+
marital_status: extract_marital_status,
|
59
|
+
dependents_count: extract_dependents_count,
|
60
|
+
education_level: extract_education_level,
|
61
|
+
bill_payment_timing: extract_bill_payment_timing,
|
62
|
+
other_accounts: extract_other_accounts,
|
63
|
+
money_preference: extract_money_preference,
|
64
|
+
business_license: extract_business_license,
|
65
|
+
bank_statements: extract_bank_statements,
|
66
|
+
transaction_frequency: extract_transaction_frequency,
|
67
|
+
purchase_volume: extract_purchase_volume,
|
68
|
+
cancellation_rate: extract_cancellation_rate,
|
69
|
+
reliability_score: extract_reliability_score
|
70
|
+
}
|
71
|
+
end
|
72
|
+
|
73
|
+
# Calculate weighted score based on scoring parameters
|
74
|
+
def calculate_weighted_score(scoring_tables, scoring_data)
|
75
|
+
total_score = 0.0
|
76
|
+
total_weight = 0.0
|
77
|
+
|
78
|
+
scoring_tables.each do |table|
|
79
|
+
parameter = table.scoring_parameter
|
80
|
+
raw_value = find_parameter_value(parameter, scoring_data)
|
81
|
+
|
82
|
+
next if raw_value.nil?
|
83
|
+
|
84
|
+
normalized_score = normalize_parameter_value(parameter, raw_value)
|
85
|
+
weighted_score = normalized_score * table.weight
|
86
|
+
|
87
|
+
total_score += weighted_score
|
88
|
+
total_weight += table.weight
|
89
|
+
end
|
90
|
+
|
91
|
+
return 0.0 if total_weight.zero?
|
92
|
+
|
93
|
+
(total_score / total_weight) * 100
|
94
|
+
end
|
95
|
+
|
96
|
+
# Find parameter value from scoring data using flexible key matching
|
97
|
+
def find_parameter_value(parameter, scoring_data)
|
98
|
+
param_name = parameter.name
|
99
|
+
|
100
|
+
# Try exact match first
|
101
|
+
if scoring_data.key?(param_name.to_sym)
|
102
|
+
return scoring_data[param_name.to_sym]
|
103
|
+
end
|
104
|
+
|
105
|
+
if scoring_data.key?(param_name)
|
106
|
+
return scoring_data[param_name]
|
107
|
+
end
|
108
|
+
|
109
|
+
# Try snake_case version
|
110
|
+
snake_case_name = param_name.downcase.gsub(/\s+/, "_")
|
111
|
+
if scoring_data.key?(snake_case_name.to_sym)
|
112
|
+
return scoring_data[snake_case_name.to_sym]
|
113
|
+
end
|
114
|
+
|
115
|
+
if scoring_data.key?(snake_case_name)
|
116
|
+
return scoring_data[snake_case_name]
|
117
|
+
end
|
118
|
+
|
119
|
+
# Try common mappings
|
120
|
+
case param_name.downcase
|
121
|
+
when "monthly income"
|
122
|
+
scoring_data[:monthly_income] || scoring_data["monthly_income"]
|
123
|
+
when "credit history score"
|
124
|
+
scoring_data[:credit_history_score] || scoring_data["credit_history_score"]
|
125
|
+
when "employment type"
|
126
|
+
scoring_data[:employment_type] || scoring_data["employment_type"]
|
127
|
+
else
|
128
|
+
nil
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Calculate facility limit based on score and business metrics
|
133
|
+
def calculate_facility_limit(score, scoring_data)
|
134
|
+
return 0 if score < 50 # Below 50% score = no facility
|
135
|
+
|
136
|
+
average_daily_purchase = scoring_data[:average_daily_purchase] || 0
|
137
|
+
|
138
|
+
credit_line_multiplier = get_default_credit_line_multiplier
|
139
|
+
|
140
|
+
base_limit = average_daily_purchase * credit_line_multiplier * (score / 100.0)
|
141
|
+
|
142
|
+
apply_limit_constraints(base_limit)
|
143
|
+
end
|
144
|
+
|
145
|
+
# Get default credit line multiplier from the bank's approved credit line specs
|
146
|
+
def get_default_credit_line_multiplier
|
147
|
+
return 30.0 unless loan_profile.bank
|
148
|
+
|
149
|
+
credit_line_spec = loan_profile.bank.credit_lines.approved
|
150
|
+
.joins(:credit_line_specs)
|
151
|
+
.merge(Dscf::Credit::CreditLineSpec.active)
|
152
|
+
.select("dscf_credit_credit_line_specs.credit_line_multiplier")
|
153
|
+
.first&.credit_line_specs&.active&.first
|
154
|
+
|
155
|
+
credit_line_spec&.credit_line_multiplier || 30.0
|
156
|
+
end
|
157
|
+
|
158
|
+
def apply_limit_constraints(base_limit)
|
159
|
+
min_limit = get_bank_config("min_credit_limit", 0)
|
160
|
+
max_limit = get_bank_config("max_credit_limit", 1000000)
|
161
|
+
|
162
|
+
return 0 if base_limit < min_limit
|
163
|
+
return max_limit if base_limit > max_limit
|
164
|
+
|
165
|
+
base_limit
|
166
|
+
end
|
167
|
+
|
168
|
+
# Get bank configuration value from system configs
|
169
|
+
def get_bank_config(config_key, default_value)
|
170
|
+
return default_value unless loan_profile.bank
|
171
|
+
|
172
|
+
config_definition = loan_profile.bank.system_config_definitions
|
173
|
+
.find_by(config_key: config_key)
|
174
|
+
return default_value unless config_definition
|
175
|
+
|
176
|
+
system_config = Dscf::Credit::SystemConfig.approved
|
177
|
+
.find_by(config_definition: config_definition)
|
178
|
+
|
179
|
+
if system_config&.config_value
|
180
|
+
numeric_value = system_config.config_value.to_f
|
181
|
+
numeric_value > 0 ? numeric_value : default_value
|
182
|
+
else
|
183
|
+
default_value
|
184
|
+
end
|
185
|
+
end
|
186
|
+
|
187
|
+
# Normalize parameter value to 0-1 scale
|
188
|
+
def normalize_parameter_value(parameter, raw_value)
|
189
|
+
case parameter.data_type
|
190
|
+
when "boolean"
|
191
|
+
raw_value ? 1.0 : 0.0
|
192
|
+
when "integer", "decimal"
|
193
|
+
normalize_numeric_value(parameter, raw_value.to_f)
|
194
|
+
when "string"
|
195
|
+
normalize_string_value(parameter, raw_value)
|
196
|
+
else
|
197
|
+
0.5 # Default neutral score
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
def normalize_numeric_value(parameter, value)
|
202
|
+
min_val = parameter.min_value || 0
|
203
|
+
max_val = parameter.max_value || 100
|
204
|
+
|
205
|
+
return 0.0 if value < min_val
|
206
|
+
return 1.0 if value > max_val
|
207
|
+
|
208
|
+
(value - min_val) / (max_val - min_val)
|
209
|
+
end
|
210
|
+
|
211
|
+
def normalize_string_value(parameter, value)
|
212
|
+
normalizer = parameter.parameter_normalizers.find { |n| n.raw_value == value }
|
213
|
+
return normalizer.normalized_value if normalizer
|
214
|
+
|
215
|
+
case value.downcase
|
216
|
+
when "excellent", "high", "good" then 1.0
|
217
|
+
when "fair", "medium", "average" then 0.7
|
218
|
+
when "poor", "low", "bad" then 0.3
|
219
|
+
else 0.5
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
def extract_average_daily_purchase
|
224
|
+
5000.0 # Default average daily purchase
|
225
|
+
end
|
226
|
+
|
227
|
+
def extract_years_at_location
|
228
|
+
2 # Default years at current location
|
229
|
+
end
|
230
|
+
|
231
|
+
def extract_marital_status
|
232
|
+
"single" # Default marital status
|
233
|
+
end
|
234
|
+
|
235
|
+
def extract_dependents_count
|
236
|
+
0 # Default number of dependents
|
237
|
+
end
|
238
|
+
|
239
|
+
def extract_education_level
|
240
|
+
"bachelor" # Default education level
|
241
|
+
end
|
242
|
+
|
243
|
+
def extract_bill_payment_timing
|
244
|
+
"on_time" # Default payment timing
|
245
|
+
end
|
246
|
+
|
247
|
+
def extract_other_accounts
|
248
|
+
false # Default: no other active accounts
|
249
|
+
end
|
250
|
+
|
251
|
+
def extract_money_preference
|
252
|
+
"save" # Default money preference
|
253
|
+
end
|
254
|
+
|
255
|
+
def extract_business_license
|
256
|
+
false # Default: no business license
|
257
|
+
end
|
258
|
+
|
259
|
+
def extract_bank_statements
|
260
|
+
true # Default: bank statements available
|
261
|
+
end
|
262
|
+
|
263
|
+
def extract_transaction_frequency
|
264
|
+
15 # Default monthly transaction frequency
|
265
|
+
end
|
266
|
+
|
267
|
+
def extract_purchase_volume
|
268
|
+
50000.0 # Default monthly purchase volume
|
269
|
+
end
|
270
|
+
|
271
|
+
def extract_cancellation_rate
|
272
|
+
0.05 # Default 5% cancellation rate
|
273
|
+
end
|
274
|
+
|
275
|
+
def extract_reliability_score
|
276
|
+
75.0 # Default reliability score (0-100)
|
277
|
+
end
|
278
|
+
|
279
|
+
def success_result(score, facility_limit)
|
280
|
+
{
|
281
|
+
success: true,
|
282
|
+
score: score.round(2),
|
283
|
+
facility_limit: facility_limit.round(2),
|
284
|
+
message: "Credit score calculated successfully"
|
285
|
+
}
|
286
|
+
end
|
287
|
+
|
288
|
+
def error_result(message)
|
289
|
+
{
|
290
|
+
success: false,
|
291
|
+
score: 0,
|
292
|
+
facility_limit: 0,
|
293
|
+
error: message
|
294
|
+
}
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
data/config/locales/en.yml
CHANGED
@@ -77,6 +77,25 @@ en:
|
|
77
77
|
resubmit: "Failed to resubmit scoring parameter"
|
78
78
|
destroy: "Failed to delete scoring parameter"
|
79
79
|
|
80
|
+
loan_profile:
|
81
|
+
success:
|
82
|
+
index: "Loan profiles retrieved successfully"
|
83
|
+
show: "Loan profile details retrieved successfully"
|
84
|
+
create: "Loan profile created successfully"
|
85
|
+
update: "Loan profile updated successfully"
|
86
|
+
approve: "Loan profile approved successfully"
|
87
|
+
reject: "Loan profile rejected successfully"
|
88
|
+
calculate_score: "Credit score calculated successfully"
|
89
|
+
errors:
|
90
|
+
index: "Failed to retrieve loan profiles"
|
91
|
+
show: "Failed to retrieve loan profile details"
|
92
|
+
create: "Failed to create loan profile"
|
93
|
+
update: "Failed to update loan profile"
|
94
|
+
approve: "Failed to approve loan profile"
|
95
|
+
reject: "Failed to reject loan profile"
|
96
|
+
calculate_score: "Failed to calculate credit score"
|
97
|
+
not_found: "Loan profile not found"
|
98
|
+
|
80
99
|
parameter_normalizer:
|
81
100
|
success:
|
82
101
|
index: "Parameter normalizers retrieved successfully"
|
@@ -157,6 +176,20 @@ en:
|
|
157
176
|
request_modification: "Failed to request modification"
|
158
177
|
resubmit: "Failed to resubmit credit line"
|
159
178
|
|
179
|
+
eligible_credit_line:
|
180
|
+
success:
|
181
|
+
index: "Eligible credit lines retrieved successfully"
|
182
|
+
show: "Eligible credit line details retrieved successfully"
|
183
|
+
create: "Eligible credit line created successfully"
|
184
|
+
update: "Eligible credit line updated successfully"
|
185
|
+
apply_risk: "Risk applied successfully"
|
186
|
+
errors:
|
187
|
+
index: "Failed to retrieve eligible credit lines"
|
188
|
+
show: "Failed to retrieve eligible credit line details"
|
189
|
+
create: "Failed to create eligible credit line"
|
190
|
+
update: "Failed to update eligible credit line"
|
191
|
+
apply_risk: "Failed to apply risk"
|
192
|
+
|
160
193
|
system_config_definition:
|
161
194
|
success:
|
162
195
|
index: "System config definitions retrieved successfully"
|
@@ -225,6 +258,24 @@ en:
|
|
225
258
|
deactivate: "Failed to deactivate scoring table"
|
226
259
|
destroy: "Failed to delete scoring table"
|
227
260
|
|
261
|
+
payment_request:
|
262
|
+
success:
|
263
|
+
index: "Payment requests retrieved successfully"
|
264
|
+
show: "Payment request details retrieved successfully"
|
265
|
+
create: "Payment request created successfully"
|
266
|
+
update: "Payment request updated successfully"
|
267
|
+
approve: "Payment request approved successfully"
|
268
|
+
eligible_credit_lines: "Eligible credit lines retrieved successfully"
|
269
|
+
destroy: "Payment request deleted successfully"
|
270
|
+
errors:
|
271
|
+
index: "Failed to retrieve payment requests"
|
272
|
+
show: "Failed to retrieve payment request details"
|
273
|
+
create: "Failed to create payment request"
|
274
|
+
update: "Failed to update payment request"
|
275
|
+
approve: "Failed to approve payment request"
|
276
|
+
eligible_credit_lines: "Failed to retrieve eligible credit lines"
|
277
|
+
destroy: "Failed to delete payment request"
|
278
|
+
|
228
279
|
# Global messages
|
229
280
|
operations:
|
230
281
|
success:
|
@@ -234,6 +285,34 @@ en:
|
|
234
285
|
failed: "Operation failed"
|
235
286
|
not_found: "Resource not found"
|
236
287
|
|
288
|
+
loan_profile:
|
289
|
+
success:
|
290
|
+
calculate_score: "Credit score calculated successfully"
|
291
|
+
errors:
|
292
|
+
calculate_score: "Failed to calculate credit score"
|
293
|
+
not_found: "Loan profile not found"
|
294
|
+
|
295
|
+
credit_limit_calculation:
|
296
|
+
success:
|
297
|
+
create: "Credit limits calculated successfully"
|
298
|
+
errors:
|
299
|
+
create: "Failed to calculate credit limits"
|
300
|
+
not_found: "Loan profile or category not found"
|
301
|
+
|
302
|
+
disbursement:
|
303
|
+
success:
|
304
|
+
create: "Disbursement processed successfully"
|
305
|
+
errors:
|
306
|
+
create: "Failed to process disbursement"
|
307
|
+
not_found: "Credit line or payment request not found"
|
308
|
+
|
309
|
+
repayment:
|
310
|
+
success:
|
311
|
+
create: "Repayment processed successfully"
|
312
|
+
errors:
|
313
|
+
create: "Failed to process repayment"
|
314
|
+
not_found: "Loan not found"
|
315
|
+
|
237
316
|
errors:
|
238
317
|
validation_failed: "Validation failed"
|
239
318
|
access_denied: "Access denied"
|
data/config/routes.rb
CHANGED
@@ -19,14 +19,19 @@ Dscf::Credit::Engine.routes.draw do
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
resources :loan_profiles
|
22
|
+
resources :loan_profiles do
|
23
23
|
member do
|
24
|
-
|
25
|
-
patch "
|
24
|
+
patch "approve"
|
25
|
+
patch "reject"
|
26
|
+
post "calculate_score"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
resources :payment_requests do
|
30
|
+
member do
|
31
|
+
patch "approve"
|
32
|
+
get "eligible_credit_lines"
|
26
33
|
end
|
27
34
|
end
|
28
|
-
|
29
|
-
resources :payment_requests
|
30
35
|
resources :scoring_parameters do
|
31
36
|
member do
|
32
37
|
patch "approve"
|
@@ -54,6 +59,11 @@ Dscf::Credit::Engine.routes.draw do
|
|
54
59
|
end
|
55
60
|
end
|
56
61
|
resources :credit_line_specs
|
62
|
+
resources :eligible_credit_lines do
|
63
|
+
member do
|
64
|
+
patch "apply_risk", to: "eligible_credit_lines#apply_risk"
|
65
|
+
end
|
66
|
+
end
|
57
67
|
resources :categories
|
58
68
|
resources :scoring_tables do
|
59
69
|
member do
|
@@ -64,4 +74,8 @@ Dscf::Credit::Engine.routes.draw do
|
|
64
74
|
resources :users
|
65
75
|
resources :kyc_reviews, only: [ :index, :show ]
|
66
76
|
resources :bank_staffs
|
77
|
+
|
78
|
+
resources :credit_limit_calculations, only: [ :create ]
|
79
|
+
resources :disbursements, only: [ :create ]
|
80
|
+
resources :repayments, only: [ :create ]
|
67
81
|
end
|
@@ -8,6 +8,7 @@ class CreateDscfCreditCreditLineSpecs < ActiveRecord::Migration[8.0]
|
|
8
8
|
t.decimal :penalty_rate, precision: 5, scale: 4, null: false
|
9
9
|
t.decimal :facilitation_fee_rate, precision: 5, scale: 4, null: false
|
10
10
|
t.decimal :tax_rate, precision: 5, scale: 4, null: false
|
11
|
+
t.decimal :credit_line_multiplier, precision: 5, scale: 2, null: false, default: 30.0
|
11
12
|
t.integer :max_penalty_days, null: false
|
12
13
|
t.integer :loan_duration, null: false
|
13
14
|
t.string :interest_frequency, null: false
|
@@ -7,9 +7,10 @@ class CreateDscfCreditLoanProfiles < ActiveRecord::Migration[8.0]
|
|
7
7
|
t.string :status, default: 'pending'
|
8
8
|
t.decimal :total_amount, precision: 15, scale: 2, default: 0
|
9
9
|
t.decimal :available_amount, precision: 15, scale: 2, default: 0
|
10
|
-
t.references :reviewed_by, polymorphic: true
|
10
|
+
t.references :reviewed_by, polymorphic: true
|
11
11
|
t.datetime :review_date
|
12
12
|
t.jsonb :review_feedback, default: {}
|
13
|
+
t.references :review_branch, null: true, foreign_key: { to_table: :dscf_credit_bank_branches }
|
13
14
|
|
14
15
|
t.timestamps
|
15
16
|
end
|
@@ -2,22 +2,19 @@ class CreateDscfCreditLoanProfileScoringSpecs < ActiveRecord::Migration[8.0]
|
|
2
2
|
def change
|
3
3
|
create_table :dscf_credit_loan_profile_scoring_specs do |t|
|
4
4
|
t.references :loan_profile, null: false, foreign_key: { to_table: :dscf_credit_loan_profiles }
|
5
|
-
t.jsonb :scoring_input_data
|
6
|
-
t.decimal :score, precision: 5, scale: 2
|
5
|
+
t.jsonb :scoring_input_data
|
6
|
+
t.decimal :score, precision: 5, scale: 2
|
7
|
+
t.decimal :total_limit, precision: 15, scale: 2
|
7
8
|
t.boolean :active, default: true
|
8
|
-
t.references :created_by, polymorphic: true
|
9
|
-
t.references :reviewed_by, polymorphic: true, null: false
|
10
|
-
t.datetime :review_date
|
11
|
-
t.jsonb :review_feedback, default: {}
|
9
|
+
t.references :created_by, polymorphic: true
|
12
10
|
|
13
11
|
t.timestamps
|
14
12
|
end
|
15
13
|
|
16
14
|
add_index :dscf_credit_loan_profile_scoring_specs, :score
|
15
|
+
add_index :dscf_credit_loan_profile_scoring_specs, :total_limit
|
17
16
|
add_index :dscf_credit_loan_profile_scoring_specs, :active
|
18
17
|
add_index :dscf_credit_loan_profile_scoring_specs, [ :created_by_type, :created_by_id ]
|
19
|
-
add_index :dscf_credit_loan_profile_scoring_specs, [ :reviewed_by_type, :reviewed_by_id ]
|
20
|
-
add_index :dscf_credit_loan_profile_scoring_specs, :review_date
|
21
18
|
add_index :dscf_credit_loan_profile_scoring_specs, :scoring_input_data, using: :gin
|
22
19
|
add_index :dscf_credit_loan_profile_scoring_specs, [ :loan_profile_id, :active ]
|
23
20
|
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class CreateDscfCreditScoringTables < ActiveRecord::Migration[8.0]
|
2
2
|
def change
|
3
3
|
create_table :dscf_credit_scoring_tables do |t|
|
4
|
-
t.references :
|
4
|
+
t.references :category, null: false, foreign_key: { to_table: :dscf_credit_categories }
|
5
5
|
t.references :scoring_parameter, null: false, foreign_key: { to_table: :dscf_credit_scoring_parameters }
|
6
6
|
t.decimal :weight, precision: 5, scale: 4, null: false
|
7
7
|
t.boolean :active, default: true
|
@@ -13,6 +13,6 @@ class CreateDscfCreditScoringTables < ActiveRecord::Migration[8.0]
|
|
13
13
|
add_index :dscf_credit_scoring_tables, :weight
|
14
14
|
add_index :dscf_credit_scoring_tables, :active
|
15
15
|
add_index :dscf_credit_scoring_tables, [ :created_by_type, :created_by_id ]
|
16
|
-
add_index :dscf_credit_scoring_tables, [ :
|
16
|
+
add_index :dscf_credit_scoring_tables, [ :category_id, :scoring_parameter_id ], unique: true, name: 'index_scoring_tables_on_category_and_scoring_parameter'
|
17
17
|
end
|
18
18
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateDscfCreditEligibleCreditLines < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_credit_eligible_credit_lines do |t|
|
4
|
+
t.references :loan_profile, null: false, foreign_key: { to_table: :dscf_credit_loan_profiles }
|
5
|
+
t.references :credit_line, null: false, foreign_key: { to_table: :dscf_credit_credit_lines }
|
6
|
+
t.decimal :credit_limit, precision: 15, scale: 2, null: false
|
7
|
+
t.decimal :available_limit, precision: 15, scale: 2, null: false
|
8
|
+
t.decimal :risk, precision: 5, scale: 4, null: true
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :dscf_credit_eligible_credit_lines, [ :loan_profile_id, :credit_line_id ]
|
14
|
+
add_index :dscf_credit_eligible_credit_lines, :credit_limit
|
15
|
+
add_index :dscf_credit_eligible_credit_lines, :available_limit
|
16
|
+
add_index :dscf_credit_eligible_credit_lines, :risk
|
17
|
+
end
|
18
|
+
end
|