* Implement basic version of MoneyValue with decimal conversion

* Implement MoneyValue in cart classes

* Add minor unit to schema

* Update tests

* Add tests

* Tweak minor unit description

* Replace pow

* Dump rounding mode and use constant values

* Only return strings

* prepare_money_response method

* Update types back to string

* Remove unnecessary parentheses

* Feedback; force integer rounding mode to prevent notices
This commit is contained in:
Mike Jolley 2019-12-13 15:37:11 +00:00 committed by GitHub
parent 810341d084
commit 901e996cc5
5 changed files with 116 additions and 76 deletions

View File

@ -63,4 +63,23 @@ abstract class AbstractSchema {
$properties
);
}
/**
* Convert monetary values from WooCommerce to string based integers, using
* the smallest unit of a currency.
*
* @param string|float $amount Monetary amount with decimals.
* @param int $decimals Number of decimals the amount is formatted with.
* @param int $rounding_mode Defaults to the PHP_ROUND_HALF_UP constant.
* @return string The new amount.
*/
protected function prepare_money_response( $amount, $decimals = 2, $rounding_mode = PHP_ROUND_HALF_UP ) {
return (string) intval(
round(
wc_format_decimal( $amount ) * ( 10 ** $decimals ),
0,
absint( $rounding_mode )
)
);
}
}

View File

@ -33,13 +33,13 @@ class CartItemSchema extends AbstractSchema {
*/
protected function get_properties() {
return [
'key' => array(
'key' => array(
'description' => __( 'Unique identifier for the item within the cart.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'id' => array(
'id' => array(
'description' => __( 'The cart item product or variation ID.', 'woo-gutenberg-products-block' ),
'type' => 'integer',
'context' => array( 'view', 'edit' ),
@ -49,7 +49,7 @@ class CartItemSchema extends AbstractSchema {
'validate_callback' => array( $this, 'product_id_exists' ),
),
),
'quantity' => array(
'quantity' => array(
'description' => __( 'Quantity of this item in the cart.', 'woo-gutenberg-products-block' ),
'type' => 'integer',
'context' => array( 'view', 'edit' ),
@ -58,26 +58,26 @@ class CartItemSchema extends AbstractSchema {
'sanitize_callback' => 'wc_stock_amount',
),
),
'name' => array(
'name' => array(
'description' => __( 'Product name.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'sku' => array(
'sku' => array(
'description' => __( 'Stock keeping unit, if applicable.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'permalink' => array(
'permalink' => array(
'description' => __( 'Product URL.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'format' => 'uri',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'images' => array(
'images' => array(
'description' => __( 'List of images.', 'woo-gutenberg-products-block' ),
'type' => 'array',
'context' => array( 'view', 'edit' ),
@ -113,25 +113,37 @@ class CartItemSchema extends AbstractSchema {
),
),
),
'product_price' => array(
'product_price' => array(
'description' => __( 'Current product price.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'line_subtotal' => array(
'description' => __( 'Line price subtotal (excluding coupons and discounts).', 'woo-gutenberg-products-block' ),
'line_subtotal' => array(
'description' => __( 'Line price subtotal (excluding coupons and discounts). Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'line_total' => array(
'description' => __( 'Line price total (including coupons and discounts).', 'woo-gutenberg-products-block' ),
'line_subtotal_tax' => array(
'description' => __( 'Line price subtotal tax. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'variation' => array(
'line_total' => array(
'description' => __( 'Line price total (including coupons and discounts). Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'line_total_tax' => array(
'description' => __( 'Line price total tax. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => array( 'view', 'edit' ),
'readonly' => true,
),
'variation' => array(
'description' => __( 'Chosen attributes (for variations).', 'woo-gutenberg-products-block' ),
'type' => 'array',
'context' => array( 'view', 'edit' ),
@ -172,22 +184,22 @@ class CartItemSchema extends AbstractSchema {
* @return array
*/
public function get_item_response( $cart_item ) {
$product = $cart_item['data'];
$line_subtotal = $product->get_price() * wc_stock_amount( $cart_item['quantity'] );
$line_total_incl_coupons = isset( $cart_item['line_total'] ) ? $cart_item['line_total'] : $line_subtotal;
$product = $cart_item['data'];
return [
'key' => $cart_item['key'],
'id' => $product->get_id(),
'quantity' => wc_stock_amount( $cart_item['quantity'] ),
'name' => $product->get_title(),
'sku' => $product->get_sku(),
'permalink' => $product->get_permalink(),
'images' => ( new ProductImages() )->images_to_array( $product ),
'product_price' => wc_format_decimal( $product->get_price() ),
'line_total' => wc_format_decimal( $line_total_incl_coupons ),
'line_subtotal' => wc_format_decimal( $line_subtotal ),
'variation' => $this->format_variation_data( $cart_item['variation'], $product ),
'key' => $cart_item['key'],
'id' => $product->get_id(),
'quantity' => wc_stock_amount( $cart_item['quantity'] ),
'name' => $product->get_title(),
'sku' => $product->get_sku(),
'permalink' => $product->get_permalink(),
'images' => ( new ProductImages() )->images_to_array( $product ),
'product_price' => $this->prepare_money_response( $product->get_price(), wc_get_price_decimals() ),
'line_subtotal' => $this->prepare_money_response( $cart_item['line_subtotal'], wc_get_price_decimals() ),
'line_subtotal_tax' => $this->prepare_money_response( $cart_item['line_subtotal_tax'], wc_get_price_decimals() ),
'line_total' => $this->prepare_money_response( $cart_item['line_total'], wc_get_price_decimals() ),
'line_total_tax' => $this->prepare_money_response( $cart_item['line_tax'], wc_get_price_decimals() ),
'variation' => $this->format_variation_data( $cart_item['variation'], $product ),
];
}

View File

@ -29,7 +29,7 @@ class CartSchema extends AbstractSchema {
*/
protected function get_properties() {
return [
'currency' => [
'currency' => [
'description' => __( 'Currency code (in ISO format) of the cart item prices.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'default' => get_woocommerce_currency(),
@ -37,7 +37,14 @@ class CartSchema extends AbstractSchema {
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'items' => [
'currency_minor_unit' => [
'description' => __( 'Currency minor unit (number of digits after the decimal separator) used for cart item prices.', 'woo-gutenberg-products-block' ),
'type' => 'integer',
'default' => wc_get_price_decimals(),
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'items' => [
'description' => __( 'List of cart items.', 'woo-gutenberg-products-block' ),
'type' => 'array',
'context' => [ 'view', 'edit' ],
@ -47,85 +54,85 @@ class CartSchema extends AbstractSchema {
'properties' => $this->force_schema_readonly( ( new CartItemSchema() )->get_properties() ),
],
],
'items_count' => [
'items_count' => [
'description' => __( 'Number of items in the cart.', 'woo-gutenberg-products-block' ),
'type' => 'integer',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'items_weight' => [
'items_weight' => [
'description' => __( 'Total weight (in grams) of all products in the cart.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'needs_shipping' => [
'needs_shipping' => [
'description' => __( 'True if the cart needs shipping. False for carts with only digital goods or stores with no shipping methods set-up.', 'woo-gutenberg-products-block' ),
'type' => 'boolean',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_items' => [
'description' => __( 'Total price of items in the cart.', 'woo-gutenberg-products-block' ),
'total_items' => [
'description' => __( 'Total price of items in the cart. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_items_tax' => [
'description' => __( 'Total tax on items in the cart.', 'woo-gutenberg-products-block' ),
'total_items_tax' => [
'description' => __( 'Total tax on items in the cart. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_fees' => [
'description' => __( 'Total price of any applied fees.', 'woo-gutenberg-products-block' ),
'total_fees' => [
'description' => __( 'Total price of any applied fees. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_fees_tax' => [
'description' => __( 'Total tax on fees.', 'woo-gutenberg-products-block' ),
'total_fees_tax' => [
'description' => __( 'Total tax on fees. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_discount' => [
'description' => __( 'Total discount from applied coupons.', 'woo-gutenberg-products-block' ),
'total_discount' => [
'description' => __( 'Total discount from applied coupons. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_discount_tax' => [
'description' => __( 'Total tax removed due to discount from applied coupons.', 'woo-gutenberg-products-block' ),
'total_discount_tax' => [
'description' => __( 'Total tax removed due to discount from applied coupons. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_shipping' => [
'description' => __( 'Total price of shipping.', 'woo-gutenberg-products-block' ),
'total_shipping' => [
'description' => __( 'Total price of shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_shipping_tax' => [
'description' => __( 'Total tax on shipping.', 'woo-gutenberg-products-block' ),
'total_shipping_tax' => [
'description' => __( 'Total tax on shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_tax' => [
'description' => __( 'Total tax applied to items and shipping.', 'woo-gutenberg-products-block' ),
'total_tax' => [
'description' => __( 'Total tax applied to items and shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'total_price' => [
'description' => __( 'Total price the customer will pay.', 'woo-gutenberg-products-block' ),
'total_price' => [
'description' => __( 'Total price the customer will pay. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
],
'tax_lines' => [
'tax_lines' => [
'description' => __( 'Lines of taxes applied to items and shipping.', 'woo-gutenberg-products-block' ),
'type' => 'array',
'context' => [ 'view', 'edit' ],
@ -140,7 +147,7 @@ class CartSchema extends AbstractSchema {
'readonly' => true,
],
'price' => [
'description' => __( 'The amount of tax charged.', 'woo-gutenberg-products-block' ),
'description' => __( 'The amount of tax charged. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string',
'context' => [ 'view', 'edit' ],
'readonly' => true,
@ -160,22 +167,23 @@ class CartSchema extends AbstractSchema {
public function get_item_response( $cart ) {
$cart_item_schema = new CartItemSchema();
return [
'currency' => get_woocommerce_currency(),
'items' => array_values( array_map( [ $cart_item_schema, 'get_item_response' ], array_filter( $cart->get_cart() ) ) ),
'items_count' => $cart->get_cart_contents_count(),
'items_weight' => wc_get_weight( $cart->get_cart_contents_weight(), 'g' ),
'needs_shipping' => $cart->needs_shipping(),
'total_items' => wc_format_decimal( $cart->get_subtotal() ),
'total_items_tax' => wc_format_decimal( $cart->get_subtotal_tax() ),
'total_fees' => wc_format_decimal( $cart->get_fee_total() ),
'total_fees_tax' => wc_format_decimal( $cart->get_fee_tax() ),
'total_discount' => wc_format_decimal( $cart->get_discount_total() ),
'total_discount_tax' => wc_format_decimal( $cart->get_discount_tax() ),
'total_shipping' => wc_format_decimal( $cart->get_shipping_total() ),
'total_shipping_tax' => wc_format_decimal( $cart->get_shipping_tax() ),
'total_tax' => wc_format_decimal( $cart->get_total_tax() ),
'total_price' => wc_format_decimal( $cart->get_total() ),
'tax_lines' => $this->get_tax_lines( $cart ),
'currency' => get_woocommerce_currency(),
'currency_minor_unit' => wc_get_price_decimals(),
'items' => array_values( array_map( [ $cart_item_schema, 'get_item_response' ], array_filter( $cart->get_cart() ) ) ),
'items_count' => $cart->get_cart_contents_count(),
'items_weight' => wc_get_weight( $cart->get_cart_contents_weight(), 'g' ),
'needs_shipping' => $cart->needs_shipping(),
'total_items' => $this->prepare_money_response( $cart->get_subtotal(), wc_get_price_decimals() ),
'total_items_tax' => $this->prepare_money_response( $cart->get_subtotal_tax(), wc_get_price_decimals() ),
'total_fees' => $this->prepare_money_response( $cart->get_fee_total(), wc_get_price_decimals() ),
'total_fees_tax' => $this->prepare_money_response( $cart->get_fee_tax(), wc_get_price_decimals() ),
'total_discount' => $this->prepare_money_response( $cart->get_discount_total(), wc_get_price_decimals() ),
'total_discount_tax' => $this->prepare_money_response( $cart->get_discount_tax(), wc_get_price_decimals() ),
'total_shipping' => $this->prepare_money_response( $cart->get_shipping_total(), wc_get_price_decimals() ),
'total_shipping_tax' => $this->prepare_money_response( $cart->get_shipping_tax(), wc_get_price_decimals() ),
'total_tax' => $this->prepare_money_response( $cart->get_total_tax(), wc_get_price_decimals() ),
'total_price' => $this->prepare_money_response( $cart->get_total(), wc_get_price_decimals() ),
'tax_lines' => $this->get_tax_lines( $cart ),
];
}
@ -192,7 +200,7 @@ class CartSchema extends AbstractSchema {
foreach ( $cart_tax_totals as $cart_tax_total ) {
$tax_lines[] = array(
'name' => $cart_tax_total->label,
'price' => wc_format_decimal( $cart_tax_total->amount ),
'price' => $this->prepare_money_response( $cart_tax_total->amount, wc_get_price_decimals() ),
);
}

View File

@ -62,12 +62,13 @@ class Cart extends TestCase {
$this->assertEquals( 200, $response->get_status() );
$this->assertEquals( 'GBP', $data['currency'] );
$this->assertEquals( 2, $data['currency_minor_unit'] );
$this->assertEquals( 3, $data['items_count'] );
$this->assertEquals( 2, count( $data['items'] ) );
$this->assertEquals( false, $data['needs_shipping'] );
$this->assertEquals( '30', $data['items_weight'] );
$this->assertEquals( '30.00', $data['total_items'] );
$this->assertEquals( '3000', $data['total_items'] );
$this->assertEquals( '0', $data['total_items_tax'] );
$this->assertEquals( '0', $data['total_fees'] );
$this->assertEquals( '0', $data['total_fees_tax'] );
@ -76,7 +77,7 @@ class Cart extends TestCase {
$this->assertEquals( '0', $data['total_shipping'] );
$this->assertEquals( '0', $data['total_shipping_tax'] );
$this->assertEquals( '0', $data['total_tax'] );
$this->assertEquals( '30.00', $data['total_price'] );
$this->assertEquals( '3000', $data['total_price'] );
}
/**

View File

@ -79,9 +79,9 @@ class CartItems extends TestCase {
$this->assertEquals( $this->products[0]->get_sku(), $data['sku'] );
$this->assertEquals( $this->products[0]->get_permalink(), $data['permalink'] );
$this->assertEquals( 2, $data['quantity'] );
$this->assertEquals( '10.00', $data['product_price'] );
$this->assertEquals( '20.00', $data['line_subtotal'] );
$this->assertEquals( '20.00', $data['line_total'] );
$this->assertEquals( '1000', $data['product_price'] );
$this->assertEquals( '2000', $data['line_subtotal'] );
$this->assertEquals( '2000', $data['line_total'] );
$request = new WP_REST_Request( 'DELETE', '/wc/store/cart/items/XXX815416f775098fe977004015c6193' );
$response = $this->server->dispatch( $request );