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.
Files changed (89) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/dscf/core/authenticatable.rb +81 -0
  3. data/app/controllers/concerns/dscf/core/common.rb +200 -0
  4. data/app/controllers/concerns/dscf/core/filterable.rb +12 -0
  5. data/app/controllers/concerns/dscf/core/json_response.rb +77 -0
  6. data/app/controllers/concerns/dscf/core/pagination.rb +71 -0
  7. data/app/controllers/concerns/dscf/core/token_authenticatable.rb +53 -0
  8. data/app/controllers/concerns/dscf/credit/reviewable.rb +112 -0
  9. data/app/controllers/dscf/credit/categories_controller.rb +6 -5
  10. data/app/controllers/dscf/credit/credit_limit_calculations_controller.rb +50 -0
  11. data/app/controllers/dscf/credit/credit_line_specs_controller.rb +2 -1
  12. data/app/controllers/dscf/credit/credit_lines_controller.rb +11 -38
  13. data/app/controllers/dscf/credit/disbursements_controller.rb +55 -0
  14. data/app/controllers/dscf/credit/eligible_credit_lines_controller.rb +50 -0
  15. data/app/controllers/dscf/credit/facilitators_controller.rb +39 -150
  16. data/app/controllers/dscf/credit/loan_profiles_controller.rb +138 -0
  17. data/app/controllers/dscf/credit/payment_requests_controller.rb +54 -5
  18. data/app/controllers/dscf/credit/repayments_controller.rb +53 -0
  19. data/app/controllers/dscf/credit/scoring_param_types_controller.rb +31 -0
  20. data/app/controllers/dscf/credit/scoring_parameters_controller.rb +13 -8
  21. data/app/controllers/dscf/credit/scoring_tables_controller.rb +8 -8
  22. data/app/controllers/dscf/credit/system_configs_controller.rb +10 -7
  23. data/app/models/dscf/credit/bank_branch.rb +2 -1
  24. data/app/models/dscf/credit/category.rb +4 -2
  25. data/app/models/dscf/credit/credit_line.rb +9 -6
  26. data/app/models/dscf/credit/credit_line_spec.rb +3 -3
  27. data/app/models/dscf/credit/eligible_credit_line.rb +28 -0
  28. data/app/models/dscf/credit/facilitator.rb +5 -4
  29. data/app/models/dscf/credit/facilitator_performance.rb +1 -2
  30. data/app/models/dscf/credit/loan_profile.rb +8 -4
  31. data/app/models/dscf/credit/loan_profile_scoring_spec.rb +4 -6
  32. data/app/models/dscf/credit/scoring_param_type.rb +17 -0
  33. data/app/models/dscf/credit/scoring_parameter.rb +8 -7
  34. data/app/models/dscf/credit/scoring_table.rb +4 -4
  35. data/app/models/dscf/credit/system_config.rb +5 -4
  36. data/app/serializers/dscf/credit/bank_branch_serializer.rb +1 -0
  37. data/app/serializers/dscf/credit/category_serializer.rb +3 -1
  38. data/app/serializers/dscf/credit/credit_line_serializer.rb +4 -2
  39. data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +1 -1
  40. data/app/serializers/dscf/credit/daily_routine_transaction_serializer.rb +8 -0
  41. data/app/serializers/dscf/credit/eligible_credit_line_serializer.rb +8 -0
  42. data/app/serializers/dscf/credit/facilitator_performance_serializer.rb +1 -1
  43. data/app/serializers/dscf/credit/facilitator_serializer.rb +2 -2
  44. data/app/serializers/dscf/credit/loan_profile_scoring_spec_serializer.rb +8 -0
  45. data/app/serializers/dscf/credit/loan_profile_serializer.rb +15 -0
  46. data/app/serializers/dscf/credit/loan_serializer.rb +12 -0
  47. data/app/serializers/dscf/credit/loan_transaction_serializer.rb +8 -0
  48. data/app/serializers/dscf/credit/payment_request_serializer.rb +10 -0
  49. data/app/serializers/dscf/credit/payment_serializer.rb +8 -0
  50. data/app/serializers/dscf/credit/scoring_param_type_serializer.rb +7 -0
  51. data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +6 -3
  52. data/app/serializers/dscf/credit/scoring_table_serializer.rb +1 -1
  53. data/app/serializers/dscf/credit/system_config_serializer.rb +2 -2
  54. data/app/services/dscf/credit/credit_limit_calculation_service.rb +153 -0
  55. data/app/services/dscf/credit/disbursement_service.rb +180 -0
  56. data/app/services/dscf/credit/facilitator_approval_service.rb +4 -3
  57. data/app/services/dscf/credit/facilitator_creation_service.rb +157 -0
  58. data/app/services/dscf/credit/repayment_service.rb +216 -0
  59. data/app/services/dscf/credit/risk_application_service.rb +27 -0
  60. data/app/services/dscf/credit/scoring_service.rb +297 -0
  61. data/config/locales/en.yml +125 -8
  62. data/config/routes.rb +42 -11
  63. data/db/migrate/20250822091011_create_dscf_credit_categories.rb +2 -0
  64. data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +7 -4
  65. data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +1 -0
  66. data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +5 -2
  67. data/db/migrate/20250822092040_create_dscf_credit_scoring_param_types.rb +12 -0
  68. data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +11 -6
  69. data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +6 -3
  70. data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +5 -7
  71. data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +5 -2
  72. data/db/migrate/20250822092528_create_dscf_credit_facilitator_performances.rb +0 -3
  73. data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +2 -2
  74. data/db/migrate/20250917120000_create_dscf_credit_eligible_credit_lines.rb +18 -0
  75. data/db/seeds.rb +134 -40
  76. data/lib/dscf/credit/version.rb +1 -1
  77. data/spec/factories/dscf/credit/categories.rb +1 -0
  78. data/spec/factories/dscf/credit/credit_line_specs.rb +1 -0
  79. data/spec/factories/dscf/credit/credit_lines.rb +9 -7
  80. data/spec/factories/dscf/credit/eligible_credit_lines.rb +33 -0
  81. data/spec/factories/dscf/credit/facilitator_performances.rb +0 -5
  82. data/spec/factories/dscf/credit/facilitators.rb +6 -1
  83. data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +1 -7
  84. data/spec/factories/dscf/credit/loan_profiles.rb +11 -6
  85. data/spec/factories/dscf/credit/scoring_param_types.rb +31 -0
  86. data/spec/factories/dscf/credit/scoring_parameters.rb +26 -4
  87. data/spec/factories/dscf/credit/scoring_tables.rb +1 -1
  88. data/spec/factories/dscf/credit/system_configs.rb +8 -2
  89. 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.approved_by = admin_user
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.approved_by = admin_user
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.approved_by = bank_admin
353
- line.approval_date = 1.week.ago
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.approved_by = bank_admin
363
- line.approval_date = 5.days.ago
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.approved_by = admin_user
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.approved_by = admin_user
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.expires_at = 1.year.from_now
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.approved_by = admin_user
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.expires_at = 1.year.from_now
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.approved_by = bank_admin
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.expires_at = 1.year.from_now
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 = 100
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 = 70
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 = 60
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
- credit_line: credit_line1,
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
- credit_line: credit_line1,
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
- credit_line: credit_line1,
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
- credit_line: credit_line2,
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
- credit_line: credit_line2,
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
- credit_line: credit_line2,
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.approved_by = bank_admin
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
- # 12. Loan Profile Scoring Specs (depends on loan profiles)
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
- approved_by: admin_user,
597
- created_by: admin_user,
598
- expires_at: 6.months.from_now
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
- # 13. Payment Requests (depends on users)
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
- # 14. Loans (depends on loan profiles, credit lines, and payment requests)
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
- # 15. Loan Transactions (depends on loans)
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
- # 16. Daily Routine Transactions (depends on loans)
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
- # 17. Payments (depends on payment requests)
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
- # 18. Accounting Audit Requests (depends on various entities)
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
- # 19. Accounting Entries (depends on audit requests)
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
- # 20. Failed Operations Logs (depends on various entities)
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,
@@ -1,5 +1,5 @@
1
1
  module Dscf
2
2
  module Credit
3
- VERSION = "0.1.1"
3
+ VERSION = "0.1.3"
4
4
  end
5
5
  end
@@ -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 :approved_by, factory: :user if defined?(Dscf::Core::User)
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 active suspended].sample }
11
- approval_date { Faker::Date.between(from: 6.months.ago, to: Date.current) if status.in?(%w[approved active]) }
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
- approval_date { nil }
17
+ review_date { nil }
16
18
  end
17
19
 
18
20
  trait :approved do
19
21
  status { "approved" }
20
- approval_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
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 { "active" }
25
- approval_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
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 :kyc_approved_by, factory: :user if defined?(Dscf::Core::User)
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 :approved_by, factory: :user if defined?(Dscf::Core::User)
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
- rejection_reason { Faker::Lorem.sentence if status == "rejected" }
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
- rejection_reason { nil }
16
+ review_feedback { {} }
17
+ review_date { nil }
15
18
  end
16
19
 
17
20
  trait :approved do
18
21
  status { "approved" }
19
- rejection_reason { nil }
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
- rejection_reason { Faker::Lorem.sentence }
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 :approved_by, factory: :user if defined?(Dscf::Core::User)
6
- name { Faker::Lorem.words(number: 2).join('_').downcase }
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
- expires_at { Faker::Date.between(from: 6.months.from_now, to: 2.years.from_now) }
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