I have some products that I can only sell to specific regions. So I added a javascript popup that asks user's for their shipping zip code, and I hit a web service to confirm that I can sell this item to them.
Also, on the the cart page, I ask for the shipping zip code so that I can calculate the proper shipping rates.
I figured that if I was already asking the user for their zip I'd automatically set it on Add to Cart and present them with the appropriate shipping rates/methods.
I assumed that I could simply extend the Cart Controller's addAction to set the zip and collect the rates but the zip doesn't seem to be saved, nor can I collect the rates.
Here's the function I'm using:
public function addAction()
{
$cart = $this->_getCart();
$params = $this->getRequest()->getParams();
try {
if (isset($params['qty'])) {
$filter = new Zend_Filter_LocalizedToNormalized(
array('locale' => Mage::app()->getLocale()->getLocaleCode())
);
$params['qty'] = $filter->filter($params['qty']);
}
$product = $this->_initProduct();
$related = $this->getRequest()->getParam('related_product');
/**
* Check product availability
*/
if (!$product) {
$this->_goBack();
return;
}
$cart->addProduct($product, $params);
if (!empty($related)) {
$cart->addProductsByIds(explode(',', $related));
}
if((isset($params['territory_value_submitted']) && !empty($params['territory_value_submitted']))) {
$shipping_address = $cart->getQuote()->getShippingAddress();
$billing_address = $cart->getQuote()->getBillingAddress();
Mage::log('Current Zip: '.$shipping_address->getPostcode());
Mage::log('New Zip: '.$params['territory_value_submitted']);
if($shipping_address->getPostcode() != $params['territory_value_submitted']) {
$region = '';
// use web service to lookup coresponding state for this zip code
$state_lookup_response = file_get_contents(WEB_SERVICE_URL.'?zip='.$params['territory_value_submitted'].'&field=state');
if($state_lookup_response != '') {
$region = $state_lookup_response;
}
$shipping_address
->setCountryId('')
->setCity('')
->setPostcode($params['territory_value_submitted'])
->setRegion($region)
->setRegionId('')
->setCollectShippingRates(true)
->save();
Mage::log('Set Zip: '.$shipping_address->getPostalcode());
$current_bill_zip = $billing_address->getPostcode();
if(empty($current_bill_zip)) {
$billing_address
->setCountryId('')
->setCity('')
->setPostcode($params['territory_value_submitted'])
->setRegion($region)
->setRegionId('')
->save();
}
$ship_method = $shipping_address->getShippingMethod();
if(empty($ship_method)) {
$shipping_address->collectShippingRates();
$rates = $shipping_address->getAllShippingRates();
Mage::log('Available Rate Count: '.count($rates));
//if a free shipping method exists, choose that as the default
if($this->_getQuote()->getShippingAddress()->getShippingRateByCode('productmatrix_Free_Shipping') !== false) {
$this->_getQuote()->getShippingAddress()->setShippingMethod('productmatrix_Free_Shipping');
}
//if a standard shipping method exists, choose that as the default
elseif($this->_getQuote()->getShippingAddress()->getShippingRateByCode('productmatrix_Standard_Delivery') !== false) {
$this->_getQuote()->getShippingAddress()->setShippingMethod('productmatrix_Standard_Delivery');
}
$shipping_address->save();
}
$cart->getQuote()->save();
}
}
$cart->save();
$this->_getSession()->setCartWasUpdated(true);
/**
* @todo remove wishlist observer processAddToCart
*/
Mage::dispatchEvent('checkout_cart_add_product_complete',
array('product' => $product, 'request' => $this->getRequest(), 'response' => $this->getResponse())
);
if (!$this->_getSession()->getNoCartRedirect(true)) {
if (!$cart->getQuote()->getHasError()){
$message = $this->__('%s was added to your shopping cart.', Mage::helper('core')->htmlEscape($product->getName()));
$this->_getSession()->addSuccess($message);
}
$this->_goBack();
}
} catch (Mage_Core_Exception $e) {
if ($this->_getSession()->getUseNotice(true)) {
$this->_getSession()->addNotice($e->getMessage());
} else {
$messages = array_unique(explode("\n", $e->getMessage()));
foreach ($messages as $message) {
$this->_getSession()->addError($message);
}
}
$url = $this->_getSession()->getRedirectUrl(true);
if ($url) {
$this->getResponse()->setRedirect($url);
} else {
$this->_redirectReferer(Mage::helper('checkout/cart')->getCartUrl());
}
} catch (Exception $e) {
$this->_getSession()->addException($e, $this->__('Cannot add the item to shopping cart.'));
Mage::logException($e);
$this->_goBack();
}
}
What seems even more bizarre is that what gets logged will change as I run through the process multiple times on the same session.
Here's the log from adding the same product to the cart 3 times, always using the zip code of 10001
:
2012-04-04T19:58:20+00:00 DEBUG (7): Current Zip:
2012-04-04T19:58:20+00:00 DEBUG (7): New Zip: 10001
2012-04-04T19:58:20+00:00 DEBUG (7): Set Zip:
2012-04-04T19:58:20+00:00 DEBUG (7): Available Rate Count: 0
2012-04-04T19:58:36+00:00 DEBUG (7): Current Zip:
2012-04-04T19:58:36+00:00 DEBUG (7): New Zip: 10001
2012-04-04T19:58:36+00:00 DEBUG (7): Set Zip:
2012-04-04T19:58:36+00:00 DEBUG (7): Available Rate Count: 0
2012-04-04T19:59:00+00:00 DEBUG (7): Current Zip: 10001
2012-04-04T19:59:00+00:00 DEBUG (7): New Zip: 10001
Notice that the Set Zip
in the 2nd add is empty, but the Current Zip
isn't for the 3rd add. And that the 3rd add doesn't attempt to collect shipping rates, since the new zip is the same as the current zip.
Does anyone know what's preventing the data from either being loaded or saved properly?
The solution was to add a function call I found in the CartController::indexAction
function.
$cart->init();
will set the cart's status to CHECKOUT_STATE_BEGIN and clear all associated addresses. So it needs to be called before getting the address objects.
Such as:
if((isset($params['territory_value_submitted']) && !empty($params['territory_value_submitted']))) {
$cart->init();
$shipping_address = $cart->getQuote()->getShippingAddress();
$billing_address = $cart->getQuote()->getBillingAddress();
Now changes made to the addresses won't be cleared out by $cart->init()
being called on the cart page.
Also, the correct shipping methods could not be chosen without setting the CountryId for the shipping address. Since I only ship within the US all my shipping rules were restricted to the US and no matching rules were found.
This was fixed by changing
$shipping_address
->setCountryId('')
to
$shipping_address
->setCountryId('US')