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