REST: Update product stock when removing line item from order (#50606)

* REST: Update product stock when removing line item from order

This ensures that the REST endpoint behaves the same as the UI when
updating an order to remove a line item.

Fixes #49651

* Add changefile(s) from automation for the following project(s): woocommerce

* phpcs cleanup

* Add unit tests

* phpcs cleanup

* Update plugins/woocommerce/changelog/50606-fix-49651-rest-remove-line-item-stock

Co-authored-by: Barry Hughes <3594411+barryhughes@users.noreply.github.com>

* Add changefile(s) from automation for the following project(s): woocommerce

* Add void return type to new method

---------

Co-authored-by: github-actions <github-actions@github.com>
Co-authored-by: Barry Hughes <3594411+barryhughes@users.noreply.github.com>
This commit is contained in:
Corey McKrill 2024-08-15 15:20:10 -07:00 committed by GitHub
parent 6185185589
commit 4ff92e0f1f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 122 additions and 1 deletions

View File

@ -0,0 +1,4 @@
Significance: minor
Type: fix
Ensure that the orders REST endpoint behaves the same as the UI when updating an order to remove a line item.

View File

@ -131,7 +131,7 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
foreach ( $value as $item ) {
if ( is_array( $item ) ) {
if ( $this->item_is_null( $item ) || ( isset( $item['quantity'] ) && 0 === $item['quantity'] ) ) {
$order->remove_item( $item['id'] );
$this->remove_item( $order, $key, $item['id'] );
} else {
$this->set_item( $order, $key, $item );
}
@ -170,6 +170,46 @@ class WC_REST_Orders_Controller extends WC_REST_Orders_V2_Controller {
return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $order, $request, $creating );
}
/**
* Wrapper method to remove order items.
* When updating, the item ID provided is checked to ensure it is associated
* with the order.
*
* @param WC_Order $order The order to remove the item from.
* @param string $item_type The item type (from the request, not from the item, e.g. 'line_items' rather than 'line_item').
* @param int $item_id The ID of the item to remove.
*
* @return void
* @throws WC_REST_Exception If item ID is not associated with order.
*/
protected function remove_item( WC_Order $order, string $item_type, int $item_id ): void {
$item = $order->get_item( $item_id );
if ( ! $item ) {
throw new WC_REST_Exception(
'woocommerce_rest_invalid_item_id',
esc_html__( 'Order item ID provided is not associated with order.', 'woocommerce' ),
400
);
}
if ( 'line_items' === $item_type ) {
require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';
wc_maybe_adjust_line_item_product_stock( $item, 0 );
}
/**
* Allow extensions be notified before the item is removed.
*
* @param WC_Order_Item $item The item object.
*
* @since 9.3.0.
*/
do_action( 'woocommerce_rest_remove_order_item', $item );
$order->remove_item( $item_id );
}
/**
* Save an object data.
*

View File

@ -403,4 +403,81 @@ class WC_REST_Orders_Controller_Tests extends WC_REST_Unit_Test_Case {
$this->assertIsArray( $decoded_data_object[0]->meta_data );
}
/**
* @testdox When a line item quantity in an order is updated via REST API, the product's stock should also be updated.
*/
public function test_order_update_line_item_quantity_updates_product_stock() {
require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';
$product = WC_Helper_Product::create_simple_product();
$product->set_manage_stock( true );
$product->set_stock_quantity( 10 );
$product->save();
$order = WC_Helper_Order::create_order( 1, $product, array( 'status' => 'on-hold' ) ); // Initial qty of 4.
$items = $order->get_items();
$item = reset( $items );
wc_maybe_adjust_line_item_product_stock( $item );
$product = wc_get_product( $product->get_id() );
$this->assertEquals( 6, $product->get_stock_quantity() );
$request = new WP_REST_Request( 'POST', '/wc/v3/orders/' . $order->get_id() );
$request->set_body_params(
array(
'line_items' => array(
array(
'id' => $item->get_id(),
'quantity' => 5,
),
),
)
);
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$product = wc_get_product( $product );
$this->assertEquals( 5, $product->get_stock_quantity() );
}
/**
* @testdox When a line item in an order is removed via REST API, the product's stock should also be updated.
*/
public function test_order_remove_line_item_updates_product_stock() {
require_once WC_ABSPATH . 'includes/admin/wc-admin-functions.php';
$product = WC_Helper_Product::create_simple_product();
$product->set_manage_stock( true );
$product->set_stock_quantity( 10 );
$product->save();
$order = WC_Helper_Order::create_order( 1, $product, array( 'status' => 'on-hold' ) ); // Initial qty of 4.
$items = $order->get_items();
$item = reset( $items );
wc_maybe_adjust_line_item_product_stock( $item );
$product = wc_get_product( $product->get_id() );
$this->assertEquals( 6, $product->get_stock_quantity() );
$request = new WP_REST_Request( 'POST', '/wc/v3/orders/' . $order->get_id() );
$request->set_body_params(
array(
'line_items' => array(
array(
'id' => $item->get_id(),
'quantity' => 0,
),
),
)
);
$response = $this->server->dispatch( $request );
$this->assertEquals( 200, $response->get_status() );
$order = wc_get_order( $order );
$this->assertEmpty( $order->get_items() );
$product = wc_get_product( $product );
$this->assertEquals( 10, $product->get_stock_quantity() );
}
}