Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
87.90% |
109 / 124 |
|
76.92% |
10 / 13 |
CRAP | |
0.00% |
0 / 1 |
SlotList | |
87.90% |
109 / 124 |
|
76.92% |
10 / 13 |
57.97 | |
0.00% |
0 / 1 |
takeLowerSlotValue | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
5 | |||
setEmptySlotValues | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
2 | |||
isAvailableForAll | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
3 | |||
getByDateTime | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
4 | |||
withSlotsForAppointment | |
94.74% |
18 / 19 |
|
0.00% |
0 / 1 |
9.01 | |||
extendList | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
2 | |||
removeAppointment | |
75.00% |
12 / 16 |
|
0.00% |
0 / 1 |
7.77 | |||
getSlot | |
100.00% |
4 / 4 |
|
100.00% |
1 / 1 |
2 | |||
getSummerizedSlot | |
100.00% |
7 / 7 |
|
100.00% |
1 / 1 |
3 | |||
withTimeGreaterThan | |
100.00% |
9 / 9 |
|
100.00% |
1 / 1 |
3 | |||
getFreeProcesses | |
100.00% |
24 / 24 |
|
100.00% |
1 / 1 |
7 | |||
withReducedSlots | |
100.00% |
10 / 10 |
|
100.00% |
1 / 1 |
6 | |||
__toString | |
100.00% |
3 / 3 |
|
100.00% |
1 / 1 |
1 |
1 | <?php |
2 | |
3 | namespace BO\Zmsentities\Collection; |
4 | |
5 | use BO\Zmsentities\Slot; |
6 | |
7 | /** |
8 | * @SuppressWarnings(Complexity) |
9 | */ |
10 | class 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 | } |