dscf-credit 0.1.4 → 0.1.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.
Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/dscf/core/copilot-instructions.md +683 -0
  3. data/app/controllers/concerns/dscf/core/reviewable_controller.rb +347 -0
  4. data/app/controllers/dscf/credit/categories_controller.rb +3 -3
  5. data/app/controllers/dscf/credit/credit_lines_controller.rb +21 -13
  6. data/app/controllers/dscf/credit/disbursements_controller.rb +15 -16
  7. data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -8
  8. data/app/controllers/dscf/credit/facilitator_applications_controller.rb +35 -0
  9. data/app/controllers/dscf/credit/facilitators_controller.rb +8 -96
  10. data/app/controllers/dscf/credit/loan_applications_controller.rb +252 -0
  11. data/app/controllers/dscf/credit/loan_profiles_controller.rb +61 -68
  12. data/app/controllers/dscf/credit/loans_controller.rb +7 -7
  13. data/app/controllers/dscf/credit/scoring_parameters_controller.rb +59 -13
  14. data/app/controllers/dscf/credit/system_configs_controller.rb +30 -12
  15. data/app/models/concerns/core/reviewable_model.rb +31 -0
  16. data/app/models/dscf/credit/bank.rb +3 -3
  17. data/app/models/dscf/credit/bank_branch.rb +1 -1
  18. data/app/models/dscf/credit/category.rb +1 -2
  19. data/app/models/dscf/credit/credit_line.rb +4 -10
  20. data/app/models/dscf/credit/eligible_credit_line.rb +2 -2
  21. data/app/models/dscf/credit/facilitator.rb +6 -17
  22. data/app/models/dscf/credit/facilitator_application.rb +20 -0
  23. data/app/models/dscf/credit/loan.rb +4 -4
  24. data/app/models/dscf/credit/loan_application.rb +30 -0
  25. data/app/models/dscf/credit/loan_profile.rb +10 -30
  26. data/app/models/dscf/credit/parameter_normalizer.rb +1 -1
  27. data/app/models/dscf/credit/scoring_parameter.rb +5 -7
  28. data/app/models/dscf/credit/system_config.rb +4 -9
  29. data/app/serializers/dscf/credit/category_serializer.rb +0 -1
  30. data/app/serializers/dscf/credit/credit_line_serializer.rb +2 -2
  31. data/app/serializers/dscf/credit/facilitator_application_serializer.rb +7 -0
  32. data/app/serializers/dscf/credit/facilitator_serializer.rb +3 -6
  33. data/app/serializers/dscf/credit/loan_application_serializer.rb +12 -0
  34. data/app/serializers/dscf/credit/loan_profile_serializer.rb +3 -6
  35. data/app/serializers/dscf/credit/loan_serializer.rb +1 -2
  36. data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +3 -4
  37. data/app/serializers/dscf/credit/system_config_serializer.rb +2 -2
  38. data/app/services/dscf/credit/credit_scoring_engine.rb +258 -0
  39. data/app/services/dscf/credit/disbursement_service.rb +39 -84
  40. data/app/services/dscf/credit/facility_limit_calculation_engine.rb +159 -0
  41. data/app/services/dscf/credit/loan_profile_creation_service.rb +91 -0
  42. data/app/services/dscf/credit/risk_application_service.rb +61 -11
  43. data/config/locales/en.yml +65 -50
  44. data/config/routes.rb +30 -18
  45. data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +1 -8
  46. data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +0 -7
  47. data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +2 -6
  48. data/db/migrate/20250822092225_create_dscf_credit_parameter_normalizers.rb +1 -1
  49. data/db/migrate/20250822092236_create_dscf_credit_loan_applications.rb +20 -0
  50. data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +7 -19
  51. data/db/migrate/20250822092426_create_dscf_credit_facilitator_applications.rb +10 -0
  52. data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +1 -16
  53. data/db/migrate/20250822092654_create_dscf_credit_loans.rb +3 -1
  54. data/db/seeds.rb +321 -290
  55. data/lib/dscf/credit/version.rb +1 -1
  56. data/spec/factories/dscf/credit/banks.rb +1 -1
  57. data/spec/factories/dscf/credit/credit_lines.rb +0 -23
  58. data/spec/factories/dscf/credit/facilitator_applications.rb +37 -0
  59. data/spec/factories/dscf/credit/facilitators.rb +8 -30
  60. data/spec/factories/dscf/credit/loan_applications.rb +42 -0
  61. data/spec/factories/dscf/credit/loan_profiles.rb +20 -34
  62. data/spec/factories/dscf/credit/loans.rb +5 -1
  63. data/spec/factories/dscf/credit/parameter_normalizers.rb +4 -4
  64. data/spec/factories/dscf/credit/scoring_parameters.rb +14 -11
  65. data/spec/factories/dscf/credit/system_configs.rb +21 -5
  66. metadata +20 -20
  67. data/app/controllers/concerns/dscf/credit/reviewable.rb +0 -112
  68. data/app/controllers/dscf/credit/payment_requests_controller.rb +0 -87
  69. data/app/controllers/dscf/credit/payments_controller.rb +0 -36
  70. data/app/controllers/dscf/credit/scoring_tables_controller.rb +0 -63
  71. data/app/models/dscf/credit/payment.rb +0 -22
  72. data/app/models/dscf/credit/payment_request.rb +0 -29
  73. data/app/models/dscf/credit/scoring_table.rb +0 -24
  74. data/app/serializers/dscf/credit/payment_request_serializer.rb +0 -10
  75. data/app/serializers/dscf/credit/payment_serializer.rb +0 -8
  76. data/app/serializers/dscf/credit/scoring_table_serializer.rb +0 -9
  77. data/db/migrate/20250822092608_create_dscf_credit_payment_requests.rb +0 -26
  78. data/db/migrate/20250822092843_create_dscf_credit_payments.rb +0 -23
  79. data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +0 -18
  80. data/spec/factories/dscf/credit/payment_requests.rb +0 -40
  81. data/spec/factories/dscf/credit/payments.rb +0 -39
  82. data/spec/factories/dscf/credit/scoring_tables.rb +0 -25
@@ -1,23 +1,40 @@
1
1
  module Dscf::Credit
2
2
  class ScoringParametersController < ApplicationController
3
3
  include Dscf::Core::Common
4
- include Dscf::Credit::Reviewable
4
+ include Dscf::Core::ReviewableController
5
5
 
6
6
  def create
7
7
  super do
8
8
  scoring_parameter = @clazz.new(model_params)
9
9
  scoring_parameter.created_by = current_user
10
- scoring_parameter.reviewed_by = current_user
10
+ scoring_parameter.reviews.build(context: "default", status: "pending")
11
11
 
12
- scoring_parameter
12
+ if validate_category_weight_limit(scoring_parameter)
13
+ scoring_parameter
14
+ else
15
+ scoring_parameter.errors.add(:weight, "would exceed the total weight limit of 1.0 for this category")
16
+ render_error(errors: scoring_parameter.errors.full_messages, status: :unprocessable_entity)
17
+ return
18
+ end
13
19
  end
14
20
  end
15
21
 
22
+ def update
23
+ unless validate_category_weight_limit(@obj, exclude_current: true)
24
+ @obj.errors.add(:weight, "would exceed the total weight limit of 1.0 for this category")
25
+ render_error(errors: @obj.errors.full_messages, status: :unprocessable_entity)
26
+ return
27
+ end
28
+
29
+ super
30
+ end
31
+
16
32
  private
17
33
 
18
34
  def model_params
19
35
  params.require(:scoring_parameter).permit(
20
36
  :bank_id,
37
+ :category_id,
21
38
  :name,
22
39
  :description,
23
40
  :data_type,
@@ -26,30 +43,59 @@ module Dscf::Credit
26
43
  :max_value,
27
44
  :active,
28
45
  :previous_version_id,
29
- :review_date,
30
46
  :source,
31
47
  :scoring_param_type_id,
32
- :document_reference,
33
- :status,
34
- :review_feedback
48
+ :document_reference
35
49
  )
36
50
  end
37
51
 
38
52
  def eager_loaded_associations
39
- [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers, :scoring_tables, :categories ]
53
+ [
54
+ :bank, :category, :created_by, :scoring_param_type, :previous_version, :parameter_normalizers,
55
+ reviews: { reviewed_by: :user_profile }
56
+ ]
40
57
  end
41
58
 
42
59
  def allowed_order_columns
43
- %w[id name data_type weight active review_date created_at updated_at]
60
+ %w[id name data_type weight active created_at updated_at]
44
61
  end
45
62
 
46
63
  def default_serializer_includes
47
64
  {
48
- index: [ :bank ],
49
- show: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers, :scoring_tables, :categories ],
50
- create: [ :bank, :created_by, :reviewed_by, :scoring_param_type ],
51
- update: [ :bank, :created_by, :reviewed_by, :scoring_param_type, :parameter_normalizers, :scoring_tables, :categories ]
65
+ index: [ :bank, :category, reviews: { reviewed_by: :user_profile } ],
66
+ show: [
67
+ :bank, :category, :created_by, :scoring_param_type, :previous_version, :parameter_normalizers,
68
+ reviews: { reviewed_by: :user_profile }
69
+ ],
70
+ create: [ :bank, :category, :created_by, :scoring_param_type ],
71
+ update: [
72
+ :bank, :category, :created_by, :scoring_param_type, :parameter_normalizers,
73
+ reviews: { reviewed_by: :user_profile }
74
+ ]
52
75
  }
53
76
  end
77
+
78
+ def validate_category_weight_limit(scoring_parameter, exclude_current: false)
79
+ return true unless scoring_parameter.category_id && scoring_parameter.weight
80
+
81
+ # Get existing parameters in the same category for the same bank
82
+ existing_params = ScoringParameter.where(
83
+ bank_id: scoring_parameter.bank_id,
84
+ category_id: scoring_parameter.category_id,
85
+ active: true
86
+ )
87
+
88
+ # Exclude current parameter from the calculation when updating
89
+ if exclude_current && scoring_parameter.persisted?
90
+ existing_params = existing_params.where.not(id: scoring_parameter.id)
91
+ end
92
+
93
+ # Calculate total weight
94
+ current_total_weight = existing_params.sum(:weight)
95
+ new_total_weight = current_total_weight + scoring_parameter.weight.to_f
96
+
97
+ # Allow small floating point tolerance (e.g., 1.0001 should be acceptable)
98
+ new_total_weight <= 1.001
99
+ end
54
100
  end
55
101
  end
@@ -1,13 +1,14 @@
1
1
  module Dscf::Credit
2
2
  class SystemConfigsController < ApplicationController
3
3
  include Dscf::Core::Common
4
- include Dscf::Credit::Reviewable
4
+ include Dscf::Core::ReviewableController
5
5
 
6
6
  def create
7
7
  super do
8
8
  obj = @clazz.new(model_params)
9
- obj.reviewed_by = current_user
10
9
  obj.last_updated_by = current_user
10
+ obj.reviews.build(context: "default", status: "pending")
11
+
11
12
  obj
12
13
  end
13
14
  end
@@ -25,27 +26,44 @@ module Dscf::Credit
25
26
  def model_params
26
27
  params.require(:system_config).permit(
27
28
  :config_definition_id,
28
- :config_value,
29
- :status,
30
- :review_date,
31
- :review_feedback
29
+ :config_value
32
30
  )
33
31
  end
34
32
 
35
33
  def eager_loaded_associations
36
- [ :config_definition, :last_updated_by, :reviewed_by ]
34
+ [
35
+ :config_definition,
36
+ :last_updated_by,
37
+ reviews: { reviewed_by: :user_profile }
38
+ ]
37
39
  end
38
40
 
39
41
  def allowed_order_columns
40
- %w[id config_value status review_date created_at updated_at]
42
+ %w[id config_value created_at updated_at]
41
43
  end
42
44
 
43
45
  def default_serializer_includes
44
46
  {
45
- index: [ :config_definition ],
46
- show: [ :config_definition, :last_updated_by, :reviewed_by ],
47
- create: [ :config_definition, :last_updated_by, :reviewed_by ],
48
- update: [ :config_definition, :last_updated_by, :reviewed_by ]
47
+ index: [
48
+ :config_definition,
49
+ reviews: { reviewed_by: :user_profile }
50
+ ],
51
+ show: [
52
+ :config_definition,
53
+ :last_updated_by,
54
+ reviews: { reviewed_by: :user_profile }
55
+ ],
56
+ create: [
57
+ :config_definition,
58
+ :last_updated_by,
59
+ :reviews
60
+
61
+ ],
62
+ update: [
63
+ :config_definition,
64
+ :last_updated_by,
65
+ reviews: { reviewed_by: :user_profile }
66
+ ]
49
67
  }
50
68
  end
51
69
  end
@@ -0,0 +1,31 @@
1
+ module Dscf
2
+ module Core
3
+ module ReviewableModel
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ has_many :reviews, as: :reviewable, class_name: "Dscf::Core::Review", dependent: :destroy
8
+ end
9
+
10
+ def review_for(context = :default)
11
+ reviews.with_context(context).order(created_at: :desc)
12
+ end
13
+
14
+ def current_review_for(context = :default)
15
+ review_for(context).first
16
+ end
17
+
18
+ def current_status_for(context = :default)
19
+ current_review = current_review_for(context)
20
+ return current_review.status if current_review
21
+
22
+ # Return default initial status for zero-config
23
+ "pending"
24
+ end
25
+
26
+ def build_review_for(context = :default)
27
+ reviews.build(context: context.to_s)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -13,10 +13,10 @@ module Dscf::Credit
13
13
  foreign_key: "bank_id", dependent: :destroy
14
14
  has_many :scoring_parameters, class_name: "Dscf::Credit::ScoringParameter",
15
15
  foreign_key: "bank_id", dependent: :destroy
16
- has_many :loan_profiles, class_name: "Dscf::Credit::LoanProfile",
17
- foreign_key: "bank_id", dependent: :destroy
18
- has_many :facilitators, class_name: "Dscf::Credit::Facilitator",
16
+ has_many :loan_applications, class_name: "Dscf::Credit::LoanApplication",
19
17
  foreign_key: "bank_id", dependent: :destroy
18
+ has_many :facilitators, through: :facilitator_applications, class_name: "Dscf::Credit::Facilitator"
19
+ has_many :facilitator_applications, class_name: "Dscf::Credit::FacilitatorApplication"
20
20
  has_many :bank_staffs, through: :bank_branches, class_name: "Dscf::Credit::BankStaff"
21
21
 
22
22
  validates :name, presence: true
@@ -4,7 +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
+ has_many :loan_applications, class_name: "Dscf::Credit::LoanApplication", foreign_key: "review_branch_id", dependent: :nullify
8
8
 
9
9
  validates :branch_name, presence: true
10
10
  validates :branch_name, uniqueness: { scope: :bank_id }
@@ -4,8 +4,7 @@ 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
+ has_many :scoring_parameters, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "category_id", dependent: :nullify
9
8
 
10
9
  validates :type, :name, presence: true
11
10
  validates :name, uniqueness: { scope: :type }
@@ -2,10 +2,11 @@ module Dscf::Credit
2
2
  class CreditLine < ApplicationRecord
3
3
  self.table_name = "dscf_credit_credit_lines"
4
4
 
5
+ include Dscf::Core::ReviewableModel
6
+
5
7
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
8
  belongs_to :category, class_name: "Dscf::Credit::Category", foreign_key: "category_id"
7
9
  belongs_to :created_by, polymorphic: true
8
- belongs_to :reviewed_by, polymorphic: true, optional: true
9
10
 
10
11
  has_many :credit_line_specs, class_name: "Dscf::Credit::CreditLineSpec", foreign_key: "credit_line_id", dependent: :destroy
11
12
  has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "credit_line_id", dependent: :destroy
@@ -14,20 +15,13 @@ module Dscf::Credit
14
15
 
15
16
  validates :name, presence: true
16
17
  validates :code, uniqueness: { scope: :bank_id }, allow_blank: true
17
- validates :status, inclusion: { in: %w[pending approved active suspended rejected modify] }
18
- enum :status, { pending: "pending", approved: "approved", rejected: "rejected", modify: "modify", active: "active", suspended: "suspended" }
19
-
20
-
21
- scope :active, -> { where(status: "active") }
22
- scope :approved, -> { where(status: "approved") }
23
- scope :pending, -> { where(status: "pending") }
24
18
 
25
19
  def self.ransackable_attributes(auth_object = nil)
26
- %w[id name code description status review_date review_feedback document_reference created_at updated_at]
20
+ %w[id name code description document_reference created_at updated_at]
27
21
  end
28
22
 
29
23
  def self.ransackable_associations(auth_object = nil)
30
- %w[bank category created_by reviewed_by credit_line_specs loans eligible_credit_lines scoring_parameters]
24
+ %w[bank category created_by credit_line_specs loans eligible_credit_lines scoring_parameters reviews]
31
25
  end
32
26
  end
33
27
  end
@@ -6,10 +6,10 @@ module Dscf::Credit
6
6
  belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
7
7
 
8
8
  validates :credit_limit, presence: true
9
- validates :credit_limit, numericality: { greater_than: 0 }
9
+ validates :credit_limit, numericality: { greater_than_or_equal_to: 0 }
10
10
  validates :available_limit, presence: true
11
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
12
+ validates :risk, numericality: { greater_than_or_equal_to: 0.0, less_than_or_equal_to: 0.9 }, allow_nil: true
13
13
 
14
14
  scope :by_loan_profile, ->(loan_profile_id) { where(loan_profile_id: loan_profile_id) }
15
15
  scope :by_credit_line, ->(credit_line_id) { where(credit_line_id: credit_line_id) }
@@ -1,32 +1,21 @@
1
1
  module Dscf::Credit
2
2
  class Facilitator < ApplicationRecord
3
3
  self.table_name = "dscf_credit_facilitators"
4
- self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
4
 
6
- belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
7
- belongs_to :kyc_reviewed_by, polymorphic: true
8
- if defined?(Dscf::Core::User)
9
- belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
- end
5
+ include Dscf::Core::ReviewableModel
6
+
7
+ belongs_to :facilitator_application, class_name: "Dscf::Credit::FacilitatorApplication", foreign_key: "facilitator_application_id"
11
8
  has_many :facilitator_performances, class_name: "Dscf::Credit::FacilitatorPerformance", foreign_key: "facilitator_id", dependent: :destroy
12
9
 
13
- validates :name, :type, presence: true
14
- validates :type, inclusion: { in: %w[individual corporate agent] }
15
- validates :user_id, uniqueness: { scope: :bank_id }
16
10
  validates :total_limit, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
17
-
18
- enum :kyc_status, { pending: "pending", approved: "approved", rejected: "rejected" }
19
-
20
- scope :approved, -> { where(kyc_status: "approved") }
21
- scope :pending_kyc, -> { where(kyc_status: "pending") }
22
- scope :rejected, -> { where(kyc_status: "rejected") }
11
+ validates :facilitator_application_id, uniqueness: true
23
12
 
24
13
  def self.ransackable_attributes(auth_object = nil)
25
- %w[id name type total_limit kyc_status kyc_review_date review_feedback created_at updated_at]
14
+ %w[id total_limit created_at updated_at facilitator_application_id]
26
15
  end
27
16
 
28
17
  def self.ransackable_associations(auth_object = nil)
29
- %w[bank user kyc_reviewed_by facilitator_performances]
18
+ %w[facilitator_application facilitator_performances reviews]
30
19
  end
31
20
  end
32
21
  end
@@ -0,0 +1,20 @@
1
+ module Dscf::Credit
2
+ class FacilitatorApplication < ApplicationRecord
3
+ self.table_name = "dscf_credit_facilitator_applications"
4
+
5
+ include Dscf::Core::ReviewableModel
6
+
7
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
8
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
9
+
10
+ validates :facilitator_info, presence: true
11
+
12
+ def self.ransackable_attributes(auth_object = nil)
13
+ %w[id user_id bank_id created_at updated_at]
14
+ end
15
+
16
+ def self.ransackable_associations(auth_object = nil)
17
+ %w[user bank reviews]
18
+ end
19
+ end
20
+ end
@@ -4,7 +4,6 @@ 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 :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
7
- belongs_to :payment_request, class_name: "Dscf::Credit::PaymentRequest", foreign_key: "payment_request_id"
8
7
  has_many :loan_transactions, class_name: "Dscf::Credit::LoanTransaction", foreign_key: "loan_id", dependent: :destroy
9
8
  has_many :daily_routine_transactions, class_name: "Dscf::Credit::DailyRoutineTransaction", foreign_key: "loan_id", dependent: :destroy
10
9
 
@@ -13,18 +12,19 @@ module Dscf::Credit
13
12
  validates :accrued_interest, :accrued_penalty, :facilitation_fee, numericality: { greater_than_or_equal_to: 0 }
14
13
  validates :status, inclusion: { in: %w[pending approved disbursed active overdue paid closed] }
15
14
 
16
- scope :active, -> { where(status: "active") }
15
+ scope :active, -> { where(active: true) }
16
+ scope :inactive, -> { where(active: false) }
17
17
  scope :overdue, -> { where(status: "overdue") }
18
18
  scope :paid, -> { where(status: "paid") }
19
19
  scope :due_today, -> { where(due_date: Date.current) }
20
20
  scope :past_due, -> { where("due_date < ? AND status IN (?)", Date.current, [ "active", "overdue" ]) }
21
21
 
22
22
  def self.ransackable_attributes(auth_object = nil)
23
- %w[id status principal_amount accrued_interest accrued_penalty facilitation_fee total_loan_amount remaining_amount due_date disbursed_at created_at updated_at]
23
+ %w[id status principal_amount accrued_interest accrued_penalty facilitation_fee total_loan_amount remaining_amount due_date disbursed_at active created_at updated_at]
24
24
  end
25
25
 
26
26
  def self.ransackable_associations(auth_object = nil)
27
- %w[loan_profile credit_line payment_request loan_transactions daily_routine_transactions]
27
+ %w[loan_profile credit_line loan_transactions daily_routine_transactions]
28
28
  end
29
29
 
30
30
  delegate :user, to: :loan_profile, allow_nil: true
@@ -0,0 +1,30 @@
1
+ module Dscf::Credit
2
+ class LoanApplication < ApplicationRecord
3
+ self.table_name = "dscf_credit_loan_applications"
4
+
5
+ include Dscf::Core::ReviewableModel
6
+
7
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
8
+ if defined?(Dscf::Core::User)
9
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
+ end
11
+ belongs_to :backer, polymorphic: true
12
+ belongs_to :review_branch, class_name: "Dscf::Credit::BankBranch", foreign_key: "review_branch_id"
13
+
14
+ has_one :loan_profile, class_name: "Dscf::Credit::LoanProfile", foreign_key: "loan_application_id", dependent: :destroy
15
+
16
+ has_many_attached :bank_statement_attachments
17
+
18
+ validates :bank_statement_source, presence: true, inclusion: { in: %w[internal external] }
19
+ validates :user_info, presence: true
20
+ validates :score, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, allow_nil: true
21
+
22
+ def self.ransackable_attributes(auth_object = nil)
23
+ %w[id bank_statement_source score created_at updated_at]
24
+ end
25
+
26
+ def self.ransackable_associations(auth_object = nil)
27
+ %w[bank user backer review_branch loan_profile reviews]
28
+ end
29
+ end
30
+ end
@@ -2,46 +2,26 @@ module Dscf::Credit
2
2
  class LoanProfile < ApplicationRecord
3
3
  self.table_name = "dscf_credit_loan_profiles"
4
4
 
5
- belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
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
8
- belongs_to :backer, polymorphic: true, optional: true
9
- if defined?(Dscf::Core::User)
10
- belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
11
- end
5
+ include Dscf::Core::ReviewableModel
6
+
7
+ belongs_to :loan_application, class_name: "Dscf::Credit::LoanApplication", foreign_key: "loan_application_id"
12
8
  has_many :loan_profile_scoring_specs, class_name: "Dscf::Credit::LoanProfileScoringSpec", foreign_key: "loan_profile_id", dependent: :destroy
13
9
  has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "loan_profile_id", dependent: :destroy
14
10
  has_many :eligible_credit_lines, class_name: "Dscf::Credit::EligibleCreditLine", foreign_key: "loan_profile_id", dependent: :destroy
15
11
 
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" }
18
- validates :total_amount, :available_amount, numericality: { greater_than_or_equal_to: 0 }
19
- validate :available_amount_not_greater_than_total_amount
12
+ validates :score, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 100 }, allow_nil: true
13
+ validates :total_limit, numericality: { greater_than_or_equal_to: 0 }
14
+ validates :code, presence: true, uniqueness: true, length: { maximum: 8 }, format: { with: /\ABB\d{0,6}\z/, message: "must start with BB followed by up to 6 digits" }
20
15
 
21
- scope :pending, -> { where(status: "pending") }
22
- scope :approved, -> { where(status: "approved") }
23
- scope :rejected, -> { where(status: "rejected") }
24
- scope :manual_review, -> { where(status: "manual_review") }
25
- scope :kyc_approved, -> { where(status: "kyc_approved") }
26
- scope :kyc_rejected, -> { where(status: "kyc_rejected") }
27
- scope :with_available_amount, -> { where("available_amount > 0") }
28
- scope :by_total_amount_range, ->(min, max) { where(total_amount: min..max) }
29
- scope :by_available_amount_range, ->(min, max) { where(available_amount: min..max) }
16
+ scope :with_score_range, ->(min, max) { where(score: min..max) }
17
+ scope :by_total_limit_range, ->(min, max) { where(total_limit: min..max) }
30
18
 
31
19
  def self.ransackable_attributes(auth_object = nil)
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]
20
+ %w[id code score total_limit created_at updated_at loan_application_id]
33
21
  end
34
22
 
35
23
  def self.ransackable_associations(auth_object = nil)
36
- %w[bank user backer reviewed_by review_branch loan_profile_scoring_specs loans eligible_credit_lines]
37
- end
38
-
39
- private
40
-
41
- def available_amount_not_greater_than_total_amount
42
- return unless total_amount && available_amount
43
-
44
- errors.add(:available_amount, "cannot be greater than total amount") if available_amount > total_amount
24
+ %w[loan_application loan_profile_scoring_specs loans eligible_credit_lines reviews]
45
25
  end
46
26
  end
47
27
  end
@@ -6,6 +6,6 @@ module Dscf::Credit
6
6
 
7
7
  validates :name, :raw_value, :normalized_value, presence: true
8
8
  validates :raw_value, uniqueness: { scope: :scoring_parameter_id }
9
- validates :normalized_value, numericality: { greater_than_or_equal_to: 0 }
9
+ validates :normalized_value, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }
10
10
  end
11
11
  end
@@ -2,30 +2,28 @@ module Dscf::Credit
2
2
  class ScoringParameter < ApplicationRecord
3
3
  self.table_name = "dscf_credit_scoring_parameters"
4
4
 
5
+ include Dscf::Core::ReviewableModel
6
+
5
7
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
8
+ belongs_to :category, class_name: "Dscf::Credit::Category", foreign_key: "category_id"
6
9
  belongs_to :created_by, polymorphic: true
7
- belongs_to :reviewed_by, polymorphic: true
8
10
  belongs_to :scoring_param_type, class_name: "Dscf::Credit::ScoringParamType", foreign_key: "scoring_param_type_id"
9
11
  belongs_to :previous_version, class_name: "Dscf::Credit::ScoringParameter", optional: true
10
12
  has_many :parameter_normalizers, class_name: "Dscf::Credit::ParameterNormalizer", foreign_key: "scoring_parameter_id", dependent: :destroy
11
- has_many :scoring_tables, class_name: "Dscf::Credit::ScoringTable", foreign_key: "scoring_parameter_id", dependent: :destroy
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 }
16
16
  validates :data_type, inclusion: { in: %w[integer decimal boolean string] }
17
17
  validates :name, uniqueness: { scope: [ :bank_id, :active ] }
18
- validates :status, inclusion: { in: %w[pending approved rejected modify] }
19
- enum :status, { pending: "pending", approved: "approved", rejected: "rejected", modify: "modify" }
20
18
 
21
19
  scope :active, -> { where(active: true) }
22
20
 
23
21
  def self.ransackable_attributes(auth_object = nil)
24
- %w[id name description data_type weight min_value max_value active status review_date review_feedback source document_reference created_at updated_at]
22
+ %w[id name description data_type weight min_value max_value active source document_reference created_at updated_at]
25
23
  end
26
24
 
27
25
  def self.ransackable_associations(auth_object = nil)
28
- %w[bank created_by reviewed_by scoring_param_type previous_version parameter_normalizers scoring_tables categories]
26
+ %w[bank created_by scoring_param_type previous_version parameter_normalizers categories reviews]
29
27
  end
30
28
  end
31
29
  end
@@ -2,25 +2,20 @@ module Dscf::Credit
2
2
  class SystemConfig < ApplicationRecord
3
3
  self.table_name = "dscf_credit_system_configs"
4
4
 
5
+ include Dscf::Core::ReviewableModel
6
+
5
7
  belongs_to :config_definition, class_name: "Dscf::Credit::SystemConfigDefinition", foreign_key: "config_definition_id"
6
8
  belongs_to :last_updated_by, polymorphic: true
7
- belongs_to :reviewed_by, polymorphic: true
8
9
 
9
10
  validates :config_value, presence: true
10
- validates :status, inclusion: { in: %w[pending approved rejected modify] }
11
- enum :status, { pending: "pending", approved: "approved", rejected: "rejected", modify: "modify" }
12
11
  validates :config_definition_id, uniqueness: true
13
12
 
14
- scope :pending, -> { where(status: "pending") }
15
- scope :approved, -> { where(status: "approved") }
16
- scope :rejected, -> { where(status: "rejected") }
17
-
18
13
  def self.ransackable_attributes(auth_object = nil)
19
- %w[id config_value status review_date review_feedback created_at updated_at]
14
+ %w[id config_value created_at updated_at]
20
15
  end
21
16
 
22
17
  def self.ransackable_associations(auth_object = nil)
23
- %w[config_definition last_updated_by reviewed_by]
18
+ %w[config_definition last_updated_by reviews]
24
19
  end
25
20
  end
26
21
  end
@@ -3,7 +3,6 @@ module Dscf::Credit
3
3
  attributes :id, :type, :name, :description, :document_reference, :created_at, :updated_at
4
4
 
5
5
  has_many :credit_lines, serializer: Dscf::Credit::CreditLineSerializer
6
- has_many :scoring_tables, serializer: Dscf::Credit::ScoringTableSerializer
7
6
  has_many :scoring_parameters, serializer: Dscf::Credit::ScoringParameterSerializer
8
7
  end
9
8
  end
@@ -1,14 +1,14 @@
1
1
  module Dscf::Credit
2
2
  class CreditLineSerializer < ActiveModel::Serializer
3
- attributes :id, :name, :code, :description, :status, :review_date, :review_feedback, :document_reference,
3
+ attributes :id, :name, :code, :description, :document_reference,
4
4
  :created_at, :updated_at
5
5
 
6
6
  belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
7
  belongs_to :category, serializer: Dscf::Credit::CategorySerializer
8
8
  belongs_to :created_by, serializer: Dscf::Core::UserSerializer
9
- belongs_to :reviewed_by, serializer: Dscf::Core::UserSerializer
10
9
  has_many :credit_line_specs, serializer: Dscf::Credit::CreditLineSpecSerializer
11
10
  has_many :loans, serializer: Dscf::Credit::LoanSerializer
12
11
  has_many :eligible_credit_lines, serializer: Dscf::Credit::EligibleCreditLineSerializer
12
+ has_many :reviews, serializer: Dscf::Core::ReviewSerializer
13
13
  end
14
14
  end
@@ -0,0 +1,7 @@
1
+ class Dscf::Credit::FacilitatorApplicationSerializer < ActiveModel::Serializer
2
+ attributes :id, :user_id, :bank_id, :facilitator_info, :created_at, :updated_at
3
+
4
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
5
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
6
+ has_many :reviews, serializer: Dscf::Core::ReviewSerializer
7
+ end
@@ -1,11 +1,8 @@
1
1
  module Dscf::Credit
2
2
  class FacilitatorSerializer < ActiveModel::Serializer
3
- attributes :id, :name, :type, :total_limit, :kyc_status, :kyc_review_date, :review_feedback,
4
- :created_at, :updated_at
3
+ attributes :id, :total_limit, :created_at, :updated_at
5
4
 
6
- belongs_to :user, serializer: Dscf::Core::UserSerializer
7
- belongs_to :bank, serializer: Dscf::Credit::BankSerializer
8
- belongs_to :kyc_reviewed_by, polymorphic: true
9
- has_many :facilitator_performances, serializer: Dscf::Credit::FacilitatorPerformanceSerializer
5
+ belongs_to :facilitator_application, serializer: Dscf::Credit::FacilitatorApplicationSerializer
6
+ has_many :reviews, serializer: Dscf::Core::ReviewSerializer
10
7
  end
11
8
  end
@@ -0,0 +1,12 @@
1
+ module Dscf::Credit
2
+ class LoanApplicationSerializer < ActiveModel::Serializer
3
+ attributes :id, :bank_statement_source, :score, :user_info, :facilitator_info,
4
+ :bank_info, :field_assessment, :created_at, :updated_at
5
+
6
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
8
+ belongs_to :backer, polymorphic: true
9
+ belongs_to :review_branch, serializer: Dscf::Credit::BankBranchSerializer
10
+ has_many :reviews, serializer: Dscf::Core::ReviewSerializer
11
+ end
12
+ end
@@ -1,13 +1,10 @@
1
1
  module Dscf::Credit
2
2
  class LoanProfileSerializer < ActiveModel::Serializer
3
- attributes :id, :status, :total_amount, :available_amount, :review_date, :review_feedback, :created_at, :updated_at
3
+ attributes :id, :code, :score, :total_limit, :created_at, :updated_at
4
4
 
5
- belongs_to :bank, serializer: Dscf::Credit::BankSerializer
6
- belongs_to :review_branch, serializer: Dscf::Credit::BankBranchSerializer
7
- belongs_to :reviewed_by
8
- belongs_to :backer
9
- belongs_to :user, serializer: Dscf::Core::UserSerializer
5
+ belongs_to :loan_application, serializer: Dscf::Credit::LoanApplicationSerializer
10
6
 
7
+ has_many :reviews, serializer: Dscf::Core::ReviewSerializer
11
8
  has_many :loan_profile_scoring_specs, serializer: Dscf::Credit::LoanProfileScoringSpecSerializer
12
9
  has_many :loans, serializer: Dscf::Credit::LoanSerializer
13
10
  has_many :eligible_credit_lines, serializer: Dscf::Credit::EligibleCreditLineSerializer