dscf-credit 0.1.1 → 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.
Files changed (89) 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/concerns/dscf/credit/reviewable.rb +112 -0
  9. data/app/controllers/dscf/credit/categories_controller.rb +6 -5
  10. data/app/controllers/dscf/credit/credit_limit_calculations_controller.rb +50 -0
  11. data/app/controllers/dscf/credit/credit_line_specs_controller.rb +2 -1
  12. data/app/controllers/dscf/credit/credit_lines_controller.rb +11 -38
  13. data/app/controllers/dscf/credit/disbursements_controller.rb +55 -0
  14. data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -0
  15. data/app/controllers/dscf/credit/facilitators_controller.rb +39 -150
  16. data/app/controllers/dscf/credit/loan_profiles_controller.rb +138 -0
  17. data/app/controllers/dscf/credit/payment_requests_controller.rb +54 -5
  18. data/app/controllers/dscf/credit/repayments_controller.rb +53 -0
  19. data/app/controllers/dscf/credit/scoring_param_types_controller.rb +31 -0
  20. data/app/controllers/dscf/credit/scoring_parameters_controller.rb +13 -8
  21. data/app/controllers/dscf/credit/scoring_tables_controller.rb +8 -8
  22. data/app/controllers/dscf/credit/system_configs_controller.rb +10 -7
  23. data/app/models/dscf/credit/bank_branch.rb +2 -1
  24. data/app/models/dscf/credit/category.rb +4 -2
  25. data/app/models/dscf/credit/credit_line.rb +9 -6
  26. data/app/models/dscf/credit/credit_line_spec.rb +3 -3
  27. data/app/models/dscf/credit/eligible_credit_line.rb +28 -0
  28. data/app/models/dscf/credit/facilitator.rb +5 -4
  29. data/app/models/dscf/credit/facilitator_performance.rb +1 -2
  30. data/app/models/dscf/credit/loan_profile.rb +8 -4
  31. data/app/models/dscf/credit/loan_profile_scoring_spec.rb +4 -6
  32. data/app/models/dscf/credit/scoring_param_type.rb +17 -0
  33. data/app/models/dscf/credit/scoring_parameter.rb +8 -7
  34. data/app/models/dscf/credit/scoring_table.rb +4 -4
  35. data/app/models/dscf/credit/system_config.rb +5 -4
  36. data/app/serializers/dscf/credit/bank_branch_serializer.rb +1 -0
  37. data/app/serializers/dscf/credit/category_serializer.rb +3 -1
  38. data/app/serializers/dscf/credit/credit_line_serializer.rb +4 -2
  39. data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +1 -1
  40. data/app/serializers/dscf/credit/daily_routine_transaction_serializer.rb +8 -0
  41. data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +8 -0
  42. data/app/serializers/dscf/credit/facilitator_performance_serializer.rb +1 -1
  43. data/app/serializers/dscf/credit/facilitator_serializer.rb +2 -2
  44. data/app/serializers/dscf/credit/loan_profile_scoring_spec_serializer.rb +8 -0
  45. data/app/serializers/dscf/credit/loan_profile_serializer.rb +15 -0
  46. data/app/serializers/dscf/credit/loan_serializer.rb +12 -0
  47. data/app/serializers/dscf/credit/loan_transaction_serializer.rb +8 -0
  48. data/app/serializers/dscf/credit/payment_request_serializer.rb +10 -0
  49. data/app/serializers/dscf/credit/payment_serializer.rb +8 -0
  50. data/app/serializers/dscf/credit/scoring_param_type_serializer.rb +7 -0
  51. data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +6 -3
  52. data/app/serializers/dscf/credit/scoring_table_serializer.rb +1 -1
  53. data/app/serializers/dscf/credit/system_config_serializer.rb +2 -2
  54. data/app/services/dscf/credit/credit_limit_calculation_service.rb +153 -0
  55. data/app/services/dscf/credit/disbursement_service.rb +180 -0
  56. data/app/services/dscf/credit/facilitator_approval_service.rb +4 -3
  57. data/app/services/dscf/credit/facilitator_creation_service.rb +157 -0
  58. data/app/services/dscf/credit/repayment_service.rb +216 -0
  59. data/app/services/dscf/credit/risk_application_service.rb +27 -0
  60. data/app/services/dscf/credit/scoring_service.rb +297 -0
  61. data/config/locales/en.yml +125 -8
  62. data/config/routes.rb +42 -11
  63. data/db/migrate/20250822091011_create_dscf_credit_categories.rb +2 -0
  64. data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +7 -4
  65. data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +1 -0
  66. data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +5 -2
  67. data/db/migrate/20250822092040_create_dscf_credit_scoring_param_types.rb +12 -0
  68. data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +11 -6
  69. data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +6 -3
  70. data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +5 -7
  71. data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +5 -2
  72. data/db/migrate/20250822092528_create_dscf_credit_facilitator_performances.rb +0 -3
  73. data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +2 -2
  74. data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +18 -0
  75. data/db/seeds.rb +134 -40
  76. data/lib/dscf/credit/version.rb +1 -1
  77. data/spec/factories/dscf/credit/categories.rb +1 -0
  78. data/spec/factories/dscf/credit/credit_line_specs.rb +1 -0
  79. data/spec/factories/dscf/credit/credit_lines.rb +9 -7
  80. data/spec/factories/dscf/credit/eligible_credit_lines.rb +33 -0
  81. data/spec/factories/dscf/credit/facilitator_performances.rb +0 -5
  82. data/spec/factories/dscf/credit/facilitators.rb +6 -1
  83. data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +1 -7
  84. data/spec/factories/dscf/credit/loan_profiles.rb +11 -6
  85. data/spec/factories/dscf/credit/scoring_param_types.rb +31 -0
  86. data/spec/factories/dscf/credit/scoring_parameters.rb +26 -4
  87. data/spec/factories/dscf/credit/scoring_tables.rb +1 -1
  88. data/spec/factories/dscf/credit/system_configs.rb +8 -2
  89. metadata +50 -2
@@ -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
@@ -0,0 +1,31 @@
1
+ module Dscf::Credit
2
+ class ScoringParamTypesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:scoring_param_type).permit(
9
+ :name,
10
+ :description
11
+ )
12
+ end
13
+
14
+ def eager_loaded_associations
15
+ [ :scoring_parameters ]
16
+ end
17
+
18
+ def allowed_order_columns
19
+ %w[id name created_at updated_at]
20
+ end
21
+
22
+ def default_serializer_includes
23
+ {
24
+ index: [],
25
+ show: [ :scoring_parameters ],
26
+ create: [],
27
+ update: [ :scoring_parameters ]
28
+ }
29
+ end
30
+ end
31
+ end
@@ -1,12 +1,13 @@
1
1
  module Dscf::Credit
2
2
  class ScoringParametersController < ApplicationController
3
3
  include Dscf::Core::Common
4
+ include Dscf::Credit::Reviewable
4
5
 
5
6
  def create
6
7
  super do
7
8
  scoring_parameter = @clazz.new(model_params)
8
9
  scoring_parameter.created_by = current_user
9
- scoring_parameter.approved_by = current_user
10
+ scoring_parameter.reviewed_by = current_user
10
11
 
11
12
  scoring_parameter
12
13
  end
@@ -20,30 +21,34 @@ module Dscf::Credit
20
21
  :name,
21
22
  :description,
22
23
  :data_type,
23
- :type,
24
24
  :weight,
25
25
  :min_value,
26
26
  :max_value,
27
27
  :active,
28
28
  :previous_version_id,
29
- :expires_at
29
+ :review_date,
30
+ :source,
31
+ :scoring_param_type_id,
32
+ :document_reference,
33
+ :status,
34
+ :review_feedback
30
35
  )
31
36
  end
32
37
 
33
38
  def eager_loaded_associations
34
- [ :bank, :created_by, :approved_by, :previous_version, :parameter_normalizers ]
39
+ [ :bank, :created_by, :reviewed_by, :scoring_param_type, :previous_version, :parameter_normalizers, :scoring_tables, :categories ]
35
40
  end
36
41
 
37
42
  def allowed_order_columns
38
- %w[id name data_type type weight active expires_at created_at updated_at]
43
+ %w[id name data_type weight active review_date created_at updated_at]
39
44
  end
40
45
 
41
46
  def default_serializer_includes
42
47
  {
43
48
  index: [ :bank ],
44
- show: [ :bank, :created_by, :approved_by, :previous_version, :parameter_normalizers ],
45
- create: [ :bank, :created_by, :approved_by ],
46
- update: [ :bank, :created_by, :approved_by, :parameter_normalizers ]
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 ]
47
52
  }
48
53
  end
49
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
@@ -1,11 +1,12 @@
1
1
  module Dscf::Credit
2
2
  class SystemConfigsController < ApplicationController
3
3
  include Dscf::Core::Common
4
+ include Dscf::Credit::Reviewable
4
5
 
5
6
  def create
6
7
  super do
7
8
  obj = @clazz.new(model_params)
8
- obj.approved_by = current_user
9
+ obj.reviewed_by = current_user
9
10
  obj.last_updated_by = current_user
10
11
  obj
11
12
  end
@@ -25,24 +26,26 @@ module Dscf::Credit
25
26
  params.require(:system_config).permit(
26
27
  :config_definition_id,
27
28
  :config_value,
28
- :status
29
+ :status,
30
+ :review_date,
31
+ :review_feedback
29
32
  )
30
33
  end
31
34
 
32
35
  def eager_loaded_associations
33
- [ :config_definition, :last_updated_by, :approved_by ]
36
+ [ :config_definition, :last_updated_by, :reviewed_by ]
34
37
  end
35
38
 
36
39
  def allowed_order_columns
37
- %w[id config_value status created_at updated_at]
40
+ %w[id config_value status review_date created_at updated_at]
38
41
  end
39
42
 
40
43
  def default_serializer_includes
41
44
  {
42
45
  index: [ :config_definition ],
43
- show: [ :config_definition, :last_updated_by, :approved_by ],
44
- create: [ :config_definition, :last_updated_by, :approved_by ],
45
- update: [ :config_definition, :last_updated_by, :approved_by ]
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 ]
46
49
  }
47
50
  end
48
51
  end
@@ -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 }
@@ -11,11 +13,11 @@ module Dscf::Credit
11
13
  scope :by_type, ->(category_type) { where(type: category_type) }
12
14
 
13
15
  def self.ransackable_attributes(auth_object = nil)
14
- %w[id type name description created_at updated_at]
16
+ %w[id type name description document_reference created_at updated_at]
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
@@ -5,26 +5,29 @@ module Dscf::Credit
5
5
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
6
  belongs_to :category, class_name: "Dscf::Credit::Category", foreign_key: "category_id"
7
7
  belongs_to :created_by, polymorphic: true
8
- belongs_to :approved_by, polymorphic: true, optional: true
8
+ belongs_to :reviewed_by, polymorphic: true, optional: true
9
+
9
10
  has_many :credit_line_specs, class_name: "Dscf::Credit::CreditLineSpec", foreign_key: "credit_line_id", dependent: :destroy
10
11
  has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "credit_line_id", dependent: :destroy
11
- has_many :scoring_tables, class_name: "Dscf::Credit::ScoringTable", foreign_key: "credit_line_id", dependent: :destroy
12
- 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
13
14
 
14
15
  validates :name, presence: true
15
16
  validates :code, uniqueness: { scope: :bank_id }, allow_blank: true
16
- validates :status, inclusion: { in: %w[pending approved active suspended] }
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
+
17
20
 
18
21
  scope :active, -> { where(status: "active") }
19
22
  scope :approved, -> { where(status: "approved") }
20
23
  scope :pending, -> { where(status: "pending") }
21
24
 
22
25
  def self.ransackable_attributes(auth_object = nil)
23
- %w[id name code description status approval_date created_at updated_at]
26
+ %w[id name code description status review_date review_feedback document_reference created_at updated_at]
24
27
  end
25
28
 
26
29
  def self.ransackable_associations(auth_object = nil)
27
- %w[bank category created_by approved_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]
28
31
  end
29
32
  end
30
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
@@ -4,7 +4,7 @@ module Dscf::Credit
4
4
  self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
5
 
6
6
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
7
- belongs_to :kyc_approved_by, polymorphic: true
7
+ belongs_to :kyc_reviewed_by, polymorphic: true
8
8
  if defined?(Dscf::Core::User)
9
9
  belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
10
  end
@@ -12,20 +12,21 @@ module Dscf::Credit
12
12
 
13
13
  validates :name, :type, presence: true
14
14
  validates :type, inclusion: { in: %w[individual corporate agent] }
15
- validates :kyc_status, inclusion: { in: %w[pending approved rejected] }
16
15
  validates :user_id, uniqueness: { scope: :bank_id }
17
16
  validates :total_limit, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
18
17
 
18
+ enum :kyc_status, { pending: "pending", approved: "approved", rejected: "rejected" }
19
+
19
20
  scope :approved, -> { where(kyc_status: "approved") }
20
21
  scope :pending_kyc, -> { where(kyc_status: "pending") }
21
22
  scope :rejected, -> { where(kyc_status: "rejected") }
22
23
 
23
24
  def self.ransackable_attributes(auth_object = nil)
24
- %w[id name type total_limit kyc_status created_at updated_at]
25
+ %w[id name type total_limit kyc_status kyc_review_date review_feedback created_at updated_at]
25
26
  end
26
27
 
27
28
  def self.ransackable_associations(auth_object = nil)
28
- %w[bank user kyc_approved_by facilitator_performances]
29
+ %w[bank user kyc_reviewed_by facilitator_performances]
29
30
  end
30
31
  end
31
32
  end
@@ -11,7 +11,6 @@ module Dscf::Credit
11
11
  validates :score, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
12
12
 
13
13
  scope :requiring_approval, -> { where(approval_required: true) }
14
- scope :expired, -> { where("expires_at < ?", Time.current) }
15
- scope :current, -> { where("expires_at IS NULL OR expires_at > ?", Time.current) }
14
+ scope :current, -> { where("created_at > ?", 30.days.ago) } # Assuming current means recent
16
15
  end
17
16
  end
@@ -3,21 +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 :approved_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] }
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" }
15
18
  validates :total_amount, :available_amount, numericality: { greater_than_or_equal_to: 0 }
16
19
  validate :available_amount_not_greater_than_total_amount
17
20
 
18
21
  scope :pending, -> { where(status: "pending") }
19
22
  scope :approved, -> { where(status: "approved") }
20
23
  scope :rejected, -> { where(status: "rejected") }
24
+ scope :manual_review, -> { where(status: "manual_review") }
21
25
  scope :kyc_approved, -> { where(status: "kyc_approved") }
22
26
  scope :kyc_rejected, -> { where(status: "kyc_rejected") }
23
27
  scope :with_available_amount, -> { where("available_amount > 0") }
@@ -25,11 +29,11 @@ module Dscf::Credit
25
29
  scope :by_available_amount_range, ->(min, max) { where(available_amount: min..max) }
26
30
 
27
31
  def self.ransackable_attributes(auth_object = nil)
28
- %w[id status total_amount available_amount rejection_reason 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]
29
33
  end
30
34
 
31
35
  def self.ransackable_associations(auth_object = nil)
32
- %w[bank user backer approved_by reviewed_by loan_profile_scoring_specs loans]
36
+ %w[bank user backer reviewed_by review_branch loan_profile_scoring_specs loans eligible_credit_lines]
33
37
  end
34
38
 
35
39
  private
@@ -4,20 +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 :approved_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
- scope :expired, -> { where("expires_at < ?", Time.current) }
14
12
 
15
13
  def self.ransackable_attributes(auth_object = nil)
16
- %w[id score active expires_at created_at updated_at]
14
+ %w[id score total_limit active created_at updated_at]
17
15
  end
18
16
 
19
17
  def self.ransackable_associations(auth_object = nil)
20
- %w[loan_profile created_by approved_by]
18
+ %w[loan_profile created_by]
21
19
  end
22
20
  end
23
21
  end
@@ -0,0 +1,17 @@
1
+ module Dscf::Credit
2
+ class ScoringParamType < ApplicationRecord
3
+ self.table_name = "dscf_credit_scoring_param_types"
4
+
5
+ has_many :scoring_parameters, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "scoring_param_type_id", dependent: :restrict_with_error
6
+
7
+ validates :name, presence: true, uniqueness: true
8
+
9
+ def self.ransackable_attributes(auth_object = nil)
10
+ %w[id name description created_at updated_at]
11
+ end
12
+
13
+ def self.ransackable_associations(auth_object = nil)
14
+ %w[scoring_parameters]
15
+ end
16
+ end
17
+ end
@@ -1,30 +1,31 @@
1
1
  module Dscf::Credit
2
2
  class ScoringParameter < ApplicationRecord
3
3
  self.table_name = "dscf_credit_scoring_parameters"
4
- self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
4
 
6
5
  belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
7
6
  belongs_to :created_by, polymorphic: true
8
- belongs_to :approved_by, polymorphic: true
7
+ belongs_to :reviewed_by, polymorphic: true
8
+ belongs_to :scoring_param_type, class_name: "Dscf::Credit::ScoringParamType", foreign_key: "scoring_param_type_id"
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
- validates :name, :data_type, :type, :weight, presence: true
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
- validates :type, inclusion: { in: %w[income credit_history employment_status collateral] }
18
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" }
19
20
 
20
21
  scope :active, -> { where(active: true) }
21
22
 
22
23
  def self.ransackable_attributes(auth_object = nil)
23
- %w[id name description data_type type weight min_value max_value active expires_at created_at updated_at]
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]
24
25
  end
25
26
 
26
27
  def self.ransackable_associations(auth_object = nil)
27
- %w[bank created_by approved_by 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]
28
29
  end
29
30
  end
30
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
@@ -4,10 +4,11 @@ module Dscf::Credit
4
4
 
5
5
  belongs_to :config_definition, class_name: "Dscf::Credit::SystemConfigDefinition", foreign_key: "config_definition_id"
6
6
  belongs_to :last_updated_by, polymorphic: true
7
- belongs_to :approved_by, polymorphic: true
7
+ belongs_to :reviewed_by, polymorphic: true
8
8
 
9
9
  validates :config_value, presence: true
10
- validates :status, inclusion: { in: %w[pending approved rejected] }
10
+ validates :status, inclusion: { in: %w[pending approved rejected modify] }
11
+ enum :status, { pending: "pending", approved: "approved", rejected: "rejected", modify: "modify" }
11
12
  validates :config_definition_id, uniqueness: true
12
13
 
13
14
  scope :pending, -> { where(status: "pending") }
@@ -15,11 +16,11 @@ module Dscf::Credit
15
16
  scope :rejected, -> { where(status: "rejected") }
16
17
 
17
18
  def self.ransackable_attributes(auth_object = nil)
18
- %w[id config_value status created_at updated_at]
19
+ %w[id config_value status review_date review_feedback created_at updated_at]
19
20
  end
20
21
 
21
22
  def self.ransackable_associations(auth_object = nil)
22
- %w[config_definition last_updated_by approved_by]
23
+ %w[config_definition last_updated_by reviewed_by]
23
24
  end
24
25
  end
25
26
  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
@@ -1,7 +1,9 @@
1
1
  module Dscf::Credit
2
2
  class CategorySerializer < ActiveModel::Serializer
3
- attributes :id, :type, :name, :description, :created_at, :updated_at
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
+ has_many :scoring_parameters, serializer: Dscf::Credit::ScoringParameterSerializer
6
8
  end
7
9
  end
@@ -1,12 +1,14 @@
1
1
  module Dscf::Credit
2
2
  class CreditLineSerializer < ActiveModel::Serializer
3
- attributes :id, :name, :code, :description, :status, :approval_date,
3
+ attributes :id, :name, :code, :description, :status, :review_date, :review_feedback, :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 :approved_by, serializer: Dscf::Core::UserSerializer
9
+ belongs_to :reviewed_by, serializer: Dscf::Core::UserSerializer
10
10
  has_many :credit_line_specs, serializer: Dscf::Credit::CreditLineSpecSerializer
11
+ has_many :loans, serializer: Dscf::Credit::LoanSerializer
12
+ has_many :eligible_credit_lines, serializer: Dscf::Credit::EligibleCreditLineSerializer
11
13
  end
12
14
  end
@@ -1,7 +1,7 @@
1
1
  module Dscf::Credit
2
2
  class CreditLineSpecSerializer < ActiveModel::Serializer
3
3
  attributes :id, :min_amount, :max_amount, :interest_rate, :penalty_rate,
4
- :facilitation_fee_rate, :tax_rate, :max_penalty_days,
4
+ :facilitation_fee_rate, :tax_rate, :credit_line_multiplier, :max_penalty_days,
5
5
  :loan_duration, :interest_frequency, :interest_income_tax,
6
6
  :vat, :max_interest_calculation_days, :penalty_frequency,
7
7
  :penalty_income_tax, :active, :created_at, :updated_at