← All changes
|
core/module/google-calendar/Controller/calendar-sync-controller.php
+61
-7
1.0.21
→
1.0.28
View file →
| @@ -67,8 +67,9 @@ | ||
| 67 | 67 | add_filter( 'booktics_booking_calendar_urls', array( $this, 'get_add_to_calendar_links' ), 10, 2 ); |
| 68 | 68 | |
| 69 | 69 | add_action( 'booktics_after_appointment_created', array( $this, 'handle_appointment_created' ), 10, 1 ); |
| 70 | 70 | add_action( 'booktics_after_appointment_updated', array( $this, 'handle_appointment_updated' ), 10, 1 ); |
| 71 | + add_action( 'booktics_after_appointment_cancelled', array( $this, 'handle_appointment_cancelled' ), 10, 1 ); | |
| 71 | 72 | add_action( 'before_delete_post', array( $this, 'capture_appointment_data_before_delete' ), 10, 1 ); |
| 72 | 73 | add_action( 'booktics_after_appointment_deleted', array( $this, 'handle_appointment_deleted' ), 10, 1 ); |
| 73 | 74 | } |
| 74 | 75 | |
| @@ -161,9 +162,9 @@ | ||
| 161 | 162 | if ( $event_id ) { |
| 162 | 163 | $this->calendar->update_event( $event_id, $appointment, $team_member_id ); |
| 163 | 164 | } |
| 164 | 165 | } catch ( \Throwable $e ) { |
| 165 | - if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { | |
| 166 | + if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { | |
| 166 | 167 | error_log( 'Failed to updated Google Calendar event: ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log |
| 167 | 168 | } |
| 168 | 169 | } |
| 169 | 170 | } |
| @@ -168,8 +169,47 @@ | ||
| 168 | 169 | } |
| 169 | 170 | } |
| 170 | 171 | |
| 171 | 172 | /** |
| 173 | + * On appointment cancelled, remove its Google Calendar event. | |
| 174 | + * | |
| 175 | + * A cancellation only flips the appointment status (no post deletion), so | |
| 176 | + * the delete hooks never fire. Without this the calendar event lingers in | |
| 177 | + * the team member's calendar for a booking that was auto-cancelled (e.g. an | |
| 178 | + * abandoned/unpaid online booking) or refunded, keeping the slot visually | |
| 179 | + * blocked. Deleting the event frees the calendar slot to match the freed | |
| 180 | + * appointment slot. | |
| 181 | + * | |
| 182 | + * @param \Booktics\Models\Appointment_Model $appointment appointment data | |
| 183 | + */ | |
| 184 | + public function handle_appointment_cancelled( $appointment ) { | |
| 185 | + try { | |
| 186 | + if ( ! isset( $appointment->id ) ) { | |
| 187 | + return; | |
| 188 | + } | |
| 189 | + | |
| 190 | + $event_id = get_post_meta( $appointment->id, '_google_calendar_event_id', true ); | |
| 191 | + $team_member_id = get_post_meta( $appointment->id, 'team_member_id', true ); | |
| 192 | + | |
| 193 | + if ( ! $team_member_id && isset( $appointment->team_member_id ) ) { | |
| 194 | + $team_member_id = $appointment->team_member_id; | |
| 195 | + } | |
| 196 | + | |
| 197 | + if ( ! $event_id || ! $team_member_id ) { | |
| 198 | + return; | |
| 199 | + } | |
| 200 | + | |
| 201 | + if ( $this->calendar->delete_event( $event_id, $team_member_id ) ) { | |
| 202 | + delete_post_meta( $appointment->id, '_google_calendar_event_id' ); | |
| 203 | + } | |
| 204 | + } catch ( \Throwable $e ) { | |
| 205 | + if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { | |
| 206 | + error_log( '[Booktics Google Calendar] Failed to delete Google Calendar event on cancel: ' . $e->getMessage() ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 207 | + } | |
| 208 | + } | |
| 209 | + } | |
| 210 | + | |
| 211 | + /** | |
| 172 | 212 | * Capture appointment Google Calendar data before WordPress deletes the post. |
| 173 | 213 | * |
| 174 | 214 | * @param int $post_id Post ID being deleted |
| 175 | 215 | */ |
| @@ -285,13 +325,26 @@ | ||
| 285 | 325 | if ( ! $formatted_appointment['service']['enable_google_meet'] ) { |
| 286 | 326 | return $notification_flow_data; |
| 287 | 327 | } |
| 288 | 328 | |
| 289 | - $user_id = get_current_user_id(); | |
| 290 | - $tokens = get_user_meta( $user_id, 'booktics_google_auth_token', true ); | |
| 291 | - $access_token = $tokens['access_token'] ?? ''; | |
| 329 | + $user_id = $formatted_appointment['team']['id'] ?? null; | |
| 292 | 330 | |
| 331 | + if ( empty( $user_id ) ) { | |
| 332 | + return $notification_flow_data; | |
| 333 | + } | |
| 334 | + | |
| 335 | + // get_access_token_for_api() also refreshes the token when it has expired. | |
| 336 | + $access_token = $this->calendar->get_access_token_for_api( $user_id ); | |
| 337 | + | |
| 293 | 338 | if ( empty( $access_token ) ) { |
| 339 | + // The booking's own team member owns the Meet link, so a member who never | |
| 340 | + // connected Google - or whose token has died - silently drops the link | |
| 341 | + // from the email. Log it, otherwise the only symptom is an empty | |
| 342 | + // google_meet_url smart tag with nothing to explain it. | |
| 343 | + if ( defined( 'WP_DEBUG_LOG' ) && WP_DEBUG_LOG ) { | |
| 344 | + error_log( '[Booktics Google Calendar] No usable Google token for team member ' . $user_id . '; skipping Meet link.' ); // phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_error_log | |
| 345 | + } | |
| 346 | + | |
| 294 | 347 | return $notification_flow_data; |
| 295 | 348 | } |
| 296 | 349 | try { |
| 297 | 350 | // Get the WordPress timezone |
| @@ -297,11 +350,12 @@ | ||
| 297 | 350 | // Get the WordPress timezone |
| 298 | 351 | $timezone = wp_timezone(); |
| 299 | 352 | $timezone_string = $timezone->getName(); |
| 300 | 353 | |
| 301 | - // Convert timestamps to ISO 8601 format with timezone | |
| 302 | - $start_timestamp = strtotime( $formatted_appointment['start_time'] ); | |
| 303 | - $end_timestamp = strtotime( $formatted_appointment['end_time'] ); | |
| 354 | + // Appointment date/time are stored in UTC. Combine them so the Meet event lands on the actual appointment date instead of "today". | |
| 355 | + $appointment_date = $formatted_appointment['date'] ?? ''; | |
| 356 | + $start_timestamp = strtotime( $appointment_date . ' ' . $formatted_appointment['start_time'] . ' UTC' ); | |
| 357 | + $end_timestamp = strtotime( $appointment_date . ' ' . $formatted_appointment['end_time'] . ' UTC' ); | |
| 304 | 358 | |
| 305 | 359 | // Format dates in ISO 8601 format with timezone using wp_date |
| 306 | 360 | $start_time = wp_date( 'c', $start_timestamp, $timezone ); |
| 307 | 361 | $end_time = wp_date( 'c', $end_timestamp, $timezone ); |