Test Failed
Pull Request — master (#83)
by Бабичев
13:48 queued 09:03
created

HasWallet::withdraw()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1.037

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 2
b 0
f 0
nc 1
nop 3
dl 0
loc 4
ccs 2
cts 3
cp 0.6667
crap 1.037
rs 10
1
<?php
2
3
namespace Bavix\Wallet\Traits;
4
5
use Bavix\Wallet\Interfaces\Wallet;
6
use Bavix\Wallet\Models\Transaction;
7
use Bavix\Wallet\Models\Transfer;
8
use Bavix\Wallet\Models\Wallet as WalletModel;
9
use Bavix\Wallet\Services\CommonService;
10
use Bavix\Wallet\Services\WalletService;
11
use Illuminate\Database\Eloquent\Relations\MorphMany;
12
use Illuminate\Database\Eloquent\Relations\MorphOne;
13
use Illuminate\Support\Collection;
14
use Illuminate\Support\Facades\DB;
15
use Throwable;
16
use function app;
17
use function config;
18
19
/**
20
 * Trait HasWallet
21
 *
22
 * @package Bavix\Wallet\Traits
23
 *
24
 * @property-read WalletModel $wallet
25
 * @property-read Collection|WalletModel[] $wallets
26
 * @property-read int $balance
27
 */
28
trait HasWallet
29
{
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
     */
40 39
    public function deposit(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
41
    {
42 39
        $self = $this;
43
        return DB::transaction(static function() use ($self, $amount, $meta, $confirmed) {
44 39
            return app(CommonService::class)
45 39
                ->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

45
                ->deposit(/** @scrutinizer ignore-type */ $self, $amount, $meta, $confirmed);
Loading history...
46 39
        });
47
    }
48
49
    /**
50
     * Magic laravel framework method, makes it
51
     *  possible to call property balance
52
     *
53
     * Example:
54
     *  $user1 = User::first()->load('wallet');
55
     *  $user2 = User::first()->load('wallet');
56
     *
57
     * Without static:
58
     *  var_dump($user1->balance, $user2->balance); // 100 100
59
     *  $user1->deposit(100);
60
     *  $user2->deposit(100);
61
     *  var_dump($user1->balance, $user2->balance); // 200 200
62
     *
63
     * With static:
64
     *  var_dump($user1->balance, $user2->balance); // 100 100
65
     *  $user1->deposit(100);
66
     *  var_dump($user1->balance); // 200
67
     *  $user2->deposit(100);
68
     *  var_dump($user2->balance); // 300
69
     *
70
     * @return int
71
     * @throws
72
     */
73 51
    public function getBalanceAttribute(): int
74
    {
75 51
        return app(WalletService::class)->getBalance($this);
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::getBalance(). ( Ignorable by Annotation )

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

75
        return app(WalletService::class)->getBalance(/** @scrutinizer ignore-type */ $this);
Loading history...
76
    }
77
78
    /**
79
     * all user actions on wallets will be in this method
80
     *
81
     * @return MorphMany
82
     */
83
    public function transactions(): MorphMany
84
    {
85
        return ($this instanceof WalletModel ? $this->holder : $this)
86
            ->morphMany(config('wallet.transaction.model'), '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

86
            ->/** @scrutinizer ignore-call */ morphMany(config('wallet.transaction.model'), 'payable');
Loading history...
87
    }
88
89
    /**
90
     * This method ignores errors that occur when transferring funds
91
     *
92
     * @param Wallet $wallet
93
     * @param int $amount
94
     * @param array|null $meta
95
     * @return null|Transfer
96
     */
97
    public function safeTransfer(Wallet $wallet, int $amount, ?array $meta = null): ?Transfer
98
    {
99
        try {
100
            return $this->transfer($wallet, $amount, $meta);
101
        } catch (Throwable $throwable) {
102
            return null;
103
        }
104
    }
105
106
    /**
107
     * A method that transfers funds from host to host
108
     *
109
     * @param Wallet $wallet
110
     * @param int $amount
111
     * @param array|null $meta
112
     * @return Transfer
113
     * @throws
114
     */
115
    public function transfer(Wallet $wallet, int $amount, ?array $meta = null): Transfer
116
    {
117
        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

117
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
118
        return $this->forceTransfer($wallet, $amount, $meta);
119
    }
120
121
    /**
122
     * Withdrawals from the system
123
     *
124
     * @param int $amount
125
     * @param array|null $meta
126
     * @param bool $confirmed
127
     *
128
     * @return Transaction
129
     */
130 6
    public function withdraw(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
131
    {
132 6
        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

132
        app(CommonService::class)->verifyWithdraw(/** @scrutinizer ignore-type */ $this, $amount);
Loading history...
133
        return $this->forceWithdraw($amount, $meta, $confirmed);
134
    }
135
136
    /**
137
     * Checks if you can withdraw funds
138
     *
139
     * @param int $amount
140
     * @param bool $allowZero
141
     * @return bool
142
     */
143 3
    public function canWithdraw(int $amount, bool $allowZero = null): bool
144
    {
145
        /**
146
         * Allow to buy for free with a negative balance
147
         */
148 3
        if ($allowZero && $amount === 0) {
149 2
            return true;
150
        }
151
152 1
        return $this->balance >= $amount;
153
    }
154
155
    /**
156
     * Forced to withdraw funds from system
157
     *
158
     * @param int $amount
159
     * @param array|null $meta
160
     * @param bool $confirmed
161
     *
162
     * @return Transaction
163
     */
164 4
    public function forceWithdraw(int $amount, ?array $meta = null, bool $confirmed = true): Transaction
165
    {
166 4
        $self = $this;
167
        return DB::transaction(static function() use ($self, $amount, $meta, $confirmed) {
168 4
            return app(CommonService::class)
169 4
                ->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

169
                ->forceWithdraw(/** @scrutinizer ignore-type */ $self, $amount, $meta, $confirmed);
Loading history...
170 4
        });
171
    }
172
173
    /**
174
     * the forced transfer is needed when the user does not have the money and we drive it.
175
     * Sometimes you do. Depends on business logic.
176
     *
177
     * @param Wallet $wallet
178
     * @param int $amount
179
     * @param array|null $meta
180
     * @return Transfer
181
     */
182
    public function forceTransfer(Wallet $wallet, int $amount, ?array $meta = null): Transfer
183
    {
184
        $self = $this;
185
        return DB::transaction(static function() use ($self, $amount, $wallet, $meta) {
186
            return app(CommonService::class)
187
                ->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

187
                ->forceTransfer(/** @scrutinizer ignore-type */ $self, $wallet, $amount, $meta);
Loading history...
188
        });
189
    }
190
191
    /**
192
     * the transfer table is used to confirm the payment
193
     * this method receives all transfers
194
     *
195
     * @return MorphMany
196
     */
197 2
    public function transfers(): MorphMany
198
    {
199 2
        return app(WalletService::class)
200 2
            ->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

200
            ->getWallet(/** @scrutinizer ignore-type */ $this, false)
Loading history...
201 2
            ->morphMany(config('wallet.transfer.model'), 'from');
202
    }
203
204
    /**
205
     * Get default Wallet
206
     * this method is used for Eager Loading
207
     *
208
     * @return MorphOne|WalletModel
209
     */
210 53
    public function wallet(): MorphOne
211
    {
212 53
        return ($this instanceof WalletModel ? $this->holder : $this)
213 53
            ->morphOne(config('wallet.wallet.model'), 'holder')
0 ignored issues
show
Bug introduced by
It seems like morphOne() 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

213
            ->/** @scrutinizer ignore-call */ morphOne(config('wallet.wallet.model'), 'holder')
Loading history...
214 53
            ->where('slug', config('wallet.wallet.default.slug'))
215 53
            ->withDefault([
216 53
                'name' => config('wallet.wallet.default.name'),
217 53
                'slug' => config('wallet.wallet.default.slug'),
218 53
                'balance' => 0,
219
            ]);
220
    }
221
222
}
223