Year End Sale -2o% Off On All Products - Coupon: FMADEC2017

Common Queries About WooCommerce Bookings Plugin With Answers - Part 1

WooCommerce provides a huge array of free and premium plugins for their customers, however; sometimes it becomes quite hard for both new and experienced programmers to successfully integrate/install plugins on their website. Here we would like to pick some top trending questions asked by customers related to WooCommerce bookings plugin. These answers are picked from top programming communities.

Here in this article top 5 questions related to WooCommerce bookings and reservations plugin are being picked by FMEAddons. These questions contain both custom programming questions as well as related to plugin installation and configuration in WooCommerce.

Important Note:

These are the answers provided by the community and best rated by the community as well. 

Question 1

The user has installed booking plugin on WooCommerce and created another custom plugin in which user want to add booking to the cart page at the end of checkout process. The user wants to add the bookable product to "Add to Cart" and then check out with the payment gateway used?  He/she is asking for the possibility of this process

Answer:

Yes, it is possible; you can create simple product type with its own custom fields. Just copy and paste the code given below into your functions.php file. Hopefully, this will help you

// add a product type
add_filter( 'product_type_selector', 'wdm_add_custom_product_type' );
function wdm_add_custom_product_type( $types ){
    $types[ 'booking_product' ] = __( 'Booking Product' );
    return $types;
}
add_action( 'plugins_loaded', 'wdm_create_custom_product_type' );
function wdm_create_custom_product_type(){
     // declare the product class
     class WC_Product_Wdm extends WC_Product{
        public function __construct( $product ) {
           $this->product_type = 'booking_product';
           parent::__construct( $product );
           // add additional functions here
        }
    }
}

Question 2

Woocommerce booking: Fatal error: Call to a member function has_persons() on a non-object in

I have an issue with WooCommerce bookings, it throws a fatal error which is given below

**Fatal error: Call to a member function `has_persons()` on a non-object in `xxx/plugins/woocommerce-bookings/includes/class-wc-booking.php` on line 557**

The WooCommerce booking class:

/** @public int */
public $id;

/** @public string */
public $booking_date;

/** @public string */
public $start;

/** @public string */
public $end;

/** @public bool */
public $all_day;

/** @public string */
public $modified_date;

/** @public object */
public $post;

/** @public int */
public $product_id;

/** @public object */
public $product;

/** @public int */
public $order_id;

/** @public object */
public $order;

/** @public int */
public $customer_id;

/** @public string */
public $status;

/** @public array - contains all post meta values for this booking */
public $custom_fields;

/** @public bool */
public $populated;

/** @private array - used to temporarily hold order data for new bookings */
private $order_data;

/**
 * Constructor, possibly sets up with post or id belonging to existing booking
 * or supplied with an array to construct a new booking
 * @param int/array/obj $booking_data
 */
public function __construct( $booking_data = false ) {
    $populated = false;

    if ( is_array( $booking_data ) ) {
        $this->order_data = $booking_data;
        $populated = false;
    } else if ( is_int( intval( $booking_data ) ) && 0 < $booking_data ) {
        $populated = $this->populate_data( $booking_data );
    } else if ( is_object( $booking_data ) && isset( $booking_data->ID ) ) {
        $this->post = $booking_data;
        $populated = $this->populate_data( $booking_data->ID );
    }

    $this->populated = $populated;
}

/**
 * Actual create for the new booking belonging to an order
 * @param string Status for new order
 */
public function create( $status = 'unpaid' ) {
    $this->new_booking( $status, $this->order_data );
    $this->schedule_events();
}

/**
 * Schedule events for this booking
 */
public function schedule_events() {
    switch ( get_post_status( $this->id ) ) {
        case "paid" :
            if ( $this->start && $this->get_order() ) {
                $order_status = $this->get_order()->get_status();

                if ( ! in_array( $order_status, array( 'cancelled', 'refunded', 'pending', 'on-hold' ) ) ) {
                    wp_schedule_single_event( strtotime( '-' . absint( apply_filters( 'woocommerce_bookings_remind_before_days', 1 ) ) . ' day', $this->start ), 'wc-booking-reminder', array( $this->id ) );
                }
            }
            if ( $this->end ) {
                wp_schedule_single_event( $this->end, 'wc-booking-complete', array( $this->id ) );
            }
        break;
        default :
            wp_clear_scheduled_hook( 'wc-booking-reminder', array( $this->id ) );
            wp_clear_scheduled_hook( 'wc-booking-complete', array( $this->id ) );
        break;
    }
}

/**
 * Makes the new booking belonging to an order
 * @param string $status The status for this new booking
 * @param array $order_data Array with all the new order data
 */
private function new_booking( $status, $order_data ) {
    global $wpdb;

    $order_data = wp_parse_args( $order_data, array(
        'user_id'           => 0,
        'resource_id'       => '',
        'product_id'        => '',
        'order_item_id'     => '',
        'persons'           => array(),
        'cost'              => '',
        'start_date'        => '',
        'end_date'          => '',
        'all_day'           => 0,
        'parent_id'         => 0,
    ) );

    // Get parent data
    if ( $order_data['parent_id'] ) {
        if ( ! $order_data['order_item_id'] ) {
            $order_data['order_item_id'] = get_post_meta( $order_data['parent_id'], '_booking_order_item_id', true );
        }

        if ( ! $order_data['user_id'] ) {
            $order_data['user_id'] = get_post_meta( $order_data['parent_id'], '_booking_customer_id', true );
        }
    }

    // Get order ID from order item
    if ( $order_data['order_item_id'] ) {
        $order_id = $wpdb->get_var( $wpdb->prepare( "SELECT order_id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_item_id = %d", $order_data['order_item_id'] ) );
    } else {
        $order_id = 0;
    }

    $booking_data = array(
        'post_type'   => 'wc_booking',
        'post_title'  => sprintf( __( 'Booking &ndash; %s', 'woocommerce-bookings' ), strftime( _x( '%b %d, %Y @ %I:%M %p', 'Booking date parsed by strftime', 'woocommerce-bookings' ) ) ),
        'post_status' => $status,
        'ping_status' => 'closed',
        'post_parent' => $order_id
    );

    $this->id = wp_insert_post( $booking_data );

    // Setup the required data for the current user
    if ( ! $order_data['user_id'] ) {
        if ( is_user_logged_in() ) {
            $order_data['user_id'] = get_current_user_id();
        } else {
            $order_data['user_id'] = 0;
        }
    }

    // Convert booking start and end to requried format
    if ( is_numeric( $order_data['start_date'] ) ) {
        // Convert timestamp
        $order_data['start_date'] = date( 'YmdHis', $order_data['start_date'] );
        $order_data['end_date']   = date( 'YmdHis', $order_data['end_date'] );
    } else {
        $order_data['start_date'] = date( 'YmdHis', strtotime( $order_data['start_date'] ) );
        $order_data['end_date']   = date( 'YmdHis', strtotime( $order_data['end_date'] ) );
    }

    $meta_args = array(
        '_booking_order_item_id' => $order_data['order_item_id'],
        '_booking_product_id'    => $order_data['product_id'],
        '_booking_resource_id'   => $order_data['resource_id'],
        '_booking_persons'       => $order_data['persons'],
        '_booking_cost'          => $order_data['cost'],
        '_booking_start'         => $order_data['start_date'],
        '_booking_end'           => $order_data['end_date'],
        '_booking_all_day'       => intval( $order_data['all_day'] ),
        '_booking_parent_id'     => $order_data['parent_id'],
        '_booking_customer_id'   => $order_data['user_id'],
    );

    foreach ( $meta_args as $key => $value ) {
        update_post_meta( $this->id, $key, $value );
    }

    WC_Cache_Helper::get_transient_version( 'bookings', true );

    do_action( 'woocommerce_new_booking', $this->id );
}

/**
 * Assign this booking to an order and order item by ID
 * @param int $order_id
 * @param int $order_item_id
 */
public function set_order_id( $order_id, $order_item_id ) {
    $this->order_id = $order_id;
    wp_update_post( array( 'ID' => $this->id, 'post_parent' => $this->order_id ) );
    update_post_meta( $this->id, '_booking_order_item_id', $order_item_id );
}

/**
 * Populate the data with the id of the booking provided
 * Will query for the post belonging to this booking and store it
 * @param int $booking_id
 */
public function populate_data( $booking_id ) {
    if ( ! isset( $this->post ) ) {
        $post = get_post( $booking_id );
    }

    if ( is_object( $post ) ) {
        // We have the post object belonging to this booking, now let's populate
        $this->id            = $post->ID;
        $this->booking_date  = $post->post_date;
        $this->modified_date = $post->post_modified;
        $this->customer_id   = $post->post_author;
        $this->custom_fields = get_post_meta( $this->id );
        $this->status        = $post->post_status;
        $this->order_id      = $post->post_parent;

        // Define the data we're going to load: Key => Default value
        $load_data = array(
            'product_id'  => '',
            'resource_id' => '',
            'persons'     => array(),
            'cost'        => '',
            'start'       => '',
            'customer_id' => '',
            'end'         => '',
            'all_day'     => 0,
            'parent_id'   => 0,
        );

        // Load the data from the custom fields (with prefix for this plugin)
        $meta_prefix = '_booking_';

        foreach ( $load_data as $key => $default ) {
            if ( isset( $this->custom_fields[ $meta_prefix . $key ][0] ) && $this->custom_fields[ $meta_prefix . $key ][0] !== '' ) {
                $this->$key = maybe_unserialize( $this->custom_fields[ $meta_prefix . $key ][0] );
            } else {
                $this->$key = $default;
            }
        }

        // Start and end date converted to timestamp
        $this->start = strtotime( $this->start );
        $this->end   = strtotime( $this->end );

        // Save the post object itself for future reference
        $this->post = $post;
        return true;
    }

    return false;
}

/**
 * Will change the booking status once the order is paid for
 * @return bool
 */
public function paid() {
    $current_status = $this->status;
    $event          = wp_get_schedule( 'wc-booking-reminder', array( $this->id ) );

    if ( $this->populated && in_array( $current_status, array( 'unpaid', 'confirmed' ) ) ) {
        $this->update_status( 'paid' );

        if ( ! empty( $event ) ) {
            $this->schedule_events();
        }

        return true;
    }

    return false;
}

/**
 * Set the new status for this booking
 * @param string $status
 * @return bool
 */
public function update_status( $status ) {
    $current_status   = $this->get_status( true );
    $allowed_statuses = array( 'unpaid', 'pending-confirmation', 'confirmed', 'paid', 'cancelled', 'complete', 'in-cart', 'was-in-cart' );

    if ( $this->populated ) {
        if ( in_array( $status, $allowed_statuses ) ) {
            wp_update_post( array( 'ID' => $this->id, 'post_status' => $status ) );

            // Reschedule cron
            $this->schedule_events();

            // Trigger actions
            do_action( 'woocommerce_booking_' . $current_status . '_to_' . $status, $this->id );
            do_action( 'woocommerce_booking_' . $status, $this->id );

            // Note in the order
            if ( $order = $this->get_order() ) {
                $order->add_order_note( sprintf( __( 'Booking #%d status changed from "%s" to "%s', 'woocommerce-bookings' ), $this->id, $current_status, $status ) );
            }

            return true;
        }
    }

    return false;
}

/**
 * Checks the booking status against a passed in status.
 *
 * @return bool
 */
public function has_status( $status ) {
    return apply_filters( 'woocommerce_booking_has_status', ( is_array( $status ) && in_array( $this->get_status(), $status ) ) || $this->get_status() === $status ? true : false, $this, $status );
}

/**
 * Returns the status of this booking
 * @param Bool to ask for pretty status name (if false)
 * @return String of the booking status
 */
public function get_status( $raw = true ) {
    if ( $this->populated ) {
        if ( $raw ) {
            return $this->status;
        } else {
            $status_object = get_post_status_object( $this->status );
            return $status_object->label;
        }
    }

    return false;
}

/**
 * Returns the id of this booking
 * @return Id of the booking or false if booking is not populated
 */
public function get_id() {
    if ( $this->populated ) {
        return $this->id;
    }

    return false;
}

/**
 * Get the product ID for the booking
 * @return int or false if booking is not populated
 */
public function get_product_id() {
    if ( $this->populated ) {
        return $this->product_id;
    }

    return false;
}

/**
 * Returns the object of the order corresponding to this booking
 * @return Product object or false if booking is not populated
 */
public function get_product() {
    if ( empty( $this->product ) ) {
        if ( $this->populated && $this->product_id ) {
            $this->product = get_product( $this->product_id );
        } else {
            return false;
        }
    }

    return $this->product;
}

/**
 * Returns the object of the order corresponding to this booking
 * @return Order object or false if booking is not populated
 */
public function get_order() {
    if ( empty( $this->order ) ) {
        if ( $this->populated && ! empty( $this->order_id ) && 'shop_order' === get_post_type( $this->order_id ) ) {
            $this->order = wc_get_order( $this->order_id );
        } else {
            return false;
        }
    }

    return $this->order;
}

/**
 * Returns the cancel URL for a booking
 *
 * @param string $redirect
 * @return string
 */
public function get_cancel_url( $redirect = '' ) {
    $cancel_page = get_permalink( wc_get_page_id( 'myaccount' ) );

    if ( ! $cancel_page ) {
        $cancel_page = home_url();
    }

    return apply_filters( 'bookings_cancel_booking_url', wp_nonce_url( add_query_arg( array( 'cancel_booking' => 'true', 'booking_id' => $this->id, 'redirect' => $redirect ), $cancel_page ), 'woocommerce-bookings-cancel_booking' ) );
}

/**
 * Return if all day event
 * @return boolean
 */
public function is_all_day() {
    if ( $this->populated ) {
        if ( $this->all_day ) {
            return true;
        } else {
            return false;
        }
    }
    return false;
}

/**
 * See if this booking is booked on said date
 * @return boolean
 */
public function is_booked_on_day( $block_start, $block_end ) {
    if ( $this->populated ) {
        $loop_date        = $this->start;
        $multiday_booking = date( 'Y-m-d', $this->start ) < date( 'Y-m-d', $this->end );

        if ( $multiday_booking ) {
            if ( date( 'YmdHi', $block_end ) > date( 'YmdHi', $this->start ) || date( 'YmdHi', $block_start ) < date( 'YmdHi', $this->end ) ) {
                return true;
            }
            return false;
        }

        while ( $loop_date <= $this->end ) {
            if ( date( 'Y-m-d', $loop_date ) === date( 'Y-m-d', $block_start ) ) {
                return true;
            }
            $loop_date = strtotime( "+1 day", $loop_date );
        }
    }
    return false;
}

/**
 * See if this booking can still be cancelled by the user or not
 * @return boolean
 */
public function passed_cancel_day() {
    $booking = $this->get_product();

    if ( $booking !== false ) {
        $cancel_limit      = $booking->wc_booking_cancel_limit;
        $cancel_limit_unit = $cancel_limit > 1 ? $booking->wc_booking_cancel_limit_unit . 's' : $booking->wc_booking_cancel_limit_unit;
        $cancel_string     = sprintf( 'now +%d %s', $cancel_limit, $cancel_limit_unit );

        if ( strtotime( $cancel_string ) >= $this->start ) {
            return true;
        }
    }

    return false;
}

/**
 * Returns booking start date
 * @return string Date formatted via date_i18n
 */
public function get_start_date( $date_format = null, $time_format = null ) {
    if ( $this->populated && ! empty( $this->start ) ) {
        if ( is_null( $date_format ) ) {
            $date_format = apply_filters( 'woocommerce_bookings_date_format', 'M jS Y' );
        }
        if ( is_null( $time_format ) ) {
            $time_format = apply_filters( 'woocommerce_bookings_time_format', ', g:ia' );
        }
        if ( $this->is_all_day() ) {
            return date_i18n( $date_format, $this->start );
        } else {
            return date_i18n( $date_format . $time_format, $this->start );
        }
    }

    return false;
}

/**
 * Returns booking end date
 * @return string Date formatted via date_i18n
 */
public function get_end_date( $date_format = null, $time_format = null ) {
    if ( $this->populated && ! empty( $this->end ) ) {
        if ( is_null( $date_format ) ) {
            $date_format = apply_filters( 'woocommerce_bookings_date_format', 'M jS Y' );
        }
        if ( is_null( $time_format ) ) {
            $time_format = apply_filters( 'woocommerce_bookings_time_format', ', g:ia' );
        }
        if ( $this->is_all_day() ) {
            return date_i18n( $date_format, $this->end );
        } else {
            return date_i18n( $date_format . $time_format, $this->end );
        }
    }

    return false;
}

/**
 * Returns information about the customer of this order
 * @return array containing customer information
 */
public function get_customer() {
    if ( $this->populated ) {
        $order = $this->get_order();

        if ( $order )
            return (object) array(
                'name'    => trim( $order->billing_first_name . ' ' . $order->billing_last_name ),
                'email'   => $order->billing_email,
                'user_id' => $order->customer_user,
            );
        elseif ( $this->customer_id ) {
            $user = get_user_by( 'id', $this->customer_id );

            return (object) array(
                'name'    => $user->display_name,
                'email'   => $user->user_email,
                'user_id' => $this->customer_id
            );
        }
    }

    return false;
}

/**
 * Returns if persons are enabled/needed for the booking product
 * @return boolean
 */
public function has_persons() {
    return $this->get_product()->has_persons();
}

/**
 * Returns if resources are enabled/needed for the booking product
 * @return boolean
 */
public function has_resources() {
    return $this->get_product()->has_resources();
}

/**
 * Return a array with the booking persons.
 * @return array
 */
public function get_persons() {
    return (array) $this->persons;
}

/**
 * Return the amount of persons for this booking.
 * @return int
 */
public function get_persons_total() {
    return array_sum( $this->get_persons() );
}

/**
 * Get the resource id
 * @return int
 */
public function get_resource_id() {
    if ( $this->populated ) {
        return absint( $this->resource_id );
    }
    return 0;
}

/**
 * Get the resource/type for this booking if applicable.
 * @return bool|object WP_Post
 */
public function get_resource() {
    $resource_id = $this->get_resource_id();

    if ( ! $resource_id || ! ( $product = $this->get_product() ) || ! method_exists( $product, 'get_resource' ) ) {
        return false;
    }

    return $product->get_resource( $resource_id );
}

Answer:

In file: class-wc-booking.php

Replace:

/**
 * Returns if persons are enabled/needed for the booking product

 * @return boolean

 */



public function has_persons() {
    return $this->get_product()->has_persons();
}

With:

/**
 * Returns if persons are enabled/needed for the booking product

 * @return boolean

 */



public function has_persons() {
    if ($this->get_product()) {

    return $this->get_product()->has_persons();
        } else
        {
        return 0;
    }

}

Question 3:

How to integrate the booking system with Woocommerce plugin?

Answer:

This is basically a simple question, but for new users, it is quite complicated even to find a third party extension on their website.  Here I would like to suggest you the following plugin

  • WooCommerce Bookings

 

Question 4

How to show the start and end date of a booking or reservation on the checkout page?

User has an online hotel booking website which is developed in WordPress using the following extensions

  • Woocommerce Version 2.4.12
  • WooCommerce-Bookings Version 1.9.3

Currently, only the end data is showing in cart and checkout pages

The cart.php code:

foreach ( WC()->cart->get_cart() as $cart_item_key => $cart_item ) {
    echo WC()->cart->get_item_data( $cart_item );
}

End date is showing in the array as an integer when the user do var_dump() of the array

Array code is here

["booking"]=>
  array(19) {
    ["_year"]=>
    int(2016)
    ["_month"]=>
    int(1)
    ["_day"]=>
    int(23)
    ["_persons"]=>
    array(1) {
      [234]=>
      int(1)
    }
    ["_date"]=>
    string(9) "2016-1-23"
    ["date"]=>
    string(16) "January 23, 2016"
    ["_time"]=>
    string(0) ""
    ["_qty"]=>
    int(1)
    ["Adults"]=>
    int(1)
    ["_duration_unit"]=>
    string(3) "day"
    ["_duration"]=>
    int(3)
    ["duration"]=>
    string(8) "3 nights"
    ["_start_date"]=>
    int(1453507200)
    **["_end_date"]=>
    int(1453766399)**
    ["_all_day"]=>
    int(1)
    ["_resource_id"]=>
    int(223)
    ["type"]=>
    string(15) "Suite Saadienne"
    ["_cost"]=>
    int(360)
    ["_booking_id"]=>
    int(870)
  }

The end date is present in the array. How to show it?

Answer:

You can see the filter woocommerce_get_item_data is where you can add keys/values to the definition list that displays product information

Date formatting from Bookings start and end date is:

add_filter( 'woocommerce_get_item_data', 'so_34900999_display_cart_data', 10, 2 );
function so_34900999_display_cart_data( $item_data, $cart_item ){

    if ( ! empty( $cart_item['booking'] ) ) {

        $date_format = apply_filters( 'woocommerce_bookings_date_format', wc_date_format() );
        $time_format = apply_filters( 'woocommerce_bookings_time_format', ', ' . wc_time_format() );
        $end_date = apply_filters( 'woocommerce_bookings_get_end_date_with_time', date_i18n( $date_format . $time_format, $cart_item['booking']['_end_date'] ) );

        $item_data[] = array(
            'key'    => __( 'End Date', 'your-textdomain' ),
            'value'   => $cart_item['booking']['_end_date'],
            'display' => $end_date,
        );
    }
    return $item_data;
}

Question 5:

How to Show all Available Bookable Product in “WooCommerce Bookings” extension?

When the user selects date and time and then apply search it should show all the available products matching the criteria of date and time. How to do this?

Answer:

Follow the following steps to perform this

 

To save bookable product:

woocommerce-bookings/includes/class-wc-bookings-admin.php

Method executes saving:

save_product_data( $post_id ) at line 434

File for the searching listing page:

woocommerce/templates/archive-product.php

Skip loading booking:

/wp-content/plugins/woocommerce-bookings/includes/booking-form/class-wc-booking-form.php

skip loading date time:

/wp-content/plugins/woocommerce-bookings/assets/js/booking-form.js

/templates/single-product/add-to-cart/booking.php

Leave a Reply