Magento: showing prices ex/inc TAX/VAT depending on customer group - magento

Previously I have implemented a 'trade only' store front that shows both in and ex-VAT prices for trade customers. They still get billed the VAT, it is just the catalog shows ex-VAT prices as well as the regular inc VAT prices.
I would like to be able to implement the same functionality without creating a new store front, i.e. if someone is in the 'trade' customer group, they get prices shown inc and ex vat. They are still on the same tax rate as everyone else, so I am not looking to have a 0% tax group, what I want is just to be able to switch the prices based on their group. This also includes the labels, so there isn't just a price but a clear indication of inc/ex VAT/TAX.
It took me a while to Google this with 'tax' instead of 'VAT', however, to date I haven't found many clues as to where to start. If there is a reason why this cannot be done easily then I would like to know. Failing that, if there is a frontend hack to try, e.g. some conditional prototype to un-css-hide the prices/labels then that will have to be the route to go.
EDIT
Inspired by Clockworkgeek I did this, not yet the perfect module solution, but something that works for me for now:
Cloned core file to app/code/local/Mage/Tax/Model/Config.php and updated the getPriceDisplayType function:
public function getPriceDisplayType($store = null)
{ $customer = Mage::helper('customer')->getCustomer();
if ($customer->getGroupId() > 1) {
return self::DISPLAY_TYPE_BOTH;
} else {
return (int)Mage::getStoreConfig(self::CONFIG_XML_PATH_PRICE_DISPLAY_TYPE, $store);
}
}
This relies on 0 being not logged in, 1 being normal customers and any 'special' group higher than that. I did not think it was working at first, but then I logged in...

There is a config setting for whether to include or exclude tax but it is not customer-specific. I believe the most direct way would be to override the point where this is read. The following is pseudocode...
class Your_Module_Model_Config extends Mage_Tax_Model_Config
{
public function getPriceDisplayType($store = null)
{
$customer = Mage::helper('customer')->getCustomer();
if ($customer->getGroupId() == 'TRADE') {
return self::DISPLAY_TYPE_BOTH;
}
return parent::getPriceDisplayType($store);
}
}

You can try this to show prices without tax only for specified customer groups:
http://www.magentocommerce.com/magento-connect/catalog/product/view/id/18364/

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.

How to show all products in nopCommerce?

I would like to show all products without creating any new category and mapping to it.
Can any one help me?
Thanks in advance.
It's true; in order for a product to be displayed in nopCommerce, it must be assigned to a category. Your best bet is to create a top-level umbrella category, like "All Products", and add all of your products to that umbrella category.
As far as I know their must be a category associated with product.
You can create a plugin, map a route to it (for example map to 'allproducts' route), and create your own Controllers, Actions and Views within the plugin. Then insert in the main menu a link to the route by mean of
#Html.RouteLink(routeName, null) //or similar overloads
The plugin creation part is too huge to be described here. http://www.nopcommerce.com/documentation.aspx is a good start.
PS:/ Regarding routing, each plugin can implement a route registrar by implementing the "IRouteProvider" interface.
:)
You can do that by modifying the code. I have done it before. It is actually quite simple.
Modify the Category action of the Catalogue controller to receive a nullable CategoryId:
public ActionResult Category(int? categoryId, CatalogPagingFilteringModel command){
modify the action to not break because of this nullable paramters.
The most important part to modify is where you build the list of category Ids to filter:
var categoryIds = new List<int>();
if (category != null)
{
categoryIds.Add(category.Id);
}
if (_catalogSettings.ShowProductsFromSubcategories)
{
//include subcategories
categoryIds.AddRange(GetChildCategoryIds(category.Id));
}
The mothod _productService.SearchProducts will receive an empty list of category Ids and will not filter any products.

Magento Shipping Module: Get all items currently in cart

I'm in the middle of developing a shipping module for Magento but get stuck on How to get the items that's currently in the cart for that session.
I follow some tutorial on the internet, they use:
if ($request->getAllItems()) {
foreach ($request->getAllItems() as $item) {
//do something here.....
}
}
My problem is that I don't know exactly what info/data that's on the $item variable??.
I want to get the weight and the price of the product that's currently on the cart to calculate the shipping fee. I tried to print the $item value by using Mage::log or printing it to the screen using print_r or var_dump but it's not successful. The log is empty and the variable won't be printed on screen.
Can somebody inform me of how to get the $item attributes/method or is there any other way to get the product information that's currently in cart?
you can achive this by using one of three methods which are available in Mage::getSingleton('checkout/session')->getQuote();
getItemsCollection() - retrive sales/quote_items collection
getAllItems() - retrive all items
getAllVisibleItems() - retrive items which aren't deleted and have parent_item_id != null
I was searching for this solution and found below. but not tested.
$CartSession = Mage::getSingleton('checkout/session');
foreach($CartSession->getQuote()->getAllItems() as $item)
{
$productWeight = $item->getWeight();
$productExPrice = $item->getPrice(); // price excluding tax
$productIncPrice = $item->getPriceInclTax(); // price excluding tax
}
If you want to know information about $item, you can do this:
print_r($item->getData());
If you want to get item weight, here is it:
$item->getWeight();

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

Magento Collection Catch-22

I don't really understand how Magento collections work, so hopefully this is a simple question...
As the old adage goes, you can't observe an experiment without somehow altering it. This seems to hold true for Magento collections. I have a Featured Products module that I've written that works quite well. We have recently added Customer Reviews to our store. When viewing a category page it shows a random review of products in that category. This also works great. I added the review block to my Featured Products page, which was easy to do, but since those products aren't in a specific category, it just pulls a random, usually unrelated, review. To fix this, I modified my getProductCollection function in my Featured module and added the following to the end, after the collection has been created/saved:
$_product = $this->_productCollection->getFirstItem();
$_catIDs = $_product->getCategoryIds();
if(count($_catIDs) > 0)
{
$_cat = Mage::getModel('catalog/category')->load($_catIDs[0]);
Mage::register('current_category', $_cat);
}
Unfortunately, the mere act of looking at the first item in the collection breaks the pager in the toolbar. No matter which of the paging options I choose, it always shows all items when the above code is in place. If I comment out that section it works fine.
So my question is this: How can I get any information about the products in a collection without somehow changing the collection or breaking the paging?
Adding my code to help explain the problem more:
class VPS_Featured_Block_List extends Amasty_Sorting_Block_Catalog_Product_List//Mage_Catalog_Block_Product_List
{
protected function _getProductCollection()
{
if (is_null($this->_productCollection))
{
$_attributeNames = 'featured';
if($this->getAttributeName() != '')
$_attributeNames = $this->getAttributeName();
$_attrArray = explode(',', $_attributeNames);
$this->_productCollection = Mage::getModel('catalog/product')->getCollection();
$this->_productCollection->addAttributeToSelect('*');
$_filters = array();
foreach($_attrArray as $_attr)
$_filters[] = array('attribute' => $_attr, 'eq' => true);
$this->_productCollection->addFieldToFilter($_filters);
//$this->_productCollection->addFieldToFilter(array(array('attribute' => $_attr, 'eq' => true),));
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($this->_productCollection);
Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($this->_productCollection);
Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($this->_productCollection);
//Get category of first product in collection (used for featured review)
$_catIDs = array();
$_product = $this->_productCollection->getFirstItem();
$_catIDs = $_product->getCategoryIds();
if(count($_catIDs) > 0)
{
$_cat = Mage::getModel('catalog/category')->load($_catIDs[0]);
Mage::register('current_category', $_cat);
}
}
return $this->_productCollection;
}
}
Thanks to #clockworkgeek for this answer he posted in my followup question:
The reason is because when you call getFirstItem() (or just about any other retrieval method) on a collection that collection is loaded. Any subsequent operation ignores the database and uses only the loaded data, filters have no effect because they are SQL only, ditto for pagination and selected columns. The workaround is to use a second collection based on the first.
$secondCollection = clone $firstCollection;
$secondCollection->clear();
$_foo123 = $secondCollection->getFirstItem();
The clear() method unloads the data for that collection, forcing it to access the database again next time
Hard to say what's going on without more context (magento version, which class you're in, etc.) BUT, what I think (60% confidence) it happening is you're getting a reference to the product collection before its filters have been added. Magento collections lazy load, which means database queries aren't run until you explicitly call load or you attempt to access an item. My guess (again, a guess) is that when you access the item above, the collection loads (with no filter). Then, other parts of the system add the filter but they're ignored because the collection is already loaded.
Answering your larger question is impossible without more context.

Resources