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,31 @@
1
+ module Dscf::Credit
2
+ class ParameterNormalizersController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:parameter_normalizer).permit(
9
+ :scoring_parameter_id,
10
+ :name,
11
+ :description,
12
+ :raw_value,
13
+ :normalized_value
14
+ )
15
+ end
16
+
17
+ def eager_loaded_associations
18
+ [ :scoring_parameter ]
19
+ end
20
+
21
+ def allowed_order_columns
22
+ %w[id name raw_value normalized_value created_at updated_at]
23
+ end
24
+
25
+ def default_serializer_includes
26
+ {
27
+ default: [ :scoring_parameter ]
28
+ }
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,38 @@
1
+ module Dscf::Credit
2
+ class PaymentRequestsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:payment_request).permit(
9
+ :user_id,
10
+ :order_id,
11
+ :request_type,
12
+ :amount,
13
+ :receiver_account_reference,
14
+ :status,
15
+ :failure_reason,
16
+ :initiated_at,
17
+ :approved_at
18
+ )
19
+ end
20
+
21
+ def eager_loaded_associations
22
+ [ :user, :loans, :payments ]
23
+ end
24
+
25
+ def allowed_order_columns
26
+ %w[id order_id request_type amount status initiated_at approved_at created_at updated_at]
27
+ end
28
+
29
+ def default_serializer_includes
30
+ {
31
+ index: [ :user ],
32
+ show: [ :user, :loans, :payments ],
33
+ create: [ :user ],
34
+ update: [ :user, :loans, :payments ]
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,36 @@
1
+ module Dscf::Credit
2
+ class PaymentsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:payment).permit(
9
+ :payment_request_id,
10
+ :amount,
11
+ :receiver_account_reference,
12
+ :transaction_reference,
13
+ :status,
14
+ :failure_reason,
15
+ :processed_at
16
+ )
17
+ end
18
+
19
+ def eager_loaded_associations
20
+ [ :payment_request ]
21
+ end
22
+
23
+ def allowed_order_columns
24
+ %w[id amount status processed_at created_at updated_at]
25
+ end
26
+
27
+ def default_serializer_includes
28
+ {
29
+ index: [ :payment_request ],
30
+ show: [ :payment_request ],
31
+ create: [ :payment_request ],
32
+ update: [ :payment_request ]
33
+ }
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,50 @@
1
+ module Dscf::Credit
2
+ class ScoringParametersController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def create
6
+ super do
7
+ scoring_parameter = @clazz.new(model_params)
8
+ scoring_parameter.created_by = current_user
9
+ scoring_parameter.approved_by = current_user
10
+
11
+ scoring_parameter
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def model_params
18
+ params.require(:scoring_parameter).permit(
19
+ :bank_id,
20
+ :name,
21
+ :description,
22
+ :data_type,
23
+ :type,
24
+ :weight,
25
+ :min_value,
26
+ :max_value,
27
+ :active,
28
+ :previous_version_id,
29
+ :expires_at
30
+ )
31
+ end
32
+
33
+ def eager_loaded_associations
34
+ [ :bank, :created_by, :approved_by, :previous_version, :parameter_normalizers ]
35
+ end
36
+
37
+ def allowed_order_columns
38
+ %w[id name data_type type weight active expires_at created_at updated_at]
39
+ end
40
+
41
+ def default_serializer_includes
42
+ {
43
+ 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 ]
47
+ }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,63 @@
1
+ module Dscf::Credit
2
+ class ScoringTablesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def create
6
+ super do
7
+ scoring_table = @clazz.new(model_params)
8
+ scoring_table.created_by = current_user
9
+ scoring_table
10
+ end
11
+ end
12
+
13
+ def activate
14
+ @obj = @clazz.find(params[:id])
15
+ if @obj.update(active: true)
16
+ render_success(data: @obj, serializer_options: { include: [ :credit_line, :scoring_parameter, :created_by ] })
17
+ else
18
+ render_error(errors: @obj.errors.full_messages[0], status: :unprocessable_entity)
19
+ end
20
+ rescue StandardError => e
21
+ render_error(error: e.message)
22
+ end
23
+
24
+ def deactivate
25
+ @obj = @clazz.find(params[:id])
26
+ if @obj.update(active: false)
27
+ render_success(data: @obj, serializer_options: { include: [ :credit_line, :scoring_parameter, :created_by ] })
28
+ else
29
+ render_error(errors: @obj.errors.full_messages[0], status: :unprocessable_entity)
30
+ end
31
+ rescue StandardError => e
32
+ render_error(error: e.message)
33
+ end
34
+
35
+ private
36
+
37
+ def model_params
38
+ params.require(:scoring_table).permit(
39
+ :credit_line_id,
40
+ :scoring_parameter_id,
41
+ :weight,
42
+ :active
43
+ )
44
+ end
45
+
46
+ def eager_loaded_associations
47
+ [ :credit_line, :scoring_parameter, :created_by ]
48
+ end
49
+
50
+ def allowed_order_columns
51
+ %w[id weight active created_at updated_at]
52
+ end
53
+
54
+ def default_serializer_includes
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 ]
60
+ }
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,34 @@
1
+ module Dscf::Credit
2
+ class SystemConfigDefinitionsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:system_config_definition).permit(
9
+ :bank_id,
10
+ :config_key,
11
+ :description,
12
+ :value_type,
13
+ :frequency
14
+ )
15
+ end
16
+
17
+ def eager_loaded_associations
18
+ [ :bank, :system_config ]
19
+ end
20
+
21
+ def allowed_order_columns
22
+ %w[id config_key description value_type frequency created_at updated_at]
23
+ end
24
+
25
+ def default_serializer_includes
26
+ {
27
+ index: [ :bank ],
28
+ show: [ :bank, :system_config ],
29
+ create: [ :bank ],
30
+ update: [ :bank, :system_config ]
31
+ }
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,49 @@
1
+ module Dscf::Credit
2
+ class SystemConfigsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def create
6
+ super do
7
+ obj = @clazz.new(model_params)
8
+ obj.approved_by = current_user
9
+ obj.last_updated_by = current_user
10
+ obj
11
+ end
12
+ end
13
+
14
+ def update
15
+ super do
16
+ obj = set_object
17
+ obj.last_updated_by = current_user
18
+ obj
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def model_params
25
+ params.require(:system_config).permit(
26
+ :config_definition_id,
27
+ :config_value,
28
+ :status
29
+ )
30
+ end
31
+
32
+ def eager_loaded_associations
33
+ [ :config_definition, :last_updated_by, :approved_by ]
34
+ end
35
+
36
+ def allowed_order_columns
37
+ %w[id config_value status created_at updated_at]
38
+ end
39
+
40
+ def default_serializer_includes
41
+ {
42
+ 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
+ }
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,53 @@
1
+ module Dscf::Credit
2
+ class UsersController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def index
6
+ super do
7
+ if params[:business_type].present?
8
+ business_type_name = params[:business_type].downcase
9
+
10
+ business_type = Dscf::Core::BusinessType.find_by(name: business_type_name)
11
+ unless business_type
12
+ return render_error(
13
+ "validation.invalid",
14
+ errors: "Invalid business type: #{params[:business_type]}",
15
+ status: :not_found
16
+ )
17
+ end
18
+
19
+ @clazz.joins(businesses: :business_type)
20
+ .where(dscf_core_business_types: { name: business_type_name })
21
+ .distinct
22
+ else
23
+ @clazz.all
24
+ end
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def model_params
31
+ params.require(:user).permit(
32
+ :email,
33
+ :phone,
34
+ :verified_at,
35
+ :temp_password
36
+ )
37
+ end
38
+
39
+ def eager_loaded_associations
40
+ [ :user_profile, :businesses, :user_roles, businesses: :business_type ]
41
+ end
42
+
43
+ def allowed_order_columns
44
+ %w[id email phone verified_at created_at updated_at]
45
+ end
46
+
47
+ def default_serializer_includes
48
+ {
49
+ default: [ :user_profile, :businesses, :user_roles, "businesses.business_type" ]
50
+ }
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,6 @@
1
+ module Dscf
2
+ module Credit
3
+ class ApplicationJob < ActiveJob::Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ module Dscf
2
+ module Credit
3
+ class ApplicationMailer < ActionMailer::Base
4
+ default from: ENV.fetch("MAILER_FROM_EMAIL", "noreply@dscf-credit.com")
5
+ layout "mailer"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,15 @@
1
+ module Dscf::Credit
2
+ class BankStaffWelcomeMailer < ApplicationMailer
3
+ def welcome_email(user, generated_password, bank_staff = nil)
4
+ @user = user
5
+ @generated_password = generated_password
6
+ @bank_staff = bank_staff
7
+ @login_url = ENV["FRONTEND_BASE_URL"] || "http://localhost:3000"
8
+
9
+ mail(
10
+ to: user.email,
11
+ subject: "Welcome to DSCF Banking System - Your Account Details"
12
+ )
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,54 @@
1
+ module Dscf::Credit
2
+ class FacilitatorMailer < ApplicationMailer
3
+ def approval_notification(facilitator, additional_info_token)
4
+ @facilitator = facilitator
5
+ @merchant = facilitator.user
6
+ @additional_info_url = generate_additional_info_url(additional_info_token)
7
+ @total_limit = facilitator.total_limit
8
+
9
+ mail(
10
+ to: @merchant.email,
11
+ subject: "Facilitator Application Approved - Submit Additional Information"
12
+ )
13
+ end
14
+
15
+ def rejection_notification(facilitator, rejection_reason = nil)
16
+ @facilitator = facilitator
17
+ @merchant = facilitator.user
18
+ @rejection_reason = rejection_reason
19
+
20
+ mail(
21
+ to: @merchant.email,
22
+ subject: "Facilitator Application Update"
23
+ )
24
+ end
25
+
26
+ def limit_update_notification(facilitator)
27
+ @facilitator = facilitator
28
+ @merchant = facilitator.user
29
+ @total_limit = facilitator.total_limit
30
+
31
+ mail(
32
+ to: @merchant.email,
33
+ subject: "Facilitator Limit Updated"
34
+ )
35
+ end
36
+
37
+ def additional_info_received(facilitator)
38
+ @facilitator = facilitator
39
+ @merchant = facilitator.user
40
+
41
+ mail(
42
+ to: @merchant.email,
43
+ subject: "Additional Information Received"
44
+ )
45
+ end
46
+
47
+ private
48
+
49
+ def generate_additional_info_url(token)
50
+ frontend_base_url = ENV["FRONTEND_BASE_URL"] || "https://test-frontend.com"
51
+ "#{frontend_base_url}/facilitator/additional-info?token=#{token}"
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,17 @@
1
+ module Dscf::Credit
2
+ class AccountingAuditRequest < ApplicationRecord
3
+ self.table_name = "dscf_credit_accounting_audit_requests"
4
+
5
+ belongs_to :source_entity, polymorphic: true
6
+
7
+ validates :transaction_type, :amount, :webhook_key, :status, presence: true
8
+ validates :transaction_type, inclusion: { in: %w[debit credit transfer adjustment] }
9
+ validates :status, inclusion: { in: %w[pending processing completed failed] }
10
+ validates :amount, numericality: { greater_than: 0 }
11
+ validates :webhook_key, uniqueness: true
12
+
13
+ scope :by_status, ->(status) { where(status: status) }
14
+ scope :by_transaction_type, ->(type) { where(transaction_type: type) }
15
+ scope :by_amount_range, ->(min, max) { where(amount: min..max) }
16
+ end
17
+ end
@@ -0,0 +1,15 @@
1
+ module Dscf::Credit
2
+ class AccountingEntry < ApplicationRecord
3
+ self.table_name = "dscf_credit_accounting_entries"
4
+
5
+ belongs_to :audit_request, class_name: "Dscf::Credit::AccountingAuditRequest", foreign_key: "audit_request_id"
6
+
7
+ validates :account_code, :entry_type, :amount, :status, presence: true
8
+ validates :amount, numericality: { greater_than: 0 }
9
+ validates :entry_type, inclusion: { in: %w[debit credit] }
10
+ validates :status, inclusion: { in: %w[pending completed failed] }
11
+
12
+ scope :by_entry_type, ->(type) { where(entry_type: type) }
13
+ scope :by_status, ->(status) { where(status: status) }
14
+ end
15
+ end
@@ -0,0 +1,7 @@
1
+ module Dscf
2
+ module Credit
3
+ class ApplicationRecord < ActiveRecord::Base
4
+ self.abstract_class = true
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,38 @@
1
+ module Dscf::Credit
2
+ class Bank < ApplicationRecord
3
+ self.table_name = "dscf_credit_banks"
4
+
5
+ if defined?(Dscf::Core::User)
6
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id", optional: true
7
+ end
8
+ has_many :bank_branches, class_name: "Dscf::Credit::BankBranch",
9
+ foreign_key: "bank_id", dependent: :destroy
10
+ has_many :credit_lines, class_name: "Dscf::Credit::CreditLine",
11
+ foreign_key: "bank_id", dependent: :destroy
12
+ has_many :system_config_definitions, class_name: "Dscf::Credit::SystemConfigDefinition",
13
+ foreign_key: "bank_id", dependent: :destroy
14
+ has_many :scoring_parameters, class_name: "Dscf::Credit::ScoringParameter",
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",
19
+ foreign_key: "bank_id", dependent: :destroy
20
+ has_many :bank_staffs, through: :bank_branches, class_name: "Dscf::Credit::BankStaff"
21
+
22
+ validates :name, presence: true
23
+ validates :registration_number, uniqueness: true, allow_blank: true
24
+ validates :swift_code, uniqueness: true, allow_blank: true
25
+ validates :status, inclusion: { in: %w[active inactive suspended] }
26
+
27
+ scope :active, -> { where(status: "active") }
28
+ scope :inactive, -> { where(status: "inactive") }
29
+
30
+ def self.ransackable_attributes(auth_object = nil)
31
+ %w[id name registration_number swift_code headquarters_address city country contact_email contact_phone status created_at updated_at]
32
+ end
33
+
34
+ def self.ransackable_associations(auth_object = nil)
35
+ %w[user bank_branches credit_lines system_config_definitions scoring_parameters loan_profiles facilitators bank_staffs]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module Dscf::Credit
2
+ class BankBranch < ApplicationRecord
3
+ self.table_name = "dscf_credit_bank_branches"
4
+
5
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
+ has_many :bank_staffs, class_name: "Dscf::Credit::BankStaff", foreign_key: "bank_branch_id", dependent: :destroy
7
+
8
+ validates :branch_name, presence: true
9
+ validates :branch_name, uniqueness: { scope: :bank_id }
10
+ validates :branch_code, uniqueness: { scope: :bank_id }, allow_blank: true
11
+ validates :status, inclusion: { in: %w[active inactive] }
12
+
13
+ scope :active, -> { where(status: "active") }
14
+
15
+ def self.ransackable_attributes(auth_object = nil)
16
+ %w[id branch_name branch_code branch_address city country contact_email contact_phone status created_at updated_at]
17
+ end
18
+
19
+ def self.ransackable_associations(auth_object = nil)
20
+ %w[bank bank_staffs]
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,26 @@
1
+ module Dscf::Credit
2
+ class BankStaff < ApplicationRecord
3
+ self.table_name = "dscf_credit_bank_staffs"
4
+
5
+ if defined?(Dscf::Core::User)
6
+ belongs_to :user, class_name: "Dscf::Core::User", foreign_key: "user_id"
7
+ end
8
+ belongs_to :bank_branch, class_name: "Dscf::Credit::BankBranch", foreign_key: "bank_branch_id"
9
+
10
+ validates :status, inclusion: { in: %w[active inactive terminated] }
11
+ validates :user_id, uniqueness: { scope: :bank_branch_id }
12
+
13
+ scope :active, -> { where(status: "active") }
14
+ scope :inactive, -> { where(status: "inactive") }
15
+
16
+ def self.ransackable_attributes(auth_object = nil)
17
+ %w[id status created_at updated_at]
18
+ end
19
+
20
+ def self.ransackable_associations(auth_object = nil)
21
+ %w[user bank_branch]
22
+ end
23
+
24
+ delegate :bank, to: :bank_branch, allow_nil: true
25
+ end
26
+ end
@@ -0,0 +1,21 @@
1
+ module Dscf::Credit
2
+ class Category < ApplicationRecord
3
+ self.table_name = "dscf_credit_categories"
4
+ self.inheritance_column = nil # Disable STI since 'type' is a business attribute
5
+
6
+ has_many :credit_lines, class_name: "Dscf::Credit::CreditLine", foreign_key: "category_id", dependent: :nullify
7
+
8
+ validates :type, :name, presence: true
9
+ validates :name, uniqueness: { scope: :type }
10
+
11
+ scope :by_type, ->(category_type) { where(type: category_type) }
12
+
13
+ def self.ransackable_attributes(auth_object = nil)
14
+ %w[id type name description created_at updated_at]
15
+ end
16
+
17
+ def self.ransackable_associations(auth_object = nil)
18
+ %w[credit_lines]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,30 @@
1
+ module Dscf::Credit
2
+ class CreditLine < ApplicationRecord
3
+ self.table_name = "dscf_credit_credit_lines"
4
+
5
+ belongs_to :bank, class_name: "Dscf::Credit::Bank", foreign_key: "bank_id"
6
+ belongs_to :category, class_name: "Dscf::Credit::Category", foreign_key: "category_id"
7
+ belongs_to :created_by, polymorphic: true
8
+ belongs_to :approved_by, polymorphic: true, optional: true
9
+ has_many :credit_line_specs, class_name: "Dscf::Credit::CreditLineSpec", foreign_key: "credit_line_id", dependent: :destroy
10
+ 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
13
+
14
+ validates :name, presence: true
15
+ validates :code, uniqueness: { scope: :bank_id }, allow_blank: true
16
+ validates :status, inclusion: { in: %w[pending approved active suspended] }
17
+
18
+ scope :active, -> { where(status: "active") }
19
+ scope :approved, -> { where(status: "approved") }
20
+ scope :pending, -> { where(status: "pending") }
21
+
22
+ def self.ransackable_attributes(auth_object = nil)
23
+ %w[id name code description status approval_date created_at updated_at]
24
+ end
25
+
26
+ def self.ransackable_associations(auth_object = nil)
27
+ %w[bank category created_by approved_by credit_line_specs loans scoring_tables scoring_parameters]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,37 @@
1
+ module Dscf::Credit
2
+ class CreditLineSpec < ApplicationRecord
3
+ self.table_name = "dscf_credit_credit_line_specs"
4
+
5
+ belongs_to :credit_line, class_name: "Dscf::Credit::CreditLine", foreign_key: "credit_line_id"
6
+ belongs_to :created_by, polymorphic: true
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 }
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
+ validates :max_penalty_days, :loan_duration, :max_interest_calculation_days, numericality: { greater_than: 0 }
12
+ validates :interest_frequency, inclusion: { in: %w[daily weekly monthly quarterly annually] }
13
+ validates :penalty_frequency, inclusion: { in: %w[daily weekly monthly] }
14
+ validate :max_amount_greater_than_min_amount
15
+
16
+ scope :active, -> { where(active: true) }
17
+ scope :by_duration, ->(duration) { where(loan_duration: duration) }
18
+ scope :by_interest_frequency, ->(frequency) { where(interest_frequency: frequency) }
19
+ scope :by_penalty_frequency, ->(frequency) { where(penalty_frequency: frequency) }
20
+
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]
23
+ end
24
+
25
+ def self.ransackable_associations(auth_object = nil)
26
+ %w[credit_line created_by]
27
+ end
28
+
29
+ private
30
+
31
+ def max_amount_greater_than_min_amount
32
+ return unless min_amount && max_amount
33
+
34
+ errors.add(:max_amount, "must be greater than minimum amount") if max_amount <= min_amount
35
+ end
36
+ end
37
+ end