* 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 $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

@ -120,13 +120,25 @@ class CartItemSchema extends AbstractSchema {
'readonly' => true, 'readonly' => true,
), ),
'line_subtotal' => array( 'line_subtotal' => array(
'description' => __( 'Line price subtotal (excluding coupons and discounts).', 'woo-gutenberg-products-block' ), '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_subtotal_tax' => array(
'description' => __( 'Line price subtotal tax. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
'readonly' => true, 'readonly' => true,
), ),
'line_total' => array( 'line_total' => array(
'description' => __( 'Line price total (including coupons and discounts).', 'woo-gutenberg-products-block' ), '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', 'type' => 'string',
'context' => array( 'view', 'edit' ), 'context' => array( 'view', 'edit' ),
'readonly' => true, 'readonly' => true,
@ -173,8 +185,6 @@ class CartItemSchema extends AbstractSchema {
*/ */
public function get_item_response( $cart_item ) { public function get_item_response( $cart_item ) {
$product = $cart_item['data']; $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;
return [ return [
'key' => $cart_item['key'], 'key' => $cart_item['key'],
@ -184,9 +194,11 @@ class CartItemSchema extends AbstractSchema {
'sku' => $product->get_sku(), 'sku' => $product->get_sku(),
'permalink' => $product->get_permalink(), 'permalink' => $product->get_permalink(),
'images' => ( new ProductImages() )->images_to_array( $product ), 'images' => ( new ProductImages() )->images_to_array( $product ),
'product_price' => wc_format_decimal( $product->get_price() ), 'product_price' => $this->prepare_money_response( $product->get_price(), wc_get_price_decimals() ),
'line_total' => wc_format_decimal( $line_total_incl_coupons ), 'line_subtotal' => $this->prepare_money_response( $cart_item['line_subtotal'], wc_get_price_decimals() ),
'line_subtotal' => wc_format_decimal( $line_subtotal ), '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 ), 'variation' => $this->format_variation_data( $cart_item['variation'], $product ),
]; ];
} }

View File

@ -37,6 +37,13 @@ class CartSchema extends AbstractSchema {
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'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' => [ 'items' => [
'description' => __( 'List of cart items.', 'woo-gutenberg-products-block' ), 'description' => __( 'List of cart items.', 'woo-gutenberg-products-block' ),
'type' => 'array', 'type' => 'array',
@ -66,61 +73,61 @@ class CartSchema extends AbstractSchema {
'readonly' => true, 'readonly' => true,
], ],
'total_items' => [ 'total_items' => [
'description' => __( 'Total price of items in the cart.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total price of items in the cart. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_items_tax' => [ 'total_items_tax' => [
'description' => __( 'Total tax on items in the cart.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total tax on items in the cart. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_fees' => [ 'total_fees' => [
'description' => __( 'Total price of any applied fees.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total price of any applied fees. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_fees_tax' => [ 'total_fees_tax' => [
'description' => __( 'Total tax on fees.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total tax on fees. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_discount' => [ 'total_discount' => [
'description' => __( 'Total discount from applied coupons.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total discount from applied coupons. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_discount_tax' => [ 'total_discount_tax' => [
'description' => __( 'Total tax removed due to discount from applied coupons.', 'woo-gutenberg-products-block' ), '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', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_shipping' => [ 'total_shipping' => [
'description' => __( 'Total price of shipping.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total price of shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_shipping_tax' => [ 'total_shipping_tax' => [
'description' => __( 'Total tax on shipping.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total tax on shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_tax' => [ 'total_tax' => [
'description' => __( 'Total tax applied to items and shipping.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total tax applied to items and shipping. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
], ],
'total_price' => [ 'total_price' => [
'description' => __( 'Total price the customer will pay.', 'woo-gutenberg-products-block' ), 'description' => __( 'Total price the customer will pay. Amount provided using the smallest unit of the currency.', 'woo-gutenberg-products-block' ),
'type' => 'string', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
@ -140,7 +147,7 @@ class CartSchema extends AbstractSchema {
'readonly' => true, 'readonly' => true,
], ],
'price' => [ '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', 'type' => 'string',
'context' => [ 'view', 'edit' ], 'context' => [ 'view', 'edit' ],
'readonly' => true, 'readonly' => true,
@ -161,20 +168,21 @@ class CartSchema extends AbstractSchema {
$cart_item_schema = new CartItemSchema(); $cart_item_schema = new CartItemSchema();
return [ return [
'currency' => get_woocommerce_currency(), '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' => array_values( array_map( [ $cart_item_schema, 'get_item_response' ], array_filter( $cart->get_cart() ) ) ),
'items_count' => $cart->get_cart_contents_count(), 'items_count' => $cart->get_cart_contents_count(),
'items_weight' => wc_get_weight( $cart->get_cart_contents_weight(), 'g' ), 'items_weight' => wc_get_weight( $cart->get_cart_contents_weight(), 'g' ),
'needs_shipping' => $cart->needs_shipping(), 'needs_shipping' => $cart->needs_shipping(),
'total_items' => wc_format_decimal( $cart->get_subtotal() ), 'total_items' => $this->prepare_money_response( $cart->get_subtotal(), wc_get_price_decimals() ),
'total_items_tax' => wc_format_decimal( $cart->get_subtotal_tax() ), 'total_items_tax' => $this->prepare_money_response( $cart->get_subtotal_tax(), wc_get_price_decimals() ),
'total_fees' => wc_format_decimal( $cart->get_fee_total() ), 'total_fees' => $this->prepare_money_response( $cart->get_fee_total(), wc_get_price_decimals() ),
'total_fees_tax' => wc_format_decimal( $cart->get_fee_tax() ), 'total_fees_tax' => $this->prepare_money_response( $cart->get_fee_tax(), wc_get_price_decimals() ),
'total_discount' => wc_format_decimal( $cart->get_discount_total() ), 'total_discount' => $this->prepare_money_response( $cart->get_discount_total(), wc_get_price_decimals() ),
'total_discount_tax' => wc_format_decimal( $cart->get_discount_tax() ), 'total_discount_tax' => $this->prepare_money_response( $cart->get_discount_tax(), wc_get_price_decimals() ),
'total_shipping' => wc_format_decimal( $cart->get_shipping_total() ), 'total_shipping' => $this->prepare_money_response( $cart->get_shipping_total(), wc_get_price_decimals() ),
'total_shipping_tax' => wc_format_decimal( $cart->get_shipping_tax() ), 'total_shipping_tax' => $this->prepare_money_response( $cart->get_shipping_tax(), wc_get_price_decimals() ),
'total_tax' => wc_format_decimal( $cart->get_total_tax() ), 'total_tax' => $this->prepare_money_response( $cart->get_total_tax(), wc_get_price_decimals() ),
'total_price' => wc_format_decimal( $cart->get_total() ), 'total_price' => $this->prepare_money_response( $cart->get_total(), wc_get_price_decimals() ),
'tax_lines' => $this->get_tax_lines( $cart ), 'tax_lines' => $this->get_tax_lines( $cart ),
]; ];
} }
@ -192,7 +200,7 @@ class CartSchema extends AbstractSchema {
foreach ( $cart_tax_totals as $cart_tax_total ) { foreach ( $cart_tax_totals as $cart_tax_total ) {
$tax_lines[] = array( $tax_lines[] = array(
'name' => $cart_tax_total->label, '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( 200, $response->get_status() );
$this->assertEquals( 'GBP', $data['currency'] ); $this->assertEquals( 'GBP', $data['currency'] );
$this->assertEquals( 2, $data['currency_minor_unit'] );
$this->assertEquals( 3, $data['items_count'] ); $this->assertEquals( 3, $data['items_count'] );
$this->assertEquals( 2, count( $data['items'] ) ); $this->assertEquals( 2, count( $data['items'] ) );
$this->assertEquals( false, $data['needs_shipping'] ); $this->assertEquals( false, $data['needs_shipping'] );
$this->assertEquals( '30', $data['items_weight'] ); $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_items_tax'] );
$this->assertEquals( '0', $data['total_fees'] ); $this->assertEquals( '0', $data['total_fees'] );
$this->assertEquals( '0', $data['total_fees_tax'] ); $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'] );
$this->assertEquals( '0', $data['total_shipping_tax'] ); $this->assertEquals( '0', $data['total_shipping_tax'] );
$this->assertEquals( '0', $data['total_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_sku(), $data['sku'] );
$this->assertEquals( $this->products[0]->get_permalink(), $data['permalink'] ); $this->assertEquals( $this->products[0]->get_permalink(), $data['permalink'] );
$this->assertEquals( 2, $data['quantity'] ); $this->assertEquals( 2, $data['quantity'] );
$this->assertEquals( '10.00', $data['product_price'] ); $this->assertEquals( '1000', $data['product_price'] );
$this->assertEquals( '20.00', $data['line_subtotal'] ); $this->assertEquals( '2000', $data['line_subtotal'] );
$this->assertEquals( '20.00', $data['line_total'] ); $this->assertEquals( '2000', $data['line_total'] );
$request = new WP_REST_Request( 'DELETE', '/wc/store/cart/items/XXX815416f775098fe977004015c6193' ); $request = new WP_REST_Request( 'DELETE', '/wc/store/cart/items/XXX815416f775098fe977004015c6193' );
$response = $this->server->dispatch( $request ); $response = $this->server->dispatch( $request );