HasWallet   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 185
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 16
Bugs 1 Features 5
Metric Value
wmc 14
eloc 31
c 16
b 1
f 5
dl 0
loc 185
ccs 39
cts 39
cp 1
rs 10

10 Methods

Rating   Name   Duplication   Size   Complexity  
A forceTransfer() 0 7 1
A withdraw() 0 5 1
A transfers() 0 5 1
A transfer() 0 5 1
A deposit() 0 7 1
A forceWithdraw() 0 7 1
A transactions() 0 4 2
A safeTransfer() 0 6 2
A canWithdraw() 0 12 3
A getBalanceAttribute() 0 3 1
1
<?php
2
3
namespace Bavix\Wallet\Traits;
4
5
use function app;
6
use Bavix\Wallet\Interfaces\Mathable;
7
use Bavix\Wallet\Interfaces\Storable;
8
use Bavix\Wallet\Interfaces\Wallet;
9
use Bavix\Wallet\Models\Transaction;
10
use Bavix\Wallet\Models\Transfer;
11
use Bavix\Wallet\Models\Wallet as WalletModel;
12
use Bavix\Wallet\Services\CommonService;
13
use Bavix\Wallet\Services\DbService;
14
use Bavix\Wallet\Services\WalletService;
15
use function config;
16
use Illuminate\Database\Eloquent\Relations\MorphMany;
17
use Illuminate\Support\Collection;
18
use Throwable;
19
20
/**
21
 * Trait HasWallet.
22
 *
23
 *
24
 * @property-read Collection|WalletModel[] $wallets
25
 * @property-read int $balance
26
 */
27
trait HasWallet
28
{
29
    use MorphOneWallet;
30
31
    /**
32
     * The input means in the system.
33
     *
34
     * @param int $amount
35
     * @param array|null $meta
36
     * @param bool $confirmed
37
     *
38
     * @return Transaction
39
     * @throws
40
     */
41 92
    public function deposit($amount, ?array $meta = null, bool $confirmed = true): Transaction
42
    {
43 92
        $self = $this;
44
45
        return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) {
46 92
            return app(CommonService::class)
47 92
                ->deposit($self, $amount, $meta, $confirmed);
0 ignored issues
show
Bug introduced by
$self of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\CommonService::deposit(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

47
                ->deposit(/** @scrutinizer ignore-type */ $self, $amount, $meta, $confirmed);
Loading history...
48 92
        });
49
    }
50
51
    /**
52
     * Magic laravel framework method, makes it
53
     *  possible to call property balance.
54
     *
55
     * Example:
56
     *  $user1 = User::first()->load('wallet');
57
     *  $user2 = User::first()->load('wallet');
58
     *
59
     * Without static:
60
     *  var_dump($user1->balance, $user2->balance); // 100 100
61
     *  $user1->deposit(100);
62
     *  $user2->deposit(100);
63
     *  var_dump($user1->balance, $user2->balance); // 200 200
64
     *
65
     * With static:
66
     *  var_dump($user1->balance, $user2->balance); // 100 100
67
     *  $user1->deposit(100);
68
     *  var_dump($user1->balance); // 200
69
     *  $user2->deposit(100);
70
     *  var_dump($user2->balance); // 300
71
     *
72
     * @return int|float
73
     * @throws
74
     */
75 118
    public function getBalanceAttribute()
76
    {
77 118
        return app(Storable::class)->getBalance($this);
78
    }
79
80
    /**
81
     * all user actions on wallets will be in this method.
82
     *
83
     * @return MorphMany
84
     */
85 110
    public function transactions(): MorphMany
86
    {
87 110
        return ($this instanceof WalletModel ? $this->holder : $this)
88 110
            ->morphMany(config('wallet.transaction.model', Transaction::class), 'payable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

88
            ->/** @scrutinizer ignore-call */ morphMany(config('wallet.transaction.model', Transaction::class), 'payable');
Loading history...
89
    }
90
91
    /**
92
     * This method ignores errors that occur when transferring funds.
93
     *
94
     * @param Wallet $wallet
95
     * @param int $amount
96
     * @param array|null $meta
97
     * @return null|Transfer
98
     */
99 3
    public function safeTransfer(Wallet $wallet, $amount, ?array $meta = null): ?Transfer
100
    {
101
        try {
102 3
            return $this->transfer($wallet, $amount, $meta);
103 3
        } catch (Throwable $throwable) {
104 3
            return null;
105
        }
106
    }
107
108
    /**
109
     * A method that transfers funds from host to host.
110
     *
111
     * @param Wallet $wallet
112
     * @param int $amount
113
     * @param array|null $meta
114
     * @return Transfer
115
     * @throws
116
     */
117 8
    public function transfer(Wallet $wallet, $amount, ?array $meta = null): Transfer
118
    {
119 8
        app(CommonService::class)->verifyWithdraw($this, $amount);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...rvice::verifyWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

119
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
120
121 8
        return $this->forceTransfer($wallet, $amount, $meta);
122
    }
123
124
    /**
125
     * Withdrawals from the system.
126
     *
127
     * @param int $amount
128
     * @param array|null $meta
129
     * @param bool $confirmed
130
     *
131
     * @return Transaction
132
     */
133 39
    public function withdraw($amount, ?array $meta = null, bool $confirmed = true): Transaction
134
    {
135 39
        app(CommonService::class)->verifyWithdraw($this, $amount);
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...rvice::verifyWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

135
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
136
137 32
        return $this->forceWithdraw($amount, $meta, $confirmed);
138
    }
139
140
    /**
141
     * Checks if you can withdraw funds.
142
     *
143
     * @param int $amount
144
     * @param bool $allowZero
145
     * @return bool
146
     */
147 69
    public function canWithdraw($amount, bool $allowZero = null): bool
148
    {
149 69
        $math = app(Mathable::class);
150
151
        /**
152
         * Allow to buy for free with a negative balance.
153
         */
154 69
        if ($allowZero && ! $math->compare($amount, 0)) {
155 11
            return true;
156
        }
157
158 66
        return $math->compare($this->balance, $amount) >= 0;
159
    }
160
161
    /**
162
     * Forced to withdraw funds from system.
163
     *
164
     * @param int $amount
165
     * @param array|null $meta
166
     * @param bool $confirmed
167
     *
168
     * @return Transaction
169
     * @throws
170
     */
171 47
    public function forceWithdraw($amount, ?array $meta = null, bool $confirmed = true): Transaction
172
    {
173 47
        $self = $this;
174
175
        return app(DbService::class)->transaction(static function () use ($self, $amount, $meta, $confirmed) {
176 47
            return app(CommonService::class)
177 47
                ->forceWithdraw($self, $amount, $meta, $confirmed);
0 ignored issues
show
Bug introduced by
$self of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $wallet of Bavix\Wallet\Services\Co...ervice::forceWithdraw(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

177
                ->forceWithdraw(/** @scrutinizer ignore-type */ $self, $amount, $meta, $confirmed);
Loading history...
178 47
        });
179
    }
180
181
    /**
182
     * the forced transfer is needed when the user does not have the money and we drive it.
183
     * Sometimes you do. Depends on business logic.
184
     *
185
     * @param Wallet $wallet
186
     * @param int $amount
187
     * @param array|null $meta
188
     * @return Transfer
189
     * @throws
190
     */
191 8
    public function forceTransfer(Wallet $wallet, $amount, ?array $meta = null): Transfer
192
    {
193 8
        $self = $this;
194
195
        return app(DbService::class)->transaction(static function () use ($self, $amount, $wallet, $meta) {
196 8
            return app(CommonService::class)
197 8
                ->forceTransfer($self, $wallet, $amount, $meta);
0 ignored issues
show
Bug introduced by
$self of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $from of Bavix\Wallet\Services\Co...ervice::forceTransfer(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

197
                ->forceTransfer(/** @scrutinizer ignore-type */ $self, $wallet, $amount, $meta);
Loading history...
198 8
        });
199
    }
200
201
    /**
202
     * the transfer table is used to confirm the payment
203
     * this method receives all transfers.
204
     *
205
     * @return MorphMany
206
     */
207 39
    public function transfers(): MorphMany
208
    {
209 39
        return app(WalletService::class)
210 39
            ->getWallet($this, false)
0 ignored issues
show
Bug introduced by
$this of type Bavix\Wallet\Traits\HasWallet is incompatible with the type Bavix\Wallet\Interfaces\Wallet expected by parameter $object of Bavix\Wallet\Services\WalletService::getWallet(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

210
            ->getWallet(/** @scrutinizer ignore-type */ $this, false)
Loading history...
211 39
            ->morphMany(config('wallet.transfer.model', Transfer::class), 'from');
212
    }
213
}
214