| @@ -5,21 +5,12 @@ | ||
| 5 | 5 | use FluentBooking\App\Models\Booking; |
| 6 | 6 | use FluentBooking\App\Models\CalendarSlot; |
| 7 | 7 | |
| 8 | 8 | /** |
| 9 | - * Moves an existing booking to a new time. | |
| 9 | + * Moves an existing booking to a new time. Shared by the public form and | |
| 10 | + * programmatic callers such as MCP, so all fire the same hooks. Failures come | |
| 11 | + * back as WP_Error. A reschedule changes the time and leaves `status` alone. | |
| 10 | 12 | * |
| 11 | - * This logic used to live inline in FrontEndHandler::handleRescheduling(), where | |
| 12 | - * it was reachable only through the public booking form and signalled every | |
| 13 | - * failure with wp_send_json() — which made it impossible to call from anywhere | |
| 14 | - * else without terminating the request. It is extracted here so the public form | |
| 15 | - * and programmatic callers (the MCP server among them) run the exact same steps | |
| 16 | - * and emit the exact same hooks. Failures come back as WP_Error; the caller | |
| 17 | - * decides how to render them. | |
| 18 | - * | |
| 19 | - * Behaviour is intentionally identical to the original inline version: a | |
| 20 | - * reschedule changes the time and leaves `status` alone. | |
| 21 | - * | |
| 22 | 13 | * @since 2.2.6 |
| 23 | 14 | */ |
| 24 | 15 | class RescheduleService |
| 25 | 16 | { |
| @@ -40,12 +31,9 @@ | ||
| 40 | 31 | * @return Booking|\WP_Error The updated booking, or the reason it was refused. |
| 41 | 32 | */ |
| 42 | 33 | public static function reschedule(Booking $booking, CalendarSlot $calendarEvent, $startTime, $timezone, $args = []) |
| 43 | 34 | { |
| 44 | - // The booking must be rescheduled against its own event. Reject mixed-object | |
| 45 | - // requests where the posted event_id differs from the booking's event so the | |
| 46 | - // availability validation cannot be performed under a different event than the | |
| 47 | - // one actually being modified. | |
| 35 | + // Availability must be validated against the booking's own event. | |
| 48 | 36 | if ((int) $booking->event_id !== (int) $calendarEvent->id) { |
| 49 | 37 | return new \WP_Error('invalid_reschedule_request', __('Invalid rescheduling request', 'fluent-booking'), ['status' => 422]); |
| 50 | 38 | } |
| 51 | 39 | |
| @@ -64,20 +52,17 @@ | ||
| 64 | 52 | $previousBooking = clone $booking; |
| 65 | 53 | |
| 66 | 54 | $reason = isset($args['reason']) ? sanitize_textarea_field($args['reason']) : ''; |
| 67 | 55 | |
| 68 | - // The pivot is synced before the row is saved, so a failure between them | |
| 69 | - // left hosts() naming the new host while the row said the old one. | |
| 56 | + // The host pivot syncs before the row saves, so keep both in one transaction. | |
| 70 | 57 | try { |
| 71 | 58 | Helper::dbTransaction(function () use ($booking, $rescheduleBy, $startTime, $timezone, $endDateTime, $previousBooking, $reason, $args) { |
| 72 | - // Below the guards: both return, so writing above them stamped a | |
| 73 | - // reschedule that never happened, and NotificationHandler reads | |
| 74 | - // this to pick the host- or attendee-worded email. | |
| 59 | + // Written after the guards so a refused reschedule leaves no trace. | |
| 60 | + // NotificationHandler reads it to pick the host or attendee email. | |
| 75 | 61 | $booking->updateMeta('rescheduled_by_type', $rescheduleBy); |
| 76 | 62 | |
| 77 | 63 | if ($booking->isMultiGuestBooking()) { |
| 78 | - // Need to handle group booking type here | |
| 79 | - // check for existing group | |
| 64 | + // Join the existing group at the new time, if any. | |
| 80 | 65 | $parent = Booking::where('status', 'scheduled') |
| 81 | 66 | ->where('event_id', $booking->event_id) |
| 82 | 67 | ->where('start_time', $startTime) |
| 83 | 68 | ->orderBy('id', 'ASC') |