dscf-credit 0.2.4 → 0.2.6
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/categories_controller.rb +1 -1
- data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +3 -2
- data/app/controllers/dscf/credit/loan_applications_controller.rb +1 -1
- data/app/models/dscf/credit/category.rb +4 -5
- data/app/models/dscf/credit/eligible_credit_line.rb +3 -1
- data/app/serializers/dscf/credit/category_serializer.rb +1 -1
- data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +1 -1
- data/app/services/dscf/credit/credit_scoring_engine.rb +91 -73
- data/app/services/dscf/credit/disbursement_service.rb +10 -0
- data/app/services/dscf/credit/repayment_service.rb +11 -27
- data/db/migrate/20250822091011_create_dscf_credit_categories.rb +3 -3
- data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +1 -0
- data/db/seeds.rb +5 -5
- data/lib/dscf/credit/version.rb +1 -1
- data/spec/factories/dscf/credit/categories.rb +6 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5df5b3471a83e6efbe5e76b6fe3849eddaaae14bf80d833fff1d9cd6acc2b75f
|
4
|
+
data.tar.gz: 96f5816a3eece048974871298895351bb925f1ba75043819759a3b3ddbadad8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 595f5b367c1fef07dc381b5c0e7932a4a7c65cfd1c8b282b9b0ac6bd25ed05da5b76ad7d037f6f0858a3afd86b7a6da89e4285c4089b4f8be0aab9ec0b9f2bff
|
7
|
+
data.tar.gz: fd794ca8544b99ff9277dd5580ea9d36415308f606dca66db785a9b7970500ff52b825c06743c318b05272e9daae2fb70c578f859b5c3b706efc5e9686ddd2bc
|
@@ -68,7 +68,8 @@ module Dscf::Credit
|
|
68
68
|
:credit_line_id,
|
69
69
|
:credit_limit,
|
70
70
|
:available_limit,
|
71
|
-
:risk
|
71
|
+
:risk,
|
72
|
+
:locked
|
72
73
|
)
|
73
74
|
end
|
74
75
|
|
@@ -77,7 +78,7 @@ module Dscf::Credit
|
|
77
78
|
end
|
78
79
|
|
79
80
|
def allowed_order_columns
|
80
|
-
%w[id credit_limit available_limit risk created_at updated_at loan_profile_id credit_line_id]
|
81
|
+
%w[id credit_limit available_limit risk locked created_at updated_at loan_profile_id credit_line_id]
|
81
82
|
end
|
82
83
|
|
83
84
|
def default_serializer_includes
|
@@ -1,18 +1,17 @@
|
|
1
1
|
module Dscf::Credit
|
2
2
|
class Category < ApplicationRecord
|
3
3
|
self.table_name = "dscf_credit_categories"
|
4
|
-
self.inheritance_column = nil # Disable STI since 'type' is a business attribute
|
5
4
|
|
6
5
|
has_many :credit_lines, class_name: "Dscf::Credit::CreditLine", foreign_key: "category_id", dependent: :nullify
|
7
6
|
has_many :scoring_parameters, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "category_id", dependent: :nullify
|
8
7
|
|
9
|
-
validates :
|
10
|
-
validates :name, uniqueness: { scope: :
|
8
|
+
validates :category_type, :name, presence: true
|
9
|
+
validates :name, uniqueness: { scope: :category_type }
|
11
10
|
|
12
|
-
scope :by_type, ->(category_type) { where(
|
11
|
+
scope :by_type, ->(category_type) { where(category_type: category_type) }
|
13
12
|
|
14
13
|
def self.ransackable_attributes(auth_object = nil)
|
15
|
-
%w[id
|
14
|
+
%w[id category_type name description document_reference created_at updated_at]
|
16
15
|
end
|
17
16
|
|
18
17
|
def self.ransackable_associations(auth_object = nil)
|
@@ -16,9 +16,11 @@ module Dscf::Credit
|
|
16
16
|
scope :by_risk_range, ->(min, max) { where(risk: min..max) }
|
17
17
|
scope :by_credit_limit_range, ->(min, max) { where(credit_limit: min..max) }
|
18
18
|
scope :by_available_limit_range, ->(min, max) { where(available_limit: min..max) }
|
19
|
+
scope :locked, -> { where(locked: true) }
|
20
|
+
scope :unlocked, -> { where(locked: false) }
|
19
21
|
|
20
22
|
def self.ransackable_attributes(auth_object = nil)
|
21
|
-
%w[id credit_limit available_limit risk created_at updated_at]
|
23
|
+
%w[id credit_limit available_limit risk locked created_at updated_at]
|
22
24
|
end
|
23
25
|
|
24
26
|
def self.ransackable_associations(auth_object = nil)
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Dscf::Credit
|
2
2
|
class CategorySerializer < ActiveModel::Serializer
|
3
|
-
attributes :id, :
|
3
|
+
attributes :id, :category_type, :name, :description, :document_reference, :created_at, :updated_at
|
4
4
|
|
5
5
|
has_many :credit_lines, serializer: Dscf::Credit::CreditLineSerializer
|
6
6
|
has_many :scoring_parameters, serializer: Dscf::Credit::ScoringParameterSerializer
|
@@ -1,6 +1,6 @@
|
|
1
1
|
module Dscf::Credit
|
2
2
|
class EligibleCreditLineSerializer < ActiveModel::Serializer
|
3
|
-
attributes :id, :credit_limit, :available_limit, :risk, :created_at, :updated_at
|
3
|
+
attributes :id, :credit_limit, :available_limit, :risk, :locked, :created_at, :updated_at
|
4
4
|
|
5
5
|
belongs_to :loan_profile, serializer: Dscf::Credit::LoanProfileSerializer
|
6
6
|
belongs_to :credit_line, serializer: Dscf::Credit::CreditLineSerializer
|
@@ -46,7 +46,7 @@ module Dscf::Credit
|
|
46
46
|
.scoring_parameters
|
47
47
|
.active
|
48
48
|
.where(scoring_param_type_id: scoring_param_type_id)
|
49
|
-
.includes(:scoring_param_type, :
|
49
|
+
.includes(:scoring_param_type, :category)
|
50
50
|
end
|
51
51
|
|
52
52
|
def calculate_weighted_score(scoring_parameters)
|
@@ -54,13 +54,15 @@ module Dscf::Credit
|
|
54
54
|
total_weight = 0.0
|
55
55
|
score_breakdown = []
|
56
56
|
parameters_processed = 0
|
57
|
-
|
57
|
+
validation_errors = []
|
58
58
|
|
59
59
|
scoring_parameters.each do |parameter|
|
60
60
|
raw_value = extract_parameter_value(parameter)
|
61
61
|
|
62
62
|
if raw_value.nil?
|
63
|
-
|
63
|
+
error_msg = "Missing required parameter: #{parameter.name} (ID: #{parameter.id})"
|
64
|
+
validation_errors << error_msg
|
65
|
+
Rails.logger.error "Credit scoring validation failed: #{error_msg}"
|
64
66
|
score_breakdown << {
|
65
67
|
parameter_id: parameter.id,
|
66
68
|
parameter_name: parameter.name,
|
@@ -69,12 +71,54 @@ module Dscf::Credit
|
|
69
71
|
normalized_value: nil,
|
70
72
|
weight: parameter.weight,
|
71
73
|
weighted_score: 0.0,
|
72
|
-
status: "missing"
|
74
|
+
status: "missing",
|
75
|
+
error: "Parameter value is required"
|
76
|
+
}
|
77
|
+
next
|
78
|
+
end
|
79
|
+
|
80
|
+
unless raw_value.is_a?(Numeric)
|
81
|
+
begin
|
82
|
+
Float(raw_value)
|
83
|
+
rescue ArgumentError, TypeError
|
84
|
+
error_msg = "Invalid data type for parameter: #{parameter.name} (ID: #{parameter.id}). Expected numeric, got #{raw_value.class}"
|
85
|
+
validation_errors << error_msg
|
86
|
+
Rails.logger.error "Credit scoring validation failed: #{error_msg}"
|
87
|
+
score_breakdown << {
|
88
|
+
parameter_id: parameter.id,
|
89
|
+
parameter_name: parameter.name,
|
90
|
+
parameter_type: parameter.scoring_param_type.name,
|
91
|
+
raw_value: raw_value,
|
92
|
+
normalized_value: nil,
|
93
|
+
weight: parameter.weight,
|
94
|
+
weighted_score: 0.0,
|
95
|
+
status: "invalid",
|
96
|
+
error: "Value must be numeric"
|
97
|
+
}
|
98
|
+
next
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
normalized_value = raw_value.to_f
|
103
|
+
|
104
|
+
unless normalized_value.between?(0.0, 1.0)
|
105
|
+
error_msg = "Invalid normalized value for parameter: #{parameter.name} (ID: #{parameter.id}). Value: #{raw_value}, Expected: 0.0-1.0"
|
106
|
+
validation_errors << error_msg
|
107
|
+
Rails.logger.error "Credit scoring validation failed: #{error_msg}"
|
108
|
+
score_breakdown << {
|
109
|
+
parameter_id: parameter.id,
|
110
|
+
parameter_name: parameter.name,
|
111
|
+
parameter_type: parameter.scoring_param_type.name,
|
112
|
+
raw_value: raw_value,
|
113
|
+
normalized_value: normalized_value,
|
114
|
+
weight: parameter.weight,
|
115
|
+
weighted_score: 0.0,
|
116
|
+
status: "invalid",
|
117
|
+
error: "Value must be between 0.0 and 1.0"
|
73
118
|
}
|
74
119
|
next
|
75
120
|
end
|
76
121
|
|
77
|
-
normalized_value = normalize_parameter_value(parameter, raw_value)
|
78
122
|
weighted_score = normalized_value * parameter.weight
|
79
123
|
|
80
124
|
total_weighted_score += weighted_score
|
@@ -93,15 +137,38 @@ module Dscf::Credit
|
|
93
137
|
}
|
94
138
|
end
|
95
139
|
|
140
|
+
if validation_errors.any?
|
141
|
+
return {
|
142
|
+
success: false,
|
143
|
+
error: "Validation failed: #{validation_errors.size} parameter(s) failed validation",
|
144
|
+
validation_errors: validation_errors,
|
145
|
+
parameters_processed: parameters_processed,
|
146
|
+
parameters_failed: validation_errors.size,
|
147
|
+
breakdown: score_breakdown
|
148
|
+
}
|
149
|
+
end
|
150
|
+
|
151
|
+
if parameters_processed == 0
|
152
|
+
return {
|
153
|
+
success: false,
|
154
|
+
error: "No valid parameters available for scoring",
|
155
|
+
validation_errors: [ "All parameters failed validation or are missing" ],
|
156
|
+
parameters_processed: 0,
|
157
|
+
parameters_failed: scoring_parameters.size,
|
158
|
+
breakdown: score_breakdown
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
96
162
|
# Calculate final score using the formula: (sum of normalized_value × weight) / total_weight × 100
|
97
163
|
final_score = total_weight > 0 ? (total_weighted_score / total_weight * 100) : 0.0
|
98
164
|
|
99
165
|
{
|
166
|
+
success: true,
|
100
167
|
score: final_score.round(2),
|
101
168
|
total_weighted_score: total_weighted_score.round(4),
|
102
169
|
total_weight: total_weight.round(4),
|
103
170
|
parameters_processed: parameters_processed,
|
104
|
-
|
171
|
+
parameters_failed: 0,
|
105
172
|
breakdown: score_breakdown
|
106
173
|
}
|
107
174
|
end
|
@@ -148,74 +215,24 @@ module Dscf::Credit
|
|
148
215
|
raw_value
|
149
216
|
end
|
150
217
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
0.0 # Return 0 for any normalization errors
|
168
|
-
end
|
169
|
-
|
170
|
-
def find_normalizer(parameter, raw_value)
|
171
|
-
exact_match = parameter.parameter_normalizers.find { |normalizer| normalizer.raw_value == raw_value.to_s }
|
172
|
-
return exact_match if exact_match
|
173
|
-
|
174
|
-
if parameter.data_type.in?([ "integer", "decimal" ])
|
175
|
-
numeric_value = parse_numeric_value(raw_value)
|
176
|
-
return nil unless numeric_value
|
177
|
-
|
178
|
-
threshold_normalizer = parameter.parameter_normalizers.find do |normalizer|
|
179
|
-
evaluate_numeric_threshold(normalizer.raw_value, numeric_value)
|
180
|
-
end
|
181
|
-
|
182
|
-
return threshold_normalizer if threshold_normalizer
|
183
|
-
end
|
184
|
-
|
185
|
-
nil
|
186
|
-
end
|
187
|
-
|
188
|
-
def parse_numeric_value(raw_value)
|
189
|
-
return raw_value if raw_value.is_a?(Numeric)
|
190
|
-
|
191
|
-
begin
|
192
|
-
Float(raw_value.to_s)
|
193
|
-
rescue ArgumentError, TypeError
|
194
|
-
nil
|
195
|
-
end
|
196
|
-
end
|
197
|
-
|
198
|
-
def evaluate_numeric_threshold(threshold_expression, numeric_value)
|
199
|
-
# Handle threshold expressions like ">=10000", "<5000", etc.
|
200
|
-
case threshold_expression
|
201
|
-
when /^>=(.+)$/
|
202
|
-
numeric_value >= Float($1)
|
203
|
-
when /^>(.+)$/
|
204
|
-
numeric_value > Float($1)
|
205
|
-
when /^<=(.+)$/
|
206
|
-
numeric_value <= Float($1)
|
207
|
-
when /^<(.+)$/
|
208
|
-
numeric_value < Float($1)
|
209
|
-
when /^=(.+)$/, /^(.+)$/ # Exact match or just the number
|
210
|
-
numeric_value == Float($1)
|
211
|
-
else
|
212
|
-
false
|
218
|
+
def success_result(score_result)
|
219
|
+
if score_result[:success] == false
|
220
|
+
return {
|
221
|
+
success: false,
|
222
|
+
loan_application_id: loan_application.id,
|
223
|
+
bank_id: loan_application.bank_id,
|
224
|
+
scoring_param_type_id: scoring_param_type_id,
|
225
|
+
calculated_at: Time.current,
|
226
|
+
error: score_result[:error],
|
227
|
+
validation_errors: score_result[:validation_errors],
|
228
|
+
parameters_processed: score_result[:parameters_processed],
|
229
|
+
parameters_failed: score_result[:parameters_failed],
|
230
|
+
breakdown: score_result[:breakdown],
|
231
|
+
score: 0.0,
|
232
|
+
status: "rejected"
|
233
|
+
}
|
213
234
|
end
|
214
|
-
rescue ArgumentError, TypeError
|
215
|
-
false
|
216
|
-
end
|
217
235
|
|
218
|
-
def success_result(score_result)
|
219
236
|
final_score = score_result[:score]
|
220
237
|
review_status = determine_review_status(final_score)
|
221
238
|
|
@@ -230,7 +247,8 @@ module Dscf::Credit
|
|
230
247
|
total_weighted_score: score_result[:total_weighted_score],
|
231
248
|
total_weight: score_result[:total_weight],
|
232
249
|
parameters_processed: score_result[:parameters_processed],
|
233
|
-
parameters_skipped:
|
250
|
+
parameters_skipped: 0,
|
251
|
+
parameters_failed: score_result[:parameters_failed],
|
234
252
|
breakdown: score_result[:breakdown],
|
235
253
|
message: "Credit score calculated successfully"
|
236
254
|
}
|
@@ -30,6 +30,7 @@ module Dscf::Credit
|
|
30
30
|
ActiveRecord::Base.transaction do
|
31
31
|
loan = create_loan_record(credit_line)
|
32
32
|
update_credit_line_limits(loan)
|
33
|
+
lock_other_credit_lines(loan_profile, credit_line)
|
33
34
|
|
34
35
|
success_result(loan)
|
35
36
|
end
|
@@ -37,6 +38,15 @@ module Dscf::Credit
|
|
37
38
|
error_result("Disbursement processing failed: #{e.message}")
|
38
39
|
end
|
39
40
|
|
41
|
+
# Lock other eligible credit lines for the loan profile except the specified credit line
|
42
|
+
# @param loan_profile [Dscf::Credit::LoanProfile] The loan profile
|
43
|
+
# @param credit_line [Dscf::Credit::CreditLine] The credit line to exclude from locking
|
44
|
+
def lock_other_credit_lines(loan_profile, credit_line)
|
45
|
+
loan_profile.eligible_credit_lines
|
46
|
+
.where.not(credit_line: credit_line)
|
47
|
+
.update_all(locked: true)
|
48
|
+
end
|
49
|
+
|
40
50
|
private
|
41
51
|
|
42
52
|
def validate_disbursement_amount
|
@@ -250,7 +250,7 @@ module Dscf::Credit
|
|
250
250
|
(loan.remaining_amount || 0)
|
251
251
|
|
252
252
|
if total_remaining <= 0.01
|
253
|
-
loan.update!(status: "paid")
|
253
|
+
loan.update!(status: "paid", active: false)
|
254
254
|
else
|
255
255
|
loan.update!(status: "active") if loan.status == "overdue"
|
256
256
|
end
|
@@ -261,46 +261,30 @@ module Dscf::Credit
|
|
261
261
|
# Only runs if loan.status == "paid"
|
262
262
|
#
|
263
263
|
# Process:
|
264
|
-
# 1. Find all eligible credit lines
|
264
|
+
# 1. Find all locked eligible credit lines (locked during disbursement)
|
265
265
|
# 2. For each locked line, recalculate available_limit based on:
|
266
266
|
# - Credit limit (total allowed)
|
267
267
|
# - Minus current usage from other active loans
|
268
|
+
# 3. Unlock the credit line by setting locked: false
|
268
269
|
#
|
269
270
|
# This allows the borrower to access credit again after repayment.
|
270
271
|
#
|
271
272
|
# @return [void]
|
272
273
|
#
|
273
274
|
# @example
|
274
|
-
# # Before: eligible_line.
|
275
|
-
# # After payment: eligible_line.
|
275
|
+
# # Before: eligible_line.locked = true
|
276
|
+
# # After payment: eligible_line.locked = false
|
276
277
|
def reactivate_facilities_if_paid_off
|
277
278
|
return unless loan.status == "paid"
|
278
279
|
|
279
280
|
loan_profile = loan.loan_profile
|
281
|
+
credit_line = loan.credit_line
|
280
282
|
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
current_usage = calculate_current_usage_for_credit_line(eligible_line.credit_line)
|
287
|
-
new_available_limit = [ eligible_line.credit_limit - current_usage, 0 ].max
|
288
|
-
|
289
|
-
eligible_line.update!(available_limit: new_available_limit)
|
290
|
-
end
|
291
|
-
end
|
292
|
-
|
293
|
-
# Calculate total current usage for a credit line from all active loans
|
294
|
-
#
|
295
|
-
# Excludes the current loan (the one being paid off) from calculation.
|
296
|
-
#
|
297
|
-
# @param credit_line [Dscf::Credit::CreditLine] The credit line to check
|
298
|
-
# @return [Float] Sum of remaining_amount from active loans
|
299
|
-
def calculate_current_usage_for_credit_line(credit_line)
|
300
|
-
credit_line.loans
|
301
|
-
.where(status: [ "active", "overdue", "disbursed" ])
|
302
|
-
.where.not(id: loan.id)
|
303
|
-
.sum(:remaining_amount)
|
283
|
+
# Unlock other eligible credit lines that were locked during disbursement
|
284
|
+
loan_profile.eligible_credit_lines
|
285
|
+
.where.not(credit_line: credit_line)
|
286
|
+
.where(locked: true)
|
287
|
+
.update_all(locked: false)
|
304
288
|
end
|
305
289
|
|
306
290
|
# Build success result hash
|
@@ -1,7 +1,7 @@
|
|
1
1
|
class CreateDscfCreditCategories < ActiveRecord::Migration[8.0]
|
2
2
|
def change
|
3
3
|
create_table :dscf_credit_categories do |t|
|
4
|
-
t.string :
|
4
|
+
t.string :category_type, null: false
|
5
5
|
t.string :name, null: false
|
6
6
|
t.text :description
|
7
7
|
t.string :document_reference
|
@@ -9,9 +9,9 @@ class CreateDscfCreditCategories < ActiveRecord::Migration[8.0]
|
|
9
9
|
t.timestamps
|
10
10
|
end
|
11
11
|
|
12
|
-
add_index :dscf_credit_categories, :
|
12
|
+
add_index :dscf_credit_categories, :category_type
|
13
13
|
add_index :dscf_credit_categories, :name
|
14
14
|
add_index :dscf_credit_categories, :document_reference
|
15
|
-
add_index :dscf_credit_categories, [ :
|
15
|
+
add_index :dscf_credit_categories, [ :category_type, :name ], unique: true
|
16
16
|
end
|
17
17
|
end
|
@@ -6,6 +6,7 @@ class CreateDscfCreditEligibleCreditLines < ActiveRecord::Migration[8.0]
|
|
6
6
|
t.decimal :credit_limit, precision: 15, scale: 2, null: false
|
7
7
|
t.decimal :available_limit, precision: 15, scale: 2, null: false
|
8
8
|
t.decimal :risk, precision: 5, scale: 4, null: true
|
9
|
+
t.boolean :locked, default: false, null: false
|
9
10
|
|
10
11
|
t.timestamps
|
11
12
|
end
|
data/db/seeds.rb
CHANGED
@@ -340,24 +340,24 @@ end
|
|
340
340
|
|
341
341
|
# 4.5. Categories (independent)
|
342
342
|
puts "Seeding categories..."
|
343
|
-
sme_category = Dscf::Credit::Category.find_or_create_by(
|
343
|
+
sme_category = Dscf::Credit::Category.find_or_create_by(category_type: 'credit_line', name: 'SME') do |category|
|
344
344
|
category.description = 'Small and Medium Enterprise credit category'
|
345
345
|
end
|
346
346
|
|
347
|
-
personal_category = Dscf::Credit::Category.find_or_create_by(
|
347
|
+
personal_category = Dscf::Credit::Category.find_or_create_by(category_type: 'credit_line', name: 'Personal') do |category|
|
348
348
|
category.description = 'Personal credit category for individuals'
|
349
349
|
end
|
350
350
|
|
351
|
-
agricultural_category = Dscf::Credit::Category.find_or_create_by(
|
351
|
+
agricultural_category = Dscf::Credit::Category.find_or_create_by(category_type: 'credit_line', name: 'Agricultural') do |category|
|
352
352
|
category.description = 'Agricultural sector credit category'
|
353
353
|
end
|
354
354
|
|
355
|
-
corporate_category = Dscf::Credit::Category.find_or_create_by(
|
355
|
+
corporate_category = Dscf::Credit::Category.find_or_create_by(category_type: 'credit_line', name: 'Corporate') do |category|
|
356
356
|
category.description = 'Corporate credit category for large businesses'
|
357
357
|
end
|
358
358
|
|
359
359
|
# Add eligibility category for scoring
|
360
|
-
eligibility_category = Dscf::Credit::Category.find_or_create_by(
|
360
|
+
eligibility_category = Dscf::Credit::Category.find_or_create_by(category_type: 'eligibility', name: 'default') do |category|
|
361
361
|
category.description = 'Default eligibility category for credit scoring'
|
362
362
|
end
|
363
363
|
|
data/lib/dscf/credit/version.rb
CHANGED
@@ -1,27 +1,27 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :category, class: "Dscf::Credit::Category" do
|
3
|
-
|
4
|
-
sequence(:name) { |n| "#{
|
3
|
+
category_type { %w[scoring loan_profile payment risk_assessment].sample }
|
4
|
+
sequence(:name) { |n| "#{category_type.humanize} Category #{n}" }
|
5
5
|
description { Faker::Lorem.paragraph }
|
6
6
|
document_reference { Faker::Alphanumeric.alphanumeric(number: 10).upcase }
|
7
7
|
|
8
8
|
trait :scoring do
|
9
|
-
|
9
|
+
category_type { "scoring" }
|
10
10
|
name { "Scoring Category" }
|
11
11
|
end
|
12
12
|
|
13
13
|
trait :loan_profile do
|
14
|
-
|
14
|
+
category_type { "loan_profile" }
|
15
15
|
name { "Loan Profile Category" }
|
16
16
|
end
|
17
17
|
|
18
18
|
trait :payment do
|
19
|
-
|
19
|
+
category_type { "payment" }
|
20
20
|
name { "Payment Category" }
|
21
21
|
end
|
22
22
|
|
23
23
|
trait :risk_assessment do
|
24
|
-
|
24
|
+
category_type { "risk_assessment" }
|
25
25
|
name { "Risk Assessment Category" }
|
26
26
|
end
|
27
27
|
end
|
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.2.
|
4
|
+
version: 0.2.6
|
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-08 00:00:00.000000000 Z
|
11
11
|
dependencies:
|
12
12
|
- !ruby/object:Gem::Dependency
|
13
13
|
name: dscf-core
|