Move Draft Order Handling to Shared Trait (DraftOrderTrait) (https://github.com/woocommerce/woocommerce-blocks/pull/5323)

* Introduce shared trait for draft order handling (DraftOrderTrait)

* Use trait where needed for maybe_release_stock

* Introduce woocommerce_blocks_cart_update_customer_from_request

This hook gets the request data and customer object, and from there, 3rd parties could grab a draft order and manually update it if needed (if the use cases requires this).

* Revert "Introduce woocommerce_blocks_cart_update_customer_from_request"

This reverts commit 6620818eb1e03b87c6eca0d757ec8b78802e3c57.
This commit is contained in:
Mike Jolley 2021-12-10 10:49:04 +00:00 committed by GitHub
parent 29fa0a61e6
commit b0f9e524a3
8 changed files with 109 additions and 79 deletions

View File

@ -1,9 +1,9 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
/**
* Abstract Cart Route
@ -112,21 +112,6 @@ abstract class AbstractCartRoute extends AbstractRoute {
wc()->cart->calculate_totals();
}
/**
* If there is a draft order, releases stock.
*
* @return void
*/
protected function maybe_release_stock() {
$draft_order = wc()->session->get( 'store_api_draft_order', 0 );
if ( ! $draft_order ) {
return;
}
wc_release_stock_for_order( $draft_order );
}
/**
* For non-GET endpoints, require and validate a nonce to prevent CSRF attacks.
*

View File

@ -1,12 +1,16 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
/**
* CartRemoveItem class.
*
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
*/
class CartRemoveItem extends AbstractCartRoute {
use DraftOrderTrait;
/**
* Get the path of this REST route.
*
@ -59,4 +63,19 @@ class CartRemoveItem extends AbstractCartRoute {
return rest_ensure_response( $this->schema->get_item_response( $cart ) );
}
/**
* If there is a draft order, releases stock.
*
* @return void
*/
protected function maybe_release_stock() {
$draft_order_id = $this->get_draft_order_id();
if ( ! $draft_order_id ) {
return;
}
wc_release_stock_for_order( $draft_order_id );
}
}

View File

@ -1,9 +1,7 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\BillingAddressSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\ShippingAddressSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
/**
* CartUpdateCustomer class.
@ -13,6 +11,8 @@ use Automattic\WooCommerce\Blocks\StoreApi\Schemas\ShippingAddressSchema;
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
*/
class CartUpdateCustomer extends AbstractCartRoute {
use DraftOrderTrait;
/**
* Get the namespace for this route.
*
@ -128,13 +128,13 @@ class CartUpdateCustomer extends AbstractCartRoute {
}
/**
* If there is a draft order, update customer data there also.
* If there is a draft order, update the customer data within that also so the
* cart and order are kept in sync.
*
* @return void
*/
protected function maybe_update_order() {
$draft_order_id = wc()->session->get( 'store_api_draft_order', 0 );
$draft_order = $draft_order_id ? wc_get_order( $draft_order_id ) : false;
$draft_order = $this->get_draft_order();
if ( ! $draft_order ) {
return;

View File

@ -1,17 +1,18 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Routes;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\Domain\Services\CreateAccount;
use Automattic\WooCommerce\Blocks\Package;
use Automattic\WooCommerce\Blocks\Payments\PaymentContext;
use Automattic\WooCommerce\Blocks\Payments\PaymentResult;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\AbstractSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Schemas\CartSchema;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\CartController;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\InvalidStockLevelsInCartException;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\OrderController;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStockException;
use Automattic\WooCommerce\Blocks\Payments\PaymentResult;
use Automattic\WooCommerce\Blocks\Payments\PaymentContext;
/**
* Checkout class.
@ -19,6 +20,8 @@ use Automattic\WooCommerce\Blocks\Payments\PaymentContext;
* @internal This API is used internally by Blocks--it is still in flux and may be subject to revisions.
*/
class Checkout extends AbstractCartRoute {
use DraftOrderTrait;
/**
* Holds the current order being processed.
*
@ -349,58 +352,15 @@ class Checkout extends AbstractCartRoute {
return $error;
}
/**
* Gets draft order data from the customer session.
*
* @return array
*/
private function get_draft_order_id() {
return wc()->session->get( 'store_api_draft_order', 0 );
}
/**
* Updates draft order data in the customer session.
*
* @param integer $order_id Draft order ID.
*/
private function set_draft_order_id( $order_id ) {
wc()->session->set( 'store_api_draft_order', $order_id );
}
/**
* Whether the passed argument is a draft order or an order that is
* pending/failed and the cart hasn't changed.
*
* @param \WC_Order $order_object Order object to check.
* @return boolean Whether the order is valid as a draft order.
*/
private function is_valid_draft_order( $order_object ) {
if ( ! $order_object instanceof \WC_Order ) {
return false;
}
// Draft orders are okay.
if ( $order_object->has_status( 'checkout-draft' ) ) {
return true;
}
// Pending and failed orders can be retried if the cart hasn't changed.
if ( $order_object->needs_payment() && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) {
return true;
}
return false;
}
/**
* Create or update a draft order based on the cart.
*
* @throws RouteException On error.
*/
private function create_or_update_draft_order() {
$this->order = $this->get_draft_order_id() ? wc_get_order( $this->get_draft_order_id() ) : null;
$this->order = $this->get_draft_order();
if ( ! $this->is_valid_draft_order( $this->order ) ) {
if ( ! $this->order ) {
$this->order = $this->order_controller->create_order_from_cart();
} else {
$this->order_controller->update_order_from_cart( $this->order );
@ -466,7 +426,6 @@ class Checkout extends AbstractCartRoute {
$reserve_stock = new ReserveStock();
$reserve_stock->reserve_stock_for_order( $this->order, 10 );
} catch ( ReserveStockException $e ) {
$error_data = $e->getErrorData();
throw new RouteException(
$e->getErrorCode(),
$e->getMessage(),

View File

@ -40,14 +40,14 @@ class RoutesController {
/**
* Get a route class instance.
*
* @throws Exception If the schema does not exist.
* @throws \Exception If the schema does not exist.
*
* @param string $name Name of schema.
* @return AbstractRoute
*/
public function get( $name ) {
if ( ! isset( $this->routes[ $name ] ) ) {
throw new Exception( $name . ' route does not exist' );
throw new \Exception( $name . ' route does not exist' );
}
return $this->routes[ $name ];
}

View File

@ -1,8 +1,8 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Schemas;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
/**
* CartItemSchema class.
*
@ -10,6 +10,8 @@ use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
* @since 2.5.0
*/
class CartItemSchema extends ProductSchema {
use DraftOrderTrait;
/**
* The schema item name.
*
@ -374,9 +376,8 @@ class CartItemSchema extends ProductSchema {
return null;
}
$draft_order = wc()->session->get( 'store_api_draft_order', 0 );
$reserve_stock = new ReserveStock();
$reserved_stock = $reserve_stock->get_reserved_stock( $product, $draft_order );
$reserved_stock = $reserve_stock->get_reserved_stock( $product, $this->get_draft_order_id() );
return $product->get_stock_quantity() - $reserved_stock;
}

View File

@ -2,6 +2,7 @@
namespace Automattic\WooCommerce\Blocks\StoreApi\Utilities;
use Automattic\WooCommerce\Blocks\StoreApi\Routes\RouteException;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\DraftOrderTrait;
use Automattic\WooCommerce\Blocks\StoreApi\Utilities\NoticeHandler;
use Automattic\WooCommerce\Blocks\Utils\ArrayUtils;
use Automattic\WooCommerce\Checkout\Helpers\ReserveStock;
@ -15,6 +16,8 @@ use WP_Error;
* @since 2.5.0
*/
class CartController {
use DraftOrderTrait;
/**
* Makes the cart and sessions available to a route by loading them from core.
*/
@ -963,8 +966,7 @@ class CartController {
*/
protected function get_remaining_stock_for_product( $product ) {
$reserve_stock = new ReserveStock();
$draft_order = wc()->session->get( 'store_api_draft_order', 0 );
$qty_reserved = $reserve_stock->get_reserved_stock( $product, $draft_order );
$qty_reserved = $reserve_stock->get_reserved_stock( $product, $this->get_draft_order_id() );
return $product->get_stock_quantity() - $qty_reserved;
}

View File

@ -0,0 +1,64 @@
<?php
namespace Automattic\WooCommerce\Blocks\StoreApi\Utilities;
/**
* DraftOrderTrait
*
* Shared functionality for getting and setting draft order IDs from session.
*/
trait DraftOrderTrait {
/**
* Gets draft order data from the customer session.
*
* @return integer
*/
protected function get_draft_order_id() {
return wc()->session->get( 'store_api_draft_order', 0 );
}
/**
* Updates draft order data in the customer session.
*
* @param integer $order_id Draft order ID.
*/
protected function set_draft_order_id( $order_id ) {
wc()->session->set( 'store_api_draft_order', $order_id );
}
/**
* Uses the draft order ID to return an order object, if valid.
*
* @return \WC_Order|null;
*/
protected function get_draft_order() {
$draft_order_id = $this->get_draft_order_id();
$draft_order = $draft_order_id ? wc_get_order( $draft_order_id ) : false;
return $this->is_valid_draft_order( $draft_order ) ? $draft_order : null;
}
/**
* Whether the passed argument is a draft order or an order that is
* pending/failed and the cart hasn't changed.
*
* @param \WC_Order $order_object Order object to check.
* @return boolean Whether the order is valid as a draft order.
*/
protected function is_valid_draft_order( $order_object ) {
if ( ! $order_object instanceof \WC_Order ) {
return false;
}
// Draft orders are okay.
if ( $order_object->has_status( 'checkout-draft' ) ) {
return true;
}
// Pending and failed orders can be retried if the cart hasn't changed.
if ( $order_object->needs_payment() && $order_object->has_cart_hash( wc()->cart->get_cart_hash() ) ) {
return true;
}
return false;
}
}