Passed
Pull Request — master (#257)
by Бабичев
28:29
created

WalletService::adjustment()   A

Complexity

Conditions 3
Paths 1

Size

Total Lines 16
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 13
c 0
b 0
f 0
nc 1
nop 2
dl 0
loc 16
ccs 13
cts 13
cp 1
crap 3
rs 9.8333
1
<?php
2
3
namespace Bavix\Wallet\Services;
4
5
use function app;
6
use Bavix\Wallet\Exceptions\AmountInvalid;
7
use Bavix\Wallet\Interfaces\Customer;
8
use Bavix\Wallet\Interfaces\Discount;
9
use Bavix\Wallet\Interfaces\Mathable;
10
use Bavix\Wallet\Interfaces\MinimalTaxable;
11
use Bavix\Wallet\Interfaces\Storable;
12
use Bavix\Wallet\Interfaces\Taxable;
13
use Bavix\Wallet\Interfaces\Wallet;
14
use Bavix\Wallet\Models\Wallet as WalletModel;
15
use Bavix\Wallet\Traits\HasWallet;
16
17
class WalletService
18
{
19
    /**
20
     * @param Wallet $customer
21
     * @param Wallet $product
22
     * @return int
23
     */
24 54
    public function discount(Wallet $customer, Wallet $product): int
25
    {
26 54
        if ($customer instanceof Customer && $product instanceof Discount) {
27 21
            return $product->getPersonalDiscount($customer);
28
        }
29
30
        // without discount
31 43
        return 0;
32
    }
33
34
    /**
35
     * @param Wallet $object
36
     * @return int
37
     */
38 66
    public function decimalPlacesValue(Wallet $object): int
39
    {
40 66
        return $this->getWallet($object)->decimal_places ?: 2;
41
    }
42
43
    /**
44
     * @param Wallet $object
45
     * @return string
46
     */
47 17
    public function decimalPlaces(Wallet $object): string
48
    {
49 17
        return app(Mathable::class)
50 17
            ->pow(10, $this->decimalPlacesValue($object));
51
    }
52
53
    /**
54
     * Consider the fee that the system will receive.
55
     *
56
     * @param Wallet $wallet
57
     * @param int $amount
58
     * @return float|int
59
     */
60 55
    public function fee(Wallet $wallet, $amount)
61
    {
62 55
        $fee = 0;
63 55
        $math = app(Mathable::class);
64 55
        if ($wallet instanceof Taxable) {
65 14
            $placesValue = $this->decimalPlacesValue($wallet);
66 14
            $fee = $math->floor(
67 14
                $math->div(
68 14
                    $math->mul($amount, $wallet->getFeePercent(), 0),
69 14
                    100,
70
                    $placesValue
71
                )
72
            );
73
        }
74
75
        /**
76
         * Added minimum commission condition.
77
         *
78
         * @see https://github.com/bavix/laravel-wallet/issues/64#issuecomment-514483143
79
         */
80 55
        if ($wallet instanceof MinimalTaxable) {
81 1
            $minimal = $wallet->getMinimalFee();
82 1
            if (app(Mathable::class)->compare($fee, $minimal) === -1) {
83 1
                $fee = $minimal;
84
            }
85
        }
86
87 55
        return $fee;
88
    }
89
90
    /**
91
     * The amount of checks for errors.
92
     *
93
     * @param int $amount
94
     * @throws
95
     */
96 130
    public function checkAmount($amount): void
97
    {
98 130
        if (app(Mathable::class)->compare($amount, 0) === -1) {
99 3
            throw new AmountInvalid(trans('wallet::errors.price_positive'));
0 ignored issues
show
Bug introduced by
It seems like trans('wallet::errors.price_positive') can also be of type array and array; however, parameter $message of Bavix\Wallet\Exceptions\...tInvalid::__construct() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

99
            throw new AmountInvalid(/** @scrutinizer ignore-type */ trans('wallet::errors.price_positive'));
Loading history...
100
        }
101 127
    }
102
103
    /**
104
     * @param Wallet $object
105
     * @param bool $autoSave
106
     * @return WalletModel
107
     */
108 146
    public function getWallet(Wallet $object, bool $autoSave = true): WalletModel
109
    {
110
        /**
111
         * @var WalletModel $wallet
112
         */
113 146
        $wallet = $object;
114
115 146
        if (! ($object instanceof WalletModel)) {
116
            /**
117
             * @var HasWallet $object
118
             */
119 89
            $wallet = $object->wallet;
120
        }
121
122 146
        if ($autoSave) {
123 146
            $wallet->exists or $wallet->save();
124
        }
125
126 146
        return $wallet;
127
    }
128
129
    /**
130
     * @param WalletModel $wallet
131
     * @return bool
132
     */
133 33
    public function refresh(WalletModel $wallet): bool
134
    {
135 33
        return app(LockService::class)->lock($this, __FUNCTION__, static function () use ($wallet) {
136 33
            $math = app(Mathable::class);
137 33
            app(Storable::class)->getBalance($wallet);
138 33
            $whatIs = $wallet->balance;
139 33
            $balance = $wallet->getAvailableBalance();
140 33
            $wallet->balance = $balance;
141
142 33
            return app(Storable::class)->setBalance($wallet, $balance) &&
0 ignored issues
show
Bug introduced by
It seems like $balance can also be of type double; however, parameter $amount of Bavix\Wallet\Interfaces\Storable::setBalance() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

142
            return app(Storable::class)->setBalance($wallet, /** @scrutinizer ignore-type */ $balance) &&
Loading history...
143 33
                (! $math->compare($whatIs, $balance) || $wallet->save());
144 33
        });
145
    }
146
147
    /**
148
     * @param WalletModel $wallet
149
     * @param array|null $meta
150
     * @return void
151
     * @throws
152
     */
153 6
    public function adjustment(WalletModel $wallet, ?array $meta = null): void
154
    {
155 6
        app(DbService::class)->transaction(function () use ($wallet, $meta) {
156 6
            $math = app(Mathable::class);
157 6
            app(Storable::class)->getBalance($wallet);
158 6
            $adjustmentBalance = $wallet->balance;
159 6
            $wallet->refreshBalance();
160 6
            $difference = $math->sub($wallet->balance, $adjustmentBalance);
161
162 6
            switch ($math->compare($difference, 0)) {
163
                case -1:
164 2
                    $wallet->deposit($math->abs($difference), $meta);
0 ignored issues
show
Bug introduced by
$math->abs($difference) of type string is incompatible with the type integer expected by parameter $amount of Bavix\Wallet\Models\Wallet::deposit(). ( Ignorable by Annotation )

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

164
                    $wallet->deposit(/** @scrutinizer ignore-type */ $math->abs($difference), $meta);
Loading history...
165 2
                    break;
166 4
                case 1:
167 2
                    $wallet->forceWithdraw($math->abs($difference), $meta);
0 ignored issues
show
Bug introduced by
$math->abs($difference) of type string is incompatible with the type integer expected by parameter $amount of Bavix\Wallet\Models\Wallet::forceWithdraw(). ( Ignorable by Annotation )

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

167
                    $wallet->forceWithdraw(/** @scrutinizer ignore-type */ $math->abs($difference), $meta);
Loading history...
168 2
                    break;
169
            }
170 6
        });
171 6
    }
172
}
173