Vendure Tax Rates and Zones setup

Martijn null

Martijn

5 min read , ~4 ~hours to implement

This article teaches you to configure Vendure's tax rates for real-world operations.

Vendure Tax Rates and Zones setup

Now that your tech installation is complete, it's time to configure your store for real-world operations. This article covers the best practices related to taxes, that we've learned at Pinelab.studio. The examples used in this article are mostly for webshops based in Europe, but it will still give you a good understanding on how to use Vendure's flexible Tax Rates and Zones for your region.

Populate Countries

If all is well, your Vendure instance already has countries set up. If not, you can use Vendure's populate utility and this list of countries to populate the countries Vendure.

Your storefront should use the GraphQL query availableCountries to display the countries available to your customers. If you do not provide goods or services to all countries, you can disable the countries you don't allow. (It's easier to import them all, and disable them, so that you can easily enable them later)

Create Tax Categories

In most cases, you want to create the tax categories Zero, Reduced and Standard. Some countries, like Belgium or France, also have a Further Reduced tax category. You can create these categories through the Vendure Admin UI: Settings > Tax Categories.

Example 1: You stay below the threshold of €10.000 in sales to other EU countries

Our first example uses the EU regulations: You are based in the Netherlands and sell to other EU countries, but the total sales to other EU countries stays below €10.000. The same applies to other European Union countries.

In this case, you should still apply the Dutch tax rates, 21% or 9%, when shipping to other countries in the European Union.

For this setup, you will need to do the following:

  1. Create zone EU, and add all countries in the EU region.
  2. Create zone Other, which will have all other countries in it
  3. Create tax rate EU Reduced, for zone EU, tax category Reduced and rate 9%
  4. Create tax rate EU Standard, for zone EU, tax category Standard and rate 21%
  5. Create tax rate EU Zero, for zone EU, tax category Standard and rate 0%
  6. For zone Other you create rates Other Zero, Other reduced and Other standard, but all will have a rate of 0%.

Configure the AddressBasedTaxZoneStrategy, so that taxes are calculated based on the shipping country of the order.

Example 2: Your sales to other EU countries exceeds €10.000

If you are based in the Netherlands, but expect total sales to other EU countries to exceed the threshold of €10.000, you have to charge the VAT rates of the shipping country of your customer. For example, when shipping goods to a German customer, you have to charge 19% VAT instead of the Dutch 21%.

  1. Create a zone for each of the countries within the EU. For example, create zone BE, that will only have the country Belgium as member.
  2. Create tax rates for each country, for each tax rate. E.g. BE Standard and BE Reduced, with rates 21% and 6%
  3. Create another zone Other for all countries outside the EU.
  4. For zone Other you can create the rates Other reduced and Other standard, but both will have a rate of 0%.

You should again use the AddressBasedTaxZoneStrategy to have Vendure use the correct zones on orders.

You can search the internet for "EU VAT One Stop Shop" for more information on these tax regulations.

Example 3: Reverse VAT Charge for Business to Business Sales

For business to business sales, you should use the "Reverse Tax Charge" setup. This means the responsibility of tax registration lies with the customer.

You are required to have a valid VAT ID of the customer, and that VAT ID should also be displayed on the invoice you sent to the customer.

  1. Create a zone EU Reverse Tax Charge, that holds all EU countries.
  2. Create the rates Zero, Reduced and Standard for this zone. All rates will be 0%
  3. Implement your own TaxZoneStrategy that sets the zone to EU Reverse Tax Charge, if the order has a valid VAT ID, and is placed in a country inside the EU.

Shipping Method tax

In the Netherlands, shipping costs are taxed at the same VAT rate as the goods in the order, and if multiple VAT rates apply, the tax on shipping should be prorated based on the value of items at each rate.

Example: If an order includes €100 of 21% VAT goods and €50 of 9% VAT goods, and shipping is €15, then €10 of the shipping cost is taxed at 21%, and €5 is taxed at 9%. The total VAT on shipping would be €2.10 (21% of €10) + €0.45 (9% of €5) = €2.55.

You can implement your own ShippingCalculator to do this calculation.

Alternative option: Keep prices incl. tax the same for all customers

The example above implements local tax rates for customers, but keeps the prices excluding tax the same for everyone. This means the price including tax differs per country. For example:

  1. A product is €121,- including 21% tax in The Netherlands. So, the price excluding tax is €100.
  2. A customer selects Germany in the checkout. The tax rate used on the order is now 19%
  3. Vendure will calculate the new price including tax for this product in Germany. This will be €119, because €100 ex. tax, plus €19 VAT.

If you want the price to always be €121, regardless of the applied tax rate, you can implement the following ProductVariantPriceCalculationStrategy:

import {
  PriceCalculationResult,
  ProductVariantPriceCalculationArgs,
  ProductVariantPriceCalculationStrategy,
} from "@vendure/core";

/**
 * Make sure the gross price is the same for all customers, regardless of their country.
 *
 * @example
 * A product is €121 including 21% VAT in the Netherlands
 * The same product is also €121 including 19% VAT in Germany. This means the net price will be €101.68
 */
export class SameGrossPriceForEveryoneCalculationStrategy
  implements ProductVariantPriceCalculationStrategy
{
  async calculate(
    args: ProductVariantPriceCalculationArgs,
  ): Promise<PriceCalculationResult> {
    const { inputPrice, ctx } = args;
    return {
      price: inputPrice,
      priceIncludesTax: ctx.channel.pricesIncludeTax,
    };
  }
}

These were some examples on how to set up Tax Rates and Zones in Vendure. Need help, or want to know more? Don't hesitate to get in touch!