PluginProbe
Booktics – Appointment Booking Calendar for Service Businesses / 1.0.28
Booktics – Appointment Booking Calendar for Service Businesses v1.0.28
1.0.29 1.0.28 1.0.27 1.0.26 1.0.25 1.0.24 1.0.23 1.0.22 1.0.21 1.0.20 1.0.19 trunk 1.0.0 1.0.1 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.2 1.0.3 All 31 releases
← All changes | core/cart/controllers/cart-controller.php +95 -12 1.0.3 → 1.0.28 View file →
@@ -102,17 +102,30 @@
102 102 *
103 103 * @return []|array|bool|WP_Error
104 104 */
105 105 private function prepare_item_to_save( array $data ) {
106 - $validated = booktics_validate(
107 - $data, array(
108 - 'date' => array( 'required' ),
109 - 'start_time' => array( 'required' ),
110 - 'service_id' => array( 'required' ),
111 - 'team_member_id' => array( 'required' ),
112 - )
106 + $item_rules = array(
107 + 'date' => array( 'required' ),
108 + 'start_time' => array( 'required' ),
109 + 'service_id' => array( 'required' ),
110 + 'team_member_id' => array( 'required' ),
113 111 );
114 112
113 + /**
114 + * Filters the validation rules applied to a cart item.
115 + *
116 + * Extensions that assign a team member server-side (Booktics Pro
117 + * round robin) drop the team_member_id requirement here.
118 + *
119 + * @since 1.0.26
120 + *
121 + * @param array $item_rules Validation rules.
122 + * @param array $data The cart item being validated.
123 + */
124 + $item_rules = apply_filters( 'booktics_cart_item_validation_rules', $item_rules, $data );
125 +
126 + $validated = booktics_validate( $data, $item_rules );
127 +
115 128 if ( is_wp_error( $validated ) ) {
116 129 return $validated;
117 130 }
118 131
@@ -142,12 +155,19 @@
142 155 'Validation',
143 156 $validated->errors
144 157 );
145 158 }
159 + // Calculate end time from start time and total duration
160 + $total_duration_min = $this->calculate_total_duration( $validated );
161 + $validated['end_time'] = $this->get_endtime_from_starttime($validated['start_time'], $total_duration_min);
162 +
146 163 $response = $this->cart_manager->add_to_cart( $validated );
147 164 $response['service'] = $this->get_service( $response['service_id'] );
148 165 $response['team'] = $this->get_team_member( $response['team_member_id'] );
149 166
167 + // Allow booktics-pro to add location details
168 + $response = apply_filters( 'booktics_cart_item_response', $response );
169 +
150 170 return $this->response( $response, __( 'Item added to cart', 'booktics' ), 201 );
151 171 }
152 172
153 173 /**
@@ -157,9 +177,9 @@
157 177 *
158 178 * @return array|\WP_HTTP_Response
159 179 */
160 180 public function delete_from_cart( $request ) {
161 - $uuid = $request->get_param( 'uuid' );
181 + $uuid = sanitize_text_field( $request->get_param( 'uuid' ) );
162 182
163 183 if ( empty( $uuid ) ) {
164 184 return $this->error(
165 185 __( 'UUID is required', 'booktics' ),
@@ -204,8 +224,11 @@
204 224
205 225 $item['service'] = $this->get_service( $item['service_id'] );
206 226 $item['team'] = $this->get_team_member( $item['team_member_id'] );
207 227
228 + // Allow booktics-pro to add location details
229 + $item = apply_filters( 'booktics_cart_item_response', $item );
230 +
208 231 return $item;
209 232 }, $response['items']
210 233 ) : array();
211 234
@@ -235,11 +258,16 @@
235 258 if ( ! $service ) {
236 259 $error->add( 'invalid_service', __( 'Invalid service selected', 'booktics' ) );
237 260 }
238 261
239 - $team_member = ( new Team_Member_Model() )->find( $data['team_member_id'] );
240 - if ( empty( $team_member ) ) {
241 - $error->add( 'invalid_team_member', __( 'Invalid team member selected', 'booktics' ) );
262 + // A cart item may legitimately arrive without a team member when the
263 + // service assigns one server-side. Only verify a member that was
264 + // actually supplied; the booking is refused later if none can be found.
265 + if ( ! empty( $data['team_member_id'] ) ) {
266 + $team_member = ( new Team_Member_Model() )->find( $data['team_member_id'] );
267 + if ( empty( $team_member ) ) {
268 + $error->add( 'invalid_team_member', __( 'Invalid team member selected', 'booktics' ) );
269 + }
242 270 }
243 271
244 272 $service_price = 0;
245 273 $addition_durations = $service->additional_durations;
@@ -264,9 +292,9 @@
264 292 *
265 293 * @return array|\WP_HTTP_Response
266 294 */
267 295 public function edit_cart_item( $request ) {
268 - $uuid = $request->get_param( 'uuid' );
296 + $uuid = sanitize_text_field( (string) $request->get_param( 'uuid' ) );
269 297 $params = json_decode( $request->get_body(), true );
270 298
271 299 if ( empty( $uuid ) ) {
272 300 return $this->error(
@@ -288,8 +316,12 @@
288 316
289 317 // Add UUID to validated data
290 318 $validated['uuid'] = $uuid;
291 319
320 + // Calculate end time from start time and total duration
321 + $total_duration_min = $this->calculate_total_duration( $validated );
322 + $validated['end_time'] = $this->get_endtime_from_starttime( $validated['start_time'], $total_duration_min );
323 +
292 324 // Update the cart item
293 325 $updated_item = $this->cart_manager->edit_cart_item( $uuid, $validated );
294 326
295 327 if ( empty( $updated_item ) ) {
@@ -299,8 +331,11 @@
299 331 'NotFound'
300 332 );
301 333 }
302 334
335 + // Allow booktics-pro to add location details
336 + $updated_item = apply_filters( 'booktics_cart_item_response', $updated_item );
337 +
303 338 return $this->response(
304 339 $updated_item,
305 340 __( 'Successfully updated cart item', 'booktics' )
306 341 );
@@ -339,6 +374,54 @@
339 374 'id' => $team_member['id'],
340 375 'display_name' => $team_member['display_name'] ?? '',
341 376 'image' => $team_member['image'] ?? '',
342 377 );
378 + }
379 +
380 + /**
381 + * Calculate total duration for a cart item (appointment)
382 + *
383 + * @param array $item Cart item data
384 + *
385 + * @return int Total duration in minutes
386 + */
387 + private function calculate_total_duration( $item ) {
388 + $service_model = Service_Model::find( $item['service_id'] );
389 + if ( ! $service_model ) {
390 + return 0;
391 + }
392 + $service_duration = array_filter(
393 + $service_model->additional_durations, function ( $duration ) use ( $item ) {
394 + return $item['additional_duration_id'] === $duration['id'];
395 + }
396 + );
397 + if ( empty( $service_duration ) ) {
398 + return 0;
399 + }
400 + $service_duration = reset( $service_duration );
401 + $total_duration_min = $service_duration['duration_unit'] === 'min'
402 + ? $service_duration['duration_time']
403 + : ( $service_duration['duration_time'] * 60 );
404 + if ( ! empty( $item['extra_services'] ) ) {
405 + $total_duration_min = apply_filters( 'booktics_extra_service_duration', $total_duration_min, $item['extra_services'] );
406 + }
407 +
408 + $total_duration_min = apply_filters( 'booktics_calculate_total_duration', $total_duration_min, $item );
409 +
410 + return $total_duration_min;
411 + }
412 +
413 + /**
414 + * Get end time for appointments from start time and total duration
415 + *
416 + * @param $start_time start time of the appointment
417 + * @param $total_duration_min total duration of the appointment in minutes including service time and extra service time
418 + *
419 + * @return string end time of the appointment
420 + */
421 + private function get_endtime_from_starttime( $start_time, $total_duration_min ) {
422 + $start_time = strtotime( $start_time );
423 + $end_time = strtotime( '+' . $total_duration_min . ' minutes', $start_time );
424 +
425 + return gmdate( 'H:i', $end_time );
343 426 }
344 427 }