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
data/db/seeds.rb ADDED
@@ -0,0 +1,745 @@
1
+ # DSCF Credit Engine Seed Data
2
+ # This file seeds the database with sample data for all models in the correct dependency order
3
+
4
+ puts "Starting DSCF Credit Engine seed data..."
5
+
6
+ # First, seed business types from dscf-core
7
+ puts "Seeding business types..."
8
+ if defined?(Dscf::Core::BusinessType)
9
+ individual_business_type = Dscf::Core::BusinessType.find_or_create_by(name: 'individual')
10
+ corporate_business_type = Dscf::Core::BusinessType.find_or_create_by(name: 'corporate')
11
+ agent_business_type = Dscf::Core::BusinessType.find_or_create_by(name: 'agent')
12
+ puts "✓ Business types created/found"
13
+ end
14
+
15
+ # Create roles for the system if needed
16
+ puts "Seeding roles..."
17
+ if defined?(Dscf::Core::Role)
18
+ admin_role = Dscf::Core::Role.find_or_create_by(code: 'admin') do |role|
19
+ role.name = 'System Administrator'
20
+ end
21
+
22
+ bank_admin_role = Dscf::Core::Role.find_or_create_by(code: 'bank_admin') do |role|
23
+ role.name = 'Bank Administrator'
24
+ end
25
+
26
+ bank_staff_role = Dscf::Core::Role.find_or_create_by(code: 'bank_staff') do |role|
27
+ role.name = 'Bank Staff'
28
+ end
29
+
30
+ facilitator_role = Dscf::Core::Role.find_or_create_by(code: 'facilitator') do |role|
31
+ role.name = 'Facilitator'
32
+ end
33
+
34
+ puts "✓ Roles created/found"
35
+ end
36
+
37
+ # Create users and their businesses from dscf-core if needed
38
+ # This would typically be seeded by the core engine, but we'll reference existing users
39
+ puts "Checking for existing users..."
40
+
41
+ # users is from dscf-core engine
42
+ if defined?(Dscf::Core::User)
43
+ admin_user = Dscf::Core::User.find_or_create_by(email: 'admin@bunna.com') do |user|
44
+ user.phone = '+251911123456'
45
+ user.verified_at = Time.current
46
+ user.temp_password = false
47
+ user.password = 'SecurePassword123!'
48
+ end
49
+
50
+ bank_admin = Dscf::Core::User.find_or_create_by(email: 'bank.admin@bunna.com') do |user|
51
+ user.phone = '+251922123456'
52
+ user.verified_at = Time.current
53
+ user.temp_password = false
54
+ user.password = 'SecurePassword123!'
55
+ end
56
+
57
+ user1 = Dscf::Core::User.find_or_create_by(email: 'user1@bunna.com') do |user|
58
+ user.phone = '+251933123456'
59
+ user.verified_at = Time.current
60
+ user.temp_password = false
61
+ user.password = 'SecurePassword123!'
62
+ end
63
+
64
+ user2 = Dscf::Core::User.find_or_create_by(email: 'user2@bunna.com') do |user|
65
+ user.phone = '+251944123456'
66
+ user.verified_at = Time.current
67
+ user.temp_password = false
68
+ user.password = 'SecurePassword123!'
69
+ end
70
+
71
+ # Create additional test users for batch testing
72
+ user3 = Dscf::Core::User.find_or_create_by(email: 'user3@bunna.com') do |user|
73
+ user.phone = '+251955123456'
74
+ user.verified_at = Time.current
75
+ user.temp_password = false
76
+ user.password = 'SecurePassword123!'
77
+ end
78
+
79
+ user4 = Dscf::Core::User.find_or_create_by(email: 'user4@bunna.com') do |user|
80
+ user.phone = '+251966123456'
81
+ user.verified_at = Time.current
82
+ user.temp_password = false
83
+ user.password = 'SecurePassword123!'
84
+ end
85
+
86
+ puts "✓ Users created/found"
87
+
88
+ # Create businesses for users
89
+ puts "Seeding businesses..."
90
+ if defined?(Dscf::Core::Business) && defined?(Dscf::Core::BusinessType)
91
+ # Create business for user1 (Individual)
92
+ business1 = Dscf::Core::Business.find_or_create_by(
93
+ user_id: user1.id,
94
+ business_type_id: individual_business_type.id
95
+ ) do |business|
96
+ business.name = 'Individual Trading Business'
97
+ business.description = 'Individual business for trading services'
98
+ business.contact_email = user1.email
99
+ business.contact_phone = user1.phone
100
+ end
101
+
102
+ # Create business for user2 (Corporate)
103
+ business2 = Dscf::Core::Business.find_or_create_by(
104
+ user_id: user2.id,
105
+ business_type_id: corporate_business_type.id
106
+ ) do |business|
107
+ business.name = 'Manufacturing Corp'
108
+ business.description = 'Corporate manufacturing business'
109
+ business.contact_email = user2.email
110
+ business.contact_phone = user2.phone
111
+ end
112
+
113
+ # Create business for user3 (Corporate)
114
+ business3 = Dscf::Core::Business.find_or_create_by(
115
+ user_id: user3.id,
116
+ business_type_id: corporate_business_type.id
117
+ ) do |business|
118
+ business.name = 'Corporate Trading Ltd'
119
+ business.description = 'Corporate business for trading services'
120
+ business.contact_email = user3.email
121
+ business.contact_phone = user3.phone
122
+ end
123
+
124
+ # Create business for user4 (Agent)
125
+ business4 = Dscf::Core::Business.find_or_create_by(
126
+ user_id: user4.id,
127
+ business_type_id: agent_business_type.id
128
+ ) do |business|
129
+ business.name = 'Financial Agent Services'
130
+ business.description = 'Agent business for financial services'
131
+ business.contact_email = user4.email
132
+ business.contact_phone = user4.phone
133
+ end
134
+
135
+ puts "✓ Businesses created/found"
136
+ end
137
+ else
138
+ puts "Warning: Dscf::Core::User not found. Using placeholder user IDs."
139
+ admin_user = OpenStruct.new(id: 1)
140
+ bank_admin = OpenStruct.new(id: 2)
141
+ user1 = OpenStruct.new(id: 3)
142
+ user2 = OpenStruct.new(id: 4)
143
+ user3 = OpenStruct.new(id: 5)
144
+ user4 = OpenStruct.new(id: 6)
145
+ end
146
+
147
+ # 1. Banks (independent)
148
+ puts "Seeding banks..."
149
+ bank1 = Dscf::Credit::Bank.find_or_create_by(registration_number: 'BNK001') do |bank|
150
+ bank.name = 'Commercial Bank of Ethiopia'
151
+ bank.swift_code = 'CBETETAA'
152
+ bank.headquarters_address = 'Addis Ababa, Ethiopia'
153
+ bank.city = 'Addis Ababa'
154
+ bank.country = 'Ethiopia'
155
+ bank.contact_email = 'info@cbe.com.et'
156
+ bank.contact_phone = '+251115510000'
157
+ bank.status = 'active'
158
+ end
159
+
160
+ bank2 = Dscf::Credit::Bank.find_or_create_by(registration_number: 'BNK002') do |bank|
161
+ bank.name = 'Awash Bank'
162
+ bank.swift_code = 'AWSHETAA'
163
+ bank.headquarters_address = 'Addis Ababa, Ethiopia'
164
+ bank.city = 'Addis Ababa'
165
+ bank.country = 'Ethiopia'
166
+ bank.contact_email = 'info@awashbank.com'
167
+ bank.contact_phone = '+251115524000'
168
+ bank.status = 'active'
169
+ end
170
+
171
+ # 2. Bank Branches (depends on banks)
172
+ puts "Seeding bank branches..."
173
+ Dscf::Credit::BankBranch.find_or_create_by(bank: bank1, branch_name: 'Head Office') do |branch|
174
+ branch.branch_code = 'CBE-HO-001'
175
+ branch.branch_address = 'Churchill Avenue, Addis Ababa'
176
+ branch.city = 'Addis Ababa'
177
+ branch.country = 'Ethiopia'
178
+ branch.contact_email = 'headoffice@cbe.com.et'
179
+ branch.contact_phone = '+251115510001'
180
+ branch.status = 'active'
181
+ end
182
+
183
+ Dscf::Credit::BankBranch.find_or_create_by(bank: bank1, branch_name: 'Merkato Branch') do |branch|
184
+ branch.branch_code = 'CBE-MER-002'
185
+ branch.branch_address = 'Merkato, Addis Ababa'
186
+ branch.city = 'Addis Ababa'
187
+ branch.country = 'Ethiopia'
188
+ branch.contact_email = 'merkato@cbe.com.et'
189
+ branch.contact_phone = '+251115510002'
190
+ branch.status = 'active'
191
+ end
192
+
193
+ Dscf::Credit::BankBranch.find_or_create_by(bank: bank2, branch_name: 'Main Branch') do |branch|
194
+ branch.branch_code = 'AWB-MN-001'
195
+ branch.branch_address = 'Ras Desta Damtew Avenue, Addis Ababa'
196
+ branch.city = 'Addis Ababa'
197
+ branch.country = 'Ethiopia'
198
+ branch.contact_email = 'main@awashbank.com'
199
+ branch.contact_phone = '+251115524001'
200
+ branch.status = 'active'
201
+ end
202
+
203
+ # 3. Bank Staff (depends on bank branches and users from dscf-core)
204
+ puts "Seeding bank staff..."
205
+
206
+ # Get bank branches
207
+ cbe_main_branch = Dscf::Credit::BankBranch.find_by(bank: bank1, branch_name: 'Main Branch')
208
+ cbe_merkato_branch = Dscf::Credit::BankBranch.find_by(bank: bank1, branch_name: 'Merkato Branch')
209
+ awash_main_branch = Dscf::Credit::BankBranch.find_by(bank: bank2, branch_name: 'Main Branch')
210
+
211
+ # Create bank staff only if we have users from dscf-core
212
+ if defined?(Dscf::Core::User) && Dscf::Core::User.exists?
213
+ users = Dscf::Core::User.limit(6)
214
+
215
+ # CBE Main Branch Staff
216
+ if cbe_main_branch && users.first
217
+ Dscf::Credit::BankStaff.find_or_create_by(
218
+ user: users.first,
219
+ bank_branch: cbe_main_branch
220
+ ) do |staff|
221
+ staff.status = 'active'
222
+ end
223
+ end
224
+
225
+ if cbe_main_branch && users.second
226
+ Dscf::Credit::BankStaff.find_or_create_by(
227
+ user: users.second,
228
+ bank_branch: cbe_main_branch
229
+ ) do |staff|
230
+ staff.status = 'active'
231
+ end
232
+ end
233
+
234
+ # CBE Merkato Branch Staff
235
+ if cbe_merkato_branch && users.third
236
+ Dscf::Credit::BankStaff.find_or_create_by(
237
+ user: users.third,
238
+ bank_branch: cbe_merkato_branch
239
+ ) do |staff|
240
+ staff.status = 'active'
241
+ end
242
+ end
243
+
244
+ if cbe_merkato_branch && users.fourth
245
+ Dscf::Credit::BankStaff.find_or_create_by(
246
+ user: users.fourth,
247
+ bank_branch: cbe_merkato_branch
248
+ ) do |staff|
249
+ staff.status = 'active'
250
+ end
251
+ end
252
+
253
+ # Awash Bank Main Branch Staff
254
+ if awash_main_branch && users.fifth
255
+ Dscf::Credit::BankStaff.find_or_create_by(
256
+ user: users.fifth,
257
+ bank_branch: awash_main_branch
258
+ ) do |staff|
259
+ staff.status = 'active'
260
+ end
261
+ end
262
+
263
+ if awash_main_branch && users[5]
264
+ Dscf::Credit::BankStaff.find_or_create_by(
265
+ user: users[5],
266
+ bank_branch: awash_main_branch
267
+ ) do |staff|
268
+ staff.status = 'active'
269
+ end
270
+ end
271
+ else
272
+ puts " Skipping bank staff creation - no users found from dscf-core"
273
+ end
274
+
275
+ # 4. System Config Definitions (depends on banks)
276
+ puts "Seeding system config definitions..."
277
+ config_def1 = Dscf::Credit::SystemConfigDefinition.find_or_create_by(
278
+ bank: bank1,
279
+ config_key: 'max_loan_amount'
280
+ ) do |config|
281
+ config.description = 'Maximum loan amount allowed per transaction'
282
+ config.value_type = 'decimal'
283
+ config.frequency = 'monthly'
284
+ end
285
+
286
+ config_def2 = Dscf::Credit::SystemConfigDefinition.find_or_create_by(
287
+ bank: bank1,
288
+ config_key: 'interest_calculation_method'
289
+ ) do |config|
290
+ config.description = 'Method used for calculating interest'
291
+ config.value_type = 'string'
292
+ config.frequency = 'quarterly'
293
+ end
294
+
295
+ config_def3 = Dscf::Credit::SystemConfigDefinition.find_or_create_by(
296
+ bank: bank2,
297
+ config_key: 'max_loan_term_days'
298
+ ) do |config|
299
+ config.description = 'Maximum loan term in days'
300
+ config.value_type = 'integer'
301
+ config.frequency = 'yearly'
302
+ end
303
+
304
+ # 4. System Configs (depends on system config definitions)
305
+ puts "Seeding system configs..."
306
+ Dscf::Credit::SystemConfig.find_or_create_by(config_definition: config_def1) do |config|
307
+ config.config_value = '500000.00'
308
+ config.status = 'approved'
309
+ config.approved_by = admin_user
310
+ config.last_updated_by = admin_user
311
+ end
312
+
313
+ Dscf::Credit::SystemConfig.find_or_create_by(config_definition: config_def2) do |config|
314
+ config.config_value = 'daily'
315
+ config.status = 'approved'
316
+ config.approved_by = admin_user
317
+ config.last_updated_by = admin_user
318
+ end
319
+
320
+ Dscf::Credit::SystemConfig.find_or_create_by(config_definition: config_def3) do |config|
321
+ config.config_value = '180'
322
+ config.status = 'pending'
323
+ config.last_updated_by = bank_admin
324
+ end
325
+
326
+ # 4.5. Categories (independent)
327
+ puts "Seeding categories..."
328
+ sme_category = Dscf::Credit::Category.find_or_create_by(type: 'credit_line', name: 'SME') do |category|
329
+ category.description = 'Small and Medium Enterprise credit category'
330
+ end
331
+
332
+ personal_category = Dscf::Credit::Category.find_or_create_by(type: 'credit_line', name: 'Personal') do |category|
333
+ category.description = 'Personal credit category for individuals'
334
+ end
335
+
336
+ agricultural_category = Dscf::Credit::Category.find_or_create_by(type: 'credit_line', name: 'Agricultural') do |category|
337
+ category.description = 'Agricultural sector credit category'
338
+ end
339
+
340
+ corporate_category = Dscf::Credit::Category.find_or_create_by(type: 'credit_line', name: 'Corporate') do |category|
341
+ category.description = 'Corporate credit category for large businesses'
342
+ end
343
+
344
+ # 5. Credit Lines (depends on banks, users, and categories)
345
+ puts "Seeding credit lines..."
346
+ credit_line1 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'SME Credit Line') do |line|
347
+ line.category = sme_category
348
+ line.code = 'CBE-SME-001'
349
+ line.description = 'Credit line for Small and Medium Enterprises'
350
+ line.status = 'approved'
351
+ line.created_by = admin_user
352
+ line.approved_by = bank_admin
353
+ line.approval_date = 1.week.ago
354
+ end
355
+
356
+ credit_line2 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'Personal Credit Line') do |line|
357
+ line.category = personal_category
358
+ line.code = 'CBE-PER-002'
359
+ line.description = 'Personal credit line for individuals'
360
+ line.status = 'approved'
361
+ line.created_by = admin_user
362
+ line.approved_by = bank_admin
363
+ line.approval_date = 5.days.ago
364
+ end
365
+
366
+ Dscf::Credit::CreditLine.find_or_create_by(bank: bank2, name: 'Agricultural Credit Line') do |line|
367
+ line.category = agricultural_category
368
+ line.code = 'AWB-AGR-001'
369
+ line.description = 'Credit line for agricultural sector'
370
+ line.status = 'pending'
371
+ line.created_by = bank_admin
372
+ line.approved_by = admin_user
373
+ end
374
+
375
+ # 6. Credit Line Specs (depends on credit lines)
376
+ puts "Seeding credit line specs..."
377
+ Dscf::Credit::CreditLineSpec.find_or_create_by(credit_line: credit_line1) do |spec|
378
+ spec.min_amount = 10000.00
379
+ spec.max_amount = 500000.00
380
+ spec.interest_rate = 0.15
381
+ spec.penalty_rate = 0.02
382
+ spec.facilitation_fee_rate = 0.01
383
+ spec.tax_rate = 0.05
384
+ spec.max_penalty_days = 30
385
+ spec.loan_duration = 180
386
+ spec.interest_frequency = 'monthly'
387
+ spec.interest_income_tax = 0.03
388
+ spec.vat = 0.15
389
+ spec.max_interest_calculation_days = 365
390
+ spec.penalty_frequency = 'weekly'
391
+ spec.penalty_income_tax = 0.025
392
+ spec.active = true
393
+ spec.created_by = admin_user
394
+ end
395
+
396
+ Dscf::Credit::CreditLineSpec.find_or_create_by(credit_line: credit_line2) do |spec|
397
+ spec.min_amount = 5000.00
398
+ spec.max_amount = 100000.00
399
+ spec.interest_rate = 0.18
400
+ spec.penalty_rate = 0.025
401
+ spec.facilitation_fee_rate = 0.015
402
+ spec.tax_rate = 0.05
403
+ spec.max_penalty_days = 15
404
+ spec.loan_duration = 90
405
+ spec.interest_frequency = 'weekly'
406
+ spec.interest_income_tax = 0.035
407
+ spec.vat = 0.15
408
+ spec.max_interest_calculation_days = 180
409
+ spec.penalty_frequency = 'daily'
410
+ spec.penalty_income_tax = 0.03
411
+ spec.active = true
412
+ spec.created_by = admin_user
413
+ end
414
+
415
+ # 7. Scoring Parameters (depends on banks)
416
+ puts "Seeding scoring parameters..."
417
+ scoring_param1 = Dscf::Credit::ScoringParameter.find_or_create_by(
418
+ bank: bank1,
419
+ name: 'Monthly Income'
420
+ ) do |param|
421
+ param.description = 'Monthly income of the applicant'
422
+ param.data_type = 'decimal'
423
+ param.type = 'financial'
424
+ param.weight = 0.3
425
+ param.min_value = 0
426
+ param.max_value = 1000000
427
+ param.active = true
428
+ param.approved_by = admin_user
429
+ param.created_by = admin_user
430
+ param.expires_at = 1.year.from_now
431
+ end
432
+
433
+ scoring_param2 = Dscf::Credit::ScoringParameter.find_or_create_by(
434
+ bank: bank1,
435
+ name: 'Credit History Score'
436
+ ) do |param|
437
+ param.description = 'Credit history score from credit bureau'
438
+ param.data_type = 'integer'
439
+ param.type = 'credit_history'
440
+ param.weight = 0.4
441
+ param.min_value = 300
442
+ param.max_value = 850
443
+ param.active = true
444
+ param.approved_by = admin_user
445
+ param.created_by = admin_user
446
+ param.expires_at = 1.year.from_now
447
+ end
448
+
449
+ scoring_param3 = Dscf::Credit::ScoringParameter.find_or_create_by(
450
+ bank: bank1,
451
+ name: 'Employment Type'
452
+ ) do |param|
453
+ param.description = 'Type of employment (permanent, contract, self-employed)'
454
+ param.data_type = 'string'
455
+ param.type = 'employment'
456
+ param.weight = 0.2
457
+ param.active = true
458
+ param.approved_by = bank_admin
459
+ param.created_by = admin_user
460
+ param.expires_at = 1.year.from_now
461
+ end
462
+
463
+ # 8. Parameter Normalizers (depends on scoring parameters)
464
+ puts "Seeding parameter normalizers..."
465
+ # Ensure we have the scoring parameter reference
466
+ employment_param = Dscf::Credit::ScoringParameter.find_by(bank: bank1, name: 'Employment Type')
467
+
468
+ Dscf::Credit::ParameterNormalizer.find_or_create_by(
469
+ scoring_parameter: employment_param,
470
+ raw_value: 'permanent'
471
+ ) do |normalizer|
472
+ normalizer.name = 'Permanent Employment'
473
+ normalizer.description = 'Full-time permanent employment'
474
+ normalizer.normalized_value = 100
475
+ end
476
+
477
+ Dscf::Credit::ParameterNormalizer.find_or_create_by(
478
+ scoring_parameter: employment_param,
479
+ raw_value: 'contract'
480
+ ) do |normalizer|
481
+ normalizer.name = 'Contract Employment'
482
+ normalizer.description = 'Contract-based employment'
483
+ normalizer.normalized_value = 70
484
+ end
485
+
486
+ Dscf::Credit::ParameterNormalizer.find_or_create_by(
487
+ scoring_parameter: employment_param,
488
+ raw_value: 'self_employed'
489
+ ) do |normalizer|
490
+ normalizer.name = 'Self Employed'
491
+ normalizer.description = 'Self-employed individual'
492
+ normalizer.normalized_value = 60
493
+ end
494
+
495
+ # 8.5. Scoring Tables (depends on credit lines and scoring parameters)
496
+ puts "Seeding scoring tables..."
497
+ # Get references to scoring parameters
498
+ monthly_income_param = Dscf::Credit::ScoringParameter.find_by(bank: bank1, name: 'Monthly Income')
499
+ credit_history_param = Dscf::Credit::ScoringParameter.find_by(bank: bank1, name: 'Credit History Score')
500
+ employment_param = Dscf::Credit::ScoringParameter.find_by(bank: bank1, name: 'Employment Type')
501
+
502
+ # Create scoring tables for credit_line1 (SME Credit Line)
503
+ if monthly_income_param
504
+ Dscf::Credit::ScoringTable.find_or_create_by(
505
+ credit_line: credit_line1,
506
+ scoring_parameter: monthly_income_param
507
+ ) do |scoring_table|
508
+ scoring_table.weight = 0.4
509
+ scoring_table.active = true
510
+ scoring_table.created_by = admin_user
511
+ end
512
+ end
513
+
514
+ if credit_history_param
515
+ Dscf::Credit::ScoringTable.find_or_create_by(
516
+ credit_line: credit_line1,
517
+ scoring_parameter: credit_history_param
518
+ ) do |scoring_table|
519
+ scoring_table.weight = 0.35
520
+ scoring_table.active = true
521
+ scoring_table.created_by = admin_user
522
+ end
523
+ end
524
+
525
+ if employment_param
526
+ Dscf::Credit::ScoringTable.find_or_create_by(
527
+ credit_line: credit_line1,
528
+ scoring_parameter: employment_param
529
+ ) do |scoring_table|
530
+ scoring_table.weight = 0.25
531
+ scoring_table.active = true
532
+ scoring_table.created_by = admin_user
533
+ end
534
+ end
535
+
536
+ # Create scoring tables for credit_line2 (Personal Credit Line)
537
+ if monthly_income_param
538
+ Dscf::Credit::ScoringTable.find_or_create_by(
539
+ credit_line: credit_line2,
540
+ scoring_parameter: monthly_income_param
541
+ ) do |scoring_table|
542
+ scoring_table.weight = 0.5
543
+ scoring_table.active = true
544
+ scoring_table.created_by = admin_user
545
+ end
546
+ end
547
+
548
+ if credit_history_param
549
+ Dscf::Credit::ScoringTable.find_or_create_by(
550
+ credit_line: credit_line2,
551
+ scoring_parameter: credit_history_param
552
+ ) do |scoring_table|
553
+ scoring_table.weight = 0.3
554
+ scoring_table.active = true
555
+ scoring_table.created_by = admin_user
556
+ end
557
+ end
558
+
559
+ if employment_param
560
+ Dscf::Credit::ScoringTable.find_or_create_by(
561
+ credit_line: credit_line2,
562
+ scoring_parameter: employment_param
563
+ ) do |scoring_table|
564
+ scoring_table.weight = 0.2
565
+ scoring_table.active = true
566
+ scoring_table.created_by = admin_user
567
+ end
568
+ end
569
+
570
+ # 9. Loan Profiles (depends on banks and users)
571
+ puts "Seeding loan profiles..."
572
+ loan_profile1 = Dscf::Credit::LoanProfile.find_or_create_by(
573
+ bank: bank1,
574
+ user: user2
575
+ ) do |profile|
576
+ profile.status = 'approved'
577
+ profile.total_amount = 100000.00
578
+ profile.available_amount = 75000.00
579
+ profile.approved_by = bank_admin
580
+ # Note: backer will be set when facilitators are created via API
581
+ end
582
+
583
+ # 12. Loan Profile Scoring Specs (depends on loan profiles)
584
+ puts "Seeding loan profile scoring specs..."
585
+ Dscf::Credit::LoanProfileScoringSpec.create!(
586
+ loan_profile: loan_profile1,
587
+ scoring_input_data: {
588
+ 'monthly_income' => 45000,
589
+ 'credit_history_score' => 720,
590
+ 'employment_type' => 'permanent',
591
+ 'years_at_job' => 3,
592
+ 'existing_debt' => 15000
593
+ },
594
+ score: 78.5,
595
+ active: true,
596
+ approved_by: admin_user,
597
+ created_by: admin_user,
598
+ expires_at: 6.months.from_now
599
+ )
600
+
601
+ # 13. Payment Requests (depends on users)
602
+ puts "Seeding payment requests..."
603
+ payment_request1 = Dscf::Credit::PaymentRequest.create!(
604
+ user: user2,
605
+ order_id: "ORD-#{Time.current.to_i}-001",
606
+ request_type: 'loan_disbursement',
607
+ amount: 50000.00,
608
+ receiver_account_reference: 'ACC-BORROWER-001',
609
+ status: 'approved',
610
+ initiated_at: 2.days.ago,
611
+ approved_at: 1.day.ago
612
+ )
613
+
614
+ payment_request2 = Dscf::Credit::PaymentRequest.create!(
615
+ user: user2,
616
+ order_id: "ORD-#{Time.current.to_i}-002",
617
+ request_type: 'loan_repayment',
618
+ amount: 15000.00,
619
+ receiver_account_reference: 'ACC-BANK-001',
620
+ status: 'pending',
621
+ initiated_at: 1.hour.ago
622
+ )
623
+
624
+ # 14. Loans (depends on loan profiles, credit lines, and payment requests)
625
+ puts "Seeding loans..."
626
+ loan1 = Dscf::Credit::Loan.create!(
627
+ loan_profile: loan_profile1,
628
+ credit_line: credit_line1,
629
+ payment_request: payment_request1,
630
+ status: 'active',
631
+ principal_amount: 50000.00,
632
+ accrued_interest: 2500.00,
633
+ accrued_penalty: 0.00,
634
+ facilitation_fee: 1000.00,
635
+ total_loan_amount: 53500.00,
636
+ remaining_amount: 53500.00,
637
+ due_date: 3.months.from_now.to_date,
638
+ disbursed_at: 1.day.ago
639
+ )
640
+
641
+ # 15. Loan Transactions (depends on loans)
642
+ puts "Seeding loan transactions..."
643
+ Dscf::Credit::LoanTransaction.create!(
644
+ loan: loan1,
645
+ transaction_type: 'disbursement',
646
+ amount: 50000.00,
647
+ transaction_reference: "TXN-DISB-#{Time.current.to_i}",
648
+ status: 'completed'
649
+ )
650
+
651
+ Dscf::Credit::LoanTransaction.create!(
652
+ loan: loan1,
653
+ transaction_type: 'interest_accrual',
654
+ amount: 2500.00,
655
+ transaction_reference: "TXN-INT-#{Time.current.to_i}",
656
+ status: 'completed'
657
+ )
658
+
659
+ # 16. Daily Routine Transactions (depends on loans)
660
+ puts "Seeding daily routine transactions..."
661
+ Dscf::Credit::DailyRoutineTransaction.create!(
662
+ loan: loan1,
663
+ routine_type: 'interest_calculation',
664
+ amount: 83.33,
665
+ status: 'completed',
666
+ initiated_at: Date.current.beginning_of_day,
667
+ approved_at: Date.current.beginning_of_day + 1.hour
668
+ )
669
+
670
+ # 17. Payments (depends on payment requests)
671
+ puts "Seeding payments..."
672
+ Dscf::Credit::Payment.create!(
673
+ payment_request: payment_request1,
674
+ amount: 50000.00,
675
+ receiver_account_reference: 'ACC-BORROWER-001',
676
+ transaction_reference: "PAY-#{Time.current.to_i}-001",
677
+ status: 'completed',
678
+ processed_at: 1.day.ago
679
+ )
680
+
681
+ # 18. Accounting Audit Requests (depends on various entities)
682
+ puts "Seeding accounting audit requests..."
683
+ audit_request1 = Dscf::Credit::AccountingAuditRequest.create!(
684
+ source_entity: loan1,
685
+ transaction_type: 'debit',
686
+ amount: 50000.00,
687
+ webhook_key: "WEBHOOK-#{SecureRandom.hex(16)}",
688
+ status: 'completed'
689
+ )
690
+
691
+ Dscf::Credit::AccountingAuditRequest.create!(
692
+ source_entity: payment_request1,
693
+ transaction_type: 'transfer',
694
+ amount: 50000.00,
695
+ webhook_key: "WEBHOOK-#{SecureRandom.hex(16)}",
696
+ status: 'pending'
697
+ )
698
+
699
+ # 19. Accounting Entries (depends on audit requests)
700
+ puts "Seeding accounting entries..."
701
+ Dscf::Credit::AccountingEntry.create!(
702
+ audit_request: audit_request1,
703
+ account_code: 'LOAN_ASSET_001',
704
+ entry_type: 'debit',
705
+ amount: 50000.00,
706
+ status: 'completed'
707
+ )
708
+
709
+ Dscf::Credit::AccountingEntry.create!(
710
+ audit_request: audit_request1,
711
+ account_code: 'CASH_001',
712
+ entry_type: 'credit',
713
+ amount: 50000.00,
714
+ status: 'completed'
715
+ )
716
+
717
+ # 20. Failed Operations Logs (depends on various entities)
718
+ puts "Seeding failed operations logs..."
719
+ Dscf::Credit::FailedOperationsLog.create!(
720
+ entity: payment_request2,
721
+ failure_type: 'payment_processing',
722
+ reason: 'Payment gateway did not respond within timeout period',
723
+ retry_count: 2,
724
+ resolved: false
725
+ )
726
+
727
+ puts "DSCF Credit Engine seed data completed successfully!"
728
+ puts "Created sample data for all models with proper relationships."
729
+ puts "Summary:"
730
+ puts "- Users: #{Dscf::Core::User.count}"
731
+ puts "- Business Types: #{Dscf::Core::BusinessType.count}"
732
+ puts "- Businesses: #{Dscf::Core::Business.count}"
733
+ puts "- Banks: #{Dscf::Credit::Bank.count}"
734
+ puts "- Bank Branches: #{Dscf::Credit::BankBranch.count}"
735
+ puts "- Bank Staff: #{Dscf::Credit::BankStaff.count}"
736
+ puts "- Categories: #{Dscf::Credit::Category.count}"
737
+ puts "- Credit Lines: #{Dscf::Credit::CreditLine.count}"
738
+ puts "- Loan Profiles: #{Dscf::Credit::LoanProfile.count}"
739
+ puts "- Loans: #{Dscf::Credit::Loan.count}"
740
+ puts "- Payment Requests: #{Dscf::Credit::PaymentRequest.count}"
741
+ puts "- Payments: #{Dscf::Credit::Payment.count}"
742
+ puts "- Facilitators: #{Dscf::Credit::Facilitator.count} (will be created via API)"
743
+ puts "- System Configs: #{Dscf::Credit::SystemConfig.count}"
744
+ puts "- Scoring Parameters: #{Dscf::Credit::ScoringParameter.count}"
745
+ puts "- Scoring Tables: #{Dscf::Credit::ScoringTable.count}"