Add money formatting to API responses (https://github.com/woocommerce/woocommerce-blocks/pull/1367)
* 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:
parent
810341d084
commit
901e996cc5
|
@ -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 )
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 ),
|
||||
];
|
||||
}
|
||||
|
||||
|
|
|
@ -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() ),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -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'] );
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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 );
|
||||
|
|
Loading…
Reference in New Issue