Skip to content
This repository was archived by the owner on Feb 23, 2024. It is now read-only.

Commit 119193c

Browse files
committed
Revert "Improve error displayed to customers when an item's stock status changes during checkout. (#3656)"
This reverts commit 00f478c.
1 parent d2151c1 commit 119193c

File tree

11 files changed

+61
-448
lines changed

11 files changed

+61
-448
lines changed

assets/js/base/context/cart-checkout/checkout/processor/index.js

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -232,17 +232,6 @@ const CheckoutProcessor = () => {
232232
addErrorNotice( formatStoreApiErrorMessage( response ), {
233233
id: 'checkout',
234234
} );
235-
236-
if ( Array.isArray( response.additional_errors ) ) {
237-
response.additional_errors.forEach(
238-
( additionalError ) => {
239-
addErrorNotice( additionalError.message, {
240-
id: additionalError.error_code,
241-
} );
242-
}
243-
);
244-
}
245-
246235
dispatchActions.setHasError();
247236
dispatchActions.setAfterProcessing( response );
248237
setIsProcessingOrder( false );

src/StoreApi/Routes/AbstractRoute.php

Lines changed: 0 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
33

44
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
5-
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
6-
use WP_Error;
75

86
/**
97
* AbstractRoute class.
@@ -74,8 +72,6 @@ public function get_response( \WP_REST_Request $request ) {
7472
}
7573
} catch ( RouteException $error ) {
7674
$response = $this->get_route_error_response( $error->getErrorCode(), $error->getMessage(), $error->getCode(), $error->getAdditionalData() );
77-
} catch ( InvalidStockLevelsInCartException $error ) {
78-
$response = $this->get_route_error_response_from_object( $error->getError(), $error->getCode(), $error->getAdditionalData() );
7975
} catch ( \Exception $error ) {
8076
$response = $this->get_route_error_response( 'unknown_server_error', $error->getMessage(), 500 );
8177
}
@@ -208,21 +204,6 @@ protected function get_route_error_response( $error_code, $error_message, $http_
208204
return new \WP_Error( $error_code, $error_message, array_merge( $additional_data, [ 'status' => $http_status_code ] ) );
209205
}
210206

211-
/**
212-
* Get route response when something went wrong and the supplied error is a WP_Error. This currently only happens
213-
* when an item in the cart is out of stock, partially out of stock, can only be bought individually, or when the
214-
* item is not purchasable.
215-
*
216-
* @param WP_Error $error_object The WP_Error object containing the error.
217-
* @param int $http_status_code HTTP status. Defaults to 500.
218-
* @param array $additional_data Extra data (key value pairs) to expose in the error response.
219-
* @return WP_Error WP Error object.
220-
*/
221-
protected function get_route_error_response_from_object( $error_object, $http_status_code = 500, $additional_data = [] ) {
222-
$error_object->add_data( array_merge( $additional_data, [ 'status' => $http_status_code ] ) );
223-
return $error_object;
224-
}
225-
226207
/**
227208
* Prepare a single item for response.
228209
*

src/StoreApi/Routes/Checkout.php

Lines changed: 27 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?php
22
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
33

4-
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
54
use \Exception;
65
use \WP_Error;
76
use \WP_REST_Server;
@@ -191,10 +190,7 @@ protected function get_route_update_response( WP_REST_Request $request ) {
191190
* 5. Process Payment
192191
*
193192
* @throws RouteException On error.
194-
* @throws InvalidStockLevelsInCartException On error.
195-
*
196193
* @param WP_REST_Request $request Request object.
197-
*
198194
* @return WP_REST_Response
199195
*/
200196
protected function get_route_post_response( WP_REST_Request $request ) {
@@ -264,51 +260,36 @@ protected function get_route_post_response( WP_REST_Request $request ) {
264260
* @return WP_Error WP Error object.
265261
*/
266262
protected function get_route_error_response( $error_code, $error_message, $http_status_code = 500, $additional_data = [] ) {
267-
$error_from_message = new WP_Error(
268-
$error_code,
269-
$error_message
270-
);
271263
switch ( $http_status_code ) {
264+
case 400:
265+
return new WP_Error(
266+
$error_code,
267+
$error_message,
268+
array_merge(
269+
$additional_data,
270+
[
271+
'status' => $http_status_code,
272+
]
273+
)
274+
);
272275
case 409:
273-
// 409 is when there was a conflict, so we return the cart so the client can resolve it.
274-
return $this->add_data_to_error_object( $error_from_message, $additional_data, $http_status_code, true );
275-
}
276-
return $this->add_data_to_error_object( $error_from_message, $additional_data, $http_status_code );
277-
}
278-
279-
/**
280-
* Get route response when something went wrong.
281-
*
282-
* @param WP_Error $error_object User facing error message.
283-
* @param int $http_status_code HTTP status. Defaults to 500.
284-
* @param array $additional_data Extra data (key value pairs) to expose in the error response.
285-
* @return WP_Error WP Error object.
286-
*/
287-
protected function get_route_error_response_from_object( $error_object, $http_status_code = 500, $additional_data = [] ) {
288-
switch ( $http_status_code ) {
289-
case 409:
290-
// 409 is when there was a conflict, so we return the cart so the client can resolve it.
291-
return $this->add_data_to_error_object( $error_object, $additional_data, $http_status_code, true );
292-
}
293-
return $this->add_data_to_error_object( $error_object, $additional_data, $http_status_code );
294-
}
295-
296-
/**
297-
* Adds additional data to the WP_Error object.
298-
*
299-
* @param WP_Error $error The error object to add the cart to.
300-
* @param array $data The data to add to the error object.
301-
* @param int $http_status_code The HTTP status code this error should return.
302-
* @param bool $include_cart Whether the cart should be included in the error data.
303-
* @returns WP_Error The WP_Error with the cart added.
304-
*/
305-
private function add_data_to_error_object( $error, $data, $http_status_code, bool $include_cart = false ) {
306-
$data = array_merge( $data, [ 'status' => $http_status_code ] );
307-
if ( $include_cart ) {
308-
$data = array_merge( $data, [ 'cart' => wc()->api->get_endpoint_data( '/wc/store/cart' ) ] );
276+
// If there was a conflict, return the cart so the client can resolve it.
277+
$controller = new CartController();
278+
$cart = $controller->get_cart_instance();
279+
280+
return new WP_Error(
281+
$error_code,
282+
$error_message,
283+
array_merge(
284+
$additional_data,
285+
[
286+
'status' => $http_status_code,
287+
'cart' => wc()->api->get_endpoint_data( '/wc/store/cart' ),
288+
]
289+
)
290+
);
309291
}
310-
$error->add_data( $data );
311-
return $error;
292+
return new WP_Error( $error_code, $error_message, [ 'status' => $http_status_code ] );
312293
}
313294

314295
/**

src/StoreApi/Utilities/CartController.php

Lines changed: 34 additions & 140 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,7 @@
33

44
use Automattic\WooCommerce\Blocks\StoreApi\Routes\RouteException;
55
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\NoticeHandler;
6-
use Automattic\WooCommerce\Blocks\Utils\ArrayUtils;
76
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
8-
use WP_Error;
97

108
/**
119
* Woo Cart Controller class.
@@ -215,130 +213,17 @@ public function validate_add_to_cart( \WC_Product $product, $request ) {
215213
do_action( 'wooocommerce_store_api_validate_add_to_cart', $product, $request );
216214
}
217215

218-
/**
219-
* Generates the error message for out of stock products and adds product names to it.
220-
*
221-
* @param string $singular The message to use when only one product is in the list.
222-
* @param string $plural The message to use when more than one product is in the list.
223-
* @param array $items The list of cart items whose names should be inserted into the message.
224-
* @returns string The translated and correctly pluralised message.
225-
*/
226-
private function add_product_names_to_message( $singular, $plural, $items ) {
227-
$product_names = wc_list_pluck( $items, 'getProductName' );
228-
$message = ( count( $items ) > 1 ) ? $plural : $singular;
229-
return sprintf(
230-
$message,
231-
ArrayUtils::natural_language_join( $product_names, true )
232-
);
233-
}
234-
235216
/**
236217
* Validate all items in the cart and check for errors.
237218
*
238-
* @throws InvalidStockLevelsInCartException Exception if invalid data is detected due to insufficient stock levels.
219+
* @throws RouteException Exception if invalid data is detected.
239220
*/
240221
public function validate_cart_items() {
241222
$cart = $this->get_cart_instance();
242223
$cart_items = $this->get_cart_items();
243224

244-
$out_of_stock_products = [];
245-
$too_many_in_cart_products = [];
246-
$partial_out_of_stock_products = [];
247-
$not_purchasable_products = [];
248-
249225
foreach ( $cart_items as $cart_item_key => $cart_item ) {
250-
try {
251-
$this->validate_cart_item( $cart_item );
252-
} catch ( TooManyInCartException $error ) {
253-
$too_many_in_cart_products[] = $error;
254-
} catch ( NotPurchasableException $error ) {
255-
$not_purchasable_products[] = $error;
256-
} catch ( PartialOutOfStockException $error ) {
257-
$partial_out_of_stock_products[] = $error;
258-
} catch ( OutOfStockException $error ) {
259-
$out_of_stock_products[] = $error;
260-
}
261-
}
262-
263-
$error = new WP_Error();
264-
265-
if ( count( $out_of_stock_products ) > 0 ) {
266-
// translators: %s: product names.
267-
$singular_error = __(
268-
'%s is out of stock and cannot be purchased. It has been removed from your cart.',
269-
'woo-gutenberg-products-block'
270-
);
271-
// translators: %s: product names.
272-
$plural_error = __(
273-
'%s are out of stock and cannot be purchased. They have been removed from your cart.',
274-
'woo-gutenberg-products-block'
275-
);
276-
277-
$error->add(
278-
409,
279-
$this->add_product_names_to_message( $singular_error, $plural_error, $out_of_stock_products )
280-
);
281-
}
282-
283-
if ( count( $not_purchasable_products ) > 0 ) {
284-
// translators: %s: product names.
285-
$singular_error = __(
286-
'%s cannot be purchased. It has been removed from your cart.',
287-
'woo-gutenberg-products-block'
288-
);
289-
// translators: %s: product names.
290-
$plural_error = __(
291-
'%s cannot be purchased. They have been removed from your cart.',
292-
'woo-gutenberg-products-block'
293-
);
294-
295-
$error->add(
296-
409,
297-
$this->add_product_names_to_message( $singular_error, $plural_error, $not_purchasable_products )
298-
);
299-
}
300-
301-
if ( count( $too_many_in_cart_products ) > 0 ) {
302-
// translators: %s: product names.
303-
$singular_error = __(
304-
'There are too many %s in the cart. Only 1 can be purchased. The quantity in your cart has been reduced.',
305-
'woo-gutenberg-products-block'
306-
);
307-
// translators: %s: product names.
308-
$plural_error = __(
309-
'There are too many %s in the cart. Only 1 of each can be purchased. The quantities in your cart have been reduced.',
310-
'woo-gutenberg-products-block'
311-
);
312-
313-
$error->add(
314-
409,
315-
$this->add_product_names_to_message( $singular_error, $plural_error, $too_many_in_cart_products )
316-
);
317-
}
318-
319-
if ( count( $partial_out_of_stock_products ) > 0 ) {
320-
// translators: %s: product names.
321-
$singular_error = __(
322-
'There is not enough %s in stock. The quantity in your cart has been reduced.',
323-
'woo-gutenberg-products-block'
324-
);
325-
// translators: %s: product names.
326-
$plural_error = __(
327-
'There are not enough %s in stock. The quantities in your cart have been reduced.',
328-
'woo-gutenberg-products-block'
329-
);
330-
331-
$error->add(
332-
409,
333-
$this->add_product_names_to_message( $singular_error, $plural_error, $partial_out_of_stock_products )
334-
);
335-
}
336-
337-
if ( $error->has_errors() ) {
338-
throw new InvalidStockLevelsInCartException(
339-
'woocommerce_stock_availability_error',
340-
$error
341-
);
226+
$this->validate_cart_item( $cart_item );
342227
}
343228

344229
// Before running the woocommerce_check_cart_items hook, unhook validation from the core cart.
@@ -359,11 +244,8 @@ public function validate_cart_items() {
359244
/**
360245
* Validates an existing cart item and returns any errors.
361246
*
362-
* @throws TooManyInCartException Exception if more than one product that can only be purchased individually is in
363-
* the cart.
364-
* @throws PartialOutOfStockException Exception if an item has a quantity greater than what is available in stock.
365-
* @throws OutOfStockException Exception thrown when an item is entirely out of stock.
366-
* @throws NotPurchasableException Exception thrown when an item is not purchasable.
247+
* @throws RouteException Exception if invalid data is detected.
248+
*
367249
* @param array $cart_item Cart item array.
368250
*/
369251
public function validate_cart_item( $cart_item ) {
@@ -374,25 +256,30 @@ public function validate_cart_item( $cart_item ) {
374256
}
375257

376258
if ( ! $product->is_purchasable() ) {
377-
throw new NotPurchasableException(
378-
'woocommerce_rest_cart_product_not_purchasable',
379-
$product->get_name()
380-
);
259+
$this->throw_default_product_exception( $product );
381260
}
382261

383262
if ( $product->is_sold_individually() && $cart_item['quantity'] > 1 ) {
384-
WC()->cart->set_quantity( $cart_item['key'], 1, false );
385-
throw new TooManyInCartException(
263+
throw new RouteException(
386264
'woocommerce_rest_cart_product_sold_individually',
387-
$product->get_name()
265+
sprintf(
266+
/* translators: %s: product name */
267+
__( 'There are too many &quot;%s&quot; in the cart. Only 1 can be purchased.', 'woo-gutenberg-products-block' ),
268+
$product->get_name()
269+
),
270+
400
388271
);
389272
}
390273

391274
if ( ! $product->is_in_stock() ) {
392-
WC()->cart->remove_cart_item( $cart_item['key'] );
393-
throw new OutOfStockException(
275+
throw new RouteException(
394276
'woocommerce_rest_cart_product_no_stock',
395-
$product->get_name()
277+
sprintf(
278+
/* translators: %s: product name */
279+
__( '&quot;%s&quot; is out of stock and cannot be purchased.', 'woo-gutenberg-products-block' ),
280+
$product->get_name()
281+
),
282+
400
396283
);
397284
}
398285

@@ -401,11 +288,20 @@ public function validate_cart_item( $cart_item ) {
401288
$qty_in_cart = $this->get_product_quantity_in_cart( $product );
402289

403290
if ( $qty_remaining < $qty_in_cart ) {
404-
405-
WC()->cart->set_quantity( $cart_item['key'], $qty_remaining, false );
406-
throw new PartialOutOfStockException(
407-
'woocommerce_rest_cart_product_partially_no_stock',
408-
$product->get_name()
291+
throw new RouteException(
292+
'woocommerce_rest_cart_product_no_stock',
293+
sprintf(
294+
/* translators: 1: quantity in stock, 2: product name */
295+
_n(
296+
'There is only %1$s unit of &quot;%2$s&quot; in stock.',
297+
'There are only %1$s units of &quot;%2$s&quot; in stock.',
298+
$qty_remaining,
299+
'woo-gutenberg-products-block'
300+
),
301+
wc_format_stock_quantity_for_display( $qty_remaining, $product ),
302+
$product->get_name()
303+
),
304+
400
409305
);
410306
}
411307
}
@@ -447,9 +343,7 @@ public function get_cart_item_errors() {
447343
try {
448344
$this->validate_cart_item( $cart_item );
449345
} catch ( RouteException $error ) {
450-
$errors[] = new WP_Error( $error->getErrorCode(), $error->getMessage() );
451-
} catch ( StockAvailabilityException $error ) {
452-
$errors[] = new WP_Error( $error->getErrorCode(), $error->getMessage() );
346+
$errors[] = new \WP_Error( $error->getErrorCode(), $error->getMessage() );
453347
}
454348
}
455349

0 commit comments

Comments
 (0)