How to create recurring bookings with Woocommerce
The plugin is available here for $39. View the demos too.
Lot’s of people love what Woocommerce Bookings can do, and many buy into the pretty expensive plugin before they realise some of limitations. I was one of them.
I had to learn pretty quickly that 2 of the most important features that my client wanted weren’t available out of the box with Bookings. The first was allowing customers to select the date first before seeing what they are able to book. You can see my write up to that selecting the date first, here. The other big one is…
Woocommerce Bookings allows you to manually create a booking and this is where you start to create a recurring one. From their website here is the basic code to create a booking programmically.
The create_wc_booking function The function takes the following arguments: create_wc_booking( $product_id, $new_booking_data = array(), $status = 'confirmed', $exact = false ) Product ID: The id of the bookable product which you are creating a new booking for. New Booking data: Array of booking data. See “booking data array” below. Status: Status of the new booking. Valid statuses include: ‘unpaid’, ‘pending’, ‘confirmed’, ‘cancelled’, ‘complete’ Exact: true or false – If false, the function will look for the next available slot after your start date, if the date you tried to book is unavailable. Booking Data Array The $new_booking_data argument is passed to your new booking. By default, it consists of the following: $defaults = array( 'product_id' => $product_id, // Booking ID 'start_date' => '', 'end_date' => '', 'resource_id' => '', );
Now for many, this may just be enough to get you on your way, but some of us need a little more. Below I will map out what I have done for my client.
I have modified the plugin to create 5, 6, or 7 week recurring bookings, this depends on the time of year. These make up a course, I’m assuming same as what you are trying to do. There is quite a lot of variability in the dates and resources for my version so I haven’t automated a huge amount yet (I am planning on it for a plugin).
Here are the basics.
1. Create a product that describes the course/classes (as I’ve done here). No dates or anything are needed in the product itself, you will do this manually. I suppose there is no reason you can’t supply a calendar here and use the selected date as the start of your bookings. It wasn’t necessary for my version.
2. Create a bespoke plugin to work along side Woo Bookings. – You can do this in function.php but it can become really messy really soon.
3. Create a function to do the work that hooks into the action ‘woocommerce_payment_complete’
4. The function will loop through the items in your completed order, if the item matches the course (product) id, then you will manually create your recurring bookings.
5. Creating the manually bookings needs the following parameters, the product_id you are booking against, the start date, the number of weeks, and the resource_id (if used).
So, here is my seemingly complicated function, but if you read through it, it’s not too difficult to figure out.
// create new booking term 5th Nov to 11th Feb 2 Terms 1 level function pedal_woocommerce_payment_complete_winter( $order_id ) { global $post; //check for term product id $term_order = new WC_Order( $order_id ); $term_items = $term_order->get_items(); // get the order_item_id for bookings // get order_id for items not race day (or any other product type in future) while (list($var, $term_item) = each($term_items)) { // an array of included products $included = array(2656); if(in_array($term_item['product_id'], $included)) { $order_item_id= $var; $sh_product_id = $term_item['product_id']; if($sh_product_id == 2656) { $product_variation_id = $term_item['variation_id']; //term 8 - 5 weeks if($product_variation_id ==2659) { $time_start = '1478338200'; $time_end ='1478345400'; $resource = 2662; // this the booking product to schedule against $b_product = 2661; } // term 9 - 6 wks elseif ($product_variation_id == 2660) { $time_start = '1483781400'; $time_end ='1483788600'; // this the booking product to schedule against $resource = 2667; $b_product = 2661; } else { return; } //create arguments array for first booking date create_wc_booking($b_product, $new_booking_data = array( //'product_id' => $product_id, // Booking ID 'start_date' => $time_start, 'end_date' => $time_end, 'order_item_id' => $order_item_id, 'resource_id' => $resource, ), $status = 'confirmed', $exact = true ); //now create follow up booking 1 week on //get latest booking post id global $wpdb; $get_book = $wpdb->get_var( "SELECT ID FROM $wpdb->posts WHERE post_type='wc_booking' ORDER BY ID DESC LIMIT 1" ); //now create booking with parent id 2 times if ($product_variation_id == 2660) { $week2 = array( 'start_date' => strtotime('+7 day', $time_start), 'end_date' => strtotime('+7 day', $time_end), 'parent_id' => $get_book, 'resource_id' => $resource+1, ); create_wc_booking($b_product, $week2, $status = 'confirmed', $exact = true ); } $week3 = array( 'start_date' => strtotime('+14 day', $time_start), 'end_date' => strtotime('+14 day', $time_end), 'parent_id' => $get_book, 'resource_id' => $resource+2, ); create_wc_booking($b_product, $week3, $status = 'confirmed', $exact = true ); $week4 = array( 'start_date' => strtotime('+21 day', $time_start), 'end_date' => strtotime('+21 day', $time_end), 'parent_id' => $get_book, 'resource_id' => $resource+3, ); create_wc_booking($b_product, $week4, $status = 'confirmed', $exact = true ); $week5 = array( 'start_date' => strtotime('+28 day', $time_start), 'end_date' => strtotime('+28 day', $time_end), 'parent_id' => $get_book, 'resource_id' => $resource+4, ); create_wc_booking( $b_product, $week5, $status = 'confirmed', $exact = true ); if ($product_variation_id == 2660) { $week6 = array( 'start_date' => strtotime('+35 day', $time_start), 'end_date' => strtotime('+35 day', $time_end), 'parent_id' => $get_book, 'resource_id' => $resource+5, ); create_wc_booking($b_product, $week6, $status = 'confirmed', $exact = true ); } } else { return; } } // now check for membership purchase and create discount code // an array of membership $mem = array(1091); if(in_array($term_item['product_id'], $mem)) { $create_code = substr($term_order->billing_last_name, 0, 3).$order_id; // add the code programically to woo $coupon_code = $create_code; // Code $to = array('hello@doitsimply.co.uk', $term_order->billing_email); $subject = 'Your Membership Discount Code'; $message = ' <html> <head> <title>Little Fodder\'s Membership</title> </head> <body> <h3>Hi '.$term_order->billing_first_name.' '.$term_order->billing_last_name.', thanks for the order of a Little Fodder\'s Membership</h3> <p>Your discount code for the year is '.$create_code.' Please keep it safe.</p> <p>This code can be used in your shopping basket with every order and entitles you to 20% off.</p> <p>Thank you,</p> <p>The Pedal a Bike Away Team</p> </body> </html> '; woocommerce_mail( $to, $subject, $message, $headers = "Content-Type: text/htmlrn", $attachments = "" ); $amount = '20'; // Amount $discount_type = 'percent'; // Type: fixed_cart, percent, fixed_product, percent_product $coupon = array( 'post_title' => $coupon_code, 'post_content' => '', 'post_status' => 'publish', 'post_author' => 1, 'post_type' => 'shop_coupon' ); $new_coupon_id = wp_insert_post( $coupon ); // Add meta update_post_meta( $new_coupon_id, 'discount_type', $discount_type ); update_post_meta( $new_coupon_id, 'coupon_amount', $amount ); update_post_meta( $new_coupon_id, 'individual_use', 'no' ); update_post_meta( $new_coupon_id, 'product_ids', '' ); update_post_meta( $new_coupon_id, 'exclude_product_ids', '' ); update_post_meta( $new_coupon_id, 'usage_limit', '' ); update_post_meta( $new_coupon_id, 'expiry_date', '' ); update_post_meta( $new_coupon_id, 'apply_before_tax', 'yes' ); update_post_meta( $new_coupon_id, 'free_shipping', 'no' ); } } } add_action( 'woocommerce_payment_complete','pedal_woocommerce_payment_complete_winter' );
Some things to note on my function.
a. I am manually entering the product and resource ids because these are constantly changing for my client. You could create (and I will eventually) an admin bit to allow you to set these in the CMS.
b. Normally the resource id wouldn’t have to change per week you could use the same, again this is bespoke for my client.
c. My client has variations too, sometimes this is course level, sometimes it’s course length, so I have to check for this in my function too.
d. I have also added a check in this function to see if they have purchased a membership, if they have I add a coupon to their basket which gives them 20% off of their booking.
As I’ve previously mentioned, more of this can be automated or looped… I’m working on it.. 🙂
I’d like to hear what you’ve tried to do or got stuck on. My ultimate goal is to write this into a plugin to help others. To do this I would like more use cases. So please send me a note.
You can visit my Github repository for the first version of the recurring bookings plugin. Please feedback how you get on.
Good luck!
Hi Guillermo. I have a version of it, but it’s not ready for general release. I’m hoping to work on it in April.
Thanks Guillermo, I will send you an email. Beth
Hi Beth,
Great article and the snippets! Sorry this is not directly related to the topic discussed in here but is there a way for us to create new ‘booking statuses’ for WC Bookings (not order statuses)?
Cheers!
I’m sorry Pradeep. I’m not sure what you are asking here. What type of statuses would you want for bookings? There are already pending, confirmed, etc. Depending on your set-up. I’m sure there is a hook to add your own specific ones.
Hello! I was just brainstorming yesterday on how to use product add in’s to allow customers to automatically let bookings recur weekly!
I’ll let you know how it goes!
Great. best of luck