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
|