Jump to content
  • Checkout
  • Login
  • Get in touch

osCommerce

The e-commerce.

Sale of intangible products. New VAT rules in EU (2015)


milerwan

Recommended Posts

Since 2015, the new VAT rules on the sale of dematerialized products within the European Union require the seller to apply the VAT of the country of the buyer in the case of a sale of dematerialized product or service.

So, i there a contribution for 2.3.4 BS which can meet this new exigency ?

And if not, what changes (or tricks) would you see to bring to oscommerce to achieve this result ?

Thank you for help.

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

You can do this with the out-of-the-box features, no addon required.

You need to setup many tax rates, tax zones etc. Search for a documentation, it has been discussed here.

Only prolem: No idea how to display the correct tax rates to a customer BEFORE he logs in, as it is required per law.

Link to comment
Share on other sites

18 minutes ago, inra311 said:

You can do this with the out-of-the-box features, no addon required.

You need to setup many tax rates, tax zones etc. Search for a documentation, it has been discussed here.

Only prolem: No idea how to display the correct tax rates to a customer BEFORE he logs in, as it is required per law.

Ok for the tax zones... I see what I have to do ! :smile:

I have to create a Tax Class so-called "VAT material" and another "VAT service".
And I have to create for each EU countries a "Tax Zone" liable to Tax Class "VAT service".

Thank you Ingo. :thumbsup:

 

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

1 hour ago, inra311 said:

Only prolem: No idea how to display the correct tax rates to a customer BEFORE he logs in, as it is required per law.

The order totals module included in ship in cart:

https://apps.oscommerce.com/YLYGm&ship-in-cart-bs-reloaded

will at least show the tax for the selected country (and zone) for guests in the shopping cart.

These modules (shipping and order total for guests) might be a starting point to create a navbar module for guest- country (state) selection to show the products price everywhere with accurate tax.

You gave me an idea for this, I'll try when I finished updating all my add-ons to CE Frozen and find some time.

A nice addition (right now only for logged in customers) might be:

Display Tax Info

Link to comment
Share on other sites

3 hours ago, inra311 said:

Only prolem: No idea how to display the correct tax rates to a customer BEFORE he logs in, as it is required per law.

Is there any mechanism for osC to use the IP address to guess the country a guest is in (before login), and allow them to update it from a menu? Otherwise, this law sounds like another brain-dead EU deal where you're going to have to display multiple prices for each product, at different VAT rates, for all the possible countries. Not until the guest reveals what country they're in (or logs in, and their address is known) can you collapse it to a single price. I guess you could put the assumed VAT location next to the displayed price, and invite the shopper to update it to get the correct VAT. I wouldn't be surprised if an ISP or browser supplying the location (including country) would be considered a GDPR violation!

At least in the US, prices are displayed sans tax, and not until the shipping address is known do you have to deal with the state/local sales tax rates. There is no VAT and there is no national sales tax.

Link to comment
Share on other sites

6 hours ago, milerwan said:

Ok for the tax zones... I see what I have to do ! :smile:

I have to create a Tax Class so-called "VAT material" and another "VAT service".
And I have to create for each EU countries a "Tax Zone" liable to Tax Class "VAT service".

 

Right, I have tax zones for each possible rate, called e.g. Downloads 17%, Downloads 18%, Downloads 19%,.... Within these "folders" you add all the countries with the respective vat rate.

See pictures, they say more than words.

In the product description, you select 'Downloads' for each virtual product.

image.png.ab43fa3995eac390fd897e1330a511ff.png

image.png.4294463d56566f730241ede0a78574db.pngimage.png.9fdd2227317af8b9b1b335042454cb49.pngimage.png.e2e8a30dd8738254042469551ad93839.png

Link to comment
Share on other sites

4 hours ago, MrPhil said:

Is there any mechanism for osC to use the IP address to guess the country a guest is in (before login), and allow them to update it from a menu? Otherwise, this law sounds like another brain-dead EU deal where you're going to have to display multiple prices for each product, at different VAT rates, for all the possible countries. Not until the guest reveals what country they're in (or logs in, and their address is known) can you collapse it to a single price. I guess you could put the assumed VAT location next to the displayed price, and invite the shopper to update it to get the correct VAT. I wouldn't be surprised if an ISP or browser supplying the location (including country) would be considered a GDPR violation!

At least in the US, prices are displayed sans tax, and not until the shipping address is known do you have to deal with the state/local sales tax rates. There is no VAT and there is no national sales tax.

i have been using this contribution for a while : Display Prices with Tax by IP (https://apps.oscommerce.com/tvCFQ&display-prices-with-tax-by-ip)

With it, customer gets its country tax rate without need of log in !


Otherwise, I have modified the "Ship in Cart BS" addon to add this "Tax change feature" depending selecting country from it.

in "cm_cart_shipping_estimator.php",

1. find this :

    function execute() {
      global $oscTemplate, $currencies, $currency, $request_type, $cart, $order, $total_count, $quotes, $method, $module, $shipping, $navigation, $cart_sid, $cart_country_id, $cart_zone, $cart_zip_code, $cart_address_id, $total_weight, $language, $customer_id, $sendto;

And add $country_id_by_ip variable like this :

    function execute() {
// BOF Display Prices with Tax via Customer IP
//      global $oscTemplate, $currencies, $currency, $request_type, $cart, $order, $total_count, $quotes, $method, $module, $shipping, $navigation, $cart_sid, $cart_country_id, $cart_zone, $cart_zip_code, $cart_address_id, $total_weight, $language, $customer_id, $sendto;
      global $oscTemplate, $currencies, $currency, $request_type, $cart, $order, $total_count, $quotes, $method, $module, $shipping, $navigation, $cart_sid, $cart_country_id, $cart_zone, $cart_zip_code, $cart_address_id, $total_weight, $language, $customer_id, $sendto, $country_id_by_ip;
// EOF Display Prices with Tax via Customer IP

 

2. Then find this :

		      	// session is available
		      	$country_info = tep_get_countries($cart_country_id,true);
		      	$order->delivery = array();
		      	$order->delivery = array('postcode' => $cart_zip_code,
		                                 'country' => array('id' => $cart_country_id, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
		                                 'country_id' => $cart_country_id,
		                                 'format_id' => tep_get_address_format_id($cart_country_id),
		                                 'zone_id' => $cart_zone);
		      } else {
		      	// first timer
		      	$cart_country_id = STORE_COUNTRY;
		      	tep_session_register('cart_country_id');
		      	$country_info = tep_get_countries(STORE_COUNTRY,true);
		      	tep_session_register('cart_zip_code');
		      	$order->delivery = array();
		      	$order->delivery = array('postcode' => MODULE_CONTENT_CART_SHIPPING_ESTIMATOR_DEFAULT_ZIP,
		                                 'country' => array('id' => STORE_COUNTRY, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
		                                 'country_id' => STORE_COUNTRY,
		                                 'format_id' => tep_get_address_format_id($cart_country_id),
		                                 'zone_id' => STORE_ZONE);
		      }
		      // set the cost to be able to calculate free shipping

And change like this :

		      	// session is available
		      	$country_info = tep_get_countries($cart_country_id,true);
		      	$order->delivery = array();
		      	$order->delivery = array('postcode' => $cart_zip_code,
		                                 'country' => array('id' => $cart_country_id, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
		                                 'country_id' => $cart_country_id,
		                                 'format_id' => tep_get_address_format_id($cart_country_id),
		                                 'zone_id' => $cart_zone);
		      } else {
		      	// first timer
// BOF Display Prices with Tax via Customer IP
//		      	$cart_country_id = STORE_COUNTRY;
		      	$cart_country_id = ( (($country_id_by_ip > 0) && (MODULE_HEADER_TAGS_COUNTRY_STATUS == 'True')) ? $country_id_by_ip : STORE_COUNTRY);
// EOF Display Prices with Tax via Customer IP
		      	tep_session_register('cart_country_id');
// BOF Display Prices with Tax via Customer IP
//		      	$country_info = tep_get_countries(STORE_COUNTRY,true);
		      	$country_info = tep_get_countries($cart_country_id,true);
// EOF Display Prices with Tax via Customer IP
		      	tep_session_register('cart_zip_code');
		      	$order->delivery = array();
		      	$order->delivery = array('postcode' => MODULE_CONTENT_CART_SHIPPING_ESTIMATOR_DEFAULT_ZIP,
// BOF Display Prices with Tax via Customer IP
//		                                 'country' => array('id' => STORE_COUNTRY, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
		                                 'country' => array('id' => $cart_country_id, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
//		                                 'country' => array('id' => STORE_COUNTRY, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
		                                 'country' => array('id' => $cart_country_id, 'title' => $country_info['countries_name'], 'iso_code_2' => $country_info['countries_iso_code_2'], 'iso_code_3' =>  $country_info['countries_iso_code_3']),
//		                                 'country_id' => STORE_COUNTRY,
		                                 'country_id' => $cart_country_id,
		                                 'format_id' => tep_get_address_format_id($cart_country_id),
		                                 'zone_id' => STORE_ZONE);
// EOF Display Prices with Tax via Customer IP
		      }
		      // set the cost to be able to calculate free shipping

Of course you need to install Display Price with Tax by IP before:wink:

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

13 hours ago, inra311 said:

Right, I have tax zones for each possible rate, called e.g. Downloads 17%, Downloads 18%, Downloads 19%,.... Within these "folders" you add all the countries with the respective vat rate.

See pictures, they say more than words.

In the product description, you select 'Downloads' for each virtual product.

 

image.png.9fdd2227317af8b9b1b335042454cb49.png

 

 

For my part I opted to create a zone for each EU country, more easily identifiable in the event of a change in the country's tax system.
As tax rate is equal for immaterial services or for downloads, I named it "VAT service".

tax_rates_for_geo_zones.jpg

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

13 hours ago, milerwan said:

i have been using this contribution for a while : Display Prices with Tax by IP (https://apps.oscommerce.com/tvCFQ&display-prices-with-tax-by-ip)

Cool, I was not aware of such an addon (luckily there are so MANY :)  ) , and already modular as a header tag module, and only one line to add in functions/general :)

Will try it soon, thanks for the hint :thumbsup:

Link to comment
Share on other sites

What are the legal issues here? An IP address is not a guarantee that you know the locality (at least to the level of the country), so you could end up giving the wrong rate. Someone could be using a proxy or even be ordering while out of the country, resulting in the wrong price being displayed. Are you protected if you give the wrong price?

Link to comment
Share on other sites

21 hours ago, MrPhil said:

sound like another brain-dead EU deal

And with GDPR we need another  MATC  checkbox before sending out the IP to be externally checked, right? :ohmy:
First thing the visitor of our shop has to do!?:wacko:

 

50 minutes ago, MrPhil said:

Someone could be using a proxy or even be ordering while out of the country, resulting in the wrong price being displayed. Are you protected if you give the wrong price?

When buying at least the tax rate is based on the shipping address, right?

 

And what about virtual products, any customer just needs to enter a non-EU address and he does not pay any tax at all, I am afraid...

 

Link to comment
Share on other sites

18 hours ago, milerwan said:

i have been using this contribution for a while : Display Prices with Tax by IP (https://apps.oscommerce.com/tvCFQ&display-prices-with-tax-by-ip)

I tried that addon but it does not work. I fixed two DIR_WS_INCLUDES but still get lots of fatal errors, with fseek(), fopen(), fread(), ftell()... maybe it does not like PHP 7.1??

 

1 hour ago, MrPhil said:

What are the legal issues here? An IP address is not a guarantee that you know the locality (at least to the level of the country), so you could end up giving the wrong rate. Someone could be using a proxy or even be ordering while out of the country, resulting in the wrong price being displayed. Are you protected if you give the wrong price?

I think the law requires two independent verifactions for the VAT rate, like IP and shipping address..:huh:
I'd wish those who make such laws had to offer solutions as well :wacko:

Link to comment
Share on other sites

1 hour ago, inra311 said:

 

And what about virtual products, any customer just needs to enter a non-EU address and he does not pay any tax at all, I am afraid...

 

That's what I thought.
Nobody can verify the reality of the delivery address of the customer in the case of dematerialized.

They aren't really smart in the EU... :sleep:

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

@inra311, @MrPhil,

So I read the directive, the provider (seller) must check the purchase information in his possession and establish at least 2 "non-contradictory" elements of proofs to locate the buyer and validate his purchase.
For example, customer's IP address and his shopping habits (if usual customer), or billing address entered...

It promises to be happy this story. :tongue:

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

On 6/16/2018 at 1:07 PM, milerwan said:

For example, customer's IP address and his shopping habits (if usual customer), or billing address entered...

It promises to be happy this story. :tongue:

Hmm. "Shopping habits"? Likely a GDPR privacy violation. IP address? What if online by proxy or while traveling out of the country? Billing address? We're talking about guest shoppers, not logged-in members.

I don't think this is going to have a happy ending. More EU idiocy. Or at least, one hand doesn't know what the other is doing.

Link to comment
Share on other sites

@MrPhil If the IP address is different because the customer is out of the area and say on holiday, it would not match the location of the delivery address so would not be one of the two required non contradictory pieces needed. 

This is such a nightmare for the legislation and the paying of VAT I just stopped selling downloads to anyone in the EU apart from the UK. So much easier, but still does not stop those wise people who enter a UK address when buying outside of the UK. Just keeping separate details of every order and from which country the sale came from which is required to fill out the VAT MOSS form every month, if not every quarter, was not worth doing as I sell very few downloads. It was very cost prohibitive.

REMEMBER BACKUP, BACKUP AND BACKUP

Link to comment
Share on other sites

@14steve14, @inra311,

in includes/classes/order.php, I see that there is a content_type value that could be "virtual" and modify the tax_address :

if ($this->content_type == 'virtual') {
        $tax_address = array('entry_country_id' => $billing_address['entry_country_id'],
                             'entry_zone_id' => $billing_address['entry_zone_id']);
      } else {
        $tax_address = array('entry_country_id' => $shipping_address['entry_country_id'],
                             'entry_zone_id' => $shipping_address['entry_zone_id']);
      }

Where can we found this "content_type" value ?!? :blink:
Like for download access, how could we do to make a product a download item ?!?

I would like to be able to differentiate "download/virtual" products other than by their tax class.

Thank you.

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

5 minutes ago, milerwan said:

@14steve14, @inra311,

in includes/classes/order.php, I see that there is a content_type value that could be "virtual" and modify the tax_address :

Where can we found this "content_type" value ?!? :blink:
Like for download access, how could we do to make a product a download item ?!?

I would like to be able to differentiate "download/virtual" products from other than by their tax class.

Thank you.

Not sure if I understand your question. Where the virtual products are specified as virtual?

Maybe just when they have a download file linked to them under products attributes!? In the Filename field..

image.png.572648ded151b3d7cbd9536fa6b4550f.png

Link to comment
Share on other sites

My assumption since the first post is that VAT must be displayed for the product even before you know who the customer is. Is everyone else assuming that you don't need to figure the VAT until the customer gives his shipping address? I think we're working on two very different problems. Once you have been given the shipping address, it's easy to figure out the tax rate (merely by country in this case... within US states with multiple tax jurisdictions, it's a nightmare requiring geolocation from the address and determining which county and city the buyer is within). Not having to display VAT to random shoppers would greatly simplify matters.

Link to comment
Share on other sites

2 minutes ago, MrPhil said:

My assumption since the first post is that VAT must be displayed for the product even before you know who the customer is. Is everyone else assuming that you don't need to figure the VAT until the customer gives his shipping address? I think we're working on two very different problems. Once you have been given the shipping address, it's easy to figure out the tax rate (merely by country in this case... within US states with multiple tax jurisdictions, it's a nightmare requiring geolocation from the address and determining which county and city the buyer is within). Not having to display VAT to random shoppers would greatly simplify matters.

With this "attibute" for download/virtual item, there is no more shipping address taken into account, only billing address in that case.

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

@inra311,

I saw that to validate the access to the download for the customer it was necessary that the status of the order stay "fixed" on the predefined status that authorize the download...
This is too restrictive because if I want to change the status of the command later, access to the download disappears.
I modified the query for the download appears and remains accessible on customer side (account_history_info.php) as long as the download status was part of the orders_status_history list.

Here is the modification to apply to reach this result.
In includes/modules/dowloads.php, find/change like this:

// Now get all downloadable products in that order
//  $downloads_query = tep_db_query("select date_format(o.date_purchased, '%Y-%m-%d') as date_purchased_day, opd.download_maxdays, op.products_name, opd.orders_products_download_id, opd.orders_products_filename, opd.download_count, opd.download_maxdays from " . TABLE_ORDERS . " o, " . TABLE_ORDERS_PRODUCTS . " op, " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " opd, " . TABLE_ORDERS_STATUS . " os where o.customers_id = '" . (int)$customer_id . "' and o.orders_id = '" . (int)$last_order . "' and o.orders_id = op.orders_id and op.orders_products_id = opd.orders_products_id and opd.orders_products_filename != '' and o.orders_status = os.orders_status_id and os.downloads_flag = '1' and os.language_id = '" . (int)$languages_id . "'");
  $downloads_query = tep_db_query("select distinct date_format(o.date_purchased, '%Y-%m-%d') as date_purchased_day, opd.download_maxdays, op.products_name, opd.orders_products_download_id, opd.orders_products_filename, opd.download_count, opd.download_maxdays from " . TABLE_ORDERS . " o, " . TABLE_ORDERS_PRODUCTS . " op, " . TABLE_ORDERS_PRODUCTS_DOWNLOAD . " opd, " . TABLE_ORDERS_STATUS . " os, " . TABLE_ORDERS_STATUS_HISTORY . " osh where o.customers_id = '" . (int)$customer_id . "' and o.orders_id = '" . (int)$last_order . "' and o.orders_id = op.orders_id and o.orders_id = osh.orders_id and op.orders_products_id = opd.orders_products_id and opd.orders_products_filename != '' and osh.orders_status_id = os.orders_status_id and os.downloads_flag = '1' and os.language_id = '" . (int)$languages_id . "'");

 

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

1 minute ago, milerwan said:

I saw that to validate the access to the download for the customer it was necessary that the status of the order stay "fixed" on the predefined status that authorize the download...
This is too restrictive because if I want to change the status of the command later, access to the download disappears.

You can enable/disable download access for each status of the order, or did I get you wrong?

image.png.e39efcdbfb0f196c610a7d6bf1415f2e.png

Link to comment
Share on other sites

@inra311,

Your example seems the regular way. ^^

With mine, in case of cancel (but I had not thought of this eventuality because once the link is available it is often too late to go back), need order_editor to delete the "download" status.
 

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

@inra311@inra311,

I'have just see that, for payment method, only "Credit Card" is available when I have only dematerialized product(s) in my order but If I add normal product, I could choose other(s) payment(s) (if set in admin with zone classes of course).
 

I don't understand why just Credit Card is available and not another payment method ? :blink:
Is there a configuration to allow different payment modules for download products because I don't see how to set if ?!?

Thank you.

PS: That's strange, in local I have all the payments shown but in the online shop only Credit Card for download products ?!?

Osc v2.3.4 BS "custom"
PHP 7.3 compatible (710 modified files => o_O')

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...