simple_wallet 0.1.1 → 0.1.2

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: ecb26e24cf18bdc51ca2d483ee5570a91418108e4aa42da8138c38d7de099704
4
- data.tar.gz: a20a7b3eed5ce08422a739b1419f73da1d1ed7fbd08800ccf5ca329daef09aa7
3
+ metadata.gz: f6eac1eb277d4d4262cd05c2ced9a10c7f790613a97fb3385e496953cfd7764a
4
+ data.tar.gz: c4147c19783452b10af6c63bd97e50c4b2c75e70032129e4e3aa5f734630d151
5
5
  SHA512:
6
- metadata.gz: c4c60183ac0922910a4de5fed4defba64d56126ff2809ab9b20a02996dbbd38dcc641213787ccfda7afe6c234372674737fc5b60578832bc5dcdc36685ac5def
7
- data.tar.gz: bc129abbe862eba61e6714d1ee6665839f4f2c95665b005978479b882977dc00e36d559e7109362d3978786696daa861ec030793365af4546407cf9e6dd0a4b7
6
+ metadata.gz: 1e6c8f15d40e3d4c9612ace9b5e14ba6aadc55dd21b2fc807b8c3326d21ef83532cbbc21edad53ed2ec1e742aaae23af3ae51fbb7ffe7fa47fa0936ca1c9fed2
7
+ data.tar.gz: dacc153a8da1661f1cecc050b995740889d35359fcc1de4827d0ff28b573a8b8dd7a7bce895079ac2a8dc783558aa671eb1dcb4eedd605b92d295976d18db91e
@@ -0,0 +1,55 @@
1
+ module SimpleWallet
2
+ class AccountCreditingService < Service
3
+ validates :account, presence: true
4
+ validates :amount, presence: true, numericality: { only_integer: true, greater_than: 0 }
5
+
6
+ def initialize(account:, amount: nil, source: nil, note: nil)
7
+ @account = account
8
+ @amount = amount
9
+ @source = source
10
+ @note = note
11
+ end
12
+
13
+ # Use set_transaction_isolation_level: false to avoid getting this exception:
14
+ # ActiveRecord::TransactionIsolationError (cannot set isolation when joining a transaction)
15
+ def credit(set_transaction_isolation_level: true)
16
+ # Setting transaction isolation level in tests is problematic due to nested transactions :|
17
+ isolation_level = (::Rails.env.test? || !set_transaction_isolation_level) ? nil : :serializable
18
+
19
+ ActiveRecord::Base.transaction(isolation: isolation_level) do
20
+ return false unless valid?
21
+ unless crediting_transaction.valid?
22
+ copy_errors_from(crediting_transaction)
23
+ return false
24
+ end
25
+
26
+ crediting_transaction.save!
27
+ after_operation
28
+ end
29
+
30
+ true
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :account, :source, :amount, :note
36
+
37
+ def crediting_transaction
38
+ @crediting_transaction ||= ::SimpleWallet::Transaction::Credit.new(
39
+ account: account,
40
+ source: source,
41
+ pre_account_balance: account&.balance,
42
+ amount: amount,
43
+ note: note
44
+ )
45
+ end
46
+
47
+ def after_operation
48
+ update_accounts_balance
49
+ end
50
+
51
+ def update_accounts_balance
52
+ account.recalculate_balance!
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,81 @@
1
+ module SimpleWallet
2
+ class AccountDebitingService < Service
3
+ validates :account, presence: true
4
+ validates :amount, presence: true, numericality: { only_integer: true, greater_than: 0 }
5
+ validate :account_has_sufficient_funds
6
+
7
+ def initialize(account:, amount: nil, source: nil, note: nil, up_to_account_balance: false)
8
+ @account = account
9
+ @amount = amount
10
+ @source = source
11
+ @up_to_account_balance = up_to_account_balance
12
+ @note = note
13
+ end
14
+
15
+ # Use set_transaction_isolation_level: false to avoid getting this exception:
16
+ # ActiveRecord::TransactionIsolationError (cannot set isolation when joining a transaction)
17
+ def debit(set_transaction_isolation_level: true)
18
+ # Checking account's balance should be wrapped in a transaction:
19
+ # Setting transaction isolation level in tests is problematic due to nested transactions :|
20
+ isolation_level = (Rails.env.test? || !set_transaction_isolation_level) ? nil : :serializable
21
+
22
+ ActiveRecord::Base.transaction(isolation: isolation_level) do
23
+ return false unless valid?
24
+ unless debiting_transaction.valid?
25
+ copy_errors_from(debiting_transaction)
26
+ return false
27
+ end
28
+
29
+ debiting_transaction.save!
30
+ after_operation
31
+ end
32
+
33
+ true
34
+ end
35
+
36
+ private
37
+
38
+ attr_reader :account, :source, :amount, :note, :up_to_account_balance
39
+
40
+ def ensure_negative(amount_to_convert)
41
+ -(amount_to_convert.abs)
42
+ end
43
+
44
+ # If debiting up to account balance is allowed then we debit as much as we can.
45
+ # If not then we will return error on insufficient funds.
46
+ def actual_amount_to_debit
47
+ if up_to_account_balance
48
+ [ account.balance, amount ].min
49
+ else
50
+ amount
51
+ end
52
+ end
53
+
54
+ def account_has_sufficient_funds
55
+ return if errors.include?(:account)
56
+ return if errors.include?(:amount)
57
+
58
+ if account.balance < actual_amount_to_debit
59
+ errors.add(:amount, t(".exceeds_balance"))
60
+ end
61
+ end
62
+
63
+ def debiting_transaction
64
+ @debiting_transaction ||= ::SimpleWallet::Transaction::Debit.new(
65
+ account: account,
66
+ source: source,
67
+ pre_account_balance: account.balance,
68
+ amount: ensure_negative(actual_amount_to_debit),
69
+ note: note
70
+ )
71
+ end
72
+
73
+ def after_operation
74
+ update_accounts_balance
75
+ end
76
+
77
+ def update_accounts_balance
78
+ account.recalculate_balance!
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,35 @@
1
+ module SimpleWallet
2
+ class Service
3
+ include ActiveModel::Validations
4
+
5
+ def errors_sentence
6
+ errors.full_messages.to_sentence
7
+ end
8
+
9
+ # Shamelessly stolen from:
10
+ # https://github.com/rails/rails/blob/fbe2433be6e052a1acac63c7faf287c52ed3c5ba/actionpack/lib/abstract_controller/translation.rb#L13
11
+ def t(key, **options)
12
+ if key.to_s.first == "."
13
+ path = self.class.name.underscore.tr("/", ".")
14
+ parent_class_path = self.class.superclass.name.underscore.tr("/", ".")
15
+ defaults = [ :"#{path}#{key}", :"#{parent_class_path}#{key}" ]
16
+ defaults << options[:default] if options[:default]
17
+ options[:default] = defaults.flatten
18
+ key = "#{path}#{key}"
19
+ end
20
+
21
+ options[:raise] = (Rails.env.development? || Rails.env.test?)
22
+ I18n.t(key, **options)
23
+ end
24
+
25
+ private
26
+
27
+ delegate :logger, to: :Rails
28
+
29
+ def copy_errors_from(errors_source)
30
+ errors_source.errors.each do |error|
31
+ errors.add(error.attribute, error.message)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,4 @@
1
+ en:
2
+ simple_wallet:
3
+ account_debiting_service:
4
+ exceeds_balance: "exceeds available balance"
@@ -1,3 +1,3 @@
1
1
  module SimpleWallet
2
- VERSION = "0.1.1"
2
+ VERSION = "0.1.2"
3
3
  end