Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
87.90% covered (warning)
87.90%
109 / 124
76.92% covered (warning)
76.92%
10 / 13
CRAP
0.00% covered (danger)
0.00%
0 / 1
SlotList
87.90% covered (warning)
87.90%
109 / 124
76.92% covered (warning)
76.92%
10 / 13
57.97
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%
7 / 7
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%
7 / 7
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    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', 'callcenter'] 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['callcenter'] = 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        //error_log('check: ' . $appointment);
135        foreach ($this as $slot) {
136            if ($takeFollowingSlot > 0) {
137                if (0 == $slot['intern']) {
138                    //error_log('false 2: ' . $slot);
139                    return false;
140                }
141                $slot->removeAppointment();
142                $takeFollowingSlot--;
143                //error_log('intern 2: ' . $slot . ' | following: ' . $takeFollowingSlot);
144            }
145            if ($slot->hasTime() && $slot->time == $startTime) {
146                $takeFollowingSlot = $appointment['slotCount'] - 1;
147                //error_log('intern: ' . $slot . ' | following: ' . $takeFollowingSlot);
148                if (0 < $slot['intern']) {
149                    $containsAppointment = true;
150                    $slot->removeAppointment();
151                } else {
152                    //error_log('false: ' . $slot);
153                    return false;
154                }
155            }
156        }
157        return $containsAppointment;
158    }
159
160    public function getSlot($index)
161    {
162        $index = intval($index);
163        if (!isset($this[$index])) {
164            return null;
165        }
166        return $this[$index];
167    }
168
169    public function getSummerizedSlot($slot = null)
170    {
171        $sum = ($slot instanceof Slot) ? $slot : new Slot();
172        $sum->type = Slot::SUM;
173        foreach ($this as $slot) {
174            //error_log("$slot");
175            $sum['public'] += $slot['public'];
176            $sum['intern'] += $slot['intern'];
177            $sum['callcenter'] += $slot['callcenter'];
178        }
179        return $sum;
180    }
181
182    /*
183     * reduce slotlist from slots smaller than reference time (today) + 1800 seconds
184     */
185    public function withTimeGreaterThan(\DateTimeInterface $dateTime)
186    {
187        $slotList = clone $this;
188        $referenceTime = $dateTime->getTimestamp() + 1800;
189        foreach ($this as $index => $slot) {
190            $slotTime = \BO\Zmsentities\Helper\DateTime::create(
191                $dateTime->format('Y-m-d') . ' ' . $slot->time
192            )->getTimeStamp();
193            if ($referenceTime > $slotTime) {
194                $slotList->setEmptySlotValues($index);
195            }
196        }
197        return $slotList;
198    }
199
200    /**
201     * Creates a ProcessList for free processes
202     *
203     * @param String $selectedDate of format "YYYY-MM-DD"
204     * @param \BO\Zmsentities\Scope $scope
205     * @param \BO\Zmsentities\Availability $availability
206     * @param String $slotType one of "public", "callcenter", "intern"
207     * @param Array $requests to add to process
208     * @param $slotsRequired Number of slots required
209     *
210     * @return ProcessList
211     */
212    public function getFreeProcesses(
213        $selectedDate,
214        \BO\Zmsentities\Scope $scope,
215        \BO\Zmsentities\Availability $availability,
216        $slotType,
217        $requests,
218        $slotsRequired
219    ) {
220        $processList = new ProcessList();
221        foreach ($this as $slot) {
222            if ($slotsRequired > 1 && $slot->type != Slot::REDUCED) {
223                throw new \BO\Zmsentities\Exception\SlotRequiredWithoutReducing(
224                    "With $slotsRequired slots required, "
225                    . "do not use SlotList::getFreeProcesses without reduced slots: $slot"
226                );
227            }
228            if ($slot[$slotType] > 0) {
229                $appointment = new \BO\Zmsentities\Appointment(array(
230                    'scope' => $scope,
231                    'availability' => $availability,
232                    'slotCount' => $slotsRequired
233                ));
234                if (!$slot->hasTime()) {
235                    throw new \BO\Zmsentities\Exception\SlotMissingTime("Time on slot not set: $slot");
236                }
237                $appointment->setDateByString($selectedDate . ' ' . $slot->getTimeString());
238                $process = new \BO\Zmsentities\Process(array(
239                    'scope' => $scope,
240                    'requests' => $requests
241                ));
242                for ($count = 0; $count < $slot[$slotType]; $count++) {
243                    $process->addAppointment($appointment);
244                }
245                $processList[] = $process;
246            }
247        }
248        return $processList;
249    }
250
251    public function withReducedSlots($slotsRequired)
252    {
253        $slotList = clone $this;
254        if ($slotsRequired > 1) {
255            $slotLength = count($slotList);
256            for ($slotIndex = 0; $slotIndex < $slotLength; $slotIndex++) {
257                if ($slotIndex + $slotsRequired - 1 < $slotLength) {
258                    for ($slotRelative = 1; $slotRelative < $slotsRequired; $slotRelative++) {
259                        if ($slotIndex + $slotRelative < $slotLength) {
260                            $slotList->takeLowerSlotValue($slotIndex, $slotIndex + $slotRelative);
261                        }
262                    }
263                } else {
264                    $slotList->setEmptySlotValues($slotIndex);
265                }
266            }
267        }
268        return $slotList;
269    }
270
271    public function __toString()
272    {
273        $count = count($this);
274        $sum = $this->getSummerizedSlot();
275        return "slotlist#$count ∑$sum";
276    }
277}