get_items() as $item ) { $room_id = $item->get_meta( '_bnb_room_id', true ); if ( $room_id ) { $has_bookings = true; break; } } if ( ! $has_bookings ) { return; } // Create booking. self::create_booking_from_order( $order ); } /** * Create booking(s) from WooCommerce order. * * @param \WC_Order $order WooCommerce order. * @return int|null Booking ID or null on failure. */ public static function create_booking_from_order( \WC_Order $order ): ?int { /** * Fires before creating a booking from an order. * * @param \WC_Order $order WooCommerce order. */ do_action( 'wp_bnb_wc_before_booking_from_order', $order ); // Find or create guest. $guest_id = self::find_or_create_guest( $order ); // Get booking data from order items. $booking_id = null; foreach ( $order->get_items() as $item ) { $room_id = $item->get_meta( '_bnb_room_id', true ); if ( ! $room_id ) { continue; } $check_in = $item->get_meta( '_bnb_check_in', true ); $check_out = $item->get_meta( '_bnb_check_out', true ); $guests = $item->get_meta( '_bnb_guests', true ); $nights = $item->get_meta( '_bnb_nights', true ); $services = $item->get_meta( '_bnb_services', true ); $breakdown = $item->get_meta( '_bnb_price_breakdown', true ); // Decode JSON if necessary. if ( is_string( $services ) ) { $services = json_decode( $services, true ) ?: array(); } if ( is_string( $breakdown ) ) { $breakdown = json_decode( $breakdown, true ) ?: array(); } // Determine initial status. $status = Manager::is_auto_confirm_enabled() ? 'confirmed' : 'pending'; // Get guest notes. $guest_notes = $order->get_meta( '_bnb_guest_notes', true ); // Create booking post. $booking_data = array( 'post_type' => Booking::POST_TYPE, 'post_status' => 'publish', 'post_title' => self::generate_booking_title( $guest_id, $check_in, $check_out ), ); /** * Filter the booking data before creation. * * @param array $booking_data Booking post data. * @param \WC_Order $order WooCommerce order. */ $booking_data = apply_filters( 'wp_bnb_wc_booking_from_order_data', $booking_data, $order ); $booking_id = wp_insert_post( $booking_data ); if ( ! $booking_id || is_wp_error( $booking_id ) ) { continue; } // Store booking meta. update_post_meta( $booking_id, '_bnb_booking_room_id', $room_id ); update_post_meta( $booking_id, '_bnb_booking_check_in', $check_in ); update_post_meta( $booking_id, '_bnb_booking_check_out', $check_out ); update_post_meta( $booking_id, '_bnb_booking_status', $status ); update_post_meta( $booking_id, '_bnb_booking_adults', max( 1, (int) $guests ) ); update_post_meta( $booking_id, '_bnb_booking_children', 0 ); update_post_meta( $booking_id, '_bnb_booking_guest_notes', $guest_notes ); update_post_meta( $booking_id, '_bnb_booking_source', 'woocommerce_order_' . $order->get_id() ); // Store guest info. if ( $guest_id ) { update_post_meta( $booking_id, '_bnb_booking_guest_id', $guest_id ); update_post_meta( $booking_id, '_bnb_booking_guest_name', Guest::get_full_name( $guest_id ) ); update_post_meta( $booking_id, '_bnb_booking_guest_email', get_post_meta( $guest_id, '_bnb_guest_email', true ) ); update_post_meta( $booking_id, '_bnb_booking_guest_phone', get_post_meta( $guest_id, '_bnb_guest_phone', true ) ); } else { // Use order billing info. $guest_name = $order->get_billing_first_name() . ' ' . $order->get_billing_last_name(); update_post_meta( $booking_id, '_bnb_booking_guest_name', $guest_name ); update_post_meta( $booking_id, '_bnb_booking_guest_email', $order->get_billing_email() ); update_post_meta( $booking_id, '_bnb_booking_guest_phone', $order->get_billing_phone() ); } // Store pricing. $total = $item->get_total(); update_post_meta( $booking_id, '_bnb_booking_calculated_price', $total ); if ( ! empty( $breakdown ) ) { update_post_meta( $booking_id, '_bnb_booking_price_breakdown', $breakdown ); } // Store services. if ( ! empty( $services ) ) { update_post_meta( $booking_id, Booking::SERVICES_META_KEY, $services ); } // Generate booking reference. $reference = self::generate_booking_reference( $booking_id ); update_post_meta( $booking_id, '_bnb_booking_reference', $reference ); // Store confirmed timestamp if auto-confirmed. if ( 'confirmed' === $status ) { update_post_meta( $booking_id, '_bnb_booking_confirmed_at', current_time( 'mysql' ) ); } // Link booking to order. Manager::link_booking_to_order( $booking_id, $order ); // Also store room ID in order meta for quick access. $order->update_meta_data( Manager::ORDER_ROOM_META, $room_id ); // Store check-in/out in order meta. $order->update_meta_data( '_bnb_check_in', $check_in ); $order->update_meta_data( '_bnb_check_out', $check_out ); $order->save(); // Trigger status change action for email notifications. if ( 'confirmed' === $status ) { /** * Fires when booking status changes (for email notifications). * * @param int $booking_id Booking post ID. * @param string $old_status Old status. * @param string $new_status New status. */ do_action( 'wp_bnb_booking_status_changed', $booking_id, 'pending', 'confirmed' ); } } /** * Fires after creating a booking from an order. * * @param int|null $booking_id Last booking ID created. * @param \WC_Order $order WooCommerce order. */ do_action( 'wp_bnb_wc_after_booking_from_order', $booking_id, $order ); return $booking_id; } /** * Sync order status changes to booking status. * * @param int $order_id Order ID. * @param string $old_status Old status. * @param string $new_status New status. * @param \WC_Order $order Order object. * @return void */ public static function sync_order_status_to_booking( int $order_id, string $old_status, string $new_status, \WC_Order $order ): void { // Prevent recursive updates. if ( self::$updating_status ) { return; } $booking_id = Manager::get_booking_for_order( $order ); if ( ! $booking_id ) { return; } // Map WC status to booking status. $booking_status = Manager::map_wc_status_to_booking( $new_status ); // Get current booking status. $current_booking_status = get_post_meta( $booking_id, '_bnb_booking_status', true ); // Don't update if status is the same. if ( $current_booking_status === $booking_status ) { return; } // Don't downgrade from checked_in/checked_out. if ( in_array( $current_booking_status, array( 'checked_in', 'checked_out' ), true ) ) { return; } self::$updating_status = true; // Update booking status. update_post_meta( $booking_id, '_bnb_booking_status', $booking_status ); // Update confirmed timestamp if confirming. if ( 'confirmed' === $booking_status && 'pending' === $current_booking_status ) { update_post_meta( $booking_id, '_bnb_booking_confirmed_at', current_time( 'mysql' ) ); } // Trigger booking status changed action. do_action( 'wp_bnb_booking_status_changed', $booking_id, $current_booking_status, $booking_status ); self::$updating_status = false; } /** * Find or create guest from order data. * * @param \WC_Order $order WooCommerce order. * @return int|null Guest ID or null. */ private static function find_or_create_guest( \WC_Order $order ): ?int { $email = $order->get_billing_email(); if ( ! $email ) { return null; } // Try to find existing guest by email. $existing_guests = get_posts( array( 'post_type' => Guest::POST_TYPE, 'posts_per_page' => 1, 'post_status' => 'publish', 'meta_query' => array( array( 'key' => '_bnb_guest_email', 'value' => $email, ), ), ) ); if ( ! empty( $existing_guests ) ) { return $existing_guests[0]->ID; } // Create new guest. $first_name = $order->get_billing_first_name(); $last_name = $order->get_billing_last_name(); $full_name = trim( $first_name . ' ' . $last_name ); $guest_data = array( 'post_type' => Guest::POST_TYPE, 'post_status' => 'publish', 'post_title' => $full_name ?: $email, ); $guest_id = wp_insert_post( $guest_data ); if ( ! $guest_id || is_wp_error( $guest_id ) ) { return null; } // Store guest meta. update_post_meta( $guest_id, '_bnb_guest_first_name', $first_name ); update_post_meta( $guest_id, '_bnb_guest_last_name', $last_name ); update_post_meta( $guest_id, '_bnb_guest_email', $email ); update_post_meta( $guest_id, '_bnb_guest_phone', $order->get_billing_phone() ); update_post_meta( $guest_id, '_bnb_guest_address', $order->get_billing_address_1() ); update_post_meta( $guest_id, '_bnb_guest_city', $order->get_billing_city() ); update_post_meta( $guest_id, '_bnb_guest_postal_code', $order->get_billing_postcode() ); update_post_meta( $guest_id, '_bnb_guest_country', $order->get_billing_country() ); update_post_meta( $guest_id, '_bnb_guest_status', 'active' ); update_post_meta( $guest_id, '_bnb_guest_source', 'woocommerce' ); return $guest_id; } /** * Generate booking title. * * @param int|null $guest_id Guest ID. * @param string $check_in Check-in date. * @param string $check_out Check-out date. * @return string Booking title. */ private static function generate_booking_title( ?int $guest_id, string $check_in, string $check_out ): string { $guest_name = $guest_id ? Guest::get_full_name( $guest_id ) : __( 'Guest', 'wp-bnb' ); $check_in_date = \DateTime::createFromFormat( 'Y-m-d', $check_in ); $check_out_date = \DateTime::createFromFormat( 'Y-m-d', $check_out ); if ( ! $check_in_date || ! $check_out_date ) { return $guest_name; } // Format: "Guest Name (DD.MM - DD.MM.YYYY)" or span years. if ( $check_in_date->format( 'Y' ) === $check_out_date->format( 'Y' ) ) { return sprintf( '%s (%s - %s)', $guest_name, $check_in_date->format( 'd.m' ), $check_out_date->format( 'd.m.Y' ) ); } return sprintf( '%s (%s - %s)', $guest_name, $check_in_date->format( 'd.m.Y' ), $check_out_date->format( 'd.m.Y' ) ); } /** * Generate booking reference. * * @param int $booking_id Booking ID. * @return string Booking reference. */ private static function generate_booking_reference( int $booking_id ): string { return sprintf( 'BNB-%s-%05d', gmdate( 'Y' ), $booking_id ); } /** * Display booking info in admin order page. * * @param \WC_Order $order WooCommerce order. * @return void */ public static function display_booking_info_admin( \WC_Order $order ): void { $booking_id = Manager::get_booking_for_order( $order ); if ( ! $booking_id ) { return; } $booking = get_post( $booking_id ); $status = get_post_meta( $booking_id, '_bnb_booking_status', true ); $room_id = get_post_meta( $booking_id, '_bnb_booking_room_id', true ); $room = $room_id ? get_post( $room_id ) : null; ?>

post_title : "#{$booking_id}" ); ?>

post_title ); ?>

post_title ); ?>
post_title ); ?>
format( $date_format ) : $check_in ); ?>
format( $date_format ) : $check_out ); ?>