PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / 1.2.21
VikAppointments Services Booking Calendar v1.2.21
1.2.21 1.2.20 trunk 1.2.17 1.2.18 1.2.19
vikappointments / site / helpers / libraries / availability / timeline / parser / employee.php
vikappointments / site / helpers / libraries / availability / timeline / parser Last commit date
allemployees.php 5 days ago employee.php 5 days ago group.php 5 days ago groupext.php 5 days ago index.html 5 days ago service.php 5 days ago
employee.php
379 lines
1 <?php
2 /**
3 * @package VikAppointments
4 * @subpackage core
5 * @author E4J s.r.l.
6 * @copyright Copyright (C) 2021 E4J s.r.l. All Rights Reserved.
7 * @license http://www.gnu.org/licenses/gpl-2.0.html GNU/GPL
8 * @link https://vikwp.com
9 */
10
11 // No direct access
12 defined('ABSPATH') or die('No script kiddies please!');
13
14 VAPLoader::import('libraries.availability.timeline.parser');
15
16 /**
17 * Employee timeline parser.
18 *
19 * @since 1.7
20 */
21 class VAPAvailabilityTimelineParserEmployee extends VAPAvailabilityTimelineParser
22 {
23 /**
24 * Internal method used by children classes to implement their own logic
25 * to fetch the availability timeline.
26 *
27 * @param string $date The UTC date in military format.
28 * @param integer $people The number of participants.
29 * @param integer $id The selected appointment ID.
30 *
31 * @return mixed The resulting timeline on success, false otherwise.
32 */
33 protected function buildTimeline($date, $people = 1, $id = 0)
34 {
35 // look for a closing day/period
36 if ($this->search->isClosingDay($date))
37 {
38 // the day is closed
39 $this->setError(JText::translate('VAPFINDRESCLOSINGDAY'));
40
41 return false;
42 }
43
44 // check whether the service is published for the given date
45 if (!$this->search->isServicePublished($date))
46 {
47 // service not published
48 $this->setError(JText::translate('VAPFINDRESNODAYSERVICE'));
49
50 return false;
51 }
52
53 // get employee working times
54 $worktimes = $this->search->getWorkingTimes($date);
55
56 if (!$worktimes)
57 {
58 // the employee doesn't work on the specified date
59 $this->setError(JText::translate('VAPFINDRESNODAYEMPLOYEE'));
60
61 return false;
62 }
63
64 // extract service and employee IDs from search object
65 $id_service = (int) $this->search->get('id_service');
66 $id_employee = (int) $this->search->get('id_employee');
67
68 // get service-employee association model
69 $model = JModelVAP::getInstance('serempassoc');
70 // get service details
71 $service = $model->getOverrides($id_service, $id_employee);
72
73 // validate selected number of people (front-end only)
74 if (!$this->search->isAdmin() && $people > $service->max_capacity)
75 {
76 // number of people not supported
77 $this->setError(JText::translate('VAPFINDRESPEOPLENOTVALID'));
78
79 return false;
80 }
81
82 /**
83 * We are looking for the appointments for the current day.
84 * Extend this bounds in order to support midnight reservations.
85 *
86 * Instead having:
87 * 2021-07-09 @ 00:00:00 - 2021-07-09 23:59:59
88 * we need to have:
89 * 2021-07-08 @ 00:00:00 - 2021-07-10 23:59:59
90 *
91 * @since 1.6
92 */
93 $start = JFactory::getDate($date);
94 $start->modify('-1 day 00:00:00');
95
96 $end = JFactory::getDate($date);
97 $end->modify('+1 day 23:59:59');
98
99 // load existing bookings
100 $bookings = $this->search->getReservations($start->toSql(), $end->toSql(), $id);
101
102 // use helper method to elaborate the timeline
103 $arr = $this->elaborateTimeLine($date, $people, $worktimes, $bookings, $service);
104
105 // create timeline
106 $timeline = new VAPAvailabilityTimeline($date, $this->search);
107
108 for ($i = 0; $i < count($arr); $i++)
109 {
110 // create new level
111 $timeline->addLevel();
112
113 foreach ($arr[$i] as $k => $v)
114 {
115 // create check-in date time
116 $checkin = JFactory::getDate($date);
117 $checkin->modify(JHtml::fetch('vikappointments.min2time', $k, $string = true, $format = 'H:i:s'));
118
119 // check whether the time is in the past (preserve them in case of admin)
120 if ($arr[$i][$k] && $this->search->isPastTime($checkin))
121 {
122 // unset block if it is in the past
123 unset($arr[$i][$k]);
124 }
125 else if ($k >= 1440)
126 {
127 /**
128 * Unset the time slots that exceed the midnight.
129 *
130 * @since 1.6
131 */
132 if (!$service->checkout_selection)
133 {
134 /**
135 * Time-slots that exceed the midnight are mandatory for
136 * the checkout selection. Unset them only whether the
137 * "checkout_selection" parameter is turned off.
138 *
139 * @since 1.6.2
140 */
141 unset($arr[$i][$k]);
142 }
143 }
144
145 if (isset($arr[$i][$k]))
146 {
147 $trace = array();
148
149 // add time
150 $timeBlock = $timeline->addTime($k, $arr[$i][$k], $service->duration);
151
152 // calculate rate for the current time block
153 $price = VAPSpecialRates::getRate($id_service, $id_employee, $timeBlock->checkin(), $people, $trace);
154
155 // multiply by the number of guests (if enabled)
156 if ($service->priceperpeople)
157 {
158 $price *= $people;
159 }
160
161 // set time block price
162 $timeBlock->setPrice($price);
163
164 if ($trace && !empty($trace['rates']))
165 {
166 // register rate trace
167 $timeBlock->setRatesTrace($trace['rates']);
168 }
169 }
170 }
171 }
172
173 // make sure all the times are not in the past
174 if (!$timeline->hasTimes())
175 {
176 // all times are in the past
177 $this->setError(JText::translate('VAPFINDRESNOLONGERAVAILABLE'));
178
179 return false;
180 }
181
182 return $timeline;
183 }
184
185 /**
186 * Helper method used to elaborate the resulting timeline.
187 *
188 * @param string $date The UTC date in military format.
189 * @param integer $people The number of participants.
190 * @param array $worktime A list of working times.
191 * @param array $bookings A list of booked appointments.
192 * @param object $service The service details.
193 *
194 * @return array The resulting timeline.
195 */
196 protected function elaborateTimeLine($date, $people, $worktime, $bookings, $service)
197 {
198 $arr = array();
199
200 if ($service->interval == 1)
201 {
202 // same as service duration
203 $min_int = min(5, $service->duration + $service->sleep);
204 }
205 else
206 {
207 // default one specified by the configuration
208 $min_int = VAPFactory::getConfig()->getUint('minuteintervals');
209 }
210
211 for ($i = 0; $i < count($worktime); $i++)
212 {
213 $arr[$i] = [];
214
215 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
216 {
217 $arr[$i][$j] = 1;
218 }
219 }
220
221 // fetch default system timezone
222 $tz = JFactory::getApplication()->get('offset', 'UTC');
223
224 // re-format check-in date for correct comparison
225 $ymd = JFactory::getDate($date)->format('Y-m-d');
226
227 foreach ($bookings as $b)
228 {
229 if (!$b->timezone)
230 {
231 // use default system timezone
232 $b->timezone = $tz;
233 }
234
235 // create check-in and adjust it to the employee timezone
236 $checkin = JFactory::getDate($b->checkin_ts);
237 $checkin->setTimezone(new DateTimeZone($b->timezone));
238
239 // get check-in time adjusted to local timezone
240 $from = $checkin->format('H:i', $local = true);
241 // convert time string in minutes
242 $from = JHtml::fetch('vikappointments.time2min', $from);
243
244 /**
245 * Check the day factor of a reservation to check if it is referring
246 * to the current day or if it close to the bounds of this working time.
247 * Used to support midnight reservations.
248 *
249 * @since 1.6
250 */
251
252 if ($checkin->format('Y-m-d', $local = true) > $ymd)
253 {
254 // we are evaluating a reservation for the next day, so we need to increase the
255 // initial time by 1440 minutes (24 hours * 60).
256 $from += 1440;
257 }
258 else if ($checkin->format('Y-m-d', $local = true) < $ymd)
259 {
260 // we are evaluating a reservation for the previous day, so we need to decrease the
261 // initial time by 1440 minutes (24 hours * 60).
262 $from -= 1440;
263 }
264
265 // calculate end time by adding total duration to start time
266 $to = $from + $b->duration + $b->sleep;
267
268 for ($i = $from; $i < $to; $i += $min_int)
269 {
270 $found = false;
271 for ($j = 0; $j < count($arr) && !$found; $j++)
272 {
273 if (!empty($arr[$j][$i]))
274 {
275 $found = true;
276 $arr[$j][$i] = 0;
277 }
278 }
279 }
280 }
281
282 if ($service->interval != 1)
283 {
284 $n_step = $service->duration + $service->sleep;
285
286 for ($i = 0; $i < count($arr); $i++)
287 {
288 $step = 0;
289 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
290 {
291 if ($arr[$i][$j] == 1)
292 {
293 $step += $min_int;
294 if ($step >= $n_step)
295 {
296 $step-=$min_int;
297 }
298 }
299 else
300 {
301 if ($step != 0 && $step < $n_step)
302 {
303 for ($back = $j - $min_int; $back >= $j - $step; $back -= $min_int)
304 {
305 $arr[$i][$back] = 2;
306 }
307 }
308
309 $step = 0;
310 }
311 }
312
313 if ($step != 0 && $step < $n_step)
314 {
315 for ($back = $j - $min_int; $back >= $j - $step; $back -= $min_int)
316 {
317 $arr[$i][$back] = 2;
318 }
319 }
320 }
321 }
322
323 $mod = round(($service->duration + $service->sleep) / $min_int);
324
325 if ($service->interval == 1 && $mod != 1)
326 {
327 $new_arr = array();
328
329 for ($i = 0; $i < count($arr); $i++)
330 {
331 $new_arr[$i] = array();
332 $value = 1;
333 $start = 0;
334 $all_free = true;
335
336 $count = 0;
337
338 for ($j = $worktime[$i]->fromts; $j < $worktime[$i]->endts; $j += $min_int, $count++)
339 {
340 if ($count % $mod == 0)
341 {
342 $start = $j;
343 $value = 1;
344 $all_free = true;
345 }
346
347 $hourmin = intval($j / 60) . ' : ' . ($j % 60);
348 if ($arr[$i][$j] == 0)
349 {
350 $all_free = false;
351 }
352
353 $value &= ($arr[$i][$j] == 2 ? 0 : $arr[$i][$j]);
354
355 if ((($count + 1) % $mod == 0 || $j + $min_int == $worktime[$i]->endts))
356 {
357 // LAST TIME SLOTS is not enough length
358 if (($count+1) % $mod != 0)
359 {
360 $value = 0;
361 }
362
363 if ($value == 0 && $all_free)
364 {
365 $value = 2;
366 }
367
368 $new_arr[$i][$start] = $value;
369 }
370 }
371 }
372
373 $arr = $new_arr;
374 }
375
376 return $arr;
377 }
378 }
379