Please consider leaving a donation if you appreciate this information. Lightning network now available
Since the introduction of Bookings version 1.10.0 meta data associated with a booking has been removed from WooCommerce orders. This may add a level of frustration, particularly when using some sort of exporter which would have effortlessly fetched the booking data if that data were part of the order line item data. This is an unfortunate side effect of streamlining for performance. The removal of the meta data from the order does help with performance in reducing database size which, in turn, speeds up queries.
The good news is that the booking data can still be retrieved via some custom code. It’s a bit cumbersome, but possible. Bookings does provide a method named get_booking_ids_from_order_id which can be used to get all of the booking ids from an order number. The booking id is needed in order to query the booking for whatever data you are trying to retrieve. Here’s a code sample (this sample assumes that you have access to the WooCommerce order object):
That will simply populate the $booking_ids variable with the return value of get_booking_ids_from_order_id
Once you have the booking id(s), you could then retrieve whatever data from those bookings that are needed. Bookings does provide some CRUD methods for this, i.e. the person count for a booking can be retrieved via the get_person_counts method. Using the $booking_ids array from above, you can get the persons count via something like this:
Putting it all Together
In the case of exporting, you may want to get a person count for each of the bookings in an order. If we use the official developer documentation for the WooCommerce Order Customer CSV export as a baseline and make some tweaks to it, we can add a column to our export and populate that column with the person counts for each booking using something like this:
There is obviously more that can be done with that, but hopefully this gives you a pointer in the right direction.
Not sure what to do with code snippets? See What Do I Do With These Code Snippets?
Please consider leaving a donation if you appreciate this information. Lightning network now available
Richie Tyler says
Hi Will, this has indeed caused some frustration for me today. I wonder if you might have some advice on the below.
Up until the upgrade I had a series of SMS notifications setup via WC > Zapier > Twilio. Zapier was pulling the Date and Time of the Booking, and then outputting it into the SMS.
Can you think of any way I might be able to store that Date and Time data in a booking so that Zapier can retrieve it?
Cheers,
Richie
will says
Howdy Richie,
I lack familiarity with how the Zapier extension works but I would suspect that it is pulling the order item meta which, in the case of a booking does not exist in the order since version 1.10.0. I would suspect that the Zapier extension has some action or filter hooks that could be used to grab the dates from the booking.
FWIW there is an enhancement request open on the Bookings extension to add compatibility code so that Order CSV export and other extensions can access that data. I have no idea when (of even if) that enhancement might be added
Richie Tyler says
Hi Will,
Thanks so much for your response! The filters in Zapier are powerful but without access to the data from the WooCommerce order (which is what’s been taken away since 1.10), they don’t have anything to filter.
However, what I ended up doing – with the help of your code above, thank you! – is to create a variable from $booking->get_start_time(); and added it as an order note, which Zapier is then able to receive and parse. It’s a bit of a hack, but as we won’t be using the customer note functionality it’s a sufficient workaround.
I’ve pasted the function below in case it helps anyone else:
function booking_timestamp ( $order_id ) {
// Get the order, then make sure its payment method is COD.
$order = wc_get_order( $order_id );
if ( ‘cod’ !== $order->get_payment_method() ) {
return;
}
// Call the data store class so we can get bookings from the order.
$booking_data = new WC_Booking_Data_Store();
$booking_ids = $booking_data->get_booking_ids_from_order_id( $order_id );
// If we have bookings go through each to retrieve start date and add that as order note
if ( is_array( $booking_ids ) && count( $booking_ids ) > 0 ) {
foreach ( $booking_ids as $booking_id ) {
$booking = get_wc_booking( $booking_id );
$booker_date = $booking->get_start_date();
$order->add_order_note($booker_date, 10, true);
}
}
}
add_action( ‘woocommerce_order_status_processing’, ‘booking_timestamp’, 10 );
Thanks again!
jclark12020 says
Will, My problem is very similar to the one you explained so well by adding features to the CSV Export plugin – but I need to capture both the “person count” and the “person type” or “person name”. I am running a tour site licensed by a large entertainment network. We sell tour tickets by person type: Adult, Child, Child Free, Senior. I need to report the types and counts to them to properly pay my licensing fees. I cannot, for the life of me find where the person name is in the data structure. Is there a function much like the one you use for get_person_counts() that provide the person type/name as well? Or where can I pull that data from?
will says
Howdy ?
The bookable product type provides a method called
get_person_types
. One would first need to get the bookable prooovduct id (or object) from the booking and then call that method to get the person types applicable to that product.Bookings provides a
WC_Product_Booking_Person_Type
class that can be constructed with a$person_id
and the the person type name can be retrieved via theget_name
method of that same class.If you have the most recent version of Bookings, have a look at
includes/admin/class-wc-bookings-details-meta-box.php
starting around line 256 for an example of how one might put all of that together.Earlier versions of Bookings will have that same example, I’m just not sure about the line number.
jclark12020 says
I actually wrote the code myself based on your input, which was wonderful by the way. I haven’t incorporated the recommendation you made, but this one works.
Here it is:
function wc_csv_export_modify_column_headers( $column_headers ) {
$person_array = array();
$persons = new WP_Query(array('post_type' => 'bookable_person'));
if ( $persons->have_posts() ) {
while ( $persons->have_posts() ) {
$persons->the_post();
$name = get_the_title();
$key = sanitize_title($name);
$person_array[$key] = $name;
}
/* Restore original Post Data */
beardedguy_reset_postdata();
}
ksort($person_array);
$new_headers = array(
'booking_id' => 'Booking ID',
);
foreach ($person_array as $key => $name) {
$new_headers[$key] = $name;
}
return array_merge( $column_headers, $new_headers );
}
add_filter( 'wc_customer_order_csv_export_order_headers', 'wc_csv_export_modify_column_headers' );
// set the data for each for custom columns
function wc_csv_export_modify_row_data( $order_data, $order, $csv_generator ) {
$custom_data = array();
$person_array = array();
$persons = new WP_Query(array('post_type' => 'bookable_person'));
if ( $persons->have_posts() ) {
while ( $persons->have_posts() ) {
$persons->the_post();
$name = get_the_title();
$key = sanitize_title($name);
$person_array[$key] = 0;
}
/* Restore original Post Data */
beardedguy_reset_postdata();
}
ksort($person_array);
if ( is_callable( 'WC_Booking_Data_Store::get_booking_ids_from_order_id' ) ) {
$booking_data = new WC_Booking_Data_Store();
$booking_ids = $booking_data->get_booking_ids_from_order_id( $order->get_id() );
foreach ( $booking_ids as $booking_id ) {
$booking = new WC_Booking( $booking_id );
$custom_data['booking_id'] = $booking_id;
foreach ( $booking->get_persons() as $id => $qty ) {
$name = get_the_title( $id );
$key = sanitize_title($name);
$person_array[$key] = $qty;
}
foreach ($person_array as $key => $qty) {
$custom_data[$key] = $qty;
$person_array[$key] = 0;
}
}
}
$new_order_data = array();
$one_row_per_item = false;
if ( version_compare( wc_customer_order_csv_export()->get_version(), '4.0.0', '<' ) ) {
// pre 4.0 compatibility
$one_row_per_item = ( 'default_one_row_per_item' === $csv_generator->order_format || 'legacy_one_row_per_item' === $csv_generator->order_format );
} elseif ( isset( $csv_generator->format_definition ) ) {
// post 4.0 (requires 4.0.3+)
$one_row_per_item = 'item' === $csv_generator->format_definition['row_type'];
}
if ( $one_row_per_item ) {
foreach ( $order_data as $data ) {
$new_order_data[] = array_merge( (array) $data, $custom_data );
}
} else {
$new_order_data = array_merge( $order_data, $custom_data );
}
return $new_order_data;
}
add_filter( 'wc_customer_order_csv_export_order_row', 'wc_csv_export_modify_row_data', 10, 3 );
jclark12020 says
Perhaps a silly question: This modifies the “default” views. Any way to modify the available fields for the “Custom” data mapping so that I can selectively choose what columns to display and to add or remove these columns from custom reports?
Will says
I’m not following. What do you mean by “default views”?