Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
61.11% covered (warning)
61.11%
77 / 126
66.67% covered (warning)
66.67%
14 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
AvailabilityList
61.11% covered (warning)
61.11%
77 / 126
66.67% covered (warning)
66.67%
14 / 21
288.08
0.00% covered (danger)
0.00%
0 / 1
 getMaxWorkstationCount
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 withCalculatedSlots
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 withType
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 withOutDoubles
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 hasMatchOf
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 withDateTime
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 withDateTimeInRange
0.00% covered (danger)
0.00%
0 / 8
0.00% covered (danger)
0.00%
0 / 1
20
 getAvailableSecondsOnDateTime
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 isOpenedByDate
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 isOpened
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 hasAppointment
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getSlotList
100.00% covered (success)
100.00%
5 / 5
100.00% covered (success)
100.00%
1 / 1
3
 getSlotListByType
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
20
 getConflicts
100.00% covered (success)
100.00%
18 / 18
100.00% covered (success)
100.00%
1 / 1
6
 hasOverlapWith
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 hasDayOffOverride
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
12
 validateTimeRangesAndRules
0.00% covered (danger)
0.00%
0 / 16
0.00% covered (danger)
0.00%
0 / 1
6
 getSummerizedSlotCount
0.00% covered (danger)
0.00%
0 / 5
0.00% covered (danger)
0.00%
0 / 1
12
 getCalculatedSlotCount
0.00% covered (danger)
0.00%
0 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 withScope
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 withLessData
0.00% covered (danger)
0.00%
0 / 4
0.00% covered (danger)
0.00%
0 / 1
6
1<?php
2
3/**
4 * @copyright BerlinOnline Stadtportal GmbH & Co. KG
5 **/
6
7namespace BO\Zmsentities\Collection;
8
9use BO\Zmsentities\Availability;
10
11/**
12 * @SuppressWarnings(Complexity)
13 * @extends Base<\BO\Zmsentities\Availability>
14 */
15class AvailabilityList extends Base
16{
17    public const string ENTITY_CLASS = '\BO\Zmsentities\Availability';
18
19    public function getMaxWorkstationCount(): mixed
20    {
21        $max = 0;
22        foreach ($this as $availability) {
23            if ($availability['workstationCount']['intern'] >  $max) {
24                $max = $availability['workstationCount']['intern'];
25            }
26        }
27        return $max;
28    }
29
30    public function withCalculatedSlots(): static
31    {
32        $list = clone $this;
33        foreach ($list as $key => $availability) {
34            $list[$key] = $availability->withCalculatedSlots();
35        }
36        return $list;
37    }
38
39    public function withType(string $type): static
40    {
41        $collection = new static();
42        foreach ($this as $availability) {
43            if ($availability->type == $type) {
44                $collection[] = clone $availability;
45            }
46        }
47        return $collection;
48    }
49
50    public function withOutDoubles(): static
51    {
52        $collection = new static();
53        foreach ($this as $availability) {
54            if (false === $collection->hasMatchOf($availability)) {
55                $collection[] = clone $availability;
56            }
57        }
58        return $collection;
59    }
60
61    public function hasMatchOf(Availability $availability): Availability|false
62    {
63        foreach ($this as $item) {
64            if ($item->isMatchOf($availability)) {
65                return $item;
66            }
67        }
68        return false;
69    }
70
71    public function withDateTime(\DateTimeInterface $dateTime): self
72    {
73        $list = new self();
74        foreach ($this as $availability) {
75            if ($availability->isOpenedOnDate($dateTime)) {
76                $list->addEntity($availability);
77            }
78        }
79        return $list;
80    }
81
82    public function withDateTimeInRange(\DateTimeInterface $startDateTime, \DateTimeInterface $endDateTime): mixed
83    {
84        $list = new self();
85        $currentDateTime = \BO\Zmsentities\Helper\DateTime::create($startDateTime);
86        while ($currentDateTime <= $endDateTime) {
87            foreach ($this as $availability) {
88                if ($availability->isOpenedOnDate($currentDateTime)) {
89                    $list->addEntity($availability);
90                }
91            }
92            $currentDateTime = $currentDateTime->modify('+1 day');
93        }
94        return $list->withOutDoubles();
95    }
96
97    public function getAvailableSecondsOnDateTime(\DateTimeInterface $dateTime, string $type = "intern"): mixed
98    {
99        $seconds = 0;
100        foreach ($this->withType('appointment')->withDateTime($dateTime) as $availability) {
101            $seconds += $availability->getAvailableSecondsPerDay($type);
102        }
103        return $seconds;
104    }
105
106    /*
107     * is opened on a day -> not specified by a time
108     */
109    public function isOpenedByDate(\DateTimeInterface $dateTime, string|false $type = false): bool
110    {
111        foreach ($this as $availability) {
112            if ($availability->isOpenedOnDate($dateTime, $type)) {
113                return true;
114            }
115        }
116        return false;
117    }
118
119    /*
120     * is opened on a day with specified time
121     */
122    public function isOpened(\DateTimeInterface $dateTime, string $type = "openinghours"): bool
123    {
124        foreach ($this as $availability) {
125            if ($availability->isOpened($dateTime, $type)) {
126                return true;
127            }
128        }
129        return false;
130    }
131
132    public function hasAppointment(\BO\Zmsentities\Appointment $appointment): bool
133    {
134        foreach ($this as $availability) {
135            if ($availability->hasAppointment($appointment)) {
136                return true;
137            }
138        }
139        return false;
140    }
141
142    public function getSlotList(): SlotList
143    {
144        $slotList = new SlotList();
145        foreach ($this as $availability) {
146            foreach ($availability->getSlotList() as $slot) {
147                $slotList->addEntity($slot);
148            }
149        }
150        return $slotList;
151    }
152
153    public function getSlotListByType(mixed $type): SlotList
154    {
155        $slotList = new SlotList();
156        foreach ($this as $availability) {
157            if ($availability->type == $type) {
158                foreach ($availability->getSlotList() as $slot) {
159                    $slotList->addEntity($slot);
160                }
161            }
162        }
163        return $slotList;
164    }
165
166    public function getConflicts(mixed $startDate, mixed $endDate): ProcessList
167    {
168        $processList = new ProcessList();
169        foreach ($this as $availability) {
170            $conflict = $availability->getConflict();
171            $currentDate = \BO\Zmsentities\Helper\DateTime::create($startDate);
172            $endDateTime = \BO\Zmsentities\Helper\DateTime::create($endDate);
173            while ($currentDate <= $endDateTime) {
174                if ($availability->isOpenedOnDate($currentDate)) {
175                    if ($conflict) {
176                        $conflictOnDay = clone $conflict;
177                        // to avoid overwrite time settings from availability getConflict lets modify
178                        $appointmentTime = $conflictOnDay->getFirstAppointment()->getStartTime()->format('H:i');
179                        $newDate = \BO\Zmsentities\Helper\DateTime::create($currentDate);
180                        $conflictOnDay->getFirstAppointment()->setDateTime($newDate->modify($appointmentTime));
181                        $processList->addEntity($conflictOnDay);
182                    }
183                    $overlapList = $this->hasOverlapWith($availability, $currentDate);
184                    if ($overlapList->count()) {
185                        $processList->addList($overlapList);
186                    }
187                }
188                $currentDate = $currentDate->modify('+1day');
189            }
190        }
191        return $processList;
192    }
193
194    public function hasOverlapWith(Availability $availability, \DateTimeInterface $currentDate): ProcessList
195    {
196        $processList = new ProcessList();
197        foreach ($this as $availabilityCompare) {
198            if ($availabilityCompare->isOpenedOnDate($currentDate)) {
199                $overlaps = $availability->getTimeOverlaps($availabilityCompare, $currentDate);
200                $processList->addList($overlaps);
201            }
202        }
203        return $processList;
204    }
205
206    public function hasDayOffOverride(): bool
207    {
208        foreach ($this as $availability) {
209            if ($availability->overridesDayOff()) {
210                return true;
211            }
212        }
213        return false;
214    }
215
216    public function validateTimeRangesAndRules(
217        \DateTimeImmutable $startDate,
218        \DateTimeImmutable $endDate,
219        \DateTimeImmutable $selectedDate,
220        string $kind,
221        mixed $startInDays,
222        mixed $endInDays,
223        array $weekday
224    ): array {
225        $errorList = [];
226
227        $today = new \DateTimeImmutable('now', $selectedDate->getTimezone());
228        $yesterday = (clone $selectedDate)->modify('-1 day');
229        $tomorrow = (clone $selectedDate)->modify('+1 day');
230
231        foreach ($this as $availability) {
232            $errorList = array_merge(
233                $errorList,
234                $availability->validateWeekdays($startDate, $endDate, $weekday, $kind),
235                $availability->validateStartTime($today, $tomorrow, $startDate, $selectedDate, $kind),
236                $availability->validateEndTime($startDate, $endDate),
237                $availability->validateOriginEndTime($today, $yesterday, $endDate, $selectedDate, $kind),
238                $availability->validateType($kind),
239                $availability->validateSlotTime($startDate, $endDate),
240                $availability->validateBookableDayRange((int) $startInDays, (int) $endInDays)
241            );
242        }
243        return $errorList;
244    }
245
246    /**
247     * @return array
248     */
249    public function getSummerizedSlotCount(): array
250    {
251        $slotCounts = [];
252        foreach ($this as $item) {
253            $itemId = ($item->id) ? $item->id : $item->tempId;
254            $slotCounts[$itemId] = (int) $item->getSlotList()->getSummerizedSlot()->intern;
255        }
256        return $slotCounts;
257    }
258
259    /**
260     * @return array
261     */
262    public function getCalculatedSlotCount(\BO\Zmsentities\Collection\ProcessList $processList): array
263    {
264        $slotCounts = [];
265        foreach ($this as $item) {
266            $itemId = $item->id;
267            $listWithAvailability = $processList->withAvailability($item);
268            $slotCounts[$itemId] = $listWithAvailability->getAppointmentList()->getCalculatedSlotCount();
269        }
270        return $slotCounts;
271    }
272
273
274    public function withScope(\BO\Zmsentities\Scope $scope): static
275    {
276        $list = clone $this;
277        foreach ($list as $key => $availability) {
278            $list[$key] = $availability->withScope($scope);
279        }
280        return $list;
281    }
282
283    /**
284     * @return static
285     */
286    #[\Override]
287    public function withLessData(array $keepArray = [])
288    {
289        /** @psalm-suppress UnsafeGenericInstantiation */
290        $list = new static();
291        foreach ($this as $availability) {
292            $list->addEntity(clone $availability->withLessData($keepArray));
293        }
294        return $list;
295    }
296}