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