phpwoocommerceproductsetterwoocommerce-bookings

Add a buffer period to WooCommerce Bookable products only if there are existing bookings


I'm using WooCommerce Bookings and if I add a buffer to a product using the admin UI it adds the buffer even if there are no bookings for that product on that day. That restricts the hours someone can choose, which isn't good since I've set products up so that customers can choose a six hour block starting at the top of an hour. Using the admin UI, it creates a buffer so that instead of being able to book a product at 7am, 8am, 9am, etc you can only book products (if the buffer is set for 2 hours, say) at 7am, 9am, 11am, etc.

What I'm trying to do is to add a buffer to a product when (and only when) there's a booking for it.

Say a product has a booking for 8am to 12pm on June 2nd. I want someone to be able to reserve it, but I want it to have a 2 hour buffer after the existing booking ends. So, if someone tried to reserve for June 2nd, they'd see availability starting at 2pm. However, I don't want the buffer to affect available start times if there are no bookings for that product on that day.

The code below works fine to get the product ID of the product someone is looking at and I get the bookings fine ($bookings returns valid info), but the filter is where it fails silently. It's not altering the buffer (or at least if it is, that is not reflected in the Start time dropdown in the UI), so the first available start time in my example is 12pm, i.e without any buffer at all. "wc_bookings_get_time_slots" doesn't seem to be the thing I need, but I can't seem to find what is...

add_action('woocommerce_before_single_product', 'custom_set_buffer_period_for_viewed_product');
function custom_set_buffer_period_for_viewed_product() {
    // Ensure WooCommerce functions are available
    if (function_exists('is_product') && is_product()) {
        global $product;
     
        $product_id = $product->get_id();
        error_log("Product ID $product_id");
    
        $buffer_before = 0;
        $buffer_after = 120;
    
        if ($product_id) {
            // Check if there are existing bookings for the product
            $bookings = WC_Bookings_Controller::get_bookings_for_objects(array($product_id));
            error_log(print_r($bookings,true));
            add_filter('wc_bookings_get_time_slots', function($booking, $booking_id) use ($buffer_before, $buffer_after) {
                 // Apply the buffer period if there are existing bookings
                 $booking->set_buffer_period($buffer_before, $buffer_after);
                 return $booking;
            }, 10, 2);
        }
    }
}

Solution

  • There are multiple mistakes in your code:

    Try the following (untested):

    add_action('woocommerce_before_single_product', 'custom_set_buffer_period_for_viewed_product');
    function custom_set_buffer_period_for_viewed_product() {
        global $product;
    
        // Targetting bookable products only
        if ( is_a($product, 'WC_Product_Booking') ) {
            $product_ids = array( $product->get_id() );
            $today_time  = strtotime( date('y-m-d') );
    
            // Check if there are existing bookings for the product today
            $bookings = WC_Bookings_Controller::get_bookings_for_objects( $product_ids, array(), $today_time );
    
            if ( count($bookings) > 0 ) {
                ## Increase the buffer after period if there are bookings
                $product->set_buffer_period(120);
                $product->save(); // Always save changes
            } else {
                // Revert back the buffer after period if there are no bookings
                $product->set_buffer_period(0);
                $product->save();
            }
        }
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). It could work.