0

I have some custom order statuses (made with WooCommerce Order Status Manager). But when I use a custom paid status, the booking status is not updated to "paid". I've cobbled together some code from various references but it results in a fatal error. Or maybe I am missing something where custom statuses are supposed to update the booking paid status without extra code?

My code:

add_action('woocommerce_order_status_pool-payment-rec','auto_change_booking_status_to_paid', 10, 1);
function auto_change_booking_status_to_paid($booking_id) {
    $booking = new WC_Booking( $booking_id );
    $order_id = $booking->get_order_id();
    $booking->update_status('paid', 'order_note');
    exit;
}

The error:

[20-Mar-2018 23:32:05 UTC] PHP Fatal error:  Uncaught Exception: Invalid booking. in /home/ahbc/public_html/wp-content/plugins/woocommerce-bookings/includes/data-stores/class-wc-booking-data-store.php:83
Stack trace:
#0 /home/ahbc/public_html/wp-content/plugins/woocommerce/includes/class-wc-data-store.php(149): WC_Booking_Data_Store->read(Object(WC_Booking))
#1 /home/ahbc/public_html/wp-content/plugins/woocommerce-bookings/includes/data-objects/class-wc-booking.php(149): WC_Data_Store->read(Object(WC_Booking))
#2 /home/ahbc/public_html/wp-content/plugins/ahbc-website-tweaks/ahbc-website-tweaks.php(104): WC_Booking->__construct(2223)
#3 /home/ahbc/public_html/wp-includes/class-wp-hook.php(288): auto_change_booking_status_to_paid(2223)
#4 /home/ahbc/public_html/wp-includes/class-wp-hook.php(310): WP_Hook->apply_filters('', Array)
#5 /home/ahbc/public_html/wp-includes/plugin.php(453): WP_Hook->do_action(Array)
#6 /home/ahbc/public_html/wp-content/plugins/woocommerce/includes/class-wc-order.php(327): do_action('woocommerce_ord...', 2223, Object(WC_Order))
# in /home/ahbc/public_html/wp-content/plugins/woocommerce-bookings/includes/data-stores/class-wc-booking-data-store.php on line 83

I've also tried this but it seemingly does nothing:

function sv_wc_order_statuses_needs_payment( $statuses, $order ) {
    // use your custom order status slug here
    $statuses[] = 'pool-payment-rec';
    return $statuses;
}
add_filter( 'woocommerce_valid_order_statuses_for_payment_complete', 'sv_wc_order_statuses_needs_payment', 10, 2 );

My references:

woocommerce booking status changes woocommerce order status

Change Woocommerce order status for Cash on delivery

https://gist.github.com/bekarice/e922e79bc40eb0729095abc561cfe621

EDIT: Have also tried several variations on the following:

add_action( 'woocommerce_order_status_changed', 'auto_change_booking_status_to_paid' );

function auto_change_booking_status_to_paid( $order_id ) {
    if( ! $order_id ) return;   

    $order = wc_get_order($order_id);
    $booking = get_wc_booking($booking_id);

    if( $order->get_status() == 'test' )
//  $order_id = $booking->get_order_id();
    $booking->update_status('confirmed', 'order_note');
}
Rose Thorn
  • 179
  • 3
  • 16
  • You were kind of on the right path but when you set a new status $statuses[] = 'pool-payment-rec'; it should be $statuses['pool-payment-rec'] = 'Payment Received'. The slug needs to be set as the array key. – Andrew Schultz Mar 21 '18 at 07:05

3 Answers3

2

You need to get first the Booking ID from the order ID in this hook. Then you will be able to update the booking status to 'paid' without any error.

I have tested with another custom status than your custom one and it works… If I use your code I get the same error than you.

In the code below I use a very light query to get the booking ID from the order ID, just as WC_Order methods do…

The code:

// Utility function to get the booking ID from the Order ID
function get_The_booking_id( $order_id ){
    global $wpdb;
    return $wpdb->get_var("SELECT ID FROM {$wpdb->prefix}posts WHERE post_parent = '$order_id'");
}

// On custom order status change, update booking status to "paid"
add_action('woocommerce_order_status_pool-payment-rec', 'auto_change_booking_status_to_paid', 20, 2 );
function auto_change_booking_status_to_paid( $order_id, $order ) {

    // Get the booking ID from the order ID
    $booking_id = get_The_booking_id( $order_id );

    if( empty($booking_id) )
        return; // Exit

    // Get an instance of the WC_Booking object
    $booking = new WC_Booking( $booking_id );

    // Update status
    if( $booking->get_status() != 'paid' )
        $booking->update_status( 'paid', 'order_note' );
}

Code goes in function.php file of your active child theme (or theme). Tested and works.

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • @LoicTheAztec correct that your code does indeed work thank you. However as Andrew pointed out, it uses a database query which I would really prefer to avoid. Woocommerce and Bookings clearly have an existing hook to make this work as using the default order statuses "Processing" or "Complete", the booking status changes to "Paid". I just cannot work out how to do the same with a custom order status. – Rose Thorn Mar 21 '18 at 07:36
  • @RoseFisher So finally how do you have managed to get this working? Just curious. Thanks. – LoicTheAztec Mar 25 '18 at 18:32
  • The code you posted above works fine and that's what I've been using in between trying to get WooCommerce's hooks to work. I've been banging my head against them for a while now and I'm about ready to give up. – Rose Thorn Mar 26 '18 at 05:44
1

Ok here is the solution not needing any custom written queries but using the appropriate methods available in the WooCommerce Booking plugin.

add_action('woocommerce_order_status_pool-payment-rec', 'auto_change_booking_status_to_paid', 20, 2 );

function auto_change_booking_status_to_paid( $order_id, $order ) {

    if( $order->get_status() === 'pool-payment-rec' ) {
        foreach( $order->get_items() as $item_id => $item ) {
            $product = wc_get_product($item['product_id']);
            if( $product->get_type() === 'booking' ) {
                $booking_ids = WC_Booking_Data_Store::get_booking_ids_from_order_item_id( $item_id );

                foreach( $booking_ids as $booking_id ) {
                    $booking = new WC_Booking($booking_id);

                    if( $booking->get_status() != 'paid' )
                        $booking->update_status( 'paid', 'order_note' );
                }

            }
        }
    }
}
Andrew Schultz
  • 4,092
  • 2
  • 21
  • 44
  • I would love to say that worked but alas, no. On the upside, it didn't result in a fatal error. – Rose Thorn Mar 21 '18 at 06:19
  • You need to add the custom status to the list of allowed Booking Statuses first. I've added this to the answer, once you do this then the hook woocommerce_booking_pool-payment-rec will fire. – Andrew Schultz Mar 21 '18 at 06:46
  • @AndrewSchultz so the filter works wonderfully for adding custom booking statuses although that's an aside here. However the Custom Order Status -> Booking Status still isn't working. I may just be confused how to implement it though. – Rose Thorn Mar 21 '18 at 07:28
  • @RoseFisher I went into a booking order and saved the order with the custom "Pool Payment Recevied" status. When the page reloads it show a status of "Paid". Are you not seeing this? – Andrew Schultz Mar 21 '18 at 07:44
  • @AndrewSchultz I added both action and filter. The result was that I had a "Pool Payment Received" custom booking status. But changing the order status from whatever to 'Pool Payment Received" did not change the booking status to "Paid" (or the the custom booking status). But again, I may be confused or missing something. (I'm tired and have a disability that can affect cognitive function). – Rose Thorn Mar 21 '18 at 07:46
  • @RoseFisher hmm it's definitely firing for me, maybe add a debug statement to ensure that the hook is firing for 'woocommerce_booking_pool-payment-rec' – Andrew Schultz Mar 21 '18 at 07:50
  • In case it matters, I am using a little custom plugin where I throw in all my tweaks and custom code instead of using functions.php – Rose Thorn Mar 21 '18 at 08:02
  • What is happening on my end is this: Order status: On Hold | Booking status: Unpaid -- Order status On Hold -> Birth Pool Payment Received does not change Booking status from Unpaid to paid. -- Order status: On Hold | Booking status: Unpaid -- Booking status Unpaid -> Pool Payment Received makes the Booking status instantly change to Paid but the Order status remains the same. – Rose Thorn Mar 21 '18 at 08:02
  • @RoseFisher ah ok I see what you mean so we need to also update the order status. At present we are just updating the booking status but not the order. What does the order status need to be if the booking status is paid? Complete? – Andrew Schultz Mar 21 '18 at 08:25
  • @RoseFisher I have updated my code to change the order status to "Completed" and the booking status to "Paid" – Andrew Schultz Mar 21 '18 at 08:30
  • Woocommerce does this by default. To clarify, I'm trying to do it the other way around. Changing the order status (to a custom order status) changes to the booking status. – Rose Thorn Mar 22 '18 at 04:25
  • @RoseFisher when I set the booking to a status of "pool-payment-rec" it sets it to "paid" after I save the booking. If this is not what is occurring for you then the hook is not firing for some reason. Do you know how to write text to the debug.log file to check this? – Andrew Schultz Mar 22 '18 at 06:22
  • Re: debug.log probably not. I've not had to delve into code this much in a long time and with memory problems, that sort of things disappears into the aether of my memory. I've been trying many different things over the last few days but it's just not working. It only works changing the booking status. But I need it so that changing the order status changes the booking status i.e. custom order status -> booking status. Not booking status -> order status. – Rose Thorn Mar 26 '18 at 05:48
  • @RoseFisher finally understand what you are after now, I have updated my code. My code will now will update the booking status to paid when you change the order status to the custom status :) No custom SQL is required. – Andrew Schultz Mar 26 '18 at 06:47
  • you rock my socks. Works perfectly! Could I possibly add one more request? I am trying to map several order statuses to booking statuses but using case or elseif just isn't working. I don't know if there is something funky about Bookings statuses that I am missing but, for example, if I do (custom Order Status) bond-refunded -> (Booking Status) complete, the booking status continues to sit there on paid. – Rose Thorn Mar 28 '18 at 00:27
  • ok, update. Any custom order status except "paid" is not updating the booking status. So COS "paid" -> BS "paid" works. COS "bond refunded" -> BS "complete" does not – Rose Thorn Mar 28 '18 at 00:40
  • @RoseFisher you can use the hook in this format to intercept any status change in WooCommerce => woocommerce_order_status__to_ If that doesn't work then please create a new question and I an assist. – Andrew Schultz Mar 28 '18 at 02:32
  • AH, I get it now!! Thank you so very much for your patience and help with this! :) Do you have somewhere that I could (at the very least) donate you a coffee/beer/beverage of choice? – Rose Thorn Mar 28 '18 at 02:57
  • @RoseFisher not really. The feeling I get helping people is payment enough :) – Andrew Schultz Mar 28 '18 at 03:00
  • I have discovered one bug in this. When the status is changed to paid, the system sends out another New Booking email. I'm unsure as to why :/ – Rose Thorn Apr 28 '18 at 03:50
  • @RoseFisher when does the first email get generated? – Andrew Schultz Apr 28 '18 at 08:39
1
add_action('init', 'change_order_status');
function change_order_status() {
    global $wpdb;

    // Query for orders with a status of "wc-processing"
    $my_query = "SELECT * FROM wp_wc_order_stats WHERE STATUS='wc-processing'";
    $result = $wpdb->get_results($my_query);

    // Iterate through the results
    foreach ($result as $order) {
        $order_id = $order->order_id;
        $order_date = $order->date_created_gmt;

        // Get the current date
        $current_date = date("Y-m-d h:i:s");

        // Calculate the difference in days between the order date and the current date
        $dteStart = new DateTime($order_date);
        $dteEnd = new DateTime($current_date);
        $dteDiff = $dteStart->diff($dteEnd);
        $diff_in_days = $dteDiff->format("%d");

        // Compare the difference in days to 1
        if ($diff_in_days >= 1) {
            $order = new WC_Order($order_id);
            if (!empty($order)) {
                // Change the order status to "wc-accepted"
                $order->update_status('wc-accepted');
            }
        }
    }
    // Query for orders with a status of "wc-accepted"
    $my_query = "SELECT * FROM wp_wc_order_stats WHERE STATUS='wc-accepted'";
    $result = $wpdb->get_results($my_query);

    // Iterate through the results
    foreach ($result as $order) {
        $order_id = $order->order_id;
        $order_date = $order->date_created_gmt;

        // Get the current date
        $current_date = date("Y-m-d h:i:s");

        // Calculate the difference in days between the order date and the current date
        $dteStart = new DateTime($order_date);
        $dteEnd = new DateTime($current_date);
        $dteDiff = $dteStart->diff($dteEnd);
        $diff_in_days = $dteDiff->format("%d");

        // Compare the difference in days to 5
        if ($diff_in_days >= 5) {
            $order = new WC_Order($order_id);
            if (!empty($order)) {
                // Change the order status to "wc-preparing"
                $order->update_status('wc-preparing');
            }
        }
    }
    // Query for orders with a status of "wc-preparing"
    $my_query = "SELECT * FROM wp_wc_order_stats WHERE STATUS='wc-preparing'";
    $result = $wpdb->get_results($my_query);
    // Iterate through the results
    foreach ($result as $order) {
        $order_id = $order->order_id;
        $order_date = $order->date_created_gmt;

        // Get the current date
        $current_date = date("Y-m-d h:i:s");

        // Calculate the difference in days between the order date and the current date
        $dteStart = new DateTime($order_date);
        $dteEnd = new DateTime($current_date);
        $dteDiff = $dteStart->diff($dteEnd);
        $diff_in_days = $dteDiff->format("%d");

        // Compare the difference in days to 6
        if ($diff_in_days >= 6) {
            $order = new WC_Order($order_id);
            if (!empty($order)) {
                // Change the order status to "wc-ready-to-ship"
                $order->update_status('wc-ready-to-ship');
            }
        }
    }
}