Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
1 / 1
Shift
100.00% covered (success)
100.00%
40 / 40
100.00% covered (success)
100.00%
12 / 12
23
100.00% covered (success)
100.00%
1 / 1
 neededAngelTypes
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 schedule
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 scheduleShift
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shiftEntries
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 shiftType
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 location
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 createdBy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 updatedBy
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 nextShift
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 previousShift
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
2
 isNightShift
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
9
 getNightShiftMultiplier
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
2
1<?php
2
3declare(strict_types=1);
4
5namespace Engelsystem\Models\Shifts;
6
7use Carbon\Carbon;
8use Engelsystem\Models\BaseModel;
9use Engelsystem\Models\Location;
10use Engelsystem\Models\User\User;
11use Illuminate\Database\Eloquent\Collection;
12use Illuminate\Database\Eloquent\Factories\HasFactory;
13use Illuminate\Database\Eloquent\Relations\BelongsTo;
14use Illuminate\Database\Eloquent\Relations\HasMany;
15use Illuminate\Database\Eloquent\Relations\HasOne;
16use Illuminate\Database\Eloquent\Relations\HasOneThrough;
17use Illuminate\Database\Query\Builder as QueryBuilder;
18
19/**
20 * @property int                               $id
21 * @property string                            $title
22 * @property string                            $description
23 * @property string                            $url
24 * @property Carbon                            $start
25 * @property Carbon                            $end
26 * @property int                               $shift_type_id
27 * @property int                               $location_id
28 * @property string|null                       $transaction_id
29 * @property int                               $created_by
30 * @property int|null                          $updated_by
31 * @property Carbon|null                       $created_at
32 * @property Carbon|null                       $updated_at
33 *
34 * @property-read Collection|NeededAngelType[] $neededAngelTypes
35 * @property-read Schedule                     $schedule
36 * @property-read ScheduleShift                $scheduleShift
37 * @property-read Collection|ShiftEntry[]      $shiftEntries
38 * @property-read ShiftType                    $shiftType
39 * @property-read Location                     $location
40 * @property-read User                         $createdBy
41 * @property-read User|null                    $updatedBy
42 *
43 * @method static QueryBuilder|Shift[] whereId($value)
44 * @method static QueryBuilder|Shift[] whereTitle($value)
45 * @method static QueryBuilder|Shift[] whereDescription($value)
46 * @method static QueryBuilder|Shift[] whereUrl($value)
47 * @method static QueryBuilder|Shift[] whereStart($value)
48 * @method static QueryBuilder|Shift[] whereEnd($value)
49 * @method static QueryBuilder|Shift[] whereShiftTypeId($value)
50 * @method static QueryBuilder|Shift[] whereLocationId($value)
51 * @method static QueryBuilder|Shift[] whereTransactionId($value)
52 * @method static QueryBuilder|Shift[] whereCreatedBy($value)
53 * @method static QueryBuilder|Shift[] whereUpdatedBy($value)
54 * @method static QueryBuilder|Shift[] whereCreatedAt($value)
55 * @method static QueryBuilder|Shift[] whereUpdatedAt($value)
56 */
57class Shift extends BaseModel
58{
59    use HasFactory;
60
61    /** @var array<string, string|null> default attributes */
62    protected $attributes = [ // phpcs:ignore
63        'description'    => '',
64        'url'            => '',
65        'transaction_id' => null,
66        'updated_by'     => null,
67    ];
68
69    /** @var bool enable timestamps */
70    public $timestamps = true; // phpcs:ignore
71
72    /** @var array<string, string> */
73    protected $casts = [ // phpcs:ignore
74        'shift_type_id' => 'integer',
75        'location_id'   => 'integer',
76        'created_by'    => 'integer',
77        'updated_by'    => 'integer',
78        'start'         => 'datetime',
79        'end'           => 'datetime',
80    ];
81
82    /** @var array<string> Values that are mass assignable */
83    protected $fillable = [ // phpcs:ignore
84        'title',
85        'description',
86        'url',
87        'start',
88        'end',
89        'shift_type_id',
90        'location_id',
91        'transaction_id',
92        'created_by',
93        'updated_by',
94    ];
95
96    public function neededAngelTypes(): HasMany
97    {
98        return $this->hasMany(NeededAngelType::class);
99    }
100
101    public function schedule(): HasOneThrough
102    {
103        return $this->hasOneThrough(Schedule::class, ScheduleShift::class, null, 'id', null, 'schedule_id');
104    }
105
106    public function scheduleShift(): HasOne
107    {
108        return $this->hasOne(ScheduleShift::class);
109    }
110
111    public function shiftEntries(): HasMany
112    {
113        return $this->hasMany(ShiftEntry::class);
114    }
115
116    public function shiftType(): BelongsTo
117    {
118        return $this->belongsTo(ShiftType::class);
119    }
120
121    public function location(): BelongsTo
122    {
123        return $this->belongsTo(Location::class);
124    }
125
126    public function createdBy(): BelongsTo
127    {
128        return $this->belongsTo(User::class, 'created_by');
129    }
130
131    public function updatedBy(): BelongsTo
132    {
133        return $this->belongsTo(User::class, 'updated_by');
134    }
135
136    /**
137     * get next shift with same shift type and location
138     */
139    public function nextShift(): Shift|null
140    {
141        $query = Shift::query();
142        if (Shift::whereTitle($this->title)->where('start', '>', $this->start)->exists()) {
143            $query = $query->where('title', $this->title);
144        }
145        return $query
146            ->where('shift_type_id', $this->shiftType->id)
147            ->where('location_id', $this->location->id)
148            ->where('start', '>', $this->start)
149            ->orderBy('start')
150            ->first();
151    }
152
153    /**
154     * get previous shift with same shift type and location
155     */
156    public function previousShift(): Shift|null
157    {
158        $query = Shift::query();
159        if (Shift::whereTitle($this->title)->where('end', '<', $this->end)->exists()) {
160            $query = $query->where('title', $this->title);
161        }
162        return $query
163            ->where('shift_type_id', $this->shiftType->id)
164            ->where('location_id', $this->location->id)
165            ->where('end', '<', $this->end)
166            ->orderBy('end', 'desc')
167            ->first();
168    }
169
170    /**
171     * Check if the shift is a night shift
172     */
173    public function isNightShift(): bool
174    {
175        $config = config('night_shifts');
176
177        /** @see \Engelsystem\Helpers\Goodie::shiftScoreQuery to keep them in sync */
178        return $config['enabled'] && (
179                // Starts during night
180                $this->start->hour >= $config['start'] && $this->start->hour < $config['end']
181                // Ends during night
182                || (
183                    $this->end->hour > $config['start']
184                    || $this->end->hour == $config['start'] && $this->end->minute > 0
185                ) && $this->end->hour <= $config['end']
186                // Starts before and ends after night
187                || $this->start->hour <= $config['start'] && $this->end->hour >= $config['end']
188            );
189    }
190
191    /**
192     * Calculate the shifts night multiplier
193     */
194    public function getNightShiftMultiplier(): float
195    {
196        if (!$this->isNightShift()) {
197            return 1;
198        }
199
200        return config('night_shifts')['multiplier'];
201    }
202}