WordPress Online Booking and Scheduling Plugin – Bookly - Version 18.5

Version Description

Download this release

Release Info

Developer Ladela
Plugin Icon 128x128 WordPress Online Booking and Scheduling Plugin – Bookly
Version 18.5
Comparing to
See all releases

Code changes from version 18.4 to 18.5

backend/components/dialogs/queue/resources/js/queue-dialog.js CHANGED
@@ -6,14 +6,18 @@ jQuery(function ($) {
6
  $template = $('#bookly-notification-template')
7
  ;
8
 
 
 
 
 
9
  $queue.html('');
10
  queue.forEach(function (notification, index) {
11
  $queue.append(
12
  $template.clone().show().html()
13
  .replace(/{{icon}}/g, notification.gateway == 'sms' ? 'fas fa-sms' : 'far fa-envelope')
14
- .replace(/{{recipient}}/g, notification.data.name)
15
- .replace(/{{address}}/g, notification.address)
16
- .replace(/{{description}}/g, notification.name)
17
  .replace(/{{index}}/g, index)
18
  );
19
  });
6
  $template = $('#bookly-notification-template')
7
  ;
8
 
9
+ function encodeHTML(s) {
10
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
11
+ }
12
+
13
  $queue.html('');
14
  queue.forEach(function (notification, index) {
15
  $queue.append(
16
  $template.clone().show().html()
17
  .replace(/{{icon}}/g, notification.gateway == 'sms' ? 'fas fa-sms' : 'far fa-envelope')
18
+ .replace(/{{recipient}}/g, encodeHTML(notification.data.name))
19
+ .replace(/{{address}}/g, encodeHTML(notification.address))
20
+ .replace(/{{description}}/g, encodeHTML(notification.name))
21
  .replace(/{{index}}/g, index)
22
  );
23
  });
backend/components/dialogs/service/edit/resources/js/service-edit-dialog.js CHANGED
@@ -27,7 +27,8 @@ jQuery(function ($) {
27
  }
28
  });
29
  $servicesList.on('click', '[data-action="edit"]', function () {
30
- let data = $servicesList.DataTable().row($(this).closest('td')).data();
 
31
  $containers.html('');
32
  $serviceTabs.hide();
33
  $serviceLoading.show();
@@ -76,7 +77,7 @@ jQuery(function ($) {
76
  // Providers preference.
77
  $.each(response.data.staff, function (index, category) {
78
  $.each(category.items, function (index, staff) {
79
- staff_data[staff.id] = staff.full_name;
80
  });
81
  });
82
  $staffPreference.on('change', function () {
@@ -223,6 +224,10 @@ jQuery(function ($) {
223
  /**
224
  * Local functions
225
  */
 
 
 
 
226
  function initColorPicker($jquery_collection) {
227
  $jquery_collection.each(function () {
228
  $(this).data('last-color', $(this).val());
27
  }
28
  });
29
  $servicesList.on('click', '[data-action="edit"]', function () {
30
+ let $tr = $(this).closest('tr'),
31
+ data = $servicesList.DataTable().row($tr.hasClass('child') ? $tr.prev() : $tr).data();
32
  $containers.html('');
33
  $serviceTabs.hide();
34
  $serviceLoading.show();
77
  // Providers preference.
78
  $.each(response.data.staff, function (index, category) {
79
  $.each(category.items, function (index, staff) {
80
+ staff_data[staff.id] = encodeHTML(staff.full_name);
81
  });
82
  });
83
  $staffPreference.on('change', function () {
224
  /**
225
  * Local functions
226
  */
227
+ function encodeHTML(s) {
228
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
229
+ }
230
+
231
  function initColorPicker($jquery_collection) {
232
  $jquery_collection.each(function () {
233
  $(this).data('last-color', $(this).val());
backend/components/tiny_mce/resources/js/bookly-form-settings.js CHANGED
@@ -64,7 +64,7 @@ jQuery(function ($) {
64
  }
65
 
66
  function setSelects(location_id, category_id, service_id, staff_id) {
67
- var _location_id = (BooklyFormShortCodeL10n.locationCustom && location_id) ? location_id : 0
68
  var _staff = {}, _services = {}, _categories = {}, _nop = {}, _max_capacity = null, _min_capacity = null;
69
  $.each(BooklyFormShortCodeL10n.staff, function (id, staff_member) {
70
  if (!location_id || BooklyFormShortCodeL10n.locations[location_id].staff.hasOwnProperty(id)) {
@@ -145,9 +145,9 @@ jQuery(function ($) {
145
  // Location select change
146
  $select_location.on('change', function () {
147
  var location_id = this.value,
148
- category_id = $select_category.val(),
149
- service_id = $select_service.val(),
150
- staff_id = $select_employee.val()
151
  ;
152
 
153
  // Validate selected values.
@@ -190,10 +190,10 @@ jQuery(function ($) {
190
 
191
  // Category select change
192
  $select_category.on('change', function () {
193
- var location_id = $select_location.val(),
194
  category_id = this.value,
195
- service_id = $select_service.val(),
196
- staff_id = $select_employee.val()
197
  ;
198
 
199
  // Validate selected values.
@@ -221,10 +221,10 @@ jQuery(function ($) {
221
 
222
  // Service select change
223
  $select_service.on('change', function () {
224
- var location_id = $select_location.val(),
225
  category_id = '',
226
- service_id = this.value,
227
- staff_id = $select_employee.val()
228
  ;
229
 
230
  // Validate selected values.
@@ -294,10 +294,10 @@ jQuery(function ($) {
294
 
295
  // Staff select change
296
  $select_employee.on('change', function () {
297
- var location_id = $select_location.val(),
298
- category_id = $select_category.val(),
299
- service_id = $select_service.val(),
300
- staff_id = this.value
301
  ;
302
 
303
  setSelects(location_id, category_id, service_id, staff_id);
64
  }
65
 
66
  function setSelects(location_id, category_id, service_id, staff_id) {
67
+ var _location_id = (BooklyFormShortCodeL10n.locationCustom == '1' && location_id) ? location_id : 0;
68
  var _staff = {}, _services = {}, _categories = {}, _nop = {}, _max_capacity = null, _min_capacity = null;
69
  $.each(BooklyFormShortCodeL10n.staff, function (id, staff_member) {
70
  if (!location_id || BooklyFormShortCodeL10n.locations[location_id].staff.hasOwnProperty(id)) {
145
  // Location select change
146
  $select_location.on('change', function () {
147
  var location_id = this.value,
148
+ category_id = $select_category.val()||'',
149
+ service_id = $select_service.val()||'',
150
+ staff_id = $select_employee.val()||''
151
  ;
152
 
153
  // Validate selected values.
190
 
191
  // Category select change
192
  $select_category.on('change', function () {
193
+ var location_id = $select_location.val()||'',
194
  category_id = this.value,
195
+ service_id = $select_service.val()||'',
196
+ staff_id = $select_employee.val()||''
197
  ;
198
 
199
  // Validate selected values.
221
 
222
  // Service select change
223
  $select_service.on('change', function () {
224
+ var location_id = $select_location.val()||'',
225
  category_id = '',
226
+ service_id = this.value,
227
+ staff_id = $select_employee.val()||''
228
  ;
229
 
230
  // Validate selected values.
294
 
295
  // Staff select change
296
  $select_employee.on('change', function () {
297
+ var location_id = $select_location.val()||'',
298
+ category_id = $select_category.val()||'',
299
+ service_id = $select_service.val()||'',
300
+ staff_id = this.value
301
  ;
302
 
303
  setSelects(location_id, category_id, service_id, staff_id);
backend/modules/appointments/resources/js/appointments.js CHANGED
@@ -115,7 +115,7 @@ jQuery(function($) {
115
  columns.push({data: 'customer.email', render: $.fn.dataTable.render.text()});
116
  break;
117
  case 'staff_name':
118
- columns.push({data: 'staff.name'});
119
  break;
120
  case 'service_title':
121
  columns.push({
@@ -174,6 +174,7 @@ jQuery(function($) {
174
  break;
175
  case 'number_of_persons':
176
  case 'locations':
 
177
  columns.push({data: column, render: $.fn.dataTable.render.text()});
178
  break;
179
  case 'online_meeting':
@@ -197,7 +198,7 @@ jQuery(function($) {
197
  orderable: false
198
  });
199
  } else {
200
- columns.push({data: column});
201
  }
202
  break;
203
  }
115
  columns.push({data: 'customer.email', render: $.fn.dataTable.render.text()});
116
  break;
117
  case 'staff_name':
118
+ columns.push({data: 'staff.name', render: $.fn.dataTable.render.text()});
119
  break;
120
  case 'service_title':
121
  columns.push({
174
  break;
175
  case 'number_of_persons':
176
  case 'locations':
177
+ case 'notes':
178
  columns.push({data: column, render: $.fn.dataTable.render.text()});
179
  break;
180
  case 'online_meeting':
198
  orderable: false
199
  });
200
  } else {
201
+ columns.push({data: column, render: $.fn.dataTable.render.text()});
202
  }
203
  break;
204
  }
backend/modules/appointments/templates/index.php CHANGED
@@ -60,7 +60,7 @@ use Bookly\Lib\Utils\DateTime;
60
  data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>" <?php echo $customers === false ? 'data-ajax--action' : 'data-action' ?>="bookly_get_customers_list">
61
  <?php if ( $customers !== false ) : ?>
62
  <?php foreach ( $customers as $customer_id => $customer ) : ?>
63
- <option value="<?php echo $customer_id ?>" data-search='<?php echo json_encode( array_values( $customer ) ) ?>'><?php echo esc_html( $customer['full_name'] ) ?></option>
64
  <?php endforeach ?>
65
  <?php endif ?>
66
  </select>
60
  data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>" <?php echo $customers === false ? 'data-ajax--action' : 'data-action' ?>="bookly_get_customers_list">
61
  <?php if ( $customers !== false ) : ?>
62
  <?php foreach ( $customers as $customer_id => $customer ) : ?>
63
+ <option value="<?php echo $customer_id ?>" data-search='<?php echo esc_attr( json_encode( array_values( $customer ) ) ) ?>'><?php echo esc_html( $customer['full_name'] ) ?></option>
64
  <?php endforeach ?>
65
  <?php endif ?>
66
  </select>
backend/modules/calendar/Page.php CHANGED
@@ -199,7 +199,7 @@ class Page extends Lib\Base\Ajax
199
  $codes['{appointment_date}'] = Lib\Utils\DateTime::formatDate( $appointment['start_date'] );
200
  $codes['{appointment_time}'] = $appointment['duration'] >= DAY_IN_SECONDS ? $appointment['start_time_info'] : Lib\Utils\DateTime::formatTime( $appointment['start_date'] );
201
  $codes['{booking_number}'] = $appointment['id'];
202
- $codes['{internal_note}'] = $appointment['internal_note'];
203
  $codes['{on_waiting_list}'] = $appointment['on_waiting_list'];
204
  $codes['{service_name}'] = $appointment['service_name'] ? esc_html( $appointment['service_name'] ) : __( 'Untitled', 'bookly' );
205
  $codes['{service_price}'] = Lib\Utils\Price::format( $appointment['service_price'] * $appointment['units'] );
199
  $codes['{appointment_date}'] = Lib\Utils\DateTime::formatDate( $appointment['start_date'] );
200
  $codes['{appointment_time}'] = $appointment['duration'] >= DAY_IN_SECONDS ? $appointment['start_time_info'] : Lib\Utils\DateTime::formatTime( $appointment['start_date'] );
201
  $codes['{booking_number}'] = $appointment['id'];
202
+ $codes['{internal_note}'] = esc_html( $appointment['internal_note'] );
203
  $codes['{on_waiting_list}'] = $appointment['on_waiting_list'];
204
  $codes['{service_name}'] = $appointment['service_name'] ? esc_html( $appointment['service_name'] ) : __( 'Untitled', 'bookly' );
205
  $codes['{service_price}'] = Lib\Utils\Price::format( $appointment['service_price'] * $appointment['units'] );
backend/modules/calendar/resources/js/calendar.js CHANGED
@@ -57,7 +57,7 @@ jQuery(function ($) {
57
  staffMembers.length = 0;
58
  this.booklyDropdown('getSelectedExt').forEach(function (item) {
59
  ids.push(item.value);
60
- staffMembers.push({id: item.value, name: item.name});
61
  });
62
  setCookie('bookly_cal_st_ids', ids);
63
  if (all) {
@@ -87,7 +87,7 @@ jQuery(function ($) {
87
  }
88
  // Populate staffMembers.
89
  $staffFilter.booklyDropdown('getSelectedExt').forEach(function (item) {
90
- staffMembers.push({id: item.value, name: item.name});
91
  $tabs.filter('[data-staff_id=' + item.value + ']').parent().show();
92
  });
93
 
@@ -128,6 +128,10 @@ jQuery(function ($) {
128
  }
129
  }
130
 
 
 
 
 
131
  $('#bookly-calendar-refresh').on('click', function () {
132
  $fullCalendar.fullCalendar('refetchEvents');
133
  });
57
  staffMembers.length = 0;
58
  this.booklyDropdown('getSelectedExt').forEach(function (item) {
59
  ids.push(item.value);
60
+ staffMembers.push({id: item.value, name: encodeHTML(item.name)});
61
  });
62
  setCookie('bookly_cal_st_ids', ids);
63
  if (all) {
87
  }
88
  // Populate staffMembers.
89
  $staffFilter.booklyDropdown('getSelectedExt').forEach(function (item) {
90
+ staffMembers.push({id: item.value, name: encodeHTML(item.name)});
91
  $tabs.filter('[data-staff_id=' + item.value + ']').parent().show();
92
  });
93
 
128
  }
129
  }
130
 
131
+ function encodeHTML(s) {
132
+ return s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;').replace(/"/g, '&quot;');
133
+ }
134
+
135
  $('#bookly-calendar-refresh').on('click', function () {
136
  $fullCalendar.fullCalendar('refetchEvents');
137
  });
backend/modules/customers/resources/js/customers.js CHANGED
@@ -27,7 +27,7 @@ jQuery(function($) {
27
  case 'total_appointments':
28
  case 'payments':
29
  case 'wp_user':
30
- columns.push({data: column});
31
  break;
32
  case 'address':
33
  columns.push({data: column, render: $.fn.dataTable.render.text(), orderable: false});
@@ -166,7 +166,7 @@ jQuery(function($) {
166
  address: '',
167
  info_fields: {},
168
  notes: '',
169
- birthday: ''
170
  };
171
  BooklyL10n.infoFields.forEach(function (field) {
172
  customer.info_fields[field.id] = {id: field.id, value: field.type === 'checkboxes' ? [] : ''};
27
  case 'total_appointments':
28
  case 'payments':
29
  case 'wp_user':
30
+ columns.push({data: column, render: $.fn.dataTable.render.text()});
31
  break;
32
  case 'address':
33
  columns.push({data: column, render: $.fn.dataTable.render.text(), orderable: false});
166
  address: '',
167
  info_fields: {},
168
  notes: '',
169
+ birthday: null
170
  };
171
  BooklyL10n.infoFields.forEach(function (field) {
172
  customer.info_fields[field.id] = {id: field.id, value: field.type === 'checkboxes' ? [] : ''};
backend/modules/customers/templates/index.php CHANGED
@@ -18,7 +18,7 @@ use Bookly\Backend\Modules\Customers\Proxy;
18
  <div class="row">
19
  <div class="col-md-4">
20
  <div class="form-group">
21
- <input class="form-control" type="text" id="bookly-filter" placeholder="<?php esc_attr_e( 'Quick search services', 'bookly' ) ?>"/>
22
  </div>
23
  </div>
24
  <div class="col-md-8 form-row justify-content-end pr-0">
18
  <div class="row">
19
  <div class="col-md-4">
20
  <div class="form-group">
21
+ <input class="form-control" type="text" id="bookly-filter" placeholder="<?php esc_attr_e( 'Quick search customers', 'bookly' ) ?>"/>
22
  </div>
23
  </div>
24
  <div class="col-md-8 form-row justify-content-end pr-0">
backend/modules/payments/resources/js/payments.js CHANGED
@@ -137,7 +137,7 @@ jQuery(function($) {
137
  columns.push({data: column, render: $.fn.dataTable.render.text()});
138
  break;
139
  default:
140
- columns.push({data: column});
141
  break;
142
  }
143
  }
137
  columns.push({data: column, render: $.fn.dataTable.render.text()});
138
  break;
139
  default:
140
+ columns.push({data: column, render: $.fn.dataTable.render.text()});
141
  break;
142
  }
143
  }
backend/modules/payments/templates/index.php CHANGED
@@ -46,7 +46,7 @@ use Bookly\Backend\Components\Controls;
46
  <select class="form-control <?php echo $customers === false ? 'bookly-js-select-ajax' : 'bookly-js-select' ?>" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>" <?php echo $customers === false ? 'data-ajax--action' : 'data-action' ?>="bookly_get_customers_list">
47
  <?php if ( $customers !== false ) : ?>
48
  <?php foreach ( $customers as $customer_id => $customer ) : ?>
49
- <option value="<?php echo $customer_id ?>" data-search='<?php echo json_encode( array_values( $customer ) ) ?>'><?php echo esc_html( $customer['full_name'] ) ?></option>
50
  <?php endforeach ?>
51
  <?php endif ?>
52
  </select>
46
  <select class="form-control <?php echo $customers === false ? 'bookly-js-select-ajax' : 'bookly-js-select' ?>" id="bookly-filter-customer" data-placeholder="<?php esc_attr_e( 'Customer', 'bookly' ) ?>" <?php echo $customers === false ? 'data-ajax--action' : 'data-action' ?>="bookly_get_customers_list">
47
  <?php if ( $customers !== false ) : ?>
48
  <?php foreach ( $customers as $customer_id => $customer ) : ?>
49
+ <option value="<?php echo $customer_id ?>" data-search='<?php echo esc_attr( json_encode( array_values( $customer ) ) ) ?>'><?php echo esc_html( $customer['full_name'] ) ?></option>
50
  <?php endforeach ?>
51
  <?php endif ?>
52
  </select>
backend/modules/services/resources/js/services-list.js CHANGED
@@ -85,13 +85,13 @@ jQuery(function ($) {
85
  });
86
  break;
87
  default:
88
- columns.push({data: column});
89
  break;
90
  }
91
  }
92
  });
93
  columns.push({
94
- responsivePriority: 1,
95
  orderable: false,
96
  searchable: false,
97
  render: function (data, type, row, meta) {
@@ -235,7 +235,8 @@ jQuery(function ($) {
235
  $servicesList.on('click', '[data-action="duplicate"]', function () {
236
  if (confirm(BooklyL10n.are_you_sure + "\n\n" + BooklyL10n.private_warning)) {
237
  let ladda = rangeTools.ladda(this),
238
- data = $servicesList.DataTable().row($(this).closest('td')).data();
 
239
  $.post(
240
  ajaxurl,
241
  {
85
  });
86
  break;
87
  default:
88
+ columns.push({data: column, render: $.fn.dataTable.render.text()});
89
  break;
90
  }
91
  }
92
  });
93
  columns.push({
94
+ responsivePriority: 2,
95
  orderable: false,
96
  searchable: false,
97
  render: function (data, type, row, meta) {
235
  $servicesList.on('click', '[data-action="duplicate"]', function () {
236
  if (confirm(BooklyL10n.are_you_sure + "\n\n" + BooklyL10n.private_warning)) {
237
  let ladda = rangeTools.ladda(this),
238
+ $tr = $(this).closest('tr'),
239
+ data = $servicesList.DataTable().row($tr.hasClass('child') ? $tr.prev() : $tr).data();
240
  $.post(
241
  ajaxurl,
242
  {
backend/modules/staff/resources/js/staff-list.js CHANGED
@@ -49,7 +49,7 @@ jQuery(function ($) {
49
  });
50
  break;
51
  default:
52
- columns.push({data: column});
53
  break;
54
  }
55
  }
49
  });
50
  break;
51
  default:
52
+ columns.push({data: column, render: $.fn.dataTable.render.text()});
53
  break;
54
  }
55
  }
frontend/components/booking/InfoText.php CHANGED
@@ -91,8 +91,8 @@ class InfoText
91
  $staff = Lib\Entities\Staff::find( $staff_ids[0] );
92
  }
93
  if ( $staff ) {
94
- $data['staff_names'][] = $staff->getTranslatedName();
95
- $data['staff_info'][] = $staff->getTranslatedInfo();
96
  if ( $staff->getAttachmentId() && $img = wp_get_attachment_image_src( $staff->getAttachmentId(), 'full' ) ) {
97
  $staff_photo = '<img src="' . $img[0] . '"/>';
98
  }
@@ -198,8 +198,8 @@ class InfoText
198
  }
199
  // For Task when time step can be skipped, staff can be false
200
  $staff = $cart_item->getStaff();
201
- $data['staff_info'][] = $staff ? $staff->getTranslatedInfo() : '';
202
- $data['staff_name'][] = $staff ? $staff->getTranslatedName() : '';
203
  if ( $staff && $staff->getAttachmentId() && $img = wp_get_attachment_image_src( $staff->getAttachmentId(), 'full' ) ) {
204
  $data['staff_photo'][] = '<img src="' . $img[0] . '"/>';
205
  } else {
91
  $staff = Lib\Entities\Staff::find( $staff_ids[0] );
92
  }
93
  if ( $staff ) {
94
+ $data['staff_names'][] = esc_html( $staff->getTranslatedName() );
95
+ $data['staff_info'][] = esc_html( $staff->getTranslatedInfo() );
96
  if ( $staff->getAttachmentId() && $img = wp_get_attachment_image_src( $staff->getAttachmentId(), 'full' ) ) {
97
  $staff_photo = '<img src="' . $img[0] . '"/>';
98
  }
198
  }
199
  // For Task when time step can be skipped, staff can be false
200
  $staff = $cart_item->getStaff();
201
+ $data['staff_info'][] = $staff ? esc_html( $staff->getTranslatedInfo() ) : '';
202
+ $data['staff_name'][] = $staff ? esc_html( $staff->getTranslatedName() ) : '';
203
  if ( $staff && $staff->getAttachmentId() && $img = wp_get_attachment_image_src( $staff->getAttachmentId(), 'full' ) ) {
204
  $data['staff_photo'][] = '<img src="' . $img[0] . '"/>';
205
  } else {
lib/CartItem.php CHANGED
@@ -124,30 +124,30 @@ class CartItem
124
  static $service_prices_cache = array();
125
 
126
  $service = $this->getService();
127
- list ( $service_id, $staff_id, , $location_id ) = $this->slots[0];
128
-
129
- if ( Config::specialHoursActive() ) {
130
- $service_start = date( 'H:i:s', strtotime( $this->slots[0][2] ) );
131
  } else {
132
- $service_start = 'unused'; //the price is the same for all services in day
133
- }
134
 
135
- if ( isset ( $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ] ) ) {
136
- $service_price = $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ];
137
- } else {
138
- if ( $service->withSubServices() ) {
139
- $service_price = $service->getPrice();
 
 
140
  } else {
141
  $staff_service = new Entities\StaffService();
142
- $location_id = Proxy\Locations::prepareStaffLocationId( $location_id, $staff_id ) ?: null;
143
  $staff_service->loadBy( compact( 'staff_id', 'service_id', 'location_id' ) );
144
  if ( ! $staff_service->isLoaded() ) {
145
  $staff_service->loadBy( array( 'staff_id' => $staff_id, 'service_id' => $service_id, 'location_id' => null ) );
146
  }
147
  $service_price = $staff_service->getPrice() * $this->getUnits();
148
  $service_price = Proxy\SpecialHours::adjustPrice( $service_price, $staff_id, $service_id, $location_id, $service_start, $this->getUnits() );
 
149
  }
150
- $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ] = $service_price;
151
  }
152
 
153
  return $service_price;
124
  static $service_prices_cache = array();
125
 
126
  $service = $this->getService();
127
+ if ( $service->withSubServices() ) {
128
+ $service_price = $service->getPrice();
 
 
129
  } else {
130
+ list ( $service_id, $staff_id, , $location_id ) = $this->slots[0];
 
131
 
132
+ if ( Config::specialHoursActive() ) {
133
+ $service_start = date( 'H:i:s', strtotime( $this->slots[0][2] ) );
134
+ } else {
135
+ $service_start = 'unused'; //the price is the same for all services in day
136
+ }
137
+ if ( isset ( $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ] ) ) {
138
+ $service_price = $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ];
139
  } else {
140
  $staff_service = new Entities\StaffService();
141
+ $location_id = Proxy\Locations::prepareStaffLocationId( $location_id, $staff_id ) ?: null;
142
  $staff_service->loadBy( compact( 'staff_id', 'service_id', 'location_id' ) );
143
  if ( ! $staff_service->isLoaded() ) {
144
  $staff_service->loadBy( array( 'staff_id' => $staff_id, 'service_id' => $service_id, 'location_id' => null ) );
145
  }
146
  $service_price = $staff_service->getPrice() * $this->getUnits();
147
  $service_price = Proxy\SpecialHours::adjustPrice( $service_price, $staff_id, $service_id, $location_id, $service_start, $this->getUnits() );
148
+ $service_prices_cache[ $staff_id ][ $service_id ][ $location_id ][ $service_start ] = $service_price;
149
  }
150
+
151
  }
152
 
153
  return $service_price;
lib/Updater.php CHANGED
@@ -16,21 +16,21 @@ class Updater extends Base\Updater
16
 
17
  function update_18_3()
18
  {
19
- $disposable_options[] = $this->disposable( __FUNCTION__ . '-add-tokens-1', function () {
20
- $this->alterTables( array(
 
21
  'bookly_payments' => array(
22
  'ALTER TABLE `%s` ADD COLUMN `token` VARCHAR(255) DEFAULT NULL AFTER `status`',
23
  ),
24
  ) );
25
- });
26
-
27
 
28
- $disposable_options[] = $this->disposable( __FUNCTION__ . '-add-tokens-2', function () {
29
  /** @global \wpdb $wpdb */
30
  global $wpdb;
31
 
32
  // Setup tokens for existing payments
33
- $payments_table = $this->getTableName( 'bookly_payments' );
34
 
35
  foreach ( $wpdb->get_results( 'SELECT id FROM `' . $payments_table . '` WHERE token IS NULL' ) as $record ) {
36
  $wpdb->query( $wpdb->prepare( 'UPDATE `' . $payments_table . '` SET `token` = %s WHERE id = %d', Utils\Common::generateToken( 'Bookly\Lib\Entities\Payment', 'token' ), $record->id ) );
16
 
17
  function update_18_3()
18
  {
19
+ $self = $this;
20
+ $disposable_options[] = $this->disposable( __FUNCTION__ . '-add-tokens-1', function () use ( $self ) {
21
+ $self->alterTables( array(
22
  'bookly_payments' => array(
23
  'ALTER TABLE `%s` ADD COLUMN `token` VARCHAR(255) DEFAULT NULL AFTER `status`',
24
  ),
25
  ) );
26
+ } );
 
27
 
28
+ $disposable_options[] = $this->disposable( __FUNCTION__ . '-add-tokens-2', function () use ( $self ) {
29
  /** @global \wpdb $wpdb */
30
  global $wpdb;
31
 
32
  // Setup tokens for existing payments
33
+ $payments_table = $self->getTableName( 'bookly_payments' );
34
 
35
  foreach ( $wpdb->get_results( 'SELECT id FROM `' . $payments_table . '` WHERE token IS NULL' ) as $record ) {
36
  $wpdb->query( $wpdb->prepare( 'UPDATE `' . $payments_table . '` SET `token` = %s WHERE id = %d', Utils\Common::generateToken( 'Bookly\Lib\Entities\Payment', 'token' ), $record->id ) );
lib/entities/Service.php CHANGED
@@ -274,6 +274,7 @@ class Service extends Lib\Base\Entity
274
  break;
275
  default:
276
  foreach ( $appointment_dates as $appointment_date ) {
 
277
  switch ( $this->getLimitPeriod() ) {
278
  case 'calendar_day':
279
  $bound_start = date_create( $appointment_date )->format( 'Y-m-d 00:00:00' );
@@ -295,32 +296,45 @@ class Service extends Lib\Base\Entity
295
  $bound_start = date_create( $appointment_date )->modify( 'first day of January' )->format( 'Y-m-d 00:00:00' );
296
  $bound_end = date_create( $appointment_date )->modify( 'last day of December' )->format( 'Y-m-d 23:59:59' );
297
  break;
 
298
  case 'day':
299
  $bound_start = date_create( $appointment_date )->modify( '-1 day' )->format( 'Y-m-d H:i:s' );
300
  $bound_end = $appointment_date;
 
301
  break;
302
  case 'week':
303
  $bound_start = date_create( $appointment_date )->modify( '-1 week' )->format( 'Y-m-d H:i:s' );
304
  $bound_end = $appointment_date;
 
305
  break;
306
  case 'month':
307
  $bound_start = date_create( $appointment_date )->modify( '-30 days' )->format( 'Y-m-d H:i:s' );
308
  $bound_end = $appointment_date;
 
309
  break;
310
  case 'year':
311
  $bound_start = date_create( $appointment_date )->modify( '-365 days' )->format( 'Y-m-d H:i:s' );
312
  $bound_end = $appointment_date;
 
313
  break;
314
  }
315
- $db_count = CustomerAppointment::query( 'ca' )
316
  ->leftJoin( 'Appointment', 'a', 'ca.appointment_id = a.id' )
317
  ->where( 'a.service_id', $service_id )
318
  ->where( 'ca.compound_service_id', $compound_service_id )
319
  ->where( 'ca.customer_id', $customer_id )
320
- ->whereGte( 'a.start_date', $bound_start )
321
- ->whereLt( 'a.start_date', $bound_end )
322
- ->whereNot( 'ca.status', CustomerAppointment::STATUS_WAITLISTED )
323
- ->count();
 
 
 
 
 
 
 
 
324
  $cart_count = 0;
325
  $bound_start = strtotime( $bound_start );
326
  $bound_end = strtotime( $bound_end );
274
  break;
275
  default:
276
  foreach ( $appointment_dates as $appointment_date ) {
277
+ $regarding_appointment = false;
278
  switch ( $this->getLimitPeriod() ) {
279
  case 'calendar_day':
280
  $bound_start = date_create( $appointment_date )->format( 'Y-m-d 00:00:00' );
296
  $bound_start = date_create( $appointment_date )->modify( 'first day of January' )->format( 'Y-m-d 00:00:00' );
297
  $bound_end = date_create( $appointment_date )->modify( 'last day of December' )->format( 'Y-m-d 23:59:59' );
298
  break;
299
+
300
  case 'day':
301
  $bound_start = date_create( $appointment_date )->modify( '-1 day' )->format( 'Y-m-d H:i:s' );
302
  $bound_end = $appointment_date;
303
+ $regarding_appointment = true;
304
  break;
305
  case 'week':
306
  $bound_start = date_create( $appointment_date )->modify( '-1 week' )->format( 'Y-m-d H:i:s' );
307
  $bound_end = $appointment_date;
308
+ $regarding_appointment = true;
309
  break;
310
  case 'month':
311
  $bound_start = date_create( $appointment_date )->modify( '-30 days' )->format( 'Y-m-d H:i:s' );
312
  $bound_end = $appointment_date;
313
+ $regarding_appointment = true;
314
  break;
315
  case 'year':
316
  $bound_start = date_create( $appointment_date )->modify( '-365 days' )->format( 'Y-m-d H:i:s' );
317
  $bound_end = $appointment_date;
318
+ $regarding_appointment = true;
319
  break;
320
  }
321
+ $query = CustomerAppointment::query( 'ca' )
322
  ->leftJoin( 'Appointment', 'a', 'ca.appointment_id = a.id' )
323
  ->where( 'a.service_id', $service_id )
324
  ->where( 'ca.compound_service_id', $compound_service_id )
325
  ->where( 'ca.customer_id', $customer_id )
326
+ ->whereNot( 'ca.status', CustomerAppointment::STATUS_WAITLISTED );
327
+ if ( $regarding_appointment ) {
328
+ $query
329
+ ->whereGt( 'a.start_date', $bound_start )
330
+ ->whereLte( 'a.start_date', $bound_end );
331
+ } else {
332
+ $query
333
+ ->whereGte( 'a.start_date', $bound_start )
334
+ ->whereLt( 'a.start_date', $bound_end );
335
+ }
336
+
337
+ $db_count = $query->count();
338
  $cart_count = 0;
339
  $bound_start = strtotime( $bound_start );
340
  $bound_end = strtotime( $bound_end );
main.php CHANGED
@@ -3,7 +3,7 @@
3
  Plugin Name: Bookly
4
  Plugin URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
5
  Description: Bookly Plugin – is a great easy-to-use and easy-to-manage booking tool for service providers who think about their customers. The plugin supports a wide range of services provided by business and individuals who offer reservations through websites. Set up any reservation quickly, pleasantly and easily with Bookly!
6
- Version: 18.4
7
  Author: Bookly
8
  Author URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
9
  Text Domain: bookly
3
  Plugin Name: Bookly
4
  Plugin URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
5
  Description: Bookly Plugin – is a great easy-to-use and easy-to-manage booking tool for service providers who think about their customers. The plugin supports a wide range of services provided by business and individuals who offer reservations through websites. Set up any reservation quickly, pleasantly and easily with Bookly!
6
+ Version: 18.5
7
  Author: Bookly
8
  Author URI: https://www.booking-wp-plugin.com/?utm_source=bookly_admin&utm_medium=plugins_page&utm_campaign=plugins_page
9
  Text Domain: bookly
readme.txt CHANGED
@@ -5,7 +5,7 @@ Donate link: https://www.booking-wp-plugin.com/
5
  Requires at least: 3.7
6
  Tested up to: 5.4.2
7
  Requires PHP: 5.3.7
8
- Stable tag: 18.4
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
@@ -13,7 +13,7 @@ Bookly is a booking plugin for WordPress for building an advanced automated onli
13
 
14
  == Description ==
15
 
16
- **Bookly** is a free **scheduling plugin for WordPress** that allows accepting **online bookings** on your website and automating your **reservation system**. Manage your **booking calendar**, services, client base, save time and money – all in one place. Join more than 30,000 businesses all around the world that have already automated their **online booking system**!
17
 
18
  https://youtu.be/XkCrADjLt2s
19
 
5
  Requires at least: 3.7
6
  Tested up to: 5.4.2
7
  Requires PHP: 5.3.7
8
+ Stable tag: 18.5
9
  License: GPLv3
10
  License URI: http://www.gnu.org/licenses/gpl-3.0.html
11
 
13
 
14
  == Description ==
15
 
16
+ **Bookly** is a free **scheduling plugin for WordPress** that allows accepting **online bookings** on your website and automating your **reservation system**. Manage your **booking calendar**, services, client base, save time and money – all in one place. Join more than 40,000 businesses all around the world that have already automated their **online booking system**!
17
 
18
  https://youtu.be/XkCrADjLt2s
19