PluginProbe ʕ •ᴥ•ʔ
VikAppointments Services Booking Calendar / trunk
VikAppointments Services Booking Calendar vtrunk
trunk 1.2.17 1.2.18 1.2.19
vikappointments / admin / controllers / reservation.php
vikappointments / admin / controllers Last commit date
analytics.php 4 years ago apiban.php 4 years ago apilog.php 4 years ago apiplugin.php 4 years ago apiuser.php 4 years ago backup.php 4 years ago calendar.php 4 years ago city.php 4 years ago closure.php 1 month ago configapp.php 4 years ago configcldays.php 2 years ago configcron.php 4 years ago configemp.php 4 years ago configsmsapi.php 4 years ago configuration.php 1 month ago conversion.php 1 year ago country.php 4 years ago coupon.php 4 years ago coupongroup.php 4 years ago cronjob.php 2 years ago cronjoblog.php 4 years ago customer.php 5 months ago customf.php 1 year ago dashboard.php 4 years ago emplocwdays.php 4 years ago employee.php 1 year ago emprates.php 4 years ago export.php 4 years ago exportres.php 4 years ago file.php 5 months ago findreservation.php 1 month ago group.php 4 years ago import.php 4 years ago index.html 4 years ago invoice.php 1 month ago langcustomf.php 4 years ago langemployee.php 4 years ago langgroup.php 4 years ago langmedia.php 4 years ago langoption.php 4 years ago langoptiongroup.php 4 years ago langpackage.php 4 years ago langpackgroup.php 4 years ago langpayment.php 4 years ago langservice.php 4 years ago langstatuscode.php 4 years ago langsubscr.php 4 years ago langtax.php 4 years ago location.php 4 years ago mailtext.php 2 years ago makerecurrence.php 1 month ago media.php 4 years ago multiorder.php 4 years ago option.php 5 months ago optiongroup.php 4 years ago package.php 2 years ago packgroup.php 4 years ago packorder.php 1 year ago payment.php 4 years ago rate.php 4 years ago reportsemp.php 4 years ago reportsser.php 4 years ago reservation.php 1 month ago restriction.php 4 years ago review.php 4 years ago service.php 1 year ago serworkday.php 5 months ago state.php 4 years ago statuscode.php 4 years ago subscription.php 4 years ago subscrorder.php 4 years ago tag.php 4 years ago tax.php 4 years ago usernote.php 4 years ago waitinglist.php 4 years ago webhook.php 4 years ago wizard.php 1 year ago
reservation.php
926 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.mvc.controllers.admin');
15
16 /**
17 * VikAppointments appointment controller.
18 *
19 * @since 1.7
20 */
21 class VikAppointmentsControllerReservation extends VAPControllerAdmin
22 {
23 /**
24 * Task used to access the creation page of a new record.
25 *
26 * @return boolean
27 */
28 public function add()
29 {
30 $app = JFactory::getApplication();
31 $user = JFactory::getUser();
32
33 $data = array();
34 $data['id_service'] = $app->input->getUint('id_ser');
35 $data['id_employee'] = $app->input->getUint('id_emp');
36 $data['people'] = $app->input->getUint('people');
37 $data['day'] = $app->input->getString('day');
38
39 if (!empty($data['day']))
40 {
41 // get employee timezone
42 $tz = $this->getModel('employee')->getTimezone($data['id_employee']);
43
44 $h = $app->input->getUint('hour', 0);
45 $m = $app->input->getUint('min', 0);
46
47 // create date instance adjusted to employee timezone
48 $date = JFactory::getDate($data['day'] . " $h:$m:00", $tz);
49
50 // get check-in UTC
51 $data['checkin_ts'] = $date->toSql();
52 }
53
54 // strip missing information
55 $data = array_filter($data);
56
57 // unset user state for being recovered again
58 $app->setUserState('vap.reservation.data', $data);
59
60 // check user permissions
61 if (!$user->authorise('core.create', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
62 {
63 // back to main list, not authorised to create records
64 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
65 $this->cancel();
66
67 return false;
68 }
69
70 $url = 'index.php?option=com_vikappointments&view=managereservation';
71
72 // check whether a custom return view has been specified
73 $from = $app->input->get('from');
74
75 if ($from)
76 {
77 $url .= '&from=' . $from;
78 }
79
80 $this->setRedirect($url);
81
82 return true;
83 }
84
85 /**
86 * Task used to access the management page of an existing record.
87 *
88 * @return boolean
89 */
90 public function edit()
91 {
92 $app = JFactory::getApplication();
93 $user = JFactory::getUser();
94
95 $data = array();
96 $data['id_service'] = $app->input->getUint('id_ser');
97 $data['id_employee'] = $app->input->getUint('id_emp');
98 $data['people'] = $app->input->getUint('people');
99 $data['day'] = $app->input->getString('day');
100
101 if (!empty($data['day']))
102 {
103 // get employee timezone
104 $tz = $this->getModel('employee')->getTimezone($data['id_employee']);
105
106 $h = $app->input->getUint('hour', 0);
107 $m = $app->input->getUint('min', 0);
108
109 // create date instance adjusted to employee timezone
110 $date = JFactory::getDate($data['day'] . " $h:$m:00", $tz);
111
112 // get check-in UTC
113 $data['checkin_ts'] = $date->toSql();
114 }
115
116 // strip missing information
117 $data = array_filter($data);
118
119 // unset user state for being recovered again
120 $app->setUserState('vap.reservation.data', $data);
121
122 // check user permissions
123 if (!$user->authorise('core.edit', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
124 {
125 // back to main list, not authorised to edit records
126 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
127 $this->cancel();
128
129 return false;
130 }
131
132 $cid = $app->input->getUint('cid', array(0));
133
134 $url = 'index.php?option=com_vikappointments&view=managereservation&cid[]=' . $cid[0];
135
136 // check whether a custom return view has been specified
137 $from = $app->input->get('from');
138
139 if ($from)
140 {
141 $url .= '&from=' . $from;
142 }
143
144 $this->setRedirect($url);
145
146 return true;
147 }
148
149 /**
150 * Task used to save the record data set in the request.
151 * After saving, the user is redirected to the main list.
152 *
153 * @return void
154 */
155 public function saveclose()
156 {
157 if ($this->save())
158 {
159 $this->cancel();
160 }
161 }
162
163 /**
164 * Task used to save the record data set in the request.
165 * After saving, the user is redirected to the creation
166 * page of a new record.
167 *
168 * @return void
169 */
170 public function savenew()
171 {
172 if ($this->save())
173 {
174 $this->setRedirect('index.php?option=com_vikappointments&view=findreservation');
175 }
176 }
177
178 /**
179 * Task used to save the record data set in the request.
180 * After saving, the user is redirected to the management
181 * page of the record that has been saved.
182 *
183 * @return boolean
184 */
185 public function save()
186 {
187 $app = JFactory::getApplication();
188 $input = $app->input;
189 $user = JFactory::getUser();
190
191 /**
192 * Added token validation.
193 *
194 * @since 1.7
195 */
196 if (!JSession::checkToken())
197 {
198 // back to main list, missing CSRF-proof token
199 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
200 $this->cancel();
201
202 return false;
203 }
204
205 $args = array();
206
207 // get order details
208 $args['id'] = $input->getUint('id', 0);
209 $args['id_payment'] = $input->getUint('id_payment', 0);
210 $args['status'] = $input->getString('status', '');
211 $args['status_comment'] = $input->getString('comment', '');
212 $args['notifycust'] = $input->getBool('notifycust', false);
213 $args['notifyemp'] = $input->getBool('notifyemp', false);
214 $args['notifywl'] = $input->getBool('notifywl', false);
215 $args['notes'] = JComponentHelper::filterText($input->getRaw('notes', ''));
216
217 // get billing details
218 $args['id_user'] = $input->getUint('id_user', 0);
219 $args['purchaser_nominative'] = $input->getString('purchaser_nominative', '');
220 $args['purchaser_mail'] = $input->getString('purchaser_mail', '');
221 $args['purchaser_phone'] = $input->getString('purchaser_phone', '');
222 $args['purchaser_prefix'] = $input->getString('purchaser_prefix', '');
223 $args['purchaser_country'] = $input->getString('purchaser_country', '');
224
225 // get service details
226 $args['id_service'] = $input->getUint('id_service', 0);
227 $args['id_employee'] = $input->getUint('id_employee', 0);
228 $args['checkin_ts'] = $input->getString('checkin_ts', '');
229 $args['duration'] = $input->getUint('duration', 0);
230 $args['sleep'] = $input->getUint('sleep', 0);
231 $args['people'] = $input->getUint('people', 1);
232
233 // get order totals
234 $args['total_cost'] = $input->getFloat('total_cost', 0);
235 $args['total_net'] = $input->getFloat('total_net', 0);
236 $args['total_tax'] = $input->getFloat('total_tax', 0);
237 $args['payment_charge'] = $input->getFloat('payment_charge', 0);
238 $args['payment_tax'] = $input->getFloat('payment_tax', 0);
239 $args['deposit'] = $input->getFloat('deposit', 0);
240
241 // get service totals
242 $args['service_price'] = $input->getFloat('service_price', 0);
243 $args['service_net'] = $input->getFloat('service_net', 0);
244 $args['service_tax'] = $input->getFloat('service_tax', 0);
245 $args['service_gross'] = $input->getFloat('service_gross', 0);
246 $args['tax_breakdown'] = $input->getString('tax_breakdown', '');
247
248 // get actions
249 $args['add_discount'] = $input->getString('add_discount', '');
250 $args['remove_discount'] = $input->getBool('remove_discount', false);
251
252 if ($args['add_discount'] === 'manual')
253 {
254 // fetch manual discount from request
255 $args['add_discount'] = $input->get('manual_discount', [], 'array');
256 }
257
258 // check whether the model should (re)validate the availability
259 $args['validate_availability'] = $input->getBool('validate_availability', false);
260
261 // import custom fields requestor and loader (as dependency)
262 VAPLoader::import('libraries.customfields.requestor');
263
264 // get relevant custom fields only
265 $_cf = VAPCustomFieldsLoader::getInstance()
266 ->ofEmployee($args['id_employee'])
267 ->forService($args['id_service'])
268 ->noSeparator()
269 ->noRequiredCheckbox()
270 ->fetch();
271
272 // load custom fields from request
273 $args['custom_f'] = VAPCustomFieldsRequestor::loadForm($_cf, $tmp, $strict = false);
274
275 // copy uploads into the apposite column
276 $args['uploads'] = $tmp['uploads'];
277
278 // register data fetched by the custom fields so that the reservation
279 // model is able to use them for saving purposes
280 $args['fields_data'] = $tmp;
281
282 $args['attendees'] = array();
283
284 /**
285 * Recover attendees custom fields.
286 *
287 * @since 1.7
288 */
289 for ($people = 0; $people < $args['people'] - 1; $people++)
290 {
291 // reset attendee array
292 $attendee = array();
293
294 // load custom fields from request for other attendees
295 $tmp = VAPCustomFieldsRequestor::loadFormAttendee($people + 1, $_cf, $attendee, $strict = false);
296 // inject attendee custom fields within the array containing the fetched rules
297 $attendee['fields'] = $tmp;
298
299 // register attendee
300 $args['attendees'][] = $attendee;
301 }
302
303 // get selected options
304 $args['options'] = $input->get('option_json', array(), 'array');
305 // load deleted options
306 $args['deletedOptions'] = $input->get('option_deleted', array(), 'uint');
307
308 $rule = 'core.' . ($args['id'] > 0 ? 'edit' : 'create');
309
310 // check user permissions
311 if (!$user->authorise($rule, 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
312 {
313 // back to main list, not authorised to create/edit records
314 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
315 $this->cancel();
316
317 return false;
318 }
319
320 if ($args['notifycust'])
321 {
322 /**
323 * Loads any additional custom text to include within the e-mail notification.
324 *
325 * @since 1.6.5
326 */
327 $custMail = array();
328 $custMail['id'] = $input->getUint('custmail_id', 0);
329 $custMail['name'] = $input->getString('custmail_name', '');
330 $custMail['position'] = $input->getString('custmail_position', '');
331 $custMail['content'] = JComponentHelper::filterText($input->getRaw('custmail_content', ''));
332
333 if (!empty($custMail['name']) && !empty($custMail['content']))
334 {
335 // create new custom e-mail template (unpublished)
336 $custMail['published'] = 0;
337
338 // get e-mail text model
339 $custMailModel = $this->getModel('mailtext');
340 // attempt to create new mail text
341 $mail_id = $custMailModel->save($custMail);
342
343 if ($mail_id)
344 {
345 // inject selected custom e-mail within order details
346 // for being retrieved while generating the notification
347 $args['mail_custom_text'] = $mail_id;
348
349 /**
350 * Added the possibility to exclude the default mail custom texts.
351 *
352 * @since 1.6.6
353 */
354 $args['exclude_default_mail_texts'] = $input->getBool('exclude_default_mail_texts', false);
355 }
356 }
357 }
358
359 // get reservation model
360 $order = $this->getModel();
361
362 if ($args['id'] == 0)
363 {
364 VAPLoader::import('libraries.models.subscriptions');
365
366 // In case the service owns a cost and the system supports the subscriptions
367 // for the customers, always recalculate the totals. Ignore subscription benefits
368 // in case of updates, because the price might have been manually changed.
369 if ($args['service_price'] > 0 && $args['id_user'] > 0 && VAPSubscriptions::has())
370 {
371 // recalculate costs by unsetting the service price
372 $order->recalculateTotals($args);
373 }
374 }
375
376 // get packages order model
377 $package = JModelVAP::getInstance('packorder');
378
379 $can_redeem_pack = false;
380
381 $approved = JHtml::fetch('vaphtml.status.isapproved', 'appointments', $args['status']);
382
383 // attempt to redeem the package only in case the service has a cost
384 // and the status is approved
385 if ($args['service_price'] > 0 && !empty($args['id_user']) && $approved)
386 {
387 $count = $package->countRemaining($args['id_service'], $args['id_user']);
388
389 // in case the count of remaining packages is equals or higher than the selected
390 // number of people, we can redeem the packages
391 if ($count >= $args['people'])
392 {
393 // we can redeem the packages
394 $can_redeem_pack = true;
395
396 // recalculate costs by unsetting the service price
397 $order->recalculateTotals($args, 0.0);
398
399 // use a different status comment, if blank
400 if (empty($args['status_comment']))
401 {
402 $args['status_comment'] = 'VAP_STATUS_PACKAGE_REDEEMED';
403 }
404 }
405 }
406
407 // try to save arguments
408 $id = $order->save($args);
409
410 if (!$id)
411 {
412 // get string error
413 $error = $order->getError(null, true);
414
415 // update user state data by refactoring the options structure
416 $data = (array) $app->getUserState('vap.reservation.data', array());
417
418 if (!empty($data['options']))
419 {
420 foreach ($data['options'] as $i => $opt)
421 {
422 if (is_string($opt))
423 {
424 // JSON decode options
425 $data['options'][$i] = json_decode($opt);
426 }
427 }
428
429 // commit changes
430 $app->setUserState('vap.reservation.data', $data);
431 }
432
433 // display error message
434 $app->enqueueMessage(JText::sprintf('JLIB_APPLICATION_ERROR_SAVE_FAILED', $error), 'error');
435
436 $url = 'index.php?option=com_vikappointments&view=managereservation';
437
438 if ($args['id'])
439 {
440 $url .= '&cid[]=' . $args['id'];
441 }
442
443 // redirect to new/edit page
444 $this->setRedirect($url);
445
446 return false;
447 }
448
449 if ($can_redeem_pack)
450 {
451 // finally redeem the packages
452 $redeemed = $package->usePackages($id);
453
454 if ($redeemed)
455 {
456 // display message to the user
457 $app->enqueueMessage(JText::sprintf('VAPORDERREDEEMEDPACKS', $redeemed));
458 }
459 }
460
461 // display generic successful message
462 $app->enqueueMessage(JText::translate('JLIB_APPLICATION_SAVE_SUCCESS'));
463
464 // redirect to edit page
465 $this->setRedirect('index.php?option=com_vikappointments&task=reservation.edit&cid[]=' . $id);
466
467 return true;
468 }
469
470 /**
471 * Deletes a list of records set in the request.
472 *
473 * @return boolean
474 */
475 public function delete()
476 {
477 $app = JFactory::getApplication();
478 $user = JFactory::getUser();
479
480 /**
481 * Added token validation.
482 * Both GET and POST are supported.
483 *
484 * @since 1.7
485 */
486 if (!JSession::checkToken() && !JSession::checkToken('get'))
487 {
488 // back to main list, missing CSRF-proof token
489 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
490 $this->cancel();
491
492 return false;
493 }
494
495 $cid = $app->input->get('cid', array(), 'uint');
496
497 // check user permissions
498 if (!$user->authorise('core.delete', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
499 {
500 // back to main list, not authorised to delete records
501 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
502 $this->cancel();
503
504 return false;
505 }
506
507 // delete selected records
508 $this->getModel()->delete($cid);
509
510 // back to main list
511 $this->cancel();
512
513 return true;
514 }
515
516 /**
517 * Redirects the users to the main records list.
518 *
519 * @return void
520 */
521 public function cancel()
522 {
523 $input = JFactory::getApplication()->input;
524
525 // check whether a custom return view has been specified
526 $view = $input->get('from');
527
528 if (!$view)
529 {
530 // back to appointments list by default
531 $view = 'reservations';
532 }
533
534 $this->setRedirect('index.php?option=com_vikappointments&view=' . $view);
535 }
536
537 /**
538 * Task used to send an e-mail notification to the customer
539 * of the specified appointment.
540 *
541 * @return void
542 */
543 public function notify()
544 {
545 $app = JFactory::getApplication();
546 $input = $app->input;
547 $user = JFactory::getUser();
548
549 /**
550 * Added token validation.
551 * Both GET and POST are supported.
552 *
553 * @since 1.7
554 */
555 if (!JSession::checkToken() && !JSession::checkToken('get'))
556 {
557 // back to main list, missing CSRF-proof token
558 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
559 $this->cancel();
560
561 return false;
562 }
563
564 // check user permissions
565 if (!$user->authorise('core.edit.state', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
566 {
567 // back to main list, not authorised to send e-mail notifications
568 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
569 $this->cancel();
570
571 return false;
572 }
573
574 $ids = $input->getUint('cid', array(0), 'uint');
575
576 // get appointment model
577 $model = $this->getModel();
578
579 $errors = array();
580
581 foreach ($ids as $id)
582 {
583 // try to send e-mail notification
584 if (!$model->sendEmailNotification($id))
585 {
586 // get string error
587 $error = $model->getError(null, true);
588
589 // enqueue error message
590 $errors[] = $error ? $error : JText::translate('VAPNOTIFYCUSTERR');
591 }
592 }
593
594 if ($errors)
595 {
596 // display duplicate error messages only once
597 foreach (array_unique($errors) as $err)
598 {
599 $app->enqueueMessage($err, 'error');
600 }
601 }
602 else
603 {
604 // display successful message
605 $app->enqueueMessage(JText::translate('VAPNOTIFYCUSTOK'));
606 }
607
608 // back to main list
609 $this->cancel();
610 }
611
612 /**
613 * Task used to send a SMS notification to the customer
614 * of the specified appointment.
615 *
616 * @return void
617 */
618 public function sendsms()
619 {
620 $app = JFactory::getApplication();
621 $input = $app->input;
622 $user = JFactory::getUser();
623
624 /**
625 * Added token validation.
626 * Both GET and POST are supported.
627 *
628 * @since 1.7
629 */
630 if (!JSession::checkToken() && !JSession::checkToken('get'))
631 {
632 // back to main list, missing CSRF-proof token
633 $app->enqueueMessage(JText::translate('JINVALID_TOKEN'), 'error');
634 $this->cancel();
635
636 return false;
637 }
638
639 // check user permissions
640 if (!$user->authorise('core.edit.state', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
641 {
642 // back to main list, not authorised to send SMS notifications
643 $app->enqueueMessage(JText::translate('JERROR_ALERTNOAUTHOR'), 'error');
644 $this->cancel();
645
646 return false;
647 }
648
649 $ids = $input->getUint('cid', array(0), 'uint');
650
651 // get appointment model
652 $model = $this->getModel();
653
654 $notified = 0;
655 $errors = array();
656
657 foreach ($ids as $id)
658 {
659 // try to send SMS notification
660 if ($model->sendSmsNotification($id))
661 {
662 $notified++;
663 }
664 else
665 {
666 // get string error
667 $error = $model->getError(null, true);
668
669 // enqueue error message
670 $errors[] = $error;
671 }
672 }
673
674 if ($notified)
675 {
676 // successful message
677 $app->enqueueMessage(JText::plural('VAPCUSTOMERSMSSENT', $notified));
678 }
679 else
680 {
681 // no notifications sent
682 $app->enqueueMessage(JText::plural('VAPCUSTOMERSMSSENT', $notified), 'warning');
683 }
684
685 // display any returned errors
686 if ($errors)
687 {
688 // do not display duplicate or empty errors
689 $errors = array_unique(array_filter($errors));
690
691 foreach ($errors as $err)
692 {
693 $app->enqueueMessage($err, 'error');
694 }
695 }
696
697 // back to main list
698 $this->cancel();
699 }
700
701 /**
702 * AJAX end-point used to change the status code.
703 * The task expects the following arguments to be set in request.
704 *
705 * @param integer id The appointment ID.
706 * @param string status The new status code.
707 * @param string layout The layout to use (for return).
708 *
709 * @return void
710 */
711 public function changestatusajax()
712 {
713 $input = JFactory::getApplication()->input;
714 $user = JFactory::getUser();
715
716 /**
717 * Added token validation.
718 *
719 * @since 1.7
720 */
721 if (!JSession::checkToken())
722 {
723 // missing CSRF-proof token
724 UIErrorFactory::raiseError(403, JText::translate('JINVALID_TOKEN'));
725 }
726
727 $data = array();
728 $data['id'] = $input->getUint('id');
729 $data['status'] = $input->getString('status');
730
731 // check user permissions
732 if (!$data['id'] || !$user->authorise('core.edit.state', 'com_vikappointments') || !$user->authorise('core.access.reservations', 'com_vikappointments'))
733 {
734 // not authorised to edit records
735 UIErrorFactory::raiseError(403, JText::translate('JERROR_ALERTNOAUTHOR'));
736 }
737
738 // get status code details
739 $code = JHtml::fetch('vaphtml.status.find', '*', array('code' => $data['status']), $limit = true);
740
741 if (!$code)
742 {
743 // code not found
744 UIErrorFactory::raiseError(404, JText::translate('JGLOBAL_NO_MATCHING_RESULTS'));
745 }
746
747 // register comment for status change
748 $data['status_comment'] = 'VAP_STATUS_CHANGED_FROM_LIST';
749
750 /**
751 * Always include the flag to process the waiting list.
752 * The model will then make sure that the new status is actually cancelled.
753 *
754 * @since 1.7.7
755 */
756 $data['notifywl'] = true;
757
758 $model = $this->getModel();
759
760 // update status
761 $model->save($data);
762
763 /**
764 * Automatically send a notification to the customer according to the mailing preferences.
765 *
766 * @since 1.7.7
767 */
768 if (VAPFactory::getConfig()->getBool('notifyonstatuschange'))
769 {
770 try
771 {
772 // send e-mail notification to the customer
773 $model->sendEmailNotification($data['id'], [
774 // validate e-mail rules before sending
775 'check' => true,
776 ]);
777 }
778 catch (Exception $error)
779 {
780 // go ahead silently
781 }
782 }
783
784 // render HTML
785 $code->html = JHtml::fetch('vaphtml.status.display', $code, $input->getString('layout'));
786
787 // send code to caller
788 $this->sendJSON($code);
789 }
790
791 /**
792 * AJAX end-point used return a list of employees available for the
793 * specified check-in arguments.
794 *
795 * @param integer id_employee The currently set employee ID.
796 * @param integer id_service The service ID.
797 * @param string checkin_ts The appointment check-in.
798 * @param integer people The number of attendees.
799 *
800 * @return void
801 */
802 public function employeespreviewajax()
803 {
804 $input = JFactory::getApplication()->input;
805
806 if (!JSession::checkToken())
807 {
808 // missing CSRF-proof token
809 UIErrorFactory::raiseError(403, JText::translate('JINVALID_TOKEN'));
810 }
811
812 $args = array();
813 $args['id_service'] = $input->getUint('id_service', 0);
814 $args['checkin_ts'] = $input->getString('checkin_ts', '');
815 $args['people'] = $input->getUint('people', 0);
816
817 $curr_employee = $input->getUint('id_employee', 0);
818
819 // load all employees assigned to the specified service
820 $employees = $this->getModel('service')->getEmployees($args['id_service']);
821
822 if (!$employees)
823 {
824 // no employees, raise error
825 UIErrorFactory::raiseError(404, 'No employees found');
826 }
827
828 // get reservation model
829 $model = $this->getModel();
830
831 $lookup = array();
832
833 // iterate all employees and validate availability one by one
834 foreach ($employees as $employee)
835 {
836 // prepare lookup data
837 $tmp = array(
838 'id' => $employee->id,
839 'name' => $employee->nickname,
840 );
841
842 if ($employee->id == $curr_employee)
843 {
844 // always available the currently set employee
845 $tmp['status'] = true;
846 }
847 else
848 {
849 // update search query with current employee ID
850 $args['id_employee'] = $employee->id;
851 // validate employee availability
852 $tmp['status'] = $model->isAvailable($args);
853 }
854
855 // register result
856 $lookup[] = $tmp;
857 }
858
859 // send response to caller
860 $this->sendJSON($lookup);
861 }
862
863 /**
864 * AJAX end-point used to change the check-in of the selected appointment.
865 *
866 * @param integer id The appointment ID.
867 * @param string checkin_ts The appointment check-in.
868 *
869 * @return void
870 */
871 public function changecheckinajax()
872 {
873 $input = JFactory::getApplication()->input;
874
875 if (!JSession::checkToken())
876 {
877 // missing CSRF-proof token
878 UIErrorFactory::raiseError(403, JText::translate('JINVALID_TOKEN'));
879 }
880
881 $args = array();
882 $args['id'] = $input->getUint('id', 0);
883 $args['checkin_ts'] = $input->getString('checkin_ts', '');
884 $args['duration'] = $input->getUint('duration', null);
885
886 // get reservation model
887 $model = $this->getModel();
888
889 // get reservation details
890 $item = $model->getItem($args['id']);
891
892 if (!$item)
893 {
894 // no appointment, raise error
895 UIErrorFactory::raiseError(404, 'Appointment not found');
896 }
897
898 $args['id_employee'] = $item->id_employee;
899 $args['id_service'] = $item->id_service;
900 $args['people'] = $item->people;
901 $args['sleep'] = $item->sleep;
902
903 if (!$args['duration'])
904 {
905 // use default duration
906 $args['duration'] = $item->duration;
907 }
908
909 // make sure the appointment is still available
910 $args['validate_availability'] = true;
911
912 // attempt to apply the changes
913 if (!$model->save($args))
914 {
915 // get registered error message
916 $error = $model->getError($index = null, $string = true);
917
918 // propagate error to caller
919 UIErrorFactory::raiseError(500, $error);
920 }
921
922 // send response to caller
923 $this->sendJSON(1);
924 }
925 }
926