It seems Magento left out a swath of code that should be calculating the VAT at that point. I think it was left out because the logic wasn't really straightforward.
The problem is in the structure of the bundle; it has minimum and maximum prices, which also might contain different tax rates. A bundle itself doesn't have a singular tax rate set, since the products contained in the bundle can contain different tax rates.
So we need to look inside the bundle, find the corresponding prices for the individual items, calculate minimum and maximum prices and give these back to the Bundle's price.phtml. Luckily for you, I did just that.
We need to edit two files. Because the code for the taxated bundle products wasn't built in, there was no need to show including/excluding VAT prices in price.phtml since it was always excluding VAT (as you've noticed).
We need to remedy this first to at least be able to show the including VAT price as soon as we have it.
So, replace line 263 of app/design/frontend/base/default/template/bundle/catalog/product/price.phtml, which now reads;
<?php echo $_coreHelper->currency($_minimalPriceTax) ?>
with:
<?php if ($_taxHelper->displayPriceIncludingTax()) : ?>
<?php echo $_coreHelper->currency($_minimalPriceInclTax) ?>
<?php else: ?>
<?php echo $_coreHelper->currency($_minimalPriceTax) ?>
<?php endif; ?>
Of course, adhere to Magento best practices by creating a copy of this file within your own template.
Now on to step 2; changing the code to actually retrieve the prices including tax.
The class
Mage_Bundle_Model_Product_Price
contains a function called getTotalPrices(). This function retrieves the minimum and maximum prices of a tiered/grouped/bundled product. But it doesn't retrieve the prices including tax for bundled product (which is the premise of your question).
So we are going to replace the lines 196 to 201. They now read;
if ($this->_isPricesCalculatedByIndex) {
$minimalPrice = $taxHelper->getPrice($product, $product->getData('min_price'), $includeTax,
null, null, null, null, null, false);
$maximalPrice = $taxHelper->getPrice($product, $product->getData('max_price'), $includeTax,
null, null, null, null, null, false);
} else {
Replace those lines with this code;
if ($this->_isPricesCalculatedByIndex) {
/* Add tax to Bundle product prices because the taxHelper->getPrice() function always returns without tax */
if($includeTax && $product->getTypeInstance() instanceof Mage_Bundle_Model_Product_Type) {
$maximalPrice = $minimalPrice = 0;
$bundleOptions = Mage::getResourceSingleton('bundle/selection')->getChildrenIds($product->getId(), $required=true);
$childIds = array();
foreach($bundleOptions as $option) {
foreach ($option as $childProductId) {
$childIds[] = $childProductId;
}
}
$childProductObjects = Mage::getResourceModel('catalog/product_collection')
->addIdFilter($childIds)
->addAttributeToSelect(array('price','tax_class_id'));
$childPrices = array();
foreach($childProductObjects as $childProductObject) {
$childPrices[$childProductObject->getId()] = Mage::helper('tax')->getPrice($childProductObject, $childProductObject->getFinalPrice());
}
foreach($bundleOptions as $option) {
$optionPrices = array();
foreach($option as $childProductId) {
$optionPrices[] = $childPrices[$childProductId];
}
$_minimalOptionPrice = min($optionPrices);
$_maximalOptionPrice = max($optionPrices);
$minimalPrice += $_minimalOptionPrice;
$maximalPrice += $_maximalOptionPrice;
}
} else {
$minimalPrice = $taxHelper->getPrice($product, $product->getData('min_price'), $includeTax,
null, null, null, null, null, false);
$maximalPrice = $taxHelper->getPrice($product, $product->getData('max_price'), $includeTax,
null, null, null, null, null, false);
}
} else {
Of course, you should adhere to Magento best practices and rewrite Mage_Bundle_Model_Product_Price by creating your own custom module that extends this class with the new functionality.
That should be it!