dscf-credit 0.1.2 → 0.1.4

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.
Files changed (65) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/dscf/core/authenticatable.rb +81 -0
  3. data/app/controllers/concerns/dscf/core/common.rb +200 -0
  4. data/app/controllers/concerns/dscf/core/filterable.rb +12 -0
  5. data/app/controllers/concerns/dscf/core/json_response.rb +77 -0
  6. data/app/controllers/concerns/dscf/core/pagination.rb +71 -0
  7. data/app/controllers/concerns/dscf/core/token_authenticatable.rb +53 -0
  8. data/app/controllers/dscf/credit/categories_controller.rb +3 -3
  9. data/app/controllers/dscf/credit/credit_limit_calculations_controller.rb +50 -0
  10. data/app/controllers/dscf/credit/credit_line_specs_controller.rb +2 -1
  11. data/app/controllers/dscf/credit/credit_lines_controller.rb +3 -3
  12. data/app/controllers/dscf/credit/disbursements_controller.rb +55 -0
  13. data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -0
  14. data/app/controllers/dscf/credit/facilitators_controller.rb +2 -1
  15. data/app/controllers/dscf/credit/loan_profiles_controller.rb +138 -0
  16. data/app/controllers/dscf/credit/payment_requests_controller.rb +54 -5
  17. data/app/controllers/dscf/credit/repayments_controller.rb +53 -0
  18. data/app/controllers/dscf/credit/scoring_parameters_controller.rb +3 -3
  19. data/app/controllers/dscf/credit/scoring_tables_controller.rb +8 -8
  20. data/app/controllers/dscf/credit/users_controller.rb +18 -0
  21. data/app/models/dscf/credit/bank_branch.rb +2 -1
  22. data/app/models/dscf/credit/category.rb +3 -1
  23. data/app/models/dscf/credit/credit_line.rb +3 -3
  24. data/app/models/dscf/credit/credit_line_spec.rb +3 -3
  25. data/app/models/dscf/credit/eligible_credit_line.rb +28 -0
  26. data/app/models/dscf/credit/loan_profile.rb +8 -5
  27. data/app/models/dscf/credit/loan_profile_scoring_spec.rb +4 -5
  28. data/app/models/dscf/credit/scoring_parameter.rb +2 -2
  29. data/app/models/dscf/credit/scoring_table.rb +4 -4
  30. data/app/serializers/dscf/credit/bank_branch_serializer.rb +1 -0
  31. data/app/serializers/dscf/credit/category_serializer.rb +2 -0
  32. data/app/serializers/dscf/credit/credit_line_serializer.rb +2 -0
  33. data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +1 -1
  34. data/app/serializers/dscf/credit/daily_routine_transaction_serializer.rb +8 -0
  35. data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +8 -0
  36. data/app/serializers/dscf/credit/loan_profile_scoring_spec_serializer.rb +8 -0
  37. data/app/serializers/dscf/credit/loan_profile_serializer.rb +15 -0
  38. data/app/serializers/dscf/credit/loan_serializer.rb +12 -0
  39. data/app/serializers/dscf/credit/loan_transaction_serializer.rb +8 -0
  40. data/app/serializers/dscf/credit/payment_request_serializer.rb +10 -0
  41. data/app/serializers/dscf/credit/payment_serializer.rb +8 -0
  42. data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +2 -0
  43. data/app/serializers/dscf/credit/scoring_table_serializer.rb +1 -1
  44. data/app/services/dscf/credit/credit_limit_calculation_service.rb +153 -0
  45. data/app/services/dscf/credit/disbursement_service.rb +180 -0
  46. data/app/services/dscf/credit/facilitator_approval_service.rb +3 -4
  47. data/app/services/dscf/credit/repayment_service.rb +216 -0
  48. data/app/services/dscf/credit/risk_application_service.rb +27 -0
  49. data/app/services/dscf/credit/scoring_service.rb +297 -0
  50. data/config/locales/en.yml +91 -0
  51. data/config/routes.rb +19 -7
  52. data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +1 -0
  53. data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +2 -1
  54. data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +5 -8
  55. data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +2 -2
  56. data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +18 -0
  57. data/db/seeds.rb +88 -22
  58. data/lib/dscf/credit/version.rb +1 -1
  59. data/spec/factories/dscf/credit/credit_line_specs.rb +1 -0
  60. data/spec/factories/dscf/credit/credit_lines.rb +3 -3
  61. data/spec/factories/dscf/credit/eligible_credit_lines.rb +33 -0
  62. data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +1 -4
  63. data/spec/factories/dscf/credit/loan_profiles.rb +1 -0
  64. data/spec/factories/dscf/credit/scoring_tables.rb +1 -1
  65. metadata +29 -2
@@ -0,0 +1,50 @@
1
+ module Dscf::Credit
2
+ class EligibleCreditLinesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ before_action :set_object, only: [ :show, :apply_risk ]
6
+
7
+ def apply_risk
8
+ unless params[:risk].present?
9
+ return render_error("eligible_credit_line.errors.apply_risk", errors: [ "Risk parameter is required" ])
10
+ end
11
+
12
+ service = RiskApplicationService.new(@obj, params[:risk])
13
+ result = service.apply_risk
14
+
15
+ if result[:success]
16
+ render_success(data: result[:data].reload)
17
+ else
18
+ render_error(errors: result[:errors])
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def model_params
25
+ params.require(:eligible_credit_line).permit(
26
+ :loan_profile_id,
27
+ :credit_line_id,
28
+ :credit_limit,
29
+ :available_limit
30
+ )
31
+ end
32
+
33
+ def eager_loaded_associations
34
+ [ :loan_profile, :credit_line ]
35
+ end
36
+
37
+ def allowed_order_columns
38
+ %w[id credit_limit available_limit risk created_at updated_at loan_profile_id credit_line_id]
39
+ end
40
+
41
+ def default_serializer_includes
42
+ {
43
+ index: [ :loan_profile, :credit_line ],
44
+ show: [ :loan_profile, :credit_line ],
45
+ create: [ :loan_profile, :credit_line ],
46
+ update: [ :loan_profile, :credit_line ]
47
+ }
48
+ end
49
+ end
50
+ end
@@ -16,7 +16,7 @@ module Dscf::Credit
16
16
 
17
17
  def approve
18
18
  begin
19
- FacilitatorApprovalService.new(@facilitator, current_user).approve(params[:total_limit])
19
+ FacilitatorApprovalService.new(@facilitator, current_user).approve
20
20
  render_success(data: @facilitator)
21
21
  rescue StandardError => e
22
22
  render_error(errors: e.message)
@@ -83,6 +83,7 @@ module Dscf::Credit
83
83
  :user_id,
84
84
  :bank_id,
85
85
  :kyc_status,
86
+ :total_limit,
86
87
  :kyc_review_date,
87
88
  :additional_info,
88
89
  :review_feedback
@@ -0,0 +1,138 @@
1
+ module Dscf::Credit
2
+ class LoanProfilesController < ApplicationController
3
+ include Dscf::Core::Common
4
+ include Dscf::Credit::Reviewable
5
+
6
+ def create
7
+ super do
8
+ loan_profile = @clazz.new(model_params)
9
+
10
+ ActiveRecord::Base.transaction do
11
+ loan_profile.save!
12
+
13
+ if loan_profile.user.present?
14
+ loan_profile.loan_profile_scoring_specs.create!(
15
+ created_by: loan_profile.user
16
+ )
17
+ end
18
+ end
19
+
20
+ loan_profile
21
+ end
22
+ end
23
+
24
+ def calculate_score
25
+ loan_profile = @clazz.find(params[:id])
26
+ scoring_service = Dscf::Credit::ScoringService.new(loan_profile)
27
+
28
+ external_scoring_data = params[:scoring_input_data]
29
+
30
+ result = scoring_service.calculate_credit_score(external_scoring_data)
31
+
32
+ if result[:success]
33
+ ActiveRecord::Base.transaction do
34
+ scoring_spec = if external_scoring_data.present?
35
+ loan_profile.loan_profile_scoring_specs.find_or_initialize_by(
36
+ scoring_input_data: external_scoring_data
37
+ )
38
+ else
39
+ # If no external data, find existing spec with input data or latest one
40
+ loan_profile.loan_profile_scoring_specs
41
+ .where.not(scoring_input_data: [ nil, {} ])
42
+ .order(created_at: :desc)
43
+ .first ||
44
+ loan_profile.loan_profile_scoring_specs.order(created_at: :desc).first ||
45
+ loan_profile.loan_profile_scoring_specs.build
46
+ end
47
+
48
+ loan_profile.loan_profile_scoring_specs.where.not(id: scoring_spec.id).update_all(active: false)
49
+
50
+ scoring_spec.assign_attributes(
51
+ score: result[:score],
52
+ total_limit: result[:facility_limit],
53
+ scoring_input_data: result[:scoring_input_data] || scoring_spec.scoring_input_data || {},
54
+ active: true,
55
+ created_by: scoring_spec.new_record? ? current_user : scoring_spec.created_by
56
+ )
57
+
58
+ scoring_spec.save!
59
+
60
+ update_loan_profile_status(loan_profile, result[:score])
61
+ end
62
+
63
+ loan_profile.reload
64
+
65
+ render_success(
66
+ "loan_profile.success.calculate_score",
67
+ data: {
68
+ loan_profile: loan_profile,
69
+ scoring_result: result
70
+ },
71
+ serializer_options: { include: [ :loan_profile_scoring_specs ] }
72
+ )
73
+ else
74
+ render_error(
75
+ "loan_profile.errors.calculate_score",
76
+ errors: [ result[:error] ],
77
+ status: :unprocessable_entity
78
+ )
79
+ end
80
+ rescue ActiveRecord::RecordNotFound
81
+ render_error(
82
+ "loan_profile.errors.not_found",
83
+ status: :not_found
84
+ )
85
+ end
86
+
87
+ private
88
+
89
+ def update_loan_profile_status(loan_profile, score)
90
+ new_status = case score
91
+ when 0...50
92
+ "rejected"
93
+ when 50...60
94
+ "manual_review"
95
+ else
96
+ "approved"
97
+ end
98
+
99
+ loan_profile.update!(
100
+ status: new_status,
101
+ reviewed_by: current_user,
102
+ review_date: Time.current
103
+ )
104
+ end
105
+
106
+ private
107
+
108
+ def model_params
109
+ params.require(:loan_profile).permit(
110
+ :bank_id,
111
+ :review_branch_id,
112
+ :user_id,
113
+ :backer_id,
114
+ :backer_type,
115
+ :status,
116
+ :total_amount,
117
+ :available_amount
118
+ )
119
+ end
120
+
121
+ def eager_loaded_associations
122
+ [ :bank, :review_branch, :reviewed_by, :user, :backer, :loan_profile_scoring_specs, :loans, :eligible_credit_lines ]
123
+ end
124
+
125
+ def allowed_order_columns
126
+ %w[id status total_amount available_amount review_date created_at updated_at]
127
+ end
128
+
129
+ def default_serializer_includes
130
+ {
131
+ index: [ :bank, :review_branch, :reviewed_by, :user, :backer ],
132
+ show: [ :bank, :review_branch, :reviewed_by, :user, :backer, :loan_profile_scoring_specs, :loans, :eligible_credit_lines ],
133
+ create: [ :bank, :review_branch, :reviewed_by, :user, :backer, :loan_profile_scoring_specs ],
134
+ update: [ :bank, :review_branch, :reviewed_by, :user, :backer, :loan_profile_scoring_specs, :loans, :eligible_credit_lines ]
135
+ }
136
+ end
137
+ end
138
+ end
@@ -2,20 +2,69 @@ module Dscf::Credit
2
2
  class PaymentRequestsController < ApplicationController
3
3
  include Dscf::Core::Common
4
4
 
5
+ def create
6
+ super do
7
+ obj = @clazz.new(model_params)
8
+ obj.request_type = "credit"
9
+ obj.initiated_at = Time.current
10
+ obj
11
+ end
12
+ end
13
+
14
+ def approve
15
+ @obj = @clazz.find(params[:id])
16
+
17
+ if @obj.update(status: "approved", approved_at: Time.current)
18
+ @obj = @clazz.includes(eager_loaded_associations).find(@obj.id) if eager_loaded_associations.present?
19
+ includes = serializer_includes_for_action(:show)
20
+ options = includes.present? ? { include: includes } : {}
21
+ render_success(data: @obj, serializer_options: options)
22
+ else
23
+ render_error(errors: @obj.errors.full_messages[0], status: :unprocessable_entity)
24
+ end
25
+ rescue StandardError => e
26
+ render_error(error: e.message)
27
+ end
28
+
29
+ def eligible_credit_lines
30
+ payment_request = @clazz.find(params[:id])
31
+
32
+ if payment_request.user.nil?
33
+ return render_error(error: "Payment request has no associated user", status: :unprocessable_entity)
34
+ end
35
+
36
+ # Find all eligible credit lines for the user through their approved loan profiles
37
+ # Only include credit lines with available credit
38
+ eligible_credit_lines = Dscf::Credit::EligibleCreditLine
39
+ .joins(loan_profile: :user)
40
+ .where(dscf_credit_loan_profiles: {
41
+ user_id: payment_request.user_id,
42
+ status: [ "approved", "kyc_approved" ] # Only approved loan profiles
43
+ })
44
+ .where("dscf_credit_eligible_credit_lines.available_limit > 0") # Only with available credit
45
+ .includes(:loan_profile, :credit_line)
46
+ .order("dscf_credit_eligible_credit_lines.available_limit DESC") # Order by available limit
47
+
48
+ render_success(
49
+ data: eligible_credit_lines,
50
+ )
51
+ rescue ActiveRecord::RecordNotFound
52
+ render_error(error: "Payment request not found", status: :not_found)
53
+ rescue StandardError => e
54
+ render_error(error: e.message)
55
+ end
56
+
5
57
  private
6
58
 
7
59
  def model_params
8
60
  params.require(:payment_request).permit(
9
61
  :user_id,
10
62
  :order_id,
11
- :request_type,
12
63
  :amount,
13
64
  :receiver_account_reference,
14
65
  :status,
15
- :failure_reason,
16
- :initiated_at,
17
- :approved_at
18
- )
66
+ :failure_reason
67
+ )
19
68
  end
20
69
 
21
70
  def eager_loaded_associations
@@ -0,0 +1,53 @@
1
+ module Dscf::Credit
2
+ class RepaymentsController < ApplicationController
3
+ def create
4
+ loan = Dscf::Credit::Loan.find(params[:loan_id])
5
+ payment_amount = params[:amount].to_f
6
+
7
+ service = Dscf::Credit::RepaymentService.new(loan, payment_amount, current_user)
8
+ result = service.process_repayment
9
+
10
+ if result[:success]
11
+ render_success(
12
+ "repayment.success.create",
13
+ data: {
14
+ loan: result[:loan],
15
+ payment_details: result[:payment_details]
16
+ },
17
+ serializer_options: {
18
+ include: [
19
+ :loan_profile,
20
+ :credit_line,
21
+ :payment_request,
22
+ :loan_transactions
23
+ ]
24
+ }
25
+ )
26
+ else
27
+ render_error(
28
+ "repayment.errors.create",
29
+ errors: [ result[:error] ],
30
+ status: :unprocessable_entity
31
+ )
32
+ end
33
+ rescue ActiveRecord::RecordNotFound => e
34
+ render_error(
35
+ "repayment.errors.not_found",
36
+ errors: [ e.message ],
37
+ status: :not_found
38
+ )
39
+ rescue StandardError => e
40
+ render_error(
41
+ "repayment.errors.create",
42
+ errors: [ e.message ],
43
+ status: :internal_server_error
44
+ )
45
+ end
46
+
47
+ private
48
+
49
+ def model_params
50
+ params.permit(:loan_id, :amount)
51
+ end
52
+ end
53
+ end
@@ -36,7 +36,7 @@ module Dscf::Credit
36
36
  end
37
37
 
38
38
  def eager_loaded_associations
39
- [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers ]
39
+ [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers, :scoring_tables, :categories ]
40
40
  end
41
41
 
42
42
  def allowed_order_columns
@@ -46,9 +46,9 @@ module Dscf::Credit
46
46
  def default_serializer_includes
47
47
  {
48
48
  index: [ :bank ],
49
- show: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers ],
49
+ show: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers, :scoring_tables, :categories ],
50
50
  create: [ :bank, :created_by, :reviewed_by, :scoring_param_type ],
51
- update: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :parameter_normalizers ]
51
+ update: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :parameter_normalizers, :scoring_tables, :categories ]
52
52
  }
53
53
  end
54
54
  end
@@ -13,7 +13,7 @@ module Dscf::Credit
13
13
  def activate
14
14
  @obj = @clazz.find(params[:id])
15
15
  if @obj.update(active: true)
16
- render_success(data: @obj, serializer_options: { include: [ :credit_line, :scoring_parameter, :created_by ] })
16
+ render_success(data: @obj, serializer_options: { include: [ :category, :scoring_parameter, :created_by ] })
17
17
  else
18
18
  render_error(errors: @obj.errors.full_messages[0], status: :unprocessable_entity)
19
19
  end
@@ -24,7 +24,7 @@ module Dscf::Credit
24
24
  def deactivate
25
25
  @obj = @clazz.find(params[:id])
26
26
  if @obj.update(active: false)
27
- render_success(data: @obj, serializer_options: { include: [ :credit_line, :scoring_parameter, :created_by ] })
27
+ render_success(data: @obj, serializer_options: { include: [ :category, :scoring_parameter, :created_by ] })
28
28
  else
29
29
  render_error(errors: @obj.errors.full_messages[0], status: :unprocessable_entity)
30
30
  end
@@ -36,7 +36,7 @@ module Dscf::Credit
36
36
 
37
37
  def model_params
38
38
  params.require(:scoring_table).permit(
39
- :credit_line_id,
39
+ :category_id,
40
40
  :scoring_parameter_id,
41
41
  :weight,
42
42
  :active
@@ -44,7 +44,7 @@ module Dscf::Credit
44
44
  end
45
45
 
46
46
  def eager_loaded_associations
47
- [ :credit_line, :scoring_parameter, :created_by ]
47
+ [ :category, :scoring_parameter, :created_by ]
48
48
  end
49
49
 
50
50
  def allowed_order_columns
@@ -53,10 +53,10 @@ module Dscf::Credit
53
53
 
54
54
  def default_serializer_includes
55
55
  {
56
- index: [ :credit_line, :scoring_parameter, :created_by ],
57
- show: [ :credit_line, :scoring_parameter, :created_by ],
58
- create: [ :credit_line, :scoring_parameter, :created_by ],
59
- update: [ :credit_line, :scoring_parameter, :created_by ]
56
+ index: [ :category, :scoring_parameter, :created_by ],
57
+ show: [ :category, :scoring_parameter, :created_by ],
58
+ create: [ :category, :scoring_parameter, :created_by ],
59
+ update: [ :category, :scoring_parameter, :created_by ]
60
60
  }
61
61
  end
62
62
  end
@@ -25,6 +25,24 @@ module Dscf::Credit
25
25
  end
26
26
  end
27
27
 
28
+ def show
29
+ super do
30
+ has_retailer_business = @obj.businesses.any? do |business|
31
+ business.business_type&.name&.downcase == "retailer"
32
+ end
33
+
34
+ unless has_retailer_business
35
+ return render_error(
36
+ "errors.access_denied",
37
+ errors: "User does not have retailer business type",
38
+ status: :forbidden
39
+ )
40
+ end
41
+
42
+ @obj
43
+ end
44
+ end
45
+
28
46
  private
29
47
 
30
48
  def model_params
@@ -4,6 +4,7 @@ module Dscf::Credit
4
4
 
5
5
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
6
  has_many :bank_staffs, class_name: "Dscf::Credit::BankStaff", foreign_key: "bank_branch_id", dependent: :destroy
7
+ has_many :loan_profiles, class_name: "Dscf::Credit::LoanProfile", foreign_key: "review_branch_id", dependent: :nullify
7
8
 
8
9
  validates :branch_name, presence: true
9
10
  validates :branch_name, uniqueness: { scope: :bank_id }
@@ -17,7 +18,7 @@ module Dscf::Credit
17
18
  end
18
19
 
19
20
  def self.ransackable_associations(auth_object = nil)
20
- %w[bank bank_staffs]
21
+ %w[bank bank_staffs loan_profiles]
21
22
  end
22
23
  end
23
24
  end
@@ -4,6 +4,8 @@ module Dscf::Credit
4
4
  self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
5
 
6
6
  has_many :credit_lines, class_name: "Dscf::Credit::CreditLine", foreign_key: "category_id", dependent: :nullify
7
+ has_many :scoring_tables, class_name: "Dscf::Credit::ScoringTable", foreign_key: "category_id", dependent: :destroy
8
+ has_many :scoring_parameters, through: :scoring_tables
7
9
 
8
10
  validates :type, :name, presence: true
9
11
  validates :name, uniqueness: { scope: :type }
@@ -15,7 +17,7 @@ module Dscf::Credit
15
17
  end
16
18
 
17
19
  def self.ransackable_associations(auth_object = nil)
18
- %w[credit_lines]
20
+ %w[credit_lines scoring_tables scoring_parameters]
19
21
  end
20
22
  end
21
23
  end
@@ -9,8 +9,8 @@ module Dscf::Credit
9
9
 
10
10
  has_many :credit_line_specs, class_name: "Dscf::Credit::CreditLineSpec", foreign_key: "credit_line_id", dependent: :destroy
11
11
  has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "credit_line_id", dependent: :destroy
12
- has_many :scoring_tables, class_name: "Dscf::Credit::ScoringTable", foreign_key: "credit_line_id", dependent: :destroy
13
- has_many :scoring_parameters, through: :scoring_tables
12
+ has_many :eligible_credit_lines, class_name: "Dscf::Credit::EligibleCreditLine", foreign_key: "credit_line_id", dependent: :destroy
13
+ has_many :scoring_parameters, through: :category, source: :scoring_parameters
14
14
 
15
15
  validates :name, presence: true
16
16
  validates :code, uniqueness: { scope: :bank_id }, allow_blank: true
@@ -27,7 +27,7 @@ module Dscf::Credit
27
27
  end
28
28
 
29
29
  def self.ransackable_associations(auth_object = nil)
30
- %w[bank category created_by reviewed_by credit_line_specs loans scoring_tables scoring_parameters]
30
+ %w[bank category created_by reviewed_by credit_line_specs loans eligible_credit_lines scoring_parameters]
31
31
  end
32
32
  end
33
33
  end
@@ -5,8 +5,8 @@ module Dscf::Credit
5
5
  belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
6
6
  belongs_to :created_by, polymorphic: true
7
7
 
8
- validates :min_amount, :max_amount, :interest_rate, :penalty_rate, :facilitation_fee_rate, :tax_rate, :max_penalty_days, :loan_duration, :interest_frequency, :interest_income_tax, :vat, :max_interest_calculation_days, :penalty_frequency, :penalty_income_tax, presence: true
9
- validates :min_amount, :max_amount, numericality: { greater_than: 0 }
8
+ validates :min_amount, :max_amount, :interest_rate, :penalty_rate, :facilitation_fee_rate, :tax_rate, :credit_line_multiplier, :max_penalty_days, :loan_duration, :interest_frequency, :interest_income_tax, :vat, :max_interest_calculation_days, :penalty_frequency, :penalty_income_tax, presence: true
9
+ validates :min_amount, :max_amount, :credit_line_multiplier, numericality: { greater_than: 0 }
10
10
  validates :interest_rate, :penalty_rate, :facilitation_fee_rate, :tax_rate, :interest_income_tax, :vat, :penalty_income_tax, numericality: { greater_than_or_equal_to: 0, less_than: 10 }
11
11
  validates :max_penalty_days, :loan_duration, :max_interest_calculation_days, numericality: { greater_than: 0 }
12
12
  validates :interest_frequency, inclusion: { in: %w[daily weekly monthly quarterly annually] }
@@ -19,7 +19,7 @@ module Dscf::Credit
19
19
  scope :by_penalty_frequency, ->(frequency) { where(penalty_frequency: frequency) }
20
20
 
21
21
  def self.ransackable_attributes(auth_object = nil)
22
- %w[id min_amount max_amount interest_rate penalty_rate facilitation_fee_rate tax_rate max_penalty_days loan_duration interest_frequency interest_income_tax vat max_interest_calculation_days penalty_frequency penalty_income_tax active created_at updated_at]
22
+ %w[id min_amount max_amount interest_rate penalty_rate facilitation_fee_rate tax_rate credit_line_multiplier max_penalty_days loan_duration interest_frequency interest_income_tax vat max_interest_calculation_days penalty_frequency penalty_income_tax active created_at updated_at]
23
23
  end
24
24
 
25
25
  def self.ransackable_associations(auth_object = nil)
@@ -0,0 +1,28 @@
1
+ module Dscf::Credit
2
+ class EligibleCreditLine < ApplicationRecord
3
+ self.table_name = "dscf_credit_eligible_credit_lines"
4
+
5
+ belongs_to :loan_profile, class_name: "Dscf::Credit::LoanProfile", foreign_key: "loan_profile_id"
6
+ belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
7
+
8
+ validates :credit_limit, presence: true
9
+ validates :credit_limit, numericality: { greater_than: 0 }
10
+ validates :available_limit, presence: true
11
+ validates :available_limit, numericality: { greater_than_or_equal_to: 0 }
12
+ validates :risk, numericality: { greater_than_or_equal_to: 0.01, less_than_or_equal_to: 0.9 }, allow_nil: true
13
+
14
+ scope :by_loan_profile, ->(loan_profile_id) { where(loan_profile_id: loan_profile_id) }
15
+ scope :by_credit_line, ->(credit_line_id) { where(credit_line_id: credit_line_id) }
16
+ scope :by_risk_range, ->(min, max) { where(risk: min..max) }
17
+ scope :by_credit_limit_range, ->(min, max) { where(credit_limit: min..max) }
18
+ scope :by_available_limit_range, ->(min, max) { where(available_limit: min..max) }
19
+
20
+ def self.ransackable_attributes(auth_object = nil)
21
+ %w[id credit_limit available_limit risk created_at updated_at]
22
+ end
23
+
24
+ def self.ransackable_associations(auth_object = nil)
25
+ %w[loan_profile credit_line]
26
+ end
27
+ end
28
+ end
@@ -3,22 +3,25 @@ module Dscf::Credit
3
3
  self.table_name = "dscf_credit_loan_profiles"
4
4
 
5
5
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
- belongs_to :reviewed_by, polymorphic: true
6
+ belongs_to :review_branch, class_name: "Dscf::Credit::BankBranch", foreign_key: "review_branch_id", optional: true
7
+ belongs_to :reviewed_by, polymorphic: true, optional: true
7
8
  belongs_to :backer, polymorphic: true, optional: true
8
9
  if defined?(Dscf::Core::User)
9
10
  belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
11
  end
11
12
  has_many :loan_profile_scoring_specs, class_name: "Dscf::Credit::LoanProfileScoringSpec", foreign_key: "loan_profile_id", dependent: :destroy
12
13
  has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "loan_profile_id", dependent: :destroy
14
+ has_many :eligible_credit_lines, class_name: "Dscf::Credit::EligibleCreditLine", foreign_key: "loan_profile_id", dependent: :destroy
13
15
 
14
- validates :status, inclusion: { in: %w[pending approved rejected kyc_approved kyc_rejected] }
15
- enum :status, { pending: "pending", approved: "approved", rejected: "rejected", kyc_approved: "kyc_approved", kyc_rejected: "kyc_rejected" }
16
+ validates :status, inclusion: { in: %w[pending approved rejected manual_review kyc_approved kyc_rejected] }
17
+ enum :status, { pending: "pending", approved: "approved", rejected: "rejected", manual_review: "manual_review", kyc_approved: "kyc_approved", kyc_rejected: "kyc_rejected" }
16
18
  validates :total_amount, :available_amount, numericality: { greater_than_or_equal_to: 0 }
17
19
  validate :available_amount_not_greater_than_total_amount
18
20
 
19
21
  scope :pending, -> { where(status: "pending") }
20
22
  scope :approved, -> { where(status: "approved") }
21
23
  scope :rejected, -> { where(status: "rejected") }
24
+ scope :manual_review, -> { where(status: "manual_review") }
22
25
  scope :kyc_approved, -> { where(status: "kyc_approved") }
23
26
  scope :kyc_rejected, -> { where(status: "kyc_rejected") }
24
27
  scope :with_available_amount, -> { where("available_amount > 0") }
@@ -26,11 +29,11 @@ module Dscf::Credit
26
29
  scope :by_available_amount_range, ->(min, max) { where(available_amount: min..max) }
27
30
 
28
31
  def self.ransackable_attributes(auth_object = nil)
29
- %w[id status total_amount available_amount review_feedback review_date created_at updated_at reviewed_by_type reviewed_by_id]
32
+ %w[id status total_amount available_amount review_feedback review_date created_at updated_at reviewed_by_type reviewed_by_id review_branch_id]
30
33
  end
31
34
 
32
35
  def self.ransackable_associations(auth_object = nil)
33
- %w[bank user backer reviewed_by loan_profile_scoring_specs loans]
36
+ %w[bank user backer reviewed_by review_branch loan_profile_scoring_specs loans eligible_credit_lines]
34
37
  end
35
38
 
36
39
  private
@@ -4,19 +4,18 @@ module Dscf::Credit
4
4
 
5
5
  belongs_to :loan_profile, class_name: "Dscf::Credit::LoanProfile", foreign_key: "loan_profile_id"
6
6
  belongs_to :created_by, polymorphic: true
7
- belongs_to :reviewed_by, polymorphic: true
8
7
 
9
- validates :scoring_input_data, :score, presence: true
10
- validates :score, numericality: { greater_than_or_equal_to: 0 }
8
+ validates :score, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
9
+ validates :total_limit, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
11
10
 
12
11
  scope :active, -> { where(active: true) }
13
12
 
14
13
  def self.ransackable_attributes(auth_object = nil)
15
- %w[id score active review_date review_feedback created_at updated_at]
14
+ %w[id score total_limit active created_at updated_at]
16
15
  end
17
16
 
18
17
  def self.ransackable_associations(auth_object = nil)
19
- %w[loan_profile created_by reviewed_by]
18
+ %w[loan_profile created_by]
20
19
  end
21
20
  end
22
21
  end
@@ -9,7 +9,7 @@ module Dscf::Credit
9
9
  belongs_to :previous_version, class_name: "Dscf::Credit::ScoringParameter", optional: true
10
10
  has_many :parameter_normalizers, class_name: "Dscf::Credit::ParameterNormalizer", foreign_key: "scoring_parameter_id", dependent: :destroy
11
11
  has_many :scoring_tables, class_name: "Dscf::Credit::ScoringTable", foreign_key: "scoring_parameter_id", dependent: :destroy
12
- has_many :credit_lines, through: :scoring_tables
12
+ has_many :categories, through: :scoring_tables
13
13
 
14
14
  validates :name, :data_type, :weight, presence: true
15
15
  validates :weight, numericality: { greater_than: 0, less_than_or_equal_to: 1 }
@@ -25,7 +25,7 @@ module Dscf::Credit
25
25
  end
26
26
 
27
27
  def self.ransackable_associations(auth_object = nil)
28
- %w[bank created_by reviewed_by scoring_param_type previous_version parameter_normalizers scoring_tables credit_lines]
28
+ %w[bank created_by reviewed_by scoring_param_type previous_version parameter_normalizers scoring_tables categories]
29
29
  end
30
30
  end
31
31
  end
@@ -2,15 +2,15 @@ module Dscf::Credit
2
2
  class ScoringTable < ApplicationRecord
3
3
  self.table_name = "dscf_credit_scoring_tables"
4
4
 
5
- belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
5
+ belongs_to :category, class_name: "Dscf::Credit::Category", foreign_key: "category_id"
6
6
  belongs_to :scoring_parameter, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "scoring_parameter_id"
7
7
  belongs_to :created_by, polymorphic: true
8
8
 
9
9
  validates :weight, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 1 }
10
- validates :credit_line_id, uniqueness: { scope: :scoring_parameter_id }
10
+ validates :category_id, uniqueness: { scope: :scoring_parameter_id }
11
11
 
12
12
  scope :active, -> { where(active: true) }
13
- scope :by_credit_line, ->(credit_line_id) { where(credit_line_id: credit_line_id) }
13
+ scope :by_category, ->(category_id) { where(category_id: category_id) }
14
14
  scope :by_scoring_parameter, ->(scoring_parameter_id) { where(scoring_parameter_id: scoring_parameter_id) }
15
15
 
16
16
  def self.ransackable_attributes(auth_object = nil)
@@ -18,7 +18,7 @@ module Dscf::Credit
18
18
  end
19
19
 
20
20
  def self.ransackable_associations(auth_object = nil)
21
- %w[credit_line scoring_parameter created_by]
21
+ %w[category scoring_parameter created_by]
22
22
  end
23
23
  end
24
24
  end
@@ -5,5 +5,6 @@ module Dscf::Credit
5
5
 
6
6
  belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
7
  has_many :bank_staffs, serializer: Dscf::Credit::BankStaffSerializer
8
+ has_many :loan_profiles, serializer: Dscf::Credit::LoanProfileSerializer
8
9
  end
9
10
  end