Merge pull request #25171 from woocommerce/revert-25089-fix/24009
Revert "Add support for '_held_for_checkout` records to prevent race conditions."
This commit is contained in:
commit
55fd39d493
|
@ -366,7 +366,6 @@ class WC_Checkout {
|
|||
}
|
||||
}
|
||||
|
||||
$order->hold_stock_for_checkout( WC()->cart );
|
||||
$order->set_created_via( 'checkout' );
|
||||
$order->set_cart_hash( $cart_hash );
|
||||
$order->set_customer_id( apply_filters( 'woocommerce_checkout_customer_id', get_current_user_id() ) );
|
||||
|
@ -404,9 +403,6 @@ class WC_Checkout {
|
|||
|
||||
return $order_id;
|
||||
} catch ( Exception $e ) {
|
||||
if ( $order && $order instanceof WC_Order ) {
|
||||
$order->release_held_stock();
|
||||
}
|
||||
return new WP_Error( 'checkout-error', $e->getMessage() );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2059,135 +2059,4 @@ class WC_Order extends WC_Abstract_Order {
|
|||
|
||||
return apply_filters( 'woocommerce_get_order_item_totals', $total_rows, $this, $tax_display );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a '_held_for_checkout` record for all products in cart.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param WC_Cart $cart Cart instance.
|
||||
* @throws Exception When unable to hold stock for checkout.
|
||||
*/
|
||||
public function hold_stock_for_checkout( $cart ) {
|
||||
/**
|
||||
* Filter: woocommerce_hold_stock_for_checkout
|
||||
* Allows enable/disable hold stock functionality on checkout.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param bool $enabled Default to true.
|
||||
*/
|
||||
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( ! get_option( 'woocommerce_manage_stock' ) ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 0 );
|
||||
if ( 0 >= $hold_stock_minutes ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$product_qty_in_cart = $cart->get_cart_item_quantities();
|
||||
$stock_held_keys = array();
|
||||
$error = null;
|
||||
|
||||
try {
|
||||
foreach ( $cart->get_cart() as $cart_item_key => $values ) {
|
||||
$product = wc_get_product( $values['data'] );
|
||||
if ( ! $product ) {
|
||||
// Unsupported product!
|
||||
continue;
|
||||
}
|
||||
$product_id = $product->get_stock_managed_by_id();
|
||||
$result = $this->hold_product_for_checkout( $product, $product_qty_in_cart[ $product_id ] );
|
||||
if ( false === $result ) {
|
||||
// translators: Name of the product.
|
||||
throw new Exception( sprintf( __( 'Something changed during checkout. %s is no longer available.', 'woocommerce' ), $product->get_name() ) );
|
||||
}
|
||||
if ( null === $result ) {
|
||||
continue;
|
||||
}
|
||||
$stock_held_keys[ $product_id ] = $result;
|
||||
}
|
||||
} catch ( Exception $e ) {
|
||||
$error = $e;
|
||||
} finally {
|
||||
if ( 0 < count( $stock_held_keys ) ) {
|
||||
$this->record_held_stock( $stock_held_keys );
|
||||
}
|
||||
if ( $error instanceof Exception ) {
|
||||
throw $error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a `_held_for_checkout` record for a product in checkout.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param WC_Product $product Instance of product.
|
||||
* @param int $quantity Quantity of product to hold.
|
||||
* @return bool|string|null Returns `false` when unable to hold stock, meta key when stock was held successfully, `null` when holding stock is not needed.
|
||||
*/
|
||||
protected function hold_product_for_checkout( $product, $quantity ) {
|
||||
global $wpdb;
|
||||
if ( ! $product->managing_stock() || $product->backorders_allowed() ) {
|
||||
return null;
|
||||
}
|
||||
$product_id = $product->get_stock_managed_by_id();
|
||||
$product_data_store = WC_Data_Store::load( 'product' );
|
||||
$held_unique_str = wp_generate_password( 6, false );
|
||||
$db_timestamp = $wpdb->get_var( 'SELECT UNIX_TIMESTAMP() FROM DUAL' );
|
||||
$hold_stock_minutes = (int) get_option( 'woocommerce_hold_stock_minutes', 60 );
|
||||
$expire_timestamp = (int) $db_timestamp + ( $hold_stock_minutes * 60 );
|
||||
$held_key = "_held_for_checkout_${expire_timestamp}_$held_unique_str";
|
||||
$query_for_held_stock = $product_data_store->get_query_for_held_stock( $product_id );
|
||||
$query_for_stock = $product_data_store->get_query_for_stock( $product_id );
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$insert_statement = $wpdb->prepare(
|
||||
"
|
||||
INSERT INTO $wpdb->postmeta ( post_id, meta_key, meta_value )
|
||||
SELECT %d, %s, %d from DUAL
|
||||
WHERE ( $query_for_stock ) - ( $query_for_held_stock ) >= %d
|
||||
",
|
||||
$product->get_stock_managed_by_id(),
|
||||
$held_key,
|
||||
$quantity,
|
||||
$quantity
|
||||
);
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
|
||||
$result = $wpdb->query( $insert_statement ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
|
||||
return $result > 0 ? $held_key : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Save keys used to held stock to DB.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param array $keys Array of keys to save.
|
||||
*/
|
||||
public function record_held_stock( $keys ) {
|
||||
if ( is_array( $keys ) && 0 < count( $keys ) ) {
|
||||
$this->update_meta_data( '_stock_held_keys', $keys );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Releases held stock, also deletes keys for the order.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
public function release_held_stock() {
|
||||
$stock_held_keys = $this->get_meta( '_stock_held_keys' );
|
||||
if ( is_array( $stock_held_keys ) ) {
|
||||
foreach ( $stock_held_keys as $product_managed_id => $meta_key ) {
|
||||
delete_post_meta( $product_managed_id, $meta_key );
|
||||
}
|
||||
}
|
||||
$this->delete_meta_data( '_stock_held_keys' );
|
||||
}
|
||||
}
|
||||
|
|
|
@ -879,8 +879,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
$outofstock_where = ' AND exclude_join.object_id IS NULL';
|
||||
}
|
||||
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
return $wpdb->get_results(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
"
|
||||
SELECT posts.ID as id, posts.post_parent as parent_id
|
||||
FROM {$wpdb->posts} AS posts
|
||||
|
@ -898,8 +898,8 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
)
|
||||
GROUP BY posts.ID
|
||||
"
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
);
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1595,7 +1595,7 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
|
||||
foreach ( $search_terms as $search_term ) {
|
||||
$like = '%' . $wpdb->esc_like( $search_term ) . '%';
|
||||
$term_group_query .= $wpdb->prepare( " {$searchand} ( ( posts.post_title LIKE %s) OR ( posts.post_excerpt LIKE %s) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) )", $like, $like, $like, $like ); // phpcs:ignore WordPress.DB.PreparedSQL.InterpolatedNotPrepared
|
||||
$term_group_query .= $wpdb->prepare( " {$searchand} ( ( posts.post_title LIKE %s) OR ( posts.post_excerpt LIKE %s) OR ( posts.post_content LIKE %s ) OR ( wc_product_meta_lookup.sku LIKE %s ) )", $like, $like, $like, $like ); // @codingStandardsIgnoreLine.
|
||||
$searchand = ' AND ';
|
||||
}
|
||||
|
||||
|
@ -2052,48 +2052,4 @@ class WC_Product_Data_Store_CPT extends WC_Data_Store_WP implements WC_Object_Da
|
|||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns query statement for getting current `_stock` of a product.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param int $product_id Product ID.
|
||||
* @return string|void Query statement.
|
||||
*/
|
||||
public function get_query_for_stock( $product_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->prepare(
|
||||
// MAX function below is used to make sure result is a scalar.
|
||||
"
|
||||
SELECT COALESCE ( MAX( meta_value ), 0 ) FROM $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key = '_stock'
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
FOR UPDATE
|
||||
",
|
||||
$product_id
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns query statement for getting quantity of stock held by orders in checkout.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param int $product_id Product ID.
|
||||
* @return string|void Query statement.
|
||||
*/
|
||||
public function get_query_for_held_stock( $product_id ) {
|
||||
global $wpdb;
|
||||
return $wpdb->prepare(
|
||||
"
|
||||
SELECT COALESCE ( SUM( meta_value ), 0 ) FROM $wpdb->postmeta
|
||||
WHERE {$wpdb->postmeta}.meta_key like %s
|
||||
AND {$wpdb->postmeta}.meta_key > CONCAT( %s, UNIX_TIMESTAMP() )
|
||||
AND {$wpdb->postmeta}.post_id = %d
|
||||
FOR UPDATE
|
||||
",
|
||||
'_held_for_checkout_%',
|
||||
'_held_for_checkout_',
|
||||
$product_id
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -150,7 +150,7 @@ class WC_Shortcode_Checkout {
|
|||
}
|
||||
|
||||
// Check stock based on all items in the cart and consider any held stock within pending orders.
|
||||
$held_stock = ( $hold_stock_minutes > 0 ) ? wc_get_held_stock_quantity( $product ) : 0;
|
||||
$held_stock = ( $hold_stock_minutes > 0 ) ? wc_get_held_stock_quantity( $product, $order->get_id() ) : 0;
|
||||
$required_stock = $quantities[ $product->get_stock_managed_by_id() ];
|
||||
|
||||
if ( $product->get_stock_quantity() < ( $held_stock + $required_stock ) ) {
|
||||
|
|
|
@ -803,8 +803,6 @@ function wc_order_search( $term ) {
|
|||
function wc_update_total_sales_counts( $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
$order->release_held_stock();
|
||||
|
||||
if ( ! $order || $order->get_data_store()->get_recorded_sales( $order ) ) {
|
||||
return;
|
||||
}
|
||||
|
@ -882,23 +880,6 @@ function wc_update_coupon_usage_counts( $order_id ) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Release held stock if any for an order.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param int $order_id Order ID.
|
||||
*/
|
||||
function wc_release_held_stock( $order_id ) {
|
||||
$order = wc_get_order( $order_id );
|
||||
if ( ! $order ) {
|
||||
return;
|
||||
}
|
||||
|
||||
$order->release_held_stock();
|
||||
}
|
||||
add_action( 'woocommerce_order_status_cancelled', 'wc_release_held_stock' );
|
||||
|
||||
add_action( 'woocommerce_order_status_pending', 'wc_update_coupon_usage_counts' );
|
||||
add_action( 'woocommerce_order_status_completed', 'wc_update_coupon_usage_counts' );
|
||||
add_action( 'woocommerce_order_status_processing', 'wc_update_coupon_usage_counts' );
|
||||
|
|
|
@ -302,53 +302,28 @@ function wc_increase_stock_levels( $order_id ) {
|
|||
function wc_get_held_stock_quantity( $product, $exclude_order_id = 0 ) {
|
||||
global $wpdb;
|
||||
|
||||
/**
|
||||
* Filter: woocommerce_hold_stock_for_checkout
|
||||
* Allows enable/disable hold stock functionality on checkout.
|
||||
*
|
||||
* @since 3.9.0
|
||||
* @param bool $enabled Default to true.
|
||||
*/
|
||||
if ( ! apply_filters( 'woocommerce_hold_stock_for_checkout', true ) ) {
|
||||
/**
|
||||
* DOES NOT SUPPORT `exclude_order_id` param, which was primarily being used to exclude the current order!
|
||||
* While creating an order during checkout flow, this logic relies on the fact that order is not yet saved and therefore will be excluded.
|
||||
*
|
||||
* Query for calculating held stock which uses '_held_for_checkout' records instead of actually querying all orders with wc-pending status.
|
||||
* This is introduced to handle race conditions.
|
||||
*
|
||||
* @since 3.9.0
|
||||
*/
|
||||
$product_data_store = WC_Data_Store::load( 'product' );
|
||||
$product_id = $product->get_stock_managed_by_id();
|
||||
return $wpdb->get_var( $product_data_store->get_query_for_held_stock( $product_id ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
|
||||
}
|
||||
|
||||
return $wpdb->get_var(
|
||||
// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
|
||||
$wpdb->prepare(
|
||||
"
|
||||
SELECT SUM( order_item_meta.meta_value ) AS held_qty
|
||||
FROM {$wpdb->posts} AS posts
|
||||
LEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
|
||||
WHERE order_item_meta.meta_key = '_qty'
|
||||
AND order_item_meta2.meta_key = %s
|
||||
AND order_item_meta2.meta_value = %d
|
||||
AND postmeta.meta_key = '_created_via'
|
||||
AND postmeta.meta_value = 'checkout'
|
||||
AND posts.post_type IN ( '" . implode( "','", wc_get_order_types() ) . "' )
|
||||
AND posts.post_status = 'wc-pending'
|
||||
AND posts.ID != %d;
|
||||
",
|
||||
SELECT SUM( order_item_meta.meta_value ) AS held_qty
|
||||
FROM {$wpdb->posts} AS posts
|
||||
LEFT JOIN {$wpdb->postmeta} as postmeta ON posts.ID = postmeta.post_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_items as order_items ON posts.ID = order_items.order_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta ON order_items.order_item_id = order_item_meta.order_item_id
|
||||
LEFT JOIN {$wpdb->prefix}woocommerce_order_itemmeta as order_item_meta2 ON order_items.order_item_id = order_item_meta2.order_item_id
|
||||
WHERE order_item_meta.meta_key = '_qty'
|
||||
AND order_item_meta2.meta_key = %s
|
||||
AND order_item_meta2.meta_value = %d
|
||||
AND postmeta.meta_key = '_created_via'
|
||||
AND postmeta.meta_value = 'checkout'
|
||||
AND posts.post_type IN ( '" . implode( "','", wc_get_order_types() ) . "' )
|
||||
AND posts.post_status = 'wc-pending'
|
||||
AND posts.ID != %d;",
|
||||
'product_variation' === get_post_type( $product->get_stock_managed_by_id() ) ? '_variation_id' : '_product_id',
|
||||
$product->get_stock_managed_by_id(),
|
||||
$exclude_order_id
|
||||
)
|
||||
// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
|
||||
);
|
||||
); // WPCS: unprepared SQL ok.
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -1,151 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Checkout tests.
|
||||
*
|
||||
* @package WooCommerce|Tests|Checkout
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class WC_Checkout
|
||||
*/
|
||||
class WC_Tests_Checkout extends WC_Unit_Test_Case {
|
||||
/**
|
||||
* TearDown.
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup.
|
||||
*/
|
||||
public function setUp() {
|
||||
parent::setUp();
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to create a managed product and a order for that product.
|
||||
*
|
||||
* @return array( WC_Product, WC_Order ) array
|
||||
* @throws Exception When unable to create an order.
|
||||
*/
|
||||
protected function create_order_for_managed_inventory_product() {
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
$product->set_props( array( 'manage_stock' => true ) );
|
||||
$product->set_stock_quantity( 10 );
|
||||
$product->save();
|
||||
|
||||
WC()->cart->add_to_cart( $product->get_id(), 9 );
|
||||
$this->assertEquals( true, WC()->cart->check_cart_items() );
|
||||
|
||||
$checkout = WC_Checkout::instance();
|
||||
$order_id = $checkout->create_order(
|
||||
array(
|
||||
'payment_method' => 'cod',
|
||||
'billing_email' => 'a@b.com',
|
||||
)
|
||||
);
|
||||
|
||||
// Assertions whether the order was created successfully.
|
||||
$this->assertNotWPError( $order_id );
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
return array( $product, $order );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test when order is out stock because it is held by an order in pending status.
|
||||
* @throws Exception When unable to create order.
|
||||
*/
|
||||
public function test_create_order_when_out_of_stock() {
|
||||
list( $product, $order ) = $this->create_order_for_managed_inventory_product();
|
||||
|
||||
$this->assertEquals( 9, $order->get_item_count() );
|
||||
$this->assertEquals( 'pending', $order->get_status() );
|
||||
$this->assertEquals( 9, wc_get_held_stock_quantity( $product ) );
|
||||
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->add_to_cart( $product->get_stock_managed_by_id(), 2 );
|
||||
|
||||
$this->assertEquals( false, WC()->cart->check_cart_items() );
|
||||
}
|
||||
|
||||
/**
|
||||
* Legacy version for test `test_create_order_when_out_of_stock` above.
|
||||
* @throws Exception When unable to create order.
|
||||
*/
|
||||
public function test_create_order_when_out_of_stock_legacy() {
|
||||
add_filter( 'woocommerce_hold_stock_for_checkout', '__return_false' );
|
||||
$this->test_create_order_when_out_of_stock();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if pending stock is cleared when order is cancelled.
|
||||
*
|
||||
* @throws Exception When unable to create order.
|
||||
*/
|
||||
public function test_pending_is_cleared_when_order_is_cancelled() {
|
||||
list( $product, $order ) = $this->create_order_for_managed_inventory_product();
|
||||
|
||||
$this->assertEquals( 9, wc_get_held_stock_quantity( $product ) );
|
||||
$order->set_status( 'cancelled' );
|
||||
$order->save();
|
||||
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product ) );
|
||||
$this->assertEquals( 10, $product->get_stock_quantity() );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test if pending stock is cleared when order is processing.
|
||||
*
|
||||
* @throws Exception When unable to create order.
|
||||
*/
|
||||
public function test_pending_is_cleared_when_order_processed() {
|
||||
list( $product, $order ) = $this->create_order_for_managed_inventory_product();
|
||||
|
||||
$this->assertEquals( 9, wc_get_held_stock_quantity( $product ) );
|
||||
$order->set_status( 'processing' );
|
||||
$order->save();
|
||||
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test creating order from managed stock for variable product.
|
||||
* @throws Exception When unable to create an order.
|
||||
*/
|
||||
public function test_create_order_for_variation_product() {
|
||||
$parent_product = WC_Helper_Product::create_variation_product();
|
||||
$variation = $parent_product->get_available_variations()[0];
|
||||
$variation = wc_get_product( $variation['variation_id'] );
|
||||
$variation->set_manage_stock( true );
|
||||
$variation->set_stock_quantity( 10 );
|
||||
$variation->save();
|
||||
WC()->cart->add_to_cart( $variation->get_id(), 9 );
|
||||
$this->assertEquals( true, WC()->cart->check_cart_items() );
|
||||
|
||||
$checkout = WC_Checkout::instance();
|
||||
$order_id = $checkout->create_order(
|
||||
array(
|
||||
'payment_method' => 'cod',
|
||||
'billing_email' => 'a@b.com',
|
||||
)
|
||||
);
|
||||
|
||||
// Assertions whether the first order was created successfully.
|
||||
$this->assertNotWPError( $order_id );
|
||||
$order = wc_get_order( $order_id );
|
||||
|
||||
$this->assertEquals( 9, $order->get_item_count() );
|
||||
$this->assertEquals( 'pending', $order->get_status() );
|
||||
$this->assertEquals( 9, wc_get_held_stock_quantity( $variation ) );
|
||||
|
||||
WC()->cart->empty_cart();
|
||||
WC()->cart->add_to_cart( $variation->get_stock_managed_by_id(), 2 );
|
||||
|
||||
$this->assertEquals( false, WC()->cart->check_cart_items() );
|
||||
}
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
/**
|
||||
* Class WC_Tests_Order file.
|
||||
*
|
||||
* @package WooCommerce/Tests
|
||||
*/
|
||||
|
||||
/**
|
||||
* Class Functions.
|
||||
*
|
||||
* @package WooCommerce/Tests/Order
|
||||
* @since 3.9.0
|
||||
*/
|
||||
class WC_Tests_Order extends WC_Unit_Test_Case {
|
||||
/**
|
||||
* TearDown.
|
||||
*/
|
||||
public function tearDown() {
|
||||
parent::tearDown();
|
||||
WC()->cart->empty_cart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test pending stock and `release_held_stock` and `record_held_stock` methods as well.
|
||||
* @throws Exception When unable to create an order.
|
||||
*/
|
||||
public function test_pending_stock_for_order_with_multiple_product() {
|
||||
$product1 = WC_Helper_Product::create_simple_product();
|
||||
$product1->set_props( array( 'manage_stock' => true ) );
|
||||
$product1->set_stock_quantity( 10 );
|
||||
$product1->save();
|
||||
|
||||
$product2 = WC_Helper_Product::create_simple_product();
|
||||
$product2->set_props( array( 'manage_stock' => true ) );
|
||||
$product2->set_stock_quantity( 10 );
|
||||
$product2->save();
|
||||
|
||||
WC()->cart->add_to_cart( $product1->get_id(), 9 );
|
||||
WC()->cart->add_to_cart( $product2->get_id(), 5 );
|
||||
$this->assertEquals( true, WC()->cart->check_cart_items() );
|
||||
|
||||
$checkout = WC_Checkout::instance();
|
||||
$order_id = $checkout->create_order(
|
||||
array(
|
||||
'payment_method' => 'cod',
|
||||
'billing_email' => 'a@b.com',
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertNotWPError( $order_id );
|
||||
$order_held_stock_keys = get_post_meta( $order_id, '_stock_held_keys', true );
|
||||
|
||||
$product1_id = $product1->get_stock_managed_by_id();
|
||||
$product2_id = $product2->get_stock_managed_by_id();
|
||||
|
||||
$this->assertEquals( true, in_array( $product1_id, array_keys( $order_held_stock_keys ) ) );
|
||||
$this->assertEquals( true, in_array( $product2_id, array_keys( $order_held_stock_keys ) ) );
|
||||
|
||||
$this->assertEquals( 9, wc_get_held_stock_quantity( $product1 ) );
|
||||
$this->assertEquals( 5, wc_get_held_stock_quantity( $product2 ) );
|
||||
|
||||
$order = wc_get_order( $order_id );
|
||||
$order->release_held_stock();
|
||||
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product1 ) );
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product2 ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test `release_held_stock` function of unmanaged product.
|
||||
* @throws Exception When unable to create an order.
|
||||
*/
|
||||
public function test_release_held_of_unmanaged_product() {
|
||||
$product = WC_Helper_Product::create_simple_product();
|
||||
|
||||
WC()->cart->add_to_cart( $product->get_id(), 9 );
|
||||
$this->assertEquals( true, WC()->cart->check_cart_items() );
|
||||
$checkout = WC_Checkout::instance();
|
||||
$order_id = $checkout->create_order(
|
||||
array(
|
||||
'payment_method' => 'cod',
|
||||
'billing_email' => 'a@b.com',
|
||||
)
|
||||
);
|
||||
|
||||
$this->assertNotWPError( $order_id );
|
||||
$order = wc_get_order( $order_id );
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product ) );
|
||||
|
||||
$order->release_held_stock();
|
||||
$this->assertEquals( 0, wc_get_held_stock_quantity( $product ) );
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue