php round() leading mistake - magento

My website is build by magento system use for shopping. now I add a function about nice foreign price. for example:
if the price is $35.4 , it will be changed to $34.99;
if the price is $35.5 , it will be changed to $35.99;
code is : round(35.4) + 0.99;
now here is a question, my product has several options link 'type','colour'. and different option shows different price just as the default configurable product in magento.
in the product page: price is $1000 shows $999.99, colour red + $100 shows +$99.99
,and type big +50 shows+49.99. here the total price is 1149.97. but in shopping chat the price is round(1000 + 100 +50)+0.99 = 1149.99. so I don't know how to deal with it.
some one help me ..... waiting for your advice.

because magento final price is calculate onFly ,you shall overwrite
app/code/core/Mage/CatalogRule/Helper/Data.php
and change method to like as . (you need add to adminhtml it option or hardcode it)
public function calcPriceRule($actionOperator, $ruleAmount, $price)
{
$priceRule = 0;
switch ($actionOperator) {
case 'to_fixed':
$priceRule = min($ruleAmount, $price);
break;
case 'to_percent':
$priceRule = $price * $ruleAmount / 100;
break;
case 'by_fixed':
$priceRule = max(0, $price - $ruleAmount);
break;
case 'by_percent':
$priceRule = $price * (1 - $ruleAmount / 100);
break;
case 'my_custom_prices':
//your price rules
break;
}
return $priceRule;
}

Related

Magmi overwriting position of products in category

I'm using Magmi to import products into my Magento store. Categories are created on the fly and products are imported. All is working well.
Except for one thing: each time I run a Magmi import, the position of the product in the Magento category is set to 0. This way I cannot sort my products on position.
I have searched the Magmi wiki and github for someone who has run into the same problem, but didn't find anything.
Anyone familiar with this issue and is there a way to avoid it?
I wait too long an answer and do it myself, here is a fix:
my fix works other way - category positions clear only if you add $item["category_reset"] == 1; param to product params.
:1280 sting(in current magmi version) or find the public function assignCategories($pid, $item) in magmi_productimportengine.php.
ater
$cce = $this->tablename("catalog_category_entity");
$ccpt = $this->tablename("catalog_category_product");
add next code:
$sql = "SELECT $ccpt.*
FROM $ccpt
JOIN $cce ON $cce.entity_id=$ccpt.category_id
WHERE product_id=?";
$currentPositions = $this->selectAll($sql,$pid);
then change category reset:
if (!isset($item["category_reset"]) || $item["category_reset"] == 1)
{...}
to
if (isset($item["category_reset"]) && $item["category_reset"] == 1)
{
$sql = "DELETE $ccpt.*
FROM $ccpt
JOIN $cce ON $cce.entity_id=$ccpt.category_id
WHERE product_id=?";
$this->delete($sql, $pid);
$currentPositions = array();
}
after this change block with positioning
foreach ($catids as $catdef)
{...}
to:
// find positive category assignments
if (is_array($currentPositions) && count($currentPositions)) {
foreach ($currentPositions as $currentPosition) {
$catPos[$currentPosition['category_id']] = $currentPosition['position'];
}
}
foreach ($catids as $catdef)
{
$a = explode("::", $catdef);
$catid = $a[0];
if (count($a) > 1 && $a[1] != 0) {
$catpos = $a[1];
}
else {
if (isset($catPos[$catid]) && $catPos[$catid] != 0) {
$catpos = $catPos[$catid];
}
else {
$catpos = "0";
}
}
$rel = getRelative($catid);
if ($rel == "-")
{
$ddata[] = $catid;
}
else
{
$cdata[$catid] = $catpos;
}
}
if you dont import position, your current position will save. If it 0, it stay 0.
for clear actual position - add param to product item:
$item["category_reset"] == 1;
or change string back:
if ($item["category_reset"] == 1)
{ ....}
to:
if (!isset($item["category_reset"]) || $item["category_reset"] == 1)
{...}
Only a comment but since I'm a long-time reader but never setup an account and can't leave a comment directly with no rep. I know this is an old post, but I just found it and used AlexVegas's code above (thank you!). Worked almost fine for me, but in my case I still wanted the categories to fully reset to only what was in my Magmi import but I wanted the positions to remain intact. As-is above, the categories only append to the existing unless you use the category_reset column in your import, and if you do that it also resets the position.
If you're like me and want only the position to remain intact, but allow Magmi to overwrite the categories each time, use Alex's code above but tweak it a little
Where he says to change
if (!isset($item["category_reset"]) || $item["category_reset"] == 1)
{...}
to
if (isset($item["category_reset"]) && $item["category_reset"] == 1)
{
$sql = "DELETE $ccpt.*
FROM $ccpt
JOIN $cce ON $cce.entity_id=$ccpt.category_id
WHERE product_id=?";
$this->delete($sql, $pid);
$currentPositions = array();
}
Don't change it. It's that simple. In his code it prevents the category reset unless the column is specified, which is why the if statement gets changed. If the column exists, it also wipes out the currentPositions array that stores the current positions within the categories so those get reset as well.
If you want to append to the categories unless category_reset is in your import, but don't want to overwrite the positioning, use Alex's code as it is above in his answer but leave out
$currentPositions = array();
That way it won't overwrite the array that is storing the positions within the categories
Not an actual solution-
You can try using this plugin
http://www.magentocommerce.com/magento-connect/c3-category-position-import-export-extension.html/
You can ignore UPDATE position if exist
category creator/importer v0.2.5
at line 1248 replace with
if (count($inserts) > 0) {
$sql = "INSERT IGNORE INTO $ccpt (`category_id`,`product_id`,`position`) VALUES ";
$sql .= implode(",", $inserts);
// $sql .= " ON DUPLICATE KEY IGNORE";
$this->insert($sql, $data);
unset($data);
}
The Magmi wiki specifically mentions item positioning functionality here See quoted text below. This seems to describe exactly the functionality you are looking for? I have not tested it myself.
Quote:
Item positioning
From magmi 0.7.18 , category_ids column has been enhanced with item positioning. This feature is also supported in category importer from version 0.2+ (since category importer plugin is roughly a category_ids generator)
Sample
store,sku,....,categories
admin,00001,.....,cat name with \/ in the name and positioning::3
<= here we "escaped" the tree separator with a backslash , the category will be created as "catname with / in the name and positioning"
sku 00001 will be set with position 3 in the category
End quote

Magento Shopping Cart Rule - Is this combination possible

Hoping some one can help me with a Magento Rule - is this rule even possible
I have numerous products of numerous sizes all part of the same category.
Each product regardless of size costs £3.98 - If a buyer buys 3 products of the same category regardless of the product or size they get it for £9.99. If they buy 4 products, they get 3 of them for 9.99 but pay full price for the 4th...Every group of 3 is £9.99
I have a rule created that seems to work perfect if a Customer buys 3 / 6 / 9 items of the same product and same size...However if they mix and match it doesn't work (though they are the same category)
The rule is:
IF ALL of these conditions are TRUE:
If total quantity equals or greater than 3 for a subselection of items in cart matching ALL of these conditions:
Category is 4
I have also set the Discount Qty Step to be 3
* UPDATE *
Thanks for your reply - I have tried to implement what you suggest and have got so far as to where I get the category id of the added products. I'm unsure how to set the price the previous products so it will be an automatically discounted price
$quote = Mage::getSingleton('checkout/session')->getQuote();
$cartItems = $quote->getAllVisibleItems();
$itemPrice = "3.33";
foreach ($cartItems as $items) {
$product = $items->getProduct();
$prodCats = $product->getCategoryIds();
if (in_array('4', $prodCats)) {
$itemQty = $items->getQty();
}
$totalItems += $itemQty;
}
So what I want to do is apply a discount for multiple of 3's for any product that has a category_id of 4...The price will be 3.33 instead of the normal 3.99
You need event - observer approach for this, that will give you the flexibility you need. You can build an observer that catches the add-to-cart event sales_quote_add_item and put your logic there.
Following code within your observer function will point you in the right direction:
// Get products in cart:
$quote = Mage::getSingleton('checkout/session')->getQuote();
$cartItems = $quote->getAllVisibleItems();
foreach ($cartItems as $item) {
$itemSku = $item->getSku();
$itemQty = $item->getQty();
}
// Added product:
$item = $observer->getEvent()->getQuoteItem();
$itemQty = $item->getQty();
$itemSku = $item->getSku();
// Change price of added product:
$item->setOriginalCustomPrice($newPrice);
Good luck!

Magento : Out of stock products showing last in the category page

I am using Magento 1.7.0.2 and I have this code line that i am using in /app/code/core/Mage/Catalog/Block/Product/list.php :
$this->_productCollection = $layer->getProductCollection()
->joinField(
'inventory_in_stock',
'cataloginventory_stock_item',
'is_in_stock',
'product_id=entity_id',
'is_in_stock>=0',
'left')
->setOrder('inventory_in_stock','desc');
When sorting for position and name the out of stock products are last. But when sorting for price, the out of stock products are in a normal order not last.
How can i make that the out of stock products to be last even in the sort after price ?
Great find Alex! A tip: If you want to avoid changing core files (and possibly make this into a module), you can add it to an event listener, like this (tested on 1.8.1.0):
/**
* Fires before a product collection is loaded
*
* #param Varien_Event_Observer $observer
*/
public function catalog_product_collection_load_before($observer)
{
$collection = $observer->getCollection();
$collection->getSelect()->joinLeft(
array('_inventory_table'=>$collection->getTable('cataloginventory/stock_item')),
"_inventory_table.product_id = e.entity_id",
array('is_in_stock', 'manage_stock')
);
$collection->addExpressionAttributeToSelect(
'on_top',
'(CASE WHEN (((_inventory_table.use_config_manage_stock = 1) AND (_inventory_table.is_in_stock = 1)) OR ((_inventory_table.use_config_manage_stock = 0) AND (1 - _inventory_table.manage_stock + _inventory_table.is_in_stock >= 1))) THEN 1 ELSE 0 END)',
array()
);
$collection->getSelect()->order('on_top DESC');
// Make sure on_top is the first order directive
$order = $collection->getSelect()->getPart('order');
array_unshift($order, array_pop($order));
$collection->getSelect()->setPart('order', $order);
}
Edit: Changed back to catalog_product_collection_load_before from catalog_product_collection_apply_limitations_before and fixed order part priority.
I recommend you run two collections
1. Collection of products in stock sort by price.
->addAttributeToFilter('is_in_stock', array('gt' => 0));
reference link for collection :
http://www.magentocommerce.com/knowledge-base/entry/magento-for-dev-part-8-varien-data-collections
Collection of products that are outofstock sort by price.
Refer this link :
Magento List all out of stock items
I found the answer
You must add this code :
$this->getSelect()->joinLeft(
array('_inventory_table'=>$this->getTable('cataloginventory/stock_item')),
"_inventory_table.product_id = e.entity_id",
array('is_in_stock', 'manage_stock')
);
$this->addExpressionAttributeToSelect('on_top',
'(CASE WHEN (((_inventory_table.use_config_manage_stock = 1) AND (_inventory_table.is_in_stock = 1)) OR ((_inventory_table.use_config_manage_stock = 0) AND (1 - _inventory_table.manage_stock + _inventory_table.is_in_stock >= 1))) THEN 1 ELSE 0 END)',
array());
$this->getSelect()->order('on_top DESC');
Just before
if ($attribute == 'price' && $storeId != 0) {
in app/code/core/Mage/Catalog/Model/Resource/Eav/Mysql4/Product/Collection.php
Or in app/code/core/Mage/Catalog/Model/Resource/Product/Collection.php if you have Magento 1.7.0.0 +

Magento tax rounding issue

I got strange rounding issue for VAT in Magento. My product set up is
* product price incl 20% VAT is 183.59
I added 30 items into basket and it would cost 30 * 183.59 = 5507.70. I can see this value in basket/checkout so that's fine. If I have just 1 item in basket it's ok.
Also the final VAT would be 5507.70 * 20 / 120 = 917.95, but I'm getting 918.00
Do you have any idea how to fix this or where would I take a look? Thanks in advance.
In the end I found the solution. I changed System > VAT > Tax Calculation Method Based On from Unit price to Row Total and it works, more details here
The issue which I found is in core/store model. I had to rewrite roundPrice method and change rounding precision there.
public function roundPrice($price)
{
return round($price, 4);
}
Info
Round price in Magento based on previous rounding operation delta.
app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php:1392
app/code/core/Mage/Tax/Model/Sales/Total/Quote/Subtotal.php:719
protected function _deltaRound($price, $rate, $direction, $type = 'regular')
{
if ($price) {
$rate = (string)$rate;
$type = $type . $direction;
// initialize the delta to a small number to avoid non-deterministic behavior with rounding of 0.5
$delta = isset($this->_roundingDeltas[$type][$rate]) ? $this->_roundingDeltas[$type][$rate] : 0.000001;
$price += $delta;
$this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
$price = $this->_calculator->round($price);
}
return $price;
}
Sometimes this can cause an error due to the high delta calculation error ($this->_calculator->round($price)). For example, for this reason, some prices can vary in the range of ±1 cent.
Solution
To avoid this, you need to improve the accuracy of the delta calculation.
Change
$this->_roundingDeltas[$type][$rate] = $price - $this->_calculator->round($price);
to
$this->_roundingDeltas[$type][$rate] = $price - round($price, 4);
Changes need to be made in both files:
app/code/core/Mage/Tax/Model/Sales/Total/Quote/Tax.php:1392
app/code/core/Mage/Tax/Model/Sales/Total/Quote/Subtotal.php:719
Don't modify or hack core files! Do a rewrite!
The solution was tested on different versions of Magento 1.9.x, but maybe this will work in earlier versions.
P.S.
Change roundPrice function, as shown below, can solve the rounding error problem, but it can cause others (for example, some platforms require rounding up to 2 decimal places).
app/code/core/Mage/Core/Model/Store.php:995
public function roundPrice($price)
{
return round($price, 4);
}

Replace Price Difference with Actual Price

I have a configurable product with 3 options, see below:
I want to replace the +£24.00 and the +£75.00 with the actual prices of the products.
So instead it would say: £69.00 and £120.00
I have located the code being in js/varien/product.js
I've spent a bit of time chopping and changing the code, but can't quite decipher what needs to change. Line 240 downwards in this file handles the JavaScript events for configurable products.
This is performed by javascript. You need to modify the method getOptionLabel in js/varien/configurable.js (this is Magento 1.5.1.0, your milage may vary depending on the version you're using).
This method receives the option and the price difference to be applied. If you want to just show the absolute price of the different options, you need to calculate them yourself, using the absolute base value of the configurable product and the absolute difference of the option.
The first few lines of the method look like this:
getOptionLabel: function(option, price){
var price = parseFloat(price);
Change that so you get the absolute base price and the absolute difference of the option. Then add them together to get the final absolute price of the option. Like this:
getOptionLabel: function(option, price){
var basePrice = parseFloat(this.config.basePrice);
// 'price' as passed is the RELATIVE DIFFERENCE. We won't use it.
// The ABSOLUTE DIFFERENCE is in option.price (and option.oldPrice)
var absoluteDifference = parseFloat(option.price);
var absoluteFinalPrice = basePrice + absoluteDifference;
// var price = parseFloat(price);
var price = absoluteFinalPrice;
Then you need to get rid of the + and - symbols in the options. Later on in the same method, when you call this.formatPrice, just change the second paramter to false in each call.
if(price){
if (this.taxConfig.showBothPrices) {
str+= ' ' + this.formatPrice(excl, false) + ' (' + this.formatPrice(price, false) + ' ' + this.taxConfig.inclTaxTitle + ')';
} else {
str+= ' ' + this.formatPrice(price, false);
}
Some more notes about this:
You will find another identical object called Product.Config being created in js/varien/product.js. As far as I can tell, this does absolutely nothing as it is replaced by js/varien/configurable.js.
Also, if just change js/varien/configurable.js, the next time you upgrade Magento you will probably lose your changes. Better to create another file like js/varien/my_configurable.js or whatever, and call it in the config file (product.xml) for whatever theme you are using.
This extension might be helpful, I've used it in the past (and it appears to be maintained):
http://www.magentocommerce.com/magento-connect/Matt+Dean/extension/596/simple-configurable-products
In magento 1.9 the .js method doesn't seem to work anymore.
Instead I updated Abstract.php (/app/code/core/Mage/Catalog/Block/Product/View/Options/Abstract.php), copy this file to /app/code/local/Mage/Catalog/Block/Product/View/Options/Abstract.php. On line 128, change the $_priceInclTax and $_priceExclTax variables to the following:
$_priceInclTax = $this->getPrice($sign.$value['pricing_value'], true)+$this->getProduct()->getFinalPrice();
$_priceExclTax = $this->getPrice($sign.$value['pricing_value'])+$this->getProduct()->getFinalPrice();
I've seen similar solutions, but this is the only way to ensure it also works with things such as negative product options and special price discounts.
Found a solution here that works on Magento 1.9 but it overrides core code, it should be done so that it does not override core.
I've tried something like this in a new js file, but somehow the core configurable.js get's messed up, maybe someone can come up with a way so that id doesn't.
Product.Config.prototype.getOptionLabel = Product.Config.prototype.getOptionLabel.wrap(function(parentMethod){
// BEGIN:: custom price display update
var basePrice = parseFloat(this.config.basePrice);
// 'price' as passed is the RELATIVE DIFFERENCE. We won't use it.
// The ABSOLUTE DIFFERENCE is in option.price (and option.oldPrice)
var absoluteDifference = parseFloat(option.price);
// var price = parseFloat(price);
if(absoluteDifference){
// console.log(option);
price = basePrice + absoluteDifference;
} else {
price = absoluteDifference;
}
// END:: custom price display update
if (this.taxConfig.includeTax) {
var tax = price / (100 + this.taxConfig.defaultTax) * this.taxConfig.defaultTax;
var excl = price - tax;
var incl = excl*(1+(this.taxConfig.currentTax/100));
} else {
var tax = price * (this.taxConfig.currentTax / 100);
var excl = price;
var incl = excl + tax;
}
if (this.taxConfig.showIncludeTax || this.taxConfig.showBothPrices) {
price = incl;
} else {
price = excl;
}
var str = option.label;
if(price){
if (this.taxConfig.showBothPrices) {
// BEGIN:: custom price display update
// NOTE:: 'true' was changed to 'false' in 3 places.
str+= ' ' + this.formatPrice(excl, false) + ' (' + this.formatPrice(price, false) + ' ' + this.taxConfig.inclTaxTitle + ')';
} else {
str+= ' ' + this.formatPrice(price, false);
// END:: custom price display update
}
}
return str;
});
Edit the file js/varien/product.js, looking at line number 691
If we were to change the attribute prices from the product detail page, the trigger will fire here. Just check with alert(price);, and you can get the changeable price.
In 1.7 (not sure when this was changed) things were changed around. it ends up that the price string is built in PHP, inside Mage/Catalog/Block/Product/View/Options/Abstract.php in the _formatPrice function when called from Mage/Catalog/Block/Product/View/Options/Type/Select.php
Notice that changing any of those 2 classes is going to provoke a change through the site and not just in that specific combo
I am suprised at how Magento can use by default such a weird logic.
The ability to see different prices per variant should be available, and perhaps even the default one.
Prestashop does this, and even automatically switches images when selecting each variant!
in Magento 1.9
open js/varien/configurable.js
goto function getOptionLabel
modify this function code as required.

Resources