Disallow selecting quantity of stock past what's available. (https://github.com/woocommerce/woocommerce-blocks/pull/1905)
- `useStoreCartItemQuantity` now receives the cartItem instead of the `cartItemKey` as an argument. I didn't notice in previous reviews how it's used in the context where we already have a cartItem so implementing this reduces complexity and makes the hook more precise for it's purpose. - Add `backorders_allowed` to the CartItem schema for the API. This allows for client to have correct logic for maximum quantity when this value is true. - Implement the above in the `CartLineItemRow` so that quantity picker is limited by the amount of stock remaining if that is available (`lowStockRemaining`) and the `backorders_allowed` is false. - maximum quantity is currently hardcoded to a (filtered) value of `99` when other conditions don't apply (see related issue in woocommerce/woocommerce-blocks#1913)
This commit is contained in:
parent
2f58d86fa6
commit
0cacf5e0da
|
@ -6,11 +6,6 @@ import { useState, useEffect } from '@wordpress/element';
|
||||||
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
import { CART_STORE_KEY as storeKey } from '@woocommerce/block-data';
|
||||||
import { useDebounce } from 'use-debounce';
|
import { useDebounce } from 'use-debounce';
|
||||||
|
|
||||||
/**
|
|
||||||
* Internal dependencies
|
|
||||||
*/
|
|
||||||
import { useStoreCart } from './use-store-cart';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {import('@woocommerce/type-defs/hooks').StoreCartItemQuantity} StoreCartItemQuantity
|
* @typedef {import('@woocommerce/type-defs/hooks').StoreCartItemQuantity} StoreCartItemQuantity
|
||||||
* @typedef {import('@woocommerce/type-defs/cart').CartItem} CartItem
|
* @typedef {import('@woocommerce/type-defs/cart').CartItem} CartItem
|
||||||
|
@ -22,18 +17,12 @@ import { useStoreCart } from './use-store-cart';
|
||||||
*
|
*
|
||||||
* @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/master/src/RestApi/StoreApi
|
* @see https://github.com/woocommerce/woocommerce-gutenberg-products-block/tree/master/src/RestApi/StoreApi
|
||||||
*
|
*
|
||||||
* @param {string} cartItemKey Key for a cart item.
|
* @param {CartItem} cartItem The cartItem to get quantity info from and
|
||||||
* @return {StoreCartItemQuantity} An object exposing data and actions relating to cart items.
|
* will have quantity updated on.
|
||||||
|
* @return {StoreCartItemQuantity} An object exposing data and actions relating
|
||||||
|
* to cart items.
|
||||||
*/
|
*/
|
||||||
export const useStoreCartItemQuantity = ( cartItemKey ) => {
|
export const useStoreCartItemQuantity = ( cartItem ) => {
|
||||||
const { cartItems, cartIsLoading } = useStoreCart();
|
|
||||||
/**
|
|
||||||
* @type {[CartItem, function( CartItem ):undefined]}
|
|
||||||
*/
|
|
||||||
const [ cartItem, setCartItem ] = useState( {
|
|
||||||
key: '',
|
|
||||||
quantity: 0,
|
|
||||||
} );
|
|
||||||
// Store quantity in hook state. This is used to keep the UI
|
// Store quantity in hook state. This is used to keep the UI
|
||||||
// updated while server request is updated.
|
// updated while server request is updated.
|
||||||
const [ quantity, changeQuantity ] = useState( cartItem.quantity );
|
const [ quantity, changeQuantity ] = useState( cartItem.quantity );
|
||||||
|
@ -41,43 +30,28 @@ export const useStoreCartItemQuantity = ( cartItemKey ) => {
|
||||||
const isPending = useSelect(
|
const isPending = useSelect(
|
||||||
( select ) => {
|
( select ) => {
|
||||||
const store = select( storeKey );
|
const store = select( storeKey );
|
||||||
return store.isItemQuantityPending( cartItemKey );
|
return store.isItemQuantityPending( cartItem.key );
|
||||||
},
|
},
|
||||||
[ cartItemKey ]
|
[ cartItem.key ]
|
||||||
);
|
);
|
||||||
useEffect( () => {
|
|
||||||
if ( ! cartIsLoading ) {
|
|
||||||
const foundCartItem = cartItems.find(
|
|
||||||
( item ) => item.key === cartItemKey
|
|
||||||
);
|
|
||||||
if ( foundCartItem ) {
|
|
||||||
setCartItem( foundCartItem );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [ cartItems, cartIsLoading, cartItemKey ] );
|
|
||||||
|
|
||||||
const { removeItemFromCart, changeCartItemQuantity } = useDispatch(
|
const { removeItemFromCart, changeCartItemQuantity } = useDispatch(
|
||||||
storeKey
|
storeKey
|
||||||
);
|
);
|
||||||
const removeItem = () => {
|
const removeItem = () => {
|
||||||
removeItemFromCart( cartItemKey );
|
removeItemFromCart( cartItem.key );
|
||||||
};
|
};
|
||||||
|
|
||||||
// Observe debounced quantity value, fire action to update server when it
|
// Observe debounced quantity value, fire action to update server when it
|
||||||
// changes.
|
// changes.
|
||||||
useEffect( () => {
|
useEffect( () => {
|
||||||
if ( debouncedQuantity === 0 ) {
|
changeCartItemQuantity( cartItem.key, debouncedQuantity );
|
||||||
changeQuantity( cartItem.quantity );
|
}, [ debouncedQuantity, cartItem.key ] );
|
||||||
return;
|
|
||||||
}
|
|
||||||
changeCartItemQuantity( cartItemKey, debouncedQuantity );
|
|
||||||
}, [ debouncedQuantity, cartItemKey, cartItem.quantity ] );
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
isPending,
|
isPending,
|
||||||
quantity,
|
quantity,
|
||||||
changeQuantity,
|
changeQuantity,
|
||||||
removeItem,
|
removeItem,
|
||||||
isLoading: cartIsLoading,
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,6 +9,7 @@ import FormattedMonetaryAmount from '@woocommerce/base-components/formatted-mone
|
||||||
import { getCurrency, formatPrice } from '@woocommerce/base-utils';
|
import { getCurrency, formatPrice } from '@woocommerce/base-utils';
|
||||||
import { useStoreCartItemQuantity } from '@woocommerce/base-hooks';
|
import { useStoreCartItemQuantity } from '@woocommerce/base-hooks';
|
||||||
import { Icon, trash } from '@woocommerce/icons';
|
import { Icon, trash } from '@woocommerce/icons';
|
||||||
|
import { getSetting } from '@woocommerce/settings';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Internal dependencies
|
* Internal dependencies
|
||||||
|
@ -17,18 +18,42 @@ import ProductVariationData from './product-variation-data';
|
||||||
import ProductImage from './product-image';
|
import ProductImage from './product-image';
|
||||||
import ProductLowStockBadge from './product-low-stock-badge';
|
import ProductLowStockBadge from './product-low-stock-badge';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {import('@woocommerce/type-defs/cart').CartItem} CartItem
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {boolean} backOrdersAllowed Whether to allow backorders or not.
|
||||||
|
* @param {number|null} lowStockAmount If present the number of stock
|
||||||
|
* remaining.
|
||||||
|
*
|
||||||
|
* @return {number} The maximum number value for the quantity input.
|
||||||
|
*/
|
||||||
|
const getMaximumQuantity = ( backOrdersAllowed, lowStockAmount ) => {
|
||||||
|
const maxQuantityLimit = getSetting( 'quantitySelectLimit', 99 );
|
||||||
|
if ( backOrdersAllowed || ! lowStockAmount ) {
|
||||||
|
return maxQuantityLimit;
|
||||||
|
}
|
||||||
|
return Math.min( lowStockAmount, maxQuantityLimit );
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cart line item table row component.
|
* Cart line item table row component.
|
||||||
*/
|
*/
|
||||||
const CartLineItemRow = ( { lineItem = {} } ) => {
|
const CartLineItemRow = ( { lineItem } ) => {
|
||||||
|
/**
|
||||||
|
* @type {CartItem}
|
||||||
|
*/
|
||||||
const {
|
const {
|
||||||
key = '',
|
name,
|
||||||
name = '',
|
summary,
|
||||||
summary = '',
|
low_stock_remaining: lowStockRemaining,
|
||||||
permalink = '',
|
backorders_allowed: backOrdersAllowed,
|
||||||
images = [],
|
permalink,
|
||||||
variation = [],
|
images,
|
||||||
prices = {},
|
variation,
|
||||||
|
prices,
|
||||||
} = lineItem;
|
} = lineItem;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
|
@ -36,9 +61,9 @@ const CartLineItemRow = ( { lineItem = {} } ) => {
|
||||||
changeQuantity,
|
changeQuantity,
|
||||||
removeItem,
|
removeItem,
|
||||||
isPending: itemQuantityDisabled,
|
isPending: itemQuantityDisabled,
|
||||||
} = useStoreCartItemQuantity( key );
|
} = useStoreCartItemQuantity( lineItem );
|
||||||
|
|
||||||
const currency = getCurrency();
|
const currency = getCurrency( prices );
|
||||||
const regularPrice = parseInt( prices.regular_price, 10 ) * quantity;
|
const regularPrice = parseInt( prices.regular_price, 10 ) * quantity;
|
||||||
const purchasePrice = parseInt( prices.price, 10 ) * quantity;
|
const purchasePrice = parseInt( prices.price, 10 ) * quantity;
|
||||||
const saleAmount = regularPrice - purchasePrice;
|
const saleAmount = regularPrice - purchasePrice;
|
||||||
|
@ -57,9 +82,7 @@ const CartLineItemRow = ( { lineItem = {} } ) => {
|
||||||
>
|
>
|
||||||
{ name }
|
{ name }
|
||||||
</a>
|
</a>
|
||||||
<ProductLowStockBadge
|
<ProductLowStockBadge lowStockRemaining={ lowStockRemaining } />
|
||||||
lowStockRemaining={ lineItem.low_stock_remaining }
|
|
||||||
/>
|
|
||||||
<div className="wc-block-cart-item__product-metadata">
|
<div className="wc-block-cart-item__product-metadata">
|
||||||
<RawHTML>{ summary }</RawHTML>
|
<RawHTML>{ summary }</RawHTML>
|
||||||
<ProductVariationData variation={ variation } />
|
<ProductVariationData variation={ variation } />
|
||||||
|
@ -69,7 +92,10 @@ const CartLineItemRow = ( { lineItem = {} } ) => {
|
||||||
<QuantitySelector
|
<QuantitySelector
|
||||||
disabled={ itemQuantityDisabled }
|
disabled={ itemQuantityDisabled }
|
||||||
quantity={ quantity }
|
quantity={ quantity }
|
||||||
maximum={ lineItem.sold_individually ? 1 : undefined }
|
maximum={ getMaximumQuantity(
|
||||||
|
backOrdersAllowed,
|
||||||
|
lowStockRemaining
|
||||||
|
) }
|
||||||
onChange={ changeQuantity }
|
onChange={ changeQuantity }
|
||||||
itemName={ name }
|
itemName={ name }
|
||||||
/>
|
/>
|
||||||
|
@ -121,8 +147,9 @@ CartLineItemRow.propTypes = {
|
||||||
name: PropTypes.string.isRequired,
|
name: PropTypes.string.isRequired,
|
||||||
summary: PropTypes.string.isRequired,
|
summary: PropTypes.string.isRequired,
|
||||||
images: PropTypes.array.isRequired,
|
images: PropTypes.array.isRequired,
|
||||||
low_stock_remaining: PropTypes.number,
|
low_stock_remaining: PropTypes.number.isRequired,
|
||||||
sold_individually: PropTypes.bool,
|
backorders_allowed: PropTypes.bool.isRequired,
|
||||||
|
sold_individually: PropTypes.bool.isRequired,
|
||||||
variation: PropTypes.arrayOf(
|
variation: PropTypes.arrayOf(
|
||||||
PropTypes.shape( {
|
PropTypes.shape( {
|
||||||
attribute: PropTypes.string.isRequired,
|
attribute: PropTypes.string.isRequired,
|
||||||
|
|
|
@ -92,31 +92,40 @@
|
||||||
* @property {string} line_total_tax Line total tax.
|
* @property {string} line_total_tax Line total tax.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @typedef {Object} CartItemPriceRange
|
||||||
|
*
|
||||||
|
* @property {string} min_amount Price min amount in range.
|
||||||
|
* @property {string} max_amount Price max amount in range.
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} CartItemPrices
|
* @typedef {Object} CartItemPrices
|
||||||
*
|
*
|
||||||
* @property {string} currency_code The ISO code for the currency.
|
* @property {string} currency_code The ISO code for the
|
||||||
* @property {number} currency_minor_unit The precision (decimal
|
* currency.
|
||||||
* places).
|
* @property {number} currency_minor_unit The precision (decimal
|
||||||
* @property {string} currency_symbol The symbol for the currency
|
* places).
|
||||||
* (eg '$')
|
* @property {string} currency_symbol The symbol for the
|
||||||
* @property {string} currency_prefix Price prefix for the currency
|
* currency (eg '$')
|
||||||
* which can be used to format
|
* @property {string} currency_prefix Price prefix for the
|
||||||
* returned prices.
|
* currency which can be
|
||||||
* @property {string} currency_suffix Price suffix for the currency
|
* used to format returned
|
||||||
* which can be used to format
|
* prices.
|
||||||
* returned prices.
|
* @property {string} currency_suffix Price suffix for the
|
||||||
* @property {string} currency_decimal_separator The string used for the
|
* currency which can be
|
||||||
* decimal separator.
|
* used to format returned
|
||||||
* @property {string} currency_thousand_separator The string used for the
|
* prices.
|
||||||
* thousands separator.
|
* @property {string} currency_decimal_separator The string used for the
|
||||||
* @property {string} price Current product price.
|
* decimal separator.
|
||||||
* @property {string} regular_price Regular product price.
|
* @property {string} currency_thousand_separator The string used for the
|
||||||
* @property {string} sale_price Sale product price, if
|
* thousands separator.
|
||||||
* applicable.
|
* @property {string} price Current product price.
|
||||||
* @property {Object} price_range Price range, if applicable.
|
* @property {string} regular_price Regular product price.
|
||||||
* @property {string} price_range.min_amount Price min amount in range.
|
* @property {string} sale_price Sale product price, if
|
||||||
* @property {string} price_range.max_amount Price max amount in range.
|
* applicable.
|
||||||
|
* @property {CartItemPriceRange|null} price_range Price range, if
|
||||||
|
* applicable.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
@ -140,6 +149,9 @@
|
||||||
* @property {number|null} low_stock_remaining Quantity left in stock if
|
* @property {number|null} low_stock_remaining Quantity left in stock if
|
||||||
* stock is low, or null if
|
* stock is low, or null if
|
||||||
* not applicable.
|
* not applicable.
|
||||||
|
* @property {boolean} backorders_allowed True if backorders are
|
||||||
|
* allowed past stock
|
||||||
|
* availability.
|
||||||
* @property {boolean} sold_individually If true, only one item of
|
* @property {boolean} sold_individually If true, only one item of
|
||||||
* this product is allowed
|
* this product is allowed
|
||||||
* for purchase in a single
|
* for purchase in a single
|
||||||
|
@ -150,6 +162,7 @@
|
||||||
* product/variation.
|
* product/variation.
|
||||||
* @property {CartItemVariation[]} variation Chosen attributes (for
|
* @property {CartItemVariation[]} variation Chosen attributes (for
|
||||||
* variations).
|
* variations).
|
||||||
|
* @property {CartItemPrices} prices Item prices.
|
||||||
* @property {CartItemTotals} totals Item total amounts
|
* @property {CartItemTotals} totals Item total amounts
|
||||||
* provided using the
|
* provided using the
|
||||||
* smallest unit of the
|
* smallest unit of the
|
||||||
|
|
|
@ -31,13 +31,12 @@
|
||||||
/**
|
/**
|
||||||
* @typedef {Object} StoreCartItemQuantity
|
* @typedef {Object} StoreCartItemQuantity
|
||||||
*
|
*
|
||||||
* @property {boolean} isLoading True when cart items are being
|
* @property {number} quantity The quantity of the item in the cart.
|
||||||
* loaded.
|
* @property {boolean} isPending Whether the cart item is updating or
|
||||||
* @property {number} quantity The quantity of the item in the cart.
|
* not.
|
||||||
* @property {boolean} isPending Whether the cart item is updating or not.
|
* @property {Function} changeQuantity Callback for changing quantity of
|
||||||
* @property {Function} changeQuantity Callback for changing quantity of item
|
* item in cart.
|
||||||
* in cart.
|
* @property {Function} removeItem Callback for removing a cart item.
|
||||||
* @property {Function} removeItem Callback for removing a cart item.
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export {};
|
export {};
|
||||||
|
|
|
@ -58,6 +58,16 @@ class Cart extends AbstractBlock {
|
||||||
if ( ! $data_registry->exists( 'cartData' ) ) {
|
if ( ! $data_registry->exists( 'cartData' ) ) {
|
||||||
$data_registry->add( 'cartData', WC()->api->get_endpoint_data( '/wc/store/cart' ) );
|
$data_registry->add( 'cartData', WC()->api->get_endpoint_data( '/wc/store/cart' ) );
|
||||||
}
|
}
|
||||||
|
if ( ! $data_registry->exists( 'quantitySelectLimit' ) ) {
|
||||||
|
/**
|
||||||
|
* Note: this filter will be deprecated if/when quantity select limits
|
||||||
|
* are added at the product level.
|
||||||
|
*
|
||||||
|
* @return {integer} $max_quantity_limit Maximum quantity of products that can be selected in the cart.
|
||||||
|
*/
|
||||||
|
$max_quantity_limit = apply_filters( 'woocommerce_maximum_quantity_selected_cart', 99 );
|
||||||
|
$data_registry->add( 'quantitySelectLimit', $max_quantity_limit );
|
||||||
|
}
|
||||||
\Automattic\WooCommerce\Blocks\Assets::register_block_script(
|
\Automattic\WooCommerce\Blocks\Assets::register_block_script(
|
||||||
$this->block_name . '-frontend',
|
$this->block_name . '-frontend',
|
||||||
$this->block_name . '-block-frontend'
|
$this->block_name . '-block-frontend'
|
||||||
|
|
|
@ -104,6 +104,12 @@ class CartItemSchema extends AbstractSchema {
|
||||||
'context' => [ 'view', 'edit' ],
|
'context' => [ 'view', 'edit' ],
|
||||||
'readonly' => true,
|
'readonly' => true,
|
||||||
],
|
],
|
||||||
|
'backorders_allowed' => [
|
||||||
|
'description' => __( 'True if backorders are allowed past stock availability.', 'woo-gutenberg-products-block' ),
|
||||||
|
'type' => [ 'boolean' ],
|
||||||
|
'context' => [ 'view', 'edit' ],
|
||||||
|
'readonly' => true,
|
||||||
|
],
|
||||||
'sold_individually' => [
|
'sold_individually' => [
|
||||||
'description' => __( 'If true, only one item of this product is allowed for purchase in a single order.', 'woo-gutenberg-products-block' ),
|
'description' => __( 'If true, only one item of this product is allowed for purchase in a single order.', 'woo-gutenberg-products-block' ),
|
||||||
'type' => 'boolean',
|
'type' => 'boolean',
|
||||||
|
@ -304,6 +310,7 @@ class CartItemSchema extends AbstractSchema {
|
||||||
'description' => $this->prepare_html_response( wc_format_content( $product->get_description() ) ),
|
'description' => $this->prepare_html_response( wc_format_content( $product->get_description() ) ),
|
||||||
'sku' => $this->prepare_html_response( $product->get_sku() ),
|
'sku' => $this->prepare_html_response( $product->get_sku() ),
|
||||||
'low_stock_remaining' => $this->get_low_stock_remaining( $product ),
|
'low_stock_remaining' => $this->get_low_stock_remaining( $product ),
|
||||||
|
'backorders_allowed' => $product->backorders_allowed(),
|
||||||
'sold_individually' => $product->is_sold_individually(),
|
'sold_individually' => $product->is_sold_individually(),
|
||||||
'permalink' => $product->get_permalink(),
|
'permalink' => $product->get_permalink(),
|
||||||
'images' => ( new ProductImages() )->images_to_array( $product ),
|
'images' => ( new ProductImages() )->images_to_array( $product ),
|
||||||
|
@ -322,14 +329,14 @@ class CartItemSchema extends AbstractSchema {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If a product has low stock, return the remaining stock amount for display.
|
* Returns the remaining stock for a product if it has stock.
|
||||||
*
|
*
|
||||||
* Note; unlike the products API, this also factors in draft orders so the results are more up to date.
|
* This also factors in draft orders.
|
||||||
*
|
*
|
||||||
* @param \WC_Product $product Product instance.
|
* @param \WC_Product $product Product instance.
|
||||||
* @return integer|null
|
* @return integer|null
|
||||||
*/
|
*/
|
||||||
protected function get_low_stock_remaining( \WC_Product $product ) {
|
protected function get_remaining_stock( \WC_Product $product ) {
|
||||||
if ( is_null( $product->get_stock_quantity() ) ) {
|
if ( is_null( $product->get_stock_quantity() ) ) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -343,10 +350,25 @@ class CartItemSchema extends AbstractSchema {
|
||||||
$reserve_stock = new \Automattic\WooCommerce\Blocks\RestApi\StoreApi\Utilities\ReserveStock();
|
$reserve_stock = new \Automattic\WooCommerce\Blocks\RestApi\StoreApi\Utilities\ReserveStock();
|
||||||
}
|
}
|
||||||
|
|
||||||
$reserved_stock = $reserve_stock->get_reserved_stock( $product, isset( $draft_order['id'] ) ? $draft_order['id'] : 0 );
|
$reserved_stock = $reserve_stock->get_reserved_stock( $product, isset( $draft_order['id'] ) ? $draft_order['id'] : 0 );
|
||||||
$remaining_stock = $product->get_stock_quantity() - $reserved_stock;
|
return $product->get_stock_quantity() - $reserved_stock;
|
||||||
|
}
|
||||||
|
|
||||||
if ( $remaining_stock <= wc_get_low_stock_amount( $product ) ) {
|
/**
|
||||||
|
* If a product has low stock, return the remaining stock amount for display.
|
||||||
|
*
|
||||||
|
* Note; unlike the products API, this also factors in draft orders so the results are more up to date.
|
||||||
|
*
|
||||||
|
* @param \WC_Product $product Product instance.
|
||||||
|
* @return integer|null
|
||||||
|
*/
|
||||||
|
protected function get_low_stock_remaining( \WC_Product $product ) {
|
||||||
|
$remaining_stock = $this->get_remaining_stock( $product );
|
||||||
|
|
||||||
|
if (
|
||||||
|
null !== $remaining_stock
|
||||||
|
&& $remaining_stock <= wc_get_low_stock_amount( $product )
|
||||||
|
) {
|
||||||
return $remaining_stock;
|
return $remaining_stock;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -215,6 +215,7 @@ class CartItems extends TestCase {
|
||||||
$this->assertArrayHasKey( 'short_description', $schema['properties'] );
|
$this->assertArrayHasKey( 'short_description', $schema['properties'] );
|
||||||
$this->assertArrayHasKey( 'sku', $schema['properties'] );
|
$this->assertArrayHasKey( 'sku', $schema['properties'] );
|
||||||
$this->assertArrayHasKey( 'low_stock_remaining', $schema['properties'] );
|
$this->assertArrayHasKey( 'low_stock_remaining', $schema['properties'] );
|
||||||
|
$this->assertArrayHasKey( 'backorders_allowed', $schema['properties'] );
|
||||||
$this->assertArrayHasKey( 'permalink', $schema['properties'] );
|
$this->assertArrayHasKey( 'permalink', $schema['properties'] );
|
||||||
$this->assertArrayHasKey( 'images', $schema['properties'] );
|
$this->assertArrayHasKey( 'images', $schema['properties'] );
|
||||||
$this->assertArrayHasKey( 'totals', $schema['properties'] );
|
$this->assertArrayHasKey( 'totals', $schema['properties'] );
|
||||||
|
@ -240,6 +241,7 @@ class CartItems extends TestCase {
|
||||||
$this->assertArrayHasKey( 'totals', $data );
|
$this->assertArrayHasKey( 'totals', $data );
|
||||||
$this->assertArrayHasKey( 'variation', $data );
|
$this->assertArrayHasKey( 'variation', $data );
|
||||||
$this->assertArrayHasKey( 'low_stock_remaining', $data );
|
$this->assertArrayHasKey( 'low_stock_remaining', $data );
|
||||||
|
$this->assertArrayHasKey( 'backorders_allowed', $data );
|
||||||
$this->assertArrayHasKey( 'short_description', $data );
|
$this->assertArrayHasKey( 'short_description', $data );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue