dscf-credit 0.1.1 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/concerns/dscf/core/authenticatable.rb +81 -0
- data/app/controllers/concerns/dscf/core/common.rb +200 -0
- data/app/controllers/concerns/dscf/core/filterable.rb +12 -0
- data/app/controllers/concerns/dscf/core/json_response.rb +77 -0
- data/app/controllers/concerns/dscf/core/pagination.rb +71 -0
- data/app/controllers/concerns/dscf/core/token_authenticatable.rb +53 -0
- data/app/controllers/concerns/dscf/credit/reviewable.rb +112 -0
- data/app/controllers/dscf/credit/categories_controller.rb +6 -5
- data/app/controllers/dscf/credit/credit_limit_calculations_controller.rb +50 -0
- data/app/controllers/dscf/credit/credit_line_specs_controller.rb +2 -1
- data/app/controllers/dscf/credit/credit_lines_controller.rb +11 -38
- data/app/controllers/dscf/credit/disbursements_controller.rb +55 -0
- data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -0
- data/app/controllers/dscf/credit/facilitators_controller.rb +39 -150
- data/app/controllers/dscf/credit/loan_profiles_controller.rb +138 -0
- data/app/controllers/dscf/credit/payment_requests_controller.rb +54 -5
- data/app/controllers/dscf/credit/repayments_controller.rb +53 -0
- data/app/controllers/dscf/credit/scoring_param_types_controller.rb +31 -0
- data/app/controllers/dscf/credit/scoring_parameters_controller.rb +13 -8
- data/app/controllers/dscf/credit/scoring_tables_controller.rb +8 -8
- data/app/controllers/dscf/credit/system_configs_controller.rb +10 -7
- data/app/models/dscf/credit/bank_branch.rb +2 -1
- data/app/models/dscf/credit/category.rb +4 -2
- data/app/models/dscf/credit/credit_line.rb +9 -6
- data/app/models/dscf/credit/credit_line_spec.rb +3 -3
- data/app/models/dscf/credit/eligible_credit_line.rb +28 -0
- data/app/models/dscf/credit/facilitator.rb +5 -4
- data/app/models/dscf/credit/facilitator_performance.rb +1 -2
- data/app/models/dscf/credit/loan_profile.rb +8 -4
- data/app/models/dscf/credit/loan_profile_scoring_spec.rb +4 -6
- data/app/models/dscf/credit/scoring_param_type.rb +17 -0
- data/app/models/dscf/credit/scoring_parameter.rb +8 -7
- data/app/models/dscf/credit/scoring_table.rb +4 -4
- data/app/models/dscf/credit/system_config.rb +5 -4
- data/app/serializers/dscf/credit/bank_branch_serializer.rb +1 -0
- data/app/serializers/dscf/credit/category_serializer.rb +3 -1
- data/app/serializers/dscf/credit/credit_line_serializer.rb +4 -2
- data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +1 -1
- data/app/serializers/dscf/credit/daily_routine_transaction_serializer.rb +8 -0
- data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +8 -0
- data/app/serializers/dscf/credit/facilitator_performance_serializer.rb +1 -1
- data/app/serializers/dscf/credit/facilitator_serializer.rb +2 -2
- data/app/serializers/dscf/credit/loan_profile_scoring_spec_serializer.rb +8 -0
- data/app/serializers/dscf/credit/loan_profile_serializer.rb +15 -0
- data/app/serializers/dscf/credit/loan_serializer.rb +12 -0
- data/app/serializers/dscf/credit/loan_transaction_serializer.rb +8 -0
- data/app/serializers/dscf/credit/payment_request_serializer.rb +10 -0
- data/app/serializers/dscf/credit/payment_serializer.rb +8 -0
- data/app/serializers/dscf/credit/scoring_param_type_serializer.rb +7 -0
- data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +6 -3
- data/app/serializers/dscf/credit/scoring_table_serializer.rb +1 -1
- data/app/serializers/dscf/credit/system_config_serializer.rb +2 -2
- data/app/services/dscf/credit/credit_limit_calculation_service.rb +153 -0
- data/app/services/dscf/credit/disbursement_service.rb +180 -0
- data/app/services/dscf/credit/facilitator_approval_service.rb +4 -3
- data/app/services/dscf/credit/facilitator_creation_service.rb +157 -0
- data/app/services/dscf/credit/repayment_service.rb +216 -0
- data/app/services/dscf/credit/risk_application_service.rb +27 -0
- data/app/services/dscf/credit/scoring_service.rb +297 -0
- data/config/locales/en.yml +125 -8
- data/config/routes.rb +42 -11
- data/db/migrate/20250822091011_create_dscf_credit_categories.rb +2 -0
- data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +7 -4
- data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +1 -0
- data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +5 -2
- data/db/migrate/20250822092040_create_dscf_credit_scoring_param_types.rb +12 -0
- data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +11 -6
- data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +6 -3
- data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +5 -7
- data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +5 -2
- data/db/migrate/20250822092528_create_dscf_credit_facilitator_performances.rb +0 -3
- data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +2 -2
- data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +18 -0
- data/db/seeds.rb +134 -40
- data/lib/dscf/credit/version.rb +1 -1
- data/spec/factories/dscf/credit/categories.rb +1 -0
- data/spec/factories/dscf/credit/credit_line_specs.rb +1 -0
- data/spec/factories/dscf/credit/credit_lines.rb +9 -7
- data/spec/factories/dscf/credit/eligible_credit_lines.rb +33 -0
- data/spec/factories/dscf/credit/facilitator_performances.rb +0 -5
- data/spec/factories/dscf/credit/facilitators.rb +6 -1
- data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +1 -7
- data/spec/factories/dscf/credit/loan_profiles.rb +11 -6
- data/spec/factories/dscf/credit/scoring_param_types.rb +31 -0
- data/spec/factories/dscf/credit/scoring_parameters.rb +26 -4
- data/spec/factories/dscf/credit/scoring_tables.rb +1 -1
- data/spec/factories/dscf/credit/system_configs.rb +8 -2
- metadata +50 -2
data/db/seeds.rb
CHANGED
@@ -170,7 +170,7 @@ end
|
|
170
170
|
|
171
171
|
# 2. Bank Branches (depends on banks)
|
172
172
|
puts "Seeding bank branches..."
|
173
|
-
Dscf::Credit::BankBranch.find_or_create_by(bank: bank1, branch_name: 'Head Office') do |branch|
|
173
|
+
cbe_head_office = Dscf::Credit::BankBranch.find_or_create_by(bank: bank1, branch_name: 'Head Office') do |branch|
|
174
174
|
branch.branch_code = 'CBE-HO-001'
|
175
175
|
branch.branch_address = 'Churchill Avenue, Addis Ababa'
|
176
176
|
branch.city = 'Addis Ababa'
|
@@ -306,14 +306,16 @@ puts "Seeding system configs..."
|
|
306
306
|
Dscf::Credit::SystemConfig.find_or_create_by(config_definition: config_def1) do |config|
|
307
307
|
config.config_value = '500000.00'
|
308
308
|
config.status = 'approved'
|
309
|
-
config.
|
309
|
+
config.reviewed_by = admin_user
|
310
|
+
config.review_date = Time.current
|
310
311
|
config.last_updated_by = admin_user
|
311
312
|
end
|
312
313
|
|
313
314
|
Dscf::Credit::SystemConfig.find_or_create_by(config_definition: config_def2) do |config|
|
314
315
|
config.config_value = 'daily'
|
315
316
|
config.status = 'approved'
|
316
|
-
config.
|
317
|
+
config.reviewed_by = admin_user
|
318
|
+
config.review_date = Time.current
|
317
319
|
config.last_updated_by = admin_user
|
318
320
|
end
|
319
321
|
|
@@ -341,6 +343,35 @@ corporate_category = Dscf::Credit::Category.find_or_create_by(type: 'credit_line
|
|
341
343
|
category.description = 'Corporate credit category for large businesses'
|
342
344
|
end
|
343
345
|
|
346
|
+
# Add eligibility category for scoring
|
347
|
+
eligibility_category = Dscf::Credit::Category.find_or_create_by(type: 'eligibility', name: 'default') do |category|
|
348
|
+
category.description = 'Default eligibility category for credit scoring'
|
349
|
+
end
|
350
|
+
|
351
|
+
puts "✓ Categories created/found"
|
352
|
+
|
353
|
+
# 4.6. Scoring Param Types (independent)
|
354
|
+
puts "Seeding scoring param types..."
|
355
|
+
consumer_type = Dscf::Credit::ScoringParamType.find_or_create_by(name: 'Consumer') do |type|
|
356
|
+
type.description = 'Parameters for consumer credit scoring'
|
357
|
+
end
|
358
|
+
|
359
|
+
facilitator_type = Dscf::Credit::ScoringParamType.find_or_create_by(name: 'Facilitator') do |type|
|
360
|
+
type.description = 'Parameters for facilitator credit scoring'
|
361
|
+
end
|
362
|
+
|
363
|
+
wholesaler_type = Dscf::Credit::ScoringParamType.find_or_create_by(name: 'Wholesaler') do |type|
|
364
|
+
type.description = 'Parameters for wholesaler credit scoring'
|
365
|
+
end
|
366
|
+
|
367
|
+
retailer_type = Dscf::Credit::ScoringParamType.find_or_create_by(name: 'Retailer') do |type|
|
368
|
+
type.description = 'Parameters for retailer credit scoring'
|
369
|
+
end
|
370
|
+
|
371
|
+
manufacturer_type = Dscf::Credit::ScoringParamType.find_or_create_by(name: 'Manufacturer') do |type|
|
372
|
+
type.description = 'Parameters for manufacturer credit scoring'
|
373
|
+
end
|
374
|
+
|
344
375
|
# 5. Credit Lines (depends on banks, users, and categories)
|
345
376
|
puts "Seeding credit lines..."
|
346
377
|
credit_line1 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'SME Credit Line') do |line|
|
@@ -349,8 +380,8 @@ credit_line1 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'SM
|
|
349
380
|
line.description = 'Credit line for Small and Medium Enterprises'
|
350
381
|
line.status = 'approved'
|
351
382
|
line.created_by = admin_user
|
352
|
-
line.
|
353
|
-
line.
|
383
|
+
line.reviewed_by = bank_admin
|
384
|
+
line.review_date = 1.week.ago
|
354
385
|
end
|
355
386
|
|
356
387
|
credit_line2 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'Personal Credit Line') do |line|
|
@@ -359,8 +390,8 @@ credit_line2 = Dscf::Credit::CreditLine.find_or_create_by(bank: bank1, name: 'Pe
|
|
359
390
|
line.description = 'Personal credit line for individuals'
|
360
391
|
line.status = 'approved'
|
361
392
|
line.created_by = admin_user
|
362
|
-
line.
|
363
|
-
line.
|
393
|
+
line.reviewed_by = bank_admin
|
394
|
+
line.review_date = 5.days.ago
|
364
395
|
end
|
365
396
|
|
366
397
|
Dscf::Credit::CreditLine.find_or_create_by(bank: bank2, name: 'Agricultural Credit Line') do |line|
|
@@ -369,7 +400,7 @@ Dscf::Credit::CreditLine.find_or_create_by(bank: bank2, name: 'Agricultural Cred
|
|
369
400
|
line.description = 'Credit line for agricultural sector'
|
370
401
|
line.status = 'pending'
|
371
402
|
line.created_by = bank_admin
|
372
|
-
line.
|
403
|
+
line.reviewed_by = admin_user
|
373
404
|
end
|
374
405
|
|
375
406
|
# 6. Credit Line Specs (depends on credit lines)
|
@@ -420,14 +451,17 @@ scoring_param1 = Dscf::Credit::ScoringParameter.find_or_create_by(
|
|
420
451
|
) do |param|
|
421
452
|
param.description = 'Monthly income of the applicant'
|
422
453
|
param.data_type = 'decimal'
|
423
|
-
param.type = 'financial'
|
424
454
|
param.weight = 0.3
|
425
455
|
param.min_value = 0
|
426
456
|
param.max_value = 1000000
|
427
457
|
param.active = true
|
428
|
-
param.
|
458
|
+
param.status = 'approved'
|
459
|
+
param.reviewed_by = admin_user
|
460
|
+
param.review_date = Time.current
|
429
461
|
param.created_by = admin_user
|
430
|
-
param.
|
462
|
+
param.source = 'internal'
|
463
|
+
param.scoring_param_type = consumer_type
|
464
|
+
param.document_reference = 'DOC-SP-001'
|
431
465
|
end
|
432
466
|
|
433
467
|
scoring_param2 = Dscf::Credit::ScoringParameter.find_or_create_by(
|
@@ -436,14 +470,17 @@ scoring_param2 = Dscf::Credit::ScoringParameter.find_or_create_by(
|
|
436
470
|
) do |param|
|
437
471
|
param.description = 'Credit history score from credit bureau'
|
438
472
|
param.data_type = 'integer'
|
439
|
-
param.type = 'credit_history'
|
440
473
|
param.weight = 0.4
|
441
474
|
param.min_value = 300
|
442
475
|
param.max_value = 850
|
443
476
|
param.active = true
|
444
|
-
param.
|
477
|
+
param.status = 'approved'
|
478
|
+
param.reviewed_by = admin_user
|
479
|
+
param.review_date = Time.current
|
445
480
|
param.created_by = admin_user
|
446
|
-
param.
|
481
|
+
param.source = 'external'
|
482
|
+
param.scoring_param_type = consumer_type
|
483
|
+
param.document_reference = 'DOC-SP-002'
|
447
484
|
end
|
448
485
|
|
449
486
|
scoring_param3 = Dscf::Credit::ScoringParameter.find_or_create_by(
|
@@ -452,12 +489,15 @@ scoring_param3 = Dscf::Credit::ScoringParameter.find_or_create_by(
|
|
452
489
|
) do |param|
|
453
490
|
param.description = 'Type of employment (permanent, contract, self-employed)'
|
454
491
|
param.data_type = 'string'
|
455
|
-
param.type = 'employment'
|
456
492
|
param.weight = 0.2
|
457
493
|
param.active = true
|
458
|
-
param.
|
494
|
+
param.status = 'approved'
|
495
|
+
param.reviewed_by = bank_admin
|
496
|
+
param.review_date = Time.current
|
459
497
|
param.created_by = admin_user
|
460
|
-
param.
|
498
|
+
param.source = 'internal'
|
499
|
+
param.scoring_param_type = consumer_type
|
500
|
+
param.document_reference = 'DOC-SP-003'
|
461
501
|
end
|
462
502
|
|
463
503
|
# 8. Parameter Normalizers (depends on scoring parameters)
|
@@ -471,7 +511,7 @@ Dscf::Credit::ParameterNormalizer.find_or_create_by(
|
|
471
511
|
) do |normalizer|
|
472
512
|
normalizer.name = 'Permanent Employment'
|
473
513
|
normalizer.description = 'Full-time permanent employment'
|
474
|
-
normalizer.normalized_value =
|
514
|
+
normalizer.normalized_value = 1.0
|
475
515
|
end
|
476
516
|
|
477
517
|
Dscf::Credit::ParameterNormalizer.find_or_create_by(
|
@@ -480,7 +520,7 @@ Dscf::Credit::ParameterNormalizer.find_or_create_by(
|
|
480
520
|
) do |normalizer|
|
481
521
|
normalizer.name = 'Contract Employment'
|
482
522
|
normalizer.description = 'Contract-based employment'
|
483
|
-
normalizer.normalized_value =
|
523
|
+
normalizer.normalized_value = 0.7
|
484
524
|
end
|
485
525
|
|
486
526
|
Dscf::Credit::ParameterNormalizer.find_or_create_by(
|
@@ -489,7 +529,7 @@ Dscf::Credit::ParameterNormalizer.find_or_create_by(
|
|
489
529
|
) do |normalizer|
|
490
530
|
normalizer.name = 'Self Employed'
|
491
531
|
normalizer.description = 'Self-employed individual'
|
492
|
-
normalizer.normalized_value =
|
532
|
+
normalizer.normalized_value = 0.6
|
493
533
|
end
|
494
534
|
|
495
535
|
# 8.5. Scoring Tables (depends on credit lines and scoring parameters)
|
@@ -502,7 +542,7 @@ employment_param = Dscf::Credit::ScoringParameter.find_by(bank: bank1, name: 'Em
|
|
502
542
|
# Create scoring tables for credit_line1 (SME Credit Line)
|
503
543
|
if monthly_income_param
|
504
544
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
505
|
-
|
545
|
+
category: sme_category,
|
506
546
|
scoring_parameter: monthly_income_param
|
507
547
|
) do |scoring_table|
|
508
548
|
scoring_table.weight = 0.4
|
@@ -513,7 +553,7 @@ end
|
|
513
553
|
|
514
554
|
if credit_history_param
|
515
555
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
516
|
-
|
556
|
+
category: sme_category,
|
517
557
|
scoring_parameter: credit_history_param
|
518
558
|
) do |scoring_table|
|
519
559
|
scoring_table.weight = 0.35
|
@@ -524,7 +564,7 @@ end
|
|
524
564
|
|
525
565
|
if employment_param
|
526
566
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
527
|
-
|
567
|
+
category: sme_category,
|
528
568
|
scoring_parameter: employment_param
|
529
569
|
) do |scoring_table|
|
530
570
|
scoring_table.weight = 0.25
|
@@ -536,7 +576,7 @@ end
|
|
536
576
|
# Create scoring tables for credit_line2 (Personal Credit Line)
|
537
577
|
if monthly_income_param
|
538
578
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
539
|
-
|
579
|
+
category: personal_category,
|
540
580
|
scoring_parameter: monthly_income_param
|
541
581
|
) do |scoring_table|
|
542
582
|
scoring_table.weight = 0.5
|
@@ -547,7 +587,7 @@ end
|
|
547
587
|
|
548
588
|
if credit_history_param
|
549
589
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
550
|
-
|
590
|
+
category: personal_category,
|
551
591
|
scoring_parameter: credit_history_param
|
552
592
|
) do |scoring_table|
|
553
593
|
scoring_table.weight = 0.3
|
@@ -558,7 +598,7 @@ end
|
|
558
598
|
|
559
599
|
if employment_param
|
560
600
|
Dscf::Credit::ScoringTable.find_or_create_by(
|
561
|
-
|
601
|
+
category: personal_category,
|
562
602
|
scoring_parameter: employment_param
|
563
603
|
) do |scoring_table|
|
564
604
|
scoring_table.weight = 0.2
|
@@ -567,6 +607,40 @@ if employment_param
|
|
567
607
|
end
|
568
608
|
end
|
569
609
|
|
610
|
+
# Create scoring tables for eligibility_category
|
611
|
+
if monthly_income_param
|
612
|
+
Dscf::Credit::ScoringTable.find_or_create_by(
|
613
|
+
category: eligibility_category,
|
614
|
+
scoring_parameter: monthly_income_param
|
615
|
+
) do |scoring_table|
|
616
|
+
scoring_table.weight = 0.4
|
617
|
+
scoring_table.active = true
|
618
|
+
scoring_table.created_by = admin_user
|
619
|
+
end
|
620
|
+
end
|
621
|
+
|
622
|
+
if credit_history_param
|
623
|
+
Dscf::Credit::ScoringTable.find_or_create_by(
|
624
|
+
category: eligibility_category,
|
625
|
+
scoring_parameter: credit_history_param
|
626
|
+
) do |scoring_table|
|
627
|
+
scoring_table.weight = 0.35
|
628
|
+
scoring_table.active = true
|
629
|
+
scoring_table.created_by = admin_user
|
630
|
+
end
|
631
|
+
end
|
632
|
+
|
633
|
+
if employment_param
|
634
|
+
Dscf::Credit::ScoringTable.find_or_create_by(
|
635
|
+
category: eligibility_category,
|
636
|
+
scoring_parameter: employment_param
|
637
|
+
) do |scoring_table|
|
638
|
+
scoring_table.weight = 0.25
|
639
|
+
scoring_table.active = true
|
640
|
+
scoring_table.created_by = admin_user
|
641
|
+
end
|
642
|
+
end
|
643
|
+
|
570
644
|
# 9. Loan Profiles (depends on banks and users)
|
571
645
|
puts "Seeding loan profiles..."
|
572
646
|
loan_profile1 = Dscf::Credit::LoanProfile.find_or_create_by(
|
@@ -576,11 +650,13 @@ loan_profile1 = Dscf::Credit::LoanProfile.find_or_create_by(
|
|
576
650
|
profile.status = 'approved'
|
577
651
|
profile.total_amount = 100000.00
|
578
652
|
profile.available_amount = 75000.00
|
579
|
-
profile.
|
653
|
+
profile.reviewed_by = bank_admin
|
654
|
+
profile.review_branch = cbe_head_office
|
655
|
+
profile.review_date = Time.current
|
580
656
|
# Note: backer will be set when facilitators are created via API
|
581
657
|
end
|
582
658
|
|
583
|
-
#
|
659
|
+
# 10. Loan Profile Scoring Specs (depends on loan profiles)
|
584
660
|
puts "Seeding loan profile scoring specs..."
|
585
661
|
Dscf::Credit::LoanProfileScoringSpec.create!(
|
586
662
|
loan_profile: loan_profile1,
|
@@ -589,16 +665,34 @@ Dscf::Credit::LoanProfileScoringSpec.create!(
|
|
589
665
|
'credit_history_score' => 720,
|
590
666
|
'employment_type' => 'permanent',
|
591
667
|
'years_at_job' => 3,
|
592
|
-
'existing_debt' => 15000
|
668
|
+
'existing_debt' => 15000,
|
669
|
+
'average_daily_purchase' => 15000 # Wholesaler's average daily purchase volume
|
593
670
|
},
|
594
671
|
score: 78.5,
|
672
|
+
total_limit: 100000.00,
|
595
673
|
active: true,
|
596
|
-
|
597
|
-
|
598
|
-
|
674
|
+
created_by: admin_user
|
675
|
+
)
|
676
|
+
|
677
|
+
# 11. Eligible Credit Lines (depends on loan profiles and credit lines)
|
678
|
+
puts "Seeding eligible credit lines..."
|
679
|
+
Dscf::Credit::EligibleCreditLine.create!(
|
680
|
+
loan_profile: loan_profile1,
|
681
|
+
credit_line: credit_line1,
|
682
|
+
credit_limit: 75000.00,
|
683
|
+
available_limit: 67500.00,
|
684
|
+
risk: 0.35
|
685
|
+
)
|
686
|
+
|
687
|
+
Dscf::Credit::EligibleCreditLine.create!(
|
688
|
+
loan_profile: loan_profile1,
|
689
|
+
credit_line: credit_line2,
|
690
|
+
credit_limit: 50000.00,
|
691
|
+
available_limit: 42500.00,
|
692
|
+
risk: 0.45
|
599
693
|
)
|
600
694
|
|
601
|
-
#
|
695
|
+
# 12. Payment Requests (depends on users)
|
602
696
|
puts "Seeding payment requests..."
|
603
697
|
payment_request1 = Dscf::Credit::PaymentRequest.create!(
|
604
698
|
user: user2,
|
@@ -621,7 +715,7 @@ payment_request2 = Dscf::Credit::PaymentRequest.create!(
|
|
621
715
|
initiated_at: 1.hour.ago
|
622
716
|
)
|
623
717
|
|
624
|
-
#
|
718
|
+
# 13. Loans (depends on loan profiles, credit lines, and payment requests)
|
625
719
|
puts "Seeding loans..."
|
626
720
|
loan1 = Dscf::Credit::Loan.create!(
|
627
721
|
loan_profile: loan_profile1,
|
@@ -638,7 +732,7 @@ loan1 = Dscf::Credit::Loan.create!(
|
|
638
732
|
disbursed_at: 1.day.ago
|
639
733
|
)
|
640
734
|
|
641
|
-
#
|
735
|
+
# 14. Loan Transactions (depends on loans)
|
642
736
|
puts "Seeding loan transactions..."
|
643
737
|
Dscf::Credit::LoanTransaction.create!(
|
644
738
|
loan: loan1,
|
@@ -656,7 +750,7 @@ Dscf::Credit::LoanTransaction.create!(
|
|
656
750
|
status: 'completed'
|
657
751
|
)
|
658
752
|
|
659
|
-
#
|
753
|
+
# 15. Daily Routine Transactions (depends on loans)
|
660
754
|
puts "Seeding daily routine transactions..."
|
661
755
|
Dscf::Credit::DailyRoutineTransaction.create!(
|
662
756
|
loan: loan1,
|
@@ -667,7 +761,7 @@ Dscf::Credit::DailyRoutineTransaction.create!(
|
|
667
761
|
approved_at: Date.current.beginning_of_day + 1.hour
|
668
762
|
)
|
669
763
|
|
670
|
-
#
|
764
|
+
# 16. Payments (depends on payment requests)
|
671
765
|
puts "Seeding payments..."
|
672
766
|
Dscf::Credit::Payment.create!(
|
673
767
|
payment_request: payment_request1,
|
@@ -678,7 +772,7 @@ Dscf::Credit::Payment.create!(
|
|
678
772
|
processed_at: 1.day.ago
|
679
773
|
)
|
680
774
|
|
681
|
-
#
|
775
|
+
# 17. Accounting Audit Requests (depends on various entities)
|
682
776
|
puts "Seeding accounting audit requests..."
|
683
777
|
audit_request1 = Dscf::Credit::AccountingAuditRequest.create!(
|
684
778
|
source_entity: loan1,
|
@@ -696,7 +790,7 @@ Dscf::Credit::AccountingAuditRequest.create!(
|
|
696
790
|
status: 'pending'
|
697
791
|
)
|
698
792
|
|
699
|
-
#
|
793
|
+
# 18. Accounting Entries (depends on audit requests)
|
700
794
|
puts "Seeding accounting entries..."
|
701
795
|
Dscf::Credit::AccountingEntry.create!(
|
702
796
|
audit_request: audit_request1,
|
@@ -714,7 +808,7 @@ Dscf::Credit::AccountingEntry.create!(
|
|
714
808
|
status: 'completed'
|
715
809
|
)
|
716
810
|
|
717
|
-
#
|
811
|
+
# 19. Failed Operations Logs (depends on various entities)
|
718
812
|
puts "Seeding failed operations logs..."
|
719
813
|
Dscf::Credit::FailedOperationsLog.create!(
|
720
814
|
entity: payment_request2,
|
data/lib/dscf/credit/version.rb
CHANGED
@@ -3,6 +3,7 @@ FactoryBot.define do
|
|
3
3
|
type { %w[scoring loan_profile payment risk_assessment].sample }
|
4
4
|
sequence(:name) { |n| "#{type.humanize} Category #{n}" }
|
5
5
|
description { Faker::Lorem.paragraph }
|
6
|
+
document_reference { Faker::Alphanumeric.alphanumeric(number: 10).upcase }
|
6
7
|
|
7
8
|
trait :scoring do
|
8
9
|
type { "scoring" }
|
@@ -8,6 +8,7 @@ FactoryBot.define do
|
|
8
8
|
penalty_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
9
9
|
facilitation_fee_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
10
10
|
tax_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
11
|
+
credit_line_multiplier { Faker::Number.between(from: 15, to: 60) }
|
11
12
|
max_penalty_days { Faker::Number.between(from: 30, to: 90) }
|
12
13
|
loan_duration { Faker::Number.between(from: 30, to: 365) }
|
13
14
|
interest_frequency { %w[daily weekly monthly quarterly annually].sample }
|
@@ -3,26 +3,28 @@ FactoryBot.define do
|
|
3
3
|
association :bank, factory: :bank
|
4
4
|
association :category, factory: :category
|
5
5
|
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
6
|
-
association :
|
6
|
+
association :reviewed_by, factory: :user if defined?(Dscf::Core::User)
|
7
7
|
name { "#{Faker::Finance.credit_card(:visa)} Credit Line" }
|
8
8
|
sequence(:code) { |n| "CL#{n.to_s.rjust(4, '0')}" }
|
9
9
|
description { Faker::Lorem.paragraph }
|
10
|
-
status { %w[pending approved
|
11
|
-
|
10
|
+
status { %w[pending approved suspended].sample }
|
11
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) if status.in?(%w[approved]) }
|
12
|
+
document_reference { Faker::Alphanumeric.alphanumeric(number: 10).upcase }
|
13
|
+
review_feedback { {} }
|
12
14
|
|
13
15
|
trait :pending do
|
14
16
|
status { "pending" }
|
15
|
-
|
17
|
+
review_date { nil }
|
16
18
|
end
|
17
19
|
|
18
20
|
trait :approved do
|
19
21
|
status { "approved" }
|
20
|
-
|
22
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
21
23
|
end
|
22
24
|
|
23
25
|
trait :active do
|
24
|
-
status { "
|
25
|
-
|
26
|
+
status { "approved" }
|
27
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
26
28
|
end
|
27
29
|
|
28
30
|
trait :suspended do
|
@@ -0,0 +1,33 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :eligible_credit_line, class: "Dscf::Credit::EligibleCreditLine" do
|
3
|
+
association :loan_profile, factory: :loan_profile
|
4
|
+
association :credit_line, factory: :credit_line
|
5
|
+
credit_limit { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
6
|
+
available_limit { Faker::Number.decimal(l_digits: 4, r_digits: 2) }
|
7
|
+
risk { nil } # Risk should not be set at creation, only through apply_risk endpoint
|
8
|
+
|
9
|
+
trait :with_applied_risk do
|
10
|
+
risk { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.01, 0.9) }
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :low_risk do
|
14
|
+
risk { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.01, 0.3) }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :medium_risk do
|
18
|
+
risk { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.31, 0.6) }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :high_risk do
|
22
|
+
risk { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.61, 0.9) }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :small_credit_limit do
|
26
|
+
credit_limit { Faker::Number.decimal(l_digits: 3, r_digits: 2) }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :large_credit_limit do
|
30
|
+
credit_limit { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -7,7 +7,6 @@ FactoryBot.define do
|
|
7
7
|
total_outstanding_amount { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
8
8
|
approval_required { [ true, false ].sample }
|
9
9
|
input_data { Faker::Json.shallow_json(width: 3, options: { key: 'Name.name', value: 'Number.number' }) }
|
10
|
-
expires_at { Faker::Date.between(from: 1.month.from_now, to: 1.year.from_now) }
|
11
10
|
|
12
11
|
trait :high_performance do
|
13
12
|
score { Faker::Number.between(from: 80.0, to: 100.0) }
|
@@ -22,9 +21,5 @@ FactoryBot.define do
|
|
22
21
|
trait :requiring_approval do
|
23
22
|
approval_required { true }
|
24
23
|
end
|
25
|
-
|
26
|
-
trait :expired do
|
27
|
-
expires_at { Faker::Date.between(from: 1.year.ago, to: 1.month.ago) }
|
28
|
-
end
|
29
24
|
end
|
30
25
|
end
|
@@ -2,22 +2,27 @@ FactoryBot.define do
|
|
2
2
|
factory :facilitator, class: "Dscf::Credit::Facilitator" do
|
3
3
|
association :bank, factory: :bank
|
4
4
|
association :user, factory: :user if defined?(Dscf::Core::User)
|
5
|
-
association :
|
5
|
+
association :kyc_reviewed_by, factory: :user if defined?(Dscf::Core::User)
|
6
6
|
name { Faker::Name.name }
|
7
7
|
type { %w[individual corporate agent].sample }
|
8
8
|
total_limit { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
9
9
|
kyc_status { %w[pending approved rejected].sample }
|
10
|
+
kyc_review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) if kyc_status == "approved" }
|
11
|
+
review_feedback { {} }
|
10
12
|
|
11
13
|
trait :pending_kyc do
|
12
14
|
kyc_status { "pending" }
|
15
|
+
kyc_review_date { nil }
|
13
16
|
end
|
14
17
|
|
15
18
|
trait :approved do
|
16
19
|
kyc_status { "approved" }
|
20
|
+
kyc_review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
17
21
|
end
|
18
22
|
|
19
23
|
trait :rejected do
|
20
24
|
kyc_status { "rejected" }
|
25
|
+
kyc_review_date { nil }
|
21
26
|
end
|
22
27
|
|
23
28
|
trait :individual do
|
@@ -2,12 +2,10 @@ FactoryBot.define do
|
|
2
2
|
factory :loan_profile_scoring_spec, class: "Dscf::Credit::LoanProfileScoringSpec" do
|
3
3
|
association :loan_profile, factory: :loan_profile
|
4
4
|
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
-
association :approved_by, factory: :user if defined?(Dscf::Core::User)
|
6
5
|
scoring_input_data { Faker::Json.shallow_json(width: 5, options: { key: 'Name.name', value: 'Number.number' }) }
|
7
6
|
score { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
7
|
+
total_limit { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
8
8
|
active { true }
|
9
|
-
expires_at { Faker::Date.between(from: 3.months.from_now, to: 1.year.from_now) }
|
10
|
-
|
11
9
|
trait :active do
|
12
10
|
active { true }
|
13
11
|
end
|
@@ -23,9 +21,5 @@ FactoryBot.define do
|
|
23
21
|
trait :low_score do
|
24
22
|
score { Faker::Number.between(from: 0.0, to: 30.0) }
|
25
23
|
end
|
26
|
-
|
27
|
-
trait :expired do
|
28
|
-
expires_at { Faker::Date.between(from: 1.year.ago, to: 1.month.ago) }
|
29
|
-
end
|
30
24
|
end
|
31
25
|
end
|
@@ -1,27 +1,32 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :loan_profile, class: "Dscf::Credit::LoanProfile" do
|
3
3
|
association :bank, factory: :bank
|
4
|
-
association :
|
4
|
+
association :review_branch, factory: :bank_branch
|
5
|
+
association :reviewed_by, factory: :user if defined?(Dscf::Core::User)
|
5
6
|
association :user, factory: :user if defined?(Dscf::Core::User)
|
6
7
|
association :backer, factory: :user if defined?(Dscf::Core::User)
|
7
|
-
status { %w[pending approved rejected].sample }
|
8
|
+
status { %w[pending approved rejected kyc_approved kyc_rejected].sample }
|
8
9
|
total_amount { Faker::Number.decimal(l_digits: 6, r_digits: 2).to_f }
|
9
10
|
available_amount { total_amount - Faker::Number.decimal(l_digits: 4, r_digits: 2).to_f }
|
10
|
-
|
11
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) if status == "approved" }
|
12
|
+
review_feedback { status == "rejected" ? { reason: Faker::Lorem.sentence } : {} }
|
11
13
|
|
12
14
|
trait :pending do
|
13
15
|
status { "pending" }
|
14
|
-
|
16
|
+
review_feedback { {} }
|
17
|
+
review_date { nil }
|
15
18
|
end
|
16
19
|
|
17
20
|
trait :approved do
|
18
21
|
status { "approved" }
|
19
|
-
|
22
|
+
review_feedback { {} }
|
23
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
20
24
|
end
|
21
25
|
|
22
26
|
trait :rejected do
|
23
27
|
status { "rejected" }
|
24
|
-
|
28
|
+
review_feedback { { reason: Faker::Lorem.sentence } }
|
29
|
+
review_date { nil }
|
25
30
|
end
|
26
31
|
|
27
32
|
trait :fully_available do
|
@@ -0,0 +1,31 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :scoring_param_type, class: "Dscf::Credit::ScoringParamType" do
|
3
|
+
name { Faker::Lorem.word.capitalize }
|
4
|
+
description { Faker::Lorem.paragraph }
|
5
|
+
|
6
|
+
trait :consumer do
|
7
|
+
name { "Consumer" }
|
8
|
+
description { "Parameters for consumer credit scoring" }
|
9
|
+
end
|
10
|
+
|
11
|
+
trait :facilitator do
|
12
|
+
name { "Facilitator" }
|
13
|
+
description { "Parameters for facilitator credit scoring" }
|
14
|
+
end
|
15
|
+
|
16
|
+
trait :wholesaler do
|
17
|
+
name { "Wholesaler" }
|
18
|
+
description { "Parameters for wholesaler credit scoring" }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :retailer do
|
22
|
+
name { "Retailer" }
|
23
|
+
description { "Parameters for retailer credit scoring" }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :manufacturer do
|
27
|
+
name { "Manufacturer" }
|
28
|
+
description { "Parameters for manufacturer credit scoring" }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -2,16 +2,20 @@ FactoryBot.define do
|
|
2
2
|
factory :scoring_parameter, class: "Dscf::Credit::ScoringParameter" do
|
3
3
|
association :bank, factory: :bank
|
4
4
|
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
-
association :
|
6
|
-
|
5
|
+
association :reviewed_by, factory: :user if defined?(Dscf::Core::User)
|
6
|
+
association :scoring_param_type, factory: :scoring_param_type
|
7
|
+
sequence(:name) { |n| "#{Faker::Lorem.words(number: 2).join('_').downcase}_#{n}" }
|
7
8
|
description { Faker::Lorem.paragraph }
|
8
9
|
data_type { %w[integer decimal boolean string].sample }
|
9
|
-
type { %w[income credit_history employment_status collateral].sample }
|
10
10
|
weight { Faker::Number.decimal(l_digits: 0, r_digits: 4) }
|
11
11
|
min_value { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
12
12
|
max_value { min_value + Faker::Number.decimal(l_digits: 3, r_digits: 2) }
|
13
13
|
active { true }
|
14
|
-
|
14
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
15
|
+
source { Faker::Lorem.word }
|
16
|
+
document_reference { Faker::Alphanumeric.alphanumeric(number: 10).upcase }
|
17
|
+
status { "pending" }
|
18
|
+
review_feedback { {} }
|
15
19
|
|
16
20
|
trait :active do
|
17
21
|
active { true }
|
@@ -44,5 +48,23 @@ FactoryBot.define do
|
|
44
48
|
min_value { nil }
|
45
49
|
max_value { nil }
|
46
50
|
end
|
51
|
+
|
52
|
+
trait :pending do
|
53
|
+
status { "pending" }
|
54
|
+
end
|
55
|
+
|
56
|
+
trait :approved do
|
57
|
+
status { "approved" }
|
58
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
59
|
+
end
|
60
|
+
|
61
|
+
trait :rejected do
|
62
|
+
status { "rejected" }
|
63
|
+
review_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
64
|
+
end
|
65
|
+
|
66
|
+
trait :modify do
|
67
|
+
status { "modify" }
|
68
|
+
end
|
47
69
|
end
|
48
70
|
end
|