Test Failed
Pull Request — main (#21)
by
unknown
12:07
created

Coupon::isEnabled()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
ccs 1
cts 1
cp 1
rs 10
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace MichaelRubel\Couponables\Models;
6
7
use Illuminate\Database\Eloquent\Factories\HasFactory;
8
use Illuminate\Database\Eloquent\Model;
9
use MichaelRubel\Couponables\Models\Contracts\CouponContract;
10
use MichaelRubel\Couponables\Models\Traits\DefinesColumnChecks;
11
use MichaelRubel\Couponables\Models\Traits\DefinesColumns;
12
use MichaelRubel\Couponables\Models\Traits\DefinesModelRelations;
13
use MichaelRubel\Couponables\Traits\Concerns\CalculatesCosts;
14
use MichaelRubel\EnhancedContainer\Core\CallProxy;
15
16
class Coupon extends Model implements CouponContract
17
{
18
    use HasFactory;
19
    use DefinesColumns;
20
    use DefinesColumnChecks;
21
    use DefinesModelRelations;
22
    use CalculatesCosts;
23
24
    /**
25
     * The attributes that aren't mass assignable.
26
     *
27
     * @var array<string>|bool
28
     */
29
    protected $guarded = [];
30
31
    /**
32
     * The attributes that should be cast.
33
     *
34
     * @var array<string, string>
35
     */
36
    protected $casts = [
37
        'code'       => 'string',
38
        'type'       => 'string',
39
        'value'      => 'string',
40
        'data'       => 'collection',
41
        'quantity'   => 'integer',
42
        'limit'      => 'integer',
43
        'expires_at' => 'datetime',
44
        'is_enable'  => 'boolean',
45
    ];
46
47
    /**
48
     * @var CallProxy
49
     */
50
    protected static CallProxy $bindable;
51
52
    /**
53
     * @param  array  $attributes
54 71
     */
55
    public function __construct(array $attributes = [])
56 71
    {
57
        parent::__construct($attributes);
58 71
59
        $this->table = config('couponables.table', 'coupons');
60 71
61
        static::$bindable = call($this);
62
    }
63
64
    /**
65
     * Check if code is expired.
66
     *
67
     * @return bool
68 33
     */
69
    public function isExpired(): bool
70 33
    {
71
        $expires_at = $this->{static::$bindable->getExpiresAtColumn()};
72 33
73
        return $expires_at && now()->gte($expires_at);
74
    }
75
76
    /**
77
     * Check if code is not expired.
78
     *
79
     * @return bool
80 1
     */
81
    public function isNotExpired(): bool
82 1
    {
83
        return ! static::$bindable->isExpired();
84
    }
85
86
    /**
87
     * Check if code is enabled.
88
     *
89
     * @return bool
90 29
     */
91
    public function isEnabled(): bool
92 29
    {
93
        return $this->is_enable;
0 ignored issues
show
Bug introduced by
The property is_enable does not seem to exist on MichaelRubel\Couponables\Models\Coupon. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
94 29
    }
95
96
    /**
97
     * Check if code is disabled.
98
     *
99
     * @return bool
100
     */
101
    public function isDisabled(): bool
102 29
    {
103
        return ! static::$bindable->isEnabled();
104 29
    }
105
106 29
    /**
107
     * Check if code amount is over.
108
     *
109
     * @return bool
110
     */
111
    public function isOverQuantity(): bool
112
    {
113
        $quantity = $this->{static::$bindable->getQuantityColumn()};
114
115
        return ! is_null($quantity) && $quantity <= 0;
116
    }
117 29
118
    /**
119 29
     * Check if coupon is disposable.
120 29
     *
121
     * @return bool
122
     */
123
    public function isDisposable(): bool
124
    {
125
        $limit = $this->{static::$bindable->getLimitColumn()};
126
127
        return ! is_null($limit) && $limit == 1;
128
    }
129
130 29
    /**
131
     * Check if the code is reached its global limit.
132 29
     *
133 29
     * @param  Model  $redeemer
134
     * @param  string|null  $code
135 29
     *
136 29
     * @return bool
137 29
     */
138 29
    public function isOverLimit(Model $redeemer, ?string $code): bool
139
    {
140
        return (static::$bindable->isDisposable() && call($redeemer)->isCouponAlreadyUsed($code))
141
            || static::$bindable->isOverLimitFor($redeemer);
142
    }
143
144
    /**
145
     * Check if the code is reached its limit for the passed model.
146
     *
147
     * @param  Model  $redeemer
148 2
     *
149
     * @return bool
150 2
     */
151
    public function isOverLimitFor(Model $redeemer): bool
152 2
    {
153
        $column = static::$bindable->getCodeColumn();
154
        $limit  = $this->{static::$bindable->getLimitColumn()};
155
156
        return ! is_null($limit) && $limit <= $redeemer
157
            ->coupons()
158
            ->where($column, $this->{$column})
159
            ->count();
160
    }
161
162 30
    /**
163
     * Check if coupon is already redeemed by the model.
164 30
     *
165 30
     * @param  Model  $redeemer
166 4
     *
167
     * @return bool
168
     */
169 26
    public function isRedeemedBy(Model $redeemer): bool
170 1
    {
171
        $column = static::$bindable->getCodeColumn();
172
173 25
        return $redeemer->coupons()->where($column, $this->{$column})->exists();
174 30
    }
175
176
    /**
177
     * Check if the model is allowed to redeem.
178
     *
179
     * @param  Model  $redeemer
180
     *
181
     * @return bool
182
     */
183
    public function isAllowedToRedeemBy(Model $redeemer): bool
184 1
    {
185
        return with(static::$bindable, function ($coupon) use ($redeemer) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return with(static::bind...ion(...) { /* ... */ }) could return the type MichaelRubel\EnhancedContainer\Core\CallProxy which is incompatible with the type-hinted return boolean. Consider adding an additional type-check to rule them out.
Loading history...
186 1
            if ($coupon->isMorphColumnsFilled() && ! $coupon->redeemer()->is($redeemer)) {
187 1
                return false;
188 1
            }
189 1
190 1
            if ($coupon->isOnlyRedeemerTypeFilled() && ! $coupon->isSameRedeemerModel($redeemer)) {
191
                return false;
192 1
            }
193
194 1
            return true;
195 1
        });
196
    }
197
198
    /**
199
     * Assign the model to the latest redeemed coupon.
200
     *
201
     * @param  Model  $redeemed
202
     *
203
     * @return CouponContract
204
     */
205
    public function for(Model $redeemed): CouponContract
206
    {
207
        return with($this->couponables()->first(), function ($couponable) use ($redeemed) {
208
            $morphValues = transform($couponable, fn ($bindable) => [
209
                $bindable->getRedeemedTypeColumn() => $redeemed->getMorphClass(),
210
                $bindable->getRedeemedIdColumn()   => $redeemed->id,
211
            ]);
212
213
            $couponable->update($morphValues);
214
215
            return $this;
216
        });
217
    }
218
}
219