Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
59.84% covered (warning)
59.84%
76 / 127
66.67% covered (warning)
66.67%
14 / 21
CRAP
0.00% covered (danger)
0.00%
0 / 1
AvailabilityList
59.84% covered (warning)
59.84%
76 / 127
66.67% covered (warning)
66.67%
14 / 21
293.13
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%
17 / 17
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 / 6
0.00% covered (danger)
0.00%
0 / 1
6
 getCalculatedSlotCount
0.00% covered (danger)
0.00%
0 / 7
0.00% covered (danger)
0.00%
0 / 1
2
 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 ENTITY_CLASS = '\BO\Zmsentities\Availability';
18
19    public function getMaxWorkstationCount()
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()
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($type)
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()
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)
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)
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)
83    {
84        $list = new self();
85        $currentDateTime = clone $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, $type = "intern")
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, $type = false)
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, $type = "openinghours")
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)
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()
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($type)
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($startDate, $endDate)
167    {
168        $processList = new ProcessList();
169        foreach ($this as $availability) {
170            $conflict = $availability->getConflict();
171            $currentDate = $startDate;
172            while ($currentDate <= $endDate) {
173                if ($availability->isOpenedOnDate($currentDate)) {
174                    if ($conflict) {
175                        $conflictOnDay = clone $conflict;
176                        // to avoid overwrite time settings from availability getConflict lets modify
177                        $appointmentTime = $conflictOnDay->getFirstAppointment()->getStartTime()->format('H:i');
178                        $newDate = clone $currentDate;
179                        $conflictOnDay->getFirstAppointment()->setDateTime($newDate->modify($appointmentTime));
180                        $processList->addEntity($conflictOnDay);
181                    }
182                    $overlapList = $this->hasOverlapWith($availability, $currentDate);
183                    if ($overlapList->count()) {
184                        $processList->addList($overlapList);
185                    }
186                }
187                $currentDate = $currentDate->modify('+1day');
188            }
189        }
190        return $processList;
191    }
192
193    public function hasOverlapWith(Availability $availability, \DateTimeInterface $currentDate)
194    {
195        $processList = new ProcessList();
196        foreach ($this as $availabilityCompare) {
197            if ($availabilityCompare->isOpenedOnDate($currentDate)) {
198                $overlaps = $availability->getTimeOverlaps($availabilityCompare, $currentDate);
199                $processList->addList($overlaps);
200            }
201        }
202        return $processList;
203    }
204
205    public function hasDayOffOverride(): bool
206    {
207        foreach ($this as $availability) {
208            if ($availability->overridesDayOff()) {
209                return true;
210            }
211        }
212        return false;
213    }
214
215    public function validateTimeRangesAndRules(
216        \DateTimeImmutable $startDate,
217        \DateTimeImmutable $endDate,
218        \DateTimeImmutable $selectedDate,
219        string $kind,
220        $startInDays,
221        $endInDays,
222        array $weekday
223    ): array {
224        $errorList = [];
225
226        $today = new \DateTimeImmutable('now', $selectedDate->getTimezone());
227        $yesterday = (clone $selectedDate)->modify('-1 day');
228        $tomorrow = (clone $selectedDate)->modify('+1 day');
229
230        foreach ($this as $availability) {
231            $errorList = array_merge(
232                $errorList,
233                $availability->validateWeekdays($startDate, $endDate, $weekday, $kind),
234                $availability->validateStartTime($today, $tomorrow, $startDate, $selectedDate, $kind),
235                $availability->validateEndTime($startDate, $endDate),
236                $availability->validateOriginEndTime($today, $yesterday, $endDate, $selectedDate, $kind),
237                $availability->validateType($kind),
238                $availability->validateSlotTime($startDate, $endDate),
239                $availability->validateBookableDayRange((int) $startInDays, (int) $endInDays)
240            );
241        }
242        return $errorList;
243    }
244
245    /**
246     * @return integer
247     */
248    public function getSummerizedSlotCount()
249    {
250        return array_reduce($this->getArrayCopy(), function ($carry, $item) {
251            $itemId = ($item->id) ? $item->id : $item->tempId;
252            $maxSlots = (int) $item->getSlotList()->getSummerizedSlot()->intern;
253            $carry[$itemId] = $maxSlots;
254            return $carry;
255        }, []);
256    }
257
258    /**
259     * @return integer
260     */
261    public function getCalculatedSlotCount(\BO\Zmsentities\Collection\ProcessList $processList)
262    {
263        return array_reduce($this->getArrayCopy(), function ($carry, $item) use ($processList) {
264            $itemId = $item->id;
265            $listWithAvailability = $processList->withAvailability($item);
266            $busySlots = $listWithAvailability->getAppointmentList()->getCalculatedSlotCount();
267            $carry[$itemId] = $busySlots;
268            return $carry;
269        }, []);
270    }
271
272
273    public function withScope(\BO\Zmsentities\Scope $scope)
274    {
275        $list = clone $this;
276        foreach ($list as $key => $availability) {
277            $list[$key] = $availability->withScope($scope);
278        }
279        return $list;
280    }
281
282    #[\Override]
283    public function withLessData(array $keepArray = [])
284    {
285        $list = new self();
286        foreach ($this as $availability) {
287            $list->addEntity(clone $availability->withLessData($keepArray));
288        }
289        return $list;
290    }
291}