The shipping cost very often has tax too, not just the order items.

Links:
http://blog.taxjar.com/sales-tax-and-shipping/
https://github.com/commerceguys/tax/issues/18

Looks like we have two main variations:
- Charge the standard (default) rate
- Charge the highest applied rate

The standard rate is usually the highest applied one, however if an order only has items at a reduced rate, the shipping cost might still need to get the standard rate.

This would most likely be implemented via a custom TaxType plugin, ensured to run after the other plugins (so that we have the order item tax available to us).

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

bojanz created an issue. See original summary.

jasonschweb’s picture

I need this for a project within the next week, so I've made this patch as a workaround until there's an official (better) way to do this. It's assuming that tax is not included in the shipping price, as I'm not sure if this is ever done in the US, and isn't configurable - just taxes shipping all the time. Posting in case this helps anyone else in the meantime. Also, patching against commerce, not shipping module as that way makes more sense to me.

mathiasgmeiner’s picture

Thanks for this! It's a first step into supporting taxes on shipping costs!

mirom’s picture

Status: Active » Needs review
FileSize
2.94 KB

I just rerolled patch for the latest version and fixed couple of issues which were throwing exceptions. I realise that this is not ideal way how to resolve the issue, but for TaxType there is no way how to change weight. I'm also wondering if better place for this patch isn't in commerce_tax module as it's patching it instead of commerce_shipping.

mirom’s picture

FileSize
2.97 KB

Just minor coding standards fix.

smartparty’s picture

Working good here with latest dev and a much needed (even if temporary) solution to the requirement for tax on shipping in the UK.

amjad1233’s picture

The above plugin assumes States for US.

But what I wanted was "broadly" available option for if GST is included in adjustments or not

I have done slightly different with adding Custom GST adjustment.

I know it's not good one but couldn't get it working any better way would be appreciated.

I kind of bypass the Tax plugins and just added an adjustment in OrderProcessor as below.
(Note it's with 'included' so it doesn't affect the order total)

 public function process(OrderInterface $order)
    {
        $total = $order->getTotalPrice()->getNumber();
        $new_tax = number_format(($total / 11), 2);
        $new_price = new Price("{$new_tax}", "AUD");
        $adjustments = $order->collectAdjustments();
        
        if(sizeof($adjustments) > 0 ) {
            foreach($adjustments as $index => $adjustment) {
                /**
                  * @todo Create a custom "Adjustment" type in future.
                  */
                if ($adjustment->getType() == "custom" && $adjustment->getLabel() == "GST" ) {
                    unset($adjustments[$index]);
                    $adjustments[] = new Adjustment([
                        'type' => 'custom',
                        'label' => 'GST',
                        'amount' => $new_price,
                        'included' => true,
                    ]);
                }
            }
        } else {
            $adjustments[] = new Adjustment([
                'type' => 'custom',
                'label' => 'GST',
                'amount' => $new_price,
                'included' => true,
            ]);
        }
        $order->setAdjustments($adjustments);
    }

Hope this is helpful to someone.

martin_klima’s picture

The patch #5 works perfectly. Thank you.

I tested it on Drupal Commerce 2.4 for local shipping inside country (Czech Republic). My shipping method has price defined included VAT (100 CZK). Store has also prices included VAT. The patch correctly applied standard VAT rate (21 %) to the shipping method, added VAT to the correct row in the order summary (+17.36 CZK to Standard VAT 21 % summary value, 100-100/1.21 = 17.36).

+1 to RTBC

phrabovcin’s picture

Reroll patch from #5. Apply shipping promotions (discounts) to shipping costs and calculate tax after promotions.

ayalon’s picture

We are also using thispatch. But there is a small flaw that should be improved:
The adjustment has the same label as the already added VAT. This looks a bit weird:

Before:
VAT EUR 9.73
VAT EUR 0.71

I improved this to:

VAT EUR 9.73
VAT Shipping EUR 0.71

ayalon’s picture

Status: Needs review » Reviewed & tested by the community

Status: Reviewed & tested by the community » Needs work

The last submitted patch, 10: shipping-tax-2874158-10_edit_label.patch, failed testing. View results

MegaChriz’s picture

@ayalon
I don't prefer the label "VAT Shipping". Can it be that you get two times VAT because of different tax rates? For that there is an other issue: #2881056: Can't distinguish two taxes of different percentages in the order summary.

Patch (from #9) does the trick for me too, but the code should ideally live in the Commerce Shipping project, not the Commerce project (in #2 it was already stated that the patch is meant as a temporary solution). That's why the patch fails to apply here.

Anyway, "Needs work" is the right status.

Koen.Pasman’s picture

I'm trying to add VAT over shipping costs to the VAT total as well. But this patch comes up empty as the $shipments array is empty. Any idea why this is empty for me order (that def. has shipping, using a custom ShippingMethod).
I see the shipment entity is only added after filling in your delivery address, after this, the VAT gets calculated correctly in my case with this patch. Great :)

mirom’s picture

Status: Needs work » Needs review
Issue tags: +Needs tests
FileSize
0 bytes

I tried to create custom ShippingVat tax type, let me know if it's working.

mirom’s picture

Wrong patch in previous comment

The last submitted patch, 15: shipping-tax-2874158-15.patch, failed testing. View results

Status: Needs review » Needs work

The last submitted patch, 16: shipping-tax-2874158-15.patch, failed testing. View results
- codesniffer_fixes.patch Interdiff of automated coding standards fixes only.

mirom’s picture

Status: Needs work » Needs review
Berdir’s picture

So, I've been testing this a bit and got it working, but needed to make a few adjustments, will add comments in a self-review once posted. At least one change is probably a bit problematic, might need to be configurable?

Berdir’s picture

  1. +++ b/src/Plugin/Commerce/TaxType/ShippingVat.php
    @@ -19,31 +27,34 @@ class ShippingVat extends TaxTypeBase {
         }
    -    $adjustments = $order->getAdjustments();
    +    $adjustments = $order->collectAdjustments();
         $rate = NULL;
    

    taxes are usually on the order items, so we have to loop over them all.

  2. +++ b/src/Plugin/Commerce/TaxType/ShippingVat.php
    @@ -19,31 +27,34 @@ class ShippingVat extends TaxTypeBase {
    -        /** @var \Drupal\commerce_tax\Plugin\Commerce\TaxType\TaxTypeInterface $plugin */
    -        $plugin = $taxTypeManager->createInstance($pluginId);
    -        if (method_exists($plugin, 'getZones')) {
    +        list($tax_type_id, $zoneId, $rateId) = explode('|', $sourceString);
    +
    +        /** @var \Drupal\commerce_tax\Entity\TaxTypeInterface $tax_type */
    +        $tax_type = $this->entityTypeManager->getStorage('commerce_tax_type')->load($tax_type_id);
    +
    +        if (method_exists($tax_type->getPlugin(), 'getZones')) {
               /** @var \Drupal\commerce_tax\TaxZone[] $zones */
    

    the first key is the entity id, not the plugin id, those can be the same but there is no reason for them to be.

    So instead of loading the plugin directly, we need to load the tax type and then get the plugin from that.

  3. +++ b/src/Plugin/Commerce/TaxType/ShippingVat.php
    @@ -80,10 +91,10 @@ class ShippingVat extends TaxTypeBase {
             $order->addAdjustment(new Adjustment([
               'type' => 'tax',
    -          'label' => $zone->getDisplayLabel() . ' ' . t('Shipping'),
    +          'label' => $zone->getDisplayLabel(),
               'amount' => $tax_amount,
               'percentage' => $percentage->getNumber(),
    -          'source_id' => $this->entityId . '|' . $zone->getId() . '|' . $rate->getId(),
    +          'source_id' => $rate_tax_type->id() . '|' . $zone->getId() . '|' . $rate->getId(),
               'included' => $this->isDisplayInclusive(),
             ]));
    

    In my case, I need to have a single VAT line in the report, which IMHO makes sense especially for display inclusive, not sure about others or when it's about multiple rates.

    So possibly this part needs a setting or so?

MegaChriz’s picture

In my case, I need to have a single VAT line in the report, which IMHO makes sense especially for display inclusive, not sure about others or when it's about multiple rates.

On a webshop from a client of mine, the client requested to always apply the standard rate to the shipping costs, even when all products in the cart are using the reduced rate. Because the client's webshop is B2B, the shipping costs are displayed exclusive VAT. I think that all taxes of the same rate should be added up, but different rates should always be shown separately.

Example with mixed rates, showing prices exclusive of VAT:

Product Tax rate Price total
Book Reduced (6%) 10
Event Standard (20%) 100
Subtotal 110
Shipping 20
VAT 6% 0.60
VAT 20% 24
Total 154.60

Note that VAT 20% is the sum of the tax amount of the "Event" product and the tax amount of the shipping costs as on both the same rate is applied.

So possibly this part needs a setting or so?

I agree that a setting on which rate to apply to the shipping costs would be nice.

aumcara’s picture

The way MegaChriz define it in his last message is imho the way to go at least for countries like NL (MegaChriz) and BE (me) ... Post and shipping cost are charged with Country's standard rate.

The way MegaChriz demonstrate it is just perfect !

Congrats for sharing.

Berdir’s picture

Right, that shouldn't be a big deal to set up, add a configuration setting to that plugin, something like "Report shipping VAT separately", and depending on that, either use its own entity id for the source ID like the previous patch or the one from the used tax type.

Same for the label, however, the separate label should either be a translatable string with a placeholder, something like "@rate_label Shipping" or possibly even completely configurable as another (translatable) setting?

aumcara’s picture

:-/ (to be straight, this is my first patching experience with patches from the Drupal communitiy...

I use the Dev version of commerce_shipping, I applied #16 (from mirom) ... I did not see any changes in the way VAT was added to the shipping costs (maybe I did something wrong).
After that I did apply #20 interdiff on the file with #16 applied ... and I did not see taxes amount changes with the extra VAT from the shipping rate :-(

I might be wrong but I don't see what I could have done wrong and don't understand the remarks Passes next to those 2 patches as for me it did not change anything.

:-/ help me if you see I did something wrong in the way these patches have to be applied.

Thanks.

mirom’s picture

Hi, in #16, there were some parts missing, so it wasn't working at all. Patch in #20 should do that. In order to apply taxes, you need to create instance of that plugin - https://www.flocondetoile.fr/blog/apply-vat-rate-product-drupal-commerce-2.

aumcara’s picture

a) I did follow the reco of "mirom" ... still no VAT on shipping shown (with patch #16 & #20 applied like explained earlier)

Actions taken:
1) added a VAT plugin for Shipping VAT I test it with and without the chekbox selected for the price of shipping cost incl. or excl. VAT Shipping
2) I cleared the cache, run the cron

Test in both ways ... nothing ...

Results:
- the price of the Shipping remain unchanged (with or without the checkbox for vat included or not in the price of the shipping :-P)
- the VAT amount remain the same and unchanged no VAT Shipping added to the original VAT amount.

Here is what it gives me (I am in Belgium (VAT 21%)

Test Product Price : 16,49 € (excl.VAT 21%)
Shipping cost : 3,10 EUR (excl. VAT 21%) (based on weight but we don't care in our case)

Here is what I have when I simulate a purchase (https://michel.basix-skin-repair-cream.com/web/product/2)

1 x Test Product 50 ml 16,49 € (OK)

Subtotal 16,49 € (OK)
Expédition (Shipping) 3,10 € (OK)
TVA (VAT 21%) 3,46 € (Not OK cause 3,46 is VAT of 21% on the Test PRoduct ... it should be 4,11 €)
Total 23,03 € (not OK, should 23,70 €)

I persist and agree with the way MegaChriz explain the concept for EU (at least).

I have some difficulties to understand how do other people in EU deal with such case in their shop ... as this should be like a standard way to deal with shipping VAT in the EU.

I just don't know what to do in order to have this working and launch a shop under commerce 2 in the EU (in Belgium in my case)

Is there an alternative in order to have this to work even if it's while waiting for a better solution ?

THanks for your understanding.

MegaChriz’s picture

@aumcara

Is there an alternative in order to have this to work even if it's while waiting for a better solution ?

I'm using the patch in #9 for the time being - which worked for me, but I've put testing the patch from #20 on my todo list.

aumcara’s picture

@MegaChriz

Many thanks for your quick reply :-)
Can I simply patch my #20 with #9 or do I need to revert to the original ? (sorry new in patching :-P )

MegaChriz’s picture

@aumcara
If you decide to use the patch in #9 for now, don't use the patch in #20, so revert Commerce Shipping to the original. Note that the patch in #9 needs to be applied to Commerce instead of Commerce Shipping, that's why the patch in #9 is only a temporary solution: Commerce should not have Commerce Shipping specific code, as Commerce should not depend on Commerce Shipping.

aumcara’s picture

@MegaChriz

I follow your recommandation and it work perfectly ! Thanks.
So for me #9 is OK but indeed need to be placed out of commerce.

calbasi’s picture

@mirom what do you mean with:

In order to apply taxes, you need to create instance of that plugin - https://www.flocondetoile.fr/blog/apply-vat-rate-product-drupal-commerce-2.

Well, I have an instance of that plugin, to set standatrd and reduced Spanish VAT tax... But do I need another instance to apply a tax to shipping fee?

Like @aumcara says at #27 I can get any shipping tax using patch #20. But I just applied the patch without uninstall/install Commerce Shipping module (is this needed??)

calbasi’s picture

By the way, applying patch #9 broke my site (Commerce 2.10, drupal 8.6.2).

agoradesign’s picture

How did it break your site? I'm using the one from #10 without any problems on the same setup. Without comparison, I think that #9 and #10 are quite similar to each other (at least, when you look at the comments)

mirom’s picture

@calbasi - I'm not expecting #9 to work with current releases :)
As for #20 - yes I think you need to create another instance of this shipping plugin in order to get VAT for shipping, but I might be wrong. We definitely need tests for this :)

Alexandre360’s picture

is #20 usable actually. Really need to be able to apply tax on shipping.

TwiiK’s picture

I tried the patch in #20 and the approach is wrong for Norway, at least from my understanding of how it's supposed to work for Norway.

I feel like I can adjust it to work how we need, but I'm not sure if that will affect how it works for other countries. Maybe the approach here is naive and there can be no generic shipping tax type?

What it does wrong for Norway is that we have a standard (25%) tax for normal goods and a lower (15%) tax for food products. The shipping tax should be based on the type of product purchased so if I buy a food product there should only be 15% tax for the shipping, if I buy a normal product there should be 25% tax for shipping and if I buy both normal products and food products in the same order the shipping tax should take into account how large of a percentage of the order subtotal each tax type is and be calculated based on that.

Some examples of how it should work for Norway:

A) An order with a shoe costing 100 NOK with 50 NOK shipping:

Shoe: 100 NOK
Subtotal: 100 NOK
Shipping: 50 NOK
Total: 150 NOK

Tax for shoe (25%): 100 - (100 / 1.25) = 20 NOK
Tax for shipping: 50 - (50 / 1.25) = 10 NOK
Total tax: 30 NOK

B) An order with a chocolate costing 25 NOK with 50 NOK shipping:

Chocolate: 25 NOK
Subtotal: 25 NOK
Shipping: 50 NOK
Total: 75 NOK

Tax for chocolate (15%): 25 - (25 / 1.15) = 3.260869565 NOK
Tax for shipping: 50 - (50 / 1.15) = 6.52173913 NOK
Total tax: 9.782608695 NOK

C) An order with both a shoe costing 100 NOK and a chocolate costing 25 NOK with 50 NOK shipping:

Shoe: 100 NOK
Chocolate: 25 NOK
Subtotal: 125 NOK
Shipping: 50 NOK
Total: 175 NOK

Tax for shoe (25%): 100 - (100 / 1.25) = 20 NOK
Tax for chocolate (15%): 25 - (25 / 1.15) = 3.260869565 NOK
The shoe is 100 / 125 = 0.8 or 80% of the subtotal meaning 80% of the shipping tax should be for the shoe.
Tax for shoe shipping: (0.8 * 50) * (1 - (1 / 1.25)) = 8 NOK
The chocolate is 25 / 125 = 0.2 or 20% or of the subtotal meaning 20% of the shipping tax should be for the chocolate.
Tax for chocolate shipping: (0.2 * 50) * (1 - (1 / 1.15)) = 1,304347826 NOK
Total tax: 32,565217391 NOK

I've written that the shipping tax is for the shoe and the chocolate here, but they would be for the respective tax types so if I had multiple clothing items and multiple food items on the same order you would accumulate those together and end up with a shipping tax for the food items and a shipping tax for the clothing items.

The full formula would be something like:

(((({food_subtotal} / {subtotal}) * {shipping_amount}) + {food_subtotal}) * (1 - (1 / {food_tax_percentage}))) + (((({normal_subtotal} / {subtotal}) * {shipping_amount}) + {normal_subtotal}) * (1 - (1 / {normal_tax_percentage})))

Whether or not that tax amount should be applied to the order as a single adjustment or one per tax type I don't know yet, I will have to experiment with that and see. We're also using an offsite payment gateway (Klarna Checkout) which expects the entire order (not just an order total) and then calculates its own discounts, tax, shipping etc. based on the Drupal order so I will have to figure out how to best pass these tax values to that payment gateway as well.

PS: All my examples and assumptions are for inclusive tax. No idea if anything would work differently for exclusive tax (prices entered without tax).

Edit: We're still investigating this as initially we thought shipping was a service which had a fixed 25% tax in Norway, but we've seen more webshops handle it the way I've described above than those who just have a fixed 25% tax on shipping. We're in the process of implementing this on our own sites and we want to do it correctly. We're trying to get hold of someone who can give us a definitive answer rather than us just guessing how it should be. It may turn out to be quite a bit simpler than what I've described above. One counter-argument to the above is that if you have exclusive tax then suddenly the shipping amount would depend on the items in your cart, ie. it would be more expensive to ship a chocolate than a shoe even if both cost the same, which seems wrong.

zaporylie’s picture

Issue summary: View changes
FileSize
222.2 KB

Seems like it is the same as in Sweden then - http://www.skatteverket.se/rattsinformation/skrivelser2005/02/skrivelser.... Could you share legal basis for Norway?

Not that it changes anything but I can see the stores are not implementing it correctly - see the example below from https://www.lavpriskurven.no:

TwiiK’s picture

Heh, I found the same site as an example of the alternative way of doing it (fixed 25% tax on shipping regardless of cart contents). Like I said in the edit of my post we're not sure what's correct yet. Thanks for the link to skatteverket, that makes it seem likely this is the same for Norway as well, but we've found no similar articles describing how it works in Norway so we're in the process of trying to contact someone who can tell us for sure.

Will update here when I know for sure and I won't bother writing any code until I do because one alternative is a lot simpler than the other. :p

bbuchert’s picture

Just to add the rules for Germany: Taxing Shipping in Germany is quite complicated. As the VAT is dependent on the VAT of the products. So if the VAT of the products is 19% the shipping VAT is 19% if it’s 7% is 7%. If it’s mixed it can be mixed according to the value of the cart items. https://www.it-recht-kanzlei.de/umsatzsteuer-versandkosten-mehrwertsteue...

But I think just putting a flat rate of 19% on all shipping should be possible as well.

mirom’s picture

Looks like we have two main variations:
- Charge the standard (default) rate
- Charge the highest applied rate

Cool, so current patch covers something in between - apply the highest applied rate, but if the highest applied rate is less then the default then apply default. Could you modify patch? It should be quite simple...

csedax90’s picture

For me #20 is working fine

parisek’s picture

Finally figured out how to apply patch #20 for my commerce site. I have multiple tax types so I need run them in correct order. Unfortunately Commerce module do not allow that so you need patch from #2999704: Add weight for TaxTypes first. Then I need to modify #20 and add weight parameter to annotation.

/**
 * Provides the Shipping VAT tax type.
 *
 * @CommerceTaxType(
 *   id = "commerce_shipping_vat",
 *   label = "Shipping VAT",
 *   weight = 10,
 * )
 */
Berdir’s picture

And now with the weight committed, adding it with that value seems to be required.

flocondetoile’s picture

Patch #44 works fine with commerce 2.16

So it remains to add (or not) a setting on which rate to apply to the shipping (the highest rate found on the order VS the default/standard rate) ?

bojanz’s picture

Assigned: Unassigned » bojanz
Status: Needs review » Needs work

Working on this.

We should start using shipment adjustments, introduced in #2996465: Put back adjustments to Shipments. We need proportional taxes, we need a settings form for selecting the strategy, we need tests.

bojanz’s picture

Status: Needs work » Needs review
Issue tags: -Needs tests
FileSize
29.57 KB
135 KB

Here's a version built from scratch. Uses adjustments, has test coverage, supports all three strategies.

Ideally the plugin would choose the strategy automatically, based on the shipping country, but there is almost no information online on which country uses which strategy, so we don't have a choice other than to make it configurable. We support limiting by store, to allow different stores to use different strategies (e.g. proportional in Germany and Sweden, default everywhere else).

In the US only certain states tax shipping, so we also want to be able to limit by territory. Opened a followup for that: #3108599: Allow restricting the Shipping tax type by territory. I was surprised to learn that some US states use the proportional strategy instead of the default one, not calculating shipping tax on the non-taxable portion of the order total.

Jonathan (jsacksick) and I had a discussion yesterday on order VS shipment. Are we choosing the order's highest rate or the highest rate of the order items in the shipment. Same with proportional. We consulted David Kitchen and he said "order", so going with that. It would be a minimal change to change the logic to shipment, if we find proof that is more precise. For most sites, with have a shipment per order, it doesn't matter.

Notes:
- Requires shipping -dev. If you had a previous patch applied, delete the tax type you created before replacing the patch, since the plugin ID has changed.
- The "default" strategy won't work properly for sites that use multiple shipments and an address per shipment (since it assumes one tax type per order). This is not a use case we support officially, and it raises other tax questions, so punting on that for now.
- Turns out I was also the one who implemented tax for Shipping 7.x-1.x, 8.5 years ago. Fun.

jsacksick’s picture

Status: Needs review » Reviewed & tested by the community

I haven't manually tested the patch but I just reviewed it and it looks really good and ready to go!

  • bojanz committed 8921b07 on 8.x-2.x
    Issue #2874158 by mirom, Berdir, bojanz, ayalon, phrabovcin, jasonschweb...
bojanz’s picture

Status: Reviewed & tested by the community » Fixed

Committed. Jonathan and I will be testing this patch on a client project this week.

I encourage others to test -dev and report back on this issue, so that we can handle any needed followups before tagging the next release.

flocondetoile’s picture

Test on a french site with vat, for both scenarii : highest rate and default rate. Works fine. Thanks.

Just a question : there isn't anymore the option "Display tax as inclusive" in the plugin configuration form?

Looks like you forgot to call the parent in the buildConfigurationForm(), right ?

bojanz’s picture

Not forgotten, explicitly not supported. We take the "display inclusive" setting from the applied tax type (or rather, the adjustment it produces), no point in asking the merchant twice.

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.

petednz’s picture

(appreciate this is an old/closed/fixed ticket but hopefully is okay to ask here for tiny bit of documentation - have read most links in ticket to other documentation and hints but not spotted the answer)

thx for the collective brain power that went in to this. Am on 8.x-2.4 so this fix would be included. Wondering if anyone can give me a tiny push in right direction.

I was missing something obvious -

> If you're using Commerce Tax, create a "Shipping" tax type. This will ensure that shipping costs are taxed.

and Tax Type offers an option under Plugins called shipping - duh. sorry for the noise