Magento relevant product when product is added to cart - magento

I am programming an extension which adds relevant products to the cart once you add an item to the cart. Example if you are buying a pen I am going to add paper to the cart. Bundled packages are not an option since I need to match certain conditions. I tried the following:
I set up an event listener "sales_quote_item_collection_products_after_load" scan all the products in cart and add the relevant products. Sadly you have to reload the cart in order to make the products appear.
I used this code in, my event listener, to add products to cart:
// Get cart instance
$cart = Mage::getSingleton('checkout/cart');
$cart->init();
// Add a product (simple); id:12, qty: 3
$cart->addProduct(12, 3);
$cart->save()
The strange thing is that removing products using the cart helper works (without refreshing):
$cartHelper = Mage::helper('checkout/cart');
$items = $cartHelper->getCart()->getItems();
foreach ($items as $item) {
if ($item->getProduct()->getId() == $productId) {
$itemId = $item->getItemId();
$cartHelper->getCart()->removeItem($itemId)->save();
break;
}
}
Is there a way to tell Magento to "requote" or what would you recommend? I also thought of adding the product, at the add to cart listener. But in that case I will need to implement it as well on update and remove, so it will work correctly. Using sales_quote_item_collection_products_after_load after load seemed to be the best option, since I have everything in one place.

You need to change in your code
$product_model = Mage::getModel('catalog/product');
$product_id =35;
$my_product = $product_model->load($product_id);
$cart = Mage::getModel('checkout/cart');
$cart->init();
$cart->addProduct($my_product, array('qty' => 1));
$cart->save();
Mage::getSingleton('checkout/session')->setCartWasUpdated(true);

Related

Magento: Addon Products

I'm looking to create "Addon Products" in Magento. What I mean is: If the product were a Greeting Card, and you were to add that to your cart, you might be presented with options on the checkout page to add an "Envelope" or "Stamps". These would be separate products with their own prices, however, they would not be available otherwise in the store. In other words, some sort of "Linked / Child Products" that only become available once the "Parent" product has been added.
Does this sort of product configuration exist within Magento? Doesn't seem to be a Bundled Product.
Yes Magento does this out of the box.
Against each product assign 'cross sell' products, using the cross sell tab on the left. When you first get to it, if the list is empty press the 'reset filter' button to show all the products in the store. Find the ones you want and put a tick next to them, then click save.
The block you are after is;
<block type="checkout/cart_crosssell" name="checkout.cart.crosssell" as="crosssell" template="checkout/cart/crosssell.phtml"/>
Which most template load within "content" in the layout/checkout.xml layout file of your theme,
<checkout_cart_index translate="label">
////
<reference name="content">
///// HERE
</reference>
/////
</checkout_cart_index>
And then add this to the checkout/cart.phtml template (if it's not there already);
<?php echo $this->getChildHtml('crosssell') ?>
But you can add it to wherever you wish.
To handle these products not appearing elsewhere, set their visibility to 'catalog' and either put them in a category that isnt visible or dont add them to a category.
TO ANSWER YOUR SECOND QUESTION...
You asked how you could remove all 'add on' items from the basket if there are no 'main product' items in the basket. Here is a quick solution that will do this.
Create a custom select product attribute, give it ID code 'product_type_var' and give it 2 options 'Main Product' and 'Addon Product'. Add it to your product attribute set and set the values against the appropriate products.
You can then run the following code against the basket. Ideally you would create a module with an event observer - but for the sake of this example, you could also place this code at the top of
app/design/frontend/XXX/YYY/template/checkout/cart.phtml
Here's the code;
$quote = Mage::getSingleton('checkout/session')->getQuote();
$needsAction = true;
$toRemove = array();
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$productLoad = Mage::getModel('catalog/product')->load($product->getId());
$customVariable = $productLoad->getResource()->getAttribute('product_type_var')->getFrontend()->getValue($productLoad);
if($customVariable == 'Main Product') {
$needsAction = false;
break; // No need to do anything
}
if($customVariable == 'Addon Product') {
$toRemove[] = $productLoad->getId(); // Build list of addon IDs
}
}
if($needsAction && (!empty($toRemove))) {
// There are no Main Products and 1 or more Addons
foreach($toRemove as $removeId) {
$quote->removeItem($removeId)->save();
}
}
Revision 3
To make sure any 'addon' products only remain in the cart if they relate to a specific 'main product' found in the cart, try this;
$quote = Mage::getSingleton('checkout/session')->getQuote();
$allowedUpsells = array();
$upsellsInCart = array();
$allIdsInCart = array();
foreach ($quote->getAllItems() as $item) {
$product = $item->getProduct();
$productLoad = Mage::getModel('catalog/product')->load($product->getId());
$customVariable = $productLoad->getResource()->getAttribute('product_type_var')->getFrontend()->getValue($productLoad);
if($customVariable == 'Main Product') {
$allIdsInCart[] = $productLoad->getId(); // Build list of all products in the cart
$upsells = $productLoad->getUpSellProductCollection(); // Get this products available upsells
foreach($upsells as $upsell) {
$allowedUpsells[] = $upsell->getId(); // Build full list of allowed addon IDs
}
}
if($customVariable == 'Addon Product') {
$allIdsInCart[] = $productLoad->getId(); // Build list of all products in the cart
$upsellsInCart[] = $productLoad->getId(); //Build full list of addon IDs
}
}
if(!empty($upsellsInCart)) { // Upsells might need attention
$allowedVsInCart = array_intersect($allowedUpsells, $allIdsInCart); // Remove other upsells that are avaiable to the product but not in the cart
$toBeRemoved = array_diff_assoc($allowedVsInCart, $upsellsInCart); // Now find the products in the cart that shouldnt be
if(!empty($toBeRemoved)) {
foreach($toBeRemoved as $removeId) {
$quote->removeItem($removeId)->save();
}
}
}

Magento: Delete item from cart

I need to delete one item from cart.
I use the checkout_cart_add_product_complete event wich is fired after the cart was saved.
Then I use:
$quote = Mage::getSingleton('checkout/session')->getQuote();
$allQuoteItems = $quote->getAllItems();
foreach ($allQuoteItems as $_item) {
$_product = $_item->getProduct();
if ($_product->getIsPreparedToDelete()) {
$quote->removeItem($_item->getId());
}
}
$quote->save();
But the item is still there...
I have no idea which event I can use - or if it is really possible to delte an item in the cart (I also added items, that works - with the same event..).
Thanks.
Take a look # Magento - remove one quantity from cart
$cartHelper = Mage::helper('checkout/cart');
$items = $cartHelper->getCart()->getItems();
foreach ($items as $item) {
...
Mage::getSingleton('checkout/cart')->removeItem($item->getId());
Found it out.
removeItem() works outside this event, when all is saved. Within works this ($_item->isDeleted(true)):
$quote = Mage::getSingleton('checkout/session')->getQuote();
$allQuoteItems = $quote->getAllItems();
foreach ($allQuoteItems as $_item) {
$_product = $_item->getProduct();
f ($_product->getIsPreparedToDelete()) {
$_item->isDeleted(true);
} else {
// ....
}
}
$quote->save();
I think you can use one of the below event to observe and can use to remove particular item from cart:
sales_quote_item_save_before: When the item will be added to cart, this event will be called before the save event of that item fired.
sales_quote_save_after: And every time the whole cart will be saved this event will be fired after that.
And for your information, sales quote object is being used for handling cart.
And below is the link to the list of magento's events:
http://www.magentocommerce.com/wiki/5_-_modules_and_development/reference/events
Please feel free to revert back with any queries.
Regards,
Milan

magento same product with many quantity different size change one time

I have a little experience in magento. All my products have custom size in option.
All products have different sizes and different prices.
Customer adds one product with quantity 5 to cart. So 5 products of this size are added to cart. When the customer adds another product with different size all products in cart change to this size.
How can i prevent this behavior?
Unless you're doing this programmatically (that is writing the code), there's no way to do this.
When Magento adds a product, it first looks into the quote / shopping cart to see if one already exists. If one does, it pulls that one and adds to the quantity. There is no way to turn this off.
Programmatically, you very manually add an item to a shopping cart. This is how...
$cart = Mage::getSingleton("checkout/cart");
foreach ($products_to_add as $product_id => $custom_options) {
$product = Mage::getModel("catalog/product")->load($product_id);
$options = new Varien_Object(array("options" => $custom_options,
"qty" => 1));
// some products may result in multiple products getting added to cart
// I beleive this pulls them all and sets the custom options accordingly
$add_all = $product->getTypeInstance(true)
->prepareForCartAdvanced($options, $product, Mage_Catalog_Model_Product_Type_Abstract::PROCESS_MODE_FULL);
foreach ($add_all as $add_me) {
$item = Mage::getModel('sales/quote_item');
$item->setStoreId(Mage::app()->getStore()->getId());
$item->setOptions($add_me->getCustomOptions())
->setProduct($add_me);
$item->setQty(1);
$cart->getQuote()->addItem($item);
}
}
// when done adding all the items, finally call save on the cart
$cart->save();

Add product to cart on customer registration in magento

I want that when customer register product (which is choosen in backend) should get added to cart. I have done this :
$product = Mage::getModel('catalog/product')
->setStoreId(Mage::app()->getStore()->getId())
->load(154);
$cart = Mage::getSingleton('checkout/cart');
$cart->addProduct($product, 1)->save();
Mage::getSingleton('checkout/session')->setCartWasUpdated(true);
in AccountController.php in createPostAction().
But it is not showing but whenever customer buys anything it gets added into cart. I am doing anything wrong...?
-Thnx in advance.
The problem is, the quote object in the session has already collected totals and would not do it again when the cart is saved. Because of this the quote's item count is zero, even though a quote item has been successfully added and saved. Modify your code like this:
$cart->product($product, 1);
$cart->getQuote()->setData('totals_collected_flag', false);
$cart->save();
And this should solve the problem.
A suggestion: would it not be better to implement this functionality in an observer listening to the customer_register_success event?

Magento: How to retrieve the minimal price of (i.e.) grouped products?

I am trying to display a grouped product's price on the product view page in Magento 1.7.0.2, just as it is being displayed in the category product listing ("Starting at: xx.xx"). I thought I could just use
$this->getPriceHtml($_product, true);
to do so because it works the same way in the category product listing, but as everything I tried to do in Magento, it is not that easy because that method doesnt return anything on the product view page.
I looked a little deeper into Magento's code and figured out that the cause of this problem is the product's minimal price data not being set on the product view page ($_product->getMinimalPrice() returns null).
Therefore I did some research on how to load a product's minimal price which brought up ideas like
$_product->getPriceModel()->getMinimalPrice($_product);
which doesnt work because apparently that method was deprecated and removed in one of the last updates or
$priceModel = Mage::getResourceModel('catalogindex/price');
$priceModel->setStoreId(Mage::app()->getStore()->getId());
$priceModel->setCustomerGroupId(Mage::getSingleton('customer/session')->getCustomerGroupId());
$minimalPrices = $priceModel->getMinimalPrices(array($_product->getId()));
$minimalPrice = $minimalPrices[0];
$_product->setData('minimal_price', $minimalPrice['value']);
$_product->setData('minimal_tax_class_id', $minimalPrice['tax_class_id']);
which doesnt work either because the table 'catalogindex_minimal_price' is empty.
So my question is, how does Magento load the minimal price in the category product listing?
I looked a little deeper into Magento's code and tracked down how Magento loads the products of the product list.
When viewing the product list, the product collection is loaded by the model Mage_Catalog_Model_Layer, which adds the minimal price and other stuff to the products in the collection by adding joins to the collection’s SQL, but I have not yet found a model that does the same thing for single products instead of collections.
Because I don’t want to use a direct SQL query, I checked out the CatalogIndex module, but that module seems not to be working at all because all its indexing tables are empty or even corrupted.
$data_grouped = Mage::getModel("catalogindex/data_grouped");
$minimal = $data_grouped->getMinimalPrice(array($_product->getId()), Mage::app()->getStore());
looked good at first but it gives me the following error:
SQLSTATE[42S22]: Column not found: 1054 Unknown column 'catalog_product_index_price.value' in 'field list'
The CatalogIndex module is either deprecated or I’m just too stupid to enable it.
However, I have now created a workaround in my price.phtml template, which uses the same method to retrieve the minimal price and tax percents as the product list does:
At the beginning of the template I added the following:
$this->setProduct(
Mage::getModel("catalog/product")->getCollection()
->addAttributeToSelect(Mage::getSingleton("catalog/config")->getProductAttributes())
->addAttributeToFilter("entity_id", $this->getProduct()->getId())
->setPage(1, 1)
->addMinimalPrice()
->addFinalPrice()
->addTaxPercents()
->load()
->getFirstItem()
);
which may not be the perfect solution, but it does the job and it does it a little cleaner than iterating over all child products and finding the minimal price that way.
This isn't an answer as much as a big HTML comment on Subsurf's answer. If you're reading this, you might be implementing your own custom module to display a product list. In my case, it's a single page just for wholesalers. My goal was to get a list of my own company's products and give them a list like it was another category. I copied my template's list.phtml to my new content.phtml. But getMinimalPrice() was returning NULL, meaning that when this code calls the getPriceHtml(), price.phtml wasn't showing wholesale pricing.
I did a simple module with index controller and a content.phtml. Using the boilerplate I see all over the net, in the indexController.php file, change:
$block = $this->getLayout()->createBlock(
'Mage_Core_Block_Template',
'b2b',
array('template' => 'b2b/content.phtml')
);
to:
$block = $this->getLayout()->createBlock(
'Mage_Catalog_Block_Product_List',
'b2b',
array('template' => 'b2b/content.phtml')
);
This does a lot of the heavy lifting for you to show the products list correctly.
Now, on to the template file, in my case content.phtml, you get your product collection and then use Subsurf's code in the top of the foreach() which repeats.
$_productCollection = Mage::getModel('catalog/product')
->getCollection()
// ->addAttributeToSelect('*')
// doesn't work ->addAttributeToSelect('special_price')
->addAttributeToSelect('sku')
->addAttributeToFilter('status', 1)
->addAttributeToFilter('visibility', 4)
// http://stackoverflow.com/questions/1332742/magento-retrieve-products-with-a-specific-attribute-value
->addFieldToFilter( array(
array('attribute'=>'manufacturer','eq'=>'143')
, array('attribute'=>'manufacturer','eq'=>'139')
))
;
echo "<p>There are ".count($_productCollection)." products: </p>";
echo '<div class="category-products">';
// List mode
if($this->getMode()!='grid') {
$_iterator = 0;
echo'<ol class="products-list" id="products-list">';
foreach ($_productCollection as $_product) {
?> <li class="item<?php if( ++$_iterator == sizeof($_productCollection) ): ?> last<?php endif; ?>">
<?php
$_product=Mage::getModel("catalog/product")->getCollection()
->addAttributeToSelect(Mage::getSingleton("catalog/config")->getProductAttributes())
->addAttributeToFilter("entity_id", $_product->getId())
->setPage(1, 1)
->addMinimalPrice()
->addFinalPrice()
->addTaxPercents()
->load()
->getFirstItem()
);
echo "Minimal price: ".$_product->getMinimalPrice()."<br>\n";
Since this isn't a single product's page and I'm using other template code, $this->setProduct() didn't do anything for me. I guessed if there was a set, there might be a get, so $_product=$this->getProduct() was the magic my template's price.phtml needed to work properly. Then I noticed I could just assign the Mage:: in setProduct() directly to $_product.
Thanks, Subsurf!!!
There is another way that will still allow you to use $this->getPriceHtml($_product, true); in your template, which then handles all the complex price display rules and puts "Starting at" before grouped product prices.
I'm using this for a custom featured product block. In a block class I have this function:
class MyCompany_Catalog_Block_Product_Featured extends Mage_Catalog_Block_Product_Abstract {
public function setGroupedProductPrice($group_product) {
$prices = array();
foreach ($group_product->getTypeInstance()->getChildrenIds($group_product->getId()) as $ids) {
foreach ($ids as $id) {
$product = Mage::getModel('catalog/product')->load($id);
$prices[] = $product->getPriceModel()->getPrice($product);
}
}
ksort($prices);
$group_product->setPrice(array_pop($prices));
}
}
Then in the template:
<?php
if ($_product->getTypeId() == 'grouped')
$this->prepareGroupedProduct($_product);
echo $this->getPriceHtml($_product, true);
?>
The following does the trick without using a product collection:
$priceResource = Mage::getResourceSingleton('catalogindex/price');
$select = $priceResource->getReadConnection()->select();
$select->from(
array('price_table' => $priceResource->getMainTable())
)
->where('price_table.entity_id = ?', $_product->getId())
->where('price_table.website_id = ?', Mage::app()->getStore()->getWebsiteId())
->where('price_table.customer_group_id = ?', Mage::getSingleton('customer/session')->getCustomerGroupId());
$result = $priceResource->getReadConnection()->fetchRow($select);
This code is adapted from \Mage_CatalogIndex_Model_Resource_Price::getMinimalPrices and made suitable for one single product.

Resources