1: <?php if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2:
3: class Appointments extends CI_Controller {
4: 5: 6: 7: 8: 9: 10: 11: 12: 13:
14: public function index($appointment_hash = '') {
15: $this->load->model('appointments_model');
16: $this->load->model('providers_model');
17: $this->load->model('services_model');
18: $this->load->model('customers_model');
19: $this->load->model('settings_model');
20:
21: if (strtoupper($_SERVER['REQUEST_METHOD']) !== 'POST') {
22: try {
23: $available_services = $this->services_model->get_available_services();
24: $available_providers = $this->providers_model->get_available_providers();
25: $company_name = $this->settings_model->get_setting('company_name');
26:
27:
28:
29: if ($appointment_hash !== ''){
30:
31: $manage_mode = TRUE;
32:
33: $results = $this->appointments_model->get_batch(array('hash' => $appointment_hash));
34:
35: if (count($results) === 0) {
36:
37:
38: $view = array(
39: 'message_title' => 'Appointment Not Found!',
40: 'message_text' => 'The appointment you requested does not exist in '
41: . 'the system database anymore.',
42: 'message_icon' => $this->config->item('base_url')
43: . 'assets/images/error.png'
44: );
45: $this->load->view('appointments/message', $view);
46: return;
47: }
48:
49: $appointment = $results[0];
50: $provider = $this->providers_model->get_row($appointment['id_users_provider']);
51: $customer = $this->customers_model->get_row($appointment['id_users_customer']);
52:
53: } else {
54:
55:
56: $manage_mode = FALSE;
57: $appointment = array();
58: $provider = array();
59: $customer = array();
60: }
61:
62:
63: $view = array (
64: 'available_services' => $available_services,
65: 'available_providers' => $available_providers,
66: 'company_name' => $company_name,
67: 'manage_mode' => $manage_mode,
68: 'appointment_data' => $appointment,
69: 'provider_data' => $provider,
70: 'customer_data' => $customer
71: );
72:
73: } catch(Exception $exc) {
74: $view['exceptions'][] = $exc;
75: }
76:
77: $this->load->view('appointments/book', $view);
78:
79: } else {
80:
81:
82:
83:
84: try {
85: $post_data = json_decode($_POST['post_data'], true);
86: $appointment = $post_data['appointment'];
87: $customer = $post_data['customer'];
88:
89: $customer_id = $this->customers_model->add($customer);
90: $appointment['id_users_customer'] = $customer_id;
91:
92: $appointment['id'] = $this->appointments_model->add($appointment);
93: $appointment['hash'] = $this->appointments_model->get_value('hash', $appointment['id']);
94:
95: $provider = $this->providers_model->get_row($appointment['id_users_provider']);
96: $service = $this->services_model->get_row($appointment['id_services']);
97:
98: $company_settings = array(
99: 'company_name' => $this->settings_model->get_setting('company_name'),
100: 'company_link' => $this->settings_model->get_setting('company_link'),
101: 'company_email' => $this->settings_model->get_setting('company_email')
102: );
103:
104:
105:
106:
107: try {
108: $google_sync = $this->providers_model->get_setting('google_sync',
109: $appointment['id_users_provider']);
110:
111: if ($google_sync == TRUE) {
112: $google_token = json_decode($this->providers_model
113: ->get_setting('google_token', $appointment['id_users_provider']));
114:
115: $this->load->library('google_sync');
116: $this->google_sync->refresh_token($google_token->refresh_token);
117:
118: if ($post_data['manage_mode'] === FALSE) {
119:
120: $google_event = $this->google_sync->add_appointment($appointment, $provider,
121: $service, $customer, $company_settings);
122: $appointment['id_google_calendar'] = $google_event->id;
123: $this->appointments_model->add($appointment);
124: } else {
125:
126: $appointment['id_google_calendar'] = $this->appointments_model
127: ->get_value('id_google_calendar', $appointment['id']);
128:
129: $this->google_sync->update_appointment($appointment, $provider,
130: $service, $customer, $company_settings);
131: }
132: }
133: } catch(Exception $exc) {
134: $view['exceptions'][] = $exc;
135: }
136:
137:
138: try {
139: $this->load->library('Notifications');
140:
141: if (!$post_data['manage_mode']) {
142: $customer_title = 'Your appointment has been successfully booked!';
143: $customer_message = 'Thank you for arranging an appointment with us. '
144: . 'Below you can see the appointment details. Make changes '
145: . 'by clicking the appointment link.';
146: $customer_link = $this->config->item('base_url') . 'appointments/index/'
147: . $appointment['hash'];
148:
149: $provider_title = 'A new appointment has been added to your plan.';
150: $provider_message = 'You can make changes by clicking the appointment '
151: . 'link below';
152: $provider_link = $this->config->item('base_url') . 'backend/index/'
153: . $appointment['hash'];
154: } else {
155: $customer_title = 'Appointment changes have been successfully saved!';
156: $customer_message = '';
157: $customer_link = $this->config->item('base_url') . 'appointments/index/'
158: . $appointment['hash'];
159:
160: $provider_title = 'Appointment details have changed.';
161: $provider_message = '';
162: $provider_link = $this->config->item('base_url') . 'backend/index/'
163: . $appointment['hash'];
164: }
165:
166: $this->notifications->send_appointment_details($appointment, $provider,
167: $service, $customer,$company_settings, $customer_title,
168: $customer_message, $customer_link, $customer['email']);
169:
170: $this->notifications->send_appointment_details($appointment, $provider,
171: $service, $customer, $company_settings, $provider_title,
172: $provider_message, $provider_link, $provider['email']);
173: } catch(Exception $exc) {
174: $view['exceptions'][] = $exc;
175: }
176:
177:
178: $view = array(
179: 'appointment_data' => $appointment,
180: 'provider_data' => $provider,
181: 'service_data' => $service,
182: 'company_name' => $company_settings['company_name']
183: );
184:
185: } catch(Exception $exc) {
186: $view['exceptions'][] = $exc;
187: }
188:
189: $this->load->view('appointments/book_success', $view);
190: }
191: }
192:
193: 194: 195: 196: 197: 198: 199: 200: 201: 202: 203: 204: 205:
206: public function cancel($appointment_hash) {
207: try {
208: $this->load->model('appointments_model');
209: $this->load->model('providers_model');
210: $this->load->model('customers_model');
211: $this->load->model('services_model');
212: $this->load->model('settings_model');
213:
214:
215: $records = $this->appointments_model->get_batch(array('hash' => $appointment_hash));
216: if (count($records) == 0) {
217: throw new Exception('No record matches the provided hash.');
218: }
219:
220: $appointment = $records[0];
221: $provider = $this->providers_model->get_row($appointment['id_users_provider']);
222: $customer = $this->customers_model->get_row($appointment['id_users_customer']);
223: $service = $this->services_model->get_row($appointment['id_services']);
224:
225: $company_settings = array(
226: 'company_name' => $this->settings_model->get_setting('company_name'),
227: 'company_email' => $this->settings_model->get_setting('company_email'),
228: 'company_link' => $this->settings_model->get_setting('company_link')
229: );
230:
231:
232: if (!$this->appointments_model->delete($appointment['id'])) {
233: throw new Exception('Appointment could not be deleted from the database.');
234: }
235:
236:
237: if ($appointment['id_google_calendar'] != NULL) {
238: try {
239: $google_sync = $this->providers_model->get_setting('google_sync',
240: $appointment['id_users_provider']);
241:
242: if ($google_sync == TRUE) {
243: $google_token = json_decode($this->providers_model
244: ->get_setting('google_token', $provider['id']));
245: $this->load->library('Google_Sync');
246: $this->google_sync->refresh_token($google_token->refresh_token);
247: $this->google_sync->delete_appointment($appointment['id_google_calendar']);
248: }
249: } catch(Exception $exc) {
250: $exceptions[] = $exc;
251: }
252: }
253:
254:
255: try {
256: $this->load->library('Notifications');
257: $this->notifications->send_delete_appointment($appointment, $provider,
258: $service, $customer, $company_settings, $provider['email'],
259: $_POST['cancel_reason']);
260: $this->notifications->send_delete_appointment($appointment, $provider,
261: $service, $customer, $company_settings, $customer['email'],
262: $_POST['cancel_reason']);
263: } catch(Exception $exc) {
264: $exceptions[] = $exc;
265: }
266: } catch(Exception $exc) {
267:
268: $exceptions[] = $exc;
269: }
270:
271: if (isset($exceptions)) {
272: $view['exceptions'] = $exceptions;
273: }
274:
275: $this->load->view('appointments/cancel', $view);
276: }
277:
278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291:
292: public function ajax_get_available_hours() {
293: $this->load->model('providers_model');
294: $this->load->model('appointments_model');
295: $this->load->model('settings_model');
296:
297: try {
298:
299:
300: $exclude_appointments = ($_POST['manage_mode'] === 'true')
301: ? array($_POST['appointment_id'])
302: : array();
303:
304: $empty_periods = $this->get_provider_available_time_periods($_POST['provider_id'],
305: $_POST['selected_date'], $exclude_appointments);
306:
307:
308:
309:
310:
311:
312: $available_hours = array();
313:
314: foreach($empty_periods as $period) {
315: $start_hour = new DateTime($_POST['selected_date'] . ' ' . $period['start']);
316: $end_hour = new DateTime($_POST['selected_date'] . ' ' . $period['end']);
317:
318: $minutes = $start_hour->format('i');
319:
320: if ($minutes % 15 != 0) {
321:
322:
323: if ($minutes < 15) {
324: $start_hour->setTime($start_hour->format('H'), 15);
325: } else if ($minutes < 30) {
326: $start_hour->setTime($start_hour->format('H'), 30);
327: } else if ($minutes < 45) {
328: $start_hour->setTime($start_hour->format('H'), 45);
329: } else {
330: $start_hour->setTime($start_hour->format('H') + 1, 00);
331: }
332: }
333:
334: $curr_hour = $start_hour;
335: $diff = $curr_hour->diff($end_hour);
336:
337: while(($diff->h * 60 + $diff->i) > intval($_POST['service_duration'])) {
338: $available_hours[] = $curr_hour->format('H:i');
339: $curr_hour->add(new DateInterval("PT15M"));
340: $diff = $curr_hour->diff($end_hour);
341: }
342: }
343:
344:
345:
346:
347:
348:
349: if (date('m/d/Y', strtotime($_POST['selected_date'])) == date('m/d/Y')) {
350: if ($_POST['manage_mode'] === 'true') {
351: $book_advance_timeout = 0;
352: } else {
353: $book_advance_timeout = $this->settings_model->get_setting(
354: 'book_advance_timeout');
355: }
356:
357: foreach($available_hours as $index=>$value) {
358: $available_hour = strtotime($value);
359: $current_hour = strtotime('+' . $book_advance_timeout . ' minutes',
360: strtotime('now'));
361:
362: if ($available_hour <= $current_hour) {
363: unset($available_hours[$index]);
364: }
365: }
366: }
367:
368: $available_hours = array_values($available_hours);
369:
370: echo json_encode($available_hours);
371:
372: } catch(Exception $exc) {
373: echo json_encode(array(
374: 'exceptions' => array( exceptionToJavaScript($exc) )
375: ));
376: }
377: }
378:
379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389: 390: 391: 392:
393: public function ajax_check_datetime_availability() {
394: try {
395: $this->load->model('services_model');
396:
397: $service_duration = $this->services_model->get_value('duration', $_POST['id_services']);
398:
399: $available_periods = $this->get_provider_available_time_periods(
400: $_POST['id_users_provider'], $_POST['start_datetime']);
401:
402: $is_still_available = FALSE;
403:
404: foreach($available_periods as $period) {
405: $appt_start = new DateTime($_POST['start_datetime']);
406: $appt_start = $appt_start->format('H:i');
407:
408: $appt_end = new DateTime($_POST['start_datetime']);
409: $appt_end->add(new DateInterval('PT' . $service_duration . 'M'));
410: $appt_end = $appt_end->format('H:i');
411:
412: $period_start = date('H:i', strtotime($period['start']));
413: $period_end = date('H:i', strtotime($period['end']));
414:
415: if ($period_start <= $appt_start && $period_end >= $appt_end) {
416: $is_still_available = TRUE;
417: break;
418: }
419: }
420:
421: echo json_encode($is_still_available);
422:
423: } catch(Exception $exc) {
424: echo json_encode(array(
425: 'exceptions' => array(exceptionToJavaScript($exc))
426: ));
427: }
428: }
429:
430: 431: 432: 433: 434: 435: 436: 437: 438: 439: 440: 441: 442: 443:
444: private function get_provider_available_time_periods($provider_id, $selected_date,
445: $exclude_appointments = array()) {
446: $this->load->model('appointments_model');
447: $this->load->model('providers_model');
448:
449:
450: $working_plan = json_decode($this->providers_model->get_setting('working_plan',
451: $provider_id), true);
452:
453: $where_clause = array(
454: 'DATE(start_datetime)' => date('Y-m-d', strtotime($selected_date)),
455: 'id_users_provider' => $provider_id
456: );
457:
458: $reserved_appointments = $this->appointments_model->get_batch($where_clause);
459:
460:
461:
462: foreach($exclude_appointments as $excluded_appointment) {
463: foreach($reserved_appointments as $index=>$appointment) {
464: if ($appointment['id'] == $excluded_appointment['id']) {
465: unset($reserved_appointments[$index]);
466: }
467: }
468: }
469:
470:
471:
472:
473: $selected_date_working_plan = $working_plan[strtolower(date('l', strtotime($selected_date)))];
474: $available_periods_with_breaks = array();
475:
476: if (isset($selected_date_working_plan['breaks'])) {
477: foreach($selected_date_working_plan['breaks'] as $index=>$break) {
478:
479:
480: $last_break_index = $index - 1;
481:
482: if (count($available_periods_with_breaks) === 0) {
483: $start_hour = $selected_date_working_plan['start'];
484: $end_hour = $break['start'];
485: } else {
486: $start_hour = $selected_date_working_plan['breaks'][$last_break_index]['end'];
487: $end_hour = $break['start'];
488: }
489:
490: $available_periods_with_breaks[] = array(
491: 'start' => $start_hour,
492: 'end' => $end_hour
493: );
494: }
495:
496:
497: $available_periods_with_breaks[] = array(
498: 'start' => $selected_date_working_plan['breaks'][$index]['end'],
499: 'end' => $selected_date_working_plan['end']
500: );
501: }
502:
503:
504: $available_periods_with_appointments = array();
505:
506: if (count($reserved_appointments) > 0) {
507:
508: foreach($available_periods_with_breaks as $period) {
509:
510: foreach($reserved_appointments as $index=>$appointment) {
511: $appointment_start = date('H:i', strtotime($appointment['start_datetime']));
512: $appointment_end = date('H:i', strtotime($appointment['end_datetime']));
513: $period_start = date('H:i', strtotime($period['start']));
514: $period_end = date('H:i', strtotime($period['end']));
515:
516: if ($period_start <= $appointment_start && $period_end >= $appointment_end) {
517:
518:
519:
520: foreach ($reserved_appointments as $tmp_appointment) {
521: $appt_start = date('H:i', strtotime($tmp_appointment['start_datetime']));
522: $appt_end = date('H:i', strtotime($tmp_appointment['end_datetime']));
523:
524: if ($period_start < $appt_start && $period_end > $appt_end) {
525: if ($appointment_start > $appt_start) {
526: $appointment_start = $appt_start;
527: }
528:
529: if ($appointment_end < $appt_end) {
530: $appointment_end = $appt_end;
531: }
532: }
533: }
534:
535:
536:
537:
538: $new_period = array(
539: 'start' => $period_start,
540: 'end' => $appointment_start
541: );
542:
543: if (!in_array($new_period, $available_periods_with_appointments)) {
544: $available_periods_with_appointments[] = $new_period;
545: }
546:
547: $new_period = array(
548: 'start' => $appointment_end,
549: 'end' => $period_end
550: );
551:
552: if (!in_array($new_period, $available_periods_with_appointments)) {
553: $available_periods_with_appointments[] = $new_period;
554: }
555:
556: } else {
557:
558:
559: $found = FALSE;
560:
561: foreach ($reserved_appointments as $tmp_appointment) {
562: $appt_start = date('H:i', strtotime($tmp_appointment['start_datetime']));
563: $appt_end = date('H:i', strtotime($tmp_appointment['end_datetime']));
564:
565: if ($period_start < $appt_start && $period_end > $appt_end) {
566: $found = TRUE;
567: }
568: }
569:
570:
571:
572: $empty_period = array(
573: 'start' => $period_start,
574: 'end' => $period_end
575: );
576:
577: $already_exist = in_array($empty_period, $available_periods_with_appointments);
578:
579: if ($found === FALSE && $already_exist === FALSE) {
580: $available_periods_with_appointments[] = $empty_period;
581: }
582: }
583: }
584: }
585: } else {
586: $available_periods_with_appointments = $available_periods_with_breaks;
587: }
588:
589: return $available_periods_with_appointments;
590: }
591: }
592:
593:
594: