getInputs(); $id = (int) ( $inputs['booking_id'] ?? 0 ); $when = trim( (string) ( $inputs['start_date'] ?? '' ) . ' ' . (string) ( $inputs['start_time'] ?? '' ) ); $descriptor = $this->describe_booking( $id ); $subject = '' !== $descriptor ? "booking #{$id} — {$descriptor}" : "booking #{$id}"; return "Reschedule {$subject} to {$when}"; } public function get_approval_inputs(): array { $inputs = $this->getInputs(); $descriptor = $this->describe_booking( (int) ( $inputs['booking_id'] ?? 0 ) ); if ( '' !== $descriptor ) { $inputs['current_booking'] = $descriptor; } return $inputs; } public function __invoke( int $booking_id, string $start_date, string $start_time, ?int $staff_id = null, ?string $timezone = null ): array { $check = $this->guard_admin(); if ( $check ) { return $check; } if ( $booking_id <= 0 ) { return [ 'error' => 'A valid booking_id is required.' ]; } if ( '' === trim( $start_date ) ) { return [ 'error' => 'start_date is required (YYYY-MM-DD).' ]; } $state = $this->load_booking_state( $booking_id ); if ( null === $state ) { return [ 'error' => "Booking #{$booking_id} was not found." ]; } if ( 'cancel' === $state['status'] ) { return [ 'error' => "Booking #{$booking_id} is cancelled and cannot be rescheduled." ]; } $meeting_id = (int) $state['appointment']; if ( ! class_exists( '\Timetics\Core\Appointments\Appointment' ) ) { return [ 'error' => 'Timetics is not available.' ]; } $meeting = new \Timetics\Core\Appointments\Appointment( $meeting_id ); if ( ! $meeting->is_appointment() ) { return [ 'error' => "The meeting for booking #{$booking_id} no longer exists." ]; } // Resolve target staff. $target_staff = ( null !== $staff_id && $staff_id > 0 ) ? $staff_id : (int) $state['staff']; if ( null !== $staff_id && $staff_id > 0 ) { $staff_ids = array_map( 'intval', $meeting->get_staff_ids() ); if ( ! in_array( $staff_id, $staff_ids, true ) ) { return [ 'error' => "Staff #{$staff_id} is not assigned to this meeting. Assigned staff: " . implode( ', ', $staff_ids ) . '.' ]; } } $timezone = $this->resolve_timezone( ( null !== $timezone && '' !== trim( $timezone ) ) ? $timezone : $state['timezone'] ); $normalized = $this->normalize_slot_time( $start_time ); $start_date = sanitize_text_field( $start_date ); if ( '' === $normalized ) { return [ 'error' => "Could not parse start_time '{$start_time}'. Use a time like \"9:00am\"." ]; } $slot_error = $this->validate_booking_slot( $meeting_id, $target_staff, $start_date, $normalized, $timezone ); if ( null !== $slot_error ) { return $slot_error; } try { // Overlay the new schedule onto the full current state. $body = $state; $body['staff'] = $target_staff; $body['timezone'] = $timezone; $body['start_date'] = $start_date; $body['end_date'] = $start_date; $body['start_time'] = $normalized; $body['end_time'] = $this->compute_end_time( $normalized, $meeting_id ); $request = $this->build_request( 'PUT', '/timetics/v1/bookings/' . $booking_id, [], $body, [ 'booking_id' => $booking_id ] ); if ( is_array( $request ) ) { return $request; } $controller = \Timetics\Core\Bookings\Api_Booking::instance(); $data = $this->extract_response( $controller->update_item( $request ), "Failed to reschedule booking #{$booking_id}." ); if ( isset( $data['error'] ) ) { return $data; } return [ 'success' => true, 'booking_id' => $booking_id, 'booking' => $this->shape_booking( $data ), 'message' => "Booking #{$booking_id} rescheduled successfully.", ]; } catch ( \Throwable $e ) { Log::error( 'Timetics_Reschedule_Booking_Tool error', [ 'error' => $e->getMessage() ] ); return [ 'error' => 'Failed to reschedule booking: ' . $e->getMessage() ]; } } }