Example #1
0
 /**
  * Get schedule items of staff member.
  *
  * @return array
  */
 public function getScheduleItems()
 {
     if (!$this->isLoaded()) {
         return array();
     }
     $start_of_week = (int) get_option('start_of_week');
     // Start of week affects the sorting.
     // If it is 0(Sun) then the result should be 1,2,3,4,5,6,7.
     // If it is 1(Mon) then the result should be 2,3,4,5,6,7,1.
     // If it is 2(Tue) then the result should be 3,4,5,6,7,1,2. Etc.
     return AB_StaffScheduleItem::query()->where('staff_id', $this->get('id'))->sortBy("IF(r.day_index + 10 - {$start_of_week} > 10, r.day_index + 10 - {$start_of_week}, 16 + r.day_index)")->indexBy('day_index')->find();
 }
 public function save()
 {
     if (isset($this->data['days'])) {
         foreach ($this->data['days'] as $id => $day_index) {
             $staffScheduleItem = new AB_StaffScheduleItem();
             $staffScheduleItem->load($id);
             $staffScheduleItem->set('day_index', $day_index);
             if ($this->data['start_time'][$day_index]) {
                 $staffScheduleItem->set('start_time', $this->data['start_time'][$day_index]);
                 $staffScheduleItem->set('end_time', $this->data['end_time'][$day_index]);
             } else {
                 $staffScheduleItem->set('start_time', null);
                 $staffScheduleItem->set('end_time', null);
             }
             $staffScheduleItem->save();
         }
     }
 }
Example #3
0
 /**
  * Contructor.
  *
  * @param $form_id
  */
 public function __construct($form_id)
 {
     $this->form_id = $form_id;
     // Set up default parameters.
     $prior_time = AB_BookingConfiguration::getMinimumTimePriorBooking();
     $this->set('date_from', date('Y-m-d', current_time('timestamp') + $prior_time));
     $schedule_item = AB_StaffScheduleItem::query('ssi')->select('SUBSTRING_INDEX(MIN(ssi.start_time), ":", 2) AS min_end_time')->whereNot('start_time', null)->fetchArray();
     $this->set('time_from', $schedule_item[0]['min_end_time']);
     $schedule_item = AB_StaffScheduleItem::query('ssi')->select('SUBSTRING_INDEX(MAX(end_time), ":", 2) AS max_end_time')->whereNot('start_time', null)->fetchArray();
     $this->set('time_to', $schedule_item[0]['max_end_time']);
     // If logged in then set name, email and if existing customer then also phone.
     $current_user = wp_get_current_user();
     if ($current_user && $current_user->ID) {
         $customer = new AB_Customer();
         if ($customer->loadBy(array('wp_user_id' => $current_user->ID))) {
             $this->set('name', $customer->get('name'));
             $this->set('email', $customer->get('email'));
             $this->set('phone', $customer->get('phone'));
         } else {
             $this->set('name', $current_user->display_name);
             $this->set('email', $current_user->user_email);
         }
     }
 }
Example #4
0
<?php

if (!defined('ABSPATH')) {
    exit;
}
// Exit if accessed directly
$staffScheduleItem = new AB_StaffScheduleItem();
$staffScheduleItem->load($list_item->staff_schedule_item_id);
$breaks_list = $staffScheduleItem->getBreaksList();
$display = count($breaks_list) ? 'inline-block' : 'none;';
?>
<table class="breaks-list hide-on-non-working-day" cellspacing="0" cellpadding="0"<?php 
if ($day_is_not_available) {
    ?>
 style="display: none"<?php 
}
?>
>
    <tr>
        <td class="breaks-list-label">
            <span style="display: <?php 
echo $display;
?>
">
                <?php 
_e('breaks:', 'ab');
?>
            </span>
        </td>
        <td class="breaks-list-content">
            <?php 
 /**
  * Remove all breaks for certain staff member
  *
  * @param $staff_id
  *
  * @return bool
  */
 public function removeBreaksByStaffId($staff_id)
 {
     $this->wpdb->get_results($this->wpdb->prepare('DELETE `break` FROM `' . self::getTableName() . '` AS `break`
         LEFT JOIN `' . AB_StaffScheduleItem::getTableName() . '` AS `item` ON `item`.`id` = `break`.`staff_schedule_item_id`
         WHERE `item`.`staff_id` = %d', $staff_id));
 }
Example #6
0
 function update_7_0()
 {
     global $wpdb;
     $wpdb->query('ALTER TABLE `ab_customer_appointment` ADD `coupon_deduction` DECIMAL(10,2) DEFAULT NULL AFTER `coupon_discount`');
     $wpdb->query('ALTER TABLE `ab_coupons` CHANGE COLUMN `used` `used` INT UNSIGNED NOT NULL DEFAULT 0,
                    ADD COLUMN `deduction` DECIMAL(10,2) NOT NULL DEFAULT 0 AFTER `discount`,
                    ADD COLUMN `usage_limit` INT UNSIGNED NOT NULL DEFAULT 1');
     $wpdb->query('ALTER TABLE `ab_notifications` CHANGE `slug` `type` VARCHAR(255) NOT NULL DEFAULT ""');
     // SMS.
     $wpdb->query('ALTER TABLE `ab_notifications` ADD `gateway` ENUM("email","sms") NOT NULL DEFAULT "email"');
     $wpdb->query('UPDATE `ab_notifications` SET `gateway` = "email"');
     $sms_notifies = array(array('type' => 'client_new_appointment', 'message' => __("Dear [[CLIENT_NAME]].\nThis is confirmation that you have booked [[SERVICE_NAME]].\nWe are waiting you at [[COMPANY_ADDRESS]] on [[APPOINTMENT_DATE]] at [[APPOINTMENT_TIME]].\nThank you for choosing our company.\n[[COMPANY_NAME]]\n[[COMPANY_PHONE]]\n[[COMPANY_WEBSITE]]", 'bookly'), 'active' => 1), array('type' => 'staff_new_appointment', 'message' => __("Hello.\nYou have new booking.\nService: [[SERVICE_NAME]]\nDate: [[APPOINTMENT_DATE]]\nTime: [[APPOINTMENT_TIME]]\nClient name: [[CLIENT_NAME]]\nClient phone: [[CLIENT_PHONE]]\nClient email: [[CLIENT_EMAIL]]", 'bookly'), 'active' => 0), array('type' => 'client_reminder', 'message' => __("Dear [[CLIENT_NAME]].\nWe would like to remind you that you have booked [[SERVICE_NAME]] tomorrow on [[APPOINTMENT_TIME]]. We are waiting you at [[COMPANY_ADDRESS]].\nThank you for choosing our company.\n[[COMPANY_NAME]]\n[[COMPANY_PHONE]]\n[[COMPANY_WEBSITE]]", 'bookly'), 'active' => 0), array('type' => 'client_follow_up', 'message' => __("Dear [[CLIENT_NAME]].\nThank you for choosing [[COMPANY_NAME]]. We hope you were satisfied with your [[SERVICE_NAME]].\nThank you and we look forward to seeing you again soon.\n[[COMPANY_NAME]]\n[[COMPANY_PHONE]]\n[[COMPANY_WEBSITE]]", 'bookly'), 'active' => 0), array('type' => 'staff_agenda', 'message' => __("Hello.\nYour agenda for tomorrow is:\n[[NEXT_DAY_AGENDA]]", 'bookly'), 'active' => 0), array('type' => 'staff_cancelled_appointment', 'message' => __("Hello.\nThe following booking has been cancelled.\nService: [[SERVICE_NAME]]\nDate: [[APPOINTMENT_DATE]]\nTime: [[APPOINTMENT_TIME]]\nClient name: [[CLIENT_NAME]]\nClient phone: [[CLIENT_PHONE]]\nClient email: [[CLIENT_EMAIL]]", 'bookly'), 'active' => 0), array('type' => 'client_new_wp_user', 'message' => __("Hello.\nAn account was created for you at [[SITE_ADDRESS]]\nYour user details:\nuser: [[NEW_USERNAME]]\npassword: [[NEW_PASSWORD]]\n\nThanks.", 'bookly'), 'active' => 1));
     // Insert notifications.
     foreach ($sms_notifies as $data) {
         $wpdb->insert('ab_notifications', array('gateway' => 'sms', 'type' => $data['type'], 'subject' => '', 'message' => $data['message'], 'active' => $data['active']));
     }
     // Rename notifications.
     $notifications = array('client_info' => 'client_new_appointment', 'provider_info' => 'staff_new_appointment', 'evening_next_day' => 'client_reminder', 'evening_after' => 'client_follow_up', 'event_next_day' => 'staff_agenda', 'cancel_appointment' => 'staff_cancelled_appointment', 'new_wp_user' => 'client_new_wp_user');
     foreach ($notifications as $from => $to) {
         $wpdb->query("UPDATE `ab_notifications` SET `type` = '{$to}' WHERE `type` = '{$from}'");
     }
     $this->drop('ab_email_notification');
     // Rename tables.
     $ab_tables = array('ab_appointment' => AB_Appointment::getTableName(), 'ab_category' => AB_Category::getTableName(), 'ab_coupons' => AB_Coupon::getTableName(), 'ab_customer' => AB_Customer::getTableName(), 'ab_customer_appointment' => AB_CustomerAppointment::getTableName(), 'ab_holiday' => AB_Holiday::getTableName(), 'ab_notifications' => AB_Notification::getTableName(), 'ab_payment' => AB_Payment::getTableName(), 'ab_schedule_item_break' => AB_ScheduleItemBreak::getTableName(), 'ab_service' => AB_Service::getTableName(), 'ab_staff' => AB_Staff::getTableName(), 'ab_staff_schedule_item' => AB_StaffScheduleItem::getTableName(), 'ab_staff_service' => AB_StaffService::getTableName());
     foreach ($ab_tables as $from => $to) {
         $wpdb->query("ALTER TABLE `{$from}` RENAME TO `{$to}`");
     }
     $wpdb->query("CREATE TABLE IF NOT EXISTS  `" . AB_SentNotification::getTableName() . "` (\n                `id`                      INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,\n                `customer_appointment_id` INT UNSIGNED,\n                `staff_id`                INT UNSIGNED,\n                `gateway`                 ENUM('email','sms') NOT NULL DEFAULT 'email',\n                `type`                    VARCHAR(60) NOT NULL,\n                `created`                 DATETIME NOT NULL,\n                CONSTRAINT fk_" . AB_SentNotification::getTableName() . "_" . AB_CustomerAppointment::getTableName() . "_id\n                    FOREIGN KEY (customer_appointment_id)\n                    REFERENCES  " . AB_CustomerAppointment::getTableName() . "(id)\n                    ON DELETE   CASCADE\n                    ON UPDATE   CASCADE,\n                CONSTRAINT fk_" . AB_SentNotification::getTableName() . "_" . AB_Staff::getTableName() . "_id\n                    FOREIGN KEY (staff_id)\n                    REFERENCES  " . AB_Staff::getTableName() . "(id)\n                    ON DELETE   CASCADE\n                    ON UPDATE   CASCADE\n              ) ENGINE = INNODB\n              DEFAULT CHARACTER SET = utf8\n              COLLATE = utf8_general_ci");
     // Google Calendar.
     add_option('ab_settings_google_event_title', '[[SERVICE_NAME]]');
     // Link assets.
     add_option('ab_settings_link_assets_method', 'enqueue');
     // SMS.
     add_option('ab_sms_default_country_code', '');
 }
Example #7
0
 private function _drop_tables()
 {
     /** @var wpdb $wpdb */
     global $wpdb;
     $ab_tables = array(AB_Appointment::getTableName(), AB_Category::getTableName(), AB_Coupon::getTableName(), AB_Customer::getTableName(), AB_CustomerAppointment::getTableName(), AB_Holiday::getTableName(), AB_Notification::getTableName(), AB_Payment::getTableName(), AB_ScheduleItemBreak::getTableName(), AB_SentNotification::getTableName(), AB_Service::getTableName(), AB_Staff::getTableName(), AB_StaffScheduleItem::getTableName(), AB_StaffService::getTableName());
     $this->_drop_fk($ab_tables);
     $wpdb->query('DROP TABLE IF EXISTS `' . implode('`, `', $ab_tables) . '` CASCADE;');
 }
Example #8
0
 /**
  * Prepare data for staff.
  *
  * @param DateTime $start_date
  */
 private function _prepareStaffData(DateTime $start_date)
 {
     $this->staffData = array();
     $services = AB_StaffService::query('ss')->select('ss.staff_id, ss.price, ss.capacity')->whereIn('ss.staff_id', $this->staff_ids)->where('ss.service_id', $this->userData->get('service_id'))->fetchArray();
     foreach ($services as $item) {
         $this->staffData[$item['staff_id']] = array('price' => $item['price'], 'capacity' => $item['capacity'], 'holidays' => array(), 'bookings' => array(), 'working_hours' => array());
     }
     // Load holidays.
     $holidays = AB_Holiday::query('h')->whereIn('h.staff_id', $this->staff_ids)->fetchArray();
     foreach ($holidays as $item) {
         $this->staffData[$item['staff_id']]['holidays'][] = $item;
     }
     // Load working schedule.
     $working_schedule = AB_StaffScheduleItem::query('ssi')->select('ssi.*, break.start_time AS break_start, break.end_time AS break_end')->leftJoin('AB_ScheduleItemBreak', 'break', 'break.staff_schedule_item_id = ssi.id')->whereIn('ssi.staff_id', $this->staff_ids)->whereNot('ssi.start_time', null)->fetchArray();
     foreach ($working_schedule as $item) {
         if (!isset($this->staffData[$item['staff_id']]['working_hours'][$item['day_index']])) {
             $this->staffData[$item['staff_id']]['working_hours'][$item['day_index']] = array('start_time' => $item['start_time'], 'end_time' => $item['end_time'], 'breaks' => array());
         }
         if ($item['break_start']) {
             $this->staffData[$item['staff_id']]['working_hours'][$item['day_index']]['breaks'][] = array('start' => $item['break_start'], 'end' => $item['break_end']);
         }
     }
     // Load bookings.
     $bookings = AB_CustomerAppointment::query('ca')->select('a.*, SUM(ca.number_of_persons) AS number_of_bookings')->leftJoin('AB_Appointment', 'a', 'a.id = ca.appointment_id')->leftJoin('AB_StaffService', 'ss', 'ss.staff_id = a.staff_id AND ss.service_id = a.service_id')->whereIn('a.staff_id', $this->staff_ids)->whereGte('a.start_date', $this->userData->get('date_from'))->groupBy('a.start_date')->groupBy('a.staff_id')->groupBy('a.service_id')->fetchArray();
     foreach ($bookings as $item) {
         $item['from_google'] = false;
         // Handle bookings which end at 24:00.
         if (substr($item['end_date'], 11) == '00:00:00') {
             // Set time to 24:00:00 (date part does not matter, it just needs to be 10 characters length).
             $item['end_date'] = '10_symbols 24:00:00';
         }
         $this->staffData[$item['staff_id']]['bookings'][] = $item;
     }
     // Handle Google Calendar events.
     if (get_option('ab_settings_google_two_way_sync')) {
         $query = AB_Staff::query('s')->whereIn('s.id', array_merge($this->userData->get('staff_ids'), array(0)));
         foreach ($query->find() as $staff) {
             $google = new AB_Google();
             if ($google->loadByStaff($staff)) {
                 $this->staffData[$staff->get('id')]['bookings'] = array_merge($this->staffData[$staff->get('id')]['bookings'], $google->getCalendarEvents($start_date) ?: array());
             }
         }
     }
 }
 public function executeStaffScheduleHandleBreak()
 {
     $start_time = $this->getParameter('start_time');
     $end_time = $this->getParameter('end_time');
     $working_start = $this->getParameter('working_start');
     $working_end = $this->getParameter('working_end');
     if (strtotime(date('Y-m-d ' . $start_time)) >= strtotime(date('Y-m-d ' . $end_time))) {
         echo json_encode(array('success' => false, 'error_msg' => __('The start time must be less than the end one', 'ab')));
         exit;
     }
     $staffScheduleItem = new AB_StaffScheduleItem();
     $staffScheduleItem->load($this->getParameter('staff_schedule_item_id'));
     $break_id = $this->getParameter('break_id', 0);
     $in_working_time = $working_start <= $start_time && $start_time <= $working_end && $working_start <= $end_time && $end_time <= $working_end;
     if (!$in_working_time || !$staffScheduleItem->isBreakIntervalAvailable($start_time, $end_time, $break_id)) {
         echo json_encode(array('success' => false, 'error_msg' => __('The requested interval is not available', 'ab')));
         exit;
     }
     $time_format = get_option('time_format');
     $formatted_interval_start = date_i18n($time_format, strtotime($start_time));
     $formatted_interval_end = date_i18n($time_format, strtotime($end_time));
     $formatted_interval = $formatted_interval_start . ' - ' . $formatted_interval_end;
     if ($break_id) {
         $break = new AB_ScheduleItemBreak();
         $break->load($break_id);
         $break->set('start_time', $start_time);
         $break->set('end_time', $end_time);
         $break->save();
         echo json_encode(array('success' => true, 'new_interval' => $formatted_interval));
     } else {
         $this->form = new AB_StaffScheduleItemBreakForm();
         $this->form->bind($this->getPostParameters());
         $staffScheduleItemBreak = $this->form->save();
         if ($staffScheduleItemBreak) {
             $breakStart = new AB_TimeChoiceWidget(array('use_empty' => false));
             $break_start_choices = $breakStart->render('', $start_time, array('class' => 'break-start', 'data-default_value' => AB_StaffScheduleItem::WORKING_START_TIME));
             $breakEnd = new AB_TimeChoiceWidget(array('use_empty' => false));
             $break_end_choices = $breakEnd->render('', $end_time, array('class' => 'break-end', 'data-default_value' => date('H:i:s', strtotime(AB_StaffScheduleItem::WORKING_START_TIME . ' + 1 hour'))));
             echo json_encode(array('success' => true, 'item_content' => '<div class="break-interval-wrapper" data-break_id="' . $staffScheduleItemBreak->get('id') . '">
                                       <div class="ab-popup-wrapper hide-on-non-working-day">
                                          <a class="ab-popup-trigger break-interval" href="javascript:void(0)">' . $formatted_interval . '</a>
                                          <div class="ab-popup" style="display: none">
                                              <div class="ab-arrow"></div>
                                              <div class="error" style="display: none"></div>
                                              <div class="ab-content">
                                                  <table cellspacing="0" cellpadding="0">
                                                      <tr>
                                                          <td>' . $break_start_choices . ' <span class="hide-on-non-working-day">' . __('to', 'ab') . '</span> ' . $break_end_choices . '</td>
                                                      </tr>
                                                      <tr>
                                                          <td>
                                                              <a class="btn btn-info ab-popup-save ab-save-break">' . __('Save break', 'ab') . '</a>
                                                              <a class="ab-popup-close" href="#">' . __('Cancel', 'ab') . '</a>
                                                          </td>
                                                      </tr>
                                                  </table>
                                                  <a class="ab-popup-close ab-popup-close-icon" href="javascript:void(0)"></a>
                                               </div>
                                           </div>
                                       </div>
                                       <img class="delete-break" src="' . plugins_url('backend/resources/images/delete_cross.png', AB_PATH . '/main.php') . '" />
                                    </div>'));
         } else {
             echo json_encode(array('success' => false, 'error_msg' => __('Error adding the break interval', 'ab')));
         }
     }
     exit;
 }
Example #10
0
 /**
  * Extend parent method to control access on staff member level.
  *
  * @param string $action
  * @return bool
  */
 protected function hasAccess($action)
 {
     if (parent::hasAccess($action)) {
         if (!AB_Utils::isCurrentUserAdmin()) {
             $staff = new AB_Staff();
             switch ($action) {
                 case 'executeEditStaff':
                 case 'executeDeleteStaffAvatar':
                 case 'executeStaffServices':
                 case 'executeStaffSchedule':
                 case 'executeStaffHolidays':
                     $staff->load($this->getParameter('id'));
                     break;
                 case 'executeStaffServicesUpdate':
                 case 'executeStaffHolidaysUpdate':
                     $staff->load($this->getParameter('staff_id'));
                     break;
                 case 'executeStaffScheduleHandleBreak':
                     $staffScheduleItem = new AB_StaffScheduleItem();
                     $staffScheduleItem->load($this->getParameter('staff_schedule_item_id'));
                     $staff->load($staffScheduleItem->get('staff_id'));
                     break;
                 case 'executeDeleteStaffScheduleBreak':
                     $break = new AB_ScheduleItemBreak();
                     $break->load($this->getParameter('id'));
                     $staffScheduleItem = new AB_StaffScheduleItem();
                     $staffScheduleItem->load($break->get('staff_schedule_item_id'));
                     $staff->load($staffScheduleItem->get('staff_id'));
                     break;
                 case 'executeStaffScheduleUpdate':
                     if ($this->hasParameter('days')) {
                         foreach ($this->getParameter('days') as $id => $day_index) {
                             $staffScheduleItem = new AB_StaffScheduleItem();
                             $staffScheduleItem->load($id);
                             $staff = new AB_Staff();
                             $staff->load($staffScheduleItem->get('staff_id'));
                             if ($staff->get('wp_user_id') != get_current_user_id()) {
                                 return false;
                             }
                         }
                     }
                     break;
                 default:
                     return false;
             }
             return $staff->get('wp_user_id') == get_current_user_id();
         }
         return true;
     }
     return false;
 }
 /**
  * Fetches ids of the available days + the available time range
  * For the 1st step of the booking wizard
  *
  * @return array
  */
 public function fetchAvailableWorkDaysAndTime()
 {
     /** @var WP_Locale $wp_locale */
     global $wp_locale;
     $schedule_items = AB_StaffScheduleItem::query('ssi')->select('GROUP_CONCAT( DISTINCT ssi.day_index ORDER BY ssi.day_index ) AS available_day_ids,
             SUBSTRING_INDEX(MIN(ssi.start_time), \':\', 2) AS min_start_time,
             SUBSTRING_INDEX(MAX(ssi.end_time), \':\', 2)   AS max_end_time')->whereNot('ssi.start_time', null)->fetchArray();
     $data = current($schedule_items);
     $result = array('available_days' => array(), 'time_range' => array());
     if ($data['available_day_ids']) {
         $wp_week_start_day = (int) get_option('start_of_week');
         $available_day_ids = explode(',', $data['available_day_ids']);
         $week_days = array_values($wp_locale->weekday_abbrev);
         if ($wp_week_start_day >= $available_day_ids[0]) {
             $list_start = array_slice($week_days, $wp_week_start_day, 7, TRUE);
             $list_end = array_slice($week_days, 0, $wp_week_start_day, TRUE);
             $week_days = $list_start + $list_end;
         }
         foreach ($week_days as $day_id => $day_name) {
             if (in_array($day_id + 1, $available_day_ids)) {
                 $result['available_days'][$day_id + 1] = $day_name;
             }
         }
     }
     if ($data['min_start_time'] && $data['max_end_time']) {
         $start_timestamp = strtotime(sprintf("1970-01-01 %s", $data['min_start_time']));
         $end_timestamp = strtotime(sprintf("1970-01-01 %s", $data['max_end_time']));
         $now_timestamp = $start_timestamp;
         $now_timestamp_print = $start_timestamp;
         $end_timestamp_print = $end_timestamp;
         if ($this->client_timezone_offset !== false) {
             $now_timestamp_print -= ($this->client_timezone_offset + get_option('gmt_offset')) * 3600;
             $end_timestamp_print -= ($this->client_timezone_offset + get_option('gmt_offset')) * 3600;
         }
         while ($now_timestamp <= $end_timestamp) {
             $result['time_range'][AB_DateTimeUtils::buildTimeString($now_timestamp, false)] = AB_DateTimeUtils::formatTime($now_timestamp_print);
             // The next value will be rounded to integer number of hours, i.e. e.g. 8:00, 9:00, 10:00 and so on.
             $now_timestamp = $this->roundTime($now_timestamp + 30 * 60);
             $now_timestamp_print = $this->roundTime($now_timestamp_print + 30 * 60);
         }
         // The last value should always be the end time.
         $result['time_range'][AB_DateTimeUtils::buildTimeString($end_timestamp, false)] = AB_DateTimeUtils::formatTime($end_timestamp_print);
     }
     return $result;
 }