Jump to content

EU taxes for digital products


Jan Fromm
 Share

Recommended Posts

Hi @kongondo,

I would be interested to know to what degree Padloper handles EU taxes for digital products, and what may need to be added by the developer. As I’m sure you know, this varies greatly depending on whether the seller is based in the EU or not, whether the customer is based in the EU or not, and whether it's private (without VAT number) or business (with VAT number).

There is this option »Charge EU Digital Goods VAT Taxes« in the tax settings. As far as I can see, this has no effect yet. Or did I miss something?

Is this still WIP? Or what are your plans?

Thanks!

Link to comment
Share on other sites

Hi @Jan Fromm,

5 hours ago, Jan Fromm said:

There is this option »Charge EU Digital Goods VAT Taxes« in the tax settings. As far as I can see, this has no effect yet.

It does have an effect, unless I recently broke something ?. I've just tested the below and it works fine.

5 hours ago, Jan Fromm said:

to what degree Padloper handles EU taxes for digital products, and what may need to be added by the developer.

Currently this is limited to the following:

  1. A product has been designated as NOT taxable.
  2. The product is specified as a digital product.
  3. The Charge EU Digital Goods VAT Taxes is ticked.
  4. The customer country is IN the EU (proceed to #6).
  5. The customer country is NOT in the EU (proceed to #7).
  6. (EU Digital Goods) Tax will be charged on this product. (unless it is a manual order and the order or the customer has been exempted from tax).
  7. Taxes will not be charged on this product (unless it was specified as taxable in #1).
5 hours ago, Jan Fromm said:

As I’m sure you know, this varies greatly depending on whether the seller is based in the EU or not, whether the customer is based in the EU or not, and whether it's private (without VAT number) or business (with VAT number).

These complexities (at least currently) would have to be handled by the developer. I'm not sure that we currently have somewhere to hook into. I'll have a look.

Link to comment
Share on other sites

2 hours ago, kongondo said:
  1. A product has been designated as NOT taxable.

That was the trick. Thanks!

Assuming that only digital products are to be sold, would it be enough to only care about the tax rates of EU countries? The other countries could be created without tax rates, right?

2 hours ago, kongondo said:

These complexities (at least currently) would have to be handled by the developer. I'm not sure that we currently have somewhere to hook into. I'll have a look.

Fantastic. Thanks again.

Link to comment
Share on other sites

19 minutes ago, Jan Fromm said:

Assuming that only digital products are to be sold, would it be enough to only care about the tax rates of EU countries? The other countries could be created without tax rates, right?

Theoretically, yes. However, you would need to be aware of any laws similar to the EU Digital Goods regulations.

Not closely related, but I forgot to mention (and you are probably aware of this) that we also have tax overrides that can be applied per category of products or shipping.

22 minutes ago, Jan Fromm said:

That was the trick. Thanks!

Glad you got it sorted.

  • Like 1
Link to comment
Share on other sites

  • 4 weeks later...

Hi @kongondo,

On 6/23/2022 at 6:55 PM, kongondo said:

These complexities (at least currently) would have to be handled by the developer. I'm not sure that we currently have somewhere to hook into. I'll have a look.

Have you been able to check this?

I am currently thinking about how I could add this functionality. My current idea looks like this:

1) Add new textfield »VAT« to custom customer form

2) Hook processCustomOrderCustomerForm and process VAT validation

3) Hook isChargeEUDigitalGoodsTax
At the moment, digital goods taxes are collected IF
1 It is set in the store settings AND
2 The customer is located in the EU AND
3 It is a digital product
This must be extended by
4 The customer does NOT have a valid VAT number, unless he is located in the same country as the shop owner.

Is this possible? There are some pitfalls for sure, but that would be my approach. Thanks further hints, ideas or doubts!

Link to comment
Share on other sites

9 hours ago, Jan Fromm said:

Have you been able to check this?

@Jan Fromm,

Sorry this slipped through the cracks!

9 hours ago, Jan Fromm said:

1) Add new textfield »VAT« to custom customer form

That would work..., yes.

9 hours ago, Jan Fromm said:

2) Hook processCustomOrderCustomerForm and process VAT validation

This could work. How do you intend to do the validation? I am asking to see where best to hook.

9 hours ago, Jan Fromm said:

3) Hook isChargeEUDigitalGoodsTax

Yes, good idea.

Link to comment
Share on other sites

17 hours ago, kongondo said:

This could work. How do you intend to do the validation? I am asking to see where best to hook.

Probably with a SOAP call to the EU VAT Information Exchange System (VIES). 

14 hours ago, kongondo said:

Just to add to my question, do you want to tell the customer if the validation fails?

That would already make sense.

Thanks a lot!

  • Like 1
Link to comment
Share on other sites

Hi @Jan Fromm,

The Hooks are now ready per the latest release today (004). A demo implementation is here (demo 3) in the demo repo.

The hooks are (per demo linked to above):

PadloperProcessOrder::checkCustomOrderCustomerFormForErrors

This will allow you to reject business customer form and return it to them for amendment, .e.g., in case they stated they are a business customer but did not fill in the VAT number or VAT number is invalid. It is better to hook here instead of PadloperProcessOrder::processCustomOrderCustomerForm as it gives you early access to the form. Please have a look at the demo and let me know if you have any questions.

and

PadloperUtilities::isChargeEUDigitalGoodsTax

Please see the demo for an example. This will allow you to apply/exempt EU digital tax per line item in the order based on complex conditions as you state above. Note that this demo is not a robust solution compared to the VAT library you have linked to. For instance, the demo doesn't check for country-specific prefixes in the VAT number.

Please let me know if you have any questions.

Grab version 004 using your purchase link, per usual.

Thanks.

 

  • Like 1
Link to comment
Share on other sites

  • 3 months later...

Hi @kongondo,

Sorry for the long silence. I finally had a chance to look into the demo and am amazed that your VAT validation code is already 99% functional! I just had to take into account the country prefixes and now it runs like a charm.

Anyway, one milestone passed, and the next one is just around the corner. I’d like to store the customers VAT number in OrderCustomer so I can display it on the confirmation page and on the invoice. I guess I need to add a custom field to the OrderCustomer template, right? I’m not sure where to do that, unfortunately.

My second issue is to show a note on the invoice if Reverse Charge applies (customer has valid VAT number and gets an invoice without VAT). My idea is to hook in __orderSaved and add this hint to the order using the Notes feature. Unfortunately I have not found any information about this in the demos and the forum.

As always, I appreciate any hints and help. Thanks!

  • Like 1
Link to comment
Share on other sites

On 10/27/2022 at 12:49 PM, Jan Fromm said:

I just had to take into account the country prefixes and now it runs like a charm.

Hi @Jan Fromm. Glad it worked!

On 10/27/2022 at 12:49 PM, Jan Fromm said:

I’d like to store the customers VAT number in OrderCustomer so I can display it on the confirmation page and on the invoice. I guess I need to add a custom field to the OrderCustomer template, right? I’m not sure where to do that, unfortunately.

Yes, you will need a custom field.  Customers do not have their own template You would need to add the field to the order template itself (padloper-order). Please see Demo 2. It explains how you can do this.

On 10/27/2022 at 12:49 PM, Jan Fromm said:

My idea is to hook in __orderSaved and add this hint to the order using the Notes feature.

Good idea. But there are alternatives with respect to the Notes feature. Also see the caveats below.

On 10/27/2022 at 12:49 PM, Jan Fromm said:

Unfortunately I have not found any information about this in the demos and the forum.

Apologies. Documentation is still lacking. Notes are stored in the field padloper_notes. This field is used in the template padloper-order. Padloper notes are a type of WireArray. You have access to all the methods of WireArray. 

Back to notes...

On 10/27/2022 at 12:49 PM, Jan Fromm said:

My second issue is to show a note on the invoice if Reverse Charge applies (customer has valid VAT number and gets an invoice without VAT).

Caveats

Dedicated Text or Custom Field OR Notes

Given that padloper_notes can take multiple notes and given that some of these are generated programmatically, using it to save a very specific note as described in your case might not be the better approach. For instance, you would need to search the WireArray of Notes for your 'reverse charges hint note' before you can display it on their invoice. Searching is not a big deal as you would be searching the WireArray in-memory, however, you will need some key words to match the note you are looking for amongst the order's other notes. An alternative is to add and use a dedicated text field in the order's template similar to the customer's VAT number above. A third alternative is to create your own custom field to store both notes and VAT numbers.  Just a few thoughts.

If you want to add a note using the API, it is quite simple as shown below. This example assumes you are doing this via a Hook.

<?php

namespace ProcessWire;

# CONTENT OF A HOOK METHOD #

/*
if hooking $this->addHookAfter('PadloperProcessOrder::orderSaved', null, 'nameOfYourHookFunction');
*/

// Retrieve argument by name
/** @var Page $orderPage */
$orderPage = $event->arguments('orderPage');

$orderNoteText = "The text of my note. This customer has a VAT...";

# @note: this method will add tje created and modified times for you
# you can add your user ID if you want, as the 3rd argument to this method
/** @var WireData $note */
$note = $padloper->buildNote($orderNoteText,'admin');
$orderPage->of(false);
// add the $note to the order page
$orderPage->padloper_notes->add($note);
// save the page
$orderPage->save('padloper_notes');
# --------- OR -------------
// $notes = $orderPage->padloper_notes;
// $notes->add($note);
// $orderPage->setAndSave('padloper_notes', $notes);

Method to Hook

Usually hooking into __orderSaved  would be fine. However, this method will be called every time the order is amended during the payment process. For instance, a customer can go to checkout and enter their details. This will cause an order to be created. Whilst in the order confirmation/payment page, the customer could decide to amend their shopping cart and subsequently head to the checkout. Since we already have an order created, it won't be recreated, however, it will be amended if needed, hence orderSaved() will be called again. This might mean your note would be created multiple times. You might not want this. You could always check if such a note exists for the given order and amend it if necessary. To avoid this, it could be better to hook into the PadloperProcessOrder::sendConfirmation() method instead as shown below. This method will be  called only once, when the order checkout processing is complete.

 

<?php

namespace ProcessWire;

# CONTENT OF A HOOK METHOD #


/*
if hooking $this->addHookAfter('PadloperProcessOrder::sendConfirmation', null, 'nameOfYourHookFunction');
*/

$padloper = wire('padloper');
// @NOTE: Retrieve argument by name <- won't work since argument value is Null
// instead, we get the orderPage from the session
/** @var Page $orderPage */
$orderPage = $padloper->getOrderPage();

//  -------------
$orderNoteText = "The text of my note if sendConfirmation() Hook. This customer has a VAT...";

# @note: this method will add the created and modified times for you
# you can add your user ID if you want, as the 3rd argument to this method
/** @var WireData $note */
$note = $padloper->buildNote($orderNoteText, 'admin');
$orderPage->of(false);
// add the $note to the order page
$orderPage->padloper_notes->add($note);
// save the page
$orderPage->save('padloper_notes');

 Hope this helps! 

Edited by kongondo
typos
  • Thanks 2
Link to comment
Share on other sites

On 11/3/2022 at 6:32 PM, kongondo said:

 Hope this helps! 

It did! I got it to run with two custom fields in the order-template. These are populated with a hook in __orderSaved and then shown at checkout-confirmation and on the invoice.

Thanks again for your help, @kongondo!

  • Like 2
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...