diff --git a/includes/admin/wc-admin-functions.php b/includes/admin/wc-admin-functions.php index 26be8a6a88d..32636fb904f 100644 --- a/includes/admin/wc-admin-functions.php +++ b/includes/admin/wc-admin-functions.php @@ -218,6 +218,14 @@ function wc_maybe_adjust_line_item_product_stock( $item, $item_quantity = -1 ) { $refunded_item_quantity = $order->get_qty_refunded_for_item( $item->get_id() ); $diff = $item_quantity + $refunded_item_quantity - $already_reduced_stock; + /* + * 0 as $item_quantity usually indicates we're deleting the order item. + * We need to perform different calculations for this case. + */ + if ( 0 === $item_quantity ) { + $diff = min( absint( $refunded_item_quantity ), $already_reduced_stock ) * -1; + } + if ( $diff < 0 ) { $new_stock = wc_update_product_stock( $product, $diff * -1, 'increase' ); } elseif ( $diff > 0 ) { diff --git a/tests/php/includes/admin/class-wc-admin-functions-test.php b/tests/php/includes/admin/class-wc-admin-functions-test.php index b881fe9a54d..18787eb02a7 100644 --- a/tests/php/includes/admin/class-wc-admin-functions-test.php +++ b/tests/php/includes/admin/class-wc-admin-functions-test.php @@ -69,4 +69,221 @@ class WC_Admin_Functions_Test extends \WC_Unit_Test_Case { $this->assertEquals( 990, $product->get_stock_quantity() ); } + /** + * Test adjust line item function when order item is deleted after a full refund with restock. + * + * @link https://github.com/woocommerce/woocommerce/issues/27504. + */ + public function test_admin_delete_order_item_after_full_refund_restock() { + $product = WC_Helper_Product::create_simple_product(); + $product->set_manage_stock( true ); + $product->set_stock_quantity( 100 ); + $product->set_price( 100 ); + $product->set_regular_price( 100 ); + $product->save(); + + $order = WC_Helper_Order::create_order(); + $order->set_status( 'on-hold' ); + $order_item_id = $order->add_product( $product, 10 ); + $order_item = new WC_Order_Item_Product( $order_item_id ); + + // Stocks have not reduced yet. + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 100, $product->get_stock_quantity() ); + + wc_maybe_adjust_line_item_product_stock( $order_item ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 90, $product->get_stock_quantity() ); + + $args = array( + 'amount' => 10, + 'order_id' => $order->get_id(), + 'line_items' => array( + $order_item_id => array( + 'qty' => 10, + 'refund_total' => 0, + ), + ), + 'refund_payment' => false, + 'restock_items' => true, + ); + + wc_create_refund( $args ); + + $order->remove_item( $order_item_id ); + $order->save(); + + $order_item->delete_meta_data( '_reduced_stock' ); + + wc_maybe_adjust_line_item_product_stock( $order_item, 0 ); + + $product = wc_get_product( $product->get_id() ); + + // Stocks should have been increased back to original level. + $this->assertEquals( 100, $product->get_stock_quantity() ); + } + + /** + * Test adjust line item function when order item is deleted after a full refund with no restock. + * + * @link https://github.com/woocommerce/woocommerce/issues/27504. + */ + public function test_admin_delete_order_item_after_full_refund_no_restock() { + $product = WC_Helper_Product::create_simple_product(); + $product->set_manage_stock( true ); + $product->set_stock_quantity( 100 ); + $product->set_price( 100 ); + $product->set_regular_price( 100 ); + $product->save(); + + $order = WC_Helper_Order::create_order(); + $order->set_status( 'on-hold' ); + $order_item_id = $order->add_product( $product, 10 ); + $order_item = new WC_Order_Item_Product( $order_item_id ); + + // Stocks have not reduced yet. + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 100, $product->get_stock_quantity() ); + + wc_maybe_adjust_line_item_product_stock( $order_item ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 90, $product->get_stock_quantity() ); + + $args = array( + 'amount' => 10, + 'order_id' => $order->get_id(), + 'line_items' => array( + $order_item_id => array( + 'qty' => 10, + 'refund_total' => 0, + ), + ), + 'refund_payment' => false, + 'restock_items' => false, + ); + + wc_create_refund( $args ); + + $order->remove_item( $order_item_id ); + $order->save(); + + wc_maybe_adjust_line_item_product_stock( $order_item, 0 ); + + $product = wc_get_product( $product->get_id() ); + + // Stocks should have been increased back to original level. + $this->assertEquals( 100, $product->get_stock_quantity() ); + } + + /** + * Test adjust line item function when order item is deleted after a partial refund with restock. + * + * @link https://github.com/woocommerce/woocommerce/issues/27504. + */ + public function test_admin_delete_order_item_after_partial_refund_restock() { + $product = WC_Helper_Product::create_simple_product(); + $product->set_manage_stock( true ); + $product->set_stock_quantity( 100 ); + $product->set_price( 100 ); + $product->set_regular_price( 100 ); + $product->save(); + + $order = WC_Helper_Order::create_order(); + $order->set_status( 'on-hold' ); + $order_item_id = $order->add_product( $product, 10 ); + $order_item = new WC_Order_Item_Product( $order_item_id ); + + // Stocks have not reduced yet. + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 100, $product->get_stock_quantity() ); + + wc_maybe_adjust_line_item_product_stock( $order_item ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 90, $product->get_stock_quantity() ); + + $args = array( + 'amount' => 10, + 'order_id' => $order->get_id(), + 'line_items' => array( + $order_item_id => array( + 'qty' => 5, + 'refund_total' => 0, + ), + ), + 'refund_payment' => false, + 'restock_items' => true, + ); + + wc_create_refund( $args ); + + $order->remove_item( $order_item_id ); + $order->save(); + + $order_item->update_meta_data( '_reduced_stock', 5 ); + + wc_maybe_adjust_line_item_product_stock( $order_item, 0 ); + + $product = wc_get_product( $product->get_id() ); + + // Stocks should have been increased back to original level. + $this->assertEquals( 100, $product->get_stock_quantity() ); + } + + /** + * Test adjust line item function when order item is deleted after a partial refund with no restock. + * + * @link https://github.com/woocommerce/woocommerce/issues/27504. + */ + public function test_admin_delete_order_item_after_partial_refund_no_restock() { + $product = WC_Helper_Product::create_simple_product(); + $product->set_manage_stock( true ); + $product->set_stock_quantity( 100 ); + $product->set_price( 100 ); + $product->set_regular_price( 100 ); + $product->save(); + + $order = WC_Helper_Order::create_order(); + $order->set_status( 'on-hold' ); + $order_item_id = $order->add_product( $product, 10 ); + $order_item = new WC_Order_Item_Product( $order_item_id ); + + // Stocks have not reduced yet. + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 100, $product->get_stock_quantity() ); + + wc_maybe_adjust_line_item_product_stock( $order_item ); + + $product = wc_get_product( $product->get_id() ); + $this->assertEquals( 90, $product->get_stock_quantity() ); + + $args = array( + 'amount' => 10, + 'order_id' => $order->get_id(), + 'line_items' => array( + $order_item_id => array( + 'qty' => 5, + 'refund_total' => 0, + ), + ), + 'refund_payment' => false, + 'restock_items' => false, + ); + + wc_create_refund( $args ); + + $order->remove_item( $order_item_id ); + $order->save(); + + $order_item->update_meta_data( '_reduced_stock', 5 ); + + wc_maybe_adjust_line_item_product_stock( $order_item, 0 ); + + $product = wc_get_product( $product->get_id() ); + + // Stocks should have been increased to orignal amount minus the partially refunded stock. + $this->assertEquals( 95, $product->get_stock_quantity() ); + } }