dscf-banking 0.3.5 → 0.3.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/controllers/dscf/banking/accounts_controller.rb +7 -7
- data/app/controllers/dscf/banking/applications_controller.rb +2 -2
- data/app/controllers/dscf/banking/documents_controller.rb +4 -4
- data/app/controllers/dscf/banking/interest_configurations_controller.rb +5 -3
- data/app/models/dscf/banking/interest_configuration.rb +117 -8
- data/app/models/dscf/banking/virtual_account_product.rb +2 -1
- data/app/serializers/dscf/banking/application_serializer.rb +1 -1
- data/app/serializers/dscf/banking/document_serializer.rb +9 -1
- data/app/serializers/dscf/banking/interest_configuration_serializer.rb +1 -0
- data/config/routes.rb +1 -2
- data/db/seeds/interest_rate_types.rb +38 -0
- data/db/seeds.rb +37 -0
- data/lib/dscf/banking/version.rb +1 -1
- data/spec/factories/dscf/banking/interest_configurations.rb +42 -14
- data/spec/factories/dscf/banking/interest_rate_types.rb +1 -1
- metadata +3 -4
- data/config/initializers/review_callbacks.rb +0 -23
- data/config/initializers/review_extensions.rb +0 -36
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f5ee5ccda8af113288e3eea6f69ba2a24b71d980850d72b25efb187b4f82579b
|
|
4
|
+
data.tar.gz: ab2d16dcde12321a0a18da6824e5f5788c10ca8aba568eb138085440187c3976
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e68fcc9ad5dee13a9589181cede9394c2854c32d14db75e76618d523a2c8ec82a412acb867a62281477197e143a845b3d68299fe254d446e0158a059fe8f9bde
|
|
7
|
+
data.tar.gz: 99cdc4974f9c5068416f14c3fba844c3e7b023a1da84b0e42838f9aa88d0c4bc283824225344297f5afac92f4d7dd031d20390384d3538abcb9b5e8d6ad46c31
|
|
@@ -34,7 +34,7 @@ module Dscf::Banking
|
|
|
34
34
|
user = account.application.user
|
|
35
35
|
profile = user.respond_to?(:user_profile) ? user.user_profile : nil
|
|
36
36
|
if profile
|
|
37
|
-
full_name = [profile.first_name, profile.middle_name, profile.last_name].compact.join(" ")
|
|
37
|
+
full_name = [ profile.first_name, profile.middle_name, profile.last_name ].compact.join(" ")
|
|
38
38
|
render json: { success: true, data: { full_name: full_name } }
|
|
39
39
|
else
|
|
40
40
|
render json: { success: false, error: "User profile not found" }, status: :not_found
|
|
@@ -231,29 +231,29 @@ module Dscf::Banking
|
|
|
231
231
|
|
|
232
232
|
def transactions
|
|
233
233
|
account = Dscf::Banking::Account.find(params[:id])
|
|
234
|
-
|
|
234
|
+
|
|
235
235
|
transactions = Dscf::Banking::Transaction
|
|
236
236
|
.where("account_id = ? OR debit_account_id = ? OR credit_account_id = ?", account.id, account.id, account.id)
|
|
237
237
|
.includes(:account, :transaction_type, :debit_account, :credit_account)
|
|
238
|
-
|
|
238
|
+
|
|
239
239
|
# Apply pagination if requested
|
|
240
240
|
page = params[:page].to_i
|
|
241
241
|
per_page = (params[:per_page] || 10).to_i
|
|
242
|
-
|
|
242
|
+
|
|
243
243
|
# Apply filters
|
|
244
244
|
transactions = transactions.where(status: params[:status]) if params[:status].present?
|
|
245
245
|
transactions = transactions.where(transaction_type_id: params[:transaction_type_id]) if params[:transaction_type_id].present?
|
|
246
|
-
|
|
246
|
+
|
|
247
247
|
# Apply ordering
|
|
248
248
|
order_column = %w[id reference_number amount currency status created_at updated_at].include?(params[:order_by]) ? params[:order_by] : "created_at"
|
|
249
249
|
order_direction = %w[asc desc].include?(params[:order_direction]) ? params[:order_direction] : "desc"
|
|
250
250
|
transactions = transactions.order("#{order_column} #{order_direction}")
|
|
251
|
-
|
|
251
|
+
|
|
252
252
|
if page.positive?
|
|
253
253
|
total_count = transactions.count
|
|
254
254
|
total_pages = (total_count.to_f / per_page).ceil
|
|
255
255
|
transactions = transactions.offset((page - 1) * per_page).limit(per_page)
|
|
256
|
-
|
|
256
|
+
|
|
257
257
|
render json: {
|
|
258
258
|
success: true,
|
|
259
259
|
message: "Account transactions retrieved successfully",
|
|
@@ -24,7 +24,7 @@ module Dscf::Banking
|
|
|
24
24
|
|
|
25
25
|
private
|
|
26
26
|
|
|
27
|
-
|
|
27
|
+
def application_data(application)
|
|
28
28
|
{
|
|
29
29
|
id: application.id,
|
|
30
30
|
application_number: application.application_number,
|
|
@@ -52,7 +52,7 @@ module Dscf::Banking
|
|
|
52
52
|
end
|
|
53
53
|
|
|
54
54
|
def eager_loaded_associations
|
|
55
|
-
[ :user, :virtual_account_product, :
|
|
55
|
+
[ :user, :virtual_account_product, :documents, reviews: { reviewed_by: :user_profile } ]
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
def allowed_order_columns
|
|
@@ -8,7 +8,7 @@ module Dscf::Banking
|
|
|
8
8
|
super do
|
|
9
9
|
documents = @application.documents
|
|
10
10
|
options = { each_serializer: DocumentSerializer }
|
|
11
|
-
[documents, options]
|
|
11
|
+
[ documents, options ]
|
|
12
12
|
end
|
|
13
13
|
end
|
|
14
14
|
|
|
@@ -16,17 +16,17 @@ module Dscf::Banking
|
|
|
16
16
|
super do
|
|
17
17
|
document = @application.documents.find(params[:id])
|
|
18
18
|
options = { serializer: DocumentSerializer }
|
|
19
|
-
[document, options]
|
|
19
|
+
[ document, options ]
|
|
20
20
|
end
|
|
21
21
|
end
|
|
22
22
|
|
|
23
23
|
def create
|
|
24
24
|
document = @application.documents.build(model_params)
|
|
25
|
-
|
|
25
|
+
|
|
26
26
|
if document.save
|
|
27
27
|
# Attach file if provided
|
|
28
28
|
document.files.attach(params[:document][:file]) if params[:document][:file].present?
|
|
29
|
-
|
|
29
|
+
|
|
30
30
|
render json: {
|
|
31
31
|
success: true,
|
|
32
32
|
data: DocumentSerializer.new(document).as_json
|
|
@@ -24,7 +24,7 @@ module Dscf::Banking
|
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
def eager_loaded_associations
|
|
27
|
-
[ :virtual_account_product, :interest_rate_type ]
|
|
27
|
+
[ :virtual_account_product, :interest_rate_type, :interest_rate_tiers ]
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def allowed_order_columns
|
|
@@ -33,8 +33,10 @@ module Dscf::Banking
|
|
|
33
33
|
|
|
34
34
|
def default_serializer_includes
|
|
35
35
|
{
|
|
36
|
-
virtual_account_product:
|
|
37
|
-
interest_rate_type:
|
|
36
|
+
index: [ :virtual_account_product, :interest_rate_type, :interest_rate_tiers ],
|
|
37
|
+
show: [ :virtual_account_product, :interest_rate_type, :interest_rate_tiers ],
|
|
38
|
+
create: [ :virtual_account_product, :interest_rate_type, :interest_rate_tiers ],
|
|
39
|
+
update: [ :virtual_account_product, :interest_rate_type, :interest_rate_tiers ]
|
|
38
40
|
}
|
|
39
41
|
end
|
|
40
42
|
end
|
|
@@ -2,19 +2,25 @@ module Dscf::Banking
|
|
|
2
2
|
class InterestConfiguration < ApplicationRecord
|
|
3
3
|
belongs_to :interest_rate_type, class_name: "Dscf::Banking::InterestRateType"
|
|
4
4
|
belongs_to :virtual_account_product, class_name: "Dscf::Banking::VirtualAccountProduct"
|
|
5
|
-
has_many :interest_rate_tiers, class_name: "Dscf::Banking::InterestRateTier", dependent: :destroy
|
|
5
|
+
has_many :interest_rate_tiers, class_name: "Dscf::Banking::InterestRateTier", foreign_key: :interest_config_id, dependent: :destroy
|
|
6
6
|
|
|
7
|
-
validates :annual_interest_rate, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 1 }
|
|
7
|
+
validates :annual_interest_rate, presence: true, numericality: { greater_than: 0, less_than_or_equal_to: 1 }, unless: :interest_free?
|
|
8
|
+
validates :annual_interest_rate, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }, if: :interest_free?, allow_nil: true
|
|
8
9
|
validates :income_tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0, less_than_or_equal_to: 1 }
|
|
9
10
|
validates :minimum_balance_for_interest, presence: true, numericality: { greater_than_or_equal_to: 0 }
|
|
10
|
-
validates :calculation_method, presence: true
|
|
11
|
-
|
|
12
|
-
validates :
|
|
13
|
-
validates :
|
|
14
|
-
validates :
|
|
15
|
-
validates :calculation_timing, presence: true
|
|
11
|
+
validates :calculation_method, presence: true, unless: :interest_free?
|
|
12
|
+
validates :interest_basis, presence: true, unless: :interest_free?
|
|
13
|
+
validates :accrual_frequency, presence: true, unless: :interest_free?
|
|
14
|
+
validates :rounding_rule, presence: true, unless: :interest_free?
|
|
15
|
+
validates :calculation_timing, presence: true, unless: :interest_free?
|
|
16
16
|
validates :is_active, inclusion: { in: [ true, false ] }
|
|
17
|
+
|
|
17
18
|
validate :promotional_dates_consistency
|
|
19
|
+
validate :rate_type_specific_validations
|
|
20
|
+
validate :tiers_only_for_variable_rate
|
|
21
|
+
validate :interest_rate_type_requirements
|
|
22
|
+
|
|
23
|
+
before_validation :set_interest_basis_from_accrual_frequency
|
|
18
24
|
|
|
19
25
|
enum :calculation_method, {
|
|
20
26
|
simple: 0,
|
|
@@ -57,6 +63,33 @@ module Dscf::Banking
|
|
|
57
63
|
scope :promotional, -> { where.not(promotional_start_date: nil, promotional_end_date: nil) }
|
|
58
64
|
scope :current_promotional, -> { where("promotional_start_date <= ? AND promotional_end_date >= ?", Date.current, Date.current) }
|
|
59
65
|
|
|
66
|
+
def self.ransackable_attributes(auth_object = nil)
|
|
67
|
+
%w[id virtual_account_product_id interest_rate_type_id annual_interest_rate income_tax_rate
|
|
68
|
+
minimum_balance_for_interest calculation_method compounding_period interest_basis
|
|
69
|
+
accrual_frequency rounding_rule promotional_start_date promotional_end_date is_active
|
|
70
|
+
created_at updated_at]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def self.ransackable_associations(auth_object = nil)
|
|
74
|
+
%w[virtual_account_product interest_rate_type interest_rate_tiers]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def interest_free?
|
|
78
|
+
interest_rate_type&.code == "INTEREST_FREE"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def fixed_rate?
|
|
82
|
+
interest_rate_type&.code == "FIXED"
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def variable_rate?
|
|
86
|
+
interest_rate_type&.code == "VARIABLE"
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def promotional_rate?
|
|
90
|
+
interest_rate_type&.code == "PROMOTIONAL"
|
|
91
|
+
end
|
|
92
|
+
|
|
60
93
|
private
|
|
61
94
|
|
|
62
95
|
def promotional_dates_consistency
|
|
@@ -81,5 +114,81 @@ module Dscf::Banking
|
|
|
81
114
|
errors.add(:calculation_day_of_month, "must be specified for annual accrual") if calculation_day_of_month.blank?
|
|
82
115
|
end
|
|
83
116
|
end
|
|
117
|
+
|
|
118
|
+
def rate_type_specific_validations
|
|
119
|
+
return if interest_rate_type.blank?
|
|
120
|
+
|
|
121
|
+
case interest_rate_type.code
|
|
122
|
+
when "INTEREST_FREE"
|
|
123
|
+
validate_interest_free
|
|
124
|
+
when "FIXED"
|
|
125
|
+
validate_fixed_rate
|
|
126
|
+
when "VARIABLE"
|
|
127
|
+
validate_variable_rate
|
|
128
|
+
when "PROMOTIONAL"
|
|
129
|
+
validate_promotional_rate
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def validate_interest_free
|
|
134
|
+
if annual_interest_rate.present? && annual_interest_rate > 0
|
|
135
|
+
errors.add(:annual_interest_rate, "must be 0 for interest-free accounts")
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def validate_fixed_rate
|
|
140
|
+
if annual_interest_rate.blank? || annual_interest_rate <= 0
|
|
141
|
+
errors.add(:annual_interest_rate, "must be greater than 0 for fixed rate")
|
|
142
|
+
end
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def validate_variable_rate
|
|
146
|
+
if annual_interest_rate.blank? || annual_interest_rate <= 0
|
|
147
|
+
errors.add(:annual_interest_rate, "must be greater than 0 for variable rate")
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def validate_promotional_rate
|
|
152
|
+
if promotional_start_date.blank?
|
|
153
|
+
errors.add(:promotional_start_date, "is required for promotional rate")
|
|
154
|
+
end
|
|
155
|
+
if promotional_end_date.blank?
|
|
156
|
+
errors.add(:promotional_end_date, "is required for promotional rate")
|
|
157
|
+
end
|
|
158
|
+
if annual_interest_rate.blank? || annual_interest_rate <= 0
|
|
159
|
+
errors.add(:annual_interest_rate, "must be greater than 0 for promotional rate")
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def tiers_only_for_variable_rate
|
|
164
|
+
return if interest_rate_type.blank?
|
|
165
|
+
return unless persisted? || interest_rate_tiers.loaded?
|
|
166
|
+
|
|
167
|
+
if !variable_rate? && interest_rate_tiers.any?
|
|
168
|
+
errors.add(:base, "Interest rate tiers can only be used with variable rate type")
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def interest_rate_type_requirements
|
|
173
|
+
return if interest_rate_type.blank?
|
|
174
|
+
return unless persisted?
|
|
175
|
+
|
|
176
|
+
if variable_rate? && interest_rate_tiers.empty?
|
|
177
|
+
errors.add(:base, "Variable rate type requires at least one interest rate tier")
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def set_interest_basis_from_accrual_frequency
|
|
182
|
+
return if accrual_frequency.blank?
|
|
183
|
+
return if interest_free?
|
|
184
|
+
return if interest_basis.present?
|
|
185
|
+
|
|
186
|
+
case accrual_frequency
|
|
187
|
+
when "daily"
|
|
188
|
+
self.interest_basis = :actual_365
|
|
189
|
+
when "monthly", "quarterly", "annually"
|
|
190
|
+
self.interest_basis = :thirty_360
|
|
191
|
+
end
|
|
192
|
+
end
|
|
84
193
|
end
|
|
85
194
|
end
|
|
@@ -19,6 +19,7 @@ module Dscf::Banking
|
|
|
19
19
|
|
|
20
20
|
has_many :product_approvals, class_name: "Dscf::Banking::ProductApproval", dependent: :destroy
|
|
21
21
|
has_many :product_audit_logs, class_name: "Dscf::Banking::ProductAuditLog", dependent: :destroy
|
|
22
|
+
has_many :interest_configurations, class_name: "Dscf::Banking::InterestConfiguration", dependent: :destroy
|
|
22
23
|
|
|
23
24
|
validates :product_code, presence: true, uniqueness: { case_sensitive: true }, length: { maximum: 50 }
|
|
24
25
|
validates :product_name, presence: true, length: { maximum: 200 }
|
|
@@ -42,7 +43,7 @@ module Dscf::Banking
|
|
|
42
43
|
end
|
|
43
44
|
|
|
44
45
|
def self.ransackable_associations(auth_object = nil)
|
|
45
|
-
%w[product_category created_by approved_by reviews]
|
|
46
|
+
%w[product_category created_by approved_by reviews interest_configurations]
|
|
46
47
|
end
|
|
47
48
|
|
|
48
49
|
before_validation :strip_whitespace
|
|
@@ -9,7 +9,7 @@ module Dscf::Banking
|
|
|
9
9
|
belongs_to :assigned_branch_manager, optional: true
|
|
10
10
|
has_one :account, serializer: AccountSerializer, if: :has_account?
|
|
11
11
|
has_many :reviews, serializer: Dscf::Core::ReviewSerializer
|
|
12
|
-
has_many :documents, serializer:
|
|
12
|
+
has_many :documents, serializer: DocumentSerializer
|
|
13
13
|
|
|
14
14
|
def has_account?
|
|
15
15
|
object.has_account?
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
module Dscf::Banking
|
|
2
2
|
class DocumentSerializer < ActiveModel::Serializer
|
|
3
|
-
attributes :id, :document_type, :is_verified, :verified_at, :metadata, :file_urls, :created_at, :updated_at
|
|
3
|
+
attributes :id, :document_type, :is_verified, :verified_at, :metadata, :file_urls, :created_at, :updated_at,
|
|
4
|
+
:documentable_type, :documentable_id, :documentable
|
|
5
|
+
|
|
6
|
+
def documentable
|
|
7
|
+
{
|
|
8
|
+
type: object.documentable_type,
|
|
9
|
+
id: object.documentable_id
|
|
10
|
+
}
|
|
11
|
+
end
|
|
4
12
|
|
|
5
13
|
def file_urls
|
|
6
14
|
return [] unless object.files.attached?
|
data/config/routes.rb
CHANGED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
puts "Seeding Interest Rate Types..."
|
|
2
|
+
|
|
3
|
+
interest_rate_types = [
|
|
4
|
+
{
|
|
5
|
+
code: "INTEREST_FREE",
|
|
6
|
+
name: "Interest Free",
|
|
7
|
+
description: "No interest is earned on the account balance. Suitable for transactional accounts or specific product offerings."
|
|
8
|
+
},
|
|
9
|
+
{
|
|
10
|
+
code: "FIXED",
|
|
11
|
+
name: "Fixed Rate",
|
|
12
|
+
description: "A single flat interest rate applied to the entire account balance. Rate remains constant unless manually updated."
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
code: "VARIABLE",
|
|
16
|
+
name: "Variable Rate",
|
|
17
|
+
description: "Tiered interest rates based on balance ranges. Different rates apply to different balance brackets, encouraging higher deposits."
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
code: "PROMOTIONAL",
|
|
21
|
+
name: "Promotional Rate",
|
|
22
|
+
description: "Temporary special interest rate applied for a specific period. Automatically reverts to the default rate after the promotional period ends."
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
interest_rate_types.each do |rate_type_data|
|
|
27
|
+
rate_type = Dscf::Banking::InterestRateType.find_or_initialize_by(code: rate_type_data[:code])
|
|
28
|
+
rate_type.assign_attributes(rate_type_data)
|
|
29
|
+
|
|
30
|
+
if rate_type.save
|
|
31
|
+
puts " ✓ Created/Updated: #{rate_type.name} (#{rate_type.code})"
|
|
32
|
+
else
|
|
33
|
+
puts " ✗ Failed to create #{rate_type_data[:name]}: #{rate_type.errors.full_messages.join(', ')}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
puts "Interest Rate Types seeding completed!"
|
|
38
|
+
puts "Total Interest Rate Types: #{Dscf::Banking::InterestRateType.count}"
|
data/db/seeds.rb
CHANGED
|
@@ -48,6 +48,43 @@ user7 = User.create(name: "Fikirte Alemayehu", email: "fikirte@example.com", pas
|
|
|
48
48
|
user7.roles << branch_manager_role
|
|
49
49
|
puts "Created user: #{user7.name}"
|
|
50
50
|
|
|
51
|
+
# Seed Interest Rate Types
|
|
52
|
+
puts "Seeding Interest Rate Types..."
|
|
53
|
+
interest_rate_types = [
|
|
54
|
+
{
|
|
55
|
+
code: "INTEREST_FREE",
|
|
56
|
+
name: "Interest Free",
|
|
57
|
+
description: "No interest is earned on the account balance. Suitable for transactional accounts or specific product offerings."
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
code: "FIXED",
|
|
61
|
+
name: "Fixed Rate",
|
|
62
|
+
description: "A single flat interest rate applied to the entire account balance. Rate remains constant unless manually updated."
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
code: "VARIABLE",
|
|
66
|
+
name: "Variable Rate",
|
|
67
|
+
description: "Tiered interest rates based on balance ranges. Different rates apply to different balance brackets, encouraging higher deposits."
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
code: "PROMOTIONAL",
|
|
71
|
+
name: "Promotional Rate",
|
|
72
|
+
description: "Temporary special interest rate applied for a specific period. Automatically reverts to the default rate after the promotional period ends."
|
|
73
|
+
}
|
|
74
|
+
]
|
|
75
|
+
|
|
76
|
+
interest_rate_types.each do |rate_type_data|
|
|
77
|
+
rate_type = Dscf::Banking::InterestRateType.find_or_initialize_by(code: rate_type_data[:code])
|
|
78
|
+
rate_type.assign_attributes(rate_type_data)
|
|
79
|
+
|
|
80
|
+
if rate_type.save
|
|
81
|
+
puts " ✓ Created/Updated: #{rate_type.name} (#{rate_type.code})"
|
|
82
|
+
else
|
|
83
|
+
puts " ✗ Failed to create #{rate_type_data[:name]}: #{rate_type.errors.full_messages.join(', ')}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
puts "Interest Rate Types seeding completed!"
|
|
87
|
+
|
|
51
88
|
# Seed transaction types
|
|
52
89
|
puts "Seeding transaction types..."
|
|
53
90
|
deposit_type = Dscf::Banking::TransactionType.find_or_create_by(code: "DEPOSIT") do |tt|
|
data/lib/dscf/banking/version.rb
CHANGED
|
@@ -3,34 +3,62 @@ FactoryBot.define do
|
|
|
3
3
|
association :virtual_account_product, factory: :virtual_account_product
|
|
4
4
|
association :interest_rate_type, factory: :interest_rate_type
|
|
5
5
|
|
|
6
|
-
annual_interest_rate {
|
|
7
|
-
income_tax_rate {
|
|
8
|
-
minimum_balance_for_interest {
|
|
9
|
-
calculation_method {
|
|
10
|
-
compounding_period {
|
|
11
|
-
interest_basis {
|
|
12
|
-
accrual_frequency {
|
|
13
|
-
rounding_rule {
|
|
14
|
-
calculation_timing {
|
|
6
|
+
annual_interest_rate { 0.05 }
|
|
7
|
+
income_tax_rate { 0.05 }
|
|
8
|
+
minimum_balance_for_interest { 0.0 }
|
|
9
|
+
calculation_method { :simple }
|
|
10
|
+
compounding_period { nil }
|
|
11
|
+
interest_basis { :actual_365 }
|
|
12
|
+
accrual_frequency { :monthly }
|
|
13
|
+
rounding_rule { :nearest_cent }
|
|
14
|
+
calculation_timing { Time.current }
|
|
15
15
|
is_active { true }
|
|
16
16
|
|
|
17
17
|
trait :inactive do
|
|
18
18
|
is_active { false }
|
|
19
19
|
end
|
|
20
20
|
|
|
21
|
+
trait :interest_free do
|
|
22
|
+
association :interest_rate_type, factory: [ :interest_rate_type, :interest_free ]
|
|
23
|
+
annual_interest_rate { 0 }
|
|
24
|
+
calculation_method { nil }
|
|
25
|
+
compounding_period { nil }
|
|
26
|
+
interest_basis { nil }
|
|
27
|
+
accrual_frequency { nil }
|
|
28
|
+
rounding_rule { nil }
|
|
29
|
+
calculation_timing { nil }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
trait :fixed_rate do
|
|
33
|
+
association :interest_rate_type, factory: [ :interest_rate_type, :fixed_rate ]
|
|
34
|
+
annual_interest_rate { 0.05 }
|
|
35
|
+
calculation_method { :simple }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
trait :variable_rate do
|
|
39
|
+
association :interest_rate_type, factory: [ :interest_rate_type, :variable_rate ]
|
|
40
|
+
annual_interest_rate { 0.03 }
|
|
41
|
+
|
|
42
|
+
after(:create) do |config|
|
|
43
|
+
create_list(:interest_rate_tier, 3, interest_config: config)
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
21
47
|
trait :promotional do
|
|
22
|
-
|
|
23
|
-
|
|
48
|
+
association :interest_rate_type, factory: [ :interest_rate_type, :promotional_rate ]
|
|
49
|
+
promotional_start_date { 30.days.ago.to_date }
|
|
50
|
+
promotional_end_date { 30.days.from_now.to_date }
|
|
51
|
+
annual_interest_rate { 0.08 }
|
|
24
52
|
end
|
|
25
53
|
|
|
26
54
|
trait :simple_interest do
|
|
27
|
-
calculation_method {
|
|
55
|
+
calculation_method { :simple }
|
|
28
56
|
compounding_period { nil }
|
|
29
57
|
end
|
|
30
58
|
|
|
31
59
|
trait :compound_interest do
|
|
32
|
-
calculation_method {
|
|
33
|
-
compounding_period {
|
|
60
|
+
calculation_method { :compound }
|
|
61
|
+
compounding_period { :monthly }
|
|
34
62
|
end
|
|
35
63
|
end
|
|
36
64
|
end
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
FactoryBot.define do
|
|
2
2
|
factory :interest_rate_type, class: "Dscf::Banking::InterestRateType" do
|
|
3
|
-
sequence(:code) { |n| "
|
|
3
|
+
sequence(:code) { |n| "RATE_#{n.to_s.rjust(3, '0')}" }
|
|
4
4
|
sequence(:name) { |n| "Interest Rate Type #{n}" }
|
|
5
5
|
description { Faker::Lorem.paragraph(sentence_count: 2) }
|
|
6
6
|
|
metadata
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: dscf-banking
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.3.
|
|
4
|
+
version: 0.3.6
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Eyosiyas Mekbib
|
|
8
8
|
bindir: bin
|
|
9
9
|
cert_chain: []
|
|
10
|
-
date: 2025-10-
|
|
10
|
+
date: 2025-10-25 00:00:00.000000000 Z
|
|
11
11
|
dependencies:
|
|
12
12
|
- !ruby/object:Gem::Dependency
|
|
13
13
|
name: rails
|
|
@@ -482,8 +482,6 @@ files:
|
|
|
482
482
|
- app/services/dscf/banking/deposit_service.rb
|
|
483
483
|
- app/services/dscf/banking/transfer_service.rb
|
|
484
484
|
- app/services/dscf/banking/withdrawal_service.rb
|
|
485
|
-
- config/initializers/review_callbacks.rb
|
|
486
|
-
- config/initializers/review_extensions.rb
|
|
487
485
|
- config/locales/en.yml
|
|
488
486
|
- config/routes.rb
|
|
489
487
|
- db/migrate/20250830211002_create_dscf_banking_product_categories.rb
|
|
@@ -502,6 +500,7 @@ files:
|
|
|
502
500
|
- db/migrate/20250919182831_create_dscf_banking_transaction_types.rb
|
|
503
501
|
- db/migrate/20250919184220_create_dscf_banking_transactions.rb
|
|
504
502
|
- db/seeds.rb
|
|
503
|
+
- db/seeds/interest_rate_types.rb
|
|
505
504
|
- lib/dscf/banking.rb
|
|
506
505
|
- lib/dscf/banking/engine.rb
|
|
507
506
|
- lib/dscf/banking/version.rb
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
Rails.application.config.to_prepare do
|
|
2
|
-
ActiveSupport::Notifications.subscribe("review.created") do |name, start, finish, id, payload|
|
|
3
|
-
reviewable = payload[:reviewable]
|
|
4
|
-
|
|
5
|
-
if reviewable.is_a?(Dscf::Banking::Application)
|
|
6
|
-
reviewable.reload
|
|
7
|
-
review_status = reviewable.review_status
|
|
8
|
-
|
|
9
|
-
status_map = {
|
|
10
|
-
"submitted" => "submitted",
|
|
11
|
-
"under_review" => "under_review",
|
|
12
|
-
"approved" => "approved",
|
|
13
|
-
"rejected" => "rejected",
|
|
14
|
-
"request_modification" => "request_modification"
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
mapped_status = status_map[review_status]
|
|
18
|
-
if mapped_status && reviewable.status != mapped_status
|
|
19
|
-
reviewable.update!(status: mapped_status)
|
|
20
|
-
end
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
module Dscf::Banking
|
|
2
|
-
module ReviewExtensions
|
|
3
|
-
extend ActiveSupport::Concern
|
|
4
|
-
|
|
5
|
-
included do
|
|
6
|
-
after_create :sync_application_status, if: -> { reviewable.is_a?(Dscf::Banking::Application) }
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
private
|
|
10
|
-
|
|
11
|
-
def sync_application_status
|
|
12
|
-
return unless reviewable.is_a?(Dscf::Banking::Application)
|
|
13
|
-
|
|
14
|
-
application = reviewable
|
|
15
|
-
review_status = application.review_status
|
|
16
|
-
|
|
17
|
-
status_map = {
|
|
18
|
-
"submitted" => "submitted",
|
|
19
|
-
"under_review" => "under_review",
|
|
20
|
-
"approved" => "approved",
|
|
21
|
-
"rejected" => "rejected",
|
|
22
|
-
"request_modification" => "request_modification"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
mapped_status = status_map[review_status]
|
|
26
|
-
if mapped_status && application.status != mapped_status
|
|
27
|
-
application.update!(status: mapped_status)
|
|
28
|
-
end
|
|
29
|
-
end
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
|
|
33
|
-
# Extend the Dscf::Core::Review model with our extensions
|
|
34
|
-
Rails.application.config.to_prepare do
|
|
35
|
-
Dscf::Core::Review.include(Dscf::Banking::ReviewExtensions) if defined?(Dscf::Core::Review)
|
|
36
|
-
end
|