Search code examples
phpwordpresswoocommerceinventoryorders

Exclude one specific payment gateway from auto-restock in WooCommerce


One of my payment getaway is ipay88 which only reduce stock after payment is cleared.

During the time when customer is making the payment, the order is under "pending payment" which the stock does not reduce. However, when customer does not make the payment in time, the order will automatically be marked as "cancelled".

As I have integrated with auto-restock plugin, "pending payment" to "cancelled" will increase the stock.

I would like to ask how do prevent restock for a specific payment getaway which is ipay88.

if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly

if ( ! class_exists( 'WC_Auto_Stock_Restore' ) ) {

class WC_Auto_Stock_Restore {

    public function __construct() {
    add_action( 'woocommerce_order_status_processing_to_cancelled', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_on-hold_to_cancelled', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_pending_to_cancelled', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_completed_to_cancelled', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_processing_to_refunded', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_completed_to_refunded', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_on-hold_to_refunded', array( $this, 'restore_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_cancelled_to_completed', array( $this, 'remove_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_cancelled_to_processing', array( $this, 'remove_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_cancelled_to_on-hold', array( $this, 'remove_order_stock' ), 10, 1 );
        add_action( 'woocommerce_order_status_cancelled_to_pending', array( $this, 'remove_order_stock' ), 10, 1 );
        //add_action( 'woocommerce_order_status_changed', array( $this, 'adjust_order_stock' ), 10, 3 );
    } // End __construct()

    //public function adjust_order_stock( $order_id, $old_status, $new_status ) {
        //$valid_new_statuses = array( 'complete' );
        //if ( $old_status == cancelled AND $new_status )
    //}

    public function remove_order_stock( $order_id ) {
        $order = new WC_Order( $order_id );

        if ( ! get_option('woocommerce_manage_stock') == 'yes' && ! sizeof( $order->get_items() ) > 0 ) {
            return;
        }

        foreach ( $order->get_items() as $item ) {

            if ( $item['product_id'] > 0 ) {
                $_product = $order->get_product_from_item( $item );

                if ( $_product && $_product->exists() && $_product->managing_stock() ) {
                    $old_stock = $_product->stock;

                    $qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );

                    $new_quantity = $_product->reduce_stock( $qty );

                    do_action( 'woocommerce_auto_stock_restored', $_product, $item );

                    $order->add_order_note( sprintf( __( 'Item #%s stock decremented from %s to %s.', 'woocommerce' ), $item['product_id'], $old_stock, $new_quantity) );

                    $order->send_stock_notifications( $_product, $new_quantity, $item['qty'] );
                }
            }
        }
    }

    public function restore_order_stock( $order_id ) {
        $order = new WC_Order( $order_id );

        if ( ! get_option('woocommerce_manage_stock') == 'yes' && ! sizeof( $order->get_items() ) > 0 ) {
            return;
        }

        foreach ( $order->get_items() as $item ) {

            if ( $item['product_id'] > 0 ) {
                $_product = $order->get_product_from_item( $item );

                if ( $_product && $_product->exists() && $_product->managing_stock() ) {
                    $old_stock = $_product->stock;

                    $qty = apply_filters( 'woocommerce_order_item_quantity', $item['qty'], $this, $item );

                    $new_quantity = $_product->increase_stock( $qty );

                    do_action( 'woocommerce_auto_stock_restored', $_product, $item );

                    $order->add_order_note( sprintf( __( 'Item #%s stock incremented from %s to %s.', 'woocommerce' ), $item['product_id'], $old_stock, $new_quantity) );

                    $order->send_stock_notifications( $_product, $new_quantity, $item['qty'] );
                }
            }
        }
    } // End restore_order_stock()
}
$GLOBALS['wc_auto_stock_restore'] = new WC_Auto_Stock_Restore();
}

Solution

  • Updated, tested and fully functional for Woocommerce versions 3+

    Your code is really obsolete since WooCommerce version 3. So I have revisited all the code…

    Also instead using multiple hooks for each kind of status change, you can replace all the hooks by a unique one: woocommerce_order_status_changed

    Finally with that unique hook, you will need a unique function, where it will be possible to exclude easily your 'ipay88' payment gateway ID when order status is changed from 'pending' to 'cancelled'.

    For the generated order note, It's better to get a unique order note when there is multiple items in the order…

    So the complete code for this plugin is:

    ## ---- for WooCommerce versions 3+ - Plugin code version ---- ##
    
    if ( ! defined( 'ABSPATH' ) ) exit; // Exit if accessed directly
    
    if ( ! class_exists( 'WC_Auto_Stock_Restore' ) ) {
    
        class WC_Auto_Stock_Restore {
    
            public function __construct() {
                add_action( 'woocommerce_order_status_changed', array( $this, 'adjust_order_stock' ), 10, 3 );
            }
    
            public function adjust_order_stock( $order_id, $old_status, $new_status ) {
                $order = new WC_Order( $order_id );
                $items = $order->get_items();
    
                if ( ! get_option('woocommerce_manage_stock') == 'yes' && ! count( $items ) > 0 )
                    return; // We exit
    
                // EXCEPTION for 'ipay88' payment method id, when Order status change from 'pending' to 'cancelled'
                if( $order->get_payment_method() == 'ipay88' && $old_status == 'pending' && $new_status == 'cancelled' )
                    return; // We exit
    
                // We don't need from 'pending' to 'refunded' order status
                if( $old_status == 'pending' && $new_status == 'refunded' )
                    return; // We exit
    
                // Group Order statuses in 2 groups (array)
                $statuses_group1 = array( 'processing', 'on-hold', 'pending', 'completed' );
                $statuses_group2 = array( 'cancelled', 'refunded' );
    
                $change_stock = false;
                $order_note = array();
    
                ## 1. Restore Order stock (7 order status change types)
                if ( in_array( $old_status, $statuses_group1 ) && in_array( $new_status, $statuses_group2 ) ) {
                    $change_stock = true;
                    $change_type = 'increase';
                    $change_text =  __( 'incremented', 'woocommerce' );
                }
                ## 2. Restore Order stock (4 order status change types)
                elseif ( $old_status == 'cancelled' && in_array( $new_status, $statuses_group1 ) ) {
                    $change_stock = true;
                    $change_type = 'decrease';
                    $change_text =  __( 'decremented', 'woocommerce' );
                }
                ## 3. Other cases
                else return; // We exit
    
                if( $change_stock ){
                    // Iterating through each order 'line_item'
                    // Since WC 3, $item is a WC_Order_Item_Product object
                    foreach ( $items as $item ) {
                        $product_id = $item->get_product_id(); // The product ID
                        if ( $product_id > 0 ) {
                             // Get tan instance of the WC_Product object
                            $product = $item->get_product();
                            if ( $product && $product->exists() && $product->managing_stock() ) {
    
                                // Get the product initial stock quantity (before update)
                                $initial_stock = $product->get_stock_quantity();
    
                                $item_qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $this, $item );
    
                                // Update the product stock quantity
                                // Replace DEPRECATED methods: increase_stock() & discrease_stock()
                                wc_update_product_stock( $product, $item_qty, $change_type );
    
                                // Get the product updated stock quantity
                                $updated_stock = ( $change_type == 'increase' ) ? $initial_stock + $item_qty : $initial_stock - $item_qty;
    
                                do_action( 'woocommerce_auto_stock_restored', $product, $item );
    
                                // A unique Order note: Store each order note in an array…
                                $order_note[] = sprintf( __( 'Product ID #%s stock %s from %s to %s.', 'woocommerce' ), $product_id, $change_text, $initial_stock, $updated_stock);
    
                                // DEPRECATED & NO LONGER NEEDED - can be removed
                                //$order->send_stock_notifications( $product, $updated_stock, $item_qty );
                            }
                        }
                    }
                    // Adding a unique composite order note (for multiple items)
                    $order_notes = count($order_note) > 1 ? implode(' | ', $order_note) : $order_note[0];
                    $order->add_order_note( $order_notes );
                }
            }
        }
        $GLOBALS['wc_auto_stock_restore'] = new WC_Auto_Stock_Restore();
    }
    

    This should go in a plugin php file (see this related to WooCommerce plugins creation).


    The normal version (for themes function.php file):

    ## ---- for WooCommerce versions 3+ - Theme code version ---- ##
    
    add_action( 'woocommerce_order_status_changed', 'adjust_order_stock', 10, 3 );
    function adjust_order_stock( $order_id, $old_status, $new_status ) {
        $order = new WC_Order( $order_id );
        $items = $order->get_items();
    
        if ( ! get_option('woocommerce_manage_stock') == 'yes' && ! count( $items ) > 0 )
            return; // We exit
    
        // EXCEPTION for 'ipay88' payment method id, when Order status change from 'pending' to 'cancelled'
        if( $order->get_payment_method() == 'ipay88' && $old_status == 'pending' && $new_status == 'cancelled' )
            return; // We exit
    
        // We don't need from 'pending' to 'refunded' order status
        if( $old_status == 'pending' && $new_status == 'refunded' )
            return; // We exit
    
        // Group Order statuses in 2 groups (array)
        $statuses_group1 = array( 'processing', 'on-hold', 'pending', 'completed' );
        $statuses_group2 = array( 'cancelled', 'refunded' );
    
        $change_stock = false;
        $order_note = array();
    
        ## 1. Restore Order stock (7 order status change types)
        if ( in_array( $old_status, $statuses_group1 ) && in_array( $new_status, $statuses_group2 ) ) {
            $change_stock = true;
            $change_type = 'increase';
            $change_text =  __( 'incremented', 'woocommerce' );
        }
        ## 2. Restore Order stock (4 order status change types)
        elseif ( $old_status == 'cancelled' && in_array( $new_status, $statuses_group1 ) ) {
            $change_stock = true;
            $change_type = 'decrease';
            $change_text =  __( 'decremented', 'woocommerce' );
        }
        ## 3. Other cases
        else return; // We exit
    
        if( $change_stock ){
            // Iterating through each order 'line_item'
            // Since WC 3, $item is a WC_Order_Item_Product object
            foreach ( $items as $item ) {
                $product_id = $item->get_product_id(); // The product ID
                if ( $product_id > 0 ) {
                     // Get tan instance of the WC_Product object
                    $product = $item->get_product();
                    if ( $product && $product->exists() && $product->managing_stock() ) {
    
                        // Get the product initial stock quantity (before update)
                        $initial_stock = $product->get_stock_quantity();
    
                        $item_qty = apply_filters( 'woocommerce_order_item_quantity', $item->get_quantity(), $this, $item );
    
                        // Update the product stock quantity
                        // Replace DEPRECATED methods: increase_stock() & discrease_stock()
                        wc_update_product_stock( $product, $item_qty, $change_type );
    
                        // Get the product updated stock quantity
                        $updated_stock = ( $change_type == 'increase' ) ? $initial_stock + $item_qty : $initial_stock - $item_qty;
    
                        do_action( 'woocommerce_auto_stock_restored', $product, $item );
    
                        // A unique Order note: Store each order note in an array…
                        $order_note[] = sprintf( __( 'Product ID #%s stock %s from %s to %s.', 'woocommerce' ), $product_id, $change_text, $initial_stock, $updated_stock);
    
                        // DEPRECATED & NO LONGER NEEDED - can be removed
                        //$order->send_stock_notifications( $product, $updated_stock, $item_qty );
                    }
                }
            }
            // Adding a unique composite order note (for multiple items)
            $order_notes = count($order_note) > 1 ? implode(' | ', $order_note) : $order_note[0];
            $order->add_order_note( $order_notes );
        }
    }
    

    This code goes in function.php file of your active child theme (or theme).

    All the code is tested and works only for WooCommerce versions 3+


    Woocommerce Order related: