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
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2284e9439102461eb4cb1d93804c47510c1d7b01e476e3ef8986a6b552e4aaea
4
+ data.tar.gz: d14c68b09509e8da8e2eb679f2c0a8905238ec761ff701802f2d30fa31e45db4
5
+ SHA512:
6
+ metadata.gz: fb0041c463dd58d218524a7ef033bc7c3995cb89aedc70faf2df38d0ad3e2e0d210c80fcb650f8271c8dcc09303d58029cae5e0461400e2293fcdb5c50e849da
7
+ data.tar.gz: cb8101b2a8618250a5d961c59746c45e8d364ad921875ad6967ddf458445cb2ea3d8265865a293a04a22a817b7b4653d9301cfed27b55ddd9394f88a73c38560
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright Adoniyas
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,28 @@
1
+ # Dscf::Credit
2
+ Short description and motivation.
3
+
4
+ ## Usage
5
+ How to use my plugin.
6
+
7
+ ## Installation
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem "dscf-credit"
12
+ ```
13
+
14
+ And then execute:
15
+ ```bash
16
+ $ bundle
17
+ ```
18
+
19
+ Or install it yourself as:
20
+ ```bash
21
+ $ gem install dscf-credit
22
+ ```
23
+
24
+ ## Contributing
25
+ Contribution directions go here.
26
+
27
+ ## License
28
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require "bundler/setup"
2
+
3
+ APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
4
+ load "rails/tasks/engine.rake"
5
+
6
+ load "rails/tasks/statistics.rake"
7
+
8
+ require "bundler/gem_tasks"
@@ -0,0 +1,8 @@
1
+ module Dscf
2
+ module Credit
3
+ class ApplicationController < ActionController::API
4
+ include Dscf::Core::Authenticatable
5
+ include Dscf::Core::JsonResponse
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,38 @@
1
+ module Dscf::Credit
2
+ class BankBranchesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:bank_branch).permit(
9
+ :bank_id,
10
+ :branch_name,
11
+ :branch_code,
12
+ :branch_address,
13
+ :city,
14
+ :country,
15
+ :contact_email,
16
+ :contact_phone,
17
+ :status
18
+ )
19
+ end
20
+
21
+ def eager_loaded_associations
22
+ [ :bank, :bank_staffs ]
23
+ end
24
+
25
+ def allowed_order_columns
26
+ %w[id branch_name branch_code city status created_at updated_at]
27
+ end
28
+
29
+ def default_serializer_includes
30
+ {
31
+ index: [ :bank ],
32
+ show: [ :bank, :bank_staffs ],
33
+ create: [ :bank ],
34
+ update: [ :bank, :bank_staffs ]
35
+ }
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,77 @@
1
+ module Dscf::Credit
2
+ class BankStaffsController < ApplicationController
3
+ include Dscf::Core::Common
4
+ include Dscf::Core::JsonResponse
5
+
6
+ def create
7
+ service = BankStaffCreationService.new(flattened_creation_params)
8
+
9
+ if service.call
10
+ render_success(
11
+ data: service.bank_staff,
12
+ status: :created,
13
+ serializer_options: { include: default_serializer_includes[:create] }
14
+ )
15
+ else
16
+ render_error(
17
+ errors: service.errors.full_messages,
18
+ status: :unprocessable_entity
19
+ )
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def flattened_creation_params
26
+ permitted_params = bank_staff_creation_params
27
+ user_attrs = permitted_params.delete(:user_attributes) || {}
28
+ profile_attrs = user_attrs.delete(:user_profile_attributes) || {}
29
+
30
+ permitted_params.merge(user_attrs).merge(profile_attrs)
31
+ end
32
+
33
+ def bank_staff_creation_params
34
+ params.require(:bank_staff).permit(
35
+ :bank_branch_id,
36
+ :status,
37
+ :role_code,
38
+ user_attributes: [
39
+ :email,
40
+ :phone,
41
+ user_profile_attributes: [
42
+ :first_name,
43
+ :last_name,
44
+ :middle_name,
45
+ :position_title,
46
+ :gender
47
+ ]
48
+ ]
49
+ )
50
+ end
51
+
52
+ def model_params
53
+ params.require(:bank_staff).permit(
54
+ :user_id,
55
+ :bank_branch_id,
56
+ :status
57
+ )
58
+ end
59
+
60
+ def eager_loaded_associations
61
+ [ :user, { bank_branch: :bank } ]
62
+ end
63
+
64
+ def allowed_order_columns
65
+ %w[id status created_at updated_at]
66
+ end
67
+
68
+ def default_serializer_includes
69
+ {
70
+ index: [ :user, { bank_branch: :bank } ],
71
+ show: [ { user: [ :user_profile, :roles ] }, { bank_branch: :bank } ],
72
+ create: [ :user, { bank_branch: :bank } ],
73
+ update: [ :user, { bank_branch: :bank } ]
74
+ }
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,39 @@
1
+ module Dscf::Credit
2
+ class BanksController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:bank).permit(
9
+ :user_id,
10
+ :name,
11
+ :registration_number,
12
+ :swift_code,
13
+ :headquarters_address,
14
+ :city,
15
+ :country,
16
+ :contact_email,
17
+ :contact_phone,
18
+ :status
19
+ )
20
+ end
21
+
22
+ def eager_loaded_associations
23
+ [ :user, :bank_branches, :system_config_definitions ]
24
+ end
25
+
26
+ def allowed_order_columns
27
+ %w[id name registration_number swift_code status created_at updated_at]
28
+ end
29
+
30
+ def default_serializer_includes
31
+ {
32
+ index: [ :user, :bank_branches ],
33
+ show: [ :user, :bank_branches, :system_config_definitions ],
34
+ create: [ :user, :bank_branches ],
35
+ update: [ :user, :bank_branches, :system_config_definitions ]
36
+ }
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,32 @@
1
+ module Dscf::Credit
2
+ class CategoriesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:category).permit(
9
+ :type,
10
+ :name,
11
+ :description
12
+ )
13
+ end
14
+
15
+ def eager_loaded_associations
16
+ [ :credit_lines ]
17
+ end
18
+
19
+ def allowed_order_columns
20
+ %w[id type name description created_at updated_at]
21
+ end
22
+
23
+ def default_serializer_includes
24
+ {
25
+ index: [],
26
+ show: [ :credit_lines ],
27
+ create: [],
28
+ update: [ :credit_lines ]
29
+ }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,72 @@
1
+ module Dscf::Credit
2
+ class CreditLineSpecsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def create
6
+ super do
7
+ obj = @clazz.new(model_params)
8
+ obj.created_by = current_user
9
+
10
+ if obj.active?
11
+ @clazz.where(credit_line_id: obj.credit_line_id, active: true).update_all(active: false)
12
+ end
13
+
14
+ obj
15
+ end
16
+ end
17
+
18
+ def update
19
+ super do
20
+ obj = set_object
21
+
22
+ if model_params[:active] == true || model_params[:active] == "true"
23
+ @clazz.where(credit_line_id: obj.credit_line_id, active: true)
24
+ .where.not(id: obj.id)
25
+ .update_all(active: false)
26
+ end
27
+
28
+ obj
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def model_params
35
+ params.require(:credit_line_spec).permit(
36
+ :credit_line_id,
37
+ :min_amount,
38
+ :max_amount,
39
+ :interest_rate,
40
+ :penalty_rate,
41
+ :facilitation_fee_rate,
42
+ :tax_rate,
43
+ :max_penalty_days,
44
+ :loan_duration,
45
+ :interest_frequency,
46
+ :interest_income_tax,
47
+ :vat,
48
+ :max_interest_calculation_days,
49
+ :penalty_frequency,
50
+ :penalty_income_tax,
51
+ :active
52
+ )
53
+ end
54
+
55
+ def eager_loaded_associations
56
+ [ :credit_line, :created_by ]
57
+ end
58
+
59
+ def allowed_order_columns
60
+ %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]
61
+ end
62
+
63
+ def default_serializer_includes
64
+ {
65
+ index: [ :credit_line ],
66
+ show: [ :credit_line, :created_by ],
67
+ create: [ :credit_line, :created_by ],
68
+ update: [ :credit_line, :created_by ]
69
+ }
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,76 @@
1
+ module Dscf::Credit
2
+ class CreditLinesController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ def create
6
+ super do
7
+ credit_line = @clazz.new(model_params)
8
+ credit_line.created_by = current_user
9
+ credit_line
10
+ end
11
+ end
12
+
13
+ def approve
14
+ @obj = @clazz.find(params[:id])
15
+ if @obj.update(
16
+ status: "approved",
17
+ approved_by: current_user,
18
+ approval_date: Time.current
19
+ )
20
+ render_success(data: @obj)
21
+ else
22
+ render_error(errors: @obj.errors.full_messages, status: :unprocessable_entity)
23
+ end
24
+ rescue StandardError => e
25
+ render_error(error: e.message)
26
+ end
27
+
28
+ def reject
29
+ @obj = @clazz.find(params[:id])
30
+ if @obj.update(
31
+ status: "pending",
32
+ approved_by: current_user,
33
+ approval_date: Time.current
34
+ )
35
+ render_success(data: @obj)
36
+ else
37
+ render_error(errors: @obj.errors.full_messages, status: :unprocessable_entity)
38
+ end
39
+ rescue StandardError => e
40
+ render_error(error: e.message)
41
+ end
42
+
43
+ private
44
+
45
+ def model_params
46
+ params.require(:credit_line).permit(
47
+ :bank_id,
48
+ :category_id,
49
+ :name,
50
+ :code,
51
+ :description,
52
+ :status,
53
+ :approved_by_type,
54
+ :approved_by_id,
55
+ :approval_date
56
+ )
57
+ end
58
+
59
+ def eager_loaded_associations
60
+ [ :bank, :category, :created_by, :approved_by, :credit_line_specs ]
61
+ end
62
+
63
+ def allowed_order_columns
64
+ %w[id name code status approval_date created_at updated_at category_id]
65
+ end
66
+
67
+ def default_serializer_includes
68
+ {
69
+ index: [ :bank, :category ],
70
+ show: [ :bank, :category, :created_by, :approved_by, :credit_line_specs ],
71
+ create: [ :bank, :category, :created_by, :approved_by ],
72
+ update: [ :bank, :category, :created_by, :approved_by, :credit_line_specs ]
73
+ }
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,227 @@
1
+ module Dscf::Credit
2
+ class FacilitatorsController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ skip_before_action :authenticate_user, only: [ :submit_additional_info ]
6
+ before_action :set_facilitator_for_approval, only: [ :approve, :reject, :set_limit ]
7
+
8
+ def create
9
+ super do
10
+ user = Dscf::Core::User.find(model_params[:user_id])
11
+ business = user.businesses.first
12
+
13
+ unless business
14
+ return render_error("facilitator.errors.no_business",
15
+ errors: "User must have a business associated to become a facilitator")
16
+ end
17
+
18
+ facilitator_data = model_params.to_h.merge(
19
+ name: business.name,
20
+ type: business.business_type.name.downcase
21
+ )
22
+
23
+ facilitator = @clazz.new(facilitator_data)
24
+ facilitator.kyc_approved_by = current_user
25
+ if facilitator.save
26
+ facilitator.facilitator_performances.create!(
27
+ total_outstanding_loans: 0,
28
+ total_outstanding_amount: 0.0,
29
+ approval_required: false,
30
+ created_by: current_user
31
+ )
32
+ facilitator
33
+ else
34
+ facilitator
35
+ end
36
+ end
37
+ end
38
+
39
+ def approve
40
+ begin
41
+ FacilitatorApprovalService.new(@facilitator, current_user).approve(params[:total_limit])
42
+ render_success("facilitator.success.approved", data: @facilitator)
43
+ rescue StandardError => e
44
+ render_error("facilitator.errors.approval_failed", errors: e.message)
45
+ end
46
+ end
47
+
48
+ def reject
49
+ begin
50
+ FacilitatorApprovalService.new(@facilitator, current_user).reject(params[:rejection_reason])
51
+ render_success("facilitator.success.rejected", data: @facilitator)
52
+ rescue StandardError => e
53
+ render_error("facilitator.errors.rejection_failed", errors: e.message)
54
+ end
55
+ end
56
+
57
+ def set_limit
58
+ begin
59
+ FacilitatorApprovalService.new(@facilitator, current_user).set_limit(params[:total_limit])
60
+ render_success("facilitator.success.limit_updated", data: @facilitator)
61
+ rescue StandardError => e
62
+ render_error("facilitator.errors.limit_update_failed", errors: e.message)
63
+ end
64
+ end
65
+
66
+ def submit_additional_info
67
+ token = params[:token]
68
+ additional_info = params[:additional_info]&.permit!.to_h
69
+
70
+ begin
71
+ result = FacilitatorAdditionalInfoService.new.submit_info(token, additional_info)
72
+ render_success("facilitator.success.additional_info_submitted", data: result)
73
+ rescue StandardError => e
74
+ render_error("facilitator.errors.additional_info_failed", errors: e.message)
75
+ end
76
+ end
77
+
78
+ def batch_create
79
+ facilitators_params = params[:facilitators]
80
+
81
+ unless facilitators_params.is_a?(Array)
82
+ return render_error("facilitator.errors.invalid_batch_format",
83
+ errors: "Expected an array of facilitator objects")
84
+ end
85
+
86
+ if facilitators_params.empty?
87
+ return render_error("facilitator.errors.empty_batch",
88
+ errors: "At least one facilitator is required")
89
+ end
90
+
91
+ if facilitators_params.length > 100
92
+ return render_error("facilitator.errors.batch_too_large",
93
+ errors: "Maximum 100 facilitators allowed per batch")
94
+ end
95
+
96
+ results = {
97
+ successful: [],
98
+ failed: [],
99
+ total_count: facilitators_params.length,
100
+ success_count: 0,
101
+ failure_count: 0
102
+ }
103
+
104
+ facilitators_params.each_with_index do |facilitator_attrs, index|
105
+ begin
106
+ if facilitator_attrs.is_a?(ActionController::Parameters)
107
+ permitted_attrs = facilitator_attrs.permit(:user_id, :bank_id, :kyc_status)
108
+ else
109
+ permitted_attrs = ActionController::Parameters.new(facilitator_attrs)
110
+ .permit(:user_id, :bank_id, :kyc_status)
111
+ end
112
+
113
+ user = Dscf::Core::User.find(permitted_attrs[:user_id])
114
+ business = user.businesses.first
115
+
116
+ unless business
117
+ results[:failed] << {
118
+ index: index,
119
+ user_id: permitted_attrs[:user_id],
120
+ errors: [ "User must have a business associated to become a facilitator" ]
121
+ }
122
+ results[:failure_count] += 1
123
+ next
124
+ end
125
+
126
+ facilitator_data = permitted_attrs.to_h.merge(
127
+ name: business.name,
128
+ type: business.business_type.name.downcase
129
+ )
130
+
131
+ facilitator = @clazz.new(facilitator_data)
132
+ facilitator.kyc_approved_by = current_user
133
+
134
+ if facilitator.save
135
+ facilitator.facilitator_performances.create!(
136
+ total_outstanding_loans: 0,
137
+ total_outstanding_amount: 0.0,
138
+ approval_required: false,
139
+ created_by: current_user
140
+ )
141
+
142
+ results[:successful] << {
143
+ index: index,
144
+ id: facilitator.id,
145
+ name: facilitator.name,
146
+ message: "Successfully created"
147
+ }
148
+ results[:success_count] += 1
149
+ else
150
+ results[:failed] << {
151
+ index: index,
152
+ name: business&.name || "Unknown Business",
153
+ errors: facilitator.errors.full_messages
154
+ }
155
+ results[:failure_count] += 1
156
+ end
157
+ rescue StandardError => e
158
+ user_id = permitted_attrs&.[](:user_id) || facilitator_attrs.try(:[], :user_id)
159
+ business_name = "Unknown Business"
160
+
161
+ if user_id
162
+ begin
163
+ user = Dscf::Core::User.find(user_id)
164
+ business_name = user.businesses.first&.name || "No Business Associated"
165
+ rescue
166
+ business_name = "Invalid User ID"
167
+ end
168
+ end
169
+
170
+ results[:failed] << {
171
+ index: index,
172
+ name: business_name,
173
+ errors: [ e.message ]
174
+ }
175
+ results[:failure_count] += 1
176
+ end
177
+ end
178
+
179
+ if results[:failure_count] == 0
180
+ render_success("facilitator.success.batch_created_all", data: results)
181
+ elsif results[:success_count] == 0
182
+ render_error("facilitator.errors.batch_failed_all", errors: results)
183
+ else
184
+ render json: {
185
+ success: true,
186
+ message: I18n.t("facilitator.success.batch_created_partial"),
187
+ data: results
188
+ }, status: :multi_status
189
+ end
190
+ end
191
+
192
+ private
193
+
194
+ def model_params
195
+ params.require(:facilitator).permit(
196
+ :user_id,
197
+ :bank_id,
198
+ :kyc_status,
199
+ :additional_info
200
+ )
201
+ end
202
+
203
+ def set_facilitator_for_approval
204
+ @facilitator = @clazz.find(params[:id])
205
+ end
206
+
207
+ def eager_loaded_associations
208
+ [ :user, :bank, :kyc_approved_by, :facilitator_performances ]
209
+ end
210
+
211
+ def allowed_order_columns
212
+ %w[id name type kyc_status total_limit created_at updated_at]
213
+ end
214
+
215
+ def default_serializer_includes
216
+ {
217
+ index: [ :user, :bank ],
218
+ show: [ :user, :bank, :kyc_approved_by, :facilitator_performances ],
219
+ create: [ :user, :bank, :kyc_approved_by ],
220
+ update: [ :user, :bank, :kyc_approved_by, :facilitator_performances ],
221
+ approve: [ :user, :bank, :kyc_approved_by, :facilitator_performances ],
222
+ reject: [ :user, :bank, :kyc_approved_by ],
223
+ set_limit: [ :user, :bank, :kyc_approved_by ]
224
+ }
225
+ end
226
+ end
227
+ end
@@ -0,0 +1,41 @@
1
+ module Dscf::Credit
2
+ class LoansController < ApplicationController
3
+ include Dscf::Core::Common
4
+
5
+ private
6
+
7
+ def model_params
8
+ params.require(:loan).permit(
9
+ :loan_profile_id,
10
+ :credit_line_id,
11
+ :payment_request_id,
12
+ :status,
13
+ :principal_amount,
14
+ :accrued_interest,
15
+ :accrued_penalty,
16
+ :facilitation_fee,
17
+ :total_loan_amount,
18
+ :remaining_amount,
19
+ :due_date,
20
+ :disbursed_at
21
+ )
22
+ end
23
+
24
+ def eager_loaded_associations
25
+ [ :loan_profile, :credit_line, :payment_request, :loan_transactions ]
26
+ end
27
+
28
+ def allowed_order_columns
29
+ %w[id status principal_amount total_loan_amount remaining_amount due_date disbursed_at created_at updated_at]
30
+ end
31
+
32
+ def default_serializer_includes
33
+ {
34
+ index: [ :loan_profile, :credit_line, :payment_request ],
35
+ show: [ :loan_profile, :credit_line, :payment_request, :loan_transactions ],
36
+ create: [ :loan_profile, :credit_line, :payment_request ],
37
+ update: [ :loan_profile, :credit_line, :payment_request, :loan_transactions ]
38
+ }
39
+ end
40
+ end
41
+ end