Passed
Push — main ( f9e76f...2a9db7 )
by Michael
04:00
created

CouponService::isAllowedToRedeem()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 6
CRAP Score 5

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 5
c 1
b 0
f 0
nc 3
nop 2
dl 0
loc 11
ccs 6
cts 6
cp 1
crap 5
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\Couponables\Services;
6
7
use Illuminate\Database\Eloquent\Model;
8
use Illuminate\Support\Traits\Macroable;
9
use MichaelRubel\Couponables\Events\CouponExpired;
10
use MichaelRubel\Couponables\Events\CouponIsOverLimit;
11
use MichaelRubel\Couponables\Events\CouponIsOverQuantity;
12
use MichaelRubel\Couponables\Events\CouponRedeemed;
13
use MichaelRubel\Couponables\Events\CouponVerified;
14
use MichaelRubel\Couponables\Events\FailedToRedeemCoupon;
15
use MichaelRubel\Couponables\Events\NotAllowedToRedeem;
16
use MichaelRubel\Couponables\Exceptions\CouponExpiredException;
17
use MichaelRubel\Couponables\Exceptions\InvalidCouponException;
18
use MichaelRubel\Couponables\Exceptions\NotAllowedToRedeemException;
19
use MichaelRubel\Couponables\Exceptions\OverLimitException;
20
use MichaelRubel\Couponables\Exceptions\OverQuantityException;
21
use MichaelRubel\Couponables\Models\Contracts\CouponContract;
22
use MichaelRubel\Couponables\Models\Contracts\CouponPivotContract;
23
use MichaelRubel\Couponables\Services\Contracts\CouponServiceContract;
24
use MichaelRubel\EnhancedContainer\Call;
25
use MichaelRubel\EnhancedContainer\Core\CallProxy;
26
27
class CouponService implements CouponServiceContract
28
{
29
    use Macroable;
0 ignored issues
show
Bug introduced by
The trait Illuminate\Support\Traits\Macroable requires the property $name which is not provided by MichaelRubel\Couponables\Services\CouponService.
Loading history...
30
31
    /**
32
     * @var CallProxy
33
     */
34
    public CallProxy $service;
35
36
    /**
37
     * @var CallProxy
38
     */
39
    public CallProxy $model;
40
41
    /**
42
     * @var CallProxy
43
     */
44
    public CallProxy $pivot;
45
46
    /**
47
     * @param CouponContract      $model
48
     * @param CouponPivotContract $pivot
49
     */
50 38
    public function __construct(CouponContract $model, CouponPivotContract $pivot)
51
    {
52 38
        $this->service = call($this);
53 38
        $this->model   = call($model);
54 38
        $this->pivot   = call($pivot);
55
    }
56
57
    /**
58
     * Get the coupon model by the code.
59
     *
60
     * @param string|null $code
61
     *
62
     * @return CouponContract|null
63
     */
64 28
    public function getCoupon(?string $code): ?CouponContract
65
    {
66 28
        return $this->model->firstWhere($this->model->getCodeColumn(), $code);
67
    }
68
69
    /**
70
     * Verify if coupon is valid otherwise throw an exception.
71
     *
72
     * @param string|null $code
73
     * @param Model       $redeemer
74
     *
75
     * @return CouponContract
76
     * @throws OverQuantityException
77
     * @throws OverLimitException
78
     * @throws NotAllowedToRedeemException
79
     * @throws CouponExpiredException
80
     * @throws InvalidCouponException
81
     */
82 28
    public function verifyCoupon(?string $code, Model $redeemer): CouponContract
83
    {
84 28
        $coupon = call($this->getCoupon($code) ?? throw new InvalidCouponException);
85
86 22
        if ($coupon->isExpired()) {
87 1
            event(new CouponExpired($coupon, $redeemer));
88
89 1
            throw new CouponExpiredException;
90
        }
91
92 21
        if ($coupon->isOverQuantity()) {
93 1
            event(new CouponIsOverQuantity($coupon, $redeemer));
94
95 1
            throw new OverQuantityException;
96
        }
97
98 21
        if ($coupon->isOverLimit($redeemer, $code)) {
99 2
            event(new CouponIsOverLimit($coupon, $redeemer));
100
101 2
            throw new OverLimitException;
102
        }
103
104 21
        if (! $coupon->isAllowedToRedeem($redeemer)) {
105 2
            event(new NotAllowedToRedeem($coupon, $redeemer));
106
107 2
            throw new NotAllowedToRedeemException;
108
        }
109
110 19
        event(new CouponVerified($coupon, $redeemer));
111
112 19
        return $coupon->getInternal(Call::INSTANCE);
113
    }
114
115
    /**
116
     * Apply the coupon.
117
     *
118
     * @param CouponContract $coupon
119
     * @param Model          $redeemer
120
     * @param Model|null     $redeemed
121
     *
122
     * @return CouponContract
123
     */
124 18
    public function applyCoupon(CouponContract $coupon, Model $redeemer, ?Model $redeemed): CouponContract
125
    {
126 18
        rescue(function () use ($coupon, $redeemer, $redeemed) {
127 18
            call($redeemer)->coupons()->attach($coupon, [
128 17
                $this->pivot->getRedeemedAtColumn()   => now(),
129 17
                $this->pivot->getRedeemedTypeColumn() => $redeemed?->getMorphClass(),
130 17
                $this->pivot->getRedeemedIdColumn()   => $redeemed?->id,
131
            ]);
132
133 17
            if (! is_null($coupon->{$this->model->getQuantityColumn()})) {
134 3
                $coupon->decrement($this->model->getQuantityColumn());
0 ignored issues
show
Bug introduced by
The method decrement() does not exist on MichaelRubel\Couponables...ontracts\CouponContract. Since it exists in all sub-types, consider adding an abstract or default implementation to MichaelRubel\Couponables...ontracts\CouponContract. ( Ignorable by Annotation )

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

134
                $coupon->/** @scrutinizer ignore-call */ 
135
                         decrement($this->model->getQuantityColumn());
Loading history...
135
            }
136 18
        }, function ($e) use ($coupon, $redeemer, $redeemed) {
137 1
            event(new FailedToRedeemCoupon($coupon, $redeemer, $redeemed));
138
139 1
            throw $e;
140
        });
141
142 17
        event(new CouponRedeemed($coupon, $redeemer));
143
144 17
        return $coupon;
145
    }
146
}
147