1

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();
}
LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399

1 Answers1

3

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:

LoicTheAztec
  • 229,944
  • 23
  • 356
  • 399
  • Hi, it works fine when i tested. However, other payment such as BACS, when select pending to cancelled, it has internal 500 error. – Shen Leng Tan Nov 09 '17 at 06:25
  • @ShenLengTan **UPDATE - Tested and Works:** I have just published a complete tested and rewritten version of your code that handle the exception for `'ipay88'` payment gateway ID when order status change from "pending" to "cancelled" (without any error)... Your plugin code was really obsolete since WooCommerce version 3… – LoicTheAztec Nov 10 '17 at 00:28