bscf-core 0.3.99 → 0.4.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 +4 -4
- data/app/models/bscf/core/delivery_order.rb +1 -2
- data/app/models/bscf/core/delivery_order_item.rb +2 -1
- data/app/models/bscf/core/virtual_account_transaction.rb +53 -92
- data/app/services/bscf/core/transaction_service.rb +185 -0
- data/db/migrate/20250609180530_add_accounting_fields_to_virtual_account_transactions.rb +20 -0
- data/db/migrate/20250610120351_update_delivery_tables.rb +15 -0
- data/lib/bscf/core/version.rb +1 -1
- data/spec/factories/bscf/core/delivery_order_items.rb +2 -1
- data/spec/factories/bscf/core/delivery_orders.rb +3 -26
- data/spec/factories/bscf/core/virtual_account_transactions.rb +36 -21
- metadata +4 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: edbe11919e64a7d36377d85759973899a2407eb6417e7d070eb78ee9c2062bf6
|
4
|
+
data.tar.gz: 37375af1d5dfdf4f4703540d8e3c6d7c2d7496026421b8ae8e4c5c412cb23fd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2de3ef4fe24d2536031b4f527a5d7ed90f958f736d255157a9e17836c50e8587d00fa06ed2128e14b35102af7b81a9ba74bcc6994047f755e728e9e6cf1d1b9c
|
7
|
+
data.tar.gz: 80a64d9c90c60baaf9966dee56261e41675a9fcf8e3091059b994e8fa2af36bc607caf192a22504fa74cff29b7d7fca4bc8e6061918c0db991e3556f739c590b
|
@@ -9,8 +9,7 @@ module Bscf::Core
|
|
9
9
|
has_many :order_items, through: :delivery_order_items
|
10
10
|
has_many :products, through: :delivery_order_items
|
11
11
|
|
12
|
-
validates :
|
13
|
-
:status, :estimated_delivery_time, presence: true
|
12
|
+
validates :driver_phone, :status, :estimated_delivery_time, presence: true
|
14
13
|
validate :end_time_after_start_time, if: -> { delivery_start_time.present? && delivery_end_time.present? }
|
15
14
|
|
16
15
|
before_save :update_delivery_times
|
@@ -2,7 +2,8 @@ module Bscf::Core
|
|
2
2
|
class DeliveryOrderItem < ApplicationRecord
|
3
3
|
belongs_to :delivery_order
|
4
4
|
belongs_to :order_item
|
5
|
-
belongs_to :
|
5
|
+
belongs_to :pickup_address, class_name: "Bscf::Core::Address", optional: true
|
6
|
+
belongs_to :dropoff_address, class_name: "Bscf::Core::Address", optional: true
|
6
7
|
|
7
8
|
validates :quantity, :status, presence: true
|
8
9
|
validate :quantity_not_exceeding_order_item
|
@@ -1,20 +1,27 @@
|
|
1
1
|
module Bscf
|
2
2
|
module Core
|
3
3
|
class VirtualAccountTransaction < ApplicationRecord
|
4
|
-
belongs_to :
|
5
|
-
belongs_to :
|
4
|
+
belongs_to :account, class_name: "Bscf::Core::VirtualAccount"
|
5
|
+
belongs_to :paired_transaction, class_name: "Bscf::Core::VirtualAccountTransaction", optional: true
|
6
6
|
|
7
|
-
validates :
|
8
|
-
validates :to_account_id, presence: true, if: :requires_to_account?
|
7
|
+
validates :account_id, presence: true
|
9
8
|
validates :amount, presence: true, numericality: { greater_than: 0 }
|
10
9
|
validates :transaction_type, presence: true
|
10
|
+
validates :entry_type, presence: true
|
11
11
|
validates :status, presence: true
|
12
12
|
validates :reference_number, presence: true, uniqueness: true
|
13
13
|
|
14
14
|
enum :transaction_type, {
|
15
15
|
transfer: 0,
|
16
16
|
deposit: 1,
|
17
|
-
withdrawal: 2
|
17
|
+
withdrawal: 2,
|
18
|
+
fee: 3,
|
19
|
+
adjustment: 4
|
20
|
+
}
|
21
|
+
|
22
|
+
enum :entry_type, {
|
23
|
+
debit: 0,
|
24
|
+
credit: 1
|
18
25
|
}
|
19
26
|
|
20
27
|
enum :status, {
|
@@ -27,122 +34,76 @@ module Bscf
|
|
27
34
|
before_validation :generate_reference_number, on: :create
|
28
35
|
validate :validate_transaction, on: :create
|
29
36
|
|
37
|
+
scope :debits, -> { where(entry_type: :debit) }
|
38
|
+
scope :credits, -> { where(entry_type: :credit) }
|
39
|
+
scope :for_account, ->(account_id) { where(account_id: account_id) }
|
40
|
+
scope :by_type, ->(type) { where(transaction_type: type) }
|
41
|
+
scope :by_status, ->(status) { where(status: status) }
|
42
|
+
|
30
43
|
def process!
|
31
44
|
return false unless pending?
|
32
45
|
|
33
46
|
ActiveRecord::Base.transaction do
|
34
|
-
|
47
|
+
update_account_balance
|
48
|
+
|
35
49
|
update!(status: :completed)
|
50
|
+
paired_transaction&.update!(status: :completed)
|
36
51
|
end
|
37
52
|
true
|
38
53
|
rescue StandardError => e
|
39
54
|
update(status: :failed)
|
55
|
+
paired_transaction&.update(status: :failed)
|
56
|
+
Rails.logger.error("Transaction processing failed: #{e.message}")
|
40
57
|
false
|
41
58
|
end
|
42
59
|
|
43
60
|
def cancel!
|
44
61
|
return false unless pending?
|
45
|
-
update(status: :cancelled)
|
46
|
-
end
|
47
|
-
|
48
|
-
private
|
49
|
-
|
50
|
-
def requires_from_account?
|
51
|
-
return false if transaction_type.nil?
|
52
|
-
%w[transfer withdrawal].include?(transaction_type)
|
53
|
-
end
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
def validate_transaction
|
61
|
-
case transaction_type.to_sym
|
62
|
-
when :transfer
|
63
|
-
validate_transfer
|
64
|
-
when :withdrawal
|
65
|
-
validate_withdrawal
|
66
|
-
when :deposit
|
67
|
-
validate_deposit
|
63
|
+
ActiveRecord::Base.transaction do
|
64
|
+
update(status: :cancelled)
|
65
|
+
paired_transaction&.update(status: :cancelled)
|
68
66
|
end
|
69
|
-
|
70
|
-
validate_account_requirements
|
71
|
-
end
|
72
|
-
|
73
|
-
def validate_transfer
|
74
|
-
return unless from_account && to_account
|
75
|
-
|
76
|
-
errors.add(:from_account, "must be active") unless from_account.active?
|
77
|
-
errors.add(:to_account, "must be active") unless to_account.active?
|
78
|
-
errors.add(:from_account, "insufficient balance") if from_account.balance.to_d < amount.to_d
|
67
|
+
true
|
79
68
|
end
|
80
69
|
|
81
70
|
private
|
82
71
|
|
83
|
-
def
|
84
|
-
|
85
|
-
when :transfer
|
86
|
-
return if from_account_id.present? && to_account_id.present?
|
87
|
-
errors.add(:base, "Both accounts are required for transfer")
|
88
|
-
when :withdrawal
|
89
|
-
return if from_account_id.present?
|
90
|
-
errors.add(:base, "Source account is required for withdrawal")
|
91
|
-
errors.add(:to_account_id, "must be blank for withdrawal")
|
92
|
-
when :deposit
|
93
|
-
return if to_account_id.present?
|
94
|
-
errors.add(:base, "Destination account is required for deposit")
|
95
|
-
errors.add(:from_account_id, "must be blank for deposit")
|
96
|
-
end
|
97
|
-
end
|
98
|
-
|
99
|
-
def validate_withdrawal
|
100
|
-
return unless from_account
|
101
|
-
errors.add(:from_account, "must be active") unless from_account.active?
|
102
|
-
errors.add(:from_account, "insufficient balance") if from_account.balance.to_d < amount.to_d
|
103
|
-
end
|
72
|
+
def validate_transaction
|
73
|
+
errors.add(:account, "must be active") unless account.active?
|
104
74
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
end
|
75
|
+
if debit? && !adjustment? && account.balance.to_d < amount.to_d
|
76
|
+
errors.add(:account, "insufficient balance")
|
77
|
+
end
|
109
78
|
|
110
|
-
|
111
|
-
|
112
|
-
when :transfer
|
113
|
-
process_transfer
|
114
|
-
when :withdrawal
|
115
|
-
process_withdrawal
|
116
|
-
when :deposit
|
117
|
-
process_deposit
|
79
|
+
if transfer? && paired_transaction.blank?
|
80
|
+
errors.add(:paired_transaction, "must be present for transfers")
|
118
81
|
end
|
119
82
|
end
|
120
83
|
|
121
|
-
def
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
from_account.update!(balance: new_from_balance)
|
129
|
-
to_account.update!(balance: new_to_balance)
|
130
|
-
end
|
84
|
+
def update_account_balance
|
85
|
+
account.with_lock do
|
86
|
+
new_balance = if debit?
|
87
|
+
(account.balance - amount).round(2)
|
88
|
+
else
|
89
|
+
(account.balance + amount).round(2)
|
131
90
|
end
|
132
|
-
end
|
133
|
-
end
|
134
91
|
|
135
|
-
|
136
|
-
|
137
|
-
new_balance = (from_account.balance - amount).round(2)
|
138
|
-
from_account.update!(balance: new_balance)
|
92
|
+
account.update!(balance: new_balance)
|
93
|
+
update!(running_balance: new_balance)
|
139
94
|
end
|
140
|
-
end
|
141
95
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
96
|
+
if paired_transaction.present?
|
97
|
+
paired_transaction.account.with_lock do
|
98
|
+
new_balance = if paired_transaction.debit?
|
99
|
+
(paired_transaction.account.balance - paired_transaction.amount).round(2)
|
100
|
+
else
|
101
|
+
(paired_transaction.account.balance + paired_transaction.amount).round(2)
|
102
|
+
end
|
103
|
+
|
104
|
+
paired_transaction.account.update!(balance: new_balance)
|
105
|
+
paired_transaction.update!(running_balance: new_balance)
|
106
|
+
end
|
146
107
|
end
|
147
108
|
end
|
148
109
|
|
@@ -0,0 +1,185 @@
|
|
1
|
+
module Bscf
|
2
|
+
module Core
|
3
|
+
class TransactionService
|
4
|
+
# Create a transfer between two accounts
|
5
|
+
# @param from_account_id [Integer] Source account ID
|
6
|
+
# @param to_account_id [Integer] Destination account ID
|
7
|
+
# @param amount [Decimal] Amount to transfer
|
8
|
+
# @param description [String] Optional description
|
9
|
+
# @return [Array<VirtualAccountTransaction>] The created transaction pair
|
10
|
+
def self.create_transfer(from_account_id:, to_account_id:, amount:, description: nil)
|
11
|
+
create_paired_transaction(
|
12
|
+
from_account_id: from_account_id,
|
13
|
+
to_account_id: to_account_id,
|
14
|
+
amount: amount,
|
15
|
+
transaction_type: :transfer,
|
16
|
+
description: description
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Create a deposit to an account
|
21
|
+
# @param to_account_id [Integer] Destination account ID
|
22
|
+
# @param amount [Decimal] Amount to deposit
|
23
|
+
# @param description [String] Optional description
|
24
|
+
# @return [Array<VirtualAccountTransaction>] The created transaction pair
|
25
|
+
def self.create_deposit(to_account_id:, amount:, description: nil)
|
26
|
+
system_account = find_or_create_system_account("DEPOSIT_ACCOUNT")
|
27
|
+
|
28
|
+
# Ensure system account has sufficient balance for deposits
|
29
|
+
ensure_system_account_balance(system_account, amount)
|
30
|
+
|
31
|
+
create_paired_transaction(
|
32
|
+
from_account_id: system_account.id,
|
33
|
+
to_account_id: to_account_id,
|
34
|
+
amount: amount,
|
35
|
+
transaction_type: :deposit,
|
36
|
+
description: description
|
37
|
+
)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Create a withdrawal from an account
|
41
|
+
# @param from_account_id [Integer] Source account ID
|
42
|
+
# @param amount [Decimal] Amount to withdraw
|
43
|
+
# @param description [String] Optional description
|
44
|
+
# @return [Array<VirtualAccountTransaction>] The created transaction pair
|
45
|
+
def self.create_withdrawal(from_account_id:, amount:, description: nil)
|
46
|
+
system_account = find_or_create_system_account("WITHDRAWAL_ACCOUNT")
|
47
|
+
|
48
|
+
create_paired_transaction(
|
49
|
+
from_account_id: from_account_id,
|
50
|
+
to_account_id: system_account.id,
|
51
|
+
amount: amount,
|
52
|
+
transaction_type: :withdrawal,
|
53
|
+
description: description
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Create a fee transaction
|
58
|
+
# @param from_account_id [Integer] Account to charge
|
59
|
+
# @param amount [Decimal] Fee amount
|
60
|
+
# @param description [String] Optional description
|
61
|
+
# @return [Array<VirtualAccountTransaction>] The created transaction pair
|
62
|
+
def self.create_fee(from_account_id:, amount:, description: nil)
|
63
|
+
fee_account = find_or_create_system_account("FEE_ACCOUNT")
|
64
|
+
|
65
|
+
create_paired_transaction(
|
66
|
+
from_account_id: from_account_id,
|
67
|
+
to_account_id: fee_account.id,
|
68
|
+
amount: amount,
|
69
|
+
transaction_type: :fee,
|
70
|
+
description: description
|
71
|
+
)
|
72
|
+
end
|
73
|
+
|
74
|
+
# Create an adjustment to an account
|
75
|
+
# @param account_id [Integer] Account to adjust
|
76
|
+
# @param amount [Decimal] Adjustment amount
|
77
|
+
# @param is_debit [Boolean] Whether this is a debit (true) or credit (false)
|
78
|
+
# @param description [String] Optional description
|
79
|
+
# @return [VirtualAccountTransaction] The created adjustment transaction
|
80
|
+
def self.create_adjustment(account_id:, amount:, is_debit: true, description: nil)
|
81
|
+
reference_number = generate_reference_number
|
82
|
+
|
83
|
+
VirtualAccountTransaction.create!(
|
84
|
+
transaction_type: :adjustment,
|
85
|
+
entry_type: is_debit ? :debit : :credit,
|
86
|
+
account_id: account_id,
|
87
|
+
amount: amount,
|
88
|
+
reference_number: reference_number,
|
89
|
+
description: description,
|
90
|
+
status: :pending
|
91
|
+
)
|
92
|
+
end
|
93
|
+
|
94
|
+
# Process a transaction
|
95
|
+
# @param transaction [VirtualAccountTransaction] Transaction to process
|
96
|
+
# @return [Boolean] Success or failure
|
97
|
+
def self.process(transaction)
|
98
|
+
transaction.process!
|
99
|
+
end
|
100
|
+
|
101
|
+
# Cancel a transaction
|
102
|
+
# @param transaction [VirtualAccountTransaction] Transaction to cancel
|
103
|
+
# @return [Boolean] Success or failure
|
104
|
+
def self.cancel(transaction)
|
105
|
+
transaction.cancel!
|
106
|
+
end
|
107
|
+
|
108
|
+
private
|
109
|
+
|
110
|
+
def self.create_paired_transaction(from_account_id:, to_account_id:, amount:, transaction_type:, description: nil)
|
111
|
+
reference_number = generate_reference_number
|
112
|
+
|
113
|
+
ActiveRecord::Base.transaction do
|
114
|
+
# Debit entry (from account)
|
115
|
+
debit = VirtualAccountTransaction.create!(
|
116
|
+
transaction_type: transaction_type,
|
117
|
+
entry_type: :debit,
|
118
|
+
account_id: from_account_id,
|
119
|
+
amount: amount,
|
120
|
+
reference_number: reference_number,
|
121
|
+
description: description,
|
122
|
+
status: :pending
|
123
|
+
)
|
124
|
+
|
125
|
+
# Credit entry (to account)
|
126
|
+
credit = VirtualAccountTransaction.create!(
|
127
|
+
transaction_type: transaction_type,
|
128
|
+
entry_type: :credit,
|
129
|
+
account_id: to_account_id,
|
130
|
+
amount: amount,
|
131
|
+
reference_number: reference_number,
|
132
|
+
description: description,
|
133
|
+
status: :pending,
|
134
|
+
paired_transaction_id: debit.id
|
135
|
+
)
|
136
|
+
|
137
|
+
# Update the paired_transaction_id for the debit entry
|
138
|
+
debit.update!(paired_transaction_id: credit.id)
|
139
|
+
|
140
|
+
[ debit, credit ]
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def self.find_or_create_system_account(account_identifier)
|
145
|
+
# Find or create a system account for external transactions
|
146
|
+
account = VirtualAccount.find_or_create_by!(account_number: account_identifier) do |account|
|
147
|
+
# Set default values for a new system account
|
148
|
+
account.user_id = find_or_create_system_user.id
|
149
|
+
account.cbs_account_number = account_identifier
|
150
|
+
account.branch_code = "SYSTEM"
|
151
|
+
account.product_scheme = "SAVINGS"
|
152
|
+
account.voucher_type = "REGULAR"
|
153
|
+
account.interest_rate = 0
|
154
|
+
account.interest_type = :simple
|
155
|
+
account.balance = 100000.00 # Initialize with a large balance
|
156
|
+
account.locked_amount = 0
|
157
|
+
account.status = :active
|
158
|
+
end
|
159
|
+
|
160
|
+
account
|
161
|
+
end
|
162
|
+
|
163
|
+
def self.ensure_system_account_balance(account, required_amount)
|
164
|
+
# Top up the system account if needed
|
165
|
+
if account.balance < required_amount
|
166
|
+
account.update!(balance: account.balance + required_amount * 2)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
def self.find_or_create_system_user
|
171
|
+
User.find_or_create_by!(phone_number: "SYSTEM_USER") do |user|
|
172
|
+
user.first_name = "System"
|
173
|
+
user.last_name = "User"
|
174
|
+
user.password = SecureRandom.hex(8)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
def self.generate_reference_number
|
179
|
+
timestamp = Time.current.strftime("%Y%m%d%H%M%S")
|
180
|
+
random = SecureRandom.hex(3)
|
181
|
+
"TXN#{timestamp}#{random}"
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class AddAccountingFieldsToVirtualAccountTransactions < ActiveRecord::Migration[8.0]
|
2
|
+
def change
|
3
|
+
# Add new accounting fields
|
4
|
+
add_column :bscf_core_virtual_account_transactions, :entry_type, :integer
|
5
|
+
add_reference :bscf_core_virtual_account_transactions, :account, null: false, foreign_key: { to_table: :bscf_core_virtual_accounts }
|
6
|
+
add_column :bscf_core_virtual_account_transactions, :running_balance, :decimal, precision: 10, scale: 2
|
7
|
+
add_reference :bscf_core_virtual_account_transactions, :paired_transaction, null: true, foreign_key: { to_table: :bscf_core_virtual_account_transactions }
|
8
|
+
add_column :bscf_core_virtual_account_transactions, :value_date, :datetime
|
9
|
+
|
10
|
+
# Add new indexes
|
11
|
+
add_index :bscf_core_virtual_account_transactions, [ :account_id, :reference_number ]
|
12
|
+
add_index :bscf_core_virtual_account_transactions, :entry_type
|
13
|
+
|
14
|
+
# Remove old fields and indexes
|
15
|
+
remove_index :bscf_core_virtual_account_transactions, [ :from_account_id, :reference_number ], if_exists: true
|
16
|
+
remove_index :bscf_core_virtual_account_transactions, [ :to_account_id, :reference_number ], if_exists: true
|
17
|
+
remove_reference :bscf_core_virtual_account_transactions, :from_account
|
18
|
+
remove_reference :bscf_core_virtual_account_transactions, :to_account
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class UpdateDeliveryTables < ActiveRecord::Migration[8.0]
|
2
|
+
def up
|
3
|
+
remove_column :bscf_core_delivery_orders, :buyer_phone
|
4
|
+
remove_column :bscf_core_delivery_orders, :seller_phone
|
5
|
+
|
6
|
+
add_column :bscf_core_delivery_orders, :estimated_delivery_price, :float
|
7
|
+
|
8
|
+
rename_column :bscf_core_delivery_orders, :delivery_price, :actual_delivery_price
|
9
|
+
|
10
|
+
add_reference :bscf_core_delivery_order_items, :pickup_address, foreign_key: { to_table: :bscf_core_addresses }
|
11
|
+
add_reference :bscf_core_delivery_order_items, :dropoff_address, foreign_key: { to_table: :bscf_core_addresses }
|
12
|
+
|
13
|
+
remove_reference :bscf_core_delivery_order_items, :product
|
14
|
+
end
|
15
|
+
end
|
data/lib/bscf/core/version.rb
CHANGED
@@ -2,7 +2,8 @@ FactoryBot.define do
|
|
2
2
|
factory :delivery_order_item, class: 'Bscf::Core::DeliveryOrderItem' do
|
3
3
|
association :delivery_order
|
4
4
|
association :order_item
|
5
|
-
association :
|
5
|
+
association :pickup_address, factory: :address
|
6
|
+
association :dropoff_address, factory: :address
|
6
7
|
quantity { order_item.quantity }
|
7
8
|
status { :pending }
|
8
9
|
notes { Faker::Lorem.paragraph }
|
@@ -2,38 +2,15 @@ FactoryBot.define do
|
|
2
2
|
factory :delivery_order, class: 'Bscf::Core::DeliveryOrder' do
|
3
3
|
association :pickup_address, factory: :address
|
4
4
|
association :dropoff_address, factory: :address
|
5
|
-
driver
|
5
|
+
association :driver, factory: :user
|
6
6
|
|
7
|
-
buyer_phone { Faker::PhoneNumber.phone_number }
|
8
|
-
seller_phone { Faker::PhoneNumber.phone_number }
|
9
7
|
driver_phone { Faker::PhoneNumber.phone_number }
|
10
8
|
delivery_notes { Faker::Lorem.paragraph }
|
11
9
|
estimated_delivery_time { 2.days.from_now }
|
10
|
+
estimated_delivery_price { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
11
|
+
actual_delivery_price { Faker::Number.decimal(l_digits: 2, r_digits: 2) }
|
12
12
|
delivery_start_time { nil }
|
13
13
|
delivery_end_time { nil }
|
14
14
|
status { :pending }
|
15
|
-
|
16
|
-
trait :with_driver do
|
17
|
-
association :driver, factory: :user
|
18
|
-
end
|
19
|
-
|
20
|
-
trait :in_transit do
|
21
|
-
with_driver
|
22
|
-
status { :in_transit }
|
23
|
-
delivery_start_time { Time.current }
|
24
|
-
end
|
25
|
-
|
26
|
-
trait :delivered do
|
27
|
-
with_driver
|
28
|
-
status { :delivered }
|
29
|
-
delivery_start_time { 2.hours.ago }
|
30
|
-
delivery_end_time { Time.current }
|
31
|
-
end
|
32
|
-
|
33
|
-
trait :with_orders do
|
34
|
-
after(:create) do |delivery_order|
|
35
|
-
create_list(:order, 2, delivery_order: delivery_order)
|
36
|
-
end
|
37
|
-
end
|
38
15
|
end
|
39
16
|
end
|
@@ -1,34 +1,49 @@
|
|
1
1
|
FactoryBot.define do
|
2
2
|
factory :virtual_account_transaction, class: 'Bscf::Core::VirtualAccountTransaction' do
|
3
|
-
association :
|
4
|
-
association :to_account, factory: :virtual_account, status: :active, balance: 5000
|
3
|
+
association :account, factory: :virtual_account, status: :active, balance: 10000
|
5
4
|
amount { 100.0 }
|
6
|
-
transaction_type { :
|
5
|
+
transaction_type { :adjustment }
|
6
|
+
entry_type { :debit }
|
7
7
|
status { :pending }
|
8
|
+
value_date { Time.current }
|
8
9
|
description { Faker::Lorem.sentence }
|
10
|
+
running_balance { nil }
|
9
11
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
status { :failed }
|
16
|
-
end
|
12
|
+
factory :paired_transaction do
|
13
|
+
transient do
|
14
|
+
paired_account { create(:virtual_account, status: :active, balance: 10000) }
|
15
|
+
is_credit { false }
|
16
|
+
end
|
17
17
|
|
18
|
-
|
19
|
-
|
20
|
-
|
18
|
+
after(:build) do |transaction, evaluator|
|
19
|
+
if transaction.transaction_type == 'transfer'
|
20
|
+
paired_entry_type = transaction.entry_type == 'debit' ? :credit : :debit
|
21
|
+
elsif transaction.transaction_type == 'deposit'
|
22
|
+
paired_entry_type = transaction.entry_type == 'debit' ? :credit : :debit
|
23
|
+
elsif transaction.transaction_type == 'withdrawal'
|
24
|
+
paired_entry_type = transaction.entry_type == 'debit' ? :credit : :debit
|
25
|
+
else
|
26
|
+
paired_entry_type = evaluator.is_credit ? :credit : :debit
|
27
|
+
end
|
21
28
|
|
22
|
-
|
23
|
-
|
24
|
-
|
29
|
+
paired = build(:virtual_account_transaction,
|
30
|
+
transaction_type: transaction.transaction_type,
|
31
|
+
entry_type: paired_entry_type,
|
32
|
+
account: evaluator.paired_account,
|
33
|
+
amount: transaction.amount,
|
34
|
+
reference_number: transaction.reference_number,
|
35
|
+
value_date: transaction.value_date,
|
36
|
+
status: transaction.status
|
37
|
+
)
|
25
38
|
|
26
|
-
|
27
|
-
|
28
|
-
|
39
|
+
transaction.paired_transaction = paired
|
40
|
+
paired.paired_transaction = transaction
|
41
|
+
end
|
29
42
|
|
30
|
-
|
31
|
-
|
43
|
+
after(:create) do |transaction, evaluator|
|
44
|
+
# Save the paired transaction after the main transaction is created
|
45
|
+
transaction.paired_transaction.save! if transaction.paired_transaction&.new_record?
|
46
|
+
end
|
32
47
|
end
|
33
48
|
end
|
34
49
|
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
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Asrat
|
@@ -311,6 +311,7 @@ files:
|
|
311
311
|
- app/models/bscf/core/voucher.rb
|
312
312
|
- app/models/bscf/core/wholesaler_product.rb
|
313
313
|
- app/services/bscf/core/token_service.rb
|
314
|
+
- app/services/bscf/core/transaction_service.rb
|
314
315
|
- config/database.yml
|
315
316
|
- config/routes.rb
|
316
317
|
- db/migrate/20250326065606_create_bscf_core_users.rb
|
@@ -345,6 +346,8 @@ files:
|
|
345
346
|
- db/migrate/20250524081221_create_bscf_core_vouchers.rb
|
346
347
|
- db/migrate/20250524151346_add_locked_amount_to_virtual_accounts.rb
|
347
348
|
- db/migrate/20250606100158_add_product_to_market_place_listings.rb
|
349
|
+
- db/migrate/20250609180530_add_accounting_fields_to_virtual_account_transactions.rb
|
350
|
+
- db/migrate/20250610120351_update_delivery_tables.rb
|
348
351
|
- lib/bscf/core.rb
|
349
352
|
- lib/bscf/core/engine.rb
|
350
353
|
- lib/bscf/core/version.rb
|