UPS shipping method and products with no weight
Posted on 16. Mar, 2010 by Fido in Development, Magento
Magento, where art thou documentation? Where art thou support for community code and problem solutions?
Laments to the nature of Varien’s views “Open Source” aside, the UPS shipping module has one glaring bug: The UPS API doesn’t handle weights of zero (or, perhaps, as I havent verified this, Magento’s UPS module code doesn’t properly take into account that some products may have weight of zero).
(It would seem handy if we could set a shipping method per product. If only.)
Now, before I get into details on a work around to this issue, you should know that if you do not want products considered in shipping calculations, you should be using Virtual products. These act like simple products but are not calculated in the shipping. I have not tested how these interact with Bundled or Grouped products.
You should also consider using Promotions (Shopping Cart price rules) to give items meeting certain criteria free shipping (You can then give those products a weight to avoid the zero-weight issue).
Now, without further pontification (yay, big words),
here’s the fix:
(p.s. – this is done using Magento version 1.3.2.4)
1) We are COPYING: app/code/core/Mage/Usa/Model/Shipping/Carrier/Ups.php and pasting it here: code/local/Mage/Usa/Model/Shipping/Carrier/Ups.php We do this so we overwrite the core code, without actually changing the core code (best practices). Make sure any edits are done to the Ups.php found in your “local” folder rather than the “core” folder.
2) What we want to do is test if a product has a weight of zero. If it does, we flag it, and tell UPS that we have a weight of .05. Pretending our product has some weight prevents UPS from returning a response we can’t use. We then test for this flag later in the code to determine if we should reset the cost of the shipping to zero (since presumably, no weight = no cost – Apologies if this does not work with your business logic).
Code added to Ups.php:
//Line 50
protected $_isZeroWeight = false;
//Line 155ish
if($weight == 0) {
$weight = .05;
$this->_isZeroWeight = true;
}
//Line 295ish
else if($this->_isZeroWeight == true) {
foreach ($priceArr as $method=>$price) {
$rate = Mage::getModel('shipping/rate_result_method');
$rate->setCarrier('ups');
$rate->setCarrierTitle($this->getConfigData('title'));
$rate->setMethod($method);
$method_arr = $this->getCode('method', $method);
$rate->setMethodTitle(Mage::helper('usa')->__($method_arr));
$rate->setCost(0);
$rate->setPrice(0);
$result->append($rate);
}
}
Now, here is the full code. I suggest you go by this to see how the changes relate to the rest of the code:
<?php
/**
* Magento
*
* NOTICE OF LICENSE
*
* This source file is subject to the Open Software License (OSL 3.0)
* that is bundled with this package in the file LICENSE.txt.
* It is also available through the world-wide-web at this URL:
* http://opensource.org/licenses/osl-3.0.php
* If you did not receive a copy of the license and are unable to
* obtain it through the world-wide-web, please send an email
* to license@magentocommerce.com so we can send you a copy immediately.
*
* DISCLAIMER
*
* Do not edit or add to this file if you wish to upgrade Magento to newer
* versions in the future. If you wish to customize Magento for your
* needs please refer to http://www.magentocommerce.com for more information.
*
* @category Mage
* @package Mage_Usa
* @copyright Copyright (c) 2008 Irubin Consulting Inc. DBA Varien (http://www.varien.com)
* @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0)
*/
/**
* UPS shipping rates estimation
*
* @category Mage
* @package Mage_Usa
* @author Magento Core Team <core@magentocommerce.com>
*/
class Mage_Usa_Model_Shipping_Carrier_Ups
extends Mage_Usa_Model_Shipping_Carrier_Abstract
implements Mage_Shipping_Model_Carrier_Interface
{
protected $_code = 'ups';
protected $_request = null;
protected $_result = null;
protected $_xmlAccessRequest = null;
protected $_defaultCgiGatewayUrl = 'http://www.ups.com:80/using/services/rave/qcostcgi.cgi';
protected $_isZeroWeight = false;
public function collectRates(Mage_Shipping_Model_Rate_Request $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
$this->setRequest($request);
$this->_result = $this->_getQuotes();
$this->_updateFreeMethodQuote($request);
return $this->getResult();
}
public function setRequest(Mage_Shipping_Model_Rate_Request $request)
{
$this->_request = $request;
$r = new Varien_Object();
if ($request->getLimitMethod()) {
$r->setAction($this->getCode('action', 'single'));
$r->setProduct($request->getLimitMethod());
} else {
$r->setAction($this->getCode('action', 'all'));
$r->setProduct('GND'.$this->getConfigData('dest_type'));
}
if ($request->getUpsPickup()) {
$pickup = $request->getUpsPickup();
} else {
$pickup = $this->getConfigData('pickup');
}
$r->setPickup($this->getCode('pickup', $pickup));
if ($request->getUpsContainer()) {
$container = $request->getUpsContainer();
} else {
$container = $this->getConfigData('container');
}
$r->setContainer($this->getCode('container', $container));
if ($request->getUpsDestType()) {
$destType = $request->getUpsDestType();
} else {
$destType = $this->getConfigData('dest_type');
}
$r->setDestType($this->getCode('dest_type', $destType));
if ($request->getOrigCountry()) {
$origCountry = $request->getOrigCountry();
} else {
$origCountry = Mage::getStoreConfig('shipping/origin/country_id', $this->getStore());
}
$r->setOrigCountry(Mage::getModel('directory/country')->load($origCountry)->getIso2Code());
if ($request->getOrigRegionCode()) {
$origRegionCode = $request->getOrigRegionCode();
} else {
$origRegionCode = Mage::getStoreConfig('shipping/origin/region_id', $this->getStore());
if (is_numeric($origRegionCode)) {
$origRegionCode = Mage::getModel('directory/region')->load($origRegionCode)->getCode();
}
}
$r->setOrigRegionCode($origRegionCode);
if ($request->getOrigPostcode()) {
$r->setOrigPostal($request->getOrigPostcode());
} else {
$r->setOrigPostal(Mage::getStoreConfig('shipping/origin/postcode', $this->getStore()));
}
if ($request->getOrigCity()) {
$r->setOrigCity($request->getOrigCity());
} else {
$r->setOrigCity(Mage::getStoreConfig('shipping/origin/city', $this->getStore()));
}
if ($request->getDestCountryId()) {
$destCountry = $request->getDestCountryId();
} else {
$destCountry = self::USA_COUNTRY_ID;
}
//for UPS, puero rico state for US will assume as puerto rico country
if ($destCountry==self::USA_COUNTRY_ID && ($request->getDestPostcode()=='00912' || $request->getDestRegionCode()==self::PUERTORICO_COUNTRY_ID)) {
$destCountry = self::PUERTORICO_COUNTRY_ID;
}
$r->setDestCountry(Mage::getModel('directory/country')->load($destCountry)->getIso2Code());
$r->setDestRegionCode($request->getDestRegionCode());
if ($request->getDestPostcode()) {
$r->setDestPostal($request->getDestPostcode());
} else {
}
$weight = $this->getTotalNumOfBoxes($request->getPackageWeight());
if($weight == 0) {
$weight = .05;
$this->_isZeroWeight = true;
}
$r->setWeight($weight);
if ($request->getFreeMethodWeight()!=$request->getPackageWeight()) {
$r->setFreeMethodWeight($request->getFreeMethodWeight());
}
$r->setValue($request->getPackageValue());
$r->setValueWithDiscount($request->getPackageValueWithDiscount());
if ($request->getUpsUnitMeasure()) {
$unit = $request->getUpsUnitMeasure();
} else {
$unit = $this->getConfigData('unit_of_measure');
}
$r->setUnitMeasure($unit);
$this->_rawRequest = $r;
return $this;
}
public function getResult()
{
return $this->_result;
}
protected function _getQuotes()
{
switch ($this->getConfigData('type')) {
case 'UPS':
return $this->_getCgiQuotes();
case 'UPS_XML':
return $this->_getXmlQuotes();
}
return null;
}
protected function _setFreeMethodRequest($freeMethod)
{
$r = $this->_rawRequest;
$weight = $this->getTotalNumOfBoxes($r->getFreeMethodWeight());
$r->setWeight($weight);
$r->setAction($this->getCode('action', 'single'));
$r->setProduct($freeMethod);
}
protected function _getCgiQuotes()
{
$r = $this->_rawRequest;
$params = array(
'accept_UPS_license_agreement' => 'yes',
'10_action' => $r->getAction(),
'13_product' => $r->getProduct(),
'14_origCountry' => $r->getOrigCountry(),
'15_origPostal' => $r->getOrigPostal(),
'origCity' => $r->getOrigCity(),
'19_destPostal' => substr($r->getDestPostal(), 0, 5),
'22_destCountry' => $r->getDestCountry(),
'23_weight' => $r->getWeight(),
'47_rate_chart' => $r->getPickup(),
'48_container' => $r->getContainer(),
'49_residential' => $r->getDestType(),
'weight_std' => strtolower($r->getUnitMeasure()),
);
$params['47_rate_chart'] = $params['47_rate_chart']['label'];
try {
$url = $this->getConfigData('gateway_url');
if (!$url) {
$url = $this->_defaultCgiGatewayUrl;
}
$client = new Zend_Http_Client();
$client->setUri($url);
$client->setConfig(array('maxredirects'=>0, 'timeout'=>30));
$client->setParameterGet($params);
$response = $client->request();
$responseBody = $response->getBody();
} catch (Exception $e) {
$responseBody = '';
}
return $this->_parseCgiResponse($responseBody);
}
public function getShipmentByCode($code,$origin = null){
if($origin===null){
$origin = $this->getConfigData('origin_shipment');
}
$arr = $this->getCode('originShipment',$origin);
if(isset($arr[$code]))
return $arr[$code];
else
return false;
}
protected function _parseCgiResponse($response)
{
$costArr = array();
$priceArr = array();
$errorTitle = Mage::helper('usa')->__('Unknown error');
if (strlen(trim($response))>0) {
$rRows = explode("\n", $response);
$allowedMethods = explode(",", $this->getConfigData('allowed_methods'));
foreach ($rRows as $rRow) {
$r = explode('%', $rRow);
switch (substr($r[0],-1)) {
case 3: case 4:
if (in_array($r[1], $allowedMethods)) {
$responsePrice = Mage::app()->getLocale()->getNumber($r[8]);
$costArr[$r[1]] = $responsePrice;
$priceArr[$r[1]] = $this->getMethodPrice($responsePrice, $r[1]);
}
break;
case 5:
$errorTitle = $r[1];
break;
case 6:
if (in_array($r[3], $allowedMethods)) {
$responsePrice = Mage::app()->getLocale()->getNumber($r[10]);
$costArr[$r[3]] = $responsePrice;
$priceArr[$r[3]] = $this->getMethodPrice($responsePrice, $r[3]);
}
break;
}
}
asort($priceArr);
}
$result = Mage::getModel('shipping/rate_result');
$defaults = $this->getDefaults();
if (empty($priceArr)) {
$error = Mage::getModel('shipping/rate_result_error');
$error->setCarrier('ups');
$error->setCarrierTitle($this->getConfigData('title'));
//$error->setErrorMessage($errorTitle);
$error->setErrorMessage($this->getConfigData('specificerrmsg'));
$result->append($error);
} else if($this->_isZeroWeight == true) {
foreach ($priceArr as $method=>$price) {
$rate = Mage::getModel('shipping/rate_result_method');
$rate->setCarrier('ups');
$rate->setCarrierTitle($this->getConfigData('title'));
$rate->setMethod($method);
$method_arr = $this->getCode('method', $method);
$rate->setMethodTitle(Mage::helper('usa')->__($method_arr));
$rate->setCost(0);
$rate->setPrice(0);
$result->append($rate);
}
} else {
foreach ($priceArr as $method=>$price) {
$rate = Mage::getModel('shipping/rate_result_method');
$rate->setCarrier('ups');
$rate->setCarrierTitle($this->getConfigData('title'));
$rate->setMethod($method);
$method_arr = $this->getCode('method', $method);
$rate->setMethodTitle(Mage::helper('usa')->__($method_arr));
$rate->setCost($costArr[$method]);
$rate->setPrice($price);
$result->append($rate);
}
}
//echo "<!--".print_r($result,1)."-->";
return $result;
}
//And so on..... I have truncated the rest of the file as no further changes are made. Don't delete the code here yourself.
What does this result in?
- Requesting a quote from the Cart page shows cost of zero for all shipping methods
- When checking out, users still must select a shipping method. They are all at zero cost, but users will still be able to check items such as 1-day shipping (And/or whatever methods you have enabled). Is this optimal? Probably not, however this issue is, unfortunately, outside of the scope of this article at this time.
- It appears that Magento will still calculate shopping carts with mixed products (products with and without weights) correctly. You should test this to make sure customers aren’t getting over or under charged.
Hope this helps someone!




18 Comments
andrew
17. Mar, 2010
Thanks for this post!
I’m using Magento version1.3.2.4
First problem I was having with this code is an error stating unexpected T_PUBLIC. I added a “}” at the end of your code addition and got rid of the error. But now all I receive at checkout is the following:
Sorry, no quotes are available for this order at this time.
Any thoughts? Thanks.
Fido
17. Mar, 2010
My best suggestion would be to not copy and paste the entire code snippet above, but to add the code snippets into your Ups.php file by line number as noted (Compare to the long code snippet above to see exact placement).
There might be another error happening that is causing the UPS module to spit out its standard “not available at this time” error (Which shows regardless of the type of error the UPS module might be producing).
andrew
22. Mar, 2010
Thanks for the reply! I had gone through my own Ups.php and replaced just the snippets. It was, of course, my fault I ended up missing a “}”.
I have one other question. In number two above you mention all shipping methods showing at zero. Would it be possible to include an if statement after the initial calculation that checks to see if cost is at zero and if so only the Ground shipping method will show?
Thanks again. I am so close to having this solved for my client.
andrew
22. Mar, 2010
Please delete my previous post. Because what I actually need is what you say is out of the scope of this article. I need only the method of UPS Ground to show a cost of zero if weight is zero.
If you have time to help, I would very much appreciate it. Thanks!
Fido
22. Mar, 2010
Hi Andrew -
Unfortunately I don’t have a lot of time to help . There is an issue with what you are trying to do – If there is no weight, I don’t think UPS can calculate a shipping cost for any of the shipping methods.
What I did was “fake” a weight (by setting it to .05) – So technically, the UPS module is grabbing prices based on that weight. However, I am taking the prices and setting them to zero.
In the foreach loop, you could try to say if($method == “ground”) {… } and only set ground to Zero that way. However, the price you see for other shipping methods will always still be based on .05
manoj
01. May, 2010
hello fido
i want to debug magnto code with zend sudio
but unable to load how to do best practice to overlaod core function please help me i want to write my own logic into magento. some payment configration
Jordan Walker
04. Jun, 2010
This has been very informative and helpful to me as I build a website for a luxury watch seller. Thanks!
brian
30. Jun, 2010
Does anyone know how to send a shipping record to a third party vendor using Magento? Like an ODBC file or an XML file?
Web Dev
15. Jul, 2010
Nice, I was looking for the same. Its really a great pain when can’t figure out anything in magento after all. thank you!
Sigma Infosolutions
29. Jul, 2010
How 2 different sub-domain can have same shopping cart (i.e) Items purchased from one Sub-domain should reflect when went to another sub-domain and vice-verse in magento. Please suggest ……
Thanks,
Anil
Dann
05. Aug, 2010
Hey there, I am having a problem with my shipping, lets say I have 1 product added to cart that weights 2 lbs and shipping cost is $9.00 for 2 lbs, then I add 2 for qty so the cost should be $18.00 for 4 lbs, but instead the shipping cost is only like $9.70. Please any help is greatly appreciated.
mark23
09. Aug, 2010
Thanks for posting and sharing your ideas and thoughts. Very nice.
Nicolas
01. Oct, 2010
Cool. Magento is wonderfull. Tanks for the tips
Impulsis
30. Nov, 2010
Thank you! This post with detailed explanation helped to fix the bug! No problem with it from now )
Prashant
08. Dec, 2010
Nice article. It helped me a lot thanks….
Shannon
22. Dec, 2010
This is way over my head, but I am having similar problems with my recently launched Magento website http://www.madeinmuseum.com. My developer is in Italy and it is getting to difficult to work with him. Does anyone know of a good Magento consultant who could work on touching up my site for around $30 per hour?
It would be a great site for a portfolio and we plan to expand. Check it out….design@madeinmuseum.com
Magento Host
03. Jan, 2011
Very useful, thank you for this wonderful fix!
Marahrens
10. Jan, 2011
Thanks for the information. Very nice!
Leave a reply