bscf-core 0.4.97 → 0.4.98

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ca5f351bc1c4d0ef3f5682b4b01524be4b79366267c313badd649185ba197118
4
- data.tar.gz: 257e06eed096459bc7f7479abfe00620692fe7e534f1b327ecab8056b75d07ce
3
+ metadata.gz: e21ece92c538dd2de05e92798fdbcdb484515fb2b4d51579a49a7743cd845908
4
+ data.tar.gz: 323646d95a5005ddf6ba62a24b5b3d0184ab70d2e570cb969104be978e1f378a
5
5
  SHA512:
6
- metadata.gz: dc857844cd4f14fdc8ac0d96be4b9eb06ac2513aef4b649c011dc4595387a4d63047fa4066ac85ec5cfbc9730264cb4709b4caf5a58d9c9cdff6b744c5ba0447
7
- data.tar.gz: fc8944ef60c284da3bf9489a8f4dec0afdb75d732f167cc7831f9a64aeb76da8c8f95cb8dd1f9916da8841796f7223c43ed13aeb8c4a796124470c163d44025d
6
+ metadata.gz: 19e9f2eb4f89d4849965264b20c08cff31570de7f4c67bd595900475a550986a44b8d803f93e3b82b44db1740ed4ad1c66e3c4f61c10b4e8d8dcf78a82264c88
7
+ data.tar.gz: 72ab3727d5ef514d63f5f8219e7635ec0c5446c6cc2993bd67e0f03084602c9bee6fdf67fe195f612aa5ca37621936efa43ee1e511d2971ba59e80fa9a676555
@@ -0,0 +1,28 @@
1
+ module Bscf::Core
2
+ class Loan < ApplicationRecord
3
+ belongs_to :virtual_account, class_name: "Bscf::Core::VirtualAccount"
4
+ belongs_to :disbursement_transaction, class_name: "Bscf::Core::VirtualAccountTransaction"
5
+
6
+ enum :status, { disbursed: 0, late: 1, paid: 2 }
7
+
8
+ validates :principal_amount, :interest_amount, :unpaid_balance, presence: true, numericality: { greater_than_or_equal_to: 0 }
9
+ validates :status, :due_date, presence: true
10
+ validates :status, inclusion: { in: statuses.keys }
11
+
12
+ validate :paid_at_requires_zero_balance_and_paid_status
13
+ validate :paid_status_requires_zero_balance
14
+
15
+ def paid_at_requires_zero_balance_and_paid_status
16
+ if paid_at.present?
17
+ errors.add(:paid_at, "can only be set if loan is marked as paid") if status != "paid"
18
+ errors.add(:paid_at, "can only be set if unpaid balance is zero") if unpaid_balance.to_f > 0
19
+ end
20
+ end
21
+
22
+ def paid_status_requires_zero_balance
23
+ if status == "paid" && unpaid_balance.to_f > 0
24
+ errors.add(:status, "can only be set to paid if unpaid balance is zero")
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,45 @@
1
+ module Bscf::Core
2
+ class LoanRepayment < ApplicationRecord
3
+ belongs_to :loan, class_name: "Bscf::Core::Loan"
4
+ belongs_to :repayment_transaction, class_name: "Bscf::Core::VirtualAccountTransaction"
5
+
6
+ validates :amount, presence: true, numericality: { greater_than: 0 }
7
+ validates :payment_date, presence: true
8
+ validate :amount_cannot_exceed_unpaid_balance
9
+ validate :loan_cannot_be_already_paid
10
+
11
+ after_create :apply_repayment_to_loan
12
+
13
+ private
14
+
15
+ def apply_repayment_to_loan
16
+ loan.with_lock do
17
+ loan.unpaid_balance -= amount
18
+ loan.unpaid_balance = 0 if loan.unpaid_balance.negative?
19
+
20
+ if loan.unpaid_balance.zero?
21
+ loan.status = "paid"
22
+ loan.paid_at = payment_date
23
+ end
24
+
25
+ loan.save!
26
+ end
27
+ end
28
+
29
+ def amount_cannot_exceed_unpaid_balance
30
+ return if loan.blank? || amount.blank?
31
+
32
+ if amount > loan.unpaid_balance
33
+ errors.add(:amount, "cannot be greater than the loan's unpaid balance")
34
+ end
35
+ end
36
+
37
+ def loan_cannot_be_already_paid
38
+ return if loan.blank?
39
+
40
+ if loan.paid?
41
+ errors.add(:loan, "is already marked as paid")
42
+ end
43
+ end
44
+ end
45
+ end
@@ -76,9 +76,33 @@ module Bscf
76
76
  def generate_account_number
77
77
  return if account_number.present?
78
78
 
79
- last_seq = self.class.maximum(:account_number).to_s[-6..-1].to_i
79
+ last_account = self.class.maximum(:account_number)
80
+ last_seq = last_account ? last_account[-6..-1].to_i : 0
80
81
  seq = (last_seq + 1).to_s.rjust(6, "0")
81
- self.account_number = "#{branch_code}#{product_scheme}#{voucher_type}#{seq}"
82
+
83
+ branch_part = branch_code.to_s.rjust(3, "0")[0..2]
84
+ product_part = map_product_scheme_to_digit(product_scheme)
85
+ voucher_part = map_voucher_type_to_digit(voucher_type)
86
+
87
+ self.account_number = "#{branch_part}#{product_part}#{voucher_part}#{seq}"
88
+ end
89
+
90
+ def map_product_scheme_to_digit(scheme)
91
+ case scheme
92
+ when "SAVINGS" then "1"
93
+ when "CURRENT" then "2"
94
+ when "LOAN" then "3"
95
+ else "0"
96
+ end
97
+ end
98
+
99
+ def map_voucher_type_to_digit(type)
100
+ case type
101
+ when "REGULAR" then "1"
102
+ when "SPECIAL" then "2"
103
+ when "TEMPORARY" then "3"
104
+ else "0"
105
+ end
82
106
  end
83
107
 
84
108
  def transfer_to!(to_account, amount)
@@ -0,0 +1,16 @@
1
+ class CreateBscfCoreLoans < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :bscf_core_loans do |t|
4
+ t.references :virtual_account, null: false, foreign_key: { to_table: :bscf_core_virtual_accounts }
5
+ t.references :disbursement_transaction, null: false, foreign_key: { to_table: :bscf_core_virtual_account_transactions }
6
+ t.float :principal_amount, null: false, default: 0.0
7
+ t.float :interest_amount, null: false, default: 0.0
8
+ t.float :unpaid_balance, null: false, default: 0.0
9
+ t.integer :status, null: false, default: 0
10
+ t.date :due_date, null: false, default: Date.today + 15.days
11
+ t.date :paid_at, null: true
12
+
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,12 @@
1
+ class CreateBscfCoreLoanRepayments < ActiveRecord::Migration[8.0]
2
+ def change
3
+ create_table :bscf_core_loan_repayments do |t|
4
+ t.references :loan, null: false, foreign_key: { to_table: :bscf_core_loans }
5
+ t.references :repayment_transaction, null: false, foreign_key: { to_table: :bscf_core_virtual_account_transactions }
6
+ t.float :amount, null: false
7
+ t.date :payment_date, null: false, default: Date.today
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,6 @@
1
+ class NullableOccupationSourceOfFunds < ActiveRecord::Migration[8.0]
2
+ def change
3
+ change_column_null :bscf_core_user_profiles, :occupation, true
4
+ change_column_null :bscf_core_user_profiles, :source_of_funds, true
5
+ end
6
+ end
@@ -1,5 +1,5 @@
1
1
  module Bscf
2
2
  module Core
3
- VERSION = "0.4.97"
3
+ VERSION = "0.4.98"
4
4
  end
5
5
  end
@@ -0,0 +1,9 @@
1
+ FactoryBot.define do
2
+ factory :loan_repayment, class: 'Bscf::Core::LoanRepayment' do
3
+ association :loan, factory: :loan
4
+ association :repayment_transaction, factory: :virtual_account_transaction
5
+
6
+ amount { 1.5 }
7
+ payment_date { Date.today }
8
+ end
9
+ end
@@ -0,0 +1,12 @@
1
+ FactoryBot.define do
2
+ factory :loan, class: 'Bscf::Core::Loan' do
3
+ association :virtual_account
4
+ association :disbursement_transaction, factory: :virtual_account_transaction
5
+
6
+ principal_amount { 200.5 }
7
+ interest_amount { 10.5 }
8
+ unpaid_balance { 210.5 }
9
+ status { 0 }
10
+ due_date { Date.today + 15.days }
11
+ end
12
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bscf-core
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.97
4
+ version: 0.4.98
5
5
  platform: ruby
6
6
  authors:
7
7
  - Asrat
@@ -296,6 +296,8 @@ files:
296
296
  - app/models/bscf/core/delivery_order_item.rb
297
297
  - app/models/bscf/core/invoice.rb
298
298
  - app/models/bscf/core/invoice_item.rb
299
+ - app/models/bscf/core/loan.rb
300
+ - app/models/bscf/core/loan_repayment.rb
299
301
  - app/models/bscf/core/marketplace_listing.rb
300
302
  - app/models/bscf/core/order.rb
301
303
  - app/models/bscf/core/order_item.rb
@@ -364,6 +366,9 @@ files:
364
366
  - db/migrate/20250624062746_rename_business_id_to_user_id_in_bscf_core_business_documents.rb
365
367
  - db/migrate/20250624122210_add_document_type_to_business_documents.rb
366
368
  - db/migrate/20250714201747_add_optimized_route_to_delivery_order.rb
369
+ - db/migrate/20250720123239_create_bscf_core_loans.rb
370
+ - db/migrate/20250720131241_create_bscf_core_loan_repayments.rb
371
+ - db/migrate/20250720184059_nullable_occupation_source_of_funds.rb
367
372
  - lib/bscf/core.rb
368
373
  - lib/bscf/core/engine.rb
369
374
  - lib/bscf/core/version.rb
@@ -379,6 +384,8 @@ files:
379
384
  - spec/factories/bscf/core/delivery_orders.rb
380
385
  - spec/factories/bscf/core/invoice_items.rb
381
386
  - spec/factories/bscf/core/invoices.rb
387
+ - spec/factories/bscf/core/loan_repayments.rb
388
+ - spec/factories/bscf/core/loans.rb
382
389
  - spec/factories/bscf/core/marketplace_listings.rb
383
390
  - spec/factories/bscf/core/order_items.rb
384
391
  - spec/factories/bscf/core/orders.rb