UPS shipping method and products with no weight

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 [email protected] 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 <[email protected]>
*/
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!

One Reply to “UPS shipping method and products with no weight”

Leave a Reply

Your email address will not be published. Required fields are marked *