Ok, I finally can add the related_product
to the cart. In the code/core/Mage/Checkout/Model/Cart.php
file are two functions for adding products into the cart. The first is called public function addProduct($productInfo, $requestInfo=null)
, the second function is public function addProductsByIds($productIds)
.
The related_product
is added by id, the others are added with the addProduct()
function. The adding by id does not seem to work (at least for me it did not. Although magento used the unaltered Cart.php file from 1.7.0.2.
I solved this by altering the Cart.php file like this:
/**
* Add product to shopping cart (quote)
* This is the original function pasted from
* Mage/Checkout/Model/Cart.php line 245
* The function is working and adding products to the cart
*
* @param int|Mage_Catalog_Model_Product $productInfo
* @param mixed $requestInfo
* @return Mage_Checkout_Model_Cart
*/
public function addProduct($productInfo, $requestInfo=null)
{
$product = $this->_getProduct($productInfo);
$request = $this->_getProductRequest($requestInfo);
$productId = $product->getId();
if ($product->getStockItem()) {
$minimumQty = $product->getStockItem()->getMinSaleQty();
//If product was not found in cart and there is set minimal qty for it
if ($minimumQty && $minimumQty > 0 && $request->getQty() < $minimumQty
&& !$this->getQuote()->hasProductId($productId)
){
$request->setQty($minimumQty);
}
}
if ($productId) {
try {
$result = $this->getQuote()->addProduct($product, $request);
} catch (Mage_Core_Exception $e) {
$this->getCheckoutSession()->setUseNotice(false);
$result = $e->getMessage();
}
/**
* String we can get if prepare process has error
*/
if (is_string($result)) {
$redirectUrl = ($product->hasOptionsValidationFail())
? $product->getUrlModel()->getUrl(
$product,
array('_query' => array('startcustomization' => 1))
)
: $product->getProductUrl();
$this->getCheckoutSession()->setRedirectUrl($redirectUrl);
if ($this->getCheckoutSession()->getUseNotice() === null) {
$this->getCheckoutSession()->setUseNotice(true);
}
Mage::throwException($result);
}
} else {
Mage::throwException(Mage::helper('checkout')->__('The product does not exist.'));
}
Mage::dispatchEvent('checkout_cart_product_add_after', array('quote_item' => $result, 'product' => $product));
$this->getCheckoutSession()->setLastAddedProductId($productId);
return $this;
}
/**
* Adding products to cart by ids
* This function is the altered function, which works for me
* the edited lines are marked.
*
* @param array $productIds
* @return Mage_Checkout_Model_Cart
*/
public function addProductsByIds($productIds)
{
$allAvailable = true;
$allAdded = true;
if (!empty($productIds)) {
foreach ($productIds as $productId) {
$productId = (int) $productId;
if (!$productId) {
continue;
}
$product = $this->_getProduct($productId);
if ($product->getId() && $product->isVisibleInCatalog()) {
try {
$this->getQuote()->addProduct($product);
} catch (Exception $e){
$allAdded = false;
}
/**
* START EDIT
*/
/**
* String we can get if prepare process has error
*/
if (is_string($result)) {
$redirectUrl = ($product->hasOptionsValidationFail())
? $product->getUrlModel()->getUrl(
$product,
array('_query' =>array ('startcustomization' => 1))
)
: $product->getProductUrl();
$this->getCheckoutSession()->setRedirectUrl($redirectUrl);
if ($this->getCheckoutSession()->getUseNotice() === null) {
$this->getCheckoutSession()->setUseNotice(true);
}
Mage::throwException($result);
}
Mage::dispatchEvent('checkout_cart_product_add_after', array('quote_item' => $result, 'product' => $product));
$this->getCheckoutSession()->setLastAddedProductId($productId);
/**
* STOP EDIT
*/
} else {
$allAvailable = false;
}
}
if (!$allAvailable) {
$this->getCheckoutSession()->addError(
Mage::helper('checkout')->__('Some of the requested products are unavailable.')
);
}
if (!$allAdded) {
$this->getCheckoutSession()->addError(
Mage::helper('checkout')->__('Some of the requested products are not available in the desired quantity.')
);
}
}
return $this;
}
So i copied the lines from the other function. Does anyone think this will have some drawbacks? For me it looks like this introduces another check if everything works correct by examining the return string. But i don't know if dispatching the event might have some negative effects. As i stated, I'm new to magento and would like to know if this is solid solution.
Does anyone know, why the functions are different? Because they do basically the same. The only difference is, that the second function can add more than product. Maybe in this case dispatching the event is slow?
I just ran into this with a client recently. I wound up writing a module that creates a new quote item attribute, sets it from an observer, and displays it below the product details in the cart, on the checkout page, in the new order e-mail, and in the admin order/invoice view.
In order to display the custom quote item where I wanted to show it, I call a helper method from whatever template I need to modify. I might turn this into an actual generic module some day, and when I do I'll figure out a better way to display that attribute rather hacking a bunch of templates. Due to time constraints, I had to use the "quick and dirty" method.
Following is the code I used to create, set, and get the attribute, plus a list of the templates I modified:
config.xml
<config>
<modules>
<Company_PromoName>
<version>0.2.0</version>
</Company_PromoName>
</modules>
<global>
<models>
<company_promoname>
<class>Company_PromoName_Model</class>
</company_promoname>
</models>
<helpers>
<company_promoname>
<class>Company_PromoName_Helper</class>
</company_promoname>
</helpers>
<resources>
<company_promoname_setup>
<setup>
<module>Company_PromoName</module>
<class>Company_PromoName_Model_Resource_Setup</class>
</setup>
</company_promoname_setup>
</resources>
<events>
<sales_quote_add_item>
<observers>
<company_promoname>
<class>company_promoname/observer</class>
<method>salesQuoteAddItem</method>
</company_promoname>
</observers>
</sales_quote_add_item>
</events>
<fieldsets>
<sales_convert_quote_item>
<promo_name>
<to_order_item>*</to_order_item>
</promo_name>
</sales_convert_quote_item>
<sales_convert_order_item>
<promo_name>
<to_quote_item>*</to_quote_item>
<to_invoice_item>*</to_invoice_item>
</promo_name>
</sales_convert_order_item>
</fieldsets>
</global>
</config>
Model/Resource/Setup.php
class Company_PromoName_Model_Resource_Setup extends Mage_Sales_Model_Resource_Setup
{
}
data-install-1.0.0.php
$installer = $this;
$entities = array(
'quote_item',
'order_item',
'invoice_item',
);
$options = array(
'type' => Varien_Db_Ddl_Table::TYPE_VARCHAR,
'visible' => true,
'required' => false
);
$installer->startSetup();
foreach ($entities as $entity) {
$installer->addAttribute($entity, 'promo_name', $options);
}
$installer->endSetup();
Observer.php
class Company_PromoName_Model_Observer
{
/**
* Adds the promo name to the quote item if it is a promo item when the item
* is added to the quote
*
* @param Varien_Event_Observer $observer Object containing data passed
* from the event
* @see Mage_Sales_Model_Quote::addItem()
* @return void
*/
public function salesQuoteAddItem(Varien_Event_Observer $observer)
{
$quoteItem = $observer->getEvent()->getQuoteItem();
$amPromoRule = $quoteItem->getOptionByCode('ampromo_rule');
if ($amPromoRule && $amPromoRule->getValue()) {
$rule = Mage::getModel('salesrule/rule')->load($amPromoRule->getValue());
if ($rule) {
$quoteItem->setPromoName($rule->getName());
}
}
}
}
Helper/Data.php
class Company_PromoName_Helper_Data extends Mage_Core_Helper_Abstract
{
/**
* Gets the name of the promotion from a custom item attribute
*
* @param Mage_Core_Model_Abstract $item The promo item
* @return string
*/
public function getPromoName(Mage_Core_Model_Abstract $item)
{
$promoName = '';
if ($item->getPromoName()) {
$promoName = $item->getPromoName();
}
return $promoName;
}
}
Modified templates:
frontend:
checkout/cart/item/default.phtml
checkout/onepage/review/item.phtml
adminhtml:
sales/items/column/name.phtml
Screenshot of the promo item in the cart:
Note: The name of the extension and the screenshot have been redacted to protect the privacy of the client.
I hope the code I provided gets you on your way. Enjoy!
Best Answer
Maybe some interface block doesn't refresh as I see subtotals update.
Have you tried turning off all cache and testing again?