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.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +8 -0
- data/app/controllers/dscf/credit/application_controller.rb +8 -0
- data/app/controllers/dscf/credit/bank_branches_controller.rb +38 -0
- data/app/controllers/dscf/credit/bank_staffs_controller.rb +77 -0
- data/app/controllers/dscf/credit/banks_controller.rb +39 -0
- data/app/controllers/dscf/credit/categories_controller.rb +32 -0
- data/app/controllers/dscf/credit/credit_line_specs_controller.rb +72 -0
- data/app/controllers/dscf/credit/credit_lines_controller.rb +76 -0
- data/app/controllers/dscf/credit/facilitators_controller.rb +227 -0
- data/app/controllers/dscf/credit/loans_controller.rb +41 -0
- data/app/controllers/dscf/credit/parameter_normalizers_controller.rb +31 -0
- data/app/controllers/dscf/credit/payment_requests_controller.rb +38 -0
- data/app/controllers/dscf/credit/payments_controller.rb +36 -0
- data/app/controllers/dscf/credit/scoring_parameters_controller.rb +50 -0
- data/app/controllers/dscf/credit/scoring_tables_controller.rb +63 -0
- data/app/controllers/dscf/credit/system_config_definitions_controller.rb +34 -0
- data/app/controllers/dscf/credit/system_configs_controller.rb +49 -0
- data/app/controllers/dscf/credit/users_controller.rb +53 -0
- data/app/jobs/dscf/credit/application_job.rb +6 -0
- data/app/mailers/dscf/credit/application_mailer.rb +8 -0
- data/app/mailers/dscf/credit/bank_staff_welcome_mailer.rb +15 -0
- data/app/mailers/dscf/credit/facilitator_mailer.rb +54 -0
- data/app/models/dscf/credit/accounting_audit_request.rb +17 -0
- data/app/models/dscf/credit/accounting_entry.rb +15 -0
- data/app/models/dscf/credit/application_record.rb +7 -0
- data/app/models/dscf/credit/bank.rb +38 -0
- data/app/models/dscf/credit/bank_branch.rb +23 -0
- data/app/models/dscf/credit/bank_staff.rb +26 -0
- data/app/models/dscf/credit/category.rb +21 -0
- data/app/models/dscf/credit/credit_line.rb +30 -0
- data/app/models/dscf/credit/credit_line_spec.rb +37 -0
- data/app/models/dscf/credit/daily_routine_transaction.rb +18 -0
- data/app/models/dscf/credit/facilitator.rb +31 -0
- data/app/models/dscf/credit/facilitator_performance.rb +17 -0
- data/app/models/dscf/credit/failed_operations_log.rb +17 -0
- data/app/models/dscf/credit/loan.rb +33 -0
- data/app/models/dscf/credit/loan_profile.rb +43 -0
- data/app/models/dscf/credit/loan_profile_scoring_spec.rb +23 -0
- data/app/models/dscf/credit/loan_transaction.rb +17 -0
- data/app/models/dscf/credit/parameter_normalizer.rb +11 -0
- data/app/models/dscf/credit/payment.rb +22 -0
- data/app/models/dscf/credit/payment_request.rb +29 -0
- data/app/models/dscf/credit/scoring_parameter.rb +30 -0
- data/app/models/dscf/credit/scoring_table.rb +24 -0
- data/app/models/dscf/credit/system_config.rb +25 -0
- data/app/models/dscf/credit/system_config_definition.rb +25 -0
- data/app/serializers/dscf/core/business_serializer.rb +9 -0
- data/app/serializers/dscf/core/business_type_serializer.rb +7 -0
- data/app/serializers/dscf/core/role_serializer.rb +7 -0
- data/app/serializers/dscf/core/user_profile_serializer.rb +13 -0
- data/app/serializers/dscf/core/user_role_serializer.rb +16 -0
- data/app/serializers/dscf/core/user_serializer.rb +17 -0
- data/app/serializers/dscf/credit/bank_branch_serializer.rb +9 -0
- data/app/serializers/dscf/credit/bank_serializer.rb +9 -0
- data/app/serializers/dscf/credit/bank_staff_serializer.rb +8 -0
- data/app/serializers/dscf/credit/category_serializer.rb +7 -0
- data/app/serializers/dscf/credit/credit_line_serializer.rb +12 -0
- data/app/serializers/dscf/credit/credit_line_spec_serializer.rb +12 -0
- data/app/serializers/dscf/credit/parameter_normalizer_serializer.rb +8 -0
- data/app/serializers/dscf/credit/scoring_parameter_serializer.rb +12 -0
- data/app/serializers/dscf/credit/scoring_table_serializer.rb +9 -0
- data/app/serializers/dscf/credit/system_config_definition_serializer.rb +9 -0
- data/app/serializers/dscf/credit/system_config_serializer.rb +18 -0
- data/app/services/dscf/credit/bank_staff_creation_service.rb +99 -0
- data/app/services/dscf/credit/facilitator_additional_info_service.rb +62 -0
- data/app/services/dscf/credit/facilitator_approval_service.rb +88 -0
- data/app/views/dscf/credit/bank_staff_welcome_mailer/welcome_email.html.erb +116 -0
- data/app/views/dscf/credit/bank_staff_welcome_mailer/welcome_email.text.erb +40 -0
- data/app/views/dscf/credit/facilitator_mailer/additional_info_received.html.erb +39 -0
- data/app/views/dscf/credit/facilitator_mailer/additional_info_received.text.erb +17 -0
- data/app/views/dscf/credit/facilitator_mailer/approval_notification.html.erb +59 -0
- data/app/views/dscf/credit/facilitator_mailer/approval_notification.text.erb +26 -0
- data/app/views/dscf/credit/facilitator_mailer/limit_update_notification.html.erb +39 -0
- data/app/views/dscf/credit/facilitator_mailer/limit_update_notification.text.erb +17 -0
- data/app/views/dscf/credit/facilitator_mailer/rejection_notification.html.erb +41 -0
- data/app/views/dscf/credit/facilitator_mailer/rejection_notification.text.erb +19 -0
- data/app/views/layouts/mailer.html.erb +125 -0
- data/app/views/layouts/mailer.text.erb +12 -0
- data/config/locales/en.yml +208 -0
- data/config/routes.rb +50 -0
- data/db/migrate/20250822091011_create_dscf_credit_categories.rb +15 -0
- data/db/migrate/20250822091015_create_dscf_credit_banks.rb +23 -0
- data/db/migrate/20250822091055_create_dscf_credit_bank_branches.rb +23 -0
- data/db/migrate/20250822091131_create_dscf_credit_credit_lines.rb +25 -0
- data/db/migrate/20250822091527_create_dscf_credit_credit_line_specs.rb +32 -0
- data/db/migrate/20250822091744_create_dscf_credit_system_config_definitions.rb +18 -0
- data/db/migrate/20250822091820_create_dscf_credit_system_configs.rb +17 -0
- data/db/migrate/20250822092050_create_dscf_credit_scoring_parameters.rb +30 -0
- data/db/migrate/20250822092225_create_dscf_credit_parameter_normalizers.rb +18 -0
- data/db/migrate/20250822092246_create_dscf_credit_loan_profiles.rb +25 -0
- data/db/migrate/20250822092417_create_dscf_credit_loan_profile_scoring_specs.rb +23 -0
- data/db/migrate/20250822092436_create_dscf_credit_facilitators.rb +22 -0
- data/db/migrate/20250822092528_create_dscf_credit_facilitator_performances.rb +26 -0
- data/db/migrate/20250822092608_create_dscf_credit_payment_requests.rb +26 -0
- data/db/migrate/20250822092654_create_dscf_credit_loans.rb +29 -0
- data/db/migrate/20250822092717_create_dscf_credit_loan_transactions.rb +21 -0
- data/db/migrate/20250822092738_create_dscf_credit_daily_routine_transactions.rb +23 -0
- data/db/migrate/20250822092819_create_dscf_credit_accounting_audit_requests.rb +20 -0
- data/db/migrate/20250822092843_create_dscf_credit_payments.rb +23 -0
- data/db/migrate/20250822092908_create_dscf_credit_failed_operations_logs.rb +21 -0
- data/db/migrate/20250822092936_create_dscf_credit_accounting_entries.rb +21 -0
- data/db/migrate/20250825231109_create_dscf_credit_bank_staff.rb +14 -0
- data/db/migrate/20250901172842_create_dscf_credit_scoring_tables.rb +18 -0
- data/db/seeds.rb +745 -0
- data/lib/dscf/credit/engine.rb +28 -0
- data/lib/dscf/credit/version.rb +5 -0
- data/lib/dscf/credit.rb +9 -0
- data/lib/tasks/dscf/credit_tasks.rake +4 -0
- data/spec/factories/dscf/credit/accounting_audit_requests.rb +58 -0
- data/spec/factories/dscf/credit/accounting_entries.rb +29 -0
- data/spec/factories/dscf/credit/bank_branches.rb +21 -0
- data/spec/factories/dscf/credit/bank_staffs.rb +19 -0
- data/spec/factories/dscf/credit/banks.rb +30 -0
- data/spec/factories/dscf/credit/categories.rb +27 -0
- data/spec/factories/dscf/credit/credit_line_specs.rb +57 -0
- data/spec/factories/dscf/credit/credit_lines.rb +32 -0
- data/spec/factories/dscf/credit/daily_routine_transactions.rb +36 -0
- data/spec/factories/dscf/credit/facilitator_performances.rb +30 -0
- data/spec/factories/dscf/credit/facilitators.rb +35 -0
- data/spec/factories/dscf/credit/failed_operations_logs.rb +34 -0
- data/spec/factories/dscf/credit/loan_profile_scoring_specs.rb +31 -0
- data/spec/factories/dscf/credit/loan_profiles.rb +49 -0
- data/spec/factories/dscf/credit/loan_transactions.rb +37 -0
- data/spec/factories/dscf/credit/loans.rb +37 -0
- data/spec/factories/dscf/credit/parameter_normalizers.rb +24 -0
- data/spec/factories/dscf/credit/payment_requests.rb +40 -0
- data/spec/factories/dscf/credit/payments.rb +39 -0
- data/spec/factories/dscf/credit/scoring_parameters.rb +48 -0
- data/spec/factories/dscf/credit/scoring_tables.rb +25 -0
- data/spec/factories/dscf/credit/system_config_definitions.rb +45 -0
- data/spec/factories/dscf/credit/system_configs.rb +29 -0
- metadata +456 -0
@@ -0,0 +1,28 @@
|
|
1
|
+
module Dscf
|
2
|
+
module Credit
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace Dscf::Credit
|
5
|
+
config.api_only = true
|
6
|
+
|
7
|
+
config.generators do |g|
|
8
|
+
g.test_framework :rspec, routing_specs: false
|
9
|
+
g.fixture_replacement :factory_bot
|
10
|
+
g.factory_bot dir: "spec/factories"
|
11
|
+
end
|
12
|
+
|
13
|
+
initializer "dscf_credit.factories", after: "factory_bot.set_factory_paths" do
|
14
|
+
if defined?(FactoryBot)
|
15
|
+
FactoryBot.definition_file_paths << root.join("spec/factories").to_s
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
initializer :append_migrations do |app|
|
20
|
+
unless app.root.to_s.match?(root.to_s + File::SEPARATOR)
|
21
|
+
app.config.paths["db/migrate"].concat(config.paths["db/migrate"].expanded)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
require "active_storage/engine"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
data/lib/dscf/credit.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :accounting_audit_request, class: "Dscf::Credit::AccountingAuditRequest" do
|
3
|
+
transaction_type { %w[debit credit transfer adjustment].sample }
|
4
|
+
amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
5
|
+
webhook_key { Faker::Internet.uuid }
|
6
|
+
status { "pending" }
|
7
|
+
|
8
|
+
association :source_entity, factory: :loan
|
9
|
+
|
10
|
+
trait :pending do
|
11
|
+
status { "pending" }
|
12
|
+
end
|
13
|
+
|
14
|
+
trait :processing do
|
15
|
+
status { "processing" }
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :completed do
|
19
|
+
status { "completed" }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :failed do
|
23
|
+
status { "failed" }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :debit_transaction do
|
27
|
+
transaction_type { "debit" }
|
28
|
+
end
|
29
|
+
|
30
|
+
trait :credit_transaction do
|
31
|
+
transaction_type { "credit" }
|
32
|
+
end
|
33
|
+
|
34
|
+
trait :transfer_transaction do
|
35
|
+
transaction_type { "transfer" }
|
36
|
+
end
|
37
|
+
|
38
|
+
trait :adjustment_transaction do
|
39
|
+
transaction_type { "adjustment" }
|
40
|
+
end
|
41
|
+
|
42
|
+
trait :large_amount do
|
43
|
+
amount { Faker::Number.decimal(l_digits: 8, r_digits: 2) }
|
44
|
+
end
|
45
|
+
|
46
|
+
trait :small_amount do
|
47
|
+
amount { Faker::Number.between(from: 1.0, to: 100.0).round(2) }
|
48
|
+
end
|
49
|
+
|
50
|
+
trait :with_payment_source do
|
51
|
+
association :source_entity, factory: :payment
|
52
|
+
end
|
53
|
+
|
54
|
+
trait :with_loan_source do
|
55
|
+
association :source_entity, factory: :loan
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :accounting_entry, class: "Dscf::Credit::AccountingEntry" do
|
3
|
+
association :audit_request, factory: :accounting_audit_request
|
4
|
+
account_code { Faker::Alphanumeric.alphanumeric(number: 10) }
|
5
|
+
entry_type { %w[debit credit].sample }
|
6
|
+
amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
7
|
+
status { %w[pending completed failed].sample }
|
8
|
+
|
9
|
+
trait :debit_entry do
|
10
|
+
entry_type { "debit" }
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :credit_entry do
|
14
|
+
entry_type { "credit" }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :pending do
|
18
|
+
status { "pending" }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :completed do
|
22
|
+
status { "completed" }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :failed do
|
26
|
+
status { "failed" }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :bank_branch, class: "Dscf::Credit::BankBranch" do
|
3
|
+
association :bank, factory: :bank
|
4
|
+
branch_name { "#{Faker::Address.city} Branch" }
|
5
|
+
sequence(:branch_code) { |n| "BR#{n.to_s.rjust(3, '0')}" }
|
6
|
+
branch_address { Faker::Address.full_address }
|
7
|
+
city { Faker::Address.city }
|
8
|
+
country { Faker::Address.country }
|
9
|
+
contact_email { Faker::Internet.email }
|
10
|
+
contact_phone { Faker::PhoneNumber.phone_number }
|
11
|
+
status { %w[active inactive].sample }
|
12
|
+
|
13
|
+
trait :active do
|
14
|
+
status { "active" }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :inactive do
|
18
|
+
status { "inactive" }
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :bank_staff, class: "Dscf::Credit::BankStaff" do
|
3
|
+
association :user, factory: :user if defined?(Dscf::Core::User)
|
4
|
+
association :bank_branch, factory: :bank_branch
|
5
|
+
status { %w[active inactive terminated].sample }
|
6
|
+
|
7
|
+
trait :active do
|
8
|
+
status { "active" }
|
9
|
+
end
|
10
|
+
|
11
|
+
trait :inactive do
|
12
|
+
status { "inactive" }
|
13
|
+
end
|
14
|
+
|
15
|
+
trait :terminated do
|
16
|
+
status { "terminated" }
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :bank, class: "Dscf::Credit::Bank" do
|
3
|
+
association :user, factory: :user if defined?(Dscf::Core::User)
|
4
|
+
name { Faker::Bank.name }
|
5
|
+
sequence(:registration_number) { |n| "REG#{n.to_s.rjust(8, '0')}" }
|
6
|
+
sequence(:swift_code) { |n| "#{Faker::Bank.swift_bic[0..3]}#{n.to_s.rjust(4, '0')}" }
|
7
|
+
headquarters_address { Faker::Address.full_address }
|
8
|
+
city { Faker::Address.city }
|
9
|
+
country { Faker::Address.country }
|
10
|
+
contact_email { Faker::Internet.email }
|
11
|
+
contact_phone { Faker::PhoneNumber.phone_number }
|
12
|
+
status { %w[active inactive suspended].sample }
|
13
|
+
|
14
|
+
trait :active do
|
15
|
+
status { "active" }
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :inactive do
|
19
|
+
status { "inactive" }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :suspended do
|
23
|
+
status { "suspended" }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :without_user do
|
27
|
+
user { nil }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :category, class: "Dscf::Credit::Category" do
|
3
|
+
type { %w[scoring loan_profile payment risk_assessment].sample }
|
4
|
+
sequence(:name) { |n| "#{type.humanize} Category #{n}" }
|
5
|
+
description { Faker::Lorem.paragraph }
|
6
|
+
|
7
|
+
trait :scoring do
|
8
|
+
type { "scoring" }
|
9
|
+
name { "Scoring Category" }
|
10
|
+
end
|
11
|
+
|
12
|
+
trait :loan_profile do
|
13
|
+
type { "loan_profile" }
|
14
|
+
name { "Loan Profile Category" }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :payment do
|
18
|
+
type { "payment" }
|
19
|
+
name { "Payment Category" }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :risk_assessment do
|
23
|
+
type { "risk_assessment" }
|
24
|
+
name { "Risk Assessment Category" }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :credit_line_spec, class: "Dscf::Credit::CreditLineSpec" do
|
3
|
+
association :credit_line, factory: :credit_line
|
4
|
+
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
+
min_amount { Faker::Number.decimal(l_digits: 4, r_digits: 2).to_f }
|
6
|
+
max_amount { min_amount + Faker::Number.decimal(l_digits: 5, r_digits: 2).to_f }
|
7
|
+
interest_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
8
|
+
penalty_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
9
|
+
facilitation_fee_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
10
|
+
tax_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
11
|
+
max_penalty_days { Faker::Number.between(from: 30, to: 90) }
|
12
|
+
loan_duration { Faker::Number.between(from: 30, to: 365) }
|
13
|
+
interest_frequency { %w[daily weekly monthly quarterly annually].sample }
|
14
|
+
interest_income_tax { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
15
|
+
vat { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
16
|
+
max_interest_calculation_days { Faker::Number.between(from: 30, to: 365) }
|
17
|
+
penalty_frequency { %w[daily weekly monthly].sample }
|
18
|
+
penalty_income_tax { Faker::Number.decimal(l_digits: 1, r_digits: 4).to_f }
|
19
|
+
active { true }
|
20
|
+
|
21
|
+
trait :active do
|
22
|
+
active { true }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :inactive do
|
26
|
+
active { false }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :short_term do
|
30
|
+
loan_duration { Faker::Number.between(from: 30, to: 90) }
|
31
|
+
interest_frequency { 'monthly' }
|
32
|
+
penalty_frequency { 'weekly' }
|
33
|
+
end
|
34
|
+
|
35
|
+
trait :medium_term do
|
36
|
+
loan_duration { Faker::Number.between(from: 91, to: 180) }
|
37
|
+
interest_frequency { 'monthly' }
|
38
|
+
penalty_frequency { 'monthly' }
|
39
|
+
end
|
40
|
+
|
41
|
+
trait :long_term do
|
42
|
+
loan_duration { Faker::Number.between(from: 181, to: 365) }
|
43
|
+
interest_frequency { 'quarterly' }
|
44
|
+
penalty_frequency { 'monthly' }
|
45
|
+
end
|
46
|
+
|
47
|
+
trait :daily_interest do
|
48
|
+
interest_frequency { 'daily' }
|
49
|
+
penalty_frequency { 'daily' }
|
50
|
+
end
|
51
|
+
|
52
|
+
trait :monthly_interest do
|
53
|
+
interest_frequency { 'monthly' }
|
54
|
+
penalty_frequency { 'weekly' }
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :credit_line, class: "Dscf::Credit::CreditLine" do
|
3
|
+
association :bank, factory: :bank
|
4
|
+
association :category, factory: :category
|
5
|
+
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
6
|
+
association :approved_by, factory: :user if defined?(Dscf::Core::User)
|
7
|
+
name { "#{Faker::Finance.credit_card(:visa)} Credit Line" }
|
8
|
+
sequence(:code) { |n| "CL#{n.to_s.rjust(4, '0')}" }
|
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]) }
|
12
|
+
|
13
|
+
trait :pending do
|
14
|
+
status { "pending" }
|
15
|
+
approval_date { nil }
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :approved do
|
19
|
+
status { "approved" }
|
20
|
+
approval_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
21
|
+
end
|
22
|
+
|
23
|
+
trait :active do
|
24
|
+
status { "active" }
|
25
|
+
approval_date { Faker::Date.between(from: 6.months.ago, to: Date.current) }
|
26
|
+
end
|
27
|
+
|
28
|
+
trait :suspended do
|
29
|
+
status { "suspended" }
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :daily_routine_transaction, class: "Dscf::Credit::DailyRoutineTransaction" do
|
3
|
+
association :loan, factory: :loan
|
4
|
+
routine_type { %w[interest_calculation penalty_assessment payment_due_notification].sample }
|
5
|
+
amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
6
|
+
status { %w[pending processing completed failed].sample }
|
7
|
+
failure_reason { Faker::Lorem.sentence if status == "failed" }
|
8
|
+
initiated_at { Faker::Time.between(from: 1.month.ago, to: Time.current) }
|
9
|
+
approved_at { initiated_at + rand(1..6).hours if status.in?(%w[completed failed]) }
|
10
|
+
|
11
|
+
trait :pending do
|
12
|
+
status { "pending" }
|
13
|
+
started_at { nil }
|
14
|
+
completed_at { nil }
|
15
|
+
processed_records_count { 0 }
|
16
|
+
failed_records_count { 0 }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :in_progress do
|
20
|
+
status { "in_progress" }
|
21
|
+
completed_at { nil }
|
22
|
+
end
|
23
|
+
|
24
|
+
trait :completed do
|
25
|
+
status { "completed" }
|
26
|
+
completed_at { started_at + rand(1..6).hours }
|
27
|
+
error_details { nil }
|
28
|
+
end
|
29
|
+
|
30
|
+
trait :failed do
|
31
|
+
status { "failed" }
|
32
|
+
completed_at { started_at + rand(1..6).hours }
|
33
|
+
error_details { Faker::Json.shallow_json(width: 3) }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :facilitator_performance, class: "Dscf::Credit::FacilitatorPerformance" do
|
3
|
+
association :facilitator, factory: :facilitator
|
4
|
+
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
+
score { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
6
|
+
total_outstanding_loans { Faker::Number.between(from: 0, to: 50) }
|
7
|
+
total_outstanding_amount { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
8
|
+
approval_required { [ true, false ].sample }
|
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
|
+
|
12
|
+
trait :high_performance do
|
13
|
+
score { Faker::Number.between(from: 80.0, to: 100.0) }
|
14
|
+
total_outstanding_loans { Faker::Number.between(from: 20, to: 50) }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :low_performance do
|
18
|
+
score { Faker::Number.between(from: 0.0, to: 30.0) }
|
19
|
+
total_outstanding_loans { Faker::Number.between(from: 0, to: 5) }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :requiring_approval do
|
23
|
+
approval_required { true }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :expired do
|
27
|
+
expires_at { Faker::Date.between(from: 1.year.ago, to: 1.month.ago) }
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :facilitator, class: "Dscf::Credit::Facilitator" do
|
3
|
+
association :bank, factory: :bank
|
4
|
+
association :user, factory: :user if defined?(Dscf::Core::User)
|
5
|
+
association :kyc_approved_by, factory: :user if defined?(Dscf::Core::User)
|
6
|
+
name { Faker::Name.name }
|
7
|
+
type { %w[individual corporate agent].sample }
|
8
|
+
total_limit { Faker::Number.decimal(l_digits: 6, r_digits: 2) }
|
9
|
+
kyc_status { %w[pending approved rejected].sample }
|
10
|
+
|
11
|
+
trait :pending_kyc do
|
12
|
+
kyc_status { "pending" }
|
13
|
+
end
|
14
|
+
|
15
|
+
trait :approved do
|
16
|
+
kyc_status { "approved" }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :rejected do
|
20
|
+
kyc_status { "rejected" }
|
21
|
+
end
|
22
|
+
|
23
|
+
trait :individual do
|
24
|
+
type { "individual" }
|
25
|
+
end
|
26
|
+
|
27
|
+
trait :corporate do
|
28
|
+
type { "corporate" }
|
29
|
+
end
|
30
|
+
|
31
|
+
trait :agent do
|
32
|
+
type { "agent" }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :failed_operations_log, class: "Dscf::Credit::FailedOperationsLog" do
|
3
|
+
association :entity, factory: :loan
|
4
|
+
failure_type { %w[loan_disbursement payment_processing interest_calculation credit_scoring daily_routine accounting_entry].sample }
|
5
|
+
reason { Faker::Lorem.sentence }
|
6
|
+
retry_count { Faker::Number.between(from: 0, to: 5) }
|
7
|
+
resolved { [ true, false ].sample }
|
8
|
+
resolved_at { resolved ? Faker::Time.between(from: 1.day.ago, to: Time.current) : nil }
|
9
|
+
|
10
|
+
trait :resolved do
|
11
|
+
resolved { true }
|
12
|
+
resolved_at { Faker::Time.between(from: 1.day.ago, to: Time.current) }
|
13
|
+
end
|
14
|
+
|
15
|
+
trait :unresolved do
|
16
|
+
resolved { false }
|
17
|
+
resolved_at { nil }
|
18
|
+
end
|
19
|
+
|
20
|
+
trait :with_retries do
|
21
|
+
retry_count { 3 }
|
22
|
+
end
|
23
|
+
|
24
|
+
trait :payment_failure do
|
25
|
+
failure_type { "payment_processing" }
|
26
|
+
association :entity, factory: :payment
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :loan_failure do
|
30
|
+
failure_type { "loan_disbursement" }
|
31
|
+
association :entity, factory: :loan
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :loan_profile_scoring_spec, class: "Dscf::Credit::LoanProfileScoringSpec" do
|
3
|
+
association :loan_profile, factory: :loan_profile
|
4
|
+
association :created_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
+
association :approved_by, factory: :user if defined?(Dscf::Core::User)
|
6
|
+
scoring_input_data { Faker::Json.shallow_json(width: 5, options: { key: 'Name.name', value: 'Number.number' }) }
|
7
|
+
score { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
8
|
+
active { true }
|
9
|
+
expires_at { Faker::Date.between(from: 3.months.from_now, to: 1.year.from_now) }
|
10
|
+
|
11
|
+
trait :active do
|
12
|
+
active { true }
|
13
|
+
end
|
14
|
+
|
15
|
+
trait :inactive do
|
16
|
+
active { false }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :high_score do
|
20
|
+
score { Faker::Number.between(from: 80.0, to: 100.0) }
|
21
|
+
end
|
22
|
+
|
23
|
+
trait :low_score do
|
24
|
+
score { Faker::Number.between(from: 0.0, to: 30.0) }
|
25
|
+
end
|
26
|
+
|
27
|
+
trait :expired do
|
28
|
+
expires_at { Faker::Date.between(from: 1.year.ago, to: 1.month.ago) }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :loan_profile, class: "Dscf::Credit::LoanProfile" do
|
3
|
+
association :bank, factory: :bank
|
4
|
+
association :approved_by, factory: :user if defined?(Dscf::Core::User)
|
5
|
+
association :user, factory: :user if defined?(Dscf::Core::User)
|
6
|
+
association :backer, factory: :user if defined?(Dscf::Core::User)
|
7
|
+
status { %w[pending approved rejected].sample }
|
8
|
+
total_amount { Faker::Number.decimal(l_digits: 6, r_digits: 2).to_f }
|
9
|
+
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
|
+
|
12
|
+
trait :pending do
|
13
|
+
status { "pending" }
|
14
|
+
rejection_reason { nil }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :approved do
|
18
|
+
status { "approved" }
|
19
|
+
rejection_reason { nil }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :rejected do
|
23
|
+
status { "rejected" }
|
24
|
+
rejection_reason { Faker::Lorem.sentence }
|
25
|
+
end
|
26
|
+
|
27
|
+
trait :fully_available do
|
28
|
+
available_amount { total_amount }
|
29
|
+
end
|
30
|
+
|
31
|
+
trait :fully_utilized do
|
32
|
+
available_amount { 0 }
|
33
|
+
end
|
34
|
+
|
35
|
+
trait :partially_utilized do
|
36
|
+
available_amount { total_amount * 0.5 }
|
37
|
+
end
|
38
|
+
|
39
|
+
trait :small_amount do
|
40
|
+
total_amount { Faker::Number.between(from: 1000, to: 10000) }
|
41
|
+
available_amount { total_amount * rand(0.1..1.0) }
|
42
|
+
end
|
43
|
+
|
44
|
+
trait :large_amount do
|
45
|
+
total_amount { Faker::Number.between(from: 100000, to: 1000000) }
|
46
|
+
available_amount { total_amount * rand(0.1..1.0) }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :loan_transaction, class: "Dscf::Credit::LoanTransaction" do
|
3
|
+
association :loan, factory: :loan
|
4
|
+
transaction_type { %w[disbursement repayment interest_accrual penalty fee_charge].sample }
|
5
|
+
amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
6
|
+
transaction_reference { Faker::Alphanumeric.alphanumeric(number: 20).upcase }
|
7
|
+
status { %w[pending processing completed failed reversed].sample }
|
8
|
+
|
9
|
+
trait :disbursement do
|
10
|
+
transaction_type { "disbursement" }
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :repayment do
|
14
|
+
transaction_type { "repayment" }
|
15
|
+
end
|
16
|
+
|
17
|
+
trait :interest_accrual do
|
18
|
+
transaction_type { "interest_accrual" }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :penalty do
|
22
|
+
transaction_type { "penalty" }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :fee_charge do
|
26
|
+
transaction_type { "fee_charge" }
|
27
|
+
end
|
28
|
+
|
29
|
+
trait :completed do
|
30
|
+
status { "completed" }
|
31
|
+
end
|
32
|
+
|
33
|
+
trait :failed do
|
34
|
+
status { "failed" }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :loan, class: "Dscf::Credit::Loan" do
|
3
|
+
association :loan_profile, factory: :loan_profile
|
4
|
+
association :credit_line, factory: :credit_line
|
5
|
+
association :payment_request, factory: :payment_request
|
6
|
+
status { %w[pending approved disbursed active overdue paid closed].sample }
|
7
|
+
principal_amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
8
|
+
accrued_interest { Faker::Number.decimal(l_digits: 3, r_digits: 2) }
|
9
|
+
accrued_penalty { Faker::Number.decimal(l_digits: 3, r_digits: 2) }
|
10
|
+
facilitation_fee { Faker::Number.decimal(l_digits: 3, r_digits: 2) }
|
11
|
+
total_loan_amount { principal_amount + accrued_interest + facilitation_fee }
|
12
|
+
remaining_amount { Faker::Number.decimal(l_digits: 5, r_digits: 2) }
|
13
|
+
due_date { Faker::Date.between(from: 1.month.from_now, to: 6.months.from_now) }
|
14
|
+
disbursed_at { Faker::Time.between(from: 2.months.ago, to: Time.current) if status.in?(%w[disbursed active overdue paid closed]) }
|
15
|
+
|
16
|
+
trait :pending do
|
17
|
+
status { "pending" }
|
18
|
+
disbursed_at { nil }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :active do
|
22
|
+
status { "active" }
|
23
|
+
disbursed_at { Faker::Time.between(from: 2.months.ago, to: 1.month.ago) }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :overdue do
|
27
|
+
status { "overdue" }
|
28
|
+
due_date { Faker::Date.between(from: 1.month.ago, to: Date.current) }
|
29
|
+
disbursed_at { Faker::Time.between(from: 3.months.ago, to: 2.months.ago) }
|
30
|
+
end
|
31
|
+
|
32
|
+
trait :paid do
|
33
|
+
status { "paid" }
|
34
|
+
remaining_amount { 0 }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :parameter_normalizer, class: "Dscf::Credit::ParameterNormalizer" do
|
3
|
+
association :scoring_parameter, factory: :scoring_parameter
|
4
|
+
name { Faker::Lorem.words(number: 2).join(' ').titlecase }
|
5
|
+
description { Faker::Lorem.sentence }
|
6
|
+
raw_value { Faker::Lorem.word }
|
7
|
+
normalized_value { Faker::Number.decimal(l_digits: 1, r_digits: 2) }
|
8
|
+
|
9
|
+
trait :high_value do
|
10
|
+
raw_value { "excellent" }
|
11
|
+
normalized_value { Faker::Number.between(from: 8.0, to: 10.0) }
|
12
|
+
end
|
13
|
+
|
14
|
+
trait :medium_value do
|
15
|
+
raw_value { "good" }
|
16
|
+
normalized_value { Faker::Number.between(from: 5.0, to: 7.9) }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :low_value do
|
20
|
+
raw_value { "poor" }
|
21
|
+
normalized_value { Faker::Number.between(from: 0.0, to: 4.9) }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|