dscf-banking 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/banking/application_controller.rb +6 -0
- data/app/controllers/dscf/banking/interest_configurations_controller.rb +41 -0
- data/app/controllers/dscf/banking/interest_rate_tiers_controller.rb +30 -0
- data/app/controllers/dscf/banking/interest_rate_types_controller.rb +27 -0
- data/app/controllers/dscf/banking/product_approvals_controller.rb +36 -0
- data/app/controllers/dscf/banking/product_categories_controller.rb +27 -0
- data/app/controllers/dscf/banking/virtual_account_products_controller.rb +74 -0
- data/app/jobs/dscf/banking/application_job.rb +6 -0
- data/app/mailers/dscf/banking/application_mailer.rb +8 -0
- data/app/models/concerns/dscf/banking/auditable.rb +123 -0
- data/app/models/dscf/banking/application_record.rb +7 -0
- data/app/models/dscf/banking/interest_configuration.rb +85 -0
- data/app/models/dscf/banking/interest_rate_tier.rb +24 -0
- data/app/models/dscf/banking/interest_rate_type.rb +22 -0
- data/app/models/dscf/banking/product_approval.rb +23 -0
- data/app/models/dscf/banking/product_audit_log.rb +46 -0
- data/app/models/dscf/banking/product_category.rb +22 -0
- data/app/models/dscf/banking/virtual_account_product.rb +50 -0
- data/app/serializers/dscf/banking/interest_configuration_serializer.rb +11 -0
- data/app/serializers/dscf/banking/interest_rate_tier_serializer.rb +8 -0
- data/app/serializers/dscf/banking/interest_rate_type_serializer.rb +5 -0
- data/app/serializers/dscf/banking/product_approval_serializer.rb +11 -0
- data/app/serializers/dscf/banking/product_category_serializer.rb +5 -0
- data/app/serializers/dscf/banking/virtual_account_product_serializer.rb +10 -0
- data/config/routes.rb +12 -0
- data/db/migrate/20250830211002_create_dscf_banking_product_categories.rb +14 -0
- data/db/migrate/20250830211027_create_dscf_banking_interest_rate_types.rb +17 -0
- data/db/migrate/20250831063605_add_code_and_remove_fields_from_interest_rate_types.rb +12 -0
- data/db/migrate/20250831064917_create_dscf_banking_virtual_account_products.rb +26 -0
- data/db/migrate/20250831072627_create_dscf_banking_interest_configurations.rb +26 -0
- data/db/migrate/20250831080745_create_dscf_banking_interest_rate_tiers.rb +18 -0
- data/db/migrate/20250831082356_create_dscf_banking_product_approvals.rb +22 -0
- data/db/migrate/20250831083907_create_dscf_banking_product_audit_logs.rb +18 -0
- data/db/migrate/20250831084706_allow_null_virtual_account_product_id_in_product_audit_logs.rb +5 -0
- data/lib/dscf/banking/engine.rb +24 -0
- data/lib/dscf/banking/version.rb +5 -0
- data/lib/dscf/banking.rb +10 -0
- data/lib/tasks/dscf/banking_tasks.rake +4 -0
- data/spec/factories/dscf/banking/interest_configurations.rb +36 -0
- data/spec/factories/dscf/banking/interest_rate_tiers.rb +34 -0
- data/spec/factories/dscf/banking/interest_rate_types.rb +31 -0
- data/spec/factories/dscf/banking/product_approvals.rb +40 -0
- data/spec/factories/dscf/banking/product_audit_logs.rb +34 -0
- data/spec/factories/dscf/banking/product_categories.rb +26 -0
- data/spec/factories/dscf/banking/virtual_account_products.rb +46 -0
- metadata +510 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class ProductCategory < ApplicationRecord
|
3
|
+
self.table_name = "dscf_banking_product_categories"
|
4
|
+
|
5
|
+
validates :name, presence: true, uniqueness: true, length: { maximum: 100 }
|
6
|
+
validates :description, length: { maximum: 1000 }
|
7
|
+
validates :is_active, inclusion: { in: [ true, false ] }
|
8
|
+
|
9
|
+
scope :active, -> { where(is_active: true) }
|
10
|
+
scope :inactive, -> { where(is_active: false) }
|
11
|
+
scope :by_name, ->(name) { where("name ILIKE ?", "%#{name}%") }
|
12
|
+
|
13
|
+
before_validation :strip_whitespace
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def strip_whitespace
|
18
|
+
self.name = name&.strip
|
19
|
+
self.description = description&.strip
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class VirtualAccountProduct < ApplicationRecord
|
3
|
+
include Auditable
|
4
|
+
|
5
|
+
self.table_name = "dscf_banking_virtual_account_products"
|
6
|
+
|
7
|
+
enum :status, {
|
8
|
+
draft: 0,
|
9
|
+
pending_approval: 1,
|
10
|
+
approved: 2,
|
11
|
+
rejected: 3
|
12
|
+
}
|
13
|
+
|
14
|
+
belongs_to :product_category, class_name: "Dscf::Banking::ProductCategory", optional: true
|
15
|
+
belongs_to :created_by, class_name: "Dscf::Core::User"
|
16
|
+
belongs_to :approved_by, class_name: "Dscf::Core::User", optional: true
|
17
|
+
|
18
|
+
has_many :product_approvals, class_name: "Dscf::Banking::ProductApproval", dependent: :destroy
|
19
|
+
has_many :product_audit_logs, class_name: "Dscf::Banking::ProductAuditLog", dependent: :destroy
|
20
|
+
|
21
|
+
validates :product_code, presence: true, uniqueness: { case_sensitive: true }, length: { maximum: 50 }
|
22
|
+
validates :product_name, presence: true, length: { maximum: 200 }
|
23
|
+
validates :description, length: { maximum: 1000 }
|
24
|
+
validates :document_reference, length: { maximum: 100 }
|
25
|
+
validates :rejection_reason, length: { maximum: 500 }
|
26
|
+
validates :is_active, inclusion: { in: [ true, false ] }
|
27
|
+
validates :status, presence: true
|
28
|
+
validates :approved_by, presence: true, if: -> { status == "approved" }
|
29
|
+
|
30
|
+
scope :active, -> { where(is_active: true) }
|
31
|
+
scope :inactive, -> { where(is_active: false) }
|
32
|
+
scope :by_status, ->(status) { where(status: status) }
|
33
|
+
scope :by_product_code, ->(code) { where(product_code: code) }
|
34
|
+
scope :by_product_name, ->(name) { where("product_name ILIKE ?", "%#{name}%") }
|
35
|
+
scope :by_category, ->(category_id) { where(product_category_id: category_id) }
|
36
|
+
scope :created_by_user, ->(user_id) { where(created_by_id: user_id) }
|
37
|
+
|
38
|
+
before_validation :strip_whitespace
|
39
|
+
|
40
|
+
private
|
41
|
+
|
42
|
+
def strip_whitespace
|
43
|
+
self.product_code = product_code&.strip
|
44
|
+
self.product_name = product_name&.strip
|
45
|
+
self.description = description&.strip
|
46
|
+
self.document_reference = document_reference&.strip
|
47
|
+
self.rejection_reason = rejection_reason&.strip
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class InterestConfigurationSerializer < ActiveModel::Serializer
|
3
|
+
attributes :id, :virtual_account_product_id, :interest_rate_type_id, :annual_interest_rate,
|
4
|
+
:income_tax_rate, :minimum_balance_for_interest, :calculation_method, :compounding_period,
|
5
|
+
:interest_basis, :accrual_frequency, :rounding_rule, :calculation_timing,
|
6
|
+
:promotional_start_date, :promotional_end_date, :is_active, :created_at, :updated_at
|
7
|
+
|
8
|
+
belongs_to :virtual_account_product, serializer: VirtualAccountProductSerializer
|
9
|
+
belongs_to :interest_rate_type, serializer: InterestRateTypeSerializer
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class InterestRateTierSerializer < ActiveModel::Serializer
|
3
|
+
attributes :id, :tier_order, :balance_min, :balance_max, :interest_rate, :description,
|
4
|
+
:created_at, :updated_at
|
5
|
+
|
6
|
+
belongs_to :interest_config, serializer: InterestConfigurationSerializer
|
7
|
+
end
|
8
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class ProductApprovalSerializer < ActiveModel::Serializer
|
3
|
+
attributes :id, :virtual_account_product_id, :submitted_by_id, :submitted_at,
|
4
|
+
:reviewed_by_id, :reviewed_at, :status, :comments, :approval_level,
|
5
|
+
:created_at, :updated_at
|
6
|
+
|
7
|
+
belongs_to :virtual_account_product, serializer: VirtualAccountProductSerializer
|
8
|
+
belongs_to :submitted_by
|
9
|
+
belongs_to :reviewed_by
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Dscf::Banking
|
2
|
+
class VirtualAccountProductSerializer < ActiveModel::Serializer
|
3
|
+
attributes :id, :product_code, :product_name, :description, :document_reference,
|
4
|
+
:status, :rejection_reason, :is_active, :created_at, :updated_at
|
5
|
+
|
6
|
+
belongs_to :product_category, serializer: ProductCategorySerializer
|
7
|
+
belongs_to :created_by
|
8
|
+
belongs_to :approved_by
|
9
|
+
end
|
10
|
+
end
|
data/config/routes.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
Dscf::Banking::Engine.routes.draw do
|
2
|
+
resources :product_categories
|
3
|
+
resources :interest_rate_types
|
4
|
+
resources :virtual_account_products do
|
5
|
+
member do
|
6
|
+
get :audit_logs
|
7
|
+
end
|
8
|
+
end
|
9
|
+
resources :interest_configurations
|
10
|
+
resources :interest_rate_tiers
|
11
|
+
resources :product_approvals
|
12
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
class CreateDscfBankingProductCategories < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_product_categories do |t|
|
4
|
+
t.string :name, null: false, limit: 100
|
5
|
+
t.text :description
|
6
|
+
t.boolean :is_active, null: false, default: true
|
7
|
+
|
8
|
+
t.timestamps
|
9
|
+
end
|
10
|
+
|
11
|
+
add_index :dscf_banking_product_categories, :name, unique: true
|
12
|
+
add_index :dscf_banking_product_categories, :is_active
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class CreateDscfBankingInterestRateTypes < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_interest_rate_types do |t|
|
4
|
+
t.string :name, null: false, limit: 100
|
5
|
+
t.text :description
|
6
|
+
t.string :calculation_method, null: false, limit: 50
|
7
|
+
t.string :compounding_period, limit: 50
|
8
|
+
t.boolean :is_active, null: false, default: true
|
9
|
+
|
10
|
+
t.timestamps
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :dscf_banking_interest_rate_types, :name, unique: true
|
14
|
+
add_index :dscf_banking_interest_rate_types, :calculation_method
|
15
|
+
add_index :dscf_banking_interest_rate_types, :is_active
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
class AddCodeAndRemoveFieldsFromInterestRateTypes < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
add_column :dscf_banking_interest_rate_types, :code, :string, limit: 20, null: false
|
4
|
+
add_index :dscf_banking_interest_rate_types, :code, unique: true, name: "idx_interest_rate_types_code"
|
5
|
+
|
6
|
+
remove_column :dscf_banking_interest_rate_types, :calculation_method, :string
|
7
|
+
remove_column :dscf_banking_interest_rate_types, :compounding_period, :string
|
8
|
+
remove_column :dscf_banking_interest_rate_types, :is_active, :boolean
|
9
|
+
|
10
|
+
change_column :dscf_banking_interest_rate_types, :name, :string, limit: 50, null: false
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateDscfBankingVirtualAccountProducts < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_virtual_account_products do |t|
|
4
|
+
t.string :product_code, limit: 50, null: false
|
5
|
+
t.string :product_name, limit: 200, null: false
|
6
|
+
t.references :product_category, null: true, foreign_key: { to_table: :dscf_banking_product_categories }
|
7
|
+
t.text :description
|
8
|
+
t.string :document_reference, limit: 100
|
9
|
+
t.integer :status, default: 0
|
10
|
+
t.references :created_by, null: false, foreign_key: { to_table: :dscf_core_users }
|
11
|
+
t.references :approved_by, null: true, foreign_key: { to_table: :dscf_core_users }
|
12
|
+
t.timestamp :approved_at
|
13
|
+
t.text :rejection_reason
|
14
|
+
t.boolean :is_active, default: true
|
15
|
+
|
16
|
+
t.timestamps
|
17
|
+
end
|
18
|
+
|
19
|
+
add_index :dscf_banking_virtual_account_products, :product_code, unique: true, name: "idx_products_code"
|
20
|
+
add_index :dscf_banking_virtual_account_products, :status, name: "idx_products_status"
|
21
|
+
add_index :dscf_banking_virtual_account_products, :product_category_id, name: "idx_products_category"
|
22
|
+
add_index :dscf_banking_virtual_account_products, :created_by_id, name: "idx_products_created_by"
|
23
|
+
add_index :dscf_banking_virtual_account_products, :approved_by_id, name: "idx_products_approved_by"
|
24
|
+
add_index :dscf_banking_virtual_account_products, :is_active, name: "idx_products_active"
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
class CreateDscfBankingInterestConfigurations < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_interest_configurations do |t|
|
4
|
+
t.references :virtual_account_product, null: false, foreign_key: { to_table: :dscf_banking_virtual_account_products }
|
5
|
+
t.references :interest_rate_type, null: false, foreign_key: { to_table: :dscf_banking_interest_rate_types }
|
6
|
+
t.decimal :annual_interest_rate, precision: 5, scale: 4
|
7
|
+
t.decimal :income_tax_rate, precision: 5, scale: 4, default: 0.05
|
8
|
+
t.decimal :minimum_balance_for_interest, precision: 18, scale: 2, default: 0
|
9
|
+
t.date :promotional_start_date
|
10
|
+
t.date :promotional_end_date
|
11
|
+
t.integer :calculation_method
|
12
|
+
t.integer :compounding_period
|
13
|
+
t.integer :interest_basis
|
14
|
+
t.integer :accrual_frequency
|
15
|
+
t.integer :rounding_rule
|
16
|
+
t.datetime :calculation_timing
|
17
|
+
t.boolean :is_active, default: true
|
18
|
+
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
|
22
|
+
add_index :dscf_banking_interest_configurations, :virtual_account_product_id, name: 'idx_interest_config_product'
|
23
|
+
add_index :dscf_banking_interest_configurations, :interest_rate_type_id, name: 'idx_interest_config_type'
|
24
|
+
add_index :dscf_banking_interest_configurations, :is_active, name: 'idx_interest_config_active'
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateDscfBankingInterestRateTiers < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_interest_rate_tiers do |t|
|
4
|
+
t.references :interest_config, null: false, foreign_key: { to_table: :dscf_banking_interest_configurations }
|
5
|
+
t.integer :tier_order, null: false
|
6
|
+
t.decimal :balance_min, precision: 18, scale: 2, null: false
|
7
|
+
t.decimal :balance_max, precision: 18, scale: 2
|
8
|
+
t.decimal :interest_rate, precision: 5, scale: 4, null: false
|
9
|
+
t.string :description, limit: 200
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
|
14
|
+
add_index :dscf_banking_interest_rate_tiers, [ :interest_config_id, :tier_order ], unique: true, name: "unique_tier_order"
|
15
|
+
add_index :dscf_banking_interest_rate_tiers, :interest_config_id, name: "idx_rate_tiers_config"
|
16
|
+
add_index :dscf_banking_interest_rate_tiers, [ :interest_config_id, :tier_order ], name: "idx_rate_tiers_order"
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class CreateDscfBankingProductApprovals < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_product_approvals do |t|
|
4
|
+
t.references :virtual_account_product, null: false, foreign_key: { to_table: :dscf_banking_virtual_account_products }
|
5
|
+
t.references :submitted_by, null: false, foreign_key: { to_table: :dscf_core_users }
|
6
|
+
t.timestamp :submitted_at, null: false, default: -> { "CURRENT_TIMESTAMP" }
|
7
|
+
t.references :reviewed_by, null: true, foreign_key: { to_table: :dscf_core_users }
|
8
|
+
t.timestamp :reviewed_at
|
9
|
+
t.integer :status, null: false, default: 0
|
10
|
+
t.text :comments
|
11
|
+
t.integer :approval_level, default: 1
|
12
|
+
|
13
|
+
t.timestamps null: false, default: -> { "CURRENT_TIMESTAMP" }
|
14
|
+
end
|
15
|
+
|
16
|
+
add_index :dscf_banking_product_approvals, :virtual_account_product_id, name: "idx_approvals_product"
|
17
|
+
add_index :dscf_banking_product_approvals, :status, name: "idx_approvals_status"
|
18
|
+
add_index :dscf_banking_product_approvals, :submitted_by_id, name: "idx_approvals_submitted_by"
|
19
|
+
add_index :dscf_banking_product_approvals, :reviewed_by_id, name: "idx_approvals_reviewed_by"
|
20
|
+
add_index :dscf_banking_product_approvals, :submitted_at, name: "idx_approvals_submitted_at"
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class CreateDscfBankingProductAuditLogs < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
create_table :dscf_banking_product_audit_logs do |t|
|
4
|
+
t.references :virtual_account_product, null: false, foreign_key: { to_table: :dscf_banking_virtual_account_products }
|
5
|
+
t.references :changed_by, null: false, foreign_key: { to_table: :dscf_core_users }
|
6
|
+
t.integer :change_type, null: false
|
7
|
+
t.jsonb :old_values
|
8
|
+
t.jsonb :new_values
|
9
|
+
t.text :change_description
|
10
|
+
t.timestamps null: false, default: -> { 'CURRENT_TIMESTAMP' }
|
11
|
+
end
|
12
|
+
|
13
|
+
add_index :dscf_banking_product_audit_logs, :virtual_account_product_id, name: 'idx_audit_product'
|
14
|
+
add_index :dscf_banking_product_audit_logs, :changed_by_id, name: 'idx_audit_user'
|
15
|
+
add_index :dscf_banking_product_audit_logs, :created_at, name: 'idx_audit_created_at'
|
16
|
+
add_index :dscf_banking_product_audit_logs, :change_type, name: 'idx_audit_change_type'
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Dscf
|
2
|
+
module Banking
|
3
|
+
class Engine < ::Rails::Engine
|
4
|
+
isolate_namespace Dscf::Banking
|
5
|
+
config.generators.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 "bscf_banking.factories", after: "factory_bot.set_factory_paths" do
|
14
|
+
FactoryBot.definition_file_paths << File.expand_path("../../../spec/factories", __dir__) if defined?(FactoryBot)
|
15
|
+
end
|
16
|
+
|
17
|
+
initializer :append_migrations do |app|
|
18
|
+
app.config.paths["db/migrate"].concat(config.paths["db/migrate"].expanded) unless app.root.to_s.match(root.to_s + File::SEPARATOR)
|
19
|
+
end
|
20
|
+
|
21
|
+
require "active_storage/engine"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/dscf/banking.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :interest_configuration, class: "Dscf::Banking::InterestConfiguration" do
|
3
|
+
association :virtual_account_product, factory: :virtual_account_product
|
4
|
+
association :interest_rate_type, factory: :interest_rate_type
|
5
|
+
|
6
|
+
annual_interest_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.0001, 1.0) }
|
7
|
+
income_tax_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.0, 1.0) }
|
8
|
+
minimum_balance_for_interest { Faker::Number.decimal(l_digits: 4, r_digits: 2).clamp(0.0, 999999.99) }
|
9
|
+
calculation_method { [ 0, 1 ].sample }
|
10
|
+
compounding_period { [ 0, 1, 2, 3, 4 ].sample }
|
11
|
+
interest_basis { [ 0, 1, 2 ].sample }
|
12
|
+
accrual_frequency { [ 0, 1, 2, 3, 4 ].sample }
|
13
|
+
rounding_rule { [ 0, 1, 2 ].sample }
|
14
|
+
calculation_timing { Faker::Time.between(from: Time.current.beginning_of_day, to: Time.current.end_of_day) }
|
15
|
+
is_active { true }
|
16
|
+
|
17
|
+
trait :inactive do
|
18
|
+
is_active { false }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :promotional do
|
22
|
+
promotional_start_date { Faker::Date.between(from: 30.days.ago, to: Date.current) }
|
23
|
+
promotional_end_date { Faker::Date.between(from: Date.current, to: 30.days.from_now) }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :simple_interest do
|
27
|
+
calculation_method { 0 }
|
28
|
+
compounding_period { nil }
|
29
|
+
end
|
30
|
+
|
31
|
+
trait :compound_interest do
|
32
|
+
calculation_method { 1 }
|
33
|
+
compounding_period { [ 0, 1, 2, 3, 4 ].sample }
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :interest_rate_tier, class: "Dscf::Banking::InterestRateTier" do
|
3
|
+
association :interest_config, factory: :interest_configuration
|
4
|
+
sequence(:tier_order) { |n| n }
|
5
|
+
balance_min { 1000.00 }
|
6
|
+
balance_max { 5000.00 }
|
7
|
+
interest_rate { Faker::Number.decimal(l_digits: 1, r_digits: 4).clamp(0.0001, 0.9999) }
|
8
|
+
description { Faker::Lorem.sentence(word_count: 5) }
|
9
|
+
|
10
|
+
trait :first_tier do
|
11
|
+
tier_order { 1 }
|
12
|
+
balance_min { 0 }
|
13
|
+
balance_max { 10000 }
|
14
|
+
interest_rate { 0.0250 }
|
15
|
+
description { "First tier for balances up to $10,000" }
|
16
|
+
end
|
17
|
+
|
18
|
+
trait :second_tier do
|
19
|
+
tier_order { 2 }
|
20
|
+
balance_min { 10000 }
|
21
|
+
balance_max { 50000 }
|
22
|
+
interest_rate { 0.0350 }
|
23
|
+
description { "Second tier for balances $10,000 to $50,000" }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :unlimited_tier do
|
27
|
+
tier_order { 3 }
|
28
|
+
balance_min { 50000 }
|
29
|
+
balance_max { nil }
|
30
|
+
interest_rate { 0.0450 }
|
31
|
+
description { "Unlimited tier for balances over $50,000" }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :interest_rate_type, class: "Dscf::Banking::InterestRateType" do
|
3
|
+
sequence(:code) { |n| "RATE#{n.to_s.rjust(3, '0')}" }
|
4
|
+
sequence(:name) { |n| "Interest Rate Type #{n}" }
|
5
|
+
description { Faker::Lorem.paragraph(sentence_count: 2) }
|
6
|
+
|
7
|
+
trait :fixed_rate do
|
8
|
+
code { "FIXED" }
|
9
|
+
name { "Fixed Rate" }
|
10
|
+
description { "Fixed annual interest rate" }
|
11
|
+
end
|
12
|
+
|
13
|
+
trait :variable_rate do
|
14
|
+
code { "VARIABLE" }
|
15
|
+
name { "Variable Rate" }
|
16
|
+
description { "Tiered rates based on balance" }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :promotional_rate do
|
20
|
+
code { "PROMOTIONAL" }
|
21
|
+
name { "Promotional Rate" }
|
22
|
+
description { "Special rate for limited period" }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :interest_free do
|
26
|
+
code { "INTEREST_FREE" }
|
27
|
+
name { "Interest Free" }
|
28
|
+
description { "No interest applicable" }
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :product_approval, class: "Dscf::Banking::ProductApproval" do
|
3
|
+
association :virtual_account_product, factory: :virtual_account_product
|
4
|
+
association :submitted_by, factory: :user
|
5
|
+
submitted_at { Faker::Time.backward(days: 30) }
|
6
|
+
reviewed_by { nil }
|
7
|
+
reviewed_at { nil }
|
8
|
+
status { 0 }
|
9
|
+
comments { Faker::Lorem.paragraph(sentence_count: 2) }
|
10
|
+
approval_level { 1 }
|
11
|
+
|
12
|
+
trait :approved do
|
13
|
+
status { 1 }
|
14
|
+
association :reviewed_by, factory: :user
|
15
|
+
reviewed_at { Faker::Time.backward(days: 7) }
|
16
|
+
comments { "Approval granted after thorough review" }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :rejected do
|
20
|
+
status { 2 }
|
21
|
+
association :reviewed_by, factory: :user
|
22
|
+
reviewed_at { Faker::Time.backward(days: 7) }
|
23
|
+
comments { "Rejected due to insufficient documentation" }
|
24
|
+
end
|
25
|
+
|
26
|
+
trait :under_review do
|
27
|
+
status { 3 }
|
28
|
+
association :reviewed_by, factory: :user
|
29
|
+
comments { "Currently under review by compliance team" }
|
30
|
+
end
|
31
|
+
|
32
|
+
trait :level_2 do
|
33
|
+
approval_level { 2 }
|
34
|
+
end
|
35
|
+
|
36
|
+
trait :level_3 do
|
37
|
+
approval_level { 3 }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :product_audit_log, class: 'Dscf::Banking::ProductAuditLog' do
|
3
|
+
association :virtual_account_product, factory: :virtual_account_product
|
4
|
+
association :changed_by, factory: :user
|
5
|
+
change_type { :updated }
|
6
|
+
old_values { { name: Faker::Commerce.product_name, is_active: false } }
|
7
|
+
new_values { { name: Faker::Commerce.product_name, is_active: true } }
|
8
|
+
change_description { Faker::Lorem.sentence }
|
9
|
+
|
10
|
+
trait :created do
|
11
|
+
change_type { :created }
|
12
|
+
old_values { nil }
|
13
|
+
new_values { { name: Faker::Commerce.product_name, is_active: true } }
|
14
|
+
end
|
15
|
+
|
16
|
+
trait :deleted do
|
17
|
+
change_type { :deleted }
|
18
|
+
old_values { { name: Faker::Commerce.product_name, is_active: true } }
|
19
|
+
new_values { nil }
|
20
|
+
end
|
21
|
+
|
22
|
+
trait :approved do
|
23
|
+
change_type { :approved }
|
24
|
+
old_values { { status: 'pending_approval' } }
|
25
|
+
new_values { { status: 'approved' } }
|
26
|
+
end
|
27
|
+
|
28
|
+
trait :rejected do
|
29
|
+
change_type { :rejected }
|
30
|
+
old_values { { status: 'pending_approval' } }
|
31
|
+
new_values { { status: 'rejected' } }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :product_category, class: "Dscf::Banking::ProductCategory" do
|
3
|
+
sequence(:name) { |n| "#{Faker::Bank.name} Category #{n}" }
|
4
|
+
description { Faker::Lorem.paragraph(sentence_count: 2) }
|
5
|
+
is_active { true }
|
6
|
+
|
7
|
+
trait :inactive do
|
8
|
+
is_active { false }
|
9
|
+
end
|
10
|
+
|
11
|
+
trait :savings do
|
12
|
+
name { "Savings Account" }
|
13
|
+
description { "Traditional savings account with competitive interest rates" }
|
14
|
+
end
|
15
|
+
|
16
|
+
trait :checking do
|
17
|
+
name { "Checking Account" }
|
18
|
+
description { "Everyday banking account for transactions and payments" }
|
19
|
+
end
|
20
|
+
|
21
|
+
trait :loan do
|
22
|
+
name { "Personal Loan" }
|
23
|
+
description { "Flexible personal loans for various financial needs" }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
FactoryBot.define do
|
2
|
+
factory :virtual_account_product, class: "Dscf::Banking::VirtualAccountProduct" do
|
3
|
+
sequence(:product_code) { |n| "VAP#{n.to_s.rjust(3, '0')}" }
|
4
|
+
product_name { Faker::Bank.name + " Virtual Account" }
|
5
|
+
association :product_category, factory: :product_category
|
6
|
+
description { Faker::Lorem.paragraph(sentence_count: 3) }
|
7
|
+
document_reference { "DOC-#{Faker::Alphanumeric.alphanumeric(number: 8).upcase}" }
|
8
|
+
status { :draft }
|
9
|
+
association :created_by, factory: :user
|
10
|
+
approved_by { nil }
|
11
|
+
approved_at { nil }
|
12
|
+
rejection_reason { nil }
|
13
|
+
is_active { true }
|
14
|
+
|
15
|
+
trait :pending_approval do
|
16
|
+
status { :pending_approval }
|
17
|
+
end
|
18
|
+
|
19
|
+
trait :approved do
|
20
|
+
status { :approved }
|
21
|
+
association :approved_by, factory: :user
|
22
|
+
approved_at { Faker::Time.backward(days: 7) }
|
23
|
+
end
|
24
|
+
|
25
|
+
trait :rejected do
|
26
|
+
status { :rejected }
|
27
|
+
association :approved_by, factory: :user
|
28
|
+
approved_at { Faker::Time.backward(days: 7) }
|
29
|
+
rejection_reason { Faker::Lorem.sentence(word_count: 10) }
|
30
|
+
end
|
31
|
+
|
32
|
+
trait :inactive do
|
33
|
+
is_active { false }
|
34
|
+
end
|
35
|
+
|
36
|
+
trait :savings_product do
|
37
|
+
product_name { "Premium Savings Virtual Account" }
|
38
|
+
description { "High-yield savings account with virtual account features" }
|
39
|
+
end
|
40
|
+
|
41
|
+
trait :business_product do
|
42
|
+
product_name { "Business Virtual Account" }
|
43
|
+
description { "Comprehensive virtual account solution for businesses" }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|