Catalog Price Rules applied to special_price - magento

First question on stackoverflow...i am excited :)
Currently magento is using the special price if its lower than the applied catalog price rule. If the catalog price rule makes the product cheaper than the special price, then the catalog price rule defines the shop price.
I am looking for an elegant way to make catalog price rules be applied
to the special price (additionally). Maybe there is some store config for it? Maybe
there is some neat observer way?
Thank you so much!

Works up to current Magento 1.9.3.10. Just tested it in a project after update. Josef tried another approach which might work as well.
I am sad to say, I solved my first real stackoverflow question for my own:
Goto Mage_CatalogRule_Model_Resource_Rule
Goto method _getRuleProductsStmt
Add this to the initial select of the method before the first original ->from:
$select->from(null, array('default_price' => new Zend_Db_Expr("CASE
WHEN pp_default_special.value THEN pp_default_special.value ELSE
pp_default_normal.value END")));
Add this after the first join() has happened
$specialPriceAttr = Mage::getSingleton('eav/config')
->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'special_price');
$specialPriceTable = $specialPriceAttr->getBackend()->getTable();
$specialPriceAttributeId= $specialPriceAttr->getId();
$joinCondition2 = '%1$s.entity_id=rp.product_id AND (%1$s.attribute_id=' . $specialPriceAttributeId . ')
AND %1$s.store_id=%2$s';
$select->join(
array('pp_default_special'=>$specialPriceTable),
sprintf($joinCondition2, 'pp_default_special', Mage_Core_Model_App::ADMIN_STORE_ID), null
);
How it works:
When a catalog price rule is applied (via backend or cron) the db table catalogrule_product_price is populated. The above SQL magic joins the special_price (if exists) to the resultset as column default_value, if no special_price is found the regular price gets joined.
The result has been checked and is working.
Have fun! And dont hack the core!

There seem to be some changes in newer Magento releases!
For 1.9 i had to:
copy app/code/core/Mage/CatalogRule/Model/Action/Index/Refresh.php to app/code/local/Mage/CatalogRule/Model/Action/Index/Refresh.php
Change _prepareTemporarySelect.
I post the function in full here. Joins for special_price are added and then the price added to the selection of the price field. It still prefers group prices, becuas I never use them, but that can be changed easily!
protected
function _prepareTemporarySelect(Mage_Core_Model_Website $website)
{
/** #var $catalogFlatHelper Mage_Catalog_Helper_Product_Flat */
$catalogFlatHelper = $this->_factory->getHelper('catalog/product_flat');
/** #var $eavConfig Mage_Eav_Model_Config */
$eavConfig = $this->_factory->getSingleton('eav/config');
$priceAttribute = $eavConfig->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'price');
$specialPriceAttr = Mage::getSingleton('eav/config')->getAttribute(Mage_Catalog_Model_Product::ENTITY, 'special_price');
$specialPriceTable = $specialPriceAttr->getBackend()->getTable();
$specialPriceAttributeId = $specialPriceAttr->getId();
$select = $this->_connection->select()->from(array(
'rp' => $this->_resource->getTable('catalogrule/rule_product')
) , array())->joinInner(array(
'r' => $this->_resource->getTable('catalogrule/rule')
) , 'r.rule_id = rp.rule_id', array())->where('rp.website_id = ?', $website->getId())->order(array(
'rp.product_id',
'rp.customer_group_id',
'rp.sort_order',
'rp.rule_product_id'
))->joinLeft(array(
'pg' => $this->_resource->getTable('catalog/product_attribute_group_price')
) , 'pg.entity_id = rp.product_id AND pg.customer_group_id = rp.customer_group_id' . ' AND pg.website_id = rp.website_id', array())->joinLeft(array(
'pgd' => $this->_resource->getTable('catalog/product_attribute_group_price')
) , 'pgd.entity_id = rp.product_id AND pgd.customer_group_id = rp.customer_group_id' . ' AND pgd.website_id = 0', array());
$storeId = $website->getDefaultStore()->getId();
if ($catalogFlatHelper->isEnabled() && $storeId && $catalogFlatHelper->isBuilt($storeId))
{
$select->joinInner(array(
'p' => $this->_resource->getTable('catalog/product_flat') . '_' . $storeId
) , 'p.entity_id = rp.product_id', array());
$priceColumn = $this->_connection->getIfNullSql($this->_connection->getIfNullSql('pg.value', 'pgd.value') , $this->_connection->getIfNullSql('p.special_price', 'p.price'));
}
else
{
$select->joinInner(array(
'pd' => $this->_resource->getTable(array(
'catalog/product',
$priceAttribute->getBackendType()
))
) , 'pd.entity_id = rp.product_id AND pd.store_id = 0 AND pd.attribute_id = ' . $priceAttribute->getId() , array())->joinLeft(array(
'pspd' => $specialPriceTable
) , 'pspd.entity_id = rp.product_id AND (pspd.attribute_id=' . $specialPriceAttributeId . ')' . 'AND pspd.store_id = 0', array())->joinLeft(array(
'p' => $this->_resource->getTable(array(
'catalog/product',
$priceAttribute->getBackendType()
))
) , 'p.entity_id = rp.product_id AND p.store_id = ' . $storeId . ' AND p.attribute_id = pd.attribute_id', array())->joinLeft(array(
'psp' => $specialPriceTable
) , 'psp.entity_id = rp.product_id AND (psp.attribute_id=' . $specialPriceAttributeId . ')' . 'AND psp.store_id = ' . $storeId, array());
$priceColumn = $this->_connection->getIfNullSql($this->_connection->getIfNullSql('pg.value', 'pgd.value') , $this->_connection->getIfNullSql('psp.value', $this->_connection->getIfNullSql('pspd.value', $this->_connection->getIfNullSql('p.value', 'pd.value'))));
}
$select->columns(array(
'grouped_id' => $this->_connection->getConcatSql(array(
'rp.product_id',
'rp.customer_group_id'
) , '-') ,
'product_id' => 'rp.product_id',
'customer_group_id' => 'rp.customer_group_id',
'from_date' => 'r.from_date',
'to_date' => 'r.to_date',
'action_amount' => 'rp.action_amount',
'action_operator' => 'rp.action_operator',
'action_stop' => 'rp.action_stop',
'sort_order' => 'rp.sort_order',
'price' => $priceColumn,
'rule_product_id' => 'rp.rule_product_id',
'from_time' => 'rp.from_time',
'to_time' => 'rp.to_time'
));
return $select;
}

I fixed it in another way. It was just easy to put into the price field for example 100 and then into special price field 90 when there was 10% discount on product page but now I removed special price from product page and just created catalog price rule 10% discount to that product(s) and now other rules also work:)

Related

phpfox : how to get my group name?

How can I get the group name I do belong ?
I could get these infos:
$user = Phpfox::getService('user')->get($username, false);
[user_group_id] => 6 [title] =>
user_group_title_3749ea904585437bb7e01c7e1571e162
You can use this query:
SELECT ug.title FROM phpfox_user as u
join phpfox_user_group as ug on ug.user_group_id = u.user_group_id
WHERE u.user_id=1
Then put the result in _p function
Example:
$sUserTitle = Phpfox::getLib('database')->select('ug.title')
->from(':user', 'u')
->join(':user_group', 'ug', 'ug.user_group_id = u.user_group_id')
->where('u.user_id=' . Phpfox::getUserId())
->execute('getSlaveField');
echo _p($sUserTitle)

Magento get manufacturer labels ONLY for a particular store

I'm trying to get manufacturer labels only for a particular store, but it's not working and I always get all manufacturers (of all stores). Any ideas? Thats my code:
$entityTypeIdentifier = 'catalog_product';
$attributeCode = 'manufacturer';
$storeId = 2;
$attributeModel = Mage::getModel('eav/entity_attribute');
$attributeId =
$attributeModel->getIdByCode($entityTypeIdentifier,$attributeCode);
$attribute = $attributeModel->load($attributeId);
$attribute->setStoreId( $storeId );
$attributeOptionsModel = Mage::getModel('eav/entity_attribute_source_table');
$attributeTable = $attributeOptionsModel->setAttribute($attribute);
$options = $attributeOptionsModel->getAllOptions(false);
echo '<pre>';
print_r($options);
$attribute->setStoreId( $storeId ); is ignored and thats the result:
Array
(
[0] => Array
(
[value] => 204
[label] => 3M ESPAÑA
)
.
.
.
Thats eav_attribute_option_value table:
value_id
option_id
store_id
value
1263
204
0
3M ESPAÑA
1264
204
1
3M ESPAÑA
In getAllOptions function of Mage_Eav_Model_Entity_Attribute_Source_Table class, an option's value will be returned whether it has store specific value or not.
For example, if you have 100 options for an attribute and store #2 has values for 20 of them, option model will return 100 options whether you setStoreId(2) or not. If you set store id, model will return 20 of them in store #2 value, other 80 options in default value.
But if you need an answer to get only that 20 values, here you are my friend:
$entityTypeIdentifier = 'catalog_product';
$attributeCode = 'manufacturer';
$storeId = 2;
$resource = Mage::getSingleton('core/resource');
$connection = $resource->getConnection('read');
$query = $connection->select()
->from(array('e' => $resource->getTableName('eav/attribute')), array())
->joinLeft(array(
'f' => $resource->getTableName('eav/entity_type')),
"e.entity_type_id = f.entity_type_id AND f.entity_type_code = '$entityTypeIdentifier'",
array()
)
->joinLeft(array(
'g' => $resource->getTableName('eav/attribute_option')),
'e.attribute_id = g.attribute_id',
array()
)
->joinRight(
array('h' => $resource->getTableName('eav/attribute_option_value')),
"g.option_id = h.option_id AND h.store_id = $storeId",
array('h.option_id', 'h.value')
)
->where('e.attribute_code = ?', $attributeCode);
$options = $connection->fetchAssoc($query);
echo '<pre>';
print_r($options);

Collection: filter by created_at by local time zone? (or CreatedAtStoreDate)

I have to filter a collection (invoice) by date to get all invoices of one month.
For example: the create_at date of one invoice (in database) is: 2014-04-30 22:27:30
If I filter the collection:
$dateFrom = '2014-04-01 00:00:00';
$dateTo = '2014-04-30 23:59:59';
$invoiceCollection->addAttributeToFilter('created_at', array(
'from' => $dateFrom,
'to' => $dateTo,
));
The invoice with this date will be in the collection.
There exists two kinds of dates:
$invoice->getCreatedAt()
And:
$invoice->getCreatedAtStoreDate()
In this case:
$invoice->getCreatedAt() = 2014-04-30 22:27:30 (UTC/GMT time)
$invoice->getCreatedAtStoreDate() = 2014-05-01 00:27:40 (local timezone)
Under Sales->Invoices "my" invoice will be collect under May. If I use
$invoiceCollection = Mage::getModel('sales/order_invoice')->getCollection();
the invoice is in "April".
So I have to filter my collection by CreatedAtStoreDate - but this is not a database-field.
My question:
How can I filter the order or invoice collection by CreatedAtStoreDate, so that the invoice or order with the date 2014-04-30 22:27:30 will be NOT in my April-Collection but in May?
Thanks for help.
You can use some GET param to define local offset, then add this offset to dates in filter. It will something like this (I didn't test it):
/**
* GET param which allow to set offset between local and server timezone. Server timezone is GMT.
* Examples: add ?gmt=2 in URL for set local time to GMT+2, ?gmt=-5 for GMT-5, etc.
*/
$gmt_offset = (int)Mage::app()->getRequest()->getParam('gmt');
$dateFrom = '2014-04-01 00:00:00';
$dateTo = '2014-04-30 23:59:59';
if($gmt_offset > -13 && $gmt_offset < 15 && $gmt_offset != 0){
$locale = Mage::app()->getLocale()->getLocaleCode();
$date_from = new Zend_Date(strtotime($dateFrom), Zend_Date::TIMESTAMP, $locale);
$date_to = new Zend_Date(strtotime($dateTo), Zend_Date::TIMESTAMP, $locale);
if($gmt_offset > 0){
$date_from->add($gmt_offset, Zend_Date::HOUR);
$date_to->add($gmt_offset, Zend_Date::HOUR);
}else{
$date_from->sub($gmt_offset, Zend_Date::HOUR);
$date_to->sub($gmt_offset, Zend_Date::HOUR);
}
$dateFrom = $date_from->get('YYYY-MM-dd HH:mm:ss');
$dateTo = $date_to->get('YYYY-MM-dd HH:mm:ss');
}
$invoiceCollection->addAttributeToFilter('created_at', array(
'from' => $dateFrom,
'to' => $dateTo,
));

sort by size opencart

I'm trying to add a new sorting method i'e "sort by height" in opencart.
A.location is:catalog/model/catalog/product.php -> added p.height here
$sort_data = array(
'pd.name',
'p.model',
'p.quantity',
'p.price',
'rating',
'p.sort_order',
'p.date_added',
'p.height'
);
B. in the same file
elseif($data['sort'] == 'p.height' ){
$sql .= " ORDER BY(" . $data['sort'] . ")ASC";
/*$sql .= "SELECT * FROM". DB_PREFIX . "product p ORDER BY p.height DESC";*/
}
C. location is:/catalog/controller/product/category.php
$this->data['sorts'][] = array(
'text' => $this->language->get('text_size_asc'),
'value' => 'height-ASC',
'href' => $this->url->link('product/category', 'path=' . $this->request->get['path'] . '&sort=height&order=ASC' . $url)
);
Result is i can see "sort by height" in option but nothing happens when i select it returns the default sort value.
Can any one suggest where i am doing wrong?
It should be p.height-ASC not height-ASC For the 'value' key otherwise it does not recognise it. You also need to change &sort=height to &sort=p.height in the 'url' key

Export comments from fb:comments

I'm a little desperate about this. I have a site with a comments box with around 183000 comments that I want to backup. But every time I do an fql.query, I only get 100 items. I had tried:
SELECT xid, object_id, post_id, time, text, id FROM comment WHERE xid='bdatapoyo' ORDER BY time DESC
and after that, using the last "time"
SELECT xid, object_id, post_id, time, text, id FROM comment WHERE xid='bdatapoyo' AND time >= xxxxxxx ORDER BY time DESC
but the latter only gives me one comment..
I made an PHP page with this code, but again, it only grabs 100 items:
<?php
require 'php-sdk/src/facebook.php';
$facebook = new Facebook(array(
'appId' => 'XXXXXXXXXX',
'secret' => 'XXXXXXXXXX',
'cookie' => true,
));
$conexion = mysql_connect("localhost", "XXXX", "XXXX");
mysql_select_db("TVN", $conexion);
$time_old = "1315070888";
do {
$fql = "SELECT fromid,time,text FROM comment WHERE xid='bdatapoyo' AND time >= " . $time_old . " ORDER BY time";
$response = $facebook->api(array(
'method' => 'fql.query',
'query' =>$fql,
));
foreach($response as $key => $value) {
$sql = "INSERT INTO comment (text, uuid, time) VALUES ('" . $value["text"] . "', '" . $value["fromid"] . "', '" . $value["time"] . "')";
$result = mysql_query($sql);
}
$time_old = $response[count($response)-1]["time"];
print_r($time_old);
} while (count($response) > 0);
?>
OK, I was able to pull this out with LIMIT and OFFSET. The correct FQL is
SELECT fromid,time,text FROM comment WHERE xid='bdatapoyo' LIMIT 100 OFFSET 0
and then paging with the OFFSET
is this code setup to import every single comment to a db from all users on your site? What is xid=bdatapoyo?

Resources