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