How to filter zero-prices products in listing page - magento

I have used the sorting by ->setOrder('price','desc');
but in this case its not working well when we sort the products by name
Is there any way to sort by price (from lower to bigger) in such way, that products with zero price will bein the end of the list???
public function setCollection($collection)
{
$this->_collection = $collection;
$this->_collection->setCurPage($this->getCurrentPage());
// we need to set pagination only if passed value integer and more that 0
$limit = (int)$this->getLimit();
if ($limit) {
$this->_collection->setPageSize($limit);
}
if ($this->getCurrentOrder()) {
$this->_collection->setOrder($this->getCurrentOrder(), $this->getCurrentDirection())
->setOrder('price','desc');
}
return $this;
}

The question not very clear, yet from what I understand:
(Price Sorting) This feature is provided by default in Magento in Product Listing Pages.
Maybe you have changed the price attribute # backend.
Check for price attribute.
Used in Product Listing = Yes
Used for Sorting in Product Listing = Yes
Clear Cache, and you should be ready to go.

Related

Add Child SKUs to Magento Fulltext Search Index

I'm not sure why this isn't part of the stock functionality of Magento, but I want customers to be able to search for a configurable product by a child SKU. For some reason, Magento doesn't index the child SKUs.
I found in app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php
$dynamicFields = array(
'int' => array_keys($this->_getSearchableAttributes('int')),
'varchar' => array_keys($this->_getSearchableAttributes('varchar')),
'text' => array_keys($this->_getSearchableAttributes('text')),
'decimal' => array_keys($this->_getSearchableAttributes('decimal')),
'datetime' => array_keys($this->_getSearchableAttributes('datetime')),
);
I've tried several variations, without success. SKU is a "static" attribute, accessible through $this->_getSearchableAttributes('static'). I'm fine with getting all static attributes, but it's not working. Depending on what a try I either get no change in results or an error that the static attribute table doesn't exist (which make sense, because static attributes are in the product entity table).
Does anybody have a suggestion to solve this problem?
Online research has found suggestions to add a hidden attribute with these values, but that shouldn't be needed. I would rather solve the problem properly.
So many ways to do this..
However, I will lead with something I believe would be much cleaner rather than screwing around with the actual Fulltext in app/code/core/Mage/CatalogSearch/Model/Resource/Fulltext.php.
The problem is the lines AFTER that snippet you included:
// status and visibility filter
$visibility = $this->_getSearchableAttribute('visibility');
$status = $this->_getSearchableAttribute('status');
$statusVals = Mage::getSingleton('catalog/product_status')->getVisibleStatusIds();
$allowedVisibilityValues = $this->_engine->getAllowedVisibility();
which ultimately leads to:
core/Mage/CatalogSearch/Model/Resource/Fulltext/Engine.php
/**
* Retrieve allowed visibility values for current engine
*
* #return array
*/
public function getAllowedVisibility()
{
return Mage::getSingleton('catalog/product_visibility')->getVisibleInSearchIds();
}
then:
core/Mage/Catalog/Model/Product/Visibility.php
public function getVisibleInSearchIds()
{
return array(self::VISIBILITY_IN_SEARCH, self::VISIBILITY_BOTH);
}
So what you need to do is go to your simple product associated with your configurable and change the visibility to 'Search'.
However, here it is in action now:
However, yes, that doesn't look pretty at all. The next order of business is now modifying the search results so that when it lists a simple product item that's a.) associated to a configurable product b.) visibility ID is explicitly set to Mage_Catalog_Model_Product_Visibility::VISIBILITY_IN_SEARCH, you need to render the item in search results to the actual configurable product parent (and the rest of its details, such as images).
(I'll continue this later. my computer decided to crap out on me, and I lost over 50% of what I was writing. Even the part where we modify the fulltext class! Darn. see you later.)
I've used the method of setting simple product visibility to search. Then instead of displaying the simple product load/display its parent product.
Beside the frontend query in loop performance issues I never found this solution very satisfying. So after another whack at it I found another option.
I'm adding simple product SKUs to the fulltext index for grouped products but the same approach should work for configurable products.
Here is the idea:
Mage_CatalogSearch_Model_Resource_Fulltext->_prepareProductIndex
contains
$typeInstance = $this->_getProductTypeInstance($productData['type_id']);
if ($data = $typeInstance->getSearchableData($product)) {
$index['options'] = $data;
}
So by overriding the getSearchableData method in the product type class you can add data to the fulltext index. For grouped product my updated looked like:
... extends Mage_Catalog_Model_Product_Type_Grouped
{
/**
* all child SKUs to search data
*/
public function getSearchableData($product=null)
{
$searchData = parent::getSearchableData();
$adapter = Mage::getSingleton('core/resource')->getConnection('core_read');
$select = $adapter->select()
->from(
array('p' => Mage::getSingleton('core/resource')->getTableName('catalog/product')),
array('sku')
)
->joinLeft(
array('r' => Mage::getSingleton('core/resource')->getTableName('catalog/product_relation')),
'r.child_id = p.entity_id',
array()
)
->where('r.parent_id = ?', $this->getProduct($product)->getId());
$query = $adapter->query($select);
while ($row = $query->fetch()) {
$searchData[] = $row['sku'];
}
return $searchData;
}
Reindexed search and catalogsearch_fulltext.dataindex should contain the additional data.
There is a solution for this but it is a paid extension (we license it), it is not build in to Magento standard so you cannot search by child attributes and just return the parent configurable/grouped/bundle product.

Magento - Get Product SKU's and quantities on order

I have created an event observer on the "sales_order_place_after" event, which fires when the user places an order in Magento's checkout. That event triggers my observer, which needs to get the following information, which is then sent to an external inventory management system.
Information needed: The SKU and quantity of every product on the order
I have included part of my observer model below. I am accessing the order object. I can get all the items in the order, but how do I get the SKU for every product that makes up the order. For example, with a simple product, this is very easy. However, with a bundled or configurable product I do not know how to access the children that make up that bundled product with their SKU's and quantities. That is the info I need for both bundled and configurable products. I need the SKU and quantities of the children that were selected. For the life of me, I can't figure out what method to call to access that information. I wish there was something like: $item->getBundleChildrenSkuQuantity();
$order = $observer->getEvent()->getOrder();
$joomecomPacket = array();
if ($order->getTotalItemCount() > 0) {
$items = $order->getAllItems();
foreach ($items as $item) {
$productType = $item->getProductType();
switch ($productType) {
case 'bundle':
break;
case 'configurable':
default: // simple products
if (isset($joomecomPacket[$item['sku']])) {
$joomecomPacket[$item['sku']] += $item['qty_ordered'];
} else {
$joomecomPacket[$item['sku']] = $item['qty_ordered'];
}
break;
}
}
}
You're missing a break after your configurable case. The default case is executing for configurable products.
Try this:
$items = $order->getAllVisibleItems(); // gives only parent items
foreach ($items as $item){
$childItem = $item->getChildren(); //do something with $cildItem like $childItem->getSku() etc...
}

Magento - Varien Grid - Totaling entire collection

I have created a custom product grid using the varien grid functionality and am using it as an order form. Basically, there is a Product Name, Product Price, and an input field for the user to enter the amount of a product they would like.
There are thousands of products, and I need to give a running subtotal of what the user has selected.
The closest I've gotten is as adding the following to Grid.php...
protected function _afterLoadCollection()
{
$collection = $this->getCollection()->load();
$total = 0;
if ($collection) {
foreach($collection as $item) {
$itemPrice = $item->getPrice();
$itemQty = $item->getQty();
$total = $total + ($itemPrice * $itemQty);
}
echo $total;
}
}
However, the results are affected by the current limit, and therefore only totals the current page.
Is there a way around this, or a better way to get the running total of the two columns?
Thanks in advance, I've been stuck on this for days!
You can try this to clear the collection complements, and set for the first page a big limit
$_productCollection = $this->getLoadedProductCollection();
$_productCollection->clear();
$_productCollection->setPage(1,99999);

Display collection of Shopping cart rules and products categories associated to each rule

I want to check if there is a sales promotion on the product then stick the promotion label on that product on category list page. But I don't know how to loop through all the shopping cart rules and retrieve the products/categories associated to each rule.
EDITED
Thanks seanbreeden, but I can't pull the skus from $conditions. var_dump($conditions); shows this:
{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:12:"category_ids";s:8:"operator";s:2:"==";s:5:"value";s:2:"23";s:18:"is_value_processed";b:0;}}}}}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:2:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_address";s:9:"attribute";s:13:"base_subtotal";s:8:"operator";s:2:">=";s:5:"value";s:2:"45";s:18:"is_value_processed";b:0;}i:1;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:3:"sku";s:8:"operator";s:2:"==";s:5:"value";s:46:"test-config, BLFA0968C-BK001, BLFA0968C-CR033X";s:18:"is_value_processed";b:0;}}}}}a:6:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:6:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";}a:7:{s:4:"type";s:32:"salesrule/rule_condition_combine";s:9:"attribute";N;s:8:"operator";N;s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:7:{s:4:"type";s:42:"salesrule/rule_condition_product_subselect";s:9:"attribute";s:3:"qty";s:8:"operator";s:2:">=";s:5:"value";s:1:"1";s:18:"is_value_processed";N;s:10:"aggregator";s:3:"all";s:10:"conditions";a:1:{i:0;a:5:{s:4:"type";s:32:"salesrule/rule_condition_product";s:9:"attribute";s:3:"sku";s:8:"operator";s:2:"==";s:5:"value";s:16:"BLFA0968C-CR033X";s:18:"is_value_processed";b:0;}}}}}
but when I loop through $conditions i.e.
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
$conditions = $rule->getConditionsSerialized();
foreach ($conditions as $condition) {
var_dump($condition);
}
}
it doesn't show anything so don't really know how to pull skus here.
EDIT2
As Alaxandre suggested, I'm not using unserialized approach. I'm doing it like this now:
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
if ($rule->getIsActive()) {
//print_r($rule->getData());
$rule = Mage::getModel('salesrule/rule')->load($rule->getId());
$conditions = $rule->getConditions();
$conditions = $rule->getConditions()->asArray();
foreach( $conditions['conditions'] as $_conditions ):
foreach( $_conditions['conditions'] as $_condition ):
$string = explode(',', $_condition['value']);
for ($i=0; $i<count($string); $i++) {
$skus[] = trim($string[$i]);
}
endforeach;
endforeach;
}
}
return $skus;
And then checking in list page if sku matches within $skus array then show the label. But again there are limitation with this approach as well. I'm think of another approach (I'm not sure if thats is possible).
Thinking of creating a new table (to save the sales rules products).Everytime save the sales rule, catch the save rule event and update the table with Rule name and all the associated products. Then on the list page check that table, if products exist in the table, show the appropriate label. Now I think the event is adminhtml_controller_salesrule_prepare_save (not 100% sure) but I don't know how to get the sku from the rule condition in the observer to save in the new table.
I would suggest you to do it like this. When you had a product to cart, each rules are checked to calculate the final price and reduction. You can know which rules are applied to each item of your cart. In the table sales_flat_quote_item you have the column applied_rule_ids. I think you can access to this in php, by a function getAllItemsInCart or something like this (you have to find out). After you do $item->getAppliedRuleIds() and finally you can get the name of the rule apply to an item (product).
Good luck :)
Edit:
I read again your request and I think my answer doesn't fit with your request.
Your case is even more complicated. For each product on your catalog page you have to apply all the rules of your website. But Mage_SalesRule_Model_Validator process expect item and not product...
If you have lot of rules this task will slow down your catalog and this is really not good! The best would be to cache this result of the rules label in the database, may be in the table catalog_category_product or... (and even better to generate this cache automatically).
Edit2:
Other possibility would be to have a new field in rule creation where you set manually the related products (sku). You save this data in the table salesrule or in a new table salesrule_related_sku.
Then when you display the catalog you check for the sku and if the rule still active.
This solution would be the easiest one :-)
You could pull the getMatchingProductsIds from /app/code/core/Mage/CatalogRule/Model/Rule.php and compare them with the skus displayed on the category list page.
$catalog_rule = Mage::getModel('catalogrule/rule')->load(1); // ID of your catalog rule here, or you could leave off ->load(1) and iterate through ->getCollection() instead
$catalog_rule_skus = $catalog_rule->getMatchingProductIds();
hth
EDIT
Here's a way to get the serialized conditions:
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
$conditions = $rule->getConditionsSerialized();
var_dump($conditions);
}
EDIT 2
There would have to be a better way to do this. The only way I could pull that data was to unserialize then iterate with foreach through each layer. Anyone have any better ideas for this? This works but is very sloppy.
$rules = Mage::getResourceModel('salesrule/rule_collection')->load();
foreach ($rules as $rule) {
if ($rule->getIsActive()) {
$conditions = $rule->getConditionsSerialized();
$unserialized_conditions = unserialize($conditions);
$unserialized_conditions_compact = array();
foreach($unserialized_conditions as $key => $value) {
$unserialized_conditions_compact[] = compact('key', 'value');
}
for ($i=0;$i<count($unserialized_conditions_compact);$i++) {
if (in_array("conditions",$unserialized_conditions_compact[$i])) {
foreach($unserialized_conditions_compact[$i] as $key => $value) {
foreach($value as $key1 => $value1) {
foreach($value1 as $key2 => $value2) {
foreach($value2 as $key3 => $value3) {
$skus[] = explode(",",$value3['value']);
}
}
}
}
}
}
}
}
var_dump($skus);
The rules are associated to all product for a website. There is no rules set for a specific products/categories from the database point of view. For each product in the cart, Magento will validate all the rules you have for a website. This operation is done in the class Mage_SalesRule_Model_Validator. The only way to solve your request is to extend the function process from this class (at least I think so :p).
I wanted the same thing as you want. I wanted to get associated SKUS, Category Ids and any other conditions value to generate Google feeds to be used in Google merchant promotions.
I have used the recursive function to reach to last children of the condition and fetch its value.
I am checking based on the attribute value of the condition. If an attribute value is blank then go one step down and check if attribute value present and if so then fetch the value of it otherwise continue to go down.
Here is the code that I used to fetch values. Which will also work for the case, when two conditions are on the same level.
public function get_value_recursively($value){
foreach($value as $key => $new_value) {
if(strlen($new_value[attribute]) == 0){
$value = $new_value[conditions];
return $this->get_value_recursively($value);
}else{
$resultSet = array();
if (count($value) > 1){
for ($i=0;$i<count($value);$i++) {
$resultSet[] = array('attribute' => $value[$i][attribute], 'value' => $value[$i][value]);
}
$result = $resultSet;
}else{
$result = array('attribute' => $new_value[attribute], 'value' => $new_value[value]);
}
return json_encode($result, JSON_FORCE_OBJECT);
}
}
}
according to #seanbreeden answer you can call this function from first foreach
It will return the result like this :
{"0":{"attribute":"category_ids","value":"5, 15"},"1":{"attribute":"sku","value":"msj000, msj001, msj002"}}
P.S. I am not PHP dev. So, Ignore layman style code. :)

Magento Free Shipping for Match Items Only - Using Table Rates Based on Order Total

Can I achieve the following with Magento:
I'm using a table rate system of price vs destination. And I've got a shopping cart price rule of Free Shipping for products with the attribute free_shipping -> to set yes.
This works fine if you have normal products in the basket OR free shipping products in the basket. However if you have both a normal product AND a free shipping product in the basket. It calculates the shipping based on the order total including the product with free shipping.
How can I correct this, so the shipping is applied only to the order total of products not including free shipping, when both type of products exist in the basket?
Is there a Magento Plugin for this?
I had the same problem and implemented a small code change to make it work.
Basically forget the promotion rule. Disable it. It doesn't seem to work properly with the shipping rates table when applying free shipping to individual items. The shipping rules seem to take precedence and calculate based on cart total.
The trick is to subtract the free shipping items from the cart total at the point the Shipping Rates module is making the calculation.
In my case everything inside a particular category (id:15) was to get free shippping. But you can amend the logic to suit your needs.
You will need to amend the following file (or copy it to your local codebase to do things properly).
app\code\core\Mage\Shipping\Model\Carrier\Tablerate.php
CHANGE THIS:
public function collectRates(Mage_Shipping_Model_Rate_Request $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
// exclude Virtual products price from Package value if pre-configured
if (!$this->getConfigFlag('include_virtual_price') && $request->getAllItems()) {
foreach ($request->getAllItems() as $item) {
if ($item->getParentItem()) {
continue;
}
if ($item->getHasChildren() && $item->isShipSeparately()) {
foreach ($item->getChildren() as $child) {
if ($child->getProduct()->isVirtual()) {
$request->setPackageValue($request->getPackageValue() - $child->getBaseRowTotal());
}
}
} elseif ($item->getProduct()->isVirtual()) {
$request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal());
}
}
}
TO THIS:
public function collectRates(Mage_Shipping_Model_Rate_Request $request)
{
if (!$this->getConfigFlag('active')) {
return false;
}
// exclude Virtual products price from Package value if pre-configured
//And Exclude items which should have Free Shipping
if (!$this->getConfigFlag('include_virtual_price') && $request->getAllItems()) {
$freeshipping_category_id = 15;
foreach ($request->getAllItems() as $item) {
if ($item->getParentItem()) {
continue;
}
if ($item->getHasChildren() && $item->isShipSeparately()) {
foreach ($item->getChildren() as $child) {
if ($child->getProduct()->isVirtual()) {
$request->setPackageValue($request->getPackageValue() - $child->getBaseRowTotal());
}
//If it's in the free shipping, remove it's value from the basket
$arr_category_ids = $child->getProduct()->getCategoryIds();
if ( in_array($freeshipping_category_id, $arr_category_ids) ) {
$request->setPackageValue($request->getPackageValue() - $child->getBaseRowTotal());
}
}
} elseif ($item->getProduct()->isVirtual()) {
$request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal());
}
//If it's in the free shipping category, remove it's value from the basket
$arr_category_ids = $item->getProduct()->getCategoryIds();
if ( in_array($freeshipping_category_id, $arr_category_ids) ) {
$request->setPackageValue($request->getPackageValue() - $item->getBaseRowTotal());
}
}
}
In your sales rule set the Free Shipping to 'FOR MATCHING ITEMS ONLY'.
If I understood your question correctly, the solution is really easy and quick, here is how I've solved it:
In the file: Validator.php (/app/code/core/Mage/SalesRule/Model/)
search for the string "case Mage_SalesRule_Model_Rule::FREE_SHIPPING_ITEM:" and just before the "break;" add this simple thing "$item->setWeight(0);"
This will force the weight of the item to be 0 so no shipping price will be calculated on it, and if you disable the free shipping option for hat item, the weight is considered and everything works fine.
I know that this question has been answered and accepted, but I would like to offer a way to do this without programming, or changing core files if at all possible
If you want to create your item as a "Virtual" item, you can exclude virtual items from shipping fees
If item is created already
Go to your database with a program like phpMyAdmin and navigate to catalog_product_entity, find your product SKU and change type_id to virtual
If item is not created
Create item as Virtual Product during the first step of product creation (instead of Simple Product or Bundled Product, etc.)
To exclude Virtual Products from shipping fees
Navigate to System > Configuration > Shipping Methods and under the Table Rates header set Include Virtual Products in Price Calculation to No
Please note, this will only work for, and is only necessary for, the Table Rates shipping method
UPS, USPS, FedEx and DHL determine by weight, so since Virtual Product types have a product weight of 0 it will not affect shipping costs for these methods

Resources