Jump to content

WA State Destination Sales Tax


Recommended Posts

Per a post I made previously...

Many US states currently require sales tax to be calculated based on the destination of the products, varying from locality to locality. Indeed, the tax rate can vary from one side of the street to the other.

The majority of state departments of revenue provide a lookup method, URL encoded, XML, etc... to allow internet enabled applications to determine the appropriate sales tax on the fly.

This functionality was sadly missing from Prestashop (no doubt due to limited demand).

I've implemented a hack that resolves the issue somewhat for destinations within Washington state. It currently uses simplexml and curl for the request and parsing the resulting reply.

Some issues that still exist with this code:

1) altering the address_invoice or address_delivery from those attached to the user will fail to recalculate the sales tax.

2) Due to the repeat calls to Tax->getRateByState($id_state) method, significant overhead occurs in sending and receiving the xml based request, resulting in a significant slowdown. To ameliorate this issue I am currently storing the returned rate within the Cart object because:

3) I had issues extending the ObjectModel to create a DestinationTax object. If someone wishes to provide some technical advice on this, I would be happy to provide the (not-working) code. This would result in a much cleaner code implementation.

So... here's the resulting code within the Tax.php file:

static public function getRateByState($id_state)
   {

       global $cart;

               // maintain functionality for non WA states //
       $tax = Db::getInstance()->getRow('
           SELECT ts.`id_tax`, t.`rate`
           FROM `'._DB_PREFIX_.'tax_state` ts
           LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = ts.`id_tax`)
           WHERE `id_state` = '.intval($id_state));

       if (isset($cart) AND Validate::isLoadedObject($cart) AND $cart->id_address_invoice) {

           $address = new Address(intval($cart->id_address_invoice));
           if (!Validate::isLoadedObject($address))
               die(Tools::displayError());

           if ($address->id_state) {

               $state = new State(intval($address->id_state));

               if (!Validate::isLoadedObject($state)) 
                   die(Tools::displayError());

               if ($state->iso_code == "WA") {

                                       // this is my hack //
                   if ($cart->destination_tax == 0) {

                       $base = 'http://dor.wa.gov/AddressRates.aspx';
                       $format = 'xml';
                       $params = array('output' => $format,
                                       'addr' => urlencode($address->address1),
                                       'city' => urlencode($address->city),
                                       'zip' => urlencode($address->postcode));
                       $url = $base . '?' . http_build_query($params);
                       $c = curl_init($url);
                       curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
                       $response = curl_exec($c);
                       curl_close($c);

                       $sxml = simplexml_load_string($response);

                       if ($sxml['code'] == 0 || $sxml['code'] == 1 || $sxml['code'] == 2) {
                           $r = floatval($sxml['rate']) * 100;
                           $tax = array('rate' => floatval($r));
                       } // if

                       $cart->destination_tax = floatval($tax['rate']);
                       $cart->update();

                   } // if
                   else {
                       $tax = array('rate' => floatval($cart->destination_tax));
                   } // else
               } // if
           } // if
       } // if
       return $tax ? $tax['rate'] : false;
   } // getRateByState()



There is an issue with posting this due to the size so I'm going to spread across multiple messages...

Cheers,
Finnian Burn
[email protected]

Manager, IT Department
Criterion Athletic
Sport Netting
Batting Cages Inc
Freform Manufacturing

Link to comment
Share on other sites

Continued...

The following are the changes made to the cart:

        
       public        $destination_tax;

   protected    $fieldsRequired = array('id_currency', 'id_lang');
   protected    $fieldsValidate = array('id_address_delivery' => 'isUnsignedId', 'id_address_invoice' => 'isUnsignedId',
       'id_currency' => 'isUnsignedId', 'id_customer' => 'isUnsignedId', 'id_lang' => 'isUnsignedId',
       'id_carrier' => 'isUnsignedId', 'recyclable' => 'isBool', 'gift' => 'isBool', 'gift_message' => 'isMessage',
       'destination_tax' => 'isFloat');

   private        $_nb_products = NULL;
   private        $_products = NULL;
   private        $_discounts = NULL;
   protected     $table = 'cart';
   protected     $identifier = 'id_cart';

   public function getFields()
   {
       parent::validateFields();

       $fields['id_address_delivery'] = intval($this->id_address_delivery);
       $fields['id_address_invoice'] = intval($this->id_address_invoice);
       $fields['id_currency'] = intval($this->id_currency);
       $fields['id_customer'] = intval($this->id_customer);
       $fields['id_lang'] = intval($this->id_lang);
       $fields['id_carrier'] = intval($this->id_carrier);
       $fields['recyclable'] = intval($this->recyclable);
       $fields['gift'] = intval($this->gift);
       $fields['gift_message'] = pSQL($this->gift_message);
       $fields['date_add'] = pSQL($this->date_add);
       $fields['date_upd'] = pSQL($this->date_upd);
       $fields['destination_tax'] = floatval($this->destination_tax);

       return $fields;
   }



This also requires the addition of a destination_tax field to the ps_cart table. Here is the SQL create statement:

DROP TABLE IF EXISTS `criterion_store`.`ps_cart`;
CREATE TABLE  `criterion_store`.`ps_cart` (
 `id_cart` int(10) unsigned NOT NULL auto_increment,
 `id_carrier` int(10) unsigned NOT NULL,
 `id_lang` int(10) unsigned NOT NULL,
 `id_address_delivery` int(10) unsigned NOT NULL,
 `id_address_invoice` int(10) unsigned NOT NULL,
 `id_currency` int(10) unsigned NOT NULL,
 `id_customer` int(10) unsigned NOT NULL,
 `recyclable` tinyint(1) unsigned NOT NULL default '1',
 `gift` tinyint(1) unsigned NOT NULL default '0',
 `gift_message` text,
 `date_add` datetime NOT NULL,
 `date_upd` datetime NOT NULL,
 `destination_tax` float default '0',
 PRIMARY KEY  (`id_cart`),
 KEY `cart_customer` (`id_customer`)
) ENGINE=MyISAM AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;




Any additional input or suggestions from other coders out there will be, of course, appreciated.

Cheers,
Finnian Burn
[email protected]

Manager, IT Department
Criterion Athletic
Sport Netting
Batting Cages Inc
Freform Manufacturing

Link to comment
Share on other sites

Hi fburn!

Very good idea to create such a topic.
Please create a feature request descripting as much as you can how taxes should work, because we're very interested on it, and due to all country's systems it's very difficult to manage both of them.

But for now, I think I can provide you a simple way to reduce consirabely your overhead:
you can create a cache system inside Tax::getRateByState() which could looks like that

static public function getRateByState($id_state)
   {
       global $cart;
       $tax = [...]

       if (isset($cart) AND Validate::isLoadedObject($cart) AND $cart->id_address_invoice) {
           $address = [...]

           if ($address->id_state) {
               $state = [...]

               if ($state->iso_code == "WA") {
                   if ($cart->destination_tax == 0) {

                       if (!isset(self::$_destinationTaxCache[md5($address->address1.$address->city.$address->postcode)])) {
                           $base = 'http://dor.wa.gov/AddressRates.aspx';
                           $format = 'xml';
                           $params = array('output' => $format,
                                           'addr' => urlencode($address->address1),
                                           'city' => urlencode($address->city),
                                           'zip' => urlencode($address->postcode));
                           $url = $base . '?' . http_build_query($params);
                           $c = curl_init($url);
                           curl_setopt($c, CURLOPT_RETURNTRANSFER, TRUE);
                           $response = curl_exec($c);
                           curl_close($c);

                           $sxml = simplexml_load_string($response);

                           if ($sxml['code'] == 0 || $sxml['code'] == 1 || $sxml['code'] == 2) {
                               $r = floatval($sxml['rate']) * 100;
                               $tax = array('rate' => floatval($r));
                           } // if

                           self::$_destinationTaxCache[intval($state->id)] = floatval($tax['rate']);
                       }
                       $cart->destination_tax = floatval(self::$_destinationTaxCache[md5($address->address1.$address->city.$address->postcode)]);
                       $cart->update();

                   } // if
                   else {
                       $tax = array('rate' => floatval($cart->destination_tax));
                   } // else
               } // if
           } // if
       } // if
       return $tax ? $tax['rate'] : false;
   } // getRateByState() 



And don't forget to add that line to the beginning of the Tax object

class Tax extends ObjectModel
{
   // Array Cache for tax rates depending of geolocalisation (USA)
   private static $_destinationTaxCache = array();

   [...]
}



Doing this, you'll request the dor.wa.gov and parse data only once, next time you already have your taxrate memorized ;-)

Link to comment
Share on other sites

Matthieu,

Thanks for your input. I've got two running implementations of prestashop and will try both solutions. One reason to save the rate is that we are required by law to pay the correct tax amount. To this end, it works best if we can actually save the tax information attached to an order.

Per your request, I will create a feature request and include the pertinent information on Washington State destination sales tax. I have a feeling that it will be very difficult to implement all of the destination based tax systems. The United States is fragmented and each state's taxation is completely different. It makes it untenable for most small businesses to implement across the board. However, large enterprises usually have the correct implementation for each state. Your willingness to implement the correct taxation is likely to be determined by your target market and desire to minimize complexity.

Sincerely,
Finnian Burn
[email protected]

Manager, IT Department
Criterion Athletic
Sport Netting
Batting Cages Inc
Freform Manufacturing

Link to comment
Share on other sites

That was my original plan. I created a subclass of the tax object => DestinationTax, but ran into issues with the ObjectModel persistence. No doubt due to my inexperience with PHP (from a Java Enterprise background).

The ideal situation would be to have multiple state tax implementations, something like an abstract StateTax or LocationTax object with specific implementations that could be dynamically loaded based on $cart->id_address_invoice or $cart->id_address_delivery. It could then default to the generic method of taxation if there was no specific location based taxation method.

Link to comment
Share on other sites

Hello!


Has anyone else looked at this for other states that have destination based taxation?


The following is a list of the states that use it => getting to be nearly half!


This is really going to be critical for appropriate taxation...


Participating States

Full Member States:
Arkansas
Indiana
Iowa
Kansas
Kentucky
Michigan
Minnesota
Nebraska
Nevada
New Jersey
North Carolina
North Dakota
Oklahoma
Rhode Island
South Dakota
Vermont
Washington
West Virginia
Wyoming

Associate Member States:
Ohio
Tennessee
Utah
Link to comment
Share on other sites

I've found that the when tax is calculated by state it only determines the appropriate tax after the customer has reached the address verification point in check out. This presents a bit of an issue because the customer is going to see a noticeable increase in the price of the goods they are trying to purchase.

I've added a bit of code that checks to see if the cart is loaded and if not, creates a new one and uses the customers primary address. If it's not, it continues to ignore the tax until the customer logs in.

I've tested the code both logged in as a customer and anonymously and haven't seen any errors, but I'm hoping that someone else can give me a bit of feedback on if this could break the system.

Here's the code that I am using for getApplicableTax()

    static public function getApplicableTax($id_tax, $productTax)
   {
       global $cart, $defaultCountry, $cookie;

       $customer = new Customer(intval($cookie->id_customer));
       if (Validate::isLoadedObject($customer)) {
           $customerAddresses = $customer->getAddresses(intval($cookie->id_lang));
           /* Setting default addresses for cart */
           if ((!isset($cart->id_address_delivery) OR empty($cart->id_address_delivery)) AND sizeof($customerAddresses))
           {
               $cart->id_address_delivery = intval($customerAddresses[0]['id_address']);
               $update = 1;
           }
           if ((!isset($cart->id_address_invoice) OR empty($cart->id_address_invoice)) AND sizeof($customerAddresses))
           {
               $cart->id_address_invoice = intval($customerAddresses[0]['id_address']);
               $update = 1;
           }
           /* Update cart addresses only if needed */
           if (isset($update) AND $update)
               $cart->update();
       } // if

       if (!isset($cart->id) OR !$cart->id) {
           $cart->add();
           if ($cart->id)
               $cookie->id_cart = intval($cart->id);
       }

       /* If customer have an address (implied that he is registered and log in) */
       if (isset($cart) AND Validate::isLoadedObject($cart) AND $cart->id_address_invoice)
       {

           $address = new Address(intval($cart->id_address_invoice));
           if (!Validate::isLoadedObject($address))
               die(Tools::displayError());
           $country = new Country(intval($address->id_country));
           if (!Validate::isLoadedObject($country))
               die(Tools::displayError());



           /* If customer's invoice address is inside a state */
           if ($address->id_state)
           {
               $state = new State(intval($address->id_state));

               if (!Validate::isLoadedObject($state))
                   die(Tools::displayError());
               /* Return tax value depending to the tax behavior */
               $tax_behavior = intval($state->tax_behavior);
               if ($tax_behavior == PS_PRODUCT_TAX)
                   return $productTax * Tax::zoneHasTax(intval($id_tax), intval($country->id_zone));
               if ($tax_behavior == PS_STATE_TAX) {
                   $t = Tax::getRateByState(intval($state->id));
                   return $t;
               } // if
               if ($tax_behavior == PS_BOTH_TAX)
                   return ($productTax * Tax::zoneHasTax(intval($id_tax), intval($country->id_zone))) + Tax::getRateByState(intval($state->id));
               /* Unknown behavior */
               die(Tools::displayError('Unknown tax behavior!'));
           }
           /* Else getting country zone tax */
           if (!$id_zone = Address::getZoneById(intval($address->id)))
               die(Tools::displayError());
           return $productTax * Tax::zoneHasTax(intval($id_tax), intval($id_zone));
       }
       /* Default tax application */
       if (!Validate::isLoadedObject($defaultCountry))
           die(Tools::displayError());
       return $productTax * Tax::zoneHasTax(intval($id_tax), intval($defaultCountry->id_zone));
   }

Link to comment
Share on other sites

This killed my back-end until I wrapped the changed code above with a check to make sure the cookie is a customer cookie and not a back-end login cookie. Works now.

    static public function getApplicableTax($id_tax, $productTax)
   {
       global $cart, $defaultCountry, $cookie;

       if (isset($cookie->id_customer) && !empty($cookie->id_customer)) {
           $customer = new Customer(intval($cookie->id_customer));
           if (Validate::isLoadedObject($customer)) {
               $customerAddresses = $customer->getAddresses(intval($cookie->id_lang));
               /* Setting default addresses for cart */
               if ((!isset($cart->id_address_delivery) OR empty($cart->id_address_delivery)) AND sizeof($customerAddresses))
               {
                   $cart->id_address_delivery = intval($customerAddresses[0]['id_address']);
                   $update = 1;
               }
               if ((!isset($cart->id_address_invoice) OR empty($cart->id_address_invoice)) AND sizeof($customerAddresses))
               {
                   $cart->id_address_invoice = intval($customerAddresses[0]['id_address']);
                   $update = 1;
               }
               /* Update cart addresses only if needed */
               if (isset($update) AND $update)
                   $cart->update();
           } // if

           if (!isset($cart->id) OR !$cart->id) {
               $cart->add();
               if ($cart->id)
                   $cookie->id_cart = intval($cart->id);
           }
       }

       /* If customer have an address (implied that he is registered and log in) */
       if (isset($cart) AND Validate::isLoadedObject($cart) AND $cart->id_address_invoice)
       {

           $address = new Address(intval($cart->id_address_invoice));
           if (!Validate::isLoadedObject($address))
               die(Tools::displayError());
           $country = new Country(intval($address->id_country));
           if (!Validate::isLoadedObject($country))
               die(Tools::displayError());



           /* If customer's invoice address is inside a state */
           if ($address->id_state)
           {
               $state = new State(intval($address->id_state));

               if (!Validate::isLoadedObject($state))
                   die(Tools::displayError());
               /* Return tax value depending to the tax behavior */
               $tax_behavior = intval($state->tax_behavior);
               if ($tax_behavior == PS_PRODUCT_TAX)
                   return $productTax * Tax::zoneHasTax(intval($id_tax), intval($country->id_zone));
               if ($tax_behavior == PS_STATE_TAX) {
                   $t = Tax::getRateByState(intval($state->id));
                   return $t;
               } // if
               if ($tax_behavior == PS_BOTH_TAX)
                   return ($productTax * Tax::zoneHasTax(intval($id_tax), intval($country->id_zone))) + Tax::getRateByState(intval($state->id));
               /* Unknown behavior */
               die(Tools::displayError('Unknown tax behavior!'));
           }
           /* Else getting country zone tax */
           if (!$id_zone = Address::getZoneById(intval($address->id)))
               die(Tools::displayError());
           return $productTax * Tax::zoneHasTax(intval($id_tax), intval($id_zone));
       }
       /* Default tax application */
       if (!Validate::isLoadedObject($defaultCountry))
           die(Tools::displayError());
       return $productTax * Tax::zoneHasTax(intval($id_tax), intval($defaultCountry->id_zone));
   }

Link to comment
Share on other sites

  • 4 months later...

Tax is currently calculated based on the customer invoice address as opposed to the shipping address. However invoice address actually shouldn't matter, as only items shipped to a state where a business is located should incur sales tax.

Link to comment
Share on other sites

  • 6 months later...

Taxes in general in the US are a mess. And sales tax is no exception :)

superann is right on the money for the US, shipping address matters. That's the main big difference. However, having just almost completed a US based site, if I may just add a few precisions:

1) Sales tax is applicable in states where a business has physical presence. For example, if you own physical stores in, say, NY and NJ, you will need to charge tax for both of those states as shipping destinations for your online shop. See http://www.dirjournal.com/computers-journal/internet-sales-tax-what-you-need-to-know/

2) Sales tax in the US is not only based on state, but on counties. For example here is an example of tax breakdown for the state of NY: http://ny.rand.org/stats/govtfin/salestax.html

What I did is purchase a ZIP code database and crossed it with the tax rates data based on county names. Took me a little bit of cleaning up of the tax table from rand.org before the join, but pretty quick work overall. Then I just re-used what fburn did, but instead of calling a URL with CURL, you're just making a database call, which has much lower overhead, and less chance of problems and surprises (what if the external website goes down, or the data page is no longer structured in the same way).

static public function getRateByState($id_state)
{
   global $cart;

   $tax = Db::getInstance()->getRow('
   SELECT ts.`id_tax`, t.`rate`
   FROM `'._DB_PREFIX_.'tax_state` ts
   LEFT JOIN `'._DB_PREFIX_.'tax` t ON (t.`id_tax` = ts.`id_tax`)
   WHERE `id_state` = '.intval($id_state));

    if (isset($cart) AND Validate::isLoadedObject($cart) AND $cart->id_address_delivery) 
    {
       $address = new Address(intval($cart->id_address_delivery));

       if (!Validate::isLoadedObject($address))
           die(Tools::displayError());

       if ($address->id_state) 
       {
           $state = new State(intval($address->id_state));

           if (!Validate::isLoadedObject($state)) 
               die(Tools::displayError());

           if ($state->iso_code == "NY") 
           {
               $tax = Db::getInstance()->getRow('
               SELECT `tax_rate` as rate from `'._DB_PREFIX_.'zip_codes` 
               where zip = \''.intval($address->postcode).'\'');
               if ( ! $tax['rate'] || intval($tax['rate']) == 0 ) die('invalid ZIP code for the state indicated !');
           }
       }
   }

   return $tax ? floatval($tax['rate']) : false;
}



and here is a custom function for the Address class:

public function checkZIP()
   {
       $state = Db::getInstance()->getRow('select * from '._DB_PREFIX_.'state where id_state = ' . intval($this->id_state));

       if ( $state['iso_code'] == 'NY' )
       {
           $zip = Db::getInstance()->getRow('select * from '._DB_PREFIX_.'zip_codes where region1 = \'New York\' AND zip = \''.$this->postcode.'\'');
           if ( ! $zip ) return false;
       }

       return true;
   }



This is a quick and dirty, of course it can be greatly improved upon...

3) However, even then, errors are possible. Because ZIP codes are actually not 100% reliable in determining tax rate. See http://salestaxbuzz.org/2009/01/08/zip-zip/ - Not really sure what else to do though.

Link to comment
Share on other sites

  • 2 weeks later...

David,

May I ask where did you purchase the ZIP code database from? I am interest to do the same for my site... Thanks.



What I did is purchase a ZIP code database and crossed it with the tax rates data based on county names. Took me a little bit of cleaning up of the tax table from rand.org before the join, but pretty quick work overall. Then I just re-used what fburn did, but instead of calling a URL with CURL, you're just making a database call, which has much lower overhead, and less chance of problems and surprises (what if the external website goes down, or the data page is no longer structured in the same way).

Link to comment
Share on other sites

  • 3 weeks later...

I hate to be a pain but I'm also doing a prestashop integration for a client in New York State. Do you think you could provide me a walkthrough of what I need to do to get this tax thing figured out and going. I know you have posted about it but I'm pretty confused. I would greatly appreciate any information and again, I'm sorry for the hassle.

Link to comment
Share on other sites

Hi hussew,

Attached is a zip file with all you should need.

1) Run the attached SQL on your database

2) Back up:

/classes/Tax.php
/classes/Address.php
/address.php

3) Find the lines surrounded by /* ### BT MOD ### */ and copy / paste the modified code.

That should do it. Note: this based off version 1.2.4.

Regards,

-David

NY_tax.zip

Link to comment
Share on other sites

Hi hussew,

Attached is a zip file with all you should need.

1) Run the attached SQL on your database

2) Back up:

/classes/Tax.php
/classes/Address.php
/address.php

3) Find the lines surrounded by /* ### BT MOD ### */ and copy / paste the modified code.

That should do it. Note: this based off version 1.2.4.

Regards,

-David


I am in California, can I use the same files?

I am totally lost.
Link to comment
Share on other sites



Sure, I bought it from http://www.geopostcodes.com - Pretty cheap too, it was 49.95 € (about $75) with a 1-year update subscription (ZIP codes change regularly), or you can get a one-shot without updates for 19.95 € (about $30). The quality and file format was very good.

Best regards,

-David



I have purchased the DB as you recommend, but I have difficult time to connect the DB to the prestashop. :-S
Link to comment
Share on other sites


I have purchased the DB as you recommend, but I have difficult time to connect the DB to the prestashop. :-S


Hi Jack,

The database you purchase on geopostcodes is a simple text / CSV file. You have to build your MySQL table structure first and then import the data with phpMyAdmin. You will also need to find the tax rates for each county and do a join / fusion between the two, so as to get something similar to the SQL file in my previous post from earlier today (download the zip file, the SQL is in there).

Regards,

-David
Link to comment
Share on other sites



I am in California, can I use the same files?

I am totally lost.


You can use the same PHP files and just change the hard-coded state code from NY to CA in the function, but you will need to build your own tax rates database for CA. But the basic technical principle is the same.

You can get the zip codes from geopostcodes or other source, but then you will need to find the tax rates by county for CA on your own and then do a join / fusion of the two. Again, just liek for Jack, see my previous post and download the zip file.

Best Regards,

-David
Link to comment
Share on other sites

  • 1 month later...

oh man I did not see all of these replies to my intitial post. Thank you david. Apologies for pm'ing you several times. This is awesome. How do I activate it once ive replaced all files and run the script. Do i create a new tax?

Link to comment
Share on other sites

@hussew: The way to acticate it is to go to the admin, "Shipping" tab -> "States" sub-tab, go into the NY state detail and make sure the "State tax" radio button is selected. In your products, just select "no tax". That should do it as it will apply the state tax to your products if the shipping address is in NY.

For info, you can add more states to the script and zip code database and configure them in the admin as well, should your client start having a physical presence in other states and therefore be required to pay state tax in those states as well.

Best regards,

-David

Link to comment
Share on other sites

Unfortunately, no, the update is not automatic. You'll need to update the zip code database manually each quarter after you download your updated zip code file from geopostcodes and obtain the new state tax rates from the NY state tax website. Once you properly document and get the hang of the procedure, the update process should take no more than 30-40 minutes.

Otherwise, if you have the money to invest, take a look at http://www.thestc.com/Services.stm - for $850 per year, you'll get 4 quarterly updates with the zip code AND tax rate info all joined already. The table / column name will properlly be different though, so you'll need to slightly modify the getRateByState (in /classes/Tax.php) and checkZIP (in /classes/Address.php) functions to match the column names.

IMPORTANT UPDATE: I had forgotten to do do a zip code validity check in /authentication.php (I had only done it in /address.php).

around line 76, after

$errors = array_unique(array_merge($errors, $address->validateControler()));



add the following:

if ( ! $address->checkZIP() )
   $errors[] = Tools::displayError('Invalid ZIP code for the state you specified. If you believe this is an error on our behalf, please use the contact form link at the bottom of this page and inform the webmaster.');

Link to comment
Share on other sites

  • 1 year later...
Hi hussew,

Attached is a zip file with all you should need.

1) Run the attached SQL on your database

2) Back up:

/classes/Tax.php
/classes/Address.php
/address.php

3) Find the lines surrounded by /* ### BT MOD ### */ and copy / paste the modified code.

That should do it. Note: this based off version 1.2.4.

Regards,

-David


Hi David,

Thanks so much for this. I was able to add this to a 1.3.6 installation to use California State Tax. Looking good!

Regards.
Link to comment
Share on other sites

@JayBEE: you're welcome. Glad I could help ;-)

@I2IA: sorry, I hadn't seen your response until today. I must have missed the e-mail. Anyhow, even if it's too late... :) The id bigint(20) is just the primary key / internal id.


Hi David,

I suppose there may be a way and I'm attempting to figure it out, but is there a way to not have taxes applied to the products when they are displayed on the site when logged in and to only have the taxes applied during checkout?

I hope this is clear.

Any help would be greatly appreciated.

Thanks in advance,
JayBEE
Link to comment
Share on other sites

Hi JayBEE,

That's the way we have it working on www.creedboutique.com

The way to set it up is, for each individual product, do not set a tax (select "No tax" in the pull-down menu below the Pre-tax retail price). Then, in Shipping -> States, for each state where your business needs to pay tax, select the "State tax" radio button. Finally, in Preferences -> Products, select "Tax excluded".

State tax only gets applied on checkout, whereas Sales tax, which is more representative of VAT in France and other European countries, gets pplied on product pages directly.

Best Regards,

-David

Link to comment
Share on other sites

Hi JayBEE,

That's the way we have it working on www.creedboutique.com

The way to set it up is, for each individual product, do not set a tax (select "No tax" in the pull-down menu below the Pre-tax retail price). Then, in Shipping -> States, for each state where your business needs to pay tax, select the "State tax" radio button. Finally, in Preferences -> Products, select "Tax excluded".

State tax only gets applied on checkout, whereas Sales tax, which is more representative of VAT in France and other European countries, gets pplied on product pages directly.

Best Regards,

-David


Hi David,

Thanks for your prompt response!

Fortunately I had already figured this out but forgot to come back here and update this thread.

Thanks again!
JayBEE
Link to comment
Share on other sites

  • 3 weeks later...
×
×
  • Create New...