diff --git a/doc/thesis/includes/headers.tex b/doc/thesis/includes/headers.tex index f8d95c10..e223ee25 100644 --- a/doc/thesis/includes/headers.tex +++ b/doc/thesis/includes/headers.tex @@ -47,7 +47,7 @@ emph =[1]{php}, emphstyle =[1]\color{black}, emph =[2]{if,and,or,else,public,function,try,catch,return}, - emphstyle =[2]\color{dkyellow}, + emphstyle =[2]\color{dkblue}, numbers = left, tabsize = 2, backgroundcolor = \color{ltgrey}, diff --git a/doc/thesis/snippets/google_sync_algorithm.php b/doc/thesis/snippets/google_sync_algorithm.php index 2deba89d..71fc6200 100644 --- a/doc/thesis/snippets/google_sync_algorithm.php +++ b/doc/thesis/snippets/google_sync_algorithm.php @@ -1,7 +1,7 @@ providers_model ->get_setting('google_sync', $provider['id']); if (!$google_sync) { - throw new Exception('The selected provider has not the ' - . 'google synchronization setting enabled.'); + throw new Exception('The selected provider has not the ' + . 'Google synchronization setting enabled.'); } $google_token = json_decode($this->providers_model @@ -43,66 +43,75 @@ public function sync($provider_id = NULL) { $this->load->library('google_sync'); $this->google_sync->refresh_token($google_token->refresh_token); - // Fetch provider's appointments that belong to the - // sync time period. + // Fetch provider's appointments that belong to the sync + // time period. $sync_past_days = $this->providers_model ->get_setting('sync_past_days', $provider['id']); $sync_future_days = $this->providers_model ->get_setting('sync_future_days', $provider['id']); - $start = strtotime('-' . $sync_past_days . ' days', - strtotime(date('Y-m-d'))); - $end = strtotime('+' . $sync_future_days . ' days', - strtotime(date('Y-m-d'))); + $start = strtotime('-' . $sync_past_days + . ' days', strtotime(date('Y-m-d'))); + $end = strtotime('+' . $sync_future_days + . ' days', strtotime(date('Y-m-d'))); $where_clause = array( 'start_datetime >=' => date('Y-m-d H:i:s', $start), 'end_datetime <=' => date('Y-m-d H:i:s', $end), - 'id_users_provider' => $provider['id'], - 'is_unavailable' => FALSE + 'id_users_provider' => $provider['id'] ); - $appointments = $this->appointments_model - ->get_batch($where_clause); + $appointments = + $this->appointments_model->get_batch($where_clause); $company_settings = array( - 'company_name' => - $this->settings_model->get_setting('company_name'), - 'company_link' => - $this->settings_model->get_setting('company_link'), - 'company_email' => - $this->settings_model->get_setting('company_email') + 'company_name' => $this->settings_model + ->get_setting('company_name'), + 'company_link' => $this->settings_model + ->get_setting('company_link'), + 'company_email' => $this->settings_model + ->get_setting('company_email') ); - // Sync each appointment with Google Calendar by following + // Sync each appointment with Google Calendar by following // the project's sync protocol (see documentation). foreach($appointments as $appointment) { - $service = $this->services_model - ->get_row($appointment['id_services']); - $customer = $this->customers_model - ->get_row($appointment['id_users_customer']); + if ($appointment['is_unavailable'] == FALSE) { + $service = $this->services_model + ->get_row($appointment['id_services']); + $customer = $this->customers_model + ->get_row($appointment['id_users_customer']); + } else { + $service = NULL; + $customer = NULL; + } // If current appointment not synced yet, add to gcal. if ($appointment['id_google_calendar'] == NULL) { $google_event = $this->google_sync - ->add_appointment($appointment, $provider, $service, - $customer, $company_settings); + ->add_appointment($appointment, $provider, + $service, $customer, $company_settings); $appointment['id_google_calendar'] = $google_event->id; - $this->appointments_model->add($appointment); // Save gcal id + $this->appointments_model->add($appointment);// Save gcal id } else { // Appointment is synced with google calendar. try { $google_event = $this->google_sync ->get_event($appointment['id_google_calendar']); - // If gcal event is different from e!a appointment then - // update Easy!Appointments record. + if ($google_event->status == 'cancelled') { + throw new Exception('Event is cancelled, remove the ' + . 'record from Easy!Appointments.'); + } + + // If gcal event is different from e!a appointment + // then update e!a record. $is_different = FALSE; $appt_start = strtotime($appointment['start_datetime']); $appt_end = strtotime($appointment['end_datetime']); $event_start = - strtotime($google_event->getStart()->getDateTime()); + strtotime($google_event->getStart()->getDateTime()); $event_end = - strtotime($google_event->getEnd()->getDateTime()); + strtotime($google_event->getEnd()->getDateTime()); if ($appt_start != $event_start || $appt_end != $event_end) { @@ -146,7 +155,6 @@ public function sync($provider_id = NULL) { 'id_users_customer' => NULL, 'id_services' => NULL, ); - $this->appointments_model->add($appointment); } } diff --git a/doc/thesis/snippets/provider_available_periods.php b/doc/thesis/snippets/provider_available_periods.php index 157835ec..86504815 100644 --- a/doc/thesis/snippets/provider_available_periods.php +++ b/doc/thesis/snippets/provider_available_periods.php @@ -6,16 +6,16 @@ * This method is very important because there are many cases where * the system needs to know when a provider is avaible for an * appointment. This method will return an array that belongs to the - * selected date and contains values that have the start and the end + * selected date and contains values that have the start and the end * time of an available time period. * * @param numeric $provider_id The provider's record id. - * @param string $selected_date The date to be checked (MySQL + * @param string $selected_date The date to be checked (MySQL * formatted string). - * @param array $exclude_appointments This array contains the ids of - * the appointments that will not be taken into consideration when - * the available time periods are calculated. - * @return array Returns an array with the available time periods of + * @param array $exclude_appointments This array contains the ids of + * the appointments that will not be taken into consideration when the + * available time periods are calculated. + * @return array Returns an array with the available time periods of * the provider. */ private function get_provider_available_time_periods($provider_id, @@ -28,16 +28,15 @@ private function get_provider_available_time_periods($provider_id, ->get_setting('working_plan', $provider_id), true); $where_clause = array( - 'DATE(start_datetime)' => - date('Y-m-d', strtotime($selected_date)), - 'id_users_provider' => $provider_id + 'DATE(start_datetime)'=>date('Y-m-d', strtotime($selected_date)), + 'id_users_provider'=>$provider_id ); - $reserved_appointments = - $this->appointments_model->get_batch($where_clause); + $reserved_appointments = $this->appointments_model + ->get_batch($where_clause); // Sometimes it might be necessary to not take into account some - // appointment records in order to display what the providers' + // appointment records in order to display what the providers' // available time periods would be without them. foreach ($exclude_appointments as $excluded_id) { foreach ($reserved_appointments as $index => $reserved) { @@ -47,12 +46,11 @@ private function get_provider_available_time_periods($provider_id, } } - // Find the empty spaces on the plan. The first split between - // the plan is due to a break (if exist). After that every - // reserved appointment is considered to be a taken space in the - // plan. + // Find the empty spaces on the plan. The first split between the + // plan is due to a break (if exist). After that every reserved + // appointment is considered to be a taken space in the plan. $selected_date_working_plan = - $working_plan[strtolower(date('l', strtotime($selected_date)))]; + $working_plan[strtolower(date('l', strtotime($selected_date)))]; $available_periods_with_breaks = array(); if (isset($selected_date_working_plan['breaks'])) { @@ -65,8 +63,8 @@ private function get_provider_available_time_periods($provider_id, $start_hour = $selected_date_working_plan['start']; $end_hour = $break['start']; } else { - $start_hour = $selected_date_working_plan - ['breaks'][$last_break_index]['end']; + $start_hour = $selected_date_working_plan['breaks'] + [$last_break_index]['end']; $end_hour = $break['start']; } @@ -75,118 +73,67 @@ private function get_provider_available_time_periods($provider_id, 'end' => $end_hour ); } - // Add the period from the last break to the end of the day. $available_periods_with_breaks[] = array( - 'start' =>$selected_date_working_plan['breaks'][$index]['end'], - 'end' => $selected_date_working_plan['end'] + 'start'=>$selected_date_working_plan['breaks'][$index]['end'], + 'end'=>$selected_date_working_plan['end'] ); } // Break the empty periods with the reserved appointments. - $available_periods_with_appointments = array(); + $available_periods_with_appointments = + $available_periods_with_breaks; - if (count($reserved_appointments) > 0) { - - foreach($available_periods_with_breaks as $period) { - - foreach($reserved_appointments as $index=>$reserved) { - $appointment_start = - date('H:i', strtotime($reserved['start_datetime'])); - $appointment_end = - date('H:i', strtotime($reserved['end_datetime'])); - $period_start = - date('H:i', strtotime($period['start'])); - $period_end = - date('H:i', strtotime($period['end'])); - - if ($period_start <= $appointment_start - && $period_end >= $appointment_end) { - // We need to check whether another appointment fits - // in the current time period. If this happens, then - // we need to consider the whole appointment time as - // one, because the provider will not be available. - foreach ($reserved_appointments as $tmp_appointment) { - $appt_start = date('H:i', - strtotime($tmp_appointment['start_datetime'])); - $appt_end = date('H:i', - strtotime($tmp_appointment['end_datetime'])); - - if ($period_start < $appt_start - && $period_end > $appt_end) { - if ($appointment_start > $appt_start) { - $appointment_start = $appt_start; - } - - if ($appointment_end < $appt_end) { - $appointment_end = $appt_end; - } - } - } + foreach($reserved_appointments as $appointment) { + foreach($available_periods_with_appointments as $index=>&$period){ + $a_start = + date('H:i',strtotime($appointment['start_datetime'])); + $a_end = + date('H:i', strtotime($appointment['end_datetime'])); + $p_start = + date('H:i', strtotime($period['start'])); + $p_end = + date('H:i', strtotime($period['end'])); - // Current appointment is within the current - // empty space. So we need to break the empty - // space into two other spaces that don't include - // the appointment. - $new_period = array( - 'start' => $period_start, - 'end' => $appointment_start - ); - - if (!in_array($new_period, - $available_periods_with_appointments)) { - $available_periods_with_appointments[] = $new_period; - } - - $new_period = array( - 'start' => $appointment_end, - 'end' => $period_end - ); - - if (!in_array($new_period, - $available_periods_with_appointments)) { - $available_periods_with_appointments[] = $new_period; - } - - } else { - // Check if there are any other appointments - // between this time space. If not, it is going - // to be added as it is. - $found = FALSE; - - foreach ($reserved_appointments as $tmp_appointment) { - $appt_start = date('H:i', - strtotime($tmp_appointment['start_datetime'])); - $appt_end = date('H:i', - strtotime($tmp_appointment['end_datetime'])); - - if ($period_start < $appt_start - && $period_end > $appt_end) { - $found = TRUE; - } - } - - // It is also necessary to check that this time - // period doesn't already exist in the - // "$empty_spaces_with_appointments" array. - $empty_period = array( - 'start' => $period_start, - 'end' => $period_end - ); - - $already_exist = in_array($empty_period, - $available_periods_with_appointments); - - if ($found === FALSE && $already_exist === FALSE) { - $available_periods_with_appointments[] = $empty_period; - } - } + if ($a_start <= $p_start && $a_end <= $p_end + && $a_end <= $p_start) { + // The appointment does not belong in this time period, so + // we will not change anything. + } else if ($a_start <= $p_start && $a_end <= $p_end + && $a_end >= $p_start) { + // The appointment starts before the period and finishes + // somewhere inside.We will need to break this period and + // leave the available part. + $period['start'] = $a_end; + } else if ($a_start >= $p_start && $a_end <= $p_start) { + // The appointment is inside the time period, so we will + // split the period into two new others. + unset($available_periods_with_appointments[$index]); + $available_periods_with_appointments[] = array( + 'start' => $p_start, + 'end' => $a_start + ); + $available_periods_with_appointments[] = array( + 'start' => $a_end, + 'end' => $p_end + ); + } else if ($a_start >= $p_start && $a_end >= $p_start + && $a_start <= $p_end) { + // The appointment starts in the period and finishes out + // of it. We will need to remove the time that is taken + // from the appointment. + $period['end'] = $a_start; + } else if ($a_start >= $p_start && $a_end >= $p_end + && $a_start >= $p_end) { + // The appointment does not belong in the period so do not + // change anything. + } else if ($a_start <= $p_start && $a_end >= $p_end + && $a_start <= $p_end) { + // The appointment is bigger than the period, so this period + // needs to be removed. + unset($available_periods_with_appointments[$index]); } } - } else { - $available_periods_with_appointments = - $available_periods_with_breaks; } - - return $available_periods_with_appointments; + return array_values($available_periods_with_appointments); } \ No newline at end of file diff --git a/doc/thesis/thesis.pdf b/doc/thesis/thesis.pdf index a76de7fb..d0bf701e 100644 Binary files a/doc/thesis/thesis.pdf and b/doc/thesis/thesis.pdf differ diff --git a/rsc/misc/ebooks/listings.pdf b/rsc/misc/ebooks/listings.pdf new file mode 100644 index 00000000..2e8f263b Binary files /dev/null and b/rsc/misc/ebooks/listings.pdf differ diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php index c9d28702..a7ed1e32 100644 --- a/src/application/controllers/appointments.php +++ b/src/application/controllers/appointments.php @@ -513,92 +513,53 @@ class Appointments extends CI_Controller { } // Break the empty periods with the reserved appointments. - $available_periods_with_appointments = array(); + $available_periods_with_appointments = $available_periods_with_breaks; - if (count($reserved_appointments) > 0) { + foreach($reserved_appointments as $appointment) { + foreach($available_periods_with_appointments as $index => &$period) { - foreach($available_periods_with_breaks as $period) { - - foreach($reserved_appointments as $index=>$reserved) { - $appointment_start = date('H:i', strtotime($reserved['start_datetime'])); - $appointment_end = date('H:i', strtotime($reserved['end_datetime'])); - $period_start = date('H:i', strtotime($period['start'])); - $period_end = date('H:i', strtotime($period['end'])); - - if ($period_start <= $appointment_start && $period_end >= $appointment_end) { - // We need to check whether another appointment fits in the current - // time period. If this happens, then we need to consider the whole - // appointment time as one, because the provider will not be available. - foreach ($reserved_appointments as $tmp_appointment) { - $appt_start = date('H:i', strtotime($tmp_appointment['start_datetime'])); - $appt_end = date('H:i', strtotime($tmp_appointment['end_datetime'])); - - if ($period_start < $appt_start && $period_end > $appt_end) { - if ($appointment_start > $appt_start) { - $appointment_start = $appt_start; - } - - if ($appointment_end < $appt_end) { - $appointment_end = $appt_end; - } - } - } + $a_start = date('H:i', strtotime($appointment['start_datetime'])); + $a_end = date('H:i', strtotime($appointment['end_datetime'])); + $p_start = date('H:i', strtotime($period['start'])); + $p_end = date('H:i', strtotime($period['end'])); - // Current appointment is within the current empty space. So - // we need to break the empty space into two other spaces that - // don't include the appointment. - $new_period = array( - 'start' => $period_start, - 'end' => $appointment_start - ); - - if (!in_array($new_period, $available_periods_with_appointments)) { - $available_periods_with_appointments[] = $new_period; - } - - $new_period = array( - 'start' => $appointment_end, - 'end' => $period_end - ); - - if (!in_array($new_period, $available_periods_with_appointments)) { - $available_periods_with_appointments[] = $new_period; - } - - } else { - // Check if there are any other appointments between this - // time space. If not, it is going to be added as it is. - $found = FALSE; - - foreach ($reserved_appointments as $tmp_appointment) { - $appt_start = date('H:i', strtotime($tmp_appointment['start_datetime'])); - $appt_end = date('H:i', strtotime($tmp_appointment['end_datetime'])); - - if ($period_start < $appt_start && $period_end > $appt_end) { - $found = TRUE; - } - } - - // It is also necessary to check that this time period doesn't - // already exist in the "$empty_spaces_with_appointments" array. - $empty_period = array( - 'start' => $period_start, - 'end' => $period_end - ); - - $already_exist = in_array($empty_period, $available_periods_with_appointments); - - if ($found === FALSE && $already_exist === FALSE) { - $available_periods_with_appointments[] = $empty_period; - } - } + if ($a_start <= $p_start && $a_end <= $p_end && $a_end <= $p_start) { + // The appointment does not belong in this time period, so we + // will not change anything. + } else if ($a_start <= $p_start && $a_end <= $p_end && $a_end >= $p_start) { + // The appointment starts before the period and finishes somewhere inside. + // We will need to break this period and leave the available part. + $period['start'] = $a_end; + + } else if ($a_start >= $p_start && $a_end <= $p_start) { + // The appointment is inside the time period, so we will split the period + // into two new others. + unset($available_periods_with_appointments[$index]); + $available_periods_with_appointments[] = array( + 'start' => $p_start, + 'end' => $a_start + ); + $available_periods_with_appointments[] = array( + 'start' => $a_end, + 'end' => $p_end + ); + + } else if ($a_start >= $p_start && $a_end >= $p_start && $a_start <= $p_end) { + // The appointment starts in the period and finishes out of it. We will + // need to remove the time that is taken from the appointment. + $period['end'] = $a_start; + + } else if ($a_start >= $p_start && $a_end >= $p_end && $a_start >= $p_end) { + // The appointment does not belong in the period so do not change anything. + } else if ($a_start <= $p_start && $a_end >= $p_end && $a_start <= $p_end) { + // The appointment is bigger than the period, so this period needs to be + // removed. + unset($available_periods_with_appointments[$index]); } } - } else { - $available_periods_with_appointments = $available_periods_with_breaks; } - return $available_periods_with_appointments; + return array_values($available_periods_with_appointments); } /** diff --git a/src/application/controllers/google.php b/src/application/controllers/google.php index 6e6484b8..80e95639 100644 --- a/src/application/controllers/google.php +++ b/src/application/controllers/google.php @@ -112,8 +112,7 @@ class Google extends CI_Controller { $where_clause = array( 'start_datetime >=' => date('Y-m-d H:i:s', $start), 'end_datetime <=' => date('Y-m-d H:i:s', $end), - 'id_users_provider' => $provider['id'], - 'is_unavailable' => FALSE + 'id_users_provider' => $provider['id'] ); $appointments = $this->appointments_model->get_batch($where_clause); @@ -127,8 +126,13 @@ class Google extends CI_Controller { // Sync each appointment with Google Calendar by following the project's sync // protocol (see documentation). foreach($appointments as $appointment) { - $service = $this->services_model->get_row($appointment['id_services']); - $customer = $this->customers_model->get_row($appointment['id_users_customer']); + if ($appointment['is_unavailable'] == FALSE) { + $service = $this->services_model->get_row($appointment['id_services']); + $customer = $this->customers_model->get_row($appointment['id_users_customer']); + } else { + $service = NULL; + $customer = NULL; + } // If current appointment not synced yet, add to gcal. if ($appointment['id_google_calendar'] == NULL) { @@ -141,6 +145,10 @@ class Google extends CI_Controller { try { $google_event = $this->google_sync->get_event($appointment['id_google_calendar']); + if ($google_event->status == 'cancelled') { + throw new Exception('Event is cancelled, remove the record from Easy!Appointments.'); + } + // If gcal event is different from e!a appointment then update e!a record. $is_different = FALSE; $appt_start = strtotime($appointment['start_datetime']); diff --git a/src/application/libraries/google_sync.php b/src/application/libraries/google_sync.php index e4e95f2b..6e9efe16 100644 --- a/src/application/libraries/google_sync.php +++ b/src/application/libraries/google_sync.php @@ -108,7 +108,7 @@ class Google_Sync { $this->CI->load->helper('general'); $event = new Google_Event(); - $event->setSummary($service['name']); + $event->setSummary(($service != NULL) ? $service['name'] : 'Unavailable'); $event->setLocation($company_settings['company_name']); $start = new Google_EventDateTime(); @@ -119,20 +119,21 @@ class Google_Sync { $end->setDateTime(date3339(strtotime($appointment['end_datetime']))); $event->setEnd($end); - $eventProvider = new Google_EventAttendee(); - $eventProvider->setDisplayName($provider['first_name'] . ' ' + $event->attendees = array(); + + $event_provider = new Google_EventAttendee(); + $event_provider->setDisplayName($provider['first_name'] . ' ' . $provider['last_name']); - $eventProvider->setEmail($provider['email']); + $event_provider->setEmail($provider['email']); + $event->attendees[] = $event_provider; - $eventCustomer = new Google_EventAttendee(); - $eventCustomer->setDisplayName($customer['first_name'] . ' ' - . $customer['last_name']); - $eventCustomer->setEmail($customer['email']); - - $event->attendees = array( - $eventProvider, - $eventCustomer - ); + if ($customer != NULL) { + $event_customer = new Google_EventAttendee(); + $event_customer->setDisplayName($customer['first_name'] . ' ' + . $customer['last_name']); + $event_customer->setEmail($customer['email']); + $event->attendees[] = $event_customer; + } // Add the new event to the "primary" calendar. $created_event = $this->service->events->insert('primary', $event); @@ -171,20 +172,21 @@ class Google_Sync { $end->setDateTime(date3339(strtotime($appointment['end_datetime']))); $event->setEnd($end); + $event->attendees = array(); + $event_provider = new Google_EventAttendee(); $event_provider->setDisplayName($provider['first_name'] . ' ' . $provider['last_name']); $event_provider->setEmail($provider['email']); + $event->attendees[] = $event_provider; - $event_customer = new Google_EventAttendee(); - $event_customer->setDisplayName($customer['first_name'] . ' ' - . $customer['last_name']); - $event_customer->setEmail($customer['email']); - - $event->attendees = array( - $event_provider, - $event_customer - ); + if ($customer != NULL) { + $event_customer = new Google_EventAttendee(); + $event_customer->setDisplayName($customer['first_name'] . ' ' + . $customer['last_name']); + $event_customer->setEmail($customer['email']); + $event->attendees[] = $event_customer; + } $updated_event = $this->service->events->update('primary', $event->getId(), $event); diff --git a/src/application/views/appointments/book.php b/src/application/views/appointments/book.php index 4b3b4493..86a11639 100644 --- a/src/application/views/appointments/book.php +++ b/src/application/views/appointments/book.php @@ -133,7 +133,7 @@
diff --git a/src/application/views/appointments/cancel.php b/src/application/views/appointments/cancel.php index 3a013c23..f27332b5 100644 --- a/src/application/views/appointments/cancel.php +++ b/src/application/views/appointments/cancel.php @@ -48,7 +48,7 @@