getBookingsForReminder($target_date); if (empty($bookings)) { return; } foreach ($bookings as $booking) { if (self::sendReminderEmail($booking)) { $bookingRepository->markReminderSent($booking->id); } } // Log the operation if (defined('WP_DEBUG') && WP_DEBUG) { } } /** * Ensure the daily booking-completion sweep is scheduled. * * Wired from CronHooks (the plugin's live cron bootstrap) rather than the * legacy register()/scheduleEvents() path above, which is not invoked. Only * the completion event is scheduled here — the reminder/expiry events are * intentionally left as-is to avoid changing their (separate) behavior. */ public static function registerCompletionCron(): void { if (!wp_next_scheduled('yatra_booking_completion')) { wp_schedule_event(time(), 'daily', 'yatra_booking_completion'); } } /** * Unschedule the booking-completion sweep (plugin deactivation). */ public static function unregisterCompletionCron(): void { $timestamp = wp_next_scheduled('yatra_booking_completion'); if ($timestamp) { wp_unschedule_event($timestamp, 'yatra_booking_completion'); } } /** * Mark confirmed bookings 'completed' once their tour has taken place. * * Nothing previously transitioned a booking to 'completed' automatically — * the status (and therefore the booking.completed email / Email Automation * sequence) only changed when an operator edited each booking by hand. So * the post-tour email was effectively never sent. This daily sweep does * what an operator would: for every confirmed booking whose tour date has * passed, it calls the same updateStatus() path the admin UI uses, which * fires the notification, the yatra_booking_status_changed action (Pro * sequences), and schedules the review reminder. * * Backward-compat: an activation floor (yatra_booking_autocomplete_since) is * stamped on the first run so we never retroactively complete — and email * the customers of — tours that ended before this automation shipped. Only * tours finishing from activation onward are auto-completed. Operators can * disable the sweep entirely via the yatra_auto_complete_bookings filter, * and the email itself still respects its own template on/off setting. */ public static function completeFinishedBookings(): void { /** * Allow disabling automatic booking completion entirely. * * @param bool $enabled Default true. */ if (!apply_filters('yatra_auto_complete_bookings', true)) { return; } $floorOption = 'yatra_booking_autocomplete_since'; $today = current_time('Y-m-d'); $floor = (string) get_option($floorOption, ''); if ($floor === '') { // First run on this site: establish the floor at today so historical // bookings are never retroactively completed/emailed. Tours finishing // from now on are picked up on subsequent runs. update_option($floorOption, $today); return; } $bookingRepository = self::getBookingRepository(); $ids = $bookingRepository->getConfirmedBookingIdsPastTour($today, $floor, 500); if (empty($ids)) { return; } $bookingService = new BookingService(); foreach ($ids as $id) { // Same entry point the admin "change status" action uses, so all // side effects (notification, status-changed hook, review reminder, // departure booked_count handling) stay identical to a manual mark. $bookingService->updateStatus((int) $id, 'completed'); } } /** * Send a reminder email to the customer */ private static function sendReminderEmail(object $booking): bool { $customer_email = $booking->contact_email; if (empty($customer_email)) { return false; } $reminder_days = (int) SettingsService::get('booking_reminder_days', 3); $vars = TransactionalEmailTemplateService::variablesFromBooking($booking); $vars['reminder_days'] = (string) $reminder_days; $vars['days_until_trip'] = (string) $reminder_days; $amount_due = (float) $booking->amount_due; $extra = ''; if ($amount_due > 0) { $extra = '
' . esc_html__('Payment reminder', 'yatra') . '
' . '' . esc_html(sprintf( /* translators: %s: formatted outstanding balance amount. */ __('Outstanding balance: %s — please pay before travel.', 'yatra'), yatra_format_price($amount_due) )) . '
'; } $extra .= '' . esc_html__('Preparation checklist', 'yatra') . '