Created
April 23, 2014 01:12
-
-
Save gistya/11199766 to your computer and use it in GitHub Desktop.
BundlePricesPatchFileChanges.diff: add local overrides for files to fix the bulde price method. still haven't made any changes *to* these files yet though
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
From 72bbed942cd0d3ab8e70233f6b986ee4c517fb9a Mon Sep 17 00:00:00 2001 | |
From: Gistya Eusebio <gistya@gmail.com> | |
Date: Tue, 22 Apr 2014 18:07:32 -0700 | |
Subject: [PATCH] add local overrides for files to fix the bulde price method. | |
still haven't made any changes *to* these files yet though. | |
--- | |
.../Product/Edit/Tab/Attributes/Special.php | 43 + | |
app/code/local/Mage/Bundle/Model/Product/Price.php | 975 +++++++++++++++++++++ | |
.../Mage/Bundle/Model/Resource/Indexer/Price.php | 698 +++++++++++++++ | |
.../Mage/Bundle/Model/Resource/Price/Index.php | 856 ++++++++++++++++++ | |
4 files changed, 2572 insertions(+) | |
create mode 100644 app/code/local/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes/Special.php | |
create mode 100644 app/code/local/Mage/Bundle/Model/Product/Price.php | |
create mode 100644 app/code/local/Mage/Bundle/Model/Resource/Indexer/Price.php | |
create mode 100644 app/code/local/Mage/Bundle/Model/Resource/Price/Index.php | |
diff --git a/app/code/local/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes/Special.php b/app/code/local/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes/Special.php | |
new file mode 100644 | |
index 0000000..3d6ff4f | |
--- /dev/null | |
+++ b/app/code/local/Mage/Bundle/Block/Adminhtml/Catalog/Product/Edit/Tab/Attributes/Special.php | |
@@ -0,0 +1,43 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to license@magentocommerce.com so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magentocommerce.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+/** | |
+ * Bundle Special Price Attribute Block | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @author Magento Core Team <core@magentocommerce.com> | |
+ */ | |
+class Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Attributes_Special extends Mage_Adminhtml_Block_Catalog_Form_Renderer_Fieldset_Element | |
+{ | |
+ public function getElementHtml() | |
+ { | |
+ $html = '<input id="'.$this->getElement()->getHtmlId().'" name="'.$this->getElement()->getName() | |
+ .'" value="'.$this->getElement()->getEscapedValue().'" '.$this->getElement()->serialize($this->getElement()->getHtmlAttributes()).'/>'."\n" | |
+ .'<strong>[%]</strong>';; | |
+ return $html; | |
+ } | |
+} | |
diff --git a/app/code/local/Mage/Bundle/Model/Product/Price.php b/app/code/local/Mage/Bundle/Model/Product/Price.php | |
new file mode 100644 | |
index 0000000..b69b184 | |
--- /dev/null | |
+++ b/app/code/local/Mage/Bundle/Model/Product/Price.php | |
@@ -0,0 +1,975 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to license@magentocommerce.com so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magentocommerce.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+/** | |
+ * Bundle Price Model | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @author Magento Core Team <core@magentocommerce.com> | |
+ */ | |
+class Mage_Bundle_Model_Product_Price extends Mage_Catalog_Model_Product_Type_Price | |
+{ | |
+ /** | |
+ * Fixed price type | |
+ */ | |
+ const PRICE_TYPE_FIXED = 1; | |
+ | |
+ /** | |
+ * Dynamic price type | |
+ */ | |
+ const PRICE_TYPE_DYNAMIC = 0; | |
+ | |
+ /** | |
+ * Flag which indicates - is min/max prices have been calculated by index | |
+ * | |
+ * @var bool | |
+ */ | |
+ protected $_isPricesCalculatedByIndex; | |
+ | |
+ /** | |
+ * Is min/max prices have been calculated by index | |
+ * | |
+ * @return bool | |
+ */ | |
+ public function getIsPricesCalculatedByIndex() | |
+ { | |
+ return $this->_isPricesCalculatedByIndex; | |
+ } | |
+ | |
+ /** | |
+ * Return product base price | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return string | |
+ */ | |
+ public function getPrice($product) | |
+ { | |
+ if ($product->getPriceType() == self::PRICE_TYPE_FIXED) { | |
+ return $product->getData('price'); | |
+ } else { | |
+ return 0; | |
+ } | |
+ } | |
+ | |
+ /** | |
+ * Get Total price for Bundle items | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param null|float $qty | |
+ * @return float | |
+ */ | |
+ public function getTotalBundleItemsPrice($product, $qty = null) | |
+ { | |
+ $price = 0.0; | |
+ if ($product->hasCustomOptions()) { | |
+ $customOption = $product->getCustomOption('bundle_selection_ids'); | |
+ if ($customOption) { | |
+ $selectionIds = unserialize($customOption->getValue()); | |
+ $selections = $product->getTypeInstance(true)->getSelectionsByIds($selectionIds, $product); | |
+ $selections->addTierPriceData(); | |
+ Mage::dispatchEvent('prepare_catalog_product_collection_prices', array( | |
+ 'collection' => $selections, | |
+ 'store_id' => $product->getStoreId(), | |
+ )); | |
+ foreach ($selections->getItems() as $selection) { | |
+ if ($selection->isSalable()) { | |
+ $selectionQty = $product->getCustomOption('selection_qty_' . $selection->getSelectionId()); | |
+ if ($selectionQty) { | |
+ $price += $this->getSelectionFinalTotalPrice($product, $selection, $qty, | |
+ $selectionQty->getValue()); | |
+ } | |
+ } | |
+ } | |
+ } | |
+ } | |
+ return $price; | |
+ } | |
+ | |
+ /** | |
+ * Get product final price | |
+ * | |
+ * @param double $qty | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return double | |
+ */ | |
+ public function getFinalPrice($qty = null, $product) | |
+ { | |
+ if (is_null($qty) && !is_null($product->getCalculatedFinalPrice())) { | |
+ return $product->getCalculatedFinalPrice(); | |
+ } | |
+ | |
+ $finalPrice = $this->getBasePrice($product, $qty); | |
+ $product->setFinalPrice($finalPrice); | |
+ Mage::dispatchEvent('catalog_product_get_final_price', array('product' => $product, 'qty' => $qty)); | |
+ $finalPrice = $product->getData('final_price'); | |
+ | |
+ $finalPrice = $this->_applyOptionsPrice($product, $qty, $finalPrice); | |
+ $finalPrice += $this->getTotalBundleItemsPrice($product, $qty); | |
+ | |
+ $product->setFinalPrice($finalPrice); | |
+ return max(0, $product->getData('final_price')); | |
+ } | |
+ | |
+ /** | |
+ * Returns final price of a child product | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param float $productQty | |
+ * @param Mage_Catalog_Model_Product $childProduct | |
+ * @param float $childProductQty | |
+ * @return decimal | |
+ */ | |
+ public function getChildFinalPrice($product, $productQty, $childProduct, $childProductQty) | |
+ { | |
+ return $this->getSelectionFinalTotalPrice($product, $childProduct, $productQty, $childProductQty, false); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Price | |
+ * | |
+ * @deprecated after 1.5.1.0 | |
+ * @see Mage_Bundle_Model_Product_Price::getTotalPrices() | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param string $which | |
+ * @return decimal|array | |
+ */ | |
+ public function getPrices($product, $which = null) | |
+ { | |
+ return $this->getTotalPrices($product, $which); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Prices depending on tax | |
+ * | |
+ * @deprecated after 1.5.1.0 | |
+ * @see Mage_Bundle_Model_Product_Price::getTotalPrices() | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param string $which | |
+ * @param bool|null $includeTax | |
+ * @return decimal|array | |
+ */ | |
+ public function getPricesDependingOnTax($product, $which = null, $includeTax = null) | |
+ { | |
+ return $this->getTotalPrices($product, $which, $includeTax); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Price considering tier price | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param string|null $which | |
+ * @param bool|null $includeTax | |
+ * @param bool $takeTierPrice | |
+ * @return decimal|array | |
+ */ | |
+ public function getTotalPrices($product, $which = null, $includeTax = null, $takeTierPrice = true) | |
+ { | |
+ $isPriceFixedType = ($product->getPriceType() == self::PRICE_TYPE_FIXED); | |
+ $this->_isPricesCalculatedByIndex = ($product->getData('min_price') && $product->getData('max_price')); | |
+ $taxHelper = $this->_getHelperData('tax'); | |
+ | |
+ if ($this->_isPricesCalculatedByIndex && !$includeTax) { | |
+ $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 { | |
+ /** | |
+ * Check if product price is fixed | |
+ */ | |
+ $finalPrice = $product->getFinalPrice(); | |
+ if ($isPriceFixedType) { | |
+ $minimalPrice = $maximalPrice = $taxHelper->getPrice($product, $finalPrice, $includeTax, | |
+ null, null, null, null, null, false); | |
+ } else { // PRICE_TYPE_DYNAMIC | |
+ $minimalPrice = $maximalPrice = 0; | |
+ } | |
+ | |
+ $minimalPrice += $this->_getMinimalBundleOptionsPrice($product, $includeTax, $takeTierPrice); | |
+ $maximalPrice += $this->_getMaximalBundleOptionsPrice($product, $includeTax, $takeTierPrice); | |
+ | |
+ $customOptions = $product->getOptions(); | |
+ if ($isPriceFixedType && $customOptions) { | |
+ foreach ($customOptions as $customOption) { | |
+ /* @var $customOption Mage_Catalog_Model_Product_Option */ | |
+ $minimalPrice += $taxHelper->getPrice( | |
+ $product, | |
+ $this->_getMinimalCustomOptionPrice($customOption), | |
+ $includeTax); | |
+ $maximalPrice += $taxHelper->getPrice( | |
+ $product, | |
+ $this->_getMaximalCustomOptionPrice($customOption), | |
+ $includeTax); | |
+ } | |
+ } | |
+ } | |
+ | |
+ $minimalPrice = $product->getStore()->roundPrice($minimalPrice); | |
+ $maximalPrice = $product->getStore()->roundPrice($maximalPrice); | |
+ | |
+ if ('max' == $which) { | |
+ return $maximalPrice; | |
+ } elseif ('min' == $which) { | |
+ return $minimalPrice; | |
+ } | |
+ | |
+ return array($minimalPrice, $maximalPrice); | |
+ } | |
+ | |
+ /** | |
+ * Get minimal possible price for bundle option | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param bool|null $includeTax | |
+ * @param bool $takeTierPrice | |
+ * @return int|mixed | |
+ */ | |
+ protected function _getMinimalBundleOptionsPrice($product, $includeTax, $takeTierPrice) | |
+ { | |
+ $options = $this->getOptions($product); | |
+ $minimalPrice = 0; | |
+ $minimalPriceWithTax = 0; | |
+ $hasRequiredOptions = $this->_hasRequiredOptions($product); | |
+ $selectionMinimalPrices = array(); | |
+ $selectionMinimalPricesWithTax = array(); | |
+ | |
+ if (!$options) { | |
+ return $minimalPrice; | |
+ } | |
+ | |
+ foreach ($options as $option) { | |
+ /* @var $option Mage_Bundle_Model_Option */ | |
+ $selectionPrices = $this->_getSelectionPrices($product, $option, $takeTierPrice, $includeTax); | |
+ $selectionPricesWithTax = $this->_getSelectionPrices($product, $option, $takeTierPrice, true); | |
+ | |
+ if (count($selectionPrices)) { | |
+ $selectionMinPrice = is_array($selectionPrices) ? min($selectionPrices) : $selectionPrices; | |
+ $selectMinPriceWithTax = is_array($selectionPricesWithTax) ? | |
+ min($selectionPricesWithTax) : $selectionPricesWithTax; | |
+ if ($option->getRequired()) { | |
+ $minimalPrice += $selectionMinPrice; | |
+ $minimalPriceWithTax += $selectMinPriceWithTax; | |
+ } elseif (!$hasRequiredOptions) { | |
+ $selectionMinimalPrices[] = $selectionMinPrice; | |
+ $selectionMinimalPricesWithTax[] = $selectMinPriceWithTax; | |
+ } | |
+ } | |
+ } | |
+ // condition is TRUE when all product options are NOT required | |
+ if (!$hasRequiredOptions) { | |
+ $minimalPrice = min($selectionMinimalPrices); | |
+ $minimalPriceWithTax = min($selectionMinimalPricesWithTax); | |
+ } | |
+ | |
+ $taxConfig = $this->_getHelperData('tax')->getConfig(); | |
+ | |
+ //In the case of total base calculation we round the tax first and | |
+ //deduct the tax from the price including tax | |
+ if ($taxConfig->priceIncludesTax($product->getStore()) | |
+ && Mage_Tax_Model_Calculation::CALC_TOTAL_BASE == | |
+ $taxConfig->getAlgorithm($product->getStore()) | |
+ && ($minimalPriceWithTax > $minimalPrice) | |
+ ) { | |
+ //We convert the value to string to maintain the precision | |
+ $tax = (String)($minimalPriceWithTax - $minimalPrice); | |
+ $roundedTax = $this->_getApp()->getStore()->roundPrice($tax); | |
+ $minimalPrice = $minimalPriceWithTax - $roundedTax; | |
+ } | |
+ return $minimalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Get maximal possible price for bundle option | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param bool|null $includeTax | |
+ * @param bool $takeTierPrice | |
+ * @return float | |
+ */ | |
+ protected function _getMaximalBundleOptionsPrice($product, $includeTax, $takeTierPrice) | |
+ { | |
+ $maximalPrice = 0; | |
+ $options = $this->getOptions($product); | |
+ | |
+ if (!$options) { | |
+ return $maximalPrice; | |
+ } | |
+ | |
+ foreach ($options as $option) { | |
+ $selectionPrices = $this->_getSelectionPrices($product, $option, $takeTierPrice, $includeTax); | |
+ if (count($selectionPrices)) { | |
+ $maximalPrice += ($option->isMultiSelection()) | |
+ ? array_sum($selectionPrices) | |
+ : max($selectionPrices); | |
+ } | |
+ } | |
+ return $maximalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Get all prices for bundle option selection | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param Mage_Bundle_Model_Option $option | |
+ * @param bool $takeTierPrice | |
+ * @param bool|null $includeTax | |
+ * @return array | |
+ */ | |
+ protected function _getSelectionPrices($product, $option, $takeTierPrice, $includeTax) | |
+ { | |
+ $selectionPrices = array(); | |
+ $taxHelper = $this->_getHelperData('tax'); | |
+ $taxCalcMethod = $taxHelper->getConfig()->getAlgorithm($product->getStore()); | |
+ $isPriceFixedType = ($product->getPriceType() == self::PRICE_TYPE_FIXED); | |
+ | |
+ $selections = $option->getSelections(); | |
+ if (!$selections) { | |
+ return $selectionPrices; | |
+ } | |
+ | |
+ foreach ($selections as $selection) { | |
+ /* @var $selection Mage_Bundle_Model_Selection */ | |
+ if (!$selection->isSalable()) { | |
+ /** | |
+ * @todo CatalogInventory Show out of stock Products | |
+ */ | |
+ continue; | |
+ } | |
+ | |
+ $item = $isPriceFixedType ? $product : $selection; | |
+ | |
+ $selectionUnitPrice = $this->getSelectionFinalTotalPrice( | |
+ $product, $selection, 1, null, false, $takeTierPrice); | |
+ $selectionQty = $selection->getSelectionQty(); | |
+ if ($isPriceFixedType || $taxCalcMethod == Mage_Tax_Model_Calculation::CALC_TOTAL_BASE) { | |
+ $selectionPrice = $selectionQty * $taxHelper->getPrice($item, $selectionUnitPrice, $includeTax, | |
+ null, null, null, null, null, false); | |
+ $selectionPrices[] = $selectionPrice; | |
+ } else if ($taxCalcMethod == Mage_Tax_Model_Calculation::CALC_ROW_BASE) { | |
+ $selectionPrice = $taxHelper->getPrice($item, $selectionUnitPrice * $selectionQty, $includeTax); | |
+ $selectionPrices[] = $selectionPrice; | |
+ } else { //dynamic price and Mage_Tax_Model_Calculation::CALC_UNIT_BASE | |
+ $selectionPrice = $taxHelper->getPrice($item, $selectionUnitPrice, $includeTax) * $selectionQty; | |
+ $selectionPrices[] = $selectionPrice; | |
+ } | |
+ } | |
+ return $selectionPrices; | |
+ } | |
+ | |
+ /** | |
+ * Calculate Minimal price of bundle (counting all required options) | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return decimal | |
+ */ | |
+ public function getMinimalPrice($product) | |
+ { | |
+ return $this->getPricesTierPrice($product, 'min'); | |
+ } | |
+ | |
+ /** | |
+ * Calculate maximal price of bundle | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return decimal | |
+ */ | |
+ public function getMaximalPrice($product) | |
+ { | |
+ return $this->getPricesTierPrice($product, 'max'); | |
+ } | |
+ | |
+ /** | |
+ * Get Options with attached Selections collection | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return Mage_Bundle_Model_Mysql4_Option_Collection | |
+ */ | |
+ public function getOptions($product) | |
+ { | |
+ $product->getTypeInstance(true) | |
+ ->setStoreFilter($product->getStoreId(), $product); | |
+ | |
+ $optionCollection = $product->getTypeInstance(true) | |
+ ->getOptionsCollection($product); | |
+ | |
+ $selectionCollection = $product->getTypeInstance(true) | |
+ ->getSelectionsCollection( | |
+ $product->getTypeInstance(true)->getOptionsIds($product), | |
+ $product | |
+ ); | |
+ | |
+ return $optionCollection->appendSelections($selectionCollection, false, false); | |
+ } | |
+ | |
+ /** | |
+ * Calculate price of selection | |
+ * | |
+ * @deprecated after 1.6.2.0 | |
+ * @see Mage_Bundle_Model_Product_Price::getSelectionFinalTotalPrice() | |
+ * | |
+ * @param Mage_Catalog_Model_Product $bundleProduct | |
+ * @param Mage_Catalog_Model_Product $selectionProduct | |
+ * @param float|null $selectionQty | |
+ * @param null|bool $multiplyQty Whether to multiply selection's price by its quantity | |
+ * @return float | |
+ */ | |
+ public function getSelectionPrice($bundleProduct, $selectionProduct, $selectionQty = null, $multiplyQty = true) | |
+ { | |
+ return $this->getSelectionFinalTotalPrice($bundleProduct, $selectionProduct, 0, $selectionQty, $multiplyQty); | |
+ } | |
+ | |
+ /** | |
+ * Calculate selection price for front view (with applied special of bundle) | |
+ * | |
+ * @param Mage_Catalog_Model_Product $bundleProduct | |
+ * @param Mage_Catalog_Model_Product $selectionProduct | |
+ * @param decimal $qty | |
+ * @return decimal | |
+ */ | |
+ public function getSelectionPreFinalPrice($bundleProduct, $selectionProduct, $qty = null) | |
+ { | |
+ return $this->getSelectionPrice($bundleProduct, $selectionProduct, $qty); | |
+ } | |
+ | |
+ /** | |
+ * Calculate final price of selection | |
+ * | |
+ * @deprecated after 1.5.1.0 | |
+ * @see Mage_Bundle_Model_Product_Price::getSelectionFinalTotalPrice() | |
+ * | |
+ * @param Mage_Catalog_Model_Product $bundleProduct | |
+ * @param Mage_Catalog_Model_Product $selectionProduct | |
+ * @param decimal $bundleQty | |
+ * @param decimal $selectionQty | |
+ * @param bool $multiplyQty | |
+ * @return decimal | |
+ */ | |
+ public function getSelectionFinalPrice($bundleProduct, $selectionProduct, $bundleQty, $selectionQty = null, | |
+ $multiplyQty = true) | |
+ { | |
+ return $this->getSelectionFinalTotalPrice($bundleProduct, $selectionProduct, $bundleQty, $selectionQty, | |
+ $multiplyQty); | |
+ } | |
+ | |
+ /** | |
+ * Calculate final price of selection | |
+ * with take into account tier price | |
+ * | |
+ * @param Mage_Catalog_Model_Product $bundleProduct | |
+ * @param Mage_Catalog_Model_Product $selectionProduct | |
+ * @param float $bundleQty | |
+ * @param float $selectionQty | |
+ * @param bool $multiplyQty | |
+ * @param bool $takeTierPrice | |
+ * @return float | |
+ */ | |
+ public function getSelectionFinalTotalPrice($bundleProduct, $selectionProduct, $bundleQty, $selectionQty, | |
+ $multiplyQty = true, $takeTierPrice = true) | |
+ { | |
+ if (is_null($selectionQty)) { | |
+ $selectionQty = $selectionProduct->getSelectionQty(); | |
+ } | |
+ | |
+ if ($bundleProduct->getPriceType() == self::PRICE_TYPE_DYNAMIC) { | |
+ $price = $selectionProduct->getFinalPrice($takeTierPrice ? $selectionQty : 1); | |
+ } else { | |
+ if ($selectionProduct->getSelectionPriceType()) { // percent | |
+ $product = clone $bundleProduct; | |
+ $product->setFinalPrice($this->getPrice($product)); | |
+ Mage::dispatchEvent( | |
+ 'catalog_product_get_final_price', | |
+ array('product' => $product, 'qty' => $bundleQty) | |
+ ); | |
+ $price = $product->getData('final_price') * ($selectionProduct->getSelectionPriceValue() / 100); | |
+ | |
+ } else { // fixed | |
+ $price = $selectionProduct->getSelectionPriceValue(); | |
+ } | |
+ } | |
+ | |
+ $price = $this->getLowestPrice($bundleProduct, $price, $bundleQty); | |
+ | |
+ if ($multiplyQty) { | |
+ $price *= $selectionQty; | |
+ } | |
+ | |
+ return $price; | |
+ } | |
+ | |
+ /** | |
+ * Returns the lowest price after applying any applicable bundle discounts | |
+ * | |
+ * @param Mage_Catalog_Model_Product $bundleProduct | |
+ * @param float|string $price | |
+ * @param int $bundleQty | |
+ * @return float | |
+ */ | |
+ public function getLowestPrice($bundleProduct, $price, $bundleQty = 1) | |
+ { | |
+ $price *= 1; | |
+ return min($this->_getApp()->getStore()->roundPrice($price), | |
+ $this->_applyGroupPrice($bundleProduct, $price), | |
+ $this->_applyTierPrice($bundleProduct, $bundleQty, $price), | |
+ $this->_applySpecialPrice($bundleProduct, $price) | |
+ ); | |
+ } | |
+ | |
+ /** | |
+ * Apply group price for bundle product | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param float $finalPrice | |
+ * @return float | |
+ */ | |
+ protected function _applyGroupPrice($product, $finalPrice) | |
+ { | |
+ $result = $finalPrice; | |
+ $groupPrice = $product->getGroupPrice(); | |
+ | |
+ if (is_numeric($groupPrice)) { | |
+ $groupPrice = $finalPrice - ($finalPrice * ($groupPrice / 100)); | |
+ $groupPrice = $this->_getApp()->getStore()->roundPrice($groupPrice); | |
+ $result = min($finalPrice, $groupPrice); | |
+ } | |
+ | |
+ return $result; | |
+ } | |
+ | |
+ /** | |
+ * Get product group price | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return float|null | |
+ */ | |
+ public function getGroupPrice($product) | |
+ { | |
+ $groupPrices = $product->getData('group_price'); | |
+ | |
+ if (is_null($groupPrices)) { | |
+ $attribute = $product->getResource()->getAttribute('group_price'); | |
+ if ($attribute) { | |
+ $attribute->getBackend()->afterLoad($product); | |
+ $groupPrices = $product->getData('group_price'); | |
+ } | |
+ } | |
+ | |
+ if (is_null($groupPrices) || !is_array($groupPrices)) { | |
+ return null; | |
+ } | |
+ | |
+ $customerGroup = $this->_getCustomerGroupId($product); | |
+ | |
+ $matchedPrice = 0; | |
+ | |
+ foreach ($groupPrices as $groupPrice) { | |
+ if ($groupPrice['cust_group'] == $customerGroup && $groupPrice['website_price'] > $matchedPrice) { | |
+ $matchedPrice = $groupPrice['website_price']; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ return $matchedPrice; | |
+ } | |
+ | |
+ /** | |
+ * Apply tier price for bundle | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @param decimal $qty | |
+ * @param decimal $finalPrice | |
+ * @return decimal | |
+ */ | |
+ protected function _applyTierPrice($product, $qty, $finalPrice) | |
+ { | |
+ if (is_null($qty)) { | |
+ return $finalPrice; | |
+ } | |
+ | |
+ $tierPrice = $product->getTierPrice($qty); | |
+ | |
+ if (is_numeric($tierPrice)) { | |
+ $tierPrice = $finalPrice - ($finalPrice * ($tierPrice / 100)); | |
+ $tierPrice = $this->_getApp()->getStore()->roundPrice($tierPrice); | |
+ $finalPrice = min($finalPrice, $tierPrice); | |
+ } | |
+ | |
+ return $finalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Get product tier price by qty | |
+ * | |
+ * @param decimal $qty | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return decimal | |
+ */ | |
+ public function getTierPrice($qty = null, $product) | |
+ { | |
+ $allGroups = Mage_Customer_Model_Group::CUST_GROUP_ALL; | |
+ $prices = $product->getData('tier_price'); | |
+ | |
+ if (is_null($prices)) { | |
+ $attribute = $product->getResource()->getAttribute('tier_price'); | |
+ if ($attribute) { | |
+ $attribute->getBackend()->afterLoad($product); | |
+ $prices = $product->getData('tier_price'); | |
+ } | |
+ } | |
+ | |
+ if (is_null($prices) || !is_array($prices)) { | |
+ if (!is_null($qty)) { | |
+ return 0; | |
+ } | |
+ return array(array( | |
+ 'price' => 0, | |
+ 'website_price' => 0, | |
+ 'price_qty' => 1, | |
+ 'cust_group' => $allGroups | |
+ )); | |
+ } | |
+ | |
+ $custGroup = $this->_getCustomerGroupId($product); | |
+ if ($qty) { | |
+ $prevQty = 1; | |
+ $prevPrice = 0; | |
+ $prevGroup = $allGroups; | |
+ | |
+ foreach ($prices as $price) { | |
+ if ($price['cust_group'] != $custGroup && $price['cust_group'] != $allGroups) { | |
+ // tier not for current customer group nor is for all groups | |
+ continue; | |
+ } | |
+ if ($qty < $price['price_qty']) { | |
+ // tier is higher than product qty | |
+ continue; | |
+ } | |
+ if ($price['price_qty'] < $prevQty) { | |
+ // higher tier qty already found | |
+ continue; | |
+ } | |
+ if ($price['price_qty'] == $prevQty && $prevGroup != $allGroups && $price['cust_group'] == $allGroups) { | |
+ // found tier qty is same as current tier qty but current tier group is ALL_GROUPS | |
+ continue; | |
+ } | |
+ | |
+ if ($price['website_price'] > $prevPrice) { | |
+ $prevPrice = $price['website_price']; | |
+ $prevQty = $price['price_qty']; | |
+ $prevGroup = $price['cust_group']; | |
+ } | |
+ } | |
+ | |
+ return $prevPrice; | |
+ } else { | |
+ $qtyCache = array(); | |
+ foreach ($prices as $i => $price) { | |
+ if ($price['cust_group'] != $custGroup && $price['cust_group'] != $allGroups) { | |
+ unset($prices[$i]); | |
+ } else if (isset($qtyCache[$price['price_qty']])) { | |
+ $j = $qtyCache[$price['price_qty']]; | |
+ if ($prices[$j]['website_price'] < $price['website_price']) { | |
+ unset($prices[$j]); | |
+ $qtyCache[$price['price_qty']] = $i; | |
+ } else { | |
+ unset($prices[$i]); | |
+ } | |
+ } else { | |
+ $qtyCache[$price['price_qty']] = $i; | |
+ } | |
+ } | |
+ } | |
+ | |
+ return ($prices) ? $prices : array(); | |
+ } | |
+ | |
+ /** | |
+ * Calculate product price based on special price data and price rules | |
+ * | |
+ * @param float $basePrice | |
+ * @param float $specialPrice | |
+ * @param string $specialPriceFrom | |
+ * @param string $specialPriceTo | |
+ * @param float|null|false $rulePrice | |
+ * @param mixed $wId | |
+ * @param mixed $gId | |
+ * @param null|int $productId | |
+ * @return float | |
+ */ | |
+ public static function calculatePrice($basePrice, $specialPrice, $specialPriceFrom, $specialPriceTo, | |
+ $rulePrice = false, $wId = null, $gId = null, $productId = null) | |
+ { | |
+ $resource = Mage::getResourceSingleton('bundle/bundle'); | |
+ $selectionResource = Mage::getResourceSingleton('bundle/selection'); | |
+ $productPriceTypeId = Mage::getSingleton('eav/entity_attribute')->getIdByCode( | |
+ Mage_Catalog_Model_Product::ENTITY, | |
+ 'price_type' | |
+ ); | |
+ | |
+ if ($wId instanceof Mage_Core_Model_Store) { | |
+ $store = $wId->getId(); | |
+ $wId = $wId->getWebsiteId(); | |
+ } else { | |
+ $store = Mage::app()->getStore($wId)->getId(); | |
+ $wId = Mage::app()->getStore($wId)->getWebsiteId(); | |
+ } | |
+ | |
+ if (!$gId) { | |
+ $gId = Mage::getSingleton('customer/session')->getCustomerGroupId(); | |
+ } else if ($gId instanceof Mage_Customer_Model_Group) { | |
+ $gId = $gId->getId(); | |
+ } | |
+ | |
+ if (!isset(self::$attributeCache[$productId]['price_type'])) { | |
+ $attributes = $resource->getAttributeData($productId, $productPriceTypeId, $store); | |
+ self::$attributeCache[$productId]['price_type'] = $attributes; | |
+ } else { | |
+ $attributes = self::$attributeCache[$productId]['price_type']; | |
+ } | |
+ | |
+ $options = array(0); | |
+ $results = $resource->getSelectionsData($productId); | |
+ | |
+ if (!$attributes || !$attributes[0]['value']) { //dynamic | |
+ foreach ($results as $result) { | |
+ if (!$result['product_id']) { | |
+ continue; | |
+ } | |
+ | |
+ if ($result['selection_can_change_qty'] && $result['type'] != 'multi' | |
+ && $result['type'] != 'checkbox' | |
+ ) { | |
+ $qty = 1; | |
+ } else { | |
+ $qty = $result['selection_qty']; | |
+ } | |
+ | |
+ $result['final_price'] = $selectionResource->getPriceFromIndex($result['product_id'], $qty, $store, | |
+ $gId); | |
+ | |
+ $selectionPrice = $result['final_price'] * $qty; | |
+ | |
+ if (isset($options[$result['option_id']])) { | |
+ $options[$result['option_id']] = min($options[$result['option_id']], $selectionPrice); | |
+ } else { | |
+ $options[$result['option_id']] = $selectionPrice; | |
+ } | |
+ } | |
+ $basePrice = array_sum($options); | |
+ } else { | |
+ foreach ($results as $result) { | |
+ if (!$result['product_id']) { | |
+ continue; | |
+ } | |
+ if ($result['selection_price_type']) { | |
+ $selectionPrice = $basePrice * $result['selection_price_value'] / 100; | |
+ } else { | |
+ $selectionPrice = $result['selection_price_value']; | |
+ } | |
+ | |
+ if ($result['selection_can_change_qty'] && $result['type'] != 'multi' | |
+ && $result['type'] != 'checkbox' | |
+ ) { | |
+ $qty = 1; | |
+ } else { | |
+ $qty = $result['selection_qty']; | |
+ } | |
+ | |
+ $selectionPrice = $selectionPrice * $qty; | |
+ | |
+ if (isset($options[$result['option_id']])) { | |
+ $options[$result['option_id']] = min($options[$result['option_id']], $selectionPrice); | |
+ } else { | |
+ $options[$result['option_id']] = $selectionPrice; | |
+ } | |
+ } | |
+ | |
+ $basePrice = $basePrice + array_sum($options); | |
+ } | |
+ | |
+ $finalPrice = self::calculateSpecialPrice($basePrice, $specialPrice, $specialPriceFrom, $specialPriceTo, | |
+ $store); | |
+ | |
+ /** | |
+ * adding customer defined options price | |
+ */ | |
+ $customOptions = Mage::getResourceSingleton('catalog/product_option_collection')->reset(); | |
+ $customOptions->addFieldToFilter('is_require', '1') | |
+ ->addProductToFilter($productId) | |
+ ->addPriceToResult($store, 'price') | |
+ ->addValuesToResult(); | |
+ | |
+ foreach ($customOptions as $customOption) { | |
+ $values = $customOption->getValues(); | |
+ if ($values) { | |
+ $prices = array(); | |
+ foreach ($values as $value) { | |
+ $prices[] = $value->getPrice(); | |
+ } | |
+ if (count($prices)) { | |
+ $finalPrice += min($prices); | |
+ } | |
+ } else { | |
+ $finalPrice += $customOption->getPrice(); | |
+ } | |
+ } | |
+ | |
+ if ($rulePrice === false) { | |
+ $rulePrice = Mage::getResourceModel('catalogrule/rule') | |
+ ->getRulePrice(Mage::app()->getLocale()->storeTimeStamp($store), $wId, $gId, $productId); | |
+ } | |
+ | |
+ if ($rulePrice !== null && $rulePrice !== false) { | |
+ $finalPrice = min($finalPrice, $rulePrice); | |
+ } | |
+ | |
+ $finalPrice = max($finalPrice, 0); | |
+ | |
+ return $finalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Calculate and apply special price | |
+ * | |
+ * @param float $finalPrice | |
+ * @param float $specialPrice | |
+ * @param string $specialPriceFrom | |
+ * @param string $specialPriceTo | |
+ * @param mixed $store | |
+ * @return float | |
+ */ | |
+ public static function calculateSpecialPrice($finalPrice, $specialPrice, $specialPriceFrom, $specialPriceTo, | |
+ $store = null) | |
+ { | |
+ if (!is_null($specialPrice) && $specialPrice != false) { | |
+ if (Mage::app()->getLocale()->isStoreDateInInterval($store, $specialPriceFrom, $specialPriceTo)) { | |
+ $specialPrice = Mage::app()->getStore()->roundPrice($finalPrice * $specialPrice / 100); | |
+ $finalPrice = min($finalPrice, $specialPrice); | |
+ } | |
+ } | |
+ | |
+ return $finalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Check is group price value fixed or percent of original price | |
+ * | |
+ * @return bool | |
+ */ | |
+ public function isGroupPriceFixed() | |
+ { | |
+ return false; | |
+ } | |
+ | |
+ /** | |
+ * Get data helper | |
+ * | |
+ * @param string $name | |
+ * @return Mage_Core_Helper_Abstract | |
+ */ | |
+ protected function _getHelperData($name) | |
+ { | |
+ return Mage::helper($name); | |
+ } | |
+ | |
+ /** | |
+ * Get Magento App instance | |
+ * | |
+ * @return Mage_Core_Model_App | |
+ */ | |
+ protected function _getApp() | |
+ { | |
+ return Mage::app(); | |
+ } | |
+ | |
+ /** | |
+ * Check if product has required options | |
+ * | |
+ * @param Mage_Catalog_Model_Product $product | |
+ * @return bool | |
+ */ | |
+ protected function _hasRequiredOptions($product) | |
+ { | |
+ $options = $this->getOptions($product); | |
+ if ($options) { | |
+ foreach ($options as $option) { | |
+ if ($option->getRequired()) { | |
+ return true; | |
+ } | |
+ } | |
+ } | |
+ return false; | |
+ } | |
+ | |
+ /** | |
+ * Get minimum possible price of custom options | |
+ * | |
+ * @param Mage_Catalog_Model_Product_Option $option | |
+ * @return float | |
+ */ | |
+ protected function _getMinimalCustomOptionPrice($option) | |
+ { | |
+ $prices = $this->_getCustomOptionValuesPrices($option); | |
+ $minimalOptionPrice = ($prices) ? min($prices) : (float)$option->getPrice(true); | |
+ $minimalPrice = ($option->getIsRequire()) ? $minimalOptionPrice : 0; | |
+ return $minimalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Get maximum possible price of custom options | |
+ * | |
+ * @param Mage_Catalog_Model_Product_Option $option | |
+ * @return float | |
+ */ | |
+ protected function _getMaximalCustomOptionPrice($option) | |
+ { | |
+ $prices = $this->_getCustomOptionValuesPrices($option); | |
+ $maximalOptionPrice = ($option->isMultipleType()) ? array_sum($prices) : max($prices); | |
+ $maximalPrice = ($prices) ? $maximalOptionPrice : (float)($option->getPrice(true)); | |
+ return $maximalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Get all custom option values prices | |
+ * | |
+ * @param Mage_Catalog_Model_Product_Option $option | |
+ * @return array | |
+ */ | |
+ protected function _getCustomOptionValuesPrices($option) | |
+ { | |
+ $values = $option->getValues(); | |
+ $prices = array(); | |
+ if ($values) { | |
+ foreach ($values as $value) { | |
+ /* @var $value Mage_Catalog_Model_Product_Option_Value */ | |
+ $prices[] = $value->getPrice(true); | |
+ } | |
+ } | |
+ return $prices; | |
+ } | |
+} | |
diff --git a/app/code/local/Mage/Bundle/Model/Resource/Indexer/Price.php b/app/code/local/Mage/Bundle/Model/Resource/Indexer/Price.php | |
new file mode 100644 | |
index 0000000..0fe3f7d | |
--- /dev/null | |
+++ b/app/code/local/Mage/Bundle/Model/Resource/Indexer/Price.php | |
@@ -0,0 +1,698 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to license@magentocommerce.com so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magentocommerce.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+ | |
+/** | |
+ * Bundle products Price indexer resource model | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @author Magento Core Team <core@magentocommerce.com> | |
+ */ | |
+class Mage_Bundle_Model_Resource_Indexer_Price extends Mage_Catalog_Model_Resource_Product_Indexer_Price_Default | |
+{ | |
+ /** | |
+ * Reindex temporary (price result data) for all products | |
+ * | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ public function reindexAll() | |
+ { | |
+ $this->useIdxTable(true); | |
+ | |
+ $this->beginTransaction(); | |
+ try { | |
+ $this->_prepareBundlePrice(); | |
+ $this->commit(); | |
+ } catch (Exception $e) { | |
+ $this->rollBack(); | |
+ throw $e; | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Reindex temporary (price result data) for defined product(s) | |
+ * | |
+ * @param int|array $entityIds | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ public function reindexEntity($entityIds) | |
+ { | |
+ $this->_prepareBundlePrice($entityIds); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve temporary price index table name for fixed bundle products | |
+ * | |
+ * @return string | |
+ */ | |
+ protected function _getBundlePriceTable() | |
+ { | |
+ if ($this->useIdxTable()) { | |
+ return $this->getTable('bundle/price_indexer_idx'); | |
+ } | |
+ return $this->getTable('bundle/price_indexer_tmp'); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve table name for temporary bundle selection prices index | |
+ * | |
+ * @return string | |
+ */ | |
+ protected function _getBundleSelectionTable() | |
+ { | |
+ if ($this->useIdxTable()) { | |
+ return $this->getTable('bundle/selection_indexer_idx'); | |
+ } | |
+ return $this->getTable('bundle/selection_indexer_tmp'); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve table name for temporary bundle option prices index | |
+ * | |
+ * @return string | |
+ */ | |
+ protected function _getBundleOptionTable() | |
+ { | |
+ if ($this->useIdxTable()) { | |
+ return $this->getTable('bundle/option_indexer_idx'); | |
+ } | |
+ return $this->getTable('bundle/option_indexer_tmp'); | |
+ } | |
+ | |
+ /** | |
+ * Prepare temporary price index table for fixed bundle products | |
+ * | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareBundlePriceTable() | |
+ { | |
+ $this->_getWriteAdapter()->delete($this->_getBundlePriceTable()); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare table structure for temporary bundle selection prices index | |
+ * | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareBundleSelectionTable() | |
+ { | |
+ $this->_getWriteAdapter()->delete($this->_getBundleSelectionTable()); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare table structure for temporary bundle option prices index | |
+ * | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareBundleOptionTable() | |
+ { | |
+ $this->_getWriteAdapter()->delete($this->_getBundleOptionTable()); | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare temporary price index data for bundle products by price type | |
+ * | |
+ * @param int $priceType | |
+ * @param int|array $entityIds the entity ids limitatation | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareBundlePriceByType($priceType, $entityIds = null) | |
+ { | |
+ $write = $this->_getWriteAdapter(); | |
+ $table = $this->_getBundlePriceTable(); | |
+ | |
+ $select = $write->select() | |
+ ->from(array('e' => $this->getTable('catalog/product')), array('entity_id')) | |
+ ->join( | |
+ array('cg' => $this->getTable('customer/customer_group')), | |
+ '', | |
+ array('customer_group_id') | |
+ ); | |
+ $this->_addWebsiteJoinToSelect($select, true); | |
+ $this->_addProductWebsiteJoinToSelect($select, 'cw.website_id', 'e.entity_id'); | |
+ $select->columns('website_id', 'cw') | |
+ ->join( | |
+ array('cwd' => $this->_getWebsiteDateTable()), | |
+ 'cw.website_id = cwd.website_id', | |
+ array() | |
+ ) | |
+ ->joinLeft( | |
+ array('tp' => $this->_getTierPriceIndexTable()), | |
+ 'tp.entity_id = e.entity_id AND tp.website_id = cw.website_id' | |
+ . ' AND tp.customer_group_id = cg.customer_group_id', | |
+ array() | |
+ ) | |
+ ->joinLeft( | |
+ array('gp' => $this->_getGroupPriceIndexTable()), | |
+ 'gp.entity_id = e.entity_id AND gp.website_id = cw.website_id' | |
+ . ' AND gp.customer_group_id = cg.customer_group_id', | |
+ array() | |
+ ) | |
+ ->where('e.type_id=?', $this->getTypeId()); | |
+ | |
+ // add enable products limitation | |
+ $statusCond = $write->quoteInto('=?', Mage_Catalog_Model_Product_Status::STATUS_ENABLED); | |
+ $this->_addAttributeToSelect($select, 'status', 'e.entity_id', 'cs.store_id', $statusCond, true); | |
+ if (Mage::helper('core')->isModuleEnabled('Mage_Tax')) { | |
+ $taxClassId = $this->_addAttributeToSelect($select, 'tax_class_id', 'e.entity_id', 'cs.store_id'); | |
+ } else { | |
+ $taxClassId = new Zend_Db_Expr('0'); | |
+ } | |
+ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) { | |
+ $select->columns(array('tax_class_id' => new Zend_Db_Expr('0'))); | |
+ } else { | |
+ $select->columns( | |
+ array('tax_class_id' => $write->getCheckSql($taxClassId . ' IS NOT NULL', $taxClassId, 0)) | |
+ ); | |
+ } | |
+ | |
+ $priceTypeCond = $write->quoteInto('=?', $priceType); | |
+ $this->_addAttributeToSelect($select, 'price_type', 'e.entity_id', 'cs.store_id', $priceTypeCond); | |
+ | |
+ $price = $this->_addAttributeToSelect($select, 'price', 'e.entity_id', 'cs.store_id'); | |
+ $specialPrice = $this->_addAttributeToSelect($select, 'special_price', 'e.entity_id', 'cs.store_id'); | |
+ $specialFrom = $this->_addAttributeToSelect($select, 'special_from_date', 'e.entity_id', 'cs.store_id'); | |
+ $specialTo = $this->_addAttributeToSelect($select, 'special_to_date', 'e.entity_id', 'cs.store_id'); | |
+ $curentDate = new Zend_Db_Expr('cwd.website_date'); | |
+ | |
+ $specialExpr = $write->getCheckSql( | |
+ $write->getCheckSql( | |
+ $specialFrom . ' IS NULL', | |
+ '1', | |
+ $write->getCheckSql( | |
+ $specialFrom . ' <= ' . $curentDate, | |
+ '1', | |
+ '0' | |
+ ) | |
+ ) . " > 0 AND ". | |
+ $write->getCheckSql( | |
+ $specialTo . ' IS NULL', | |
+ '1', | |
+ $write->getCheckSql( | |
+ $specialTo . ' >= ' . $curentDate, | |
+ '1', | |
+ '0' | |
+ ) | |
+ ) | |
+ . " > 0 AND {$specialPrice} > 0 AND {$specialPrice} < 100 ", | |
+ $specialPrice, | |
+ '0' | |
+ ); | |
+ | |
+ $groupPriceExpr = $write->getCheckSql( | |
+ 'gp.price IS NOT NULL AND gp.price > 0 AND gp.price < 100', | |
+ 'gp.price', | |
+ '0' | |
+ ); | |
+ | |
+ $tierExpr = new Zend_Db_Expr("tp.min_price"); | |
+ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) { | |
+ $finalPrice = $write->getCheckSql( | |
+ $specialExpr . ' > 0', | |
+ 'ROUND(' . $price . ' * (' . $specialExpr . ' / 100), 2)', | |
+ $price | |
+ ); | |
+ $tierPrice = $write->getCheckSql( | |
+ $tierExpr . ' IS NOT NULL', | |
+ 'ROUND(' . $price . ' - ' . '(' . $price . ' * (' . $tierExpr . ' / 100)), 2)', | |
+ 'NULL' | |
+ ); | |
+ $groupPrice = $write->getCheckSql( | |
+ $groupPriceExpr . ' > 0', | |
+ 'ROUND(' . $price . ' - ' . '(' . $price . ' * (' . $groupPriceExpr . ' / 100)), 2)', | |
+ 'NULL' | |
+ ); | |
+ $finalPrice = $write->getCheckSql( | |
+ "{$groupPrice} IS NOT NULL AND {$groupPrice} < {$finalPrice}", | |
+ $groupPrice, | |
+ $finalPrice | |
+ ); | |
+ } else { | |
+ $finalPrice = new Zend_Db_Expr("0"); | |
+ $tierPrice = $write->getCheckSql($tierExpr . ' IS NOT NULL', '0', 'NULL'); | |
+ $groupPrice = $write->getCheckSql($groupPriceExpr . ' > 0', $groupPriceExpr, 'NULL'); | |
+ } | |
+ | |
+ $select->columns(array( | |
+ 'price_type' => new Zend_Db_Expr($priceType), | |
+ 'special_price' => $specialExpr, | |
+ 'tier_percent' => $tierExpr, | |
+ 'orig_price' => $write->getCheckSql($price . ' IS NULL', '0', $price), | |
+ 'price' => $finalPrice, | |
+ 'min_price' => $finalPrice, | |
+ 'max_price' => $finalPrice, | |
+ 'tier_price' => $tierPrice, | |
+ 'base_tier' => $tierPrice, | |
+ 'group_price' => $groupPrice, | |
+ 'base_group_price' => $groupPrice, | |
+ 'group_price_percent' => new Zend_Db_Expr('gp.price'), | |
+ )); | |
+ | |
+ if (!is_null($entityIds)) { | |
+ $select->where('e.entity_id IN(?)', $entityIds); | |
+ } | |
+ | |
+ /** | |
+ * Add additional external limitation | |
+ */ | |
+ Mage::dispatchEvent('catalog_product_prepare_index_select', array( | |
+ 'select' => $select, | |
+ 'entity_field' => new Zend_Db_Expr('e.entity_id'), | |
+ 'website_field' => new Zend_Db_Expr('cw.website_id'), | |
+ 'store_field' => new Zend_Db_Expr('cs.store_id') | |
+ )); | |
+ | |
+ $query = $select->insertFromSelect($table); | |
+ $write->query($query); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Calculate fixed bundle product selections price | |
+ * | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _calculateBundleOptionPrice() | |
+ { | |
+ $write = $this->_getWriteAdapter(); | |
+ | |
+ $this->_prepareBundleSelectionTable(); | |
+ $this->_calculateBundleSelectionPrice(Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED); | |
+ $this->_calculateBundleSelectionPrice(Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC); | |
+ | |
+ $this->_prepareBundleOptionTable(); | |
+ | |
+ $select = $write->select() | |
+ ->from( | |
+ array('i' => $this->_getBundleSelectionTable()), | |
+ array('entity_id', 'customer_group_id', 'website_id', 'option_id') | |
+ ) | |
+ ->group(array('entity_id', 'customer_group_id', 'website_id', 'option_id', 'is_required', 'group_type')) | |
+ ->columns(array( | |
+ 'min_price' => $write->getCheckSql('i.is_required = 1', 'MIN(i.price)', '0'), | |
+ 'alt_price' => $write->getCheckSql('i.is_required = 0', 'MIN(i.price)', '0'), | |
+ 'max_price' => $write->getCheckSql('i.group_type = 1', 'SUM(i.price)', 'MAX(i.price)'), | |
+ 'tier_price' => $write->getCheckSql('i.is_required = 1', 'MIN(i.tier_price)', '0'), | |
+ 'alt_tier_price' => $write->getCheckSql('i.is_required = 0', 'MIN(i.tier_price)', '0'), | |
+ 'group_price' => $write->getCheckSql('i.is_required = 1', 'MIN(i.group_price)', '0'), | |
+ 'alt_group_price' => $write->getCheckSql('i.is_required = 0', 'MIN(i.group_price)', '0'), | |
+ )); | |
+ | |
+ $query = $select->insertFromSelect($this->_getBundleOptionTable()); | |
+ $write->query($query); | |
+ | |
+ $this->_prepareDefaultFinalPriceTable(); | |
+ | |
+ $minPrice = new Zend_Db_Expr($write->getCheckSql( | |
+ 'SUM(io.min_price) = 0', | |
+ 'MIN(io.alt_price)', | |
+ 'SUM(io.min_price)' | |
+ ) . ' + i.price'); | |
+ $maxPrice = new Zend_Db_Expr("SUM(io.max_price) + i.price"); | |
+ $tierPrice = $write->getCheckSql( | |
+ 'MIN(i.tier_percent) IS NOT NULL', | |
+ $write->getCheckSql( | |
+ 'SUM(io.tier_price) = 0', | |
+ 'SUM(io.alt_tier_price)', | |
+ 'SUM(io.tier_price)' | |
+ ) . ' + MIN(i.tier_price)', | |
+ 'NULL' | |
+ ); | |
+ $groupPrice = $write->getCheckSql( | |
+ 'MIN(i.group_price_percent) IS NOT NULL', | |
+ $write->getCheckSql( | |
+ 'SUM(io.group_price) = 0', | |
+ 'SUM(io.alt_group_price)', | |
+ 'SUM(io.group_price)' | |
+ ) . ' + MIN(i.group_price)', | |
+ 'NULL' | |
+ ); | |
+ | |
+ $select = $write->select() | |
+ ->from( | |
+ array('io' => $this->_getBundleOptionTable()), | |
+ array('entity_id', 'customer_group_id', 'website_id') | |
+ ) | |
+ ->join( | |
+ array('i' => $this->_getBundlePriceTable()), | |
+ 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' | |
+ . ' AND i.website_id = io.website_id', | |
+ array() | |
+ ) | |
+ ->group(array('io.entity_id', 'io.customer_group_id', 'io.website_id', | |
+ 'i.tax_class_id', 'i.orig_price', 'i.price')) | |
+ ->columns(array('i.tax_class_id', | |
+ 'orig_price' => 'i.orig_price', | |
+ 'price' => 'i.price', | |
+ 'min_price' => $minPrice, | |
+ 'max_price' => $maxPrice, | |
+ 'tier_price' => $tierPrice, | |
+ 'base_tier' => 'MIN(i.base_tier)', | |
+ 'group_price' => $groupPrice, | |
+ 'base_group_price' => 'MIN(i.base_group_price)', | |
+ )); | |
+ | |
+ $query = $select->insertFromSelect($this->_getDefaultFinalPriceTable()); | |
+ $write->query($query); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Calculate bundle product selections price by product type | |
+ * | |
+ * @param int $priceType | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _calculateBundleSelectionPrice($priceType) | |
+ { | |
+ $write = $this->_getWriteAdapter(); | |
+ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) { | |
+ | |
+ $selectionPriceValue = $write->getCheckSql( | |
+ 'bsp.selection_price_value IS NULL', | |
+ 'bs.selection_price_value', | |
+ 'bsp.selection_price_value' | |
+ ); | |
+ $selectionPriceType = $write->getCheckSql( | |
+ 'bsp.selection_price_type IS NULL', | |
+ 'bs.selection_price_type', | |
+ 'bsp.selection_price_type' | |
+ ); | |
+ $priceExpr = new Zend_Db_Expr( | |
+ $write->getCheckSql( | |
+ $selectionPriceType . ' = 1', | |
+ 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),2)', | |
+ $write->getCheckSql( | |
+ 'i.special_price > 0 AND i.special_price < 100', | |
+ 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),2)', | |
+ $selectionPriceValue | |
+ ) | |
+ ) . '* bs.selection_qty' | |
+ ); | |
+ | |
+ $tierExpr = $write->getCheckSql( | |
+ 'i.base_tier IS NOT NULL', | |
+ $write->getCheckSql( | |
+ $selectionPriceType .' = 1', | |
+ 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),2)', | |
+ $write->getCheckSql( | |
+ 'i.tier_percent > 0', | |
+ 'ROUND(' . $selectionPriceValue | |
+ . ' - (' . $selectionPriceValue . ' * (i.tier_percent / 100)),2)', | |
+ $selectionPriceValue | |
+ ) | |
+ ) . ' * bs.selection_qty', | |
+ 'NULL' | |
+ ); | |
+ | |
+ $groupExpr = $write->getCheckSql( | |
+ 'i.base_group_price IS NOT NULL', | |
+ $write->getCheckSql( | |
+ $selectionPriceType .' = 1', | |
+ $priceExpr, | |
+ $write->getCheckSql( | |
+ 'i.group_price_percent > 0', | |
+ 'ROUND(' . $selectionPriceValue | |
+ . ' - (' . $selectionPriceValue . ' * (i.group_price_percent / 100)),2)', | |
+ $selectionPriceValue | |
+ ) | |
+ ) . ' * bs.selection_qty', | |
+ 'NULL' | |
+ ); | |
+ $priceExpr = new Zend_Db_Expr( | |
+ $write->getCheckSql("{$groupExpr} < {$priceExpr}", $groupExpr, $priceExpr) | |
+ ); | |
+ } else { | |
+ $priceExpr = new Zend_Db_Expr( | |
+ $write->getCheckSql( | |
+ 'i.special_price > 0 AND i.special_price < 100', | |
+ 'ROUND(idx.min_price * (i.special_price / 100), 2)', | |
+ 'idx.min_price' | |
+ ) . ' * bs.selection_qty' | |
+ ); | |
+ $tierExpr = $write->getCheckSql( | |
+ 'i.base_tier IS NOT NULL', | |
+ 'ROUND(idx.min_price * (i.base_tier / 100), 2)* bs.selection_qty', | |
+ 'NULL' | |
+ ); | |
+ $groupExpr = $write->getCheckSql( | |
+ 'i.base_group_price IS NOT NULL', | |
+ 'ROUND(idx.min_price * (i.base_group_price / 100), 2)* bs.selection_qty', | |
+ 'NULL' | |
+ ); | |
+ $groupPriceExpr = new Zend_Db_Expr( | |
+ $write->getCheckSql( | |
+ 'i.base_group_price IS NOT NULL AND i.base_group_price > 0 AND i.base_group_price < 100', | |
+ 'ROUND(idx.min_price - idx.min_price * (i.base_group_price / 100), 2)', | |
+ 'idx.min_price' | |
+ ) . ' * bs.selection_qty' | |
+ ); | |
+ $priceExpr = new Zend_Db_Expr( | |
+ $write->getCheckSql("{$groupPriceExpr} < {$priceExpr}", $groupPriceExpr, $priceExpr) | |
+ ); | |
+ } | |
+ | |
+ $select = $write->select() | |
+ ->from( | |
+ array('i' => $this->_getBundlePriceTable()), | |
+ array('entity_id', 'customer_group_id', 'website_id') | |
+ ) | |
+ ->join( | |
+ array('bo' => $this->getTable('bundle/option')), | |
+ 'bo.parent_id = i.entity_id', | |
+ array('option_id') | |
+ ) | |
+ ->join( | |
+ array('bs' => $this->getTable('bundle/selection')), | |
+ 'bs.option_id = bo.option_id', | |
+ array('selection_id') | |
+ ) | |
+ ->joinLeft( | |
+ array('bsp' => $this->getTable('bundle/selection_price')), | |
+ 'bs.selection_id = bsp.selection_id AND bsp.website_id = i.website_id', | |
+ array('') | |
+ ) | |
+ ->join( | |
+ array('idx' => $this->getIdxTable()), | |
+ 'bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id' | |
+ . ' AND i.website_id = idx.website_id', | |
+ array() | |
+ ) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'bs.product_id = e.entity_id AND e.required_options=0', | |
+ array() | |
+ ) | |
+ ->where('i.price_type=?', $priceType) | |
+ ->columns(array( | |
+ 'group_type' => $write->getCheckSql( | |
+ "bo.type = 'select' OR bo.type = 'radio'", | |
+ '0', | |
+ '1' | |
+ ), | |
+ 'is_required' => 'bo.required', | |
+ 'price' => $priceExpr, | |
+ 'tier_price' => $tierExpr, | |
+ 'group_price' => $groupExpr, | |
+ )); | |
+ | |
+ $query = $select->insertFromSelect($this->_getBundleSelectionTable()); | |
+ $write->query($query); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare temporary index price for bundle products | |
+ * | |
+ * @param int|array $entityIds the entity ids limitation | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareBundlePrice($entityIds = null) | |
+ { | |
+ $this->_prepareTierPriceIndex($entityIds); | |
+ $this->_prepareGroupPriceIndex($entityIds); | |
+ $this->_prepareBundlePriceTable(); | |
+ $this->_prepareBundlePriceByType(Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED, $entityIds); | |
+ $this->_prepareBundlePriceByType(Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC, $entityIds); | |
+ | |
+ /** | |
+ * Add possibility modify prices from external events | |
+ */ | |
+ $select = $this->_getWriteAdapter()->select() | |
+ ->join(array('wd' => $this->_getWebsiteDateTable()), | |
+ 'i.website_id = wd.website_id', | |
+ array() | |
+ ); | |
+ Mage::dispatchEvent('prepare_catalog_product_price_index_table', array( | |
+ 'index_table' => array('i' => $this->_getBundlePriceTable()), | |
+ 'select' => $select, | |
+ 'entity_id' => 'i.entity_id', | |
+ 'customer_group_id' => 'i.customer_group_id', | |
+ 'website_id' => 'i.website_id', | |
+ 'website_date' => 'wd.website_date', | |
+ 'update_fields' => array('price', 'min_price', 'max_price') | |
+ )); | |
+ | |
+ $this->_calculateBundleOptionPrice(); | |
+ $this->_applyCustomOption(); | |
+ | |
+ $this->_movePriceDataToIndexTable(); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare percentage tier price for bundle products | |
+ * | |
+ * @see Mage_Catalog_Model_Resource_Product_Indexer_Price::_prepareTierPriceIndex | |
+ * | |
+ * @param int|array $entityIds | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareTierPriceIndex($entityIds = null) | |
+ { | |
+ $adapter = $this->_getWriteAdapter(); | |
+ | |
+ // remove index by bundle products | |
+ $select = $adapter->select() | |
+ ->from(array('i' => $this->_getTierPriceIndexTable()), null) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'i.entity_id=e.entity_id', | |
+ array() | |
+ ) | |
+ ->where('e.type_id=?', $this->getTypeId()); | |
+ $query = $select->deleteFromSelect('i'); | |
+ $adapter->query($query); | |
+ | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('tp' => $this->getValueTable('catalog/product', 'tier_price')), | |
+ array('entity_id') | |
+ ) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'tp.entity_id=e.entity_id', | |
+ array() | |
+ ) | |
+ ->join( | |
+ array('cg' => $this->getTable('customer/customer_group')), | |
+ 'tp.all_groups = 1 OR (tp.all_groups = 0 AND tp.customer_group_id = cg.customer_group_id)', | |
+ array('customer_group_id') | |
+ ) | |
+ ->join( | |
+ array('cw' => $this->getTable('core/website')), | |
+ 'tp.website_id = 0 OR tp.website_id = cw.website_id', | |
+ array('website_id') | |
+ ) | |
+ ->where('cw.website_id != 0') | |
+ ->where('e.type_id=?', $this->getTypeId()) | |
+ ->columns(new Zend_Db_Expr('MIN(tp.value)')) | |
+ ->group(array('tp.entity_id', 'cg.customer_group_id', 'cw.website_id')); | |
+ | |
+ if (!empty($entityIds)) { | |
+ $select->where('tp.entity_id IN(?)', $entityIds); | |
+ } | |
+ | |
+ $query = $select->insertFromSelect($this->_getTierPriceIndexTable()); | |
+ $adapter->query($query); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Prepare percentage group price for bundle products | |
+ * | |
+ * @see Mage_Catalog_Model_Resource_Product_Indexer_Price::_prepareGroupPriceIndex | |
+ * | |
+ * @param int|array $entityIds | |
+ * @return Mage_Bundle_Model_Resource_Indexer_Price | |
+ */ | |
+ protected function _prepareGroupPriceIndex($entityIds = null) | |
+ { | |
+ $adapter = $this->_getWriteAdapter(); | |
+ | |
+ // remove index by bundle products | |
+ $select = $adapter->select() | |
+ ->from(array('i' => $this->_getGroupPriceIndexTable()), null) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'i.entity_id=e.entity_id', | |
+ array() | |
+ ) | |
+ ->where('e.type_id=?', $this->getTypeId()); | |
+ $query = $select->deleteFromSelect('i'); | |
+ $adapter->query($query); | |
+ | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('gp' => $this->getValueTable('catalog/product', 'group_price')), | |
+ array('entity_id') | |
+ ) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'gp.entity_id=e.entity_id', | |
+ array() | |
+ ) | |
+ ->join( | |
+ array('cg' => $this->getTable('customer/customer_group')), | |
+ 'gp.all_groups = 1 OR (gp.all_groups = 0 AND gp.customer_group_id = cg.customer_group_id)', | |
+ array('customer_group_id') | |
+ ) | |
+ ->join( | |
+ array('cw' => $this->getTable('core/website')), | |
+ 'gp.website_id = 0 OR gp.website_id = cw.website_id', | |
+ array('website_id') | |
+ ) | |
+ ->where('cw.website_id != 0') | |
+ ->where('e.type_id=?', $this->getTypeId()) | |
+ ->columns(new Zend_Db_Expr('MIN(gp.value)')) | |
+ ->group(array('gp.entity_id', 'cg.customer_group_id', 'cw.website_id')); | |
+ | |
+ if (!empty($entityIds)) { | |
+ $select->where('gp.entity_id IN(?)', $entityIds); | |
+ } | |
+ | |
+ $query = $select->insertFromSelect($this->_getGroupPriceIndexTable()); | |
+ $adapter->query($query); | |
+ | |
+ return $this; | |
+ } | |
+} | |
diff --git a/app/code/local/Mage/Bundle/Model/Resource/Price/Index.php b/app/code/local/Mage/Bundle/Model/Resource/Price/Index.php | |
new file mode 100644 | |
index 0000000..f892ef7 | |
--- /dev/null | |
+++ b/app/code/local/Mage/Bundle/Model/Resource/Price/Index.php | |
@@ -0,0 +1,856 @@ | |
+<?php | |
+/** | |
+ * Magento | |
+ * | |
+ * NOTICE OF LICENSE | |
+ * | |
+ * This source file is subject to the Open Software License (OSL 3.0) | |
+ * that is bundled with this package in the file LICENSE.txt. | |
+ * It is also available through the world-wide-web at this URL: | |
+ * http://opensource.org/licenses/osl-3.0.php | |
+ * If you did not receive a copy of the license and are unable to | |
+ * obtain it through the world-wide-web, please send an email | |
+ * to license@magentocommerce.com so we can send you a copy immediately. | |
+ * | |
+ * DISCLAIMER | |
+ * | |
+ * Do not edit or add to this file if you wish to upgrade Magento to newer | |
+ * versions in the future. If you wish to customize Magento for your | |
+ * needs please refer to http://www.magentocommerce.com for more information. | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @copyright Copyright (c) 2013 Magento Inc. (http://www.magentocommerce.com) | |
+ * @license http://opensource.org/licenses/osl-3.0.php Open Software License (OSL 3.0) | |
+ */ | |
+ | |
+ | |
+/** | |
+ * Bundle Product Price Index Resource model | |
+ * | |
+ * @category Mage | |
+ * @package Mage_Bundle | |
+ * @author Magento Core Team <core@magentocommerce.com> | |
+ */ | |
+class Mage_Bundle_Model_Resource_Price_Index extends Mage_Core_Model_Resource_Db_Abstract | |
+{ | |
+ /** | |
+ * EAV attributes cache | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_attributes = array(); | |
+ | |
+ /** | |
+ * Websites cache | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_websites; | |
+ | |
+ /** | |
+ * Customer Groups cache | |
+ * | |
+ * @var array | |
+ */ | |
+ protected $_customerGroups; | |
+ | |
+ /** | |
+ * Initialize connection and define main table | |
+ * | |
+ */ | |
+ protected function _construct() | |
+ { | |
+ $this->_init('bundle/price_index', 'entity_id'); | |
+ } | |
+ | |
+ /** | |
+ * Retrieve attribute object | |
+ * | |
+ * @param string $attributeCode | |
+ * @return Mage_Catalog_Model_Resource_Eav_Attribute | |
+ */ | |
+ protected function _getAttribute($attributeCode) | |
+ { | |
+ if (!isset($this->_attributes[$attributeCode])) { | |
+ $this->_attributes[$attributeCode] = Mage::getSingleton('catalog/config') | |
+ ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode); | |
+ } | |
+ return $this->_attributes[$attributeCode]; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve websites collection array | |
+ * | |
+ * @return array | |
+ */ | |
+ protected function _getWebsites() | |
+ { | |
+ if (is_null($this->_websites)) { | |
+ $this->_websites = Mage::app()->getWebsites(false); | |
+ } | |
+ return $this->_websites; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve customer groups collection array | |
+ * | |
+ * @return array | |
+ */ | |
+ protected function _getCustomerGroups() | |
+ { | |
+ if (is_null($this->_customerGroups)) { | |
+ $this->_customerGroups = array(); | |
+ foreach (Mage::getModel('customer/group')->getCollection() as $group) { | |
+ $this->_customerGroups[$group->getId()] = $group; | |
+ } | |
+ } | |
+ return $this->_customerGroups; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve product ids array by product condition | |
+ * | |
+ * @param Mage_Core_Model_Product|Mage_Catalog_Model_Product_Condition_Interface|array|int $product | |
+ * @param int $lastEntityId | |
+ * @param int $limit | |
+ * @return array | |
+ */ | |
+ public function getProducts($product = null, $lastEntityId = 0, $limit = 100) | |
+ { | |
+ | |
+ $select = $this->_getReadAdapter()->select() | |
+ ->from( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ array('entity_id') | |
+ ) | |
+ ->where('e.type_id=?', Mage_Catalog_Model_Product_Type::TYPE_BUNDLE); | |
+ if ($product instanceof Mage_Catalog_Model_Product) { | |
+ $select->where('e.entity_id=?', $product->getId()); | |
+ } elseif ($product instanceof Mage_Catalog_Model_Product_Condition_Interface) { | |
+ $value = new Zend_Db_Expr($product->getIdsSelect($this->_getReadAdapter())); | |
+ $select->where('e.entity_id IN(?)', $value); | |
+ } elseif (is_numeric($product) || is_array($product)) { | |
+ $select->where('e.entity_id IN(?)', $product); | |
+ } | |
+ | |
+ $priceType = $this->_getAttribute('price_type'); | |
+ $priceTypeAlias = 't_' . $priceType->getAttributeCode(); | |
+ $joinConds = array( | |
+ $priceTypeAlias . '.attribute_id=:attribute_id', | |
+ $priceTypeAlias . '.store_id=0', | |
+ $priceTypeAlias . '.entity_id=e.entity_id' | |
+ ); | |
+ | |
+ $select->joinLeft( | |
+ array($priceTypeAlias => $priceType->getBackend()->getTable()), | |
+ join(' AND ', $joinConds), | |
+ array('price_type' => $priceTypeAlias . '.value') | |
+ ); | |
+ | |
+ $select->where('e.entity_id>:last_entity_id', $lastEntityId) | |
+ ->order('e.entity_id') | |
+ ->limit($limit); | |
+ $bind = array( | |
+ 'attribute_id' => $priceType->getAttributeId(), | |
+ 'last_entity_id' => $lastEntityId | |
+ ); | |
+ return $this->_getReadAdapter()->fetchPairs($select, $bind); | |
+ } | |
+ | |
+ /** | |
+ * Reindex Bundle product Price Index | |
+ * | |
+ * @param Mage_Core_Model_Product|Mage_Catalog_Model_Product_Condition_Interface|array|int $products | |
+ * @return Mage_Bundle_Model_Resource_Price_Index | |
+ */ | |
+ public function reindex($products = null) | |
+ { | |
+ $lastEntityId = 0; | |
+ while (true) { | |
+ $productsData = $this->getProducts($products, $lastEntityId); | |
+ if (!$productsData) { | |
+ break; | |
+ } | |
+ | |
+ foreach ($productsData as $productId => $priceType) { | |
+ $this->_reindexProduct($productId, $priceType); | |
+ $lastEntityId = $productId; | |
+ } | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Reindex product price | |
+ * | |
+ * @param int $productId | |
+ * @param int $priceType | |
+ * @return Mage_Bundle_Model_Resource_Price_Index | |
+ */ | |
+ protected function _reindexProduct($productId, $priceType) | |
+ { | |
+ $options = $this->getSelections($productId); | |
+ $selectionProducts = array(); | |
+ foreach ($options as $option) { | |
+ foreach ($option['selections'] as $selection) { | |
+ $selectionProducts[$selection['product_id']] = $selection['product_id']; | |
+ } | |
+ } | |
+ | |
+ $priceIndex = array(); | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) { | |
+ // load selection product prices from index for dynamic bundle | |
+ $priceIndex = $this->getProductsPriceFromIndex($selectionProducts); | |
+ } | |
+ | |
+ foreach ($this->_getWebsites() as $website) { | |
+ if (!$website->getDefaultStore()) { | |
+ continue; | |
+ } | |
+ $salableStatus = $this->getProductsSalableStatus($selectionProducts, $website); | |
+ $priceData = $this->getProductsPriceData($productId, $website); | |
+ $priceData = $priceData[$productId]; | |
+ | |
+ /* @var $website Mage_Core_Model_Website */ | |
+ foreach ($this->_getCustomerGroups() as $group) { | |
+ /* @var $group Mage_Customer_Model_Group */ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) { | |
+ $basePrice = $this->_getBasePrice($productId, $priceData, $website, $group); | |
+ $customOptions = $this->getCustomOptions($productId, $website); | |
+ } elseif ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) { | |
+ $basePrice = 0; | |
+ } | |
+ | |
+ list($minPrice, $maxPrice) = $this->_calculateBundleSelections($options, $salableStatus, | |
+ $productId, $priceType, $basePrice, $priceData, $priceIndex, $website, $group | |
+ ); | |
+ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) { | |
+ list($minPrice, $maxPrice) = | |
+ $this->_calculateCustomOptions($customOptions, $basePrice, $minPrice, $maxPrice); | |
+ } | |
+ | |
+ $this->_savePriceIndex($productId, $website->getId(), $group->getId(), $minPrice, $maxPrice); | |
+ } | |
+ } | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Save price index | |
+ * | |
+ * @param int $productId | |
+ * @param int $websiteId | |
+ * @param int $groupId | |
+ * @param float $minPrice | |
+ * @param float $maxPrice | |
+ * @return Mage_Bundle_Model_Resource_Price_Index | |
+ */ | |
+ protected function _savePriceIndex($productId, $websiteId, $groupId, $minPrice, $maxPrice) | |
+ { | |
+ $adapter = $this->_getWriteAdapter(); | |
+ $adapter->beginTransaction(); | |
+ $bind = array($productId, $websiteId, $groupId, $minPrice, $maxPrice); | |
+ $adapter->insertOnDuplicate($this->getMainTable(), $bind, array('min_price', 'max_price')); | |
+ $adapter->commit(); | |
+ | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve bundle options with selections and prices by product | |
+ * | |
+ * @param int $productId | |
+ * @return array | |
+ */ | |
+ public function getSelections($productId) | |
+ { | |
+ $options = array(); | |
+ $read = $this->_getReadAdapter(); | |
+ $select = $read->select() | |
+ ->from( | |
+ array('option_table' => $this->getTable('bundle/option')), | |
+ array('option_id', 'required', 'type') | |
+ ) | |
+ ->join( | |
+ array('selection_table' => $this->getTable('bundle/selection')), | |
+ 'selection_table.option_id=option_table.option_id', | |
+ array('selection_id', 'product_id', 'selection_price_type', | |
+ 'selection_price_value', 'selection_qty', 'selection_can_change_qty') | |
+ ) | |
+ ->join( | |
+ array('e' => $this->getTable('catalog/product')), | |
+ 'e.entity_id=selection_table.product_id AND e.required_options=0', | |
+ array() | |
+ ) | |
+ ->where('option_table.parent_id=:product_id'); | |
+ | |
+ $query = $read->query($select, array('product_id' => $productId)); | |
+ while ($row = $query->fetch()) { | |
+ if (!isset($options[$row['option_id']])) { | |
+ $options[$row['option_id']] = array( | |
+ 'option_id' => $row['option_id'], | |
+ 'required' => $row['required'], | |
+ 'type' => $row['type'], | |
+ 'selections' => array() | |
+ ); | |
+ } | |
+ $options[$row['option_id']]['selections'][$row['selection_id']] = array( | |
+ 'selection_id' => $row['selection_id'], | |
+ 'product_id' => $row['product_id'], | |
+ 'price_type' => $row['selection_price_type'], | |
+ 'price_value' => $row['selection_price_value'], | |
+ 'qty' => $row['selection_qty'], | |
+ 'can_change_qty' => $row['selection_can_change_qty'] | |
+ ); | |
+ } | |
+ | |
+ return $options; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve salable product statuses | |
+ * | |
+ * @param int|array $products | |
+ * @param Mage_Core_Model_Website $website | |
+ * @return array | |
+ */ | |
+ public function getProductsSalableStatus($products, Mage_Core_Model_Website $website) | |
+ { | |
+ $read = $this->_getReadAdapter(); | |
+ $productsData = array(); | |
+ $select = $read->select() | |
+ ->from(array('e' => $this->getTable('catalog/product')), 'entity_id') | |
+ ->where('e.entity_id IN(?)', $products); | |
+ // add belong to website | |
+ $select->joinLeft( | |
+ array('pw' => $this->getTable('catalog/product_website')), | |
+ 'e.entity_id=pw.product_id AND pw.website_id=:website_id', | |
+ array('pw.website_id') | |
+ ); | |
+ | |
+ $store = $website->getDefaultStore(); | |
+ | |
+ // add product status | |
+ $status = $this->_getAttribute('status'); | |
+ if ($status->isScopeGlobal()) { | |
+ $select->join( | |
+ array('t_status' => $status->getBackend()->getTable()), | |
+ 'e.entity_id=t_status.entity_id' | |
+ . ' AND t_status.attribute_id=:status_attribute_id' | |
+ . ' AND t_status.store_id=0', | |
+ array('status' => 't_status.value') | |
+ ); | |
+ } else { | |
+ | |
+ $statusField = $read->getCheckSql( | |
+ 't2_status.value_id > 0', | |
+ 't2_status.value', | |
+ 't1_status.value' | |
+ ); | |
+ | |
+ $statusTable = $status->getBackend()->getTable(); | |
+ $select->join( | |
+ array('t1_status' => $statusTable), | |
+ 'e.entity_id=t1_status.entity_id' | |
+ . ' AND t1_status.attribute_id=:status_attribute_id' | |
+ . ' AND t1_status.store_id=0', | |
+ array('status' => $statusField) | |
+ ) | |
+ ->joinLeft( | |
+ array('t2_status' => $statusTable), | |
+ 't1_status.entity_id = t2_status.entity_id' | |
+ . ' AND t1_status.attribute_id = t2_status.attribute_id' | |
+ . ' AND t2_status.store_id=:store_id', | |
+ array() | |
+ ); | |
+ } | |
+ | |
+ $bind = array( | |
+ 'status_attribute_id' => $status->getAttributeId(), | |
+ 'website_id' => $website->getId(), | |
+ 'store_id' => $store->getId() | |
+ ); | |
+ | |
+ Mage::dispatchEvent('catalog_product_prepare_index_select', array( | |
+ 'website' => $website, | |
+ 'select' => $select, | |
+ 'bind' => $bind | |
+ )); | |
+ | |
+ $query = $read->query($select, $bind); | |
+ while ($row = $query->fetch()) { | |
+ $salable = isset($row['salable']) ? $row['salable'] : true; | |
+ $website = $row['website_id'] > 0 ? true : false; | |
+ $status = $row['status']; | |
+ | |
+ $productsData[$row['entity_id']] = $salable && $status && $website; | |
+ } | |
+ | |
+ return $productsData; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve Selection Product price from Price Index | |
+ * Return index key {entity_id}-{website_id}-{customer_group_id} | |
+ * | |
+ * @param int|array $productIds | |
+ * @return array | |
+ */ | |
+ public function getProductsPriceFromIndex($productIds) | |
+ { | |
+ $price = $this->_getAttribute('price'); | |
+ $read = $this->_getReadAdapter(); | |
+ $key = $read->getConcatSql(array('entity_id', 'customer_group_id', 'website_id'), '-'); | |
+ | |
+ $select = $read->select() | |
+ ->from( | |
+ array('price_index' => $this->getTable('catalogindex/price')), | |
+ array('index_key' => $key, 'value') | |
+ ) | |
+ ->where('entity_id IN(?)', $productIds) | |
+ ->where('attribute_id= :attribute_id'); | |
+ $index = $read->fetchPairs($select, array('attribute_id' => $price->getAttributeId())); | |
+ return $index; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve product(s) price data | |
+ * | |
+ * @param int|array $products | |
+ * @param Mage_Core_Model_Website $website | |
+ * @return array | |
+ */ | |
+ public function getProductsPriceData($products, Mage_Core_Model_Website $website) | |
+ { | |
+ $productsData = array(); | |
+ $read = $this->_getReadAdapter(); | |
+ $select = $read->select() | |
+ ->from(array('e' => $this->getTable('catalog/product')), 'entity_id') | |
+ ->where('e.entity_id IN(?)', $products); | |
+ | |
+ $this->_addAttributeDataToSelect($select, 'price', $website); | |
+ $this->_addAttributeDataToSelect($select, 'special_price', $website); | |
+ $this->_addAttributeDataToSelect($select, 'special_from_date', $website); | |
+ $this->_addAttributeDataToSelect($select, 'special_to_date', $website); | |
+ | |
+ $query = $read->query($select); | |
+ while ($row = $query->fetch()) { | |
+ $productsData[$row['entity_id']] = array( | |
+ 'price' => $row['price'], | |
+ 'special_price' => $row['special_price'], | |
+ 'special_from_date' => $row['special_from_date'], | |
+ 'special_to_date' => $row['special_to_date'] | |
+ ); | |
+ } | |
+ | |
+ return $productsData; | |
+ } | |
+ | |
+ /** | |
+ * Add attribute data to select | |
+ * | |
+ * @param Varien_Db_Select $select | |
+ * @param string $attributeCode | |
+ * @param Mage_Core_Model_Website $website | |
+ * @return Mage_Bundle_Model_Resource_Price_Index | |
+ */ | |
+ protected function _addAttributeDataToSelect(Varien_Db_Select $select, $attributeCode, | |
+ Mage_Core_Model_Website $website) | |
+ { | |
+ $attribute = $this->_getAttribute($attributeCode); | |
+ $store = $website->getDefaultStore(); | |
+ if ($attribute->isScopeGlobal()) { | |
+ $table = 't_' . $attribute->getAttributeCode(); | |
+ $select->joinLeft( | |
+ array($table => $attribute->getBackend()->getTable()), | |
+ "e.entity_id={$table}.entity_id" | |
+ . " AND {$table}.attribute_id={$attribute->getAttributeId()}" | |
+ . " AND {$table}.store_id=0", | |
+ array($attribute->getAttributeCode() => $table . '.value') | |
+ ); | |
+ } else { | |
+ $tableName = $attribute->getBackend()->getTable(); | |
+ $tableGlobal = 't1_' . $attribute->getAttributeCode(); | |
+ $tableStore = 't2_' . $attribute->getAttributeCode(); | |
+ | |
+ $attributeCond = $this->getReadConnection()->getCheckSql( | |
+ $tableStore . '.value_id > 0', | |
+ $tableStore . '.value', | |
+ $tableGlobal . '.value' | |
+ ); | |
+ $select->joinLeft( | |
+ array($tableGlobal => $tableName), | |
+ "e.entity_id = {$tableGlobal}.entity_id" | |
+ . " AND {$tableGlobal}.attribute_id = {$attribute->getAttributeId()}" | |
+ . " AND {$tableGlobal}.store_id = 0", | |
+ array($attribute->getAttributeCode() => $attributeCond) | |
+ ) | |
+ ->joinLeft( | |
+ array($tableStore => $tableName), | |
+ "{$tableGlobal}.entity_id = {$tableStore}.entity_id" | |
+ . " AND {$tableGlobal}.attribute_id = {$tableStore}.attribute_id" | |
+ . " AND {$tableStore}.store_id = " . $store->getId(), | |
+ array() | |
+ ); | |
+ } | |
+ return $this; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve fixed bundle base price (with special price and rules) | |
+ * | |
+ * @param int $productId | |
+ * @param array $priceData | |
+ * @param Mage_Core_Model_Website $website | |
+ * @param Mage_Customer_Model_Group $customerGroup | |
+ * @return float | |
+ */ | |
+ protected function _getBasePrice($productId, array $priceData, $website, $customerGroup) | |
+ { | |
+ $store = $website->getDefaultStore(); | |
+ $storeTimeStamp = Mage::app()->getLocale()->storeTimeStamp($store); | |
+ $finalPrice = $this->_calculateSpecialPrice($priceData['price'], $priceData, $website); | |
+ | |
+ $rulePrice = Mage::getResourceModel('catalogrule/rule') | |
+ ->getRulePrice($storeTimeStamp, $website->getId(), $customerGroup->getId(), $productId); | |
+ | |
+ if ($rulePrice !== null && $rulePrice !== false) { | |
+ $finalPrice = min($finalPrice, $rulePrice); | |
+ } | |
+ | |
+ return $finalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve custom options for product | |
+ * | |
+ * @param int $productId | |
+ * @param Mage_Core_Model_Website $website | |
+ * @return array | |
+ */ | |
+ public function getCustomOptions($productId, Mage_Core_Model_Website $website) | |
+ { | |
+ $options = array(); | |
+ $store = $website->getDefaultStore(); | |
+ $price = $this->_getAttribute('price'); | |
+ $adapter = $this->_getReadAdapter(); | |
+ | |
+ $bind = array( | |
+ ':product_id' => $productId, | |
+ ':store_id' => $store->getId(), | |
+ | |
+ ); | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('option_table' => $this->getTable('catalog/product_option')), | |
+ array('option_id', 'is_require', 'type') | |
+ ) | |
+ ->where('option_table.product_id=:product_id'); | |
+ | |
+ if ($price->isScopeGlobal()) { | |
+ $select->join( | |
+ array('price_table' => $this->getTable('catalog/product_option_price')), | |
+ 'option_table.option_id = price_table.option_id' . | |
+ ' AND price_table.store_id = 0', | |
+ array('value_id' => 'option_price_id', 'price', 'price_type') | |
+ ); | |
+ } else { | |
+ $valueIdCond = $adapter->getCheckSql( | |
+ 'price_store_table.option_price_id IS NOT NULL', | |
+ 'price_store_table.option_price_id', | |
+ 'price_global_table.option_price_id' | |
+ ); | |
+ $priceCond = $adapter->getCheckSql( | |
+ 'price_store_table.price IS NOT NULL', | |
+ 'price_store_table.price', | |
+ 'price_global_table.price' | |
+ ); | |
+ $priceTypeCond = $adapter->getCheckSql( | |
+ 'price_store_table.price_type IS NOT NULL', | |
+ 'price_store_table.price_type', | |
+ 'price_global_table.price_type' | |
+ ); | |
+ | |
+ $select | |
+ ->join( | |
+ array('price_global_table' => $this->getTable('catalog/product_option_price')), | |
+ 'option_table.option_id=price_global_table.option_id' . | |
+ ' AND price_global_table.store_id=0', | |
+ array( | |
+ 'value_id' => $valueIdCond, | |
+ 'price' => $priceCond, | |
+ 'price_type' => $priceTypeCond | |
+ )) | |
+ ->joinLeft( | |
+ array('price_store_table' => $this->getTable('catalog/product_option_price')), | |
+ 'option_table.option_id = price_store_table.option_id' . | |
+ ' AND price_store_table.store_id=:store_id', | |
+ array() | |
+ ); | |
+ } | |
+ | |
+ $query = $adapter->query($select, $bind); | |
+ while ($row = $query->fetch()) { | |
+ if (!isset($options[$row['option_id']])) { | |
+ $options[$row['option_id']] = array( | |
+ 'option_id' => $row['option_id'], | |
+ 'is_require' => $row['is_require'], | |
+ 'type' => $row['type'], | |
+ 'values' => array() | |
+ ); | |
+ } | |
+ $options[$row['option_id']]['values'][$row['value_id']] = array( | |
+ 'price_type' => $row['price_type'], | |
+ 'price_value' => $row['price'] | |
+ ); | |
+ } | |
+ | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('option_table' => $this->getTable('catalog/product_option')), | |
+ array('option_id', 'is_require', 'type') | |
+ ) | |
+ ->join( | |
+ array('type_table' => $this->getTable('catalog/product_option_type_value')), | |
+ 'option_table.option_id=type_table.option_id', | |
+ array() | |
+ ) | |
+ ->where('option_table.product_id=:product_id'); | |
+ | |
+ if ($price->isScopeGlobal()) { | |
+ $select->join( | |
+ array('price_table' => $this->getTable('catalog/product_option_type_price')), | |
+ 'type_table.option_type_id=price_table.option_type_id' . | |
+ ' AND price_table.store_id=0', | |
+ array('value_id' => 'option_type_id', 'price', 'price_type') | |
+ ); | |
+ } else { | |
+ $select | |
+ ->join( | |
+ array('price_global_table' => $this->getTable('catalog/product_option_type_price')), | |
+ 'type_table.option_type_id=price_global_table.option_type_id' . | |
+ ' AND price_global_table.store_id=0', | |
+ array( | |
+ 'value_id' => $valueIdCond, | |
+ 'price' => $priceCond, | |
+ 'price_type' => $priceTypeCond | |
+ ) | |
+ ) | |
+ ->joinLeft( | |
+ array('price_store_table' => $this->getTable('catalog/product_option_type_price')), | |
+ 'type_table.option_type_id=price_store_table.option_type_id' . | |
+ ' AND price_store_table.store_id=:store_id', | |
+ array() | |
+ ); | |
+ } | |
+ | |
+ $query = $adapter->query($select, $bind); | |
+ while ($row = $query->fetch()) { | |
+ if (!isset($options[$row['option_id']])) { | |
+ $options[$row['option_id']] = array( | |
+ 'option_id' => $row['option_id'], | |
+ 'is_require' => $row['is_require'], | |
+ 'type' => $row['type'], | |
+ 'values' => array() | |
+ ); | |
+ } | |
+ $options[$row['option_id']]['values'][$row['value_id']] = array( | |
+ 'price_type' => $row['price_type'], | |
+ 'price_value' => $row['price'] | |
+ ); | |
+ } | |
+ | |
+ return $options; | |
+ } | |
+ | |
+ /** | |
+ * Calculate custom options price | |
+ * Return array with indexes(0 -> min_price, 1 -> max_price) | |
+ * | |
+ * @param array $options | |
+ * @param float $basePrice | |
+ * @param float $minPrice | |
+ * @param float $maxPrice | |
+ * @return array | |
+ */ | |
+ public function _calculateCustomOptions(array $options, $basePrice, $minPrice, $maxPrice) | |
+ { | |
+ foreach ($options as $option) { | |
+ $optionPrices = array(); | |
+ foreach ($option['values'] as $value) { | |
+ if ($value['price_type'] == 'percent') { | |
+ $valuePrice = $basePrice * $value['price_value'] / 100; | |
+ } else { | |
+ $valuePrice = $value['price_value']; | |
+ } | |
+ $optionPrices[] = $valuePrice; | |
+ } | |
+ if ($option['is_require']) { | |
+ $minPrice += min($optionPrices); | |
+ } | |
+ $multiTypes = array( | |
+ Mage_Catalog_Model_Product_Option::OPTION_TYPE_DROP_DOWN, | |
+ Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX, | |
+ Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE | |
+ ); | |
+ if ($optionPrices) { | |
+ if (in_array($option['type'], $multiTypes)) { | |
+ $maxPrice += array_sum($optionPrices); | |
+ } else { | |
+ $maxPrice += max($optionPrices); | |
+ } | |
+ } | |
+ } | |
+ | |
+ return array($minPrice, $maxPrice); | |
+ } | |
+ | |
+ /** | |
+ * Calculate minimal and maximal price for bundle selections | |
+ * Return array with prices (0 -> min_price, 1 -> max_price) | |
+ * | |
+ * @param array $options | |
+ * @param array $salableStatus | |
+ * @param int $productId | |
+ * @param int $priceType | |
+ * @param float $basePrice | |
+ * @param array $priceData | |
+ * @param array $priceIndex | |
+ * @param Mage_Core_Model_Website $website | |
+ * @param Mage_Customer_Model_Group $group | |
+ * @return array | |
+ */ | |
+ public function _calculateBundleSelections(array $options, array $salableStatus, $productId, $priceType, $basePrice, | |
+ $priceData, $priceIndex, $website, $group) | |
+ { | |
+ $minPrice = $maxPrice = $basePrice; | |
+ $optPrice = 0; | |
+ | |
+ foreach ($options as $option) { | |
+ $optionPrices = array(); | |
+ foreach ($option['selections'] as $selection) { | |
+ if (!$selection['product_id']) { | |
+ continue; | |
+ } | |
+ | |
+ if (!$salableStatus[$selection['product_id']]) { | |
+ continue; | |
+ } | |
+ | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) { | |
+ $basePrice = $this->_getBasePrice($productId, $priceData, $website, $group); | |
+ } | |
+ | |
+ // calculate selection price | |
+ if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) { | |
+ $priceIndexKey = join('-', array( | |
+ $selection['product_id'], | |
+ $website->getId(), | |
+ $group->getId() | |
+ )); | |
+ | |
+ $selectionPrice = isset($priceIndex[$priceIndexKey]) ? $priceIndex[$priceIndexKey] : 0; | |
+ $selectionPrice = $this->_calculateSpecialPrice($selectionPrice, $priceData, $website); | |
+ } else { | |
+ if ($selection['price_type']) { // percent | |
+ $selectionPrice = $basePrice * $selection['price_value'] / 100; | |
+ } else { | |
+ $selectionPrice = $this->_calculateSpecialPrice($selection['price_value'], | |
+ $priceData, $website); | |
+ } | |
+ } | |
+ | |
+ // calculate selection qty | |
+ if ($selection['can_change_qty'] && $option['type'] != 'multi' && $option['type'] != 'checkbox') { | |
+ $qty = 1; | |
+ } else { | |
+ $qty = $selection['qty']; | |
+ } | |
+ | |
+ $selectionPrice = $selectionPrice * $qty; | |
+ $optionPrices[$selection['selection_id']] = $selectionPrice; | |
+ } | |
+ | |
+ if ($optionPrices) { | |
+ if ($option['required']) { | |
+ $minPrice += min($optionPrices); | |
+ } else { | |
+ $optPrice = $optPrice && $optPrice < min($optionPrices) ? $optPrice : min($optionPrices); | |
+ } | |
+ if (in_array($option['type'], array('multi', 'checkbox'))) { | |
+ $maxPrice += array_sum($optionPrices); | |
+ } else { | |
+ $maxPrice += max($optionPrices); | |
+ } | |
+ } | |
+ } | |
+ | |
+ if ($minPrice == 0) { | |
+ $minPrice = $optPrice; | |
+ } | |
+ return array($minPrice, $maxPrice); | |
+ } | |
+ | |
+ /** | |
+ * Apply special price | |
+ * | |
+ * @param float $finalPrice | |
+ * @param array $priceData | |
+ * @param Mage_Core_Model_Website $website | |
+ * @return float | |
+ */ | |
+ public function _calculateSpecialPrice($finalPrice, array $priceData, Mage_Core_Model_Website $website) | |
+ { | |
+ $store = $website->getDefaultStore(); | |
+ $specialPrice = $priceData['special_price']; | |
+ | |
+ if (!is_null($specialPrice) && $specialPrice != false) { | |
+ if (Mage::app()->getLocale()->isStoreDateInInterval($store, $priceData['special_from_date'], | |
+ $priceData['special_to_date'])) { | |
+ $specialPrice = ($finalPrice * $specialPrice) / 100; | |
+ $finalPrice = min($finalPrice, $specialPrice); | |
+ } | |
+ } | |
+ | |
+ return $finalPrice; | |
+ } | |
+ | |
+ /** | |
+ * Retrieve price index for products | |
+ * | |
+ * @param int|array $productIds | |
+ * @param int $websiteId | |
+ * @param int $groupId | |
+ * @return array | |
+ */ | |
+ public function loadPriceIndex($productIds, $websiteId, $groupId) | |
+ { | |
+ $prices = array(); | |
+ $adapter = $this->_getReadAdapter(); | |
+ $select = $adapter->select() | |
+ ->from( | |
+ array('pi' => $this->getMainTable()), | |
+ array('entity_id', 'min_price', 'max_price') | |
+ ) | |
+ ->where('entity_id IN(?)', $productIds) | |
+ ->where('website_id=:website_id') | |
+ ->where('customer_group_id=:group_id'); | |
+ $bind = array( | |
+ 'website_id' => $websiteId, | |
+ 'group_id' => $groupId | |
+ ); | |
+ $query = $adapter->query($select, $bind); | |
+ while ($row = $query->fetch()) { | |
+ $prices[$row['entity_id']] = array( | |
+ 'min_price' => $row['min_price'], | |
+ 'max_price' => $row['max_price'] | |
+ ); | |
+ } | |
+ | |
+ return $prices; | |
+ } | |
+} | |
-- | |
1.8.4.2 | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment