Broadleaf Microservices
  • v1.0.0-latest-prod

Included Taxes & VAT

What is VAT?

VAT stands for "Value-Added Tax", and it’s a method of calculating and collecting taxes when an item is sold.

In a VAT context, the tax is typically included in the item’s retail price, rather than being added as an additional cost. This means that the price shown to customers during browsing is the final price that they’ll pay for a given item.

Included Taxes vs Sales Tax Example

For a $10 item with a 5% VAT vs 5% Sales Tax…​

5% VAT

5% Sales Tax

The Customer Pays

$10

$10.50

Tax Amount

$0.48

$0.50

Merchant Keeps

$9.52

$10

Calculating VAT

To calculate a VAT amount, we need to apply the tax rate to the pre-tax price of the item. Since the retail price includes VAT, we need to work backwards.

Item Price without VAT = retailPrice / (taxRate + 1)
VAT Amount = retailPrice - itemPriceWithoutVAT

With our $10 item with 5% VAT example, the math would be:

itemPriceWithoutVAT = $10 / (0.05 +1) = $10 / 1.05 = $9.52
VAT Amount = $10 - $9.52 = $0.48

We can check our math by applying the tax rate to the pre-tax amount:

$9.52 * 0.05 = $0.48
$9.52 + $0.48 = $10.00

Included Tax & VAT Configuration

While tax providers can declare if the tax rate for a given shipping address is VAT or not, it’s important to also consider if the item prices have taxes included.

To declare whether an item has included taxes baked into its price, you’ll want to customize DefaultDelegatingTaxService#isItemIncludesTaxes(Cart, FulfillmentGroup, FulfillmentItem, CartItem, ContextInfo) in the CartOperationService. While calculating taxes, the result of this method gets passed on to the TaxProvider implementation which then must decide whether to apply the resolved tax as an included tax.

SimpleTaxProvider

The SimpleTaxProvider leverages SimpleTaxConfig & SimpleTaxRecords to identify the tax rate for the provided tax address. While this configuration allows "vat": true to be declared for a given tax rate, the SimpleTaxProvider uses the result of DefaultDelegatingTaxService#isItemIncludesTaxes(Cart, FulfillmentGroup, FulfillmentItem, CartItem, ContextInfo) to decide if taxes should be calculated as included taxes.

SimpleTaxRecord

Note
com.broadleafcommerce.tax.simple.SimpleTaxRecord

A SimpleTaxRecord is the core piece of tax configuration. A SimpleTaxRecord will have the tax rate as well as fields for the taxing jurisdiction (e.g. country, state, postal code). SimpleTaxRecord also has a vat field to indicate if the tax is a VAT.

SimpleTaxRecords are configured with SimpleTaxConfig and used in SimpleTaxProvider.

SimpleTaxConfig Properties

Note
com.broadleafcommerce.tax.simple.SimpleTaxConfig

The SimpleTaxProvider is configured with properties, by default in "simple-tax-example.json".

simple-tax-example.json
{
  "defaultRate": {"rate": "0.05"},
  "taxTables" : {
    "US": [{
      "countryDefault": true,
      "rate": "0"
    },
    {
      "stateProvinceRegion": "TX",
      "rate": "0.06375"
    },
    {
      "stateProvinceRegion": "TX",
      "city": "Celina",
      "postalCode": "75009",
      "rate": "0.0625"
    }],

    "GB": [{
      "countryDefault": true,
      "rate": "0.2",
      "vat": true
    }],

    "VAT20": [{
      "countryDefault": true,
      "rate": "0.20",
      "vat": true
    }],

    "VAT5": [{
      "countryDefault": true,
      "rate": "0.05",
      "vat": true
    }]
  }
}

In the example above, the taxTables map has 4 keys: US, GB, VAT20, and VAT5. You can see that the GB, VAT20, and VAT5 keys have "vat":true. You can also see in the US example that tax records can be configured by country, region, city, and postal code.

SimpleTaxProvider will first check these keys for an item’s tax code. If no record is found by tax code, then it checks the country of the destination address (see SimpleTaxProvider#getTaxRecord).

CartOps VAT Documentation

The CartOperationsServices documentation also has a section about VAT, see CartOps VAT configuration.

Cart Pricing with Included Taxes

TaxDetail isVat Field

During tax calculation, the SimpleTaxProvider looks up the appropriate SimpleTaxRecord and adds TaxDetail objects to the tax response. TaxDetail has an isVat flag which is used to declare whether the taxes were applied as included taxes or as an additional cost. Related classes FulfillmentItemTaxDetail and OrderFulfillmentItemTaxDetail also have the isVat flag.

Note
com.broadleafcommerce.tax.domain.TaxDetail com.broadleafcommerce.tax.simple.SimpleTaxProvider

CartPricing taxIncludedType and includedTaxAmount

A CartPricing object is used to track the pricing data for the cart, such as subtotal, tax amounts, and fulfillment amounts. CartPricing also has fields taxIncludedType and includedTaxAmount to indicate if and how much tax is included in the subtotal. The subtotal is the sum of the retail prices of a cart, so in a VAT system, tax is included in the subtotal. taxIncludedType can be YES, NO, or PARTIAL. The amount of tax included in the subtotal is tracked in includedTaxAmount.

Note
com.broadleafcommerce.cart.client.domain.CartPricing

The method responsible for determining the includedTaxAmount is: com.broadleafcommerce.cartoperation.service.pricing.DefaultDelegatingTaxService#calculateIncludedTaxes. That method will check TaxInfo#isTaxIncluded from all items in the cart to determine what amount of tax is included in the subtotal.

CartPricing#totalTax will have the total amount of tax for the Cart, including VAT and Sales Taxes.

Cart Pricing Calculation

The formula for pricing a cart is subtotal + fulfillment + fees - adjustments + (totalTax - includedTax).

With our example from earlier, the $10 item with 5% tax, the CartPricing object would have the following values:

CartPricing Field Value when isVat=true Value when isVat=false

subtotal

$10

$10

taxTotal

$0.48

$0.50

taxIncludedType

YES

NO

includedTaxAmount

$0.48

$0

total

$10

$10.50

The formula for pricing a cart is subtotal + fulfillment + fees - adjustments + (totalTax - includedTax).

The logic is in the DefaultCartTotalsCalculator:

com.broadleafcommerce.cartoperation.service.pricing.DefaultCartTotalsCalculator
@Override
public MonetaryAmount calculateCartTotalBeforeTax(@lombok.NonNull CartPricing cartPricing) {
    return cartPricing.getSubtotal()
            .add(cartPricing.getFulfillmentTotal())
            .add(cartPricing.getFeesTotal())
            .subtract(cartPricing.getAdjustmentsTotal());
}

@Override
public MonetaryAmount calculateCartTotalAfterTax(@lombok.NonNull CartPricing cartPricing) {
    return calculateCartTotalBeforeTax(cartPricing)
            .add(getNonIncludedTax(cartPricing));
}

/**
 * Determine the amount of tax that was not included in the subtotal and therefore should be
 * added to the subtotal to get the grand total.
 *
 * @param cartPricing The pricing fields of a Cart to total.
 * @return The amount of tax that should be added to the subtotal to get the grand total.
 */
protected MonetaryAmount getNonIncludedTax(CartPricing cartPricing) {
    return cartPricing.getTotalTax().subtract(cartPricing.getIncludedTaxAmount());
}

VAT on Orders

After checkout, in OrderOperationServices, the CheckoutCompletionListener creates an Order from a Cart. There is a setting available to control how VAT taxes are handled

Note
com.broadleafcommerce.orderoperation.service.messaging.checkout.CheckoutCompletionListener

Save Item Prices without VAT

The property broadleaf.orderoperation.ordergeneration.removeVatFromItemPrice controls if the item’s price on the Order should include the VAT. With our previous example, our cart with a $10 item would get converted to an Order with an OrderItem, which would have a corresponding OrderFulfillmentItem, which has OrderFulfillmentTaxItemDetails, which has the isVat flag with the value from the SimpleTaxRecord.

In DefaultCartOrderGenerationService#updateOrderItemTotalsForVat and DefaultCartOrderFulfillmentGenerationService#buildOrderFulfillmentItem, the removeVatFromItemPrice flag is checked. If true, the VAT amount will be subtracted from the item’s price.

Below is a table that shows the difference when removeVatFromItemPrice is enabled.

Field removeVatFromItemPrice=true removeVatFromItemPrice=false

item price

$9.52

$10

item tax

$0.48

$0.48

item total

$10

$10

The removeVatFromItemPrice flag controls how order data is saved in the database. If instead, you would like to alter how order data is displayed, that can be accomplished by customizing the front-end admin application. The order endpoint response includes all tax info about the order, including the isVAT and includedAmount fields.