WooCommerce Bookings, by WooCommerce
WooCommerce Bookings adds a booking product type to WooCommerce that stores date, time, duration, resource, and person data as cart item meta. Phone Orders for WooCommerce doesn’t recognise the booking product type out of the box — it won’t appear in product search results, and if an agent loads an existing order containing a booking, the booking meta won’t survive the cart reload correctly.
This snippet registers the booking product type with Phone Orders and ensures all booking meta persists correctly through cart updates, with price and quantity fields locked to prevent agents from accidentally breaking a booking’s pricing.
Code Sample
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
add_filter( 'wpo_search_product_types', function ( $types ) { $types[] = 'booking'; return $types; }, 10, 1 ); add_filter('wpo_cart_item_is_price_readonly', function ($is_readonly, $cart_item_data) { return isset($cart_item_data['booking']) ? true : $is_readonly; }, 10, 2); add_filter('wpo_cart_item_is_qty_readonly', function ($is_readonly, $cart_item_data) { return isset($cart_item_data['booking']) ? true : $is_readonly; }, 10, 2); add_filter('wpo_update_cart_cart_item_meta', function ($cart_item_meta, $item) { foreach ($_POST as $key => $value) { if (preg_match('/^wc_bookings_field_(.*)/i', $key)) { unset($_POST[$key]); } } if (isset($_POST['add-to-cart'])) { unset($_POST['add-to-cart']); } if ( isset($item['booking']) ) { $persons = array(); if (isset($item['booking']['_persons'])) { if (is_array($item['booking']['_persons'])) { if (isset($item['booking']['_persons'][0])) { $persons['wc_bookings_field_persons'] = $item['booking']['_persons'][0]; } else { foreach ($item['booking']['_persons'] as $i => $v) { $persons['wc_bookings_field_persons_' . $i] = $v; } } } else { $persons['wc_bookings_field_persons'] = $item['booking']['_persons']; } } $_POST = array_merge($_POST, $persons, array( 'wc_bookings_field_start_date_month' => $item['booking']['_month'], 'wc_bookings_field_start_date_day' => $item['booking']['_day'], 'wc_bookings_field_start_date_year' => $item['booking']['_year'], 'wc_bookings_field_duration' => isset($item['booking']['_duration']) && $item['booking']['_duration'] > 0 ? $item['booking']['_duration'] : null, 'wc_bookings_field_resource' => isset($item['booking']['_resource_id']) ? $item['booking']['_resource_id'] : null, 'wc_bookings_field_start_date_time' => isset($item['booking']['_time']) && $item['booking']['_time'] !== '' ? date(DateTime::ISO8601, strtotime("{$item['booking']['_year']}-{$item['booking']['_month']}-{$item['booking']['_day']} {$item['booking']['_time']}")) : null, 'wc_bookings_field_start_date_local_timezone' => isset($item['booking']['_local_timezone']) ? $item['booking']['_local_timezone'] : null, 'add-to-cart' => $item['product_id'], )); if ( isset( $item['booking']['_booking_id'] ) ) { wp_delete_post( $item['booking']['_booking_id'], true ); } } return array_merge($cart_item_meta, array( 'booking' => isset($item['booking']) ? $item['booking'] : null, )); }, 10, 2); add_filter('wpo_update_cart_loaded_product', function ($loaded_product, $item) { return array_merge($loaded_product, array( 'booking' => isset($item['booking']) ? $item['booking'] : null, 'readonly_price' => isset($item['booking']) ? $item['data']->get_price() : null, 'readonly_custom_meta_fields_html' => isset($item['booking']) ? wc_get_formatted_cart_item_data($item) : '', )); }, 10, 3); add_filter('wpo_get_item_by_product', function ($product, $cart_item_data) { return array_merge($product, array( 'booking' => isset($cart_item_data['booking']) ? $cart_item_data['booking'] : null, )); }, 10, 2); |
Code Explained (for Developers)
| Hook | Purpose |
|---|---|
wpo_search_product_types | Adds 'booking' to the list of product types Phone Orders includes in its product search results. Without this, searching for a bookable product returns no results. |
wpo_cart_item_is_price_readonly | Locks the price field for any cart item that carries booking data. Booking prices depend on date, duration, and resource — an agent editing the price manually would break the booking’s integrity. |
wpo_cart_item_is_qty_readonly | Locks the quantity field for booking items for the same reason — booking quantities tie directly to the number of persons or resources, not a generic count. |
wpo_update_cart_cart_item_meta | Fires when Phone Orders rebuilds a cart item on update. The snippet clears stale wc_bookings_field_* POST values first, then repopulates them from the stored booking data — date, time, duration, resource, persons, and timezone — so WooCommerce Bookings recalculates the booking correctly. It also deletes the old _booking_id post if one exists, since the booking recreates on cart rebuild. |
wpo_update_cart_loaded_product | Syncs the booking data and read-only price when an existing cart item reloads, and populates readonly_custom_meta_fields_html with the formatted booking details for display in the Phone Orders UI. |
wpo_get_item_by_product | Passes the booking data array into the Phone Orders item object when a product first resolves to a cart item, ensuring the booking context travels with the item through all subsequent processing. |
How to Apply This Code
- Open Appearance → Theme File Editor or the Code Snippets plugin.
- Paste the full snippet into your child theme’s
functions.phpor create a new dedicated snippet. - Save and open Phone Orders. Search for a bookable product and confirm it appears in results.
- Add it to the cart and verify the booking date/time fields display correctly and the price and quantity fields are locked.
- Load an existing order containing a booking and confirm all booking details carry over correctly.
⚠️ Always use a child theme or Code Snippets — parent theme files get overwritten on theme updates.
When Should You Use This Fix?
This fix applies whenever your store uses WooCommerce Bookings and agents need to create or edit orders containing bookable products through Phone Orders PRO. Without it, bookable products don’t appear in Phone Orders search, and booking meta gets lost when the cart updates.