Shopping cart Rules doing the opposite - magento

We have added a "yes/no" product attribute labelled: "Allow this product to have coupons applied?" with a default value of "yes".
The reason for this is, that we never want the sales team to give discounts on certain core products out of the thousands available.
We could add these products as SKU's to every one of the thousand coupons created, but that can then be changed by accident, or improperly listed by the sales team creating the coupons.
We therefore have asked them to add this rule to their new coupons, to exclude these products. We also updated all the other coupons via code, to include this rule.
When I test the rule on Magento 1.5, Community edition, it does exactly the opposite of what the condition says.
When I test the same rule on our other Enterprise solution (magento 1.9), it actually does what it is supposed to.
When I have one product in the shopping cart, being a product that has this attribute set to "No", and I apply a coupon code, it accepts the coupon code. On the other system (Enterprise) it rejects it, as it should.
Has anyone else came across this?
UPDATE:
Ok, this just got more interesting.
When I stepped through the Rule classes, the product was passed, but the product attribute did not come through. The scope is set to website, and I did check that is it saved under that scope in the back end.
This is really weird...
On the enterprise system, the product attribute, with the same configuration and data is coming through.
I even tried to not have double negatives, with another new attribute, to make sure.
Running against the following rule, the coupon gets refused: "Coupon code is not valid."
This is correct, as the product is set as "Can use coupon? = No".
But the problem is that I do not want to do a check against all the other products, but rather against, the few that has been marked as "No".
This is not going to work for us anyway, as we don't want to apply a coupon if there is any product in the cart that has a value of "No" for the attribute "Can use coupon?".
This is a double negative rule, no matter which way you look at it.
I stepped through /app/code/core/Mage/SalesRule/Model/Rule/Condition/Product.php through the validate() function, when adding the product a second time, and the data shows up in the attributes, but when I go and re-apply the coupon, and step through this function again, those values are gone. this is weird.
When adding new product to shopping cart:
When Re-applying Coupon Code:
Where the heck did description and some of the other attributes disappear to?
UPDATE NOTE I am getting a bit closer.
When I change the following validate() function in "/app/code/core/Mage/SalesRule/Model/Rule/Condition/Product.php" from the following:
public function validate(Varien_Object $object)
{
$product = false;
if ($object->getProduct() instanceof Mage_Catalog_Model_Product) {
$product = $object->getProduct();
} else {
$product = Mage::getModel('catalog/product')
->load($object->getProductId());
}
$product
->setQuoteItemQty($object->getQty())
->setQuoteItemPrice($object->getPrice())
->setQuoteItemRowTotal($object->getBaseRowTotal());
return parent::validate($product);
}
To This:
public function validate(Varien_Object $object)
{
$product = false;
$product = Mage::getModel('catalog/product')
->load($object->getProductId());
$product
->setQuoteItemQty($object->getQty())
->setQuoteItemPrice($object->getPrice())
->setQuoteItemRowTotal($object->getBaseRowTotal());
return parent::validate($product);
}
It then works fine!

Found out the actual problem, and here is what I did for the solution:
The following bit gets the product data for the Shopping Cart Price Rules:
if ($object->getProduct() instanceof Mage_Catalog_Model_Product) {
$product = $object->getProduct();
} else {
$product = Mage::getModel('catalog/product')
->load($object->getProductId());
}
The culprit is this line: $product = $object->getProduct();
That calls a line from the "Quote/Item/Abstract.php" being: $product = $this->_getData('product');
It will simply get the cached data from the system, and that consists of a selection like this:
$productCollection = Mage::getModel('catalog/product')->getCollection()
->setStoreId($this->getStoreId())
->addIdFilter($this->_productIds)
->addAttributeToSelect(Mage::getSingleton('sales/quote_config')->getProductAttributes())
->addOptionsToResult()
->addStoreFilter()
->addUrlRewrite()
->addTierPriceData();
The problem with this is, the following line:
->addAttributeToSelect(Mage::getSingleton('sales/quote_config')->getProductAttributes())
That will get only the attributes defined in the Config.xml for quote items being:
<quote>
<item>
<product_attributes>
<sku/>
<type_id/>
<name/>
<status/>
<visibility/>
<price/>
<weight/>
<url_path/>
<url_key/>
<thumbnail/>
<small_image/>
<tax_class_id/>
<special_from_date/>
<special_to_date/>
<special_price/>
<cost/>
<is_recurring/><!-- for totals calculation, placing and processing order -->
<recurring_profile/><!-- for placing order -->
<gift_message_available/>
</product_attributes>
</item>
</quote>
This will not get the new custom attribute that I want, and the rule will never be able to compare the data with the expected value properly.
Adding the following xml to my custom sales module config.xml, which have some observers in it for other stuff, fixes the problem.
<sales>
<quote>
<item>
<product_attributes>
<exclude_from_coupon/>
</product_attributes>
</item>
</quote>
</sales>
Then it all works!
Why did it work in the enterprise version of magento
Don't really know. I think Enterprise is maybe looking for settings on the actual Attribute definition, and includes it to the list of Attributes to collect, and then return the data for it, my guess. I will research that, and update it here to the post.
At least I now know what the real issue is, and why the Shopping Cart Rule Condition (coupon) failed.
Thanks to all those who helped me with some good questions and suggestions.
Another day wiser with magento...

I tested this with Magento CE 1.5.1.0 and cannot reproduce your problem. For me, it works.
Please recheck the following for your CE installation:
You set the default attribute value to "yes"
You did reindex the indexes as required.
You edited one test product and explicitly set the attribute value to "no".
(Just to be sure) You updated the caches.
You set the shopping cart price rule exactly as described above (apply if conditions are FALSE and allow is set to NO).
If you don't have any custom code interfering, this should work. It does in the default installation.
If it still doesn't work:
What happens if you rephrase the condition to only apply if all conditions are TRUE and allow is set to TRUE (does remove the double negative which is more complicated)?
Please post screenshots of the backend forms for shopping cart price rule, product attribute and the product.

Related

Magento - Get attribute options value and quantity

Hello and good day to all the members of this great community. I'm still new in PHP and especially in Magento.
I'm not posting, waiting for answers, and leaving without replying back. This is a learning process. I hope to get a great support from all of you.
I have a product. I did create custom option for the product, that is an attribute named "a_size". The attribute has value of S, M and L. Each of the value has quantity.
In the single product view, I would like to call all the available size. That is the size (S, M, or L) that has quantity more than 0. I just want to show the available size, not how much the size left.
Can anybody guide me? I'm using Magento 1.7.x and as far for this 2 weeks, I did try pretty many of suggested answers from the community thru the search function.
The replies will be much appreciated. Thank you.
There are a few things to try.
Firstly check that when you set up your new attribute in the Magento Admin (Catalog->Attributes->Manage Attribute) that in the Frontend Properties box you have set Visible on Product View Page on Front-end to yes.
To get size values I use this code:
$cabac_sizeAttribute = $_product->getAttributeText("a_size");
but I have other code for getting attribute values that goes like this:
$_product_helper = Mage::helper('catalog/output');
$temp = $_product_helper->productAttribute($_product, $_product->getASize(), 'a_size');
I think it is related to the type of attribute: text, dropdown, multiselect etc so try both and see how you get on. But really the function productAttribute() is just applying formatting. You can read the function in the file app/core/Mage/Catalog/Helper/Output.php
Also, I wonder, if you have set up a configurable product and you are on the product view page then you will be viewing the configurable product. That product won't have an a_size value: you are trying to access the a_size attribute of the simple products that make up the configurable product, yes? Everything I wrote above is (I think) correct but to get the attribute of the simple products that are part of a configured product you should study the code in the function getJsonConfig() of the file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php
And in particular to these lines:
//file: file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php
//class: Mage_Catalog_Block_Product_View_Type_Configurable
//function: getJsonConfig()
foreach ($this->getAllowProducts() as $product) {
$productId = $product->getId();
foreach ($this->getAllowAttributes() as $attribute) {
$productAttribute = $attribute->getProductAttribute();
$productAttributeId = $productAttribute->getId();
$attributeValue = $product->getData($productAttribute->getAttributeCode());
Being careful about variable naming: $product is local here, I suggest changing it, and about $this - but if you are in a .phtml of the product view for configurables then I think your $this is already Mage_Catalog_Block_Product_View_Type_Configurable
Welcome to Magento coding. You are doing well; it is a long but rewarding path. (hints: local.xml is your vital friend and so is Alan Storm if you haven't come across his content yet.)
[Additionally, (welcome to Magento) I think you are trying to say eg S and L are out of stock and M is in stock but actually the function getAllowProducts() will disallow a product with zero stock and exclude it from the returned object. You will need to use
$allProducts = $this->getProduct()->getTypeInstance(true)
->getUsedProducts(null, $this->getProduct());
(taken from function getAllowProducts() in file app/core/Mage/Catalog/Block/Product/View/Type/Configurable.php)
and then, if needed, check that each product is allowed to be shown eg status=ENABLED, and then check its stock level...
]
Malachy.
If you want to get the values of your drop down attribute use the following code
$_product->getASize();
and initially load the product object

Magento: how to get the price of a product with catalog rules applied

I'm developing a script (external to Magento, not a module) which aims to output a text list of all available products, their prices and some other attributes. However, catalog price rules don't seem to be applied to product prices. If I use any of the following:
$_product->getPrice()
$_product->getFinalPrice()
I get the normal price (without rules being applied).
If I use:
$_product->getSpecialPrice()
I get null unless the product actually has a special price inserted in the product itself (i.e. if special price is not related with catalog rules).
I also tried
Mage::getModel('catalogrule/rule')->calcProductPriceRule($product,$product->getPrice())
as suggested in the answer given by Fabian Blechschmidt, but interestingly it returns the normal price only if the product is affected by any catalog rule, returning null otherwise.
There was a similar question in StackOverflow and Magento Forums some time ago, but the provided answer (which is to insert the code bellow) doesn't work for me (returned prices remain the same).
Mage::app()->loadAreaPart(Mage_Core_Model_App_Area::AREA_FRONTEND,Mage_Core_Model_App_Area::PART_EVENTS);
Does anybody have an idea of how to achieve this?
I'm using Magento 1.6.2.0.
Thanks in advance.
Thanks to you, I found a new site:
http://www.catgento.com/magento-useful-functions-cheatsheet/
And they mentioned:
Mage::getModel('catalogrule/rule')->calcProductPriceRule($product,$product->getPrice())
HTH
As catalog price rules heavily depend on time, store and visiting customer, you need to set those parameters when you want to retrieve the product final price with it's price rules applied.
So, in your case, make sure that provided product is passed with the desired store and customer group id, which can be set as:
Mage::getModel('catalogrule/rule')->calcProductPriceRule($product->setStoreId('STORE_ID')->setCustomerGroupId('CUSTOMER_GROUP_ID'),$product->getPrice())
I discovered the problem. The discounted prices display Ok in the store frontend. The problem was that I was developing a script "external" to Magento (thus not a Magento module), something like:
<?php
set_time_limit(0);
ignore_user_abort();
error_reporting(E_ALL^E_NOTICE);
header("Content-Type: text/plain; charset=utf-8");
require_once "app/Mage.php";
// Get default store code
$default_store = Mage::app()->getStore();
...
For everything to work properly it seems that one must follow the proper Magento bootstrap, and develop everything as a module. My script was so simple that I thought it wouldn't be necessary to code a complete module. In other words, everything in Magento should really be a module.
Concluding, using the module approach, all the methods work as expected:
$_product->getPrice()
$_product->getFinalPrice()
$_product->getSpecialPrice()
Thank you all for your input.
This helped me in this issue: http://www.magentocommerce.com/boards/viewthread/176883/
. Jernej's solution seems plausible, but it does not handle rules that Overwrite other rules by using 'stop processing' and therefore can apply more than one rule.
$original_price = $_product->getPrice();
$store_id = 1; // Use the default store
$discounted_price = Mage::getResourceModel('catalogrule/rule')->getRulePrice(
Mage::app()->getLocale()->storeTimeStamp($store_id),
Mage::app()->getStore($store_id)->getWebsiteId(),
Mage::getSingleton('customer/session')->getCustomerGroupId(),
$_product->getId());
// if the product isn't discounted then default back to the original price
if ($discounted_price===false) {
$discounted_price=$original_price;
}

Magento product load - difference between loadByAttribute and load methods

today I'm fighting with Magento again :) and I found a difference between
$product = Mage::getModel('catalog/product')->loadByAttribute('sku', $product_sku);
and
$product = Mage::getModel('catalog/product')->load($product_id);
Can anyone exaplain me a difference between these two approaches? I found that when I'm loading a product by sku then when I 'try to re-save it with changed data then I get error exception 'Varien_Exception' with message 'Invalid method Varien_Object::save in app\code\core\Mage\CatalogInventory\Model\Observer.php(153): Varien_Object->__call('save', Array) that's true because once you try to load by sku then another observer sets product's stock item as Varien_Object, that looks like pitfall or I just dont understand it enough, but
I do daily Magento development from its beginnig so I know a lot about system and this is new for me. Thanks in advance, Jaro.
Interesting. While both methods will net you a single product model instance with fully loaded EAV data (provided the third parameter of loadByAttribute() is not passed or is *), the observers which add stock-related data are different for products vs. product collections, yet both stock information objects are added to the product data key "stock_item". It's debatable, but this feels like a bug. I would think that Mage_CatalogInventory_Model_Observer::saveInventoryData() or Mage_CatalogInventory_Model_Observer::_prepareItemForSave() would handle this.
You could resolve this issue by setting the product stock item fully on your product instance using the stock_item object.
loadByAttribute is a serious misnomer in my opinion because it doesn't actually trigger a load(); rather it uses getResourceCollection():
public function loadByAttribute($attribute, $value, $additionalAttributes = '*')
{
$collection = $this->getResourceCollection()
->addAttributeToSelect($additionalAttributes)
->addAttributeToFilter($attribute, $value)
->setPage(1,1);
Because it doesn't trigger the observer events associated with load() it means the resulting product object doesn't include the full set of product data you might want. In my case I needed the "description" attribute and it wasn't included.
There are several ways to resolve this:
Use a different method to load by SKU:
$product = Mage::getModel("catalog/product");
$product->load($product->getIdBySku("whatever"));
Force the desired attribute data to be included in the default product resource data by visiting Magento Admin > Catalog > Attributes > Edit attribute > "Used in Product Listing" = "Yes" and then reindexing. You should then be able to use the attribute data (in the frontend, at least) using loadByAttribute().
See also https://magento.stackexchange.com/a/197286/18855

Magento - Credit Memo "Return to Stock" not updating Stock Availability

So when you do a credit memo in Magento, it sets the stock back to the correct level but does not change the "out of stock" back to "in stock" (if applicable). I came across this post by Wright Creatives (http://wrightcreativelabs.com/blog/55-credit-memo-in-stock.html) and it solves this problem. However, the method is too slow! It takes about 30 seconds per product.
I've ultimately had to remove this as a solution (because of the "speed") and now my boss would like the functionality reimplemented.
I know that the is_in_stock data controls this & I'm wondering if there is already a module out there, an article/tutorial, or someone who can help me get started on a "better/faster" solution.
I know it's old but because this isn't yet fixed not even in 1.7.0.1 I came up with a better solution.
Tested on 1.5.1 and above:
\app\code\core\Mage\CatalogInventory\Model\Observer.php
in
public function refundOrderInventory($observer)
after
Mage::getSingleton('cataloginventory/stock')->revertProductsSale($items);
//add this
foreach ($creditmemo->getAllItems() as $item) {
$productId = $item->getProductId();
$product = Mage::getModel('catalog/product')->load($productId);
if(!$product->isConfigurable()){
$stockItem = $product->getStockItem();
//$stockItem->setQty($item->getQty());
$stockItem->setIsInStock(1);
$stockItem->save();
$product->setStockItem($stockItem);
$product->save();
}
}
Write a module that observes the event for credit memos and sets the in_stock flag on the products stock item object before product save. I cant tell you the event you want to observe but I am positive you can find it :)
If there is none, the uglier way would be to observe the products before save. More logic to pull it off but if you always want products to be in stock if they have qty regardless of anything else, then its not a bad idea.
Stores >> Config >> Inventory >> scroll to bottom
Go to System -> Configuration -> Inventory (under Catalog) -> Product Stock Options -> Automatically Return Credit Memo Item to Stock and make sure it's set to Yes.
Or simply go to your database and in core_config_data where the path is 'cataloginventory/item_options/auto_return' make sure that the value column is set to '1';

Display Percentage or Amount of discount for catalog price rule in Magento

I'm struggling to figure out how to display the percentage or the amount of discount that is applied to a product in Magento via the Catalog Price Rules.
For example: I want the price to be displayed in the front-end as follows: [old-price] [special-price] [discount info] where [old-price] has a css strike through.
The [old-price] and [special-price] is available by default through the tax helper. I've tried using the CatalogRule model, but I have no way to load it with a product id as the load function expects an entity id and from what I can tell, there aren't any other useful methods to load by product ID. I've var dumped (as well as using get_class_methods) just about everything that I found in the price.phtml file (apart from $this of course), but nothing helps.
I could just use a simple calculation to work out the discount percentage or amount, but I have no way of knowing whether the catalog rule is based on a percentage, or fixed amount.
I hope this all makes sense?Thanks for the help.Rémy
I do agree Magento makes this kind of thing a bit too hard. I wanted to display the description of the coupon code next to the entered coupon code. I suspect my code will be able to help you on your way. I put this code at the top in the template checkout/cart/coupon.phtml:
<?php
$c = Mage::getResourceModel('salesrule/rule_collection');
$c->addBindParam('coupon_code', $this->getCouponCode());
$c->getSelect()->where("coupon_code is null or coupon_code='' or coupon_code=:coupon_code");
foreach ($c->getItems() as $item) {
$coupon_description = $item->getDescription();
}
?>
So you can see $coupon_description now holds the description of the Shopping Cart Price Rule as long as the user specified a coupon code. You can add more properties from the coupon this way.

Resources