PluginProbe
VikAppointments Services Booking Calendar / 1.2.20
VikAppointments Services Booking Calendar v1.2.20
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
employee.php
377 lines 9.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
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 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
214 {
215 $arr[$i][$j] = 1;
216 }
217 }
218
219 // fetch default system timezone
220 $tz = JFactory::getApplication()->get('offset', 'UTC');
221
222 // re-format check-in date for correct comparison
223 $ymd = JFactory::getDate($date)->format('Y-m-d');
224
225 foreach ($bookings as $b)
226 {
227 if (!$b->timezone)
228 {
229 // use default system timezone
230 $b->timezone = $tz;
231 }
232
233 // create check-in and adjust it to the employee timezone
234 $checkin = JFactory::getDate($b->checkin_ts);
235 $checkin->setTimezone(new DateTimeZone($b->timezone));
236
237 // get check-in time adjusted to local timezone
238 $from = $checkin->format('H:i', $local = true);
239 // convert time string in minutes
240 $from = JHtml::fetch('vikappointments.time2min', $from);
241
242 /**
243 * Check the day factor of a reservation to check if it is referring
244 * to the current day or if it close to the bounds of this working time.
245 * Used to support midnight reservations.
246 *
247 * @since 1.6
248 */
249
250 if ($checkin->format('Y-m-d', $local = true) > $ymd)
251 {
252 // we are evaluating a reservation for the next day, so we need to increase the
253 // initial time by 1440 minutes (24 hours * 60).
254 $from += 1440;
255 }
256 else if ($checkin->format('Y-m-d', $local = true) < $ymd)
257 {
258 // we are evaluating a reservation for the previous day, so we need to decrease the
259 // initial time by 1440 minutes (24 hours * 60).
260 $from -= 1440;
261 }
262
263 // calculate end time by adding total duration to start time
264 $to = $from + $b->duration + $b->sleep;
265
266 for ($i = $from; $i < $to; $i += $min_int)
267 {
268 $found = false;
269 for ($j = 0; $j < count($arr) && !$found; $j++)
270 {
271 if (!empty($arr[$j][$i]))
272 {
273 $found = true;
274 $arr[$j][$i] = 0;
275 }
276 }
277 }
278 }
279
280 if ($service->interval != 1)
281 {
282 $n_step = $service->duration + $service->sleep;
283
284 for ($i = 0; $i < count($arr); $i++)
285 {
286 $step = 0;
287 for ($j = $worktime[$i]->fromts; ($j + $min_int) <= $worktime[$i]->endts; $j += $min_int)
288 {
289 if ($arr[$i][$j] == 1)
290 {
291 $step += $min_int;
292 if ($step >= $n_step)
293 {
294 $step-=$min_int;
295 }
296 }
297 else
298 {
299 if ($step != 0 && $step < $n_step)
300 {
301 for ($back = $j - $min_int; $back >= $j - $step; $back -= $min_int)
302 {
303 $arr[$i][$back] = 2;
304 }
305 }
306
307 $step = 0;
308 }
309 }
310
311 if ($step != 0 && $step < $n_step)
312 {
313 for ($back = $j - $min_int; $back >= $j - $step; $back -= $min_int)
314 {
315 $arr[$i][$back] = 2;
316 }
317 }
318 }
319 }
320
321 $mod = round(($service->duration + $service->sleep) / $min_int);
322
323 if ($service->interval == 1 && $mod != 1)
324 {
325 $new_arr = array();
326
327 for ($i = 0; $i < count($arr); $i++)
328 {
329 $new_arr[$i] = array();
330 $value = 1;
331 $start = 0;
332 $all_free = true;
333
334 $count = 0;
335
336 for ($j = $worktime[$i]->fromts; $j < $worktime[$i]->endts; $j += $min_int, $count++)
337 {
338 if ($count % $mod == 0)
339 {
340 $start = $j;
341 $value = 1;
342 $all_free = true;
343 }
344
345 $hourmin = intval($j / 60) . ' : ' . ($j % 60);
346 if ($arr[$i][$j] == 0)
347 {
348 $all_free = false;
349 }
350
351 $value &= ($arr[$i][$j] == 2 ? 0 : $arr[$i][$j]);
352
353 if ((($count + 1) % $mod == 0 || $j + $min_int == $worktime[$i]->endts))
354 {
355 // LAST TIME SLOTS is not enough length
356 if (($count+1) % $mod != 0)
357 {
358 $value = 0;
359 }
360
361 if ($value == 0 && $all_free)
362 {
363 $value = 2;
364 }
365
366 $new_arr[$i][$start] = $value;
367 }
368 }
369 }
370
371 $arr = $new_arr;
372 }
373
374 return $arr;
375 }
376 }
377