Magento - save quote_address on cart addAction using observer - magento

I am writing a custom Magento module that gives rate quotes (based on an external API) on the product page. After I get the response on the product page, it adds some of the info from the response into the product view's form.
The goal is to save the address (as well as some other things, but those can be in the session for now). The address needs to be saved to the quote, so that the checkout (onestepcheckout, free version) will automatically fill those values in (city, state, zip, country, shipping method [of which there are 3]) and load the rate quote via ajax (which it's already doing).
I have gone about this by using an Observer, and watching for the cart events. I fill in the address and save it. When I end up on the cart page or checkout page ~4/5 times the data is lost, and the sql table shows that the quote_address is getting save with no address info, even though there is an explicit save.
The observer method's I've used are:
checkout_cart_update_item_complete
checkout_cart_product_add_after
The code saving is: (I've tried this with the address, quote and cart all not calling save() with the same results, as well as not calling setQuote()
// $params = Mage::app()->getRequest()->getParams()
// $quote = Mage::getSingleton('checkout/cart')->getQuote()
// or
// $quote = observer->getProduct()->getQuoteItem()->getQuote();
// where applicable, but both methods seemed to === each other
$quote->getShippingAddress()
->setCountryId($params['estimate_to_country'])
->setCity($params['estimate_to_city'])
->setPostcode($params['estimate_to_zip_code'])
->setRegionId($params['estimate_to_state_code'])
->setRegion($params['estimate_to_state'])
->setCollectShippingRates(true)
->setShippingMethod($params['carrier_method'])
->setQuote($quote)
->save();
$quote->getBillingAddress()
->setCountryId($params['estimate_to_country'])
->setCity($params['estimate_to_city'])
->setPostcode($params['estimate_to_zip_code'])
->setRegionId($params['estimate_to_state_code'])
->setRegion($params['estimate_to_state'])
->setCollectShippingRates(true)
->setShippingMethod($params['carrier_method'])
->setQuote($quote)
->save();
$quote->save();
$cart->save();
// I also tried:
Mage::getSingleton('checkout/session')->setQuote($quote);
I imagine there's a good chance this is not the best place to save this info, but I am not quite sure. I was wondering if there is a good place to do this without overriding a whole controller to save the address on the add to cart action.
Thanks so much, let me know if I need to clarify

In Magento you can create your own events wherever you need but in this case you can use checkout_cart_product_add_after event to update the quore address details.
So here is the code for the same
$quote = Mage::getSingleton('checkout/session')->getQuote();
$billingAddress = Mage::getModel('sales/quote_address')
->setStoreId($storeId)
->setAddressType(Mage_Sales_Model_Quote_Address::TYPE_BILLING)
->setCustomerId(('Your_Value_Here'))
->setCustomerAddressIpd(('Your_Value_Here'))
->setCustomer_address_id(('Your_Value_Here'))
->setPrefix(('Your_Value_Here'))
->setFirstname(('Your_Value_Here'))
->setMiddlename(('Your_Value_Here'))
->setLastname(('Your_Value_Here'))
->setSuffix(('Your_Value_Here'))
->setCompany(('Your_Value_Here'))
->setStreet(('Your_Value_Here'))
->setCity(('Your_Value_Here'))
->setCountry_id(('Your_Value_Here'))
->setRegion(('Your_Value_Here'))
->setRegion_id(('Your_Value_Here'))
->setPostcode(('Your_Value_Here'))
->setTelephone(('Your_Value_Here'))
->setFax(('Your_Value_Here'));
$quote->setBillingAddress($billingAddress);
$shippingAddress = Mage::getModel('sales/quote_address')
->setStoreId($storeId)
->setAddressType(Mage_Sales_Model_Quote_Address::TYPE_SHIPPING)
->setCustomerId(('Your_Value_Here'))
->setCustomerAddressId(('Your_Value_Here'))
->setCustomer_address_id(('Your_Value_Here'))
->setPrefix(('Your_Value_Here'))
->setFirstname(('Your_Value_Here'))
->setMiddlename(('Your_Value_Here'))
->setLastname(('Your_Value_Here'))
->setSuffix(('Your_Value_Here'))
->setCompany(('Your_Value_Here'))
->setStreet(('Your_Value_Here'))
->setCity(('Your_Value_Here'))
->setCountry_id(('Your_Value_Here'))
->setRegion(('Your_Value_Here'))
->setRegion_id(('Your_Value_Here'))
->setPostcode(('Your_Value_Here'))
->setTelephone(('Your_Value_Here'))
->setFax(('Your_Value_Here'));
$quote->setShippingAddress($shippingAddress)
->setShipping_method('flatrate_flatrate')
->setShippingDescription($this->getCarrierName('flatrate'));
$quote->save();

Related

How to exclude product(s) from search result programatically in Magento

I'm trying to exclude products from populating the search result.
It seems to be working fine on my localhost but not on clients dev server.
I'm observing the event
catalog_block_product_list_collection
and in observer method, in the end is this code:
$observer->getCollection()
->addFieldToFilter('entity_id', array('nin' => array_keys($_excludeProducts)))
->clear()
->load();
which works for catalog as well and search result list but for the moment not on search result list on clients dev server.
Any guidance/help is greatly appreciated.
Edit: Debugging this method gives me an empty collection but still the data is populating from somewhere.
I've changed the approach and used another event: catalog_product_collection_load_before
found better method to implement the approach with less code. #optimization
$excludeIds = array(2,3,4); //$excludeIds mixed
$observer->getCollection()
->addIdFilter($excludeIds, true); //exclude = true
The event also helps in keeping the correct items count on toolbar as it is dispatched before collection load.
I ran into a similar issue when trying to filter this collection using this event.
Do you have flat categories and flat products set the same in both environments? In my case, my code would only work with flat tables OFF, since I was using joining other EAV attributes.
In your case, if you are using flat, I think you just need to do addAttributeToFilter() instead.
In my case, here is what my observer looks like:
function onCategoryCollectionLoad($observer) {
$collection = $observer->getEvent()->getCategoryCollection();
$customerGroupId = (int) Mage::getSingleton('customer/session')->getCustomerGroupId();
// hidden_from_groups is an EAV attribute; I couldn't figure out how to make it work with flat because it has a backend_model
$collection->addAttributeToSelect('hidden_from_groups');
$collection->addExpressionAttributeToSelect('should_be_hidden', "COALESCE(FIND_IN_SET($customerGroupId, {{attribute}}), 0)", 'hidden_from_groups');
// should_be_hidden is not a real db field (nor EAV attribute); it only exists because of the addExpressionAttributeToSelect() above.
$collection->addFieldToFilter('should_be_hidden', array('lt' => 1));
// I don't call $collection->load(); that will get called further down the line.
}

Enable/disable cash-on-delivery only for some specific products

I want to disable cash on delivery payment method option for some specific products. I want to show cash on delivery method only for specific products and need to hide other payment options.
How can I do this? I read
this other question but it did not solve my problem.
you can use following free extension to solve your problem
http://www.magentocommerce.com/magento-connect/shipping-and-payment-filters.html
Using payment_method_is_active observer you could load the current checkout session quote and check what city the order is be shipped to.
$checkout = Mage::getSingleton('checkout/session')->getQuote();
$shipping = $checkout->getShippingAddress();
$cashOnDeliveryCities = array('city name 1','city name 2',..)
if(in_array($shipping->getCity(), $cashOnDeliveryCities)){
$result->isAvailable = true;
}else{
$result->isAvailable = false;
}
Here's how to do it without an extension.
Just go to admin > per motions > Shopping Cart Price Rule and create rules for this "cash on delivery payment method option for some specific products".
First in conditions select payment method "COD" in options. After that in "Action" add product SKU, for multiple using add SKU by comma. Activate the rule and check your payment method for those products to ensure it's working.

Magento 1.7: Add Configurable Product To Cart Via Query String

The Magento Wiki has a resource for adding a product to cart via Query String for Magento < 1.3 HERE
This quotes a method using this example:
http://www.your_domain.com/checkout/cart/add?product=68&qty=1&super_attribute[528]=55&super_attribute[525]=56
It also mentions that this was valid up to version 1.3.
I have been playing around with this in 1.7 and have noticed a Major difference in 1.7 is the encrypted key in the ->getAddUrl() method for the Form Action Attribtue so now the URLs look more like
http://www.your_domain.com.au/checkout/cart/add/uenc/aHR0cDovL3d3dy5jdWx0dXJla2luZ3MuY29tLmF1L2FjY2Vzc29yaWVzL3NvbC1yZXB1YmxpYy90cmFja3Mtb24tZWFyLWJsYWNrLTM1OTg5Lmh0bWw_X19fU0lEPVU,/product/35900/
With the product ID being the 35900.
If I use this URL in the browser it will direct me to the product page with a message saying Please specify the product's option(s).
I have been trying to pass the desired attribute options value in the URL to add the product to the cart with no success. (For the sake of saving space I'm omitting the URL up to and including the encrypted key) I've tried methods these to no avail:
/product/35900/super_attribute/49265/4834
/product/35900/super_attribute/49265=4834
/product/35900/49265=4834
/product/35900/49265/4834
My question is: Is it possible to add a configurable product via URL to the cart in Magento and if so, what is the format for passing the super_attribute id and Attribute Option Value?
You can use something like this :
$_typeInstance = $_product->getTypeInstance(true);
$_children = $_typeInstance->getUsedProducts(null, $_product);
$_attributes = $_typeInstance->getUsedProductAttributes($_product);
$_cartHelper = Mage::helper('checkout/cart');
foreach ($_children as $_child) {
$_superAttributes = array();
foreach ($_attributes as $_attribute) {
$_superAttributes[$_attribute->getAttributeId()] = $_child->getData($_attribute->getAttributeCode());
}
$_addUrl = $_cartHelper->getAddUrl($_product, array(
'_query' => array(
'super_attribute' => $_superAttributes
)));
}
This question was also posted on magento.stackexchange and user Marius kindly gave me the solution...
This has worked for me on CE 1.7.0.2 (with sample data):
/checkout/cart/add/product/126?super_attribute[525]=100&super_attribute[272]=22
NOTE (this puzzles me a bit):
There is a difference between calling:
/checkout/cart/add/product/126?super_attribute[525]=100&super_attribute[272]=22
and
/checkout/cart/add/product/126?super_attribute[272]=22&super_attribute[525]=100
I mean the order of the super_attribute parameters is important. After calling the 2 URLs above I ended up with 2 cart lines of the same product with the same options. one looked like this:
Size Small Color Green
and the other was
Color Green Size Small
I guess if you add the products to cart via URL you should keep the order of the attributes as shown in the product view page for consistency.
As per his suggestion, you can build the add to cart link using that method.
In latest magento we need to add form_key also:
https://{site-name}/checkout/cart/add/product/{product_id}/form_key/{form_key}?super_attribute[{attribute_id}]={attribute_value}&super_attribute[{attribute_id}]={attribute_value}

Magento - get results view HTML for a collection of products

I get a list of magento ids from a web service. I load these into and array $product_ids, so I have something like this:
Array
(
[0] => 1965
[1] => 3371
[2] => 1052
)
I can then make this into a collection:
$collection = Mage::getModel('catalog/product')->getCollection()
->addIdFilter($product_ids);
Using my Magento inspector, I've seen that the category pages use the class Mage_Catalog_Block_Product_List to display lists of products. I'd like to do something similar in my class. I've tried loading:
$ProductList = new Mage_Catalog_Block_Product_List();
$ProductList->setCollection($collection);
And then I've tried to load the HTML of the results as follows:
$CollectionHTML = $ProductList->_toHtml();
But $CollectionHTML is empty.
How would I get the HTML of what you see in the list view (i.e. the generated output of frontend/base/default/template/catalog/product/list.phtml, but given my collection)?
Making the code work the right way is much more easier in Magento than trying to work with ugly legacy code. I would gladly help you make the code the proper way when you have specific questions. Also, in the longterm, technical debt is gonna cost alot more.
Anyway, back to your issue.
In Magento block are not instantiated like in any app $myvar = new className ... almost never. This tutorial can help you understand better Magento's layout and blocks.
But if you want to create a block a way to do it is:
$block = Mage::getSingleton('core/layout')->createBlock('catalog/product_list')
Now related to your product collection you should check how Mage_Catalog_Block_Product_List::_getProductCollection actually works, because it uses the layered navigation, not a simple product collection.
Further, assuming that at least you are using a Magento controller and you are within a function, the following code will display the first page of products for a specified category:
//$category_id needs to be set
$layout = Mage::getSingleton('core/layout');
$toolbar = $layout->createBlock('catalog/product_list_toolbar');
$block = $layout->createBlock('catalog/product_list');
$block->setChild('toolbar', $toolbar);
$block->setCategoryId($category_id);
$block->setTemplate('catalog/product/list.phtml');
$collection = $block->getLoadedProductCollection();
$toolbar->setCollection($collection);
//render block object
echo $block->renderView();
Displaying specific ids:
you use root category id for $category_id variable (also make sure that display root category is set (or another category id that contains your product ids)
you can hook into catalog_block_product_list_collection event to add your ID Filter to the collection (this is called in _beforeToHtml function)
But, all this construction is not solid and there are still some points that require attention (other child blocks, filters and so on)

getRate() and Magento tax percentage

I'm trying to get the tax rate (percentage, not currency) for a given postcode so I can display it in a third-party quote PDF printout (no relation to the "quote" Magento uses as the shopping cart pre-checkout). While I'm still relatively new to Magento it appears that getRateRequest() and getRate() are the two main functions which get the tax rate based on all the variables (product tax class, customer tax class, etc.).
Since this is for a third-party extension and all our products are taxable I figured I would just use getRate() with the correct Varien Object input and it would return the tax rate. After a week of trial and error I can't figure out why I'm always getting a rate of zero. I've confirmed I'm calling the getRate() function and that it's not returning zero from the first if() statement checking for Country and Customer/Product class ID. In addition I've confirmed all the variables are being passed on and accessible in the getRate() function itself.
I've created an object with the below input (based on the output of getRateRequest()) that I call with getRate() and am hoping someone can shed light on what is wrong with my data input or why the getRate() function is always returning a result of zero. (I'm actually setting with $variables below, they are just defined earlier up and one of my test case values are below)
// UPDATED CODE (variable values come from 3rd party quote extension)
$country = 'US'; // use short country code
$region = '12'; // must be numeric!
$postcode = '95050';
// our quote extension stores the customer id ('2') which we use to get the tax class
$customer = Mage::getModel('customer/customer')->load( '2' );
$custTax = $customer->getTaxClassId();
$TaxRequest = new Varien_Object();
$TaxRequest->setCountryId( $country );
$TaxRequest->setRegionId( $region );
$TaxRequest->setPostcode( $postcode );
$TaxRequest->setStore( Mage::app()->getStore() );
$TaxRequest->setCustomerClassId( $custTax );
$TaxRequest->setProductClassId(2); // 2=taxable id (all our products are taxable)
$taxCalculationModel = Mage::getSingleton('tax/calculation');
$rate = $taxCalculationModel->getRate($TaxRequest);
My backup plan is to just do a direct SQL lookup formula although that will probably get a bit messy. Since our web development team didn't exactly follow good coding standards an eventual site re-write is in my future anyway once the initial launch fixes are in (all 4 pages of them).
Thanks for any help and taking the time to read this.
EDIT - Stack Overflow is awesome :)
You can also try this
$store = Mage::app()->getStore('default');
$request = Mage::getSingleton('tax/calculation')->getRateRequest(null, null, null, $store);
$taxclassid = $product->getData('tax_class_id');
$percent = Mage::getSingleton('tax/calculation')->getRate($request->setProductClassId($taxclassid));
If you change:
$TaxRequest->setRegionId(California);
to
$TaxRequest->setRegionId($stateId);
where $stateId is numeric region id. Your code should work then.

Resources