Fragmented Thought

Magento US Postal Code Tax Handling



Lance Gliser

Heads up! This content is more than six months old. Take some time to verify everything still works as expected.

One of our clients recently found an order that did not calculate tax for a postal code they have a tax rule for. The problem turned out to be that customer had entered their 9 digit postal code, instead of their 5 digit postal code, and magento did calculate tax for it. I'll include the fix for this specific problem, but also a couple debug steps too.

Start by making sure the client does have a tax rule for the postal code that should trigger:

Tax Debugging audit:

Admin > Sales > Tax > Manage Tax Zones & Rates > Filter by Postal Code: Found: US-KS-66223 - United States - KS - 66223 - 8.65

Admin > Sales > Tax > Manage Tax Rules > Ctrl+F to find "66223" Found: Kansas Taxes - Retailer Customer - Taxable Goods, Shipping - ... US-KS-66223 ... - 1 - 1

If you find both of the above records when searching, you should be in the clear. Make a quick order, try the real postal code with just 5 digits and make sure. If you get tax, then it drops out when they change to 9 digit (standard behavior), you need to override one little core file.

Copy: app/code/core/Mage/Tax/Model/Resources/Calculation.php Make: app/code/local/Mage/Tax/Model/Resources/Calculation.php

This duplication will cause Magento to use your version of the file, even when the system is updated in the future. It's stock Magento behavior if you're not use to it yet, you will be.

From there, you need one little customization. In the _getRates($request) function of that same file, add the switch statement below to allow you to customize the postal code used by tax calculation. In my case, I'm stripping input down to the first 5 characters.

A quick solution:

/** * Returns tax rates for request - either pereforms SELECT from DB, or returns already cached result * Notice that productClassId due to optimization can be array of ids * * @param Varien_Object $request * @return array */ protected function \_getRates($request) { // Extract params that influence our SELECT statement and use them to create cache key $storeId = Mage::app()->getStore($request->getStore())->getId(); $customerClassId = $request->getCustomerClassId(); $countryId = $request->getCountryId(); $regionId = $request->getRegionId(); $postcode = $request->getPostcode(); // Specialize formats for some countries switch( $countryId ){ case 'US': // Force us zip codes down to first 5 $postcode = substr( $postcode, 0, 5 ); break; } // ... }

Not the best way

While the below will work, it's flawed and can cause bugs. I suggest in this creating a local version of a core file. It's better to extend the class, and overwrite just one method. That's out of the scope of this example though.