Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.70% covered (warning)
87.70%
107 / 122
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
SlotList
87.70% covered (warning)
87.70%
107 / 122
76.92% covered (warning)
76.92%
10 / 13
58.22
0.00% covered (danger)
0.00%
0 / 1
 takeLowerSlotValue
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
5
 setEmptySlotValues
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
2
 isAvailableForAll
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
3
 getByDateTime
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
4
 withSlotsForAppointment
94.74% covered (success)
94.74%
18 / 19
0.00% covered (danger)
0.00%
0 / 1
9.01
 extendList
0.00% covered (danger)
0.00%
0 / 10
0.00% covered (danger)
0.00%
0 / 1
2
 removeAppointment
75.00% covered (warning)
75.00%
12 / 16
0.00% covered (danger)
0.00%
0 / 1
7.77
 getSlot
100.00% covered (success)
100.00%
4 / 4
100.00% covered (success)
100.00%
1 / 1
2
 getSummerizedSlot
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 withTimeGreaterThan
100.00% covered (success)
100.00%
9 / 9
100.00% covered (success)
100.00%
1 / 1
3
 getFreeProcesses
100.00% covered (success)
100.00%
24 / 24
100.00% covered (success)
100.00%
1 / 1
7
 withReducedSlots
100.00% covered (success)
100.00%
10 / 10
100.00% covered (success)
100.00%
1 / 1
6
 __toString
100.00% covered (success)
100.00%
3 / 3
100.00% covered (success)
100.00%
1 / 1
1
1<?php
2
3namespace BO\Zmsentities\Collection;
4
5use BO\Zmsentities\Slot;
6
7/**
8 * @SuppressWarnings(Complexity)
9 */
10class SlotList extends Base
11{
12    public const ENTITY_CLASS = '\BO\Zmsentities\Slot';
13
14    /**
15     * Compare two slots and return the lower values
16     * @param array $slotA
17     * @param array $slotB
18     * @return array $slotA modified
19     */
20    public function takeLowerSlotValue($indexA, $indexB)
21    {
22        $slotA = $this[$indexA];
23        $slotB = $this[$indexB];
24        if (null !== $slotA && null !== $slotB) {
25            $slotA->type = Slot::REDUCED;
26            foreach (['public', 'intern'] as $type) {
27                $slotA[$type] = $slotA[$type] < $slotB[$type] ? $slotA[$type] : $slotB[$type];
28            }
29        }
30        return $this;
31    }
32
33    public function setEmptySlotValues($index)
34    {
35        $slot = $this->getSlot($index);
36        if (null !== $slot) {
37            $slot['public'] = 0;
38            $slot['intern'] = 0;
39            $slot->type = Slot::REDUCED;
40        }
41        return $this;
42    }
43
44    public function isAvailableForAll($slotType)
45    {
46        foreach ($this as $slot) {
47            if ($slot[$slotType] < 1) {
48                return false;
49            }
50        }
51        return true;
52    }
53
54    /**
55     * Get a slot for a given time
56     *
57     */
58    public function getByDateTime(\DateTimeInterface $dateTime)
59    {
60        foreach ($this as $slot) {
61            if ($slot->hasTime() && $slot->time == $dateTime->format('H:i')) {
62                return $slot;
63            }
64        }
65        return false;
66    }
67
68    /**
69     * Get all slots for an appointment
70     *
71     */
72    public function withSlotsForAppointment(\BO\Zmsentities\Appointment $appointment, $extendSlotList = false)
73    {
74        $slotList = new SlotList();
75        $takeFollowingSlot = 0;
76        $startTime = $appointment->toDateTime();
77        $currentSlot = null;
78        foreach ($this as $slot) {
79            $currentSlot = clone $slot;
80            if ($takeFollowingSlot > 0) {
81                $takeFollowingSlot--;
82                $slotList[] = $currentSlot;
83            }
84            if ($slot->hasTime() && $slot->time == $startTime->format('H:i')) {
85                $slotList[] = $currentSlot;
86                $takeFollowingSlot = $appointment['slotCount'] - 1;
87            }
88        }
89        if (0 < $takeFollowingSlot && ! $extendSlotList) {
90            throw new \BO\Zmsentities\Exception\AppointmentNotFitInSlotList(
91                "$appointment does not fit in $this"
92            );
93        }
94        if (0 < $takeFollowingSlot && $extendSlotList) {
95            $slotList = $this->extendList($slotList, $currentSlot, $appointment);
96        }
97        return $slotList;
98    }
99
100    public function extendList($slotList, $prevSlot, $appointment)
101    {
102        $startTime = \BO\Zmsentities\Helper\DateTime::create(
103            $appointment->toDateTime()->format('Y-m-d') . ' ' . $prevSlot->time
104        )->modify('+' . $appointment->getAvailability()->slotTimeInMinutes . 'minute');
105        $stopTime = $appointment->getEndTime();
106        do {
107            $slot = clone $prevSlot;
108            $slot->setTime($startTime);
109            $slotList[] = $slot;
110            $startTime = $startTime->modify('+' . $appointment->getAvailability()->slotTimeInMinutes . 'minute');
111            // Only add a slot, if at least a minute is left, otherwise do not ("<" instead "<=")
112        } while ($startTime->getTimestamp() < $stopTime->getTimestamp());
113        return $slotList;
114    }
115
116
117    /**
118     * Reduce free appointments on slot matching appointment
119     *
120     * -----------|----------------|------------
121     * first loop | intern slots 2 | slotCount 3
122     *
123     *
124     * @return bool true on success and false if no matching slot is found or no appointments are free
125     */
126
127
128    public function removeAppointment(\BO\Zmsentities\Appointment $appointment)
129    {
130        $takeFollowingSlot = 0;
131        $startTime = $appointment->toDateTime()->format('H:i');
132        $containsAppointment = false;
133        //error_log('check: ' . $appointment);
134        foreach ($this as $slot) {
135            if ($takeFollowingSlot > 0) {
136                if (0 == $slot['intern']) {
137                    //error_log('false 2: ' . $slot);
138                    return false;
139                }
140                $slot->removeAppointment();
141                $takeFollowingSlot--;
142                //error_log('intern 2: ' . $slot . ' | following: ' . $takeFollowingSlot);
143            }
144            if ($slot->hasTime() && $slot->time == $startTime) {
145                $takeFollowingSlot = $appointment['slotCount'] - 1;
146                //error_log('intern: ' . $slot . ' | following: ' . $takeFollowingSlot);
147                if (0 < $slot['intern']) {
148                    $containsAppointment = true;
149                    $slot->removeAppointment();
150                } else {
151                    //error_log('false: ' . $slot);
152                    return false;
153                }
154            }
155        }
156        return $containsAppointment;
157    }
158
159    public function getSlot($index)
160    {
161        $index = intval($index);
162        if (!isset($this[$index])) {
163            return null;
164        }
165        return $this[$index];
166    }
167
168    public function getSummerizedSlot($slot = null)
169    {
170        $sum = ($slot instanceof Slot) ? $slot : new Slot();
171        $sum->type = Slot::SUM;
172        foreach ($this as $slot) {
173            //error_log("$slot");
174            $sum['public'] += $slot['public'];
175            $sum['intern'] += $slot['intern'];
176        }
177        return $sum;
178    }
179
180    /*
181     * reduce slotlist from slots smaller than reference time (today) + 1800 seconds
182     */
183    public function withTimeGreaterThan(\DateTimeInterface $dateTime)
184    {
185        $slotList = clone $this;
186        $referenceTime = $dateTime->getTimestamp() + 1800;
187        foreach ($this as $index => $slot) {
188            $slotTime = \BO\Zmsentities\Helper\DateTime::create(
189                $dateTime->format('Y-m-d') . ' ' . $slot->time
190            )->getTimeStamp();
191            if ($referenceTime > $slotTime) {
192                $slotList->setEmptySlotValues($index);
193            }
194        }
195        return $slotList;
196    }
197
198    public function getFreeProcesses(
199        $selectedDate,
200        \BO\Zmsentities\Scope $scope,
201        \BO\Zmsentities\Availability $availability,
202        $slotType,
203        $requests,
204        $slotsRequired
205    ) {
206        $processList = new ProcessList();
207        foreach ($this as $slot) {
208            if ($slotsRequired > 1 && $slot->type != Slot::REDUCED) {
209                throw new \BO\Zmsentities\Exception\SlotRequiredWithoutReducing(
210                    "With $slotsRequired slots required, "
211                    . "do not use SlotList::getFreeProcesses without reduced slots: $slot"
212                );
213            }
214            if ($slot[$slotType] > 0) {
215                $appointment = new \BO\Zmsentities\Appointment(array(
216                    'scope' => $scope,
217                    'availability' => $availability,
218                    'slotCount' => $slotsRequired
219                ));
220                if (!$slot->hasTime()) {
221                    throw new \BO\Zmsentities\Exception\SlotMissingTime("Time on slot not set: $slot");
222                }
223                $appointment->setDateByString($selectedDate . ' ' . $slot->getTimeString());
224                $process = new \BO\Zmsentities\Process(array(
225                    'scope' => $scope,
226                    'requests' => $requests
227                ));
228                for ($count = 0; $count < $slot[$slotType]; $count++) {
229                    $process->addAppointment($appointment);
230                }
231                $processList[] = $process;
232            }
233        }
234        return $processList;
235    }
236
237    public function withReducedSlots($slotsRequired)
238    {
239        $slotList = clone $this;
240        if ($slotsRequired > 1) {
241            $slotLength = count($slotList);
242            for ($slotIndex = 0; $slotIndex < $slotLength; $slotIndex++) {
243                if ($slotIndex + $slotsRequired - 1 < $slotLength) {
244                    for ($slotRelative = 1; $slotRelative < $slotsRequired; $slotRelative++) {
245                        if ($slotIndex + $slotRelative < $slotLength) {
246                            $slotList->takeLowerSlotValue($slotIndex, $slotIndex + $slotRelative);
247                        }
248                    }
249                } else {
250                    $slotList->setEmptySlotValues($slotIndex);
251                }
252            }
253        }
254        return $slotList;
255    }
256
257    public function __toString()
258    {
259        $count = count($this);
260        $sum = $this->getSummerizedSlot();
261        return "slotlist#$count ∑$sum";
262    }
263}