diff --git a/src/application/controllers/appointments.php b/src/application/controllers/appointments.php
index 40d26185..ed510221 100644
--- a/src/application/controllers/appointments.php
+++ b/src/application/controllers/appointments.php
@@ -30,29 +30,25 @@ class Appointments extends CI_Controller {
// Load the appointments data and enable the manage mode of the page.
$manage_mode = TRUE;
- $appointments_results = $this->appointments_model
- ->get_batch(array('hash' => $appointment_hash));
+ $results = $this->appointments_model->get_batch(array('hash' => $appointment_hash));
- if (count($appointments_results) === 0) {
+ if (count($results) === 0) {
// The requested appointment doesn't exist in the database. Display
// a message to the customer.
- $view_data = array(
+ $view = array(
'message_title' => 'Appointment Not Found!',
'message_text' => 'The appointment you requested does not exist in '
. 'the system database anymore.',
'message_icon' => $this->config->item('base_url')
. 'assets/images/error.png'
);
- $this->load->view('appointments/message', $view_data);
+ $this->load->view('appointments/message', $view);
return;
}
- $appointment = $appointments_results[0];
-
- $provider = $this->providers_model
- ->get_row($appointment['id_users_provider']);
- $customer = $this->customers_model
- ->get_row($appointment['id_users_customer']);
+ $appointment = $results[0];
+ $provider = $this->providers_model->get_row($appointment['id_users_provider']);
+ $customer = $this->customers_model->get_row($appointment['id_users_customer']);
} else {
// The customer is going to book a new appointment so there is no
@@ -64,7 +60,7 @@ class Appointments extends CI_Controller {
}
// Load the book appointment view.
- $view_data = array (
+ $view = array (
'available_services' => $available_services,
'available_providers' => $available_providers,
'company_name' => $company_name,
@@ -75,10 +71,10 @@ class Appointments extends CI_Controller {
);
} catch(Exception $exc) {
- $view_data['exceptions'][] = $exc;
+ $view['exceptions'][] = $exc;
}
- $this->load->view('appointments/book', $view_data);
+ $this->load->view('appointments/book', $view);
} else {
// The page is a post-back. Register the appointment and send notification emails
@@ -135,7 +131,7 @@ class Appointments extends CI_Controller {
}
}
} catch(Exception $exc) {
- $view_data['exceptions'][] = $exc;
+ $view['exceptions'][] = $exc;
}
// :: SEND NOTIFICATION EMAILS TO BOTH CUSTOMER AND PROVIDER
@@ -175,11 +171,11 @@ class Appointments extends CI_Controller {
$service, $customer, $company_settings, $provider_title,
$provider_message, $provider_link, $provider['email']);
} catch(Exception $exc) {
- $view_data['exceptions'][] = $exc;
+ $view['exceptions'][] = $exc;
}
// :: LOAD THE BOOK SUCCESS VIEW
- $view_data = array(
+ $view = array(
'appointment_data' => $appointment,
'provider_data' => $provider,
'service_data' => $service,
@@ -187,10 +183,10 @@ class Appointments extends CI_Controller {
);
} catch(Exception $exc) {
- $view_data['exceptions'][] = $exc;
+ $view['exceptions'][] = $exc;
}
- $this->load->view('appointments/book_success', $view_data);
+ $this->load->view('appointments/book_success', $view);
}
}
@@ -273,10 +269,10 @@ class Appointments extends CI_Controller {
}
if (isset($exceptions)) {
- $view_data['exceptions'] = $exceptions;
+ $view['exceptions'] = $exceptions;
}
- $this->load->view('appointments/cancel', $view_data);
+ $this->load->view('appointments/cancel', $view);
}
/**
@@ -451,12 +447,12 @@ class Appointments extends CI_Controller {
$this->load->model('providers_model');
// Get the provider's working plan and reserved appointments.
- $working_plan = json_decode($this->providers_model
- ->get_setting('working_plan', $provider_id), true);
+ $working_plan = json_decode($this->providers_model->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);
@@ -474,8 +470,7 @@ class Appointments extends CI_Controller {
// 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)))];
+ $selected_date_working_plan = $working_plan[strtolower(date('l', strtotime($selected_date)))];
$available_periods_with_breaks = array();
if (isset($selected_date_working_plan['breaks'])) {
@@ -486,22 +481,22 @@ class Appointments extends CI_Controller {
if (count($available_periods_with_breaks) === 0) {
$start_hour = $selected_date_working_plan['start'];
- $end_hour = $break['start'];
+ $end_hour = $break['start'];
} else {
$start_hour = $selected_date_working_plan['breaks'][$last_break_index]['end'];
- $end_hour = $break['start'];
+ $end_hour = $break['start'];
}
$available_periods_with_breaks[] = array(
'start' => $start_hour,
- 'end' => $end_hour
+ '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']
+ 'end' => $selected_date_working_plan['end']
);
}
@@ -512,19 +507,19 @@ class Appointments extends CI_Controller {
foreach($available_periods_with_breaks as $period) {
- foreach($reserved_appointments as $index=>$excluded_appointment) {
- $appointment_start = date('H:i', strtotime($excluded_appointment['start_datetime']));
- $appointment_end = date('H:i', strtotime($excluded_appointment['end_datetime']));
- $period_start = date('H:i', strtotime($period['start']));
- $period_end = date('H:i', strtotime($period['end']));
+ foreach($reserved_appointments as $index=>$appointment) {
+ $appointment_start = date('H:i', strtotime($appointment['start_datetime']));
+ $appointment_end = date('H:i', strtotime($appointment['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) {
+ 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']));
+ $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) {
diff --git a/src/application/controllers/backend.php b/src/application/controllers/backend.php
index a548d1ad..33be1ed4 100644
--- a/src/application/controllers/backend.php
+++ b/src/application/controllers/backend.php
@@ -8,23 +8,34 @@ class Backend extends CI_Controller {
* view this page which displays a calendar with the events of the selected
* provider or service. If a user has more priviledges he will see more menus
* at the top of the page.
+ *
+ * @param string $appointment_hash If given, the appointment edit dialog will
+ * appear when the page loads.
*/
- public function index() {
+ public function index($appointment_hash = '') {
// @task Require user to be logged in the application.
+ $this->load->model('appointments_model');
$this->load->model('providers_model');
$this->load->model('services_model');
$this->load->model('settings_model');
- $view_data['base_url'] = $this->config->item('base_url');
- $view_data['book_advance_timeout'] = $this->settings_model->get_setting('book_advance_timeout');
- $view_data['company_name'] = $this->settings_model->get_setting('company_name');
- $view_data['available_providers'] = $this->providers_model->get_available_providers();
- $view_data['available_services'] = $this->services_model->get_available_services();
+ $view['base_url'] = $this->config->item('base_url');
+ $view['book_advance_timeout'] = $this->settings_model->get_setting('book_advance_timeout');
+ $view['company_name'] = $this->settings_model->get_setting('company_name');
+ $view['available_providers'] = $this->providers_model->get_available_providers();
+ $view['available_services'] = $this->services_model->get_available_services();
- $this->load->view('backend/header', $view_data);
- $this->load->view('backend/calendar', $view_data);
- $this->load->view('backend/footer', $view_data);
+ if ($appointment_hash != '') {
+ $results = $this->appointments_model->get_batch(array('hash' => $appointment_hash));
+ $view['edit_appointment'] = $results[0]; // This will display the appointment edit dialog on page load.
+ } else {
+ $view['edit_appointment'] = NULL;
+ }
+
+ $this->load->view('backend/header', $view);
+ $this->load->view('backend/calendar', $view);
+ $this->load->view('backend/footer', $view);
}
/**
@@ -40,15 +51,15 @@ class Backend extends CI_Controller {
$this->load->model('services_model');
$this->load->model('settings_model');
- $view_data['base_url'] = $this->config->item('base_url');
- $view_data['company_name'] = $this->settings_model->get_setting('company_name');
- $view_data['customers'] = $this->customers_model->get_batch();
- $view_data['available_providers'] = $this->providers_model->get_available_providers();
- $view_data['available_services'] = $this->services_model->get_available_services();
+ $view['base_url'] = $this->config->item('base_url');
+ $view['company_name'] = $this->settings_model->get_setting('company_name');
+ $view['customers'] = $this->customers_model->get_batch();
+ $view['available_providers'] = $this->providers_model->get_available_providers();
+ $view['available_services'] = $this->services_model->get_available_services();
- $this->load->view('backend/header', $view_data);
- $this->load->view('backend/customers', $view_data);
- $this->load->view('backend/footer', $view_data);
+ $this->load->view('backend/header', $view);
+ $this->load->view('backend/customers', $view);
+ $this->load->view('backend/footer', $view);
}
public function services() {
diff --git a/src/application/controllers/backend_api.php b/src/application/controllers/backend_api.php
index 656e44e6..42476282 100644
--- a/src/application/controllers/backend_api.php
+++ b/src/application/controllers/backend_api.php
@@ -7,12 +7,11 @@ class Backend_api extends CI_Controller {
/**
* [AJAX] Get the registered appointments for the given date period and record.
*
- * This method returns the database appointments for the user selected date
- * period and record type (provider or service).
+ * This method returns the database appointments and unavailable periods for the
+ * user selected date period and record type (provider or service).
*
* @param {numeric} $_POST['record_id'] Selected record id.
- * @param {string} $_POST['filter_type'] Could be either FILTER_TYPE_PROVIDER
- * or FILTER_TYPE_SERVICE.
+ * @param {string} $_POST['filter_type'] Could be either FILTER_TYPE_PROVIDER or FILTER_TYPE_SERVICE.
* @param {string} $_POST['start_date'] The user selected start date.
* @param {string} $_POST['end_date'] The user selected end date.
*/
@@ -29,23 +28,35 @@ class Backend_api extends CI_Controller {
$where_id = 'id_services';
}
+ // Get appointments
$where_clause = array(
$where_id => $_POST['record_id'],
'start_datetime >=' => $_POST['start_date'],
- 'end_datetime <=' => $_POST['end_date']
+ 'end_datetime <=' => $_POST['end_date'],
+ 'is_unavailable' => FALSE
);
+
+ $response['appointments'] = $this->appointments_model->get_batch($where_clause);
- $appointments = $this->appointments_model->get_batch($where_clause);
-
- foreach($appointments as &$appointment) {
- if ($appointment['is_unavailable'] == FALSE) {
- $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
- $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
- $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']);
- }
+ foreach($response['appointments'] as &$appointment) {
+ $appointment['provider'] = $this->providers_model->get_row($appointment['id_users_provider']);
+ $appointment['service'] = $this->services_model->get_row($appointment['id_services']);
+ $appointment['customer'] = $this->customers_model->get_row($appointment['id_users_customer']);
}
-
- echo json_encode($appointments);
+
+ // Get unavailable periods (only for provider).
+ if ($_POST['filter_type'] == FILTER_TYPE_PROVIDER) {
+ $where_clause = array(
+ $where_id => $_POST['record_id'],
+ 'start_datetime >=' => $_POST['start_date'],
+ 'end_datetime <=' => $_POST['end_date'],
+ 'is_unavailable' => TRUE
+ );
+
+ $response['unavailables'] = $this->appointments_model->get_batch($where_clause);
+ }
+
+ echo json_encode($response);
} catch(Exception $exc) {
echo json_encode(array(
@@ -205,10 +216,10 @@ class Backend_api extends CI_Controller {
$this->load->model('services_model');
$this->load->model('settings_model');
- $appointment_data = $this->appointments_model->get_row($_POST['appointment_id']);
- $provider_data = $this->providers_model->get_row($appointment_data['id_users_provider']);
- $customer_data = $this->customers_model->get_row($appointment_data['id_users_customer']);
- $service_data = $this->services_model->get_row($appointment_data['id_services']);
+ $appointment = $this->appointments_model->get_row($_POST['appointment_id']);
+ $provider = $this->providers_model->get_row($appointment['id_users_provider']);
+ $customer = $this->customers_model->get_row($appointment['id_users_customer']);
+ $service = $this->services_model->get_row($appointment['id_services']);
$company_settings = array(
'company_name' => $this->settings_model->get_setting('company_name'),
@@ -220,16 +231,16 @@ class Backend_api extends CI_Controller {
$this->appointments_model->delete($_POST['appointment_id']);
// :: SYNC DELETE WITH GOOGLE CALENDAR
- if ($appointment_data['id_google_calendar'] != NULL) {
+ if ($appointment['id_google_calendar'] != NULL) {
try {
- $google_sync = $this->providers_model->get_setting('google_sync', $provider_data['id']);
+ $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
if ($google_sync == TRUE) {
$google_token = json_decode($this->providers_model
- ->get_setting('google_token', $provider_data['id']));
+ ->get_setting('google_token', $provider['id']));
$this->load->library('Google_Sync');
$this->google_sync->refresh_token($google_token->refresh_token);
- $this->google_sync->delete_appointment($appointment_data['id_google_calendar']);
+ $this->google_sync->delete_appointment($appointment['id_google_calendar']);
}
} catch(Exception $exc) {
$warnings[] = exceptionToJavascript($exc);
@@ -239,11 +250,11 @@ class Backend_api extends CI_Controller {
// :: SEND NOTIFICATION EMAILS TO PROVIDER AND CUSTOMER
try {
$this->load->library('Notifications');
- $this->notifications->send_delete_appointment($appointment_data, $provider_data,
- $service_data, $customer_data, $company_settings, $provider_data['email'],
+ $this->notifications->send_delete_appointment($appointment, $provider,
+ $service, $customer, $company_settings, $provider['email'],
$_POST['delete_reason']);
- $this->notifications->send_delete_appointment($appointment_data, $provider_data,
- $service_data, $customer_data, $company_settings, $customer_data['email'],
+ $this->notifications->send_delete_appointment($appointment, $provider,
+ $service, $customer, $company_settings, $customer['email'],
$_POST['delete_reason']);
} catch(Exception $exc) {
$warnings[] = exceptionToJavascript($exc);
@@ -334,7 +345,8 @@ class Backend_api extends CI_Controller {
// Add appointment
$unavailable = json_decode($_POST['unavailable'], true);
- $this->appointments_model->add_unavailable($unavailable);
+ $unavailable['id'] = $this->appointments_model->add_unavailable($unavailable);
+ $unavailable = $this->appointments_model->get_row($unavailable['id']);
// Google Sync
try {
@@ -348,11 +360,13 @@ class Backend_api extends CI_Controller {
$this->load->library('google_sync');
$this->google_sync->refresh_token($google_token->refresh_token);
- // @task Sync with gcal.
- $google_event = $this->google_sync->add_unavailable($unavailable);
-
- $unavailable['id_google_calendar'] = $google_event->id;
- $this->appointments_model->add_unavailable($unavailable);
+ if ($unavailable['id_google_calendar'] == NULL) {
+ $google_event = $this->google_sync->add_unavailable($unavailable);
+ $unavailable['id_google_calendar'] = $google_event->id;
+ $this->appointments_model->add_unavailable($unavailable);
+ } else {
+ $google_event = $this->google_sync->update_unavailable($unavailable);
+ }
}
} catch(Exception $exc) {
$warnings[] = $exc;
@@ -380,9 +394,35 @@ class Backend_api extends CI_Controller {
*/
public function ajax_delete_unavailable() {
try {
- // Delete unavailable
+ $this->load->model('appointments_model');
+ $this->load->model('providers_model');
+
+ $unavailable = $this->appointments_model->get_row($_POST['unavailable_id']);
+ $provider = $this->providers_model->get_row($unavailable['id_users_provider']);
+
+ // Delete unavailable
+ $this->appointments_model->delete_unavailable($unavailable['id']);
// Google Sync
+ try {
+ $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
+ if ($google_sync == TRUE) {
+ $google_token = json_decode($this->providers_model->get_setting('google_token', $provider['id']));
+ $this->load->library('google_sync');
+ $this->google_sync->refresh_token($google_token->refresh_token);
+ $this->google_sync->delete_unavailable($unavailable['id_google_calendar']);
+ }
+ } catch(Exception $exc) {
+ $warnings[] = $exc;
+ }
+
+ if (isset($warnings)) {
+ echo json_encode(array(
+ 'warnings' => $warnings
+ ));
+ } else {
+ echo json_encode('SUCCESS');
+ }
} catch(Exception $exc) {
echo json_encode(array(
diff --git a/src/application/controllers/google.php b/src/application/controllers/google.php
index 0a0a5a5c..fcf25975 100644
--- a/src/application/controllers/google.php
+++ b/src/application/controllers/google.php
@@ -63,6 +63,125 @@ class Google extends CI_Controller {
echo '
Authorization Failed! ';
}
}
+
+ /**
+ * Complete synchronization of appointments between Google Calendar and Easy!Appointments.
+ *
+ * This method will completely sync the appointments of a provider with his Google Calendar
+ * account. The sync period needs to be relatively small, because a lot of API calls might
+ * be necessary and this will lead to consuming the Google limit for the Calendar API usage.
+ *
+ * @param numeric $provider_id Provider record to be synced.
+ *
+ * @task This method must be executed only by the system and noone else outside. It is a big security issue.
+ */
+ public function sync($provider_id = NULL) {
+ try {
+ if ($provider_id === NULL) {
+ throw new Exception('Provider id not specified.');
+ }
+
+ $this->load->model('appointments_model');
+ $this->load->model('providers_model');
+ $this->load->model('services_model');
+ $this->load->model('customers_model');
+ $this->load->model('settings_model');
+
+ $provider = $this->providers_model->get_row($provider_id);
+
+ // Check whether the selected provider has google sync enabled.
+ $google_sync = $this->providers_model->get_setting('google_sync', $provider['id']);
+ if (!$google_sync) {
+ throw new Exception('The selected provider has not the google synchronization '
+ . 'setting enabled.');
+ }
+
+ $google_token = json_decode($this->providers_model->get_setting('google_token', $provider['id']));
+ $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.
+ $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')));
+
+ $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
+ );
+
+ $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')
+ );
+
+ // 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']);
+
+ // :: APPOINTMENT WITH NO GCAL_ID -> ADD TO GCAL
+ if ($appointment['id_google_calendar'] == NULL) {
+ $google_event = $this->google_sync->add_appointment($appointment, $provider,
+ $service, $customer, $company_settings);
+ $appointment['id_google_calendar'] = $google_event->id;
+ $this->appointments_model->add($appointment); // save gcal id
+ }
+
+ // :: SYNCED APPOINTMENT NOT FOUND ON GCAL -> DELETE E!A RECORD
+ if ($appointment['id_google_calendar'] != NULL) {
+ try {
+ $google_event = $this->google_sync->get_event($appointment['id_google_calendar']);
+ } catch(Exception $exc) {
+ $this->appointments_model->delete($appointment['id']);
+ $appointment['id_google_calendar'] = NULL; // Do not proceed with the rest sync actions.
+ }
+ }
+
+ // :: SYNCED APPOINTMENT DIFFERENT FROM GCAL EVENT -> UPDATE E!A RECORD
+ if ($appointment['id_google_calendar'] != NULL) {
+ $is_different = FALSE;
+ $appt_start = strtotime($appointment['start_datetime']);
+ $appt_end = strtotime($appointment['end_datetime']);
+ $event_start = strtotime($google_event->getStart()->getDateTime());
+ $event_end = strtotime($google_event->getEnd()->getDateTime());
+
+ if ($appt_start != $event_start
+ || $appt_end != $event_end) {
+ $is_different = TRUE;
+ }
+
+ if ($is_different) {
+ $appointment['start_datetime'] = date('Y-m-d H:i:s', $event_start);
+ $appointment['end_datetime'] = date('Y-m-d H:i:s', $event_end);
+ $this->appointments_model->add($appointment);
+ }
+
+ }
+
+ // @task :: GCAL EVENT NOT FOUND ON E!A -> ADD EVENT TO E!A
+
+
+ }
+
+ // @task Sync unavailable periods with Google Calendar
+
+
+ echo json_encode('SUCCESS');
+
+ } catch(Exception $exc) {
+ echo json_encode(array(
+ 'exceptions' => array($exc)
+ ));
+ }
+ }
}
/* End of file google.php */
diff --git a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php
index 7b502405..083acfc0 100644
--- a/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php
+++ b/src/application/libraries/Unit_tests/drivers/Unit_tests_appointments_model.php
@@ -82,6 +82,7 @@ class Unit_tests_appointments_model extends CI_Driver {
unset($db_data['hash']);
unset($db_data['book_datetime']);
unset($db_data['id_google_calendar']);
+ unset($db_data['is_unavailable']);
$this->CI->unit->run($appointment, $db_data, 'Test if add() appointment (insert '
. 'operation) has successfully inserted a record.');
@@ -438,6 +439,7 @@ class Unit_tests_appointments_model extends CI_Driver {
$db_data = $this->CI->appointments_model->get_row($appointment['id']);
unset($db_data['book_datetime']);
unset($db_data['id_google_calendar']);
+ unset($db_data['is_unavailable']);
// Check if this is the record we seek.
$this->CI->unit->run($db_data, $appointment, 'Test get_row() method.');
diff --git a/src/application/libraries/google_sync.php b/src/application/libraries/google_sync.php
index 337f7a51..b37b7c88 100644
--- a/src/application/libraries/google_sync.php
+++ b/src/application/libraries/google_sync.php
@@ -160,8 +160,6 @@ class Google_Sync {
$event = $this->service->events->get('primary', $appointment['id_google_calendar']);
- // Convert event to object
-
$event->setSummary($service['name']);
$event->setLocation($company_settings['company_name']);
@@ -203,8 +201,76 @@ class Google_Sync {
$this->service->events->delete('primary', $google_calendar_id);
}
- public function add_unavailble($unavailable) {
- // @task Implement
+ /**
+ * Add unavailable period event to Google Calendar.
+ *
+ * @param array $unavailable Contains unavailable period's data.
+ * @return Google_Event Returns the google event's object.
+ */
+ public function add_unavailable($unavailable) {
+ $this->CI->load->helper('general');
+
+ $event = new Google_Event();
+ $event->setSummary('Unavailalbe');
+ $event->setDescription($unavailable['notes']);
+
+ $start = new Google_EventDateTime();
+ $start->setDateTime(date3339(strtotime($unavailable['start_datetime'])));
+ $event->setStart($start);
+
+ $end = new Google_EventDateTime();
+ $end->setDateTime(date3339(strtotime($unavailable['end_datetime'])));
+ $event->setEnd($end);
+
+ // Add the new event to the "primary" calendar.
+ $created_event = $this->service->events->insert('primary', $event);
+
+ return $created_event;
+
+ }
+
+ /**
+ * Update Google Calendar unavailable period event.
+ *
+ * @param array $unavailable Contains the unavailable period data.
+ * @return Google_Event Returns the Google_Event object.
+ */
+ public function update_unavailable($unavailable) {
+ $this->CI->load->helper('general');
+
+ $event = $this->service->events->get('primary', $unavailable['id_google_calendar']);
+ $event->setDescription($unavailable['notes']);
+
+ $start = new Google_EventDateTime();
+ $start->setDateTime(date3339(strtotime($unavailable['start_datetime'])));
+ $event->setStart($start);
+
+ $end = new Google_EventDateTime();
+ $end->setDateTime(date3339(strtotime($unavailable['end_datetime'])));
+ $event->setEnd($end);
+
+ $updated_event = $this->service->events->update('primary', $event->getId(), $event);
+
+ return $updated_event;
+ }
+
+ /**
+ * Delete unavailable period event from Google Calendar.
+ *
+ * @param string $google_calendar_id Google Calendar event id to be deleted.
+ */
+ public function delete_unavailable($google_calendar_id) {
+ $this->service->events->delete('primary', $google_calendar_id);
+ }
+
+ /**
+ * Get an event object from gcal
+ *
+ * @param string $google_calendar_id Id of the google calendar event
+ * @return Google_Event Returns the google event object.
+ */
+ public function get_event($google_calendar_id) {
+ return $this->service->events->get('primary', $google_calendar_id);
}
}
diff --git a/src/application/models/appointments_model.php b/src/application/models/appointments_model.php
index b992c4d3..c980011a 100644
--- a/src/application/models/appointments_model.php
+++ b/src/application/models/appointments_model.php
@@ -360,7 +360,7 @@ class Appointments_Model extends CI_Model {
*/
public function delete_unavailable($unavailable_id) {
if (!is_numeric($unavailable_id)) {
- throw new Exception('Invalid argument type $appointment_id (value:"' .
+ throw new Exception('Invalid argument type $unavailable_id (value:"' .
$unavailable_id . '")');
}
diff --git a/src/application/models/customers_model.php b/src/application/models/customers_model.php
index 4cfd1a95..4e118247 100644
--- a/src/application/models/customers_model.php
+++ b/src/application/models/customers_model.php
@@ -129,7 +129,7 @@ class Customers_Model extends CI_Model {
*/
public function find_record_id($customer) {
if (!isset($customer['email'])) {
- throw new InvalidArgumentException('Customer\'s email was not provided : '
+ throw new Exception('Customer\'s email was not provided : '
. print_r($customer, TRUE));
}
@@ -244,7 +244,7 @@ class Customers_Model extends CI_Model {
}
if (!is_string($field_name)) {
- throw new IException('$field_name argument is not a string : '
+ throw new Exception('$field_name argument is not a string : '
. $field_name);
}
diff --git a/src/application/views/backend/calendar.php b/src/application/views/backend/calendar.php
index 355f3d27..3a827610 100644
--- a/src/application/views/backend/calendar.php
+++ b/src/application/views/backend/calendar.php
@@ -15,7 +15,8 @@
'availableProviders' : ,
'availableServices' : ,
'baseUrl' : ,
- 'bookAdvanceTimeout' :
+ 'bookAdvanceTimeout' : ,
+ 'editAppointment' :
};
$(document).ready(function() {
diff --git a/src/application/views/backend/header.php b/src/application/views/backend/header.php
index ad2dc23e..05c3579a 100644
--- a/src/application/views/backend/header.php
+++ b/src/application/views/backend/header.php
@@ -1,7 +1,7 @@
- Easy!Appointments Backend
+ Easy!Appointments Backend |
$customer_address
-
+
Appointment Link
$appointment_link
diff --git a/src/assets/css/backend.css b/src/assets/css/backend.css
index 9ec97aa8..5550028f 100644
--- a/src/assets/css/backend.css
+++ b/src/assets/css/backend.css
@@ -51,6 +51,7 @@ root {
font-size: 24px; border: none; border-radius: 0; font-weight: bold; color: #333;
text-shadow: 0px 1px 0px #FFF;}
#calendar-page #calendar .fc-break { background-image: url('../images/break.jpg'); }
+#calendar-page #calendar .fc-custom { background-image: url('../images/custom.jpg'); }
/* BACKEND CUSTOMERS PAGE
-------------------------------------------------------------------- */
diff --git a/src/assets/images/custom.jpg b/src/assets/images/custom.jpg
new file mode 100644
index 00000000..e173cb76
Binary files /dev/null and b/src/assets/images/custom.jpg differ
diff --git a/src/assets/js/backend_calendar.js b/src/assets/js/backend_calendar.js
index 94997aab..7031ea51 100644
--- a/src/assets/js/backend_calendar.js
+++ b/src/assets/js/backend_calendar.js
@@ -28,6 +28,8 @@ var BackendCalendar = {
'height': BackendCalendar.getCalendarHeight(),
'editable': true,
'slotMinutes': 30,
+ 'axisFormat': 'HH:mm',
+ 'timeFormat': 'HH:mm{ - HH:mm}',
'columnFormat': {
'month': 'ddd',
'week': 'ddd d/M',
@@ -85,6 +87,11 @@ var BackendCalendar = {
BackendCalendar.bindEventHandlers();
$('#select-filter-item').trigger('change');
}
+
+ // :: DISPLAY EDIT DIALOG IF APPOINTMENT HASH IS PROVIDED
+ if (GlobalVariables.editAppointment != null) {
+ // @task Display appointment edit dialog to user.
+ }
},
/**
@@ -160,38 +167,58 @@ var BackendCalendar = {
$(document).on('click', '.edit-popover', function() {
$(this).parents().eq(2).remove(); // Hide the popover
- var appointment = BackendCalendar.lastFocusedEventData.data;
- var $dialog = $('#manage-appointment');
+ var $dialog;
- BackendCalendar.resetAppointmentDialog();
+ if (BackendCalendar.lastFocusedEventData.data.is_unavailable == false) {
+ var appointment = BackendCalendar.lastFocusedEventData.data;
+ $dialog = $('#manage-appointment');
+
+ BackendCalendar.resetAppointmentDialog();
+
+ // :: APPLY APPOINTMENT DATA AND SHOW TO MODAL DIALOG
+ $dialog.find('.modal-header h3').text('Edit Appointment');
+ $dialog.find('#appointment-id').val(appointment['id']);
+ $dialog.find('#select-service').val(appointment['id_services']);
+ $dialog.find('#select-provider').val(appointment['id_users_provider']);
+
+ // Set the start and end datetime of the appointment.\
+ var startDatetime = Date.parseExact(appointment['start_datetime'],
+ 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm');
+ $dialog.find('#start-datetime').val(startDatetime);
+
+ var endDatetime = Date.parseExact(appointment['end_datetime'],
+ 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm');
+ $dialog.find('#end-datetime').val(endDatetime);
+
+ var customer = appointment['customer'];
+ $dialog.find('#customer-id').val(appointment['id_users_customer']);
+ $dialog.find('#first-name').val(customer['first_name']);
+ $dialog.find('#last-name').val(customer['last_name']);
+ $dialog.find('#email').val(customer['email']);
+ $dialog.find('#phone-number').val(customer['phone_number']);
+ $dialog.find('#address').val(customer['address']);
+ $dialog.find('#city').val(customer['city']);
+ $dialog.find('#zip-code').val(customer['zip_code']);
+ $dialog.find('#notes').val(appointment['notes']);
+ } else {
+ var unavailable = BackendCalendar.lastFocusedEventData.data;
+
+ // Replace string date values with actual date objects.
+ unavailable.start_datetime = GeneralFunctions.clone(BackendCalendar.lastFocusedEventData.start);
+ unavailable.end_datetime = GeneralFunctions.clone(BackendCalendar.lastFocusedEventData.end);
+
+ $dialog = $('#manage-unavailable');
+ BackendCalendar.resetUnavailableDialog();
+
+ // :: APPLY UNAVAILABLE DATA TO DIALOG
+ $dialog.find('.modal-header h3').text('Edit Unavailable Period');
+ $dialog.find('#unavailable-id').val(unavailable.id);
+ $dialog.find('#unavailable-start').val(unavailable.start_datetime.toString('dd/MM/yyyy HH:mm'));
+ $dialog.find('#unavailable-end').val(unavailable.end_datetime.toString('dd/MM/yyyy HH:mm'));
+ $dialog.find('#unavailable-notes').val(unavailable.notes);
+ }
- // :: APPLY APPOINTMENT DATA AND SHOW TO MODAL DIALOG
- $dialog.find('.modal-header h3').text('Edit Appointment');
- $dialog.find('#appointment-id').val(appointment['id']);
- $dialog.find('#select-service').val(appointment['id_services']);
- $dialog.find('#select-provider').val(appointment['id_users_provider']);
-
- // Set the start and end datetime of the appointment.\
- var startDatetime = Date.parseExact(appointment['start_datetime'],
- 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm');
- $dialog.find('#start-datetime').val(startDatetime);
-
- var endDatetime = Date.parseExact(appointment['end_datetime'],
- 'yyyy-MM-dd HH:mm:ss').toString('dd/MM/yyyy HH:mm');
- $dialog.find('#end-datetime').val(endDatetime);
-
- var customer = appointment['customer'];
- $dialog.find('#customer-id').val(appointment['id_users_customer']);
- $dialog.find('#first-name').val(customer['first_name']);
- $dialog.find('#last-name').val(customer['last_name']);
- $dialog.find('#email').val(customer['email']);
- $dialog.find('#phone-number').val(customer['phone_number']);
- $dialog.find('#address').val(customer['address']);
- $dialog.find('#city').val(customer['city']);
- $dialog.find('#zip-code').val(customer['zip_code']);
- $dialog.find('#notes').val(appointment['notes']);
-
- // :: DISPLAY THE MANAGE APPOINTMENTS MODAL DIALOG
+ // :: DIAPLY EDIT DIALOG
$dialog.modal('show');
});
@@ -205,48 +232,81 @@ var BackendCalendar = {
$(document).on('click', '.delete-popover', function() {
$(this).parents().eq(2).remove(); // Hide the popover
- var messageButtons = {
- 'OK': function() {
- var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_appointment';
-
- var postData = {
- 'appointment_id' : BackendCalendar.lastFocusedEventData.data['id'],
- 'delete_reason': $('#delete-reason').val()
- };
-
- $.post(postUrl, postData, function(response) {
- /////////////////////////////////////////////////////////
- console.log('Delete Appointment Response :', response);
- /////////////////////////////////////////////////////////
-
+ if (BackendCalendar.lastFocusedEventData.data.is_unavailable == false) {
+ var messageButtons = {
+ 'OK': function() {
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_appointment';
+
+ var postData = {
+ 'appointment_id' : BackendCalendar.lastFocusedEventData.data['id'],
+ 'delete_reason': $('#delete-reason').val()
+ };
+
+ $.post(postUrl, postData, function(response) {
+ /////////////////////////////////////////////////////////
+ console.log('Delete Appointment Response :', response);
+ /////////////////////////////////////////////////////////
+
+ $('#message_box').dialog('close');
+
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
+
+ if (response.warnings) {
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
+
+ // Refresh calendar event items.
+ $('#select-filter-item').trigger('change');
+ }, 'json');
+ },
+ 'Cancel': function() {
$('#message_box').dialog('close');
-
- if (response.exceptions) {
- response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
- GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
- $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
- return;
- }
-
- if (response.warnings) {
- response.warnings = GeneralFunctions.parseExceptions(response.warnings);
- GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
- $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
- }
-
- // Refresh calendar event items.
- $('#select-filter-item').trigger('change');
- }, 'json');
- },
- 'Cancel': function() {
+ }
+ };
+
+ GeneralFunctions.displayMessageBox('Delete Appointment', 'Please take a minute '
+ + 'to write the reason you are deleting the appointment:', messageButtons);
+ $('#message_box').append('');
+ $('#delete-reason').css('width', '320px');
+ } else {
+ // Do not display confirmation promt.
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_delete_unavailable';
+
+ var postData = {
+ 'unavailable_id' : BackendCalendar.lastFocusedEventData.data.id
+ };
+
+ $.post(postUrl, postData, function(response) {
+ /////////////////////////////////////////////////////////
+ console.log('Delete Unavailable Response :', response);
+ /////////////////////////////////////////////////////////
+
$('#message_box').dialog('close');
- }
- };
-
- GeneralFunctions.displayMessageBox('Delete Appointment', 'Please take a minute '
- + 'to write the reason you are deleting the appointment:', messageButtons);
- $('#message_box').append('');
- $('#delete-reason').css('width', '320px');
+
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
+
+ if (response.warnings) {
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
+
+ // Refresh calendar event items.
+ $('#select-filter-item').trigger('change');
+ }, 'json');
+ }
});
/**
@@ -353,6 +413,11 @@ var BackendCalendar = {
successCallback, errorCallback);
});
+ /**
+ * Event: Manage Unavailable Dialog Save Button "Click"
+ *
+ * Stores the unavailable period changes or inserts a new record.
+ */
$('#manage-unavailable #save-unavailable').click(function() {
var $dialog = $('#manage-unavailable');
@@ -365,6 +430,7 @@ var BackendCalendar = {
'' +
'Start date value is bigger than end date!' +
'
');
+ $dialog.find('.modal-message').show();
return;
}
@@ -376,6 +442,11 @@ var BackendCalendar = {
'id_users_provider': $('#select-filter-item').val() // curr provider
};
+ if ($dialog.find('#unavailable-id').val() !== '') {
+ // Set the id value, only if we are editing an appointment.
+ unavailable.id = $dialog.find('#unavailable-id').val();
+ }
+
var successCallback = function(response) {
///////////////////////////////////////////////////////////////////
console.log('Save Unavailable Time Period Response:', response);
@@ -385,6 +456,12 @@ var BackendCalendar = {
response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+
+ $dialog.find('.modal-header').append(
+ '' +
+ 'Unexpected issues occured!' +
+ '
');
+
return;
}
@@ -393,8 +470,20 @@ var BackendCalendar = {
GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
$('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
}
-
- $('#manage-unavailable').modal('hide');
+
+ // Display success message to the user.
+ $dialog.find('.modal-header').append(
+ '' +
+ 'Appointment saved successfully!' +
+ '
');
+
+ // Close the modal dialog and refresh the calendar appointments
+ // after one second.
+ setTimeout(function() {
+ $dialog.find('.alert').remove();
+ $dialog.modal('hide');
+ $('#select-filter-item').trigger('change');
+ }, 2000);
};
var errorCallback = function(jqXHR, textStatus, errorThrown) {
@@ -404,11 +493,21 @@ var BackendCalendar = {
GeneralFunctions.displayMessageBox('Communication Error', 'Unfortunately ' +
'the operation could not complete due to server communication errors.');
+
+ $dialog.find('.modal-header').append(
+ '' +
+ 'A server communication error occured, please try again.' +
+ '
');
};
- BackendCalendar.saveUnavalable(unavailable, successCallback, errorCallback);
+ BackendCalendar.saveUnavailable(unavailable, successCallback, errorCallback);
});
+ /**
+ * Event: Manage Unavailable Dialog Cancel Button "Click"
+ *
+ * Closes the dialog without saveing any changes to the database.
+ */
$('#manage-unavailable #cancel-unavailable').click(function() {
$('#manage-unavailable').modal('hide');
});
@@ -551,8 +650,9 @@ var BackendCalendar = {
// :: ADD APPOINTMENTS TO CALENDAR
var calendarEvents = [];
+ var $calendar = $('#calendar');
- $.each(response, function(index, appointment){
+ $.each(response.appointments, function(index, appointment){
var event = {
'id': appointment['id'],
'title': appointment['service']['name'] + ' - '
@@ -571,7 +671,8 @@ var BackendCalendar = {
$calendar.fullCalendar('addEventSource', calendarEvents);
// :: ADD PROVIDER'S UNAVAILABLE TIME PERIODS
- var calendarView = $('#calendar').fullCalendar('getView').name;
+ var calendarView = $calendar.fullCalendar('getView').name;
+
if (filterType === BackendCalendar.FILTER_TYPE_PROVIDER
&& calendarView !== 'month') {
@@ -582,11 +683,11 @@ var BackendCalendar = {
switch(calendarView) {
case 'agendaDay':
- var selDayName = $('#calendar').fullCalendar('getView')
+ var selDayName = $calendar.fullCalendar('getView')
.start.toString('dddd').toLowerCase();
// Add unavailable period before work starts.
- var calendarDateStart = $('#calendar').fullCalendar('getView').start;
+ var calendarDateStart = $calendar.fullCalendar('getView').start;
var workDateStart = Date.parseExact(
calendarDateStart.toString('dd/MM/yyyy') + ' '
+ workingPlan[selDayName].start,
@@ -602,11 +703,11 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod, true);
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
}
// Add unavailable period after work ends.
- var calendarDateEnd = $('#calendar').fullCalendar('getView').end;
+ var calendarDateEnd = $calendar.fullCalendar('getView').end;
var workDateEnd = Date.parseExact(
calendarDateStart.toString('dd/MM/yyyy') + ' '
+ workingPlan[selDayName].end,
@@ -621,7 +722,7 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod, true);
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
}
// Add unavailable periods for breaks.
@@ -640,16 +741,28 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable fc-break'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod, true);
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
});
- // @task Add custom unavailable periods.
-
+ // Add custom unavailable periods.
+ $.each(response.unavailables, function(index, unavailable) {
+ var unavailablePeriod = {
+ 'title': 'Unavailable',
+ 'start': Date.parse(unavailable.start_datetime),
+ 'end': Date.parse(unavailable.end_datetime),
+ 'allDay': false,
+ 'color': '#123456',
+ 'editable': true,
+ 'className': 'fc-unavailable fc-custom',
+ 'data': unavailable
+ };
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
+ });
break;
case 'agendaWeek':
- var currDateStart = GeneralFunctions.clone($('#calendar').fullCalendar('getView').start);
+ var currDateStart = GeneralFunctions.clone($calendar.fullCalendar('getView').start);
var currDateEnd = GeneralFunctions.clone(currDateStart).addDays(1);
$.each(workingPlan, function(index, workingDay) {
@@ -668,7 +781,7 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod,
+ $calendar.fullCalendar('renderEvent', unavailablePeriod,
true);
}
@@ -685,7 +798,7 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable fc-brake'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod, true);
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
}
// Add unavailable periods during day breaks.
@@ -704,10 +817,26 @@ var BackendCalendar = {
'editable': false,
'className': 'fc-unavailable fc-break'
};
- $('#calendar').fullCalendar('renderEvent', unavailablePeriod, true);
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
});
- // @task Add custom unavailable periods.
+ // Add custom unavailable periods.
+ $.each(response.unavailables, function(index, unavailable) {
+ if (currDateStart.toString('dd/MM/yyyy')
+ === Date.parse(unavailable.start_datetime).toString('dd/MM/yyyy')) {
+ var unavailablePeriod = {
+ 'title': 'Unavailable',
+ 'start': Date.parse(unavailable.start_datetime),
+ 'end': Date.parse(unavailable.end_datetime),
+ 'allDay': false,
+ 'color': '#123456',
+ 'editable': true,
+ 'className': 'fc-unavailable fc-custom',
+ 'data': unavailable
+ };
+ $calendar.fullCalendar('renderEvent', unavailablePeriod, false);
+ }
+ });
currDateStart.addDays(1);
currDateEnd.addDays(1);
@@ -778,7 +907,7 @@ var BackendCalendar = {
* @param {function} successCallback The ajax success callback function.
* @param {function} errorCallback The ajax failure callback function.
*/
- saveUnavalable: function(unavailable, successCallback, errorCallback) {
+ saveUnavailable: function(unavailable, successCallback, errorCallback) {
var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_unavailable';
var postData = {
@@ -809,64 +938,117 @@ var BackendCalendar = {
$('#notification').hide('bind');
}
- // :: PREPARE THE APPOINTMENT DATA
- var appointment = GeneralFunctions.clone(event.data);
+ if (event.data.is_unavailable == false) {
+ // :: PREPARE THE APPOINTMENT DATA
+ var appointment = GeneralFunctions.clone(event.data);
- // Must delete the following because only appointment data should be
- // provided to the ajax call.
- delete appointment['customer'];
- delete appointment['provider'];
- delete appointment['service'];
+ // Must delete the following because only appointment data should be
+ // provided to the ajax call.
+ delete appointment['customer'];
+ delete appointment['provider'];
+ delete appointment['service'];
- appointment['end_datetime'] = Date.parseExact(
- appointment['end_datetime'], 'yyyy-MM-dd HH:mm:ss')
- .add({ minutes: minuteDelta })
- .toString('yyyy-MM-dd HH:mm:ss');
+ appointment['end_datetime'] = Date.parseExact(
+ appointment['end_datetime'], 'yyyy-MM-dd HH:mm:ss')
+ .add({ minutes: minuteDelta })
+ .toString('yyyy-MM-dd HH:mm:ss');
- // :: DEFINE THE SUCCESS CALLBACK FUNCTION
- var successCallback = function(response) {
- if (response.exceptions) {
- response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
- GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
- $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
- return;
- }
-
- if (response.warnings) {
- // Display warning information to the user.
- response.warnings = GeneralFunctions.parseExceptions(response.warnings);
- GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
- $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
- }
+ // :: DEFINE THE SUCCESS CALLBACK FUNCTION
+ var successCallback = function(response) {
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
- // Display success notification to user.
- var undoFunction = function() {
- appointment['end_datetime'] = Date.parseExact(
- appointment['end_datetime'], 'yyyy-MM-dd HH:mm:ss')
- .add({ minutes: -minuteDelta })
- .toString('yyyy-MM-dd HH:mm:ss');
-
- var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_appointment';
- var postData = { 'appointment_data': JSON.stringify(appointment) };
+ if (response.warnings) {
+ // Display warning information to the user.
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
- $.post(postUrl, postData, function(response) {
- $('#notification').hide('blind');
- revertFunc();
- });
+ // Display success notification to user.
+ var undoFunction = function() {
+ appointment['end_datetime'] = Date.parseExact(
+ appointment['end_datetime'], 'yyyy-MM-dd HH:mm:ss')
+ .add({ minutes: -minuteDelta })
+ .toString('yyyy-MM-dd HH:mm:ss');
+
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_appointment';
+ var postData = { 'appointment_data': JSON.stringify(appointment) };
+
+ $.post(postUrl, postData, function(response) {
+ $('#notification').hide('blind');
+ revertFunc();
+ });
+ };
+
+ Backend.displayNotification('Appointment updated successfully!', [
+ {
+ 'label': 'Undo',
+ 'function': undoFunction
+ }
+ ]);
+ $('#footer').css('position', 'static'); // Footer position fix.
};
- Backend.displayNotification('Appointment updated successfully!', [
- {
- 'label': 'Undo',
- 'function': undoFunction
- }
- ]);
- $('#footer').css('position', 'static'); // Footer position fix.
- };
+ // :: UPDATE APPOINTMENT DATA VIA AJAX CALL
+ BackendCalendar.saveAppointment(appointment, undefined,
+ successCallback, undefined);
+ } else {
+ // :: UPDATE UNAVAILABLE TIME PERIOD
+ var unavailable = {
+ 'id': event.data.id,
+ 'start_datetime': event.start.toString('yyyy-MM-dd HH:mm:ss'),
+ 'end_datetime': event.end.toString('yyyy-MM-dd HH:mm:ss'),
+ 'id_users_provider': event.data.id_users_provider
+ };
- // :: UPDATE APPOINTMENT DATA VIA AJAX CALL
- BackendCalendar.saveAppointment(appointment, undefined,
- successCallback, undefined);
+ // :: DEFINE THE SUCCESS CALLBACK FUNCTION
+ var successCallback = function(response) {
+ if (response.exceptions) {
+ response.exceptions = GeneralFunctions.parseExceptions(response.exceptions);
+ GeneralFunctions.displayMessageBox(Backend.EXCEPTIONS_TITLE, Backend.EXCEPTIONS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.exceptions));
+ return;
+ }
+
+ if (response.warnings) {
+ // Display warning information to the user.
+ response.warnings = GeneralFunctions.parseExceptions(response.warnings);
+ GeneralFunctions.displayMessageBox(Backend.WARNINGS_TITLE, Backend.WARNINGS_MESSAGE);
+ $('#message_box').append(GeneralFunctions.exceptionsToHtml(response.warnings));
+ }
+
+ // Display success notification to user.
+ var undoFunction = function() {
+ unavailable['end_datetime'] = Date.parseExact(
+ unavailable['end_datetime'], 'yyyy-MM-dd HH:mm:ss')
+ .add({ minutes: -minuteDelta })
+ .toString('yyyy-MM-dd HH:mm:ss');
+
+ var postUrl = GlobalVariables.baseUrl + 'backend_api/ajax_save_unavailable';
+ var postData = { 'unavailable': JSON.stringify(unavailable) };
+
+ $.post(postUrl, postData, function(response) {
+ $('#notification').hide('blind');
+ revertFunc();
+ });
+ };
+
+ Backend.displayNotification('Unavailable time period updated successfully!', [
+ {
+ 'label': 'Undo',
+ 'function': undoFunction
+ }
+ ]);
+ $('#footer').css('position', 'static'); // Footer position fix.
+ };
+
+ BackendCalendar.saveUnavailable(unavailable, successCallback, undefined);
+ }
},
/**
@@ -905,10 +1087,23 @@ var BackendCalendar = {
calendarEventClick: function(event, jsEvent, view) {
$('.popover').remove(); // Close all open popovers.
- var html;
+ var html; // Popover's html code
- if ($(jsEvent.target.offsetParent).hasClass('fc-unavailable')
- || $(jsEvent.target).parents().eq(1).hasClass('fc-unavailable')) {
+ // Depending where the user clicked the event (title or empty space) we
+ // need to use different selectors to reach the parent element.
+ var $parent = $(jsEvent.target.offsetParent);
+ var $altParent = $(jsEvent.target).parents().eq(1);
+
+ if ($parent.hasClass('fc-unavailable') || $altParent.hasClass('fc-unavailable')) {
+ var displayEdit = ($parent.hasClass('fc-custom') || $altParent.hasClass('fc-custom'))
+ ? '' : 'hide';
+ var displayDelete = displayEdit; // Same value at the time.
+
+ var notes = '';
+ if (event.data) { // Only custom unavailable periods have notes.
+ notes = 'Notes ' + event.data.notes;
+ }
+
html =
'