mirror of
https://github.com/wp-graphql/wp-graphql-woocommerce.git
synced 2026-08-14 12:53:44 +02:00
Adds support for Cart tax_lines (#940)
* Adds support for tax_lines * devops: cleanup cart tax lines code style and add unit tests * feat: add tax-aware cost, subtotal, and taxTotal fields to ShippingRate (#802) * feat: add format arg to ShippingRate cost/subtotal/taxTotal fields, update test for RAW format * devops: CartQueriesTest updated --------- Co-authored-by: Geoff Taylor <geoff@axistaylor.com>
This commit is contained in:
co-authored by
Geoff Taylor
parent
f1299846c7
commit
ef26bb2479
@@ -28,6 +28,7 @@ class Cart_Type {
|
||||
self::register_cart_fee();
|
||||
self::register_cart_tax();
|
||||
self::register_applied_coupon();
|
||||
self::register_cart_tax_line();
|
||||
self::register_cart();
|
||||
}
|
||||
|
||||
@@ -372,11 +373,71 @@ class Cart_Type {
|
||||
return ! empty( $applied_coupons ) ? $applied_coupons : null;
|
||||
},
|
||||
],
|
||||
'taxLines' => [
|
||||
'type' => [ 'list_of' => 'CartTaxLine' ],
|
||||
'description' => __( 'Cart tax lines itemized', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $cart ) {
|
||||
if ( 'itemized' !== get_option( 'woocommerce_tax_total_display' ) ) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$cart_tax_totals = $cart->get_tax_totals();
|
||||
$decimals = wc_get_price_decimals();
|
||||
$tax_lines = [];
|
||||
|
||||
foreach ( $cart_tax_totals as $cart_tax_total ) {
|
||||
$tax_lines[] = [
|
||||
'name' => $cart_tax_total->label,
|
||||
'price' => number_format( $cart_tax_total->amount, $decimals ),
|
||||
'rate' => \WC_Tax::get_rate_percent( $cart_tax_total->tax_rate_id ),
|
||||
];
|
||||
}
|
||||
|
||||
return $tax_lines;
|
||||
},
|
||||
],
|
||||
],
|
||||
$other_fields
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers the "CartTaxLine" type.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function register_cart_tax_line() {
|
||||
register_graphql_object_type(
|
||||
'CartTaxLine',
|
||||
[
|
||||
'description' => __( 'The cart tax line object', 'wp-graphql-woocommerce' ),
|
||||
'fields' => [
|
||||
'name' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Tax line name', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source['name'] ) ? $source['name'] : null;
|
||||
},
|
||||
],
|
||||
'price' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Tax line price', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source['price'] ) ? wc_graphql_price( $source['price'] ) : null;
|
||||
},
|
||||
],
|
||||
'rate' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Tax line rate', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! empty( $source['rate'] ) ? $source['rate'] : null;
|
||||
},
|
||||
],
|
||||
],
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the "Cart" type connections.
|
||||
*
|
||||
|
||||
@@ -54,10 +54,70 @@ class Shipping_Rate_Type {
|
||||
},
|
||||
],
|
||||
'cost' => [
|
||||
'type' => 'Float',
|
||||
'description' => __( 'Shipping rate cost', 'wp-graphql-woocommerce' ),
|
||||
'resolve' => static function ( $source ) {
|
||||
return ! is_null( $source->get_cost() ) ? $source->get_cost() : null;
|
||||
'type' => 'String',
|
||||
'description' => __( 'Shipping rate cost. Includes tax when woocommerce_tax_display_cart is set to incl.', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, array $args ) {
|
||||
$cost = $source->get_cost();
|
||||
if ( is_null( $cost ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( 'incl' === get_option( 'woocommerce_tax_display_cart' ) ) {
|
||||
$cost = floatval( $cost ) + floatval( $source->get_shipping_tax() );
|
||||
}
|
||||
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $cost;
|
||||
}
|
||||
|
||||
return \wc_graphql_price( strval( $cost ) );
|
||||
},
|
||||
],
|
||||
'subtotal' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Shipping rate cost before tax.', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, array $args ) {
|
||||
$cost = $source->get_cost();
|
||||
if ( is_null( $cost ) ) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $cost;
|
||||
}
|
||||
|
||||
return \wc_graphql_price( $cost );
|
||||
},
|
||||
],
|
||||
'taxTotal' => [
|
||||
'type' => 'String',
|
||||
'description' => __( 'Shipping rate tax total.', 'wp-graphql-woocommerce' ),
|
||||
'args' => [
|
||||
'format' => [
|
||||
'type' => 'PricingFieldFormatEnum',
|
||||
'description' => __( 'Format of the price', 'wp-graphql-woocommerce' ),
|
||||
],
|
||||
],
|
||||
'resolve' => static function ( $source, array $args ) {
|
||||
$tax = $source->get_shipping_tax();
|
||||
|
||||
if ( isset( $args['format'] ) && 'raw' === $args['format'] ) {
|
||||
return $tax;
|
||||
}
|
||||
|
||||
return \wc_graphql_price( $tax );
|
||||
},
|
||||
],
|
||||
],
|
||||
|
||||
@@ -475,7 +475,7 @@ class QLSessionHandlerCest {
|
||||
supportsShippingCalculator
|
||||
rates {
|
||||
id
|
||||
cost
|
||||
cost(format: RAW)
|
||||
label
|
||||
}
|
||||
}
|
||||
@@ -497,14 +497,14 @@ class QLSessionHandlerCest {
|
||||
$I->expectNode(
|
||||
'rates',
|
||||
[
|
||||
$I->expectField( 'cost', 0 ),
|
||||
$I->expectField( 'cost', '0' ),
|
||||
$I->expectField( 'label', 'Flat rate' ),
|
||||
]
|
||||
),
|
||||
$I->expectNode(
|
||||
'rates',
|
||||
[
|
||||
$I->expectField( 'cost', 0 ),
|
||||
$I->expectField( 'cost', '0' ),
|
||||
$I->expectField( 'label', 'Free shipping' ),
|
||||
]
|
||||
),
|
||||
@@ -528,7 +528,7 @@ class QLSessionHandlerCest {
|
||||
supportsShippingCalculator
|
||||
rates {
|
||||
id
|
||||
cost
|
||||
cost(format: RAW)
|
||||
label
|
||||
}
|
||||
}
|
||||
|
||||
@@ -546,6 +546,221 @@ class CartQueriesTest extends \Tests\WPGraphQL\WooCommerce\TestCase\WooGraphQLTe
|
||||
]
|
||||
);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cart taxLines field returns itemized tax data.
|
||||
*/
|
||||
public function testCartTaxLines() {
|
||||
// Enable taxes and set itemized display.
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
update_option( 'woocommerce_tax_total_display', 'itemized' );
|
||||
|
||||
// Create a tax rate.
|
||||
$tax_rate_id = $this->factory->tax_rate->create(
|
||||
[
|
||||
'country' => '',
|
||||
'rate' => '10.0000',
|
||||
'name' => 'Test Tax',
|
||||
'priority' => 1,
|
||||
'compound' => 0,
|
||||
'shipping' => 1,
|
||||
'class' => '',
|
||||
]
|
||||
);
|
||||
|
||||
// Create a product and add to cart.
|
||||
$product_id = $this->factory->product->createSimple( [ 'regular_price' => '100', 'virtual' => true ] );
|
||||
WC()->cart->add_to_cart( $product_id );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
$query = '
|
||||
query {
|
||||
cart {
|
||||
taxLines {
|
||||
name
|
||||
price
|
||||
rate
|
||||
}
|
||||
}
|
||||
}
|
||||
';
|
||||
|
||||
$response = $this->graphql( compact( 'query' ) );
|
||||
|
||||
$expected = [
|
||||
$this->expectedNode(
|
||||
'cart.taxLines',
|
||||
[
|
||||
$this->expectedField( 'name', 'Test Tax' ),
|
||||
$this->expectedField( 'price', '$10.00' ),
|
||||
$this->expectedField( 'rate', '10%' ),
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
$this->assertQuerySuccessful( $response, $expected );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test cart taxLines returns empty when display is not itemized.
|
||||
*/
|
||||
public function testCartTaxLinesEmptyWhenNotItemized() {
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
update_option( 'woocommerce_tax_total_display', 'single' );
|
||||
|
||||
$tax_rate_id = $this->factory->tax_rate->create(
|
||||
[
|
||||
'country' => '',
|
||||
'rate' => '10.0000',
|
||||
'name' => 'Test Tax',
|
||||
'priority' => 1,
|
||||
'compound' => 0,
|
||||
'shipping' => 1,
|
||||
'class' => '',
|
||||
]
|
||||
);
|
||||
|
||||
$product_id = $this->factory->product->createSimple( [ 'regular_price' => '100', 'virtual' => true ] );
|
||||
WC()->cart->add_to_cart( $product_id );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
$query = '
|
||||
query {
|
||||
cart {
|
||||
taxLines {
|
||||
name
|
||||
price
|
||||
rate
|
||||
}
|
||||
}
|
||||
}
|
||||
';
|
||||
|
||||
$response = $this->graphql( compact( 'query' ) );
|
||||
|
||||
$expected = [
|
||||
$this->expectedField( 'cart.taxLines', static::IS_FALSY ),
|
||||
];
|
||||
|
||||
$this->assertQuerySuccessful( $response, $expected );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test shipping rate cost includes tax when woocommerce_tax_display_cart is incl.
|
||||
*/
|
||||
public function testShippingRateCostIncludesTax() {
|
||||
// Enable taxes and set display to include tax.
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
update_option( 'woocommerce_tax_display_cart', 'incl' );
|
||||
|
||||
// Create a tax rate that applies to shipping.
|
||||
$this->factory->tax_rate->create(
|
||||
[
|
||||
'country' => '',
|
||||
'rate' => '10.0000',
|
||||
'name' => 'Shipping Tax',
|
||||
'priority' => 1,
|
||||
'compound' => 0,
|
||||
'shipping' => 1,
|
||||
'class' => '',
|
||||
]
|
||||
);
|
||||
|
||||
// Create a physical product and add to cart.
|
||||
$product_id = $this->factory->product->createSimple( [ 'regular_price' => '50' ] );
|
||||
WC()->cart->add_to_cart( $product_id );
|
||||
|
||||
// Set shipping address so rates are calculated.
|
||||
WC()->customer->set_shipping_country( 'GB' );
|
||||
WC()->customer->set_shipping_postcode( 'CB23 1AB' );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
$query = '
|
||||
query {
|
||||
cart {
|
||||
availableShippingMethods {
|
||||
rates {
|
||||
id
|
||||
label
|
||||
cost
|
||||
subtotal
|
||||
taxTotal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
';
|
||||
|
||||
$response = $this->graphql( compact( 'query' ) );
|
||||
|
||||
// Flat rate is $10, tax is 10% = $1. Cost should be $11 (incl tax), subtotal $10.
|
||||
$expected = [
|
||||
$this->expectedField( 'cart.availableShippingMethods.0.rates.0.cost', '$11.00' ),
|
||||
$this->expectedField( 'cart.availableShippingMethods.0.rates.0.subtotal', '$10.00' ),
|
||||
$this->expectedField( 'cart.availableShippingMethods.0.rates.0.taxTotal', '$1.00' ),
|
||||
];
|
||||
|
||||
$this->assertQuerySuccessful( $response, $expected );
|
||||
}
|
||||
|
||||
/**
|
||||
* Test shipping rate cost excludes tax when woocommerce_tax_display_cart is excl.
|
||||
*/
|
||||
public function testShippingRateCostExcludesTax() {
|
||||
// Enable taxes and set display to exclude tax.
|
||||
update_option( 'woocommerce_calc_taxes', 'yes' );
|
||||
update_option( 'woocommerce_tax_display_cart', 'excl' );
|
||||
|
||||
$this->factory->tax_rate->create(
|
||||
[
|
||||
'country' => '',
|
||||
'rate' => '10.0000',
|
||||
'name' => 'Shipping Tax',
|
||||
'priority' => 1,
|
||||
'compound' => 0,
|
||||
'shipping' => 1,
|
||||
'class' => '',
|
||||
]
|
||||
);
|
||||
|
||||
$product_id = $this->factory->product->createSimple( [ 'regular_price' => '50' ] );
|
||||
WC()->cart->add_to_cart( $product_id );
|
||||
|
||||
WC()->customer->set_shipping_country( 'GB' );
|
||||
WC()->customer->set_shipping_postcode( 'CB23 1AB' );
|
||||
WC()->cart->calculate_totals();
|
||||
|
||||
$query = '
|
||||
query {
|
||||
cart {
|
||||
availableShippingMethods {
|
||||
rates {
|
||||
id
|
||||
label
|
||||
cost
|
||||
subtotal
|
||||
taxTotal
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
';
|
||||
|
||||
$response = $this->graphql( compact( 'query' ) );
|
||||
|
||||
// Cost and subtotal should both be $10 (excl tax), taxTotal still $1.
|
||||
$expected = [
|
||||
$this->expectedObject(
|
||||
'cart.availableShippingMethods.#',
|
||||
[
|
||||
$this->expectedField( 'rates.0.cost', '$10.00' ),
|
||||
$this->expectedField( 'rates.0.subtotal', '$10.00' ),
|
||||
$this->expectedField( 'rates.0.taxTotal', '$1.00' ),
|
||||
]
|
||||
),
|
||||
];
|
||||
|
||||
$this->assertQuerySuccessful( $response, $expected );
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user