dscf-credit 0.1.0

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 (135) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +28 -0
  4. data/Rakefile +8 -0
  5. data/app/controllers/dscf/credit/application_controller.rb +8 -0
  6. data/app/controllers/dscf/credit/bank_branches_controller.rb +38 -0
  7. data/app/controllers/dscf/credit/bank_staffs_controller.rb +77 -0
  8. data/app/controllers/dscf/credit/banks_controller.rb +39 -0
  9. data/app/controllers/dscf/credit/categories_controller.rb +32 -0
  10. data/app/controllers/dscf/credit/credit_line_specs_controller.rb +72 -0
  11. data/app/controllers/dscf/credit/credit_lines_controller.rb +76 -0
  12. data/app/controllers/dscf/credit/facilitators_controller.rb +227 -0
  13. data/app/controllers/dscf/credit/loans_controller.rb +41 -0
  14. data/app/controllers/dscf/credit/parameter_normalizers_controller.rb +31 -0
  15. data/app/controllers/dscf/credit/payment_requests_controller.rb +38 -0
  16. data/app/controllers/dscf/credit/payments_controller.rb +36 -0
  17. data/app/controllers/dscf/credit/scoring_parameters_controller.rb +50 -0
  18. data/app/controllers/dscf/credit/scoring_tables_controller.rb +63 -0
  19. data/app/controllers/dscf/credit/system_config_definitions_controller.rb +34 -0
  20. data/app/controllers/dscf/credit/system_configs_controller.rb +49 -0
  21. data/app/controllers/dscf/credit/users_controller.rb +53 -0
  22. data/app/jobs/dscf/credit/application_job.rb +6 -0
  23. data/app/mailers/dscf/credit/application_mailer.rb +8 -0
  24. data/app/mailers/dscf/credit/bank_staff_welcome_mailer.rb +15 -0
  25. data/app/mailers/dscf/credit/facilitator_mailer.rb +54 -0
  26. data/app/models/dscf/credit/accounting_audit_request.rb +17 -0
  27. data/app/models/dscf/credit/accounting_entry.rb +15 -0
  28. data/app/models/dscf/credit/application_record.rb +7 -0
  29. data/app/models/dscf/credit/bank.rb +38 -0
  30. data/app/models/dscf/credit/bank_branch.rb +23 -0
  31. data/app/models/dscf/credit/bank_staff.rb +26 -0
  32. data/app/models/dscf/credit/category.rb +21 -0
  33. data/app/models/dscf/credit/credit_line.rb +30 -0
  34. data/app/models/dscf/credit/credit_line_spec.rb +37 -0
  35. data/app/models/dscf/credit/daily_routine_transaction.rb +18 -0
  36. data/app/models/dscf/credit/facilitator.rb +31 -0
  37. data/app/models/dscf/credit/facilitator_performance.rb +17 -0
  38. data/app/models/dscf/credit/failed_operations_log.rb +17 -0
  39. data/app/models/dscf/credit/loan.rb +33 -0
  40. data/app/models/dscf/credit/loan_profile.rb +43 -0
  41. data/app/models/dscf/credit/loan_profile_scoring_spec.rb +23 -0
  42. data/app/models/dscf/credit/loan_transaction.rb +17 -0
  43. data/app/models/dscf/credit/parameter_normalizer.rb +11 -0
  44. data/app/models/dscf/credit/payment.rb +22 -0
  45. data/app/models/dscf/credit/payment_request.rb +29 -0
  46. data/app/models/dscf/credit/scoring_parameter.rb +30 -0
  47. data/app/models/dscf/credit/scoring_table.rb +24 -0
  48. data/app/models/dscf/credit/system_config.rb +25 -0
  49. data/app/models/dscf/credit/system_config_definition.rb +25 -0
  50. data/app/serializers/dscf/core/business_serializer.rb +9 -0
  51. data/app/serializers/dscf/core/business_type_serializer.rb +7 -0
  52. data/app/serializers/dscf/core/role_serializer.rb +7 -0
  53. data/app/serializers/dscf/core/user_profile_serializer.rb +13 -0
  54. data/app/serializers/dscf/core/user_role_serializer.rb +16 -0
  55. data/app/serializers/dscf/core/user_serializer.rb +17 -0
  56. data/app/serializers/dscf/credit/bank_branch_serializer.rb +9 -0
  57. data/app/serializers/dscf/credit/bank_serializer.rb +9 -0
  58. data/app/serializers/dscf/credit/bank_staff_serializer.rb +8 -0
  59. data/app/serializers/dscf/credit/category_serializer.rb +7 -0
  60. data/app/serializers/dscf/credit/credit_line_serializer.rb +12 -0
  61. data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +12 -0
  62. data/app/serializers/dscf/credit/parameter_normalizer_serializer.rb +8 -0
  63. data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +12 -0
  64. data/app/serializers/dscf/credit/scoring_table_serializer.rb +9 -0
  65. data/app/serializers/dscf/credit/system_config_definition_serializer.rb +9 -0
  66. data/app/serializers/dscf/credit/system_config_serializer.rb +18 -0
  67. data/app/services/dscf/credit/bank_staff_creation_service.rb +99 -0
  68. data/app/services/dscf/credit/facilitator_additional_info_service.rb +62 -0
  69. data/app/services/dscf/credit/facilitator_approval_service.rb +88 -0
  70. data/app/views/dscf/credit/bank_staff_welcome_mailer/welcome_email.html.erb +116 -0
  71. data/app/views/dscf/credit/bank_staff_welcome_mailer/welcome_email.text.erb +40 -0
  72. data/app/views/dscf/credit/facilitator_mailer/additional_info_received.html.erb +39 -0
  73. data/app/views/dscf/credit/facilitator_mailer/additional_info_received.text.erb +17 -0
  74. data/app/views/dscf/credit/facilitator_mailer/approval_notification.html.erb +59 -0
  75. data/app/views/dscf/credit/facilitator_mailer/approval_notification.text.erb +26 -0
  76. data/app/views/dscf/credit/facilitator_mailer/limit_update_notification.html.erb +39 -0
  77. data/app/views/dscf/credit/facilitator_mailer/limit_update_notification.text.erb +17 -0
  78. data/app/views/dscf/credit/facilitator_mailer/rejection_notification.html.erb +41 -0
  79. data/app/views/dscf/credit/facilitator_mailer/rejection_notification.text.erb +19 -0
  80. data/app/views/layouts/mailer.html.erb +125 -0
  81. data/app/views/layouts/mailer.text.erb +12 -0
  82. data/config/locales/en.yml +208 -0
  83. data/config/routes.rb +50 -0
  84. data/db/migrate/20250822091011_create_dscf_credit_categories.rb +15 -0
  85. data/db/migrate/20250822091015_create_dscf_credit_banks.rb +23 -0
  86. data/db/migrate/20250822091055_create_dscf_credit_bank_branches.rb +23 -0
  87. data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +25 -0
  88. data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +32 -0
  89. data/db/migrate/20250822091744_create_dscf_credit_system_config_definitions.rb +18 -0
  90. data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +17 -0
  91. data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +30 -0
  92. data/db/migrate/20250822092225_create_dscf_credit_parameter_normalizers.rb +18 -0
  93. data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +25 -0
  94. data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +23 -0
  95. data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +22 -0
  96. data/db/migrate/20250822092528_create_dscf_credit_facilitator_performances.rb +26 -0
  97. data/db/migrate/20250822092608_create_dscf_credit_payment_requests.rb +26 -0
  98. data/db/migrate/20250822092654_create_dscf_credit_loans.rb +29 -0
  99. data/db/migrate/20250822092717_create_dscf_credit_loan_transactions.rb +21 -0
  100. data/db/migrate/20250822092738_create_dscf_credit_daily_routine_transactions.rb +23 -0
  101. data/db/migrate/20250822092819_create_dscf_credit_accounting_audit_requests.rb +20 -0
  102. data/db/migrate/20250822092843_create_dscf_credit_payments.rb +23 -0
  103. data/db/migrate/20250822092908_create_dscf_credit_failed_operations_logs.rb +21 -0
  104. data/db/migrate/20250822092936_create_dscf_credit_accounting_entries.rb +21 -0
  105. data/db/migrate/20250825231109_create_dscf_credit_bank_staff.rb +14 -0
  106. data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +18 -0
  107. data/db/seeds.rb +745 -0
  108. data/lib/dscf/credit/engine.rb +28 -0
  109. data/lib/dscf/credit/version.rb +5 -0
  110. data/lib/dscf/credit.rb +9 -0
  111. data/lib/tasks/dscf/credit_tasks.rake +4 -0
  112. data/spec/factories/dscf/credit/accounting_audit_requests.rb +58 -0
  113. data/spec/factories/dscf/credit/accounting_entries.rb +29 -0
  114. data/spec/factories/dscf/credit/bank_branches.rb +21 -0
  115. data/spec/factories/dscf/credit/bank_staffs.rb +19 -0
  116. data/spec/factories/dscf/credit/banks.rb +30 -0
  117. data/spec/factories/dscf/credit/categories.rb +27 -0
  118. data/spec/factories/dscf/credit/credit_line_specs.rb +57 -0
  119. data/spec/factories/dscf/credit/credit_lines.rb +32 -0
  120. data/spec/factories/dscf/credit/daily_routine_transactions.rb +36 -0
  121. data/spec/factories/dscf/credit/facilitator_performances.rb +30 -0
  122. data/spec/factories/dscf/credit/facilitators.rb +35 -0
  123. data/spec/factories/dscf/credit/failed_operations_logs.rb +34 -0
  124. data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +31 -0
  125. data/spec/factories/dscf/credit/loan_profiles.rb +49 -0
  126. data/spec/factories/dscf/credit/loan_transactions.rb +37 -0
  127. data/spec/factories/dscf/credit/loans.rb +37 -0
  128. data/spec/factories/dscf/credit/parameter_normalizers.rb +24 -0
  129. data/spec/factories/dscf/credit/payment_requests.rb +40 -0
  130. data/spec/factories/dscf/credit/payments.rb +39 -0
  131. data/spec/factories/dscf/credit/scoring_parameters.rb +48 -0
  132. data/spec/factories/dscf/credit/scoring_tables.rb +25 -0
  133. data/spec/factories/dscf/credit/system_config_definitions.rb +45 -0
  134. data/spec/factories/dscf/credit/system_configs.rb +29 -0
  135. metadata +456 -0
@@ -0,0 +1,18 @@
1
+ module Dscf::Credit
2
+ class DailyRoutineTransaction < ApplicationRecord
3
+ self.table_name = "dscf_credit_daily_routine_transactions"
4
+
5
+ belongs_to :loan, class_name: "Dscf::Credit::Loan", foreign_key: "loan_id"
6
+
7
+ validates :routine_type, :amount, :status, presence: true
8
+ validates :amount, numericality: { greater_than: 0 }
9
+ validates :status, inclusion: { in: %w[pending processing completed failed] }
10
+ validates :routine_type, inclusion: {
11
+ in: %w[interest_calculation penalty_assessment payment_due_notification],
12
+ message: "%{value} is not a valid routine type"
13
+ }
14
+
15
+ scope :by_status, ->(status) { where(status: status) }
16
+ scope :by_routine_type, ->(type) { where(routine_type: type) }
17
+ end
18
+ end
@@ -0,0 +1,31 @@
1
+ module Dscf::Credit
2
+ class Facilitator < ApplicationRecord
3
+ self.table_name = "dscf_credit_facilitators"
4
+ self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
+
6
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
7
+ belongs_to :kyc_approved_by, polymorphic: true
8
+ if defined?(Dscf::Core::User)
9
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
+ end
11
+ has_many :facilitator_performances, class_name: "Dscf::Credit::FacilitatorPerformance", foreign_key: "facilitator_id", dependent: :destroy
12
+
13
+ validates :name, :type, presence: true
14
+ validates :type, inclusion: { in: %w[individual corporate agent] }
15
+ validates :kyc_status, inclusion: { in: %w[pending approved rejected] }
16
+ validates :user_id, uniqueness: { scope: :bank_id }
17
+ validates :total_limit, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
18
+
19
+ scope :approved, -> { where(kyc_status: "approved") }
20
+ scope :pending_kyc, -> { where(kyc_status: "pending") }
21
+ scope :rejected, -> { where(kyc_status: "rejected") }
22
+
23
+ def self.ransackable_attributes(auth_object = nil)
24
+ %w[id name type total_limit kyc_status created_at updated_at]
25
+ end
26
+
27
+ def self.ransackable_associations(auth_object = nil)
28
+ %w[bank user kyc_approved_by facilitator_performances]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module Dscf::Credit
2
+ class FacilitatorPerformance < ApplicationRecord
3
+ self.table_name = "dscf_credit_facilitator_performances"
4
+
5
+ belongs_to :facilitator, class_name: "Dscf::Credit::Facilitator", foreign_key: "facilitator_id"
6
+ belongs_to :created_by, polymorphic: true
7
+
8
+ validates :total_outstanding_loans, :total_outstanding_amount, presence: true
9
+ validates :total_outstanding_loans, numericality: { greater_than_or_equal_to: 0 }
10
+ validates :total_outstanding_amount, numericality: { greater_than_or_equal_to: 0 }
11
+ validates :score, numericality: { greater_than_or_equal_to: 0 }, allow_nil: true
12
+
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) }
16
+ end
17
+ end
@@ -0,0 +1,17 @@
1
+ module Dscf::Credit
2
+ class FailedOperationsLog < ApplicationRecord
3
+ self.table_name = "dscf_credit_failed_operations_logs"
4
+
5
+ belongs_to :entity, polymorphic: true
6
+
7
+ validates :failure_type, :reason, presence: true
8
+ validates :failure_type, inclusion: {
9
+ in: %w[loan_disbursement payment_processing interest_calculation credit_scoring daily_routine accounting_entry]
10
+ }
11
+ validates :retry_count, numericality: { greater_than_or_equal_to: 0 }
12
+
13
+ scope :by_failure_type, ->(type) { where(failure_type: type) }
14
+ scope :resolved, -> { where(resolved: true) }
15
+ scope :unresolved, -> { where(resolved: false) }
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ module Dscf::Credit
2
+ class Loan < ApplicationRecord
3
+ self.table_name = "dscf_credit_loans"
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
+ belongs_to :payment_request, class_name: "Dscf::Credit::PaymentRequest", foreign_key: "payment_request_id"
8
+ has_many :loan_transactions, class_name: "Dscf::Credit::LoanTransaction", foreign_key: "loan_id", dependent: :destroy
9
+ has_many :daily_routine_transactions, class_name: "Dscf::Credit::DailyRoutineTransaction", foreign_key: "loan_id", dependent: :destroy
10
+
11
+ validates :principal_amount, :total_loan_amount, :remaining_amount, :due_date, presence: true
12
+ validates :principal_amount, :total_loan_amount, :remaining_amount, numericality: { greater_than: 0 }
13
+ validates :accrued_interest, :accrued_penalty, :facilitation_fee, numericality: { greater_than_or_equal_to: 0 }
14
+ validates :status, inclusion: { in: %w[pending approved disbursed active overdue paid closed] }
15
+
16
+ scope :active, -> { where(status: "active") }
17
+ scope :overdue, -> { where(status: "overdue") }
18
+ scope :paid, -> { where(status: "paid") }
19
+ scope :due_today, -> { where(due_date: Date.current) }
20
+ scope :past_due, -> { where("due_date < ? AND status IN (?)", Date.current, [ "active", "overdue" ]) }
21
+
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]
24
+ end
25
+
26
+ def self.ransackable_associations(auth_object = nil)
27
+ %w[loan_profile credit_line payment_request loan_transactions daily_routine_transactions]
28
+ end
29
+
30
+ delegate :user, to: :loan_profile, allow_nil: true
31
+ delegate :bank, to: :credit_line, allow_nil: true
32
+ end
33
+ end
@@ -0,0 +1,43 @@
1
+ module Dscf::Credit
2
+ class LoanProfile < ApplicationRecord
3
+ self.table_name = "dscf_credit_loan_profiles"
4
+
5
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
+ belongs_to :approved_by, polymorphic: true
7
+ belongs_to :backer, polymorphic: true, optional: true
8
+ if defined?(Dscf::Core::User)
9
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
10
+ end
11
+ has_many :loan_profile_scoring_specs, class_name: "Dscf::Credit::LoanProfileScoringSpec", foreign_key: "loan_profile_id", dependent: :destroy
12
+ has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "loan_profile_id", dependent: :destroy
13
+
14
+ validates :status, inclusion: { in: %w[pending approved rejected kyc_approved kyc_rejected] }
15
+ validates :total_amount, :available_amount, numericality: { greater_than_or_equal_to: 0 }
16
+ validate :available_amount_not_greater_than_total_amount
17
+
18
+ scope :pending, -> { where(status: "pending") }
19
+ scope :approved, -> { where(status: "approved") }
20
+ scope :rejected, -> { where(status: "rejected") }
21
+ scope :kyc_approved, -> { where(status: "kyc_approved") }
22
+ scope :kyc_rejected, -> { where(status: "kyc_rejected") }
23
+ scope :with_available_amount, -> { where("available_amount > 0") }
24
+ scope :by_total_amount_range, ->(min, max) { where(total_amount: min..max) }
25
+ scope :by_available_amount_range, ->(min, max) { where(available_amount: min..max) }
26
+
27
+ 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]
29
+ end
30
+
31
+ def self.ransackable_associations(auth_object = nil)
32
+ %w[bank user backer approved_by reviewed_by loan_profile_scoring_specs loans]
33
+ end
34
+
35
+ private
36
+
37
+ def available_amount_not_greater_than_total_amount
38
+ return unless total_amount && available_amount
39
+
40
+ errors.add(:available_amount, "cannot be greater than total amount") if available_amount > total_amount
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,23 @@
1
+ module Dscf::Credit
2
+ class LoanProfileScoringSpec < ApplicationRecord
3
+ self.table_name = "dscf_credit_loan_profile_scoring_specs"
4
+
5
+ belongs_to :loan_profile, class_name: "Dscf::Credit::LoanProfile", foreign_key: "loan_profile_id"
6
+ belongs_to :created_by, polymorphic: true
7
+ belongs_to :approved_by, polymorphic: true
8
+
9
+ validates :scoring_input_data, :score, presence: true
10
+ validates :score, numericality: { greater_than_or_equal_to: 0 }
11
+
12
+ scope :active, -> { where(active: true) }
13
+ scope :expired, -> { where("expires_at < ?", Time.current) }
14
+
15
+ def self.ransackable_attributes(auth_object = nil)
16
+ %w[id score active expires_at created_at updated_at]
17
+ end
18
+
19
+ def self.ransackable_associations(auth_object = nil)
20
+ %w[loan_profile created_by approved_by]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,17 @@
1
+ module Dscf::Credit
2
+ class LoanTransaction < ApplicationRecord
3
+ self.table_name = "dscf_credit_loan_transactions"
4
+
5
+ belongs_to :loan, class_name: "Dscf::Credit::Loan", foreign_key: "loan_id"
6
+
7
+ validates :transaction_type, :amount, :transaction_reference, :status, presence: true
8
+ validates :amount, numericality: { greater_than: 0 }
9
+ validates :transaction_type, inclusion: { in: %w[disbursement repayment interest_accrual penalty fee_charge] }
10
+ validates :status, inclusion: { in: %w[pending processing completed failed reversed] }
11
+ validates :transaction_reference, uniqueness: true
12
+
13
+ scope :by_status, ->(status) { where(status: status) }
14
+ scope :disbursements, -> { where(transaction_type: "disbursement") }
15
+ scope :repayments, -> { where(transaction_type: "repayment") }
16
+ end
17
+ end
@@ -0,0 +1,11 @@
1
+ module Dscf::Credit
2
+ class ParameterNormalizer < ApplicationRecord
3
+ self.table_name = "dscf_credit_parameter_normalizers"
4
+
5
+ belongs_to :scoring_parameter, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "scoring_parameter_id"
6
+
7
+ validates :name, :raw_value, :normalized_value, presence: true
8
+ validates :raw_value, uniqueness: { scope: :scoring_parameter_id }
9
+ validates :normalized_value, numericality: { greater_than_or_equal_to: 0 }
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ module Dscf::Credit
2
+ class Payment < ApplicationRecord
3
+ self.table_name = "dscf_credit_payments"
4
+
5
+ belongs_to :payment_request, class_name: "Dscf::Credit::PaymentRequest", foreign_key: "payment_request_id"
6
+
7
+ validates :amount, :receiver_account_reference, :transaction_reference, :status, presence: true
8
+ validates :amount, numericality: { greater_than: 0 }
9
+ validates :status, inclusion: { in: %w[pending processing completed failed cancelled] }
10
+ validates :transaction_reference, uniqueness: true
11
+
12
+ scope :by_status, ->(status) { where(status: status) }
13
+
14
+ def self.ransackable_attributes(auth_object = nil)
15
+ %w[id amount receiver_account_reference transaction_reference status failure_reason processed_at created_at updated_at]
16
+ end
17
+
18
+ def self.ransackable_associations(auth_object = nil)
19
+ %w[payment_request]
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,29 @@
1
+ module Dscf::Credit
2
+ class PaymentRequest < ApplicationRecord
3
+ self.table_name = "dscf_credit_payment_requests"
4
+
5
+ if defined?(Dscf::Core::User)
6
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
7
+ end
8
+ has_many :loans, class_name: "Dscf::Credit::Loan", foreign_key: "payment_request_id", dependent: :destroy
9
+ has_many :payments, class_name: "Dscf::Credit::Payment", foreign_key: "payment_request_id", dependent: :destroy
10
+
11
+ validates :order_id, presence: true, uniqueness: true
12
+ validates :request_type, :amount, :receiver_account_reference, presence: true
13
+ validates :amount, numericality: { greater_than: 0 }
14
+ validates :status, inclusion: { in: %w[pending approved rejected processed failed] }
15
+
16
+ scope :pending, -> { where(status: "pending") }
17
+ scope :approved, -> { where(status: "approved") }
18
+ scope :processed, -> { where(status: "processed") }
19
+ scope :failed, -> { where(status: "failed") }
20
+
21
+ def self.ransackable_attributes(auth_object = nil)
22
+ %w[id order_id request_type amount receiver_account_reference status failure_reason initiated_at approved_at created_at updated_at]
23
+ end
24
+
25
+ def self.ransackable_associations(auth_object = nil)
26
+ %w[user loans payments]
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,30 @@
1
+ module Dscf::Credit
2
+ class ScoringParameter < ApplicationRecord
3
+ self.table_name = "dscf_credit_scoring_parameters"
4
+ self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
+
6
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
7
+ belongs_to :created_by, polymorphic: true
8
+ belongs_to :approved_by, polymorphic: true
9
+ belongs_to :previous_version, class_name: "Dscf::Credit::ScoringParameter", optional: true
10
+ 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 :credit_lines, through: :scoring_tables
13
+
14
+ validates :name, :data_type, :type, :weight, presence: true
15
+ validates :weight, numericality: { greater_than: 0, less_than_or_equal_to: 1 }
16
+ validates :data_type, inclusion: { in: %w[integer decimal boolean string] }
17
+ validates :type, inclusion: { in: %w[income credit_history employment_status collateral] }
18
+ validates :name, uniqueness: { scope: [ :bank_id, :active ] }
19
+
20
+ scope :active, -> { where(active: true) }
21
+
22
+ 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
+ end
25
+
26
+ def self.ransackable_associations(auth_object = nil)
27
+ %w[bank created_by approved_by previous_version parameter_normalizers scoring_tables credit_lines]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,24 @@
1
+ module Dscf::Credit
2
+ class ScoringTable < ApplicationRecord
3
+ self.table_name = "dscf_credit_scoring_tables"
4
+
5
+ belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
6
+ belongs_to :scoring_parameter, class_name: "Dscf::Credit::ScoringParameter", foreign_key: "scoring_parameter_id"
7
+ belongs_to :created_by, polymorphic: true
8
+
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 }
11
+
12
+ scope :active, -> { where(active: true) }
13
+ scope :by_credit_line, ->(credit_line_id) { where(credit_line_id: credit_line_id) }
14
+ scope :by_scoring_parameter, ->(scoring_parameter_id) { where(scoring_parameter_id: scoring_parameter_id) }
15
+
16
+ def self.ransackable_attributes(auth_object = nil)
17
+ %w[id weight active created_at updated_at]
18
+ end
19
+
20
+ def self.ransackable_associations(auth_object = nil)
21
+ %w[credit_line scoring_parameter created_by]
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module Dscf::Credit
2
+ class SystemConfig < ApplicationRecord
3
+ self.table_name = "dscf_credit_system_configs"
4
+
5
+ belongs_to :config_definition, class_name: "Dscf::Credit::SystemConfigDefinition", foreign_key: "config_definition_id"
6
+ belongs_to :last_updated_by, polymorphic: true
7
+ belongs_to :approved_by, polymorphic: true
8
+
9
+ validates :config_value, presence: true
10
+ validates :status, inclusion: { in: %w[pending approved rejected] }
11
+ validates :config_definition_id, uniqueness: true
12
+
13
+ scope :pending, -> { where(status: "pending") }
14
+ scope :approved, -> { where(status: "approved") }
15
+ scope :rejected, -> { where(status: "rejected") }
16
+
17
+ def self.ransackable_attributes(auth_object = nil)
18
+ %w[id config_value status created_at updated_at]
19
+ end
20
+
21
+ def self.ransackable_associations(auth_object = nil)
22
+ %w[config_definition last_updated_by approved_by]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Dscf::Credit
2
+ class SystemConfigDefinition < ApplicationRecord
3
+ self.table_name = "dscf_credit_system_config_definitions"
4
+
5
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
+ has_one :system_config, class_name: "Dscf::Credit::SystemConfig", foreign_key: "config_definition_id", dependent: :destroy
7
+
8
+ validates :config_key, :value_type, presence: true
9
+ validates :config_key, uniqueness: { scope: :bank_id }
10
+ validates :value_type, inclusion: { in: %w[string integer decimal boolean json] }
11
+ validates :frequency, inclusion: { in: %w[once daily weekly monthly quarterly yearly] }, allow_blank: true
12
+
13
+ scope :by_frequency, ->(frequency) { where(frequency: frequency) }
14
+ scope :with_frequency, -> { where.not(frequency: [ nil, "" ]) }
15
+ scope :without_frequency, -> { where(frequency: [ nil, "" ]) }
16
+
17
+ def self.ransackable_attributes(auth_object = nil)
18
+ %w[id config_key description value_type frequency created_at updated_at]
19
+ end
20
+
21
+ def self.ransackable_associations(auth_object = nil)
22
+ %w[bank system_config]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,9 @@
1
+ module Dscf::Core
2
+ class BusinessSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :description, :contact_email, :contact_phone, :tin_number,
4
+ :created_at, :updated_at
5
+
6
+ belongs_to :business_type, serializer: Dscf::Core::BusinessTypeSerializer
7
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ module Dscf::Core
2
+ class BusinessTypeSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :created_at, :updated_at
4
+
5
+ has_many :businesses, serializer: Dscf::Core::BusinessSerializer
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ module Dscf::Core
2
+ class RoleSerializer < ActiveModel::Serializer
3
+ attributes :id, :code, :name, :created_at, :updated_at
4
+
5
+ has_many :user_roles, serializer: Dscf::Core::UserRoleSerializer
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ module Dscf::Core
2
+ class UserProfileSerializer < ActiveModel::Serializer
3
+ attributes :id, :first_name, :middle_name, :last_name, :gender, :nationality,
4
+ :date_of_birth, :place_of_birth, :fayda_number, :occupation,
5
+ :position_title, :employer_name, :avg_monthly_income, :avg_annual_income,
6
+ :pep_status, :risk_score, :watchlist_hit, :verification_status,
7
+ :created_at, :updated_at
8
+
9
+ def full_name
10
+ [ object.first_name, object.middle_name, object.last_name ].compact.join(" ")
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ module Dscf::Core
2
+ class UserRoleSerializer < ActiveModel::Serializer
3
+ attributes :id, :created_at, :updated_at
4
+
5
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
6
+ belongs_to :role, serializer: Dscf::Core::RoleSerializer
7
+
8
+ def role_code
9
+ object.role&.code
10
+ end
11
+
12
+ def role_name
13
+ object.role&.name
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module Dscf::Core
2
+ class UserSerializer < ActiveModel::Serializer
3
+ attributes :id, :email, :phone, :verified_at, :temp_password, :created_at, :updated_at
4
+
5
+ has_one :user_profile, serializer: Dscf::Core::UserProfileSerializer
6
+ has_many :user_roles, serializer: Dscf::Core::UserRoleSerializer
7
+ has_many :businesses, serializer: Dscf::Core::BusinessSerializer
8
+ has_many :roles, serializer: Dscf::Core::RoleSerializer
9
+
10
+
11
+ def attributes(*args)
12
+ data = super
13
+ data.except!("password_digest")
14
+ data
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ module Dscf::Credit
2
+ class BankBranchSerializer < ActiveModel::Serializer
3
+ attributes :id, :branch_name, :branch_code, :branch_address, :city, :country,
4
+ :contact_email, :contact_phone, :status, :created_at, :updated_at
5
+
6
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
+ has_many :bank_staffs, serializer: Dscf::Credit::BankStaffSerializer
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Dscf::Credit
2
+ class BankSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :registration_number, :swift_code, :headquarters_address,
4
+ :city, :country, :contact_email, :contact_phone, :status, :created_at, :updated_at
5
+
6
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
7
+ has_many :bank_branches, serializer: Dscf::Credit::BankBranchSerializer
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module Dscf::Credit
2
+ class BankStaffSerializer < ActiveModel::Serializer
3
+ attributes :id, :status, :created_at, :updated_at
4
+
5
+ belongs_to :user, serializer: Dscf::Core::UserSerializer
6
+ belongs_to :bank_branch, serializer: Dscf::Credit::BankBranchSerializer
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ module Dscf::Credit
2
+ class CategorySerializer < ActiveModel::Serializer
3
+ attributes :id, :type, :name, :description, :created_at, :updated_at
4
+
5
+ has_many :credit_lines, serializer: Dscf::Credit::CreditLineSerializer
6
+ end
7
+ end
@@ -0,0 +1,12 @@
1
+ module Dscf::Credit
2
+ class CreditLineSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :code, :description, :status, :approval_date,
4
+ :created_at, :updated_at
5
+
6
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
+ belongs_to :category, serializer: Dscf::Credit::CategorySerializer
8
+ belongs_to :created_by, serializer: Dscf::Core::UserSerializer
9
+ belongs_to :approved_by, serializer: Dscf::Core::UserSerializer
10
+ has_many :credit_line_specs, serializer: Dscf::Credit::CreditLineSpecSerializer
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ module Dscf::Credit
2
+ class CreditLineSpecSerializer < ActiveModel::Serializer
3
+ attributes :id, :min_amount, :max_amount, :interest_rate, :penalty_rate,
4
+ :facilitation_fee_rate, :tax_rate, :max_penalty_days,
5
+ :loan_duration, :interest_frequency, :interest_income_tax,
6
+ :vat, :max_interest_calculation_days, :penalty_frequency,
7
+ :penalty_income_tax, :active, :created_at, :updated_at
8
+
9
+ belongs_to :credit_line, serializer: Dscf::Credit::CreditLineSerializer
10
+ belongs_to :created_by, serializer: Dscf::Core::UserSerializer
11
+ end
12
+ end
@@ -0,0 +1,8 @@
1
+ module Dscf::Credit
2
+ class ParameterNormalizerSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :description, :raw_value, :normalized_value,
4
+ :created_at, :updated_at
5
+
6
+ belongs_to :scoring_parameter, serializer: Dscf::Credit::ScoringParameterSerializer
7
+ end
8
+ end
@@ -0,0 +1,12 @@
1
+ module Dscf::Credit
2
+ class ScoringParameterSerializer < ActiveModel::Serializer
3
+ attributes :id, :name, :description, :data_type, :type, :weight, :min_value,
4
+ :max_value, :active, :expires_at, :created_at, :updated_at
5
+
6
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
+ belongs_to :created_by, polymorphic: true
8
+ belongs_to :approved_by, polymorphic: true
9
+ belongs_to :previous_version, serializer: Dscf::Credit::ScoringParameterSerializer
10
+ has_many :parameter_normalizers, serializer: Dscf::Credit::ParameterNormalizerSerializer
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ module Dscf::Credit
2
+ class ScoringTableSerializer < ActiveModel::Serializer
3
+ attributes :id, :weight, :active, :created_at, :updated_at
4
+
5
+ belongs_to :credit_line, serializer: Dscf::Credit::CreditLineSerializer
6
+ belongs_to :scoring_parameter, serializer: Dscf::Credit::ScoringParameterSerializer
7
+ belongs_to :created_by, serializer: Dscf::Core::UserSerializer
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module Dscf::Credit
2
+ class SystemConfigDefinitionSerializer < ActiveModel::Serializer
3
+ attributes :id, :config_key, :description, :value_type, :frequency,
4
+ :created_at, :updated_at
5
+
6
+ belongs_to :bank, serializer: Dscf::Credit::BankSerializer
7
+ has_one :system_config, serializer: Dscf::Credit::SystemConfigSerializer
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ module Dscf::Credit
2
+ class SystemConfigSerializer < ActiveModel::Serializer
3
+ attributes :id, :config_value, :status, :created_at, :updated_at
4
+
5
+ attribute :config_definition do
6
+ {
7
+ id: object.config_definition&.id,
8
+ config_key: object.config_definition&.config_key,
9
+ description: object.config_definition&.description,
10
+ value_type: object.config_definition&.value_type,
11
+ frequency: object.config_definition&.frequency
12
+ }
13
+ end
14
+
15
+ belongs_to :last_updated_by, serializer: Dscf::Core::UserSerializer
16
+ belongs_to :approved_by, serializer: Dscf::Core::UserSerializer
17
+ end
18
+ end