Overview

Packages

  • currencysymbol
  • MAbout
  • Mage
    • Admin
    • Adminhtml
    • AdminNotification
    • Api
    • Api2
    • Authorizenet
    • Backup
    • Bundle
    • Captcha
    • Catalog
    • CatalogIndex
    • CatalogInventory
    • CatalogRule
    • CatalogSearch
    • Centinel
    • Checkout
    • Cms
    • Compiler
    • Connect
    • Contacts
    • Core
    • Cron
    • CurrencySymbol
    • Customer
    • Dataflow
    • Directory
    • DirtectPost
    • Downloadable
    • Eav
    • GiftMessage
    • GoogleAnalytics
    • GoogleBase
    • GoogleCheckout
    • ImportExport
    • Index
    • Install
    • Log
    • Media
    • Newsletter
    • Oauth
    • Page
    • PageCache
    • Paygate
    • Payment
    • Paypal
    • PaypalUk
    • Persistent
    • Poll
    • ProductAlert
    • Rating
    • Reports
    • Review
    • Rss
    • Rule
    • Sales
    • SalesRule
    • Sedfriend
    • Sendfriend
    • Shipping
    • Sitemap
    • Tag
    • Tax
    • Usa
    • Weee
    • Widget
    • Wishlist
    • XmlConnect
  • None
  • Phoenix
    • Moneybookers
  • PHP
  • Zend
    • Date
    • Mime
    • XmlRpc

Classes

  • Mage_Bundle_Adminhtml_Bundle_Product_EditController
  • Mage_Bundle_Adminhtml_Bundle_SelectionController
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Composite_Fieldset_Options_Type_Checkbox
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Composite_Fieldset_Options_Type_Multi
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Composite_Fieldset_Options_Type_Radio
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Composite_Fieldset_Options_Type_Select
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Attributes
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Attributes_Extend
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Attributes_Special
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Bundle
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Bundle_Option
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Bundle_Option_Search
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Bundle_Option_Search_Grid
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tab_Bundle_Option_Selection
  • Mage_Bundle_Block_Adminhtml_Catalog_Product_Edit_Tabs
  • Mage_Bundle_Block_Adminhtml_Sales_Order_Items_Renderer
  • Mage_Bundle_Block_Adminhtml_Sales_Order_View_Items_Renderer
  • Mage_Bundle_Block_Catalog_Product_List_Partof
  • Mage_Bundle_Block_Catalog_Product_Price
  • Mage_Bundle_Block_Catalog_Product_View
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option_Checkbox
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option_Multi
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option_Radio
  • Mage_Bundle_Block_Catalog_Product_View_Type_Bundle_Option_Select
  • Mage_Bundle_Block_Checkout_Cart_Item_Renderer
  • Mage_Bundle_Block_Sales_Order_Items_Renderer
  • Mage_Bundle_Helper_Catalog_Product_Configuration
  • Mage_Bundle_Helper_Data
  • Mage_Bundle_Model_CatalogIndex_Data_Bundle
  • Mage_Bundle_Model_Mysql4_Bundle
  • Mage_Bundle_Model_Mysql4_Indexer_Price
  • Mage_Bundle_Model_Mysql4_Indexer_Stock
  • Mage_Bundle_Model_Mysql4_Option
  • Mage_Bundle_Model_Mysql4_Option_Collection
  • Mage_Bundle_Model_Mysql4_Price_Index
  • Mage_Bundle_Model_Mysql4_Selection
  • Mage_Bundle_Model_Mysql4_Selection_Collection
  • Mage_Bundle_Model_Observer
  • Mage_Bundle_Model_Option
  • Mage_Bundle_Model_Price_Index
  • Mage_Bundle_Model_Product_Attribute_Source_Price_View
  • Mage_Bundle_Model_Product_Price
  • Mage_Bundle_Model_Product_Type
  • Mage_Bundle_Model_Resource_Bundle
  • Mage_Bundle_Model_Resource_Indexer_Price
  • Mage_Bundle_Model_Resource_Indexer_Stock
  • Mage_Bundle_Model_Resource_Option
  • Mage_Bundle_Model_Resource_Option_Collection
  • Mage_Bundle_Model_Resource_Price_Index
  • Mage_Bundle_Model_Resource_Selection
  • Mage_Bundle_Model_Resource_Selection_Collection
  • Mage_Bundle_Model_Sales_Order_Pdf_Items_Abstract
  • Mage_Bundle_Model_Sales_Order_Pdf_Items_Shipment
  • Mage_Bundle_Model_Selection
  • Mage_Bundle_Model_Source_Option_Selection_Price_Type
  • Mage_Bundle_Model_Source_Option_Type
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * Magento
  4:  *
  5:  * NOTICE OF LICENSE
  6:  *
  7:  * This source file is subject to the Open Software License (OSL 3.0)
  8:  * that is bundled with this package in the file LICENSE.txt.
  9:  * It is also available through the world-wide-web at this URL:
 10:  * http://opensource.org/licenses/osl-3.0.php
 11:  * If you did not receive a copy of the license and are unable to
 12:  * obtain it through the world-wide-web, please send an email
 13:  * to license@magentocommerce.com so we can send you a copy immediately.
 14:  *
 15:  * DISCLAIMER
 16:  *
 17:  * Do not edit or add to this file if you wish to upgrade Magento to newer
 18:  * versions in the future. If you wish to customize Magento for your
 19:  * needs please refer to http://www.magentocommerce.com for more information.
 20:  *
 21:  * @category    Mage
 22:  * @package     Mage_Bundle
 23:  * @copyright   Copyright (c) 2012 Magento Inc. (http://www.magentocommerce.com)
 24:  * @license     http://opensource.org/licenses/osl-3.0.php  Open Software License (OSL 3.0)
 25:  */
 26: 
 27: 
 28: /**
 29:  * Bundle Product Price Index Resource model
 30:  *
 31:  * @category    Mage
 32:  * @package     Mage_Bundle
 33:  * @author      Magento Core Team <core@magentocommerce.com>
 34:  */
 35: class Mage_Bundle_Model_Resource_Price_Index extends Mage_Core_Model_Resource_Db_Abstract
 36: {
 37:     /**
 38:      * EAV attributes cache
 39:      *
 40:      * @var array
 41:      */
 42:     protected $_attributes       = array();
 43: 
 44:     /**
 45:      * Websites cache
 46:      *
 47:      * @var array
 48:      */
 49:     protected $_websites;
 50: 
 51:     /**
 52:      * Customer Groups cache
 53:      *
 54:      * @var array
 55:      */
 56:     protected $_customerGroups;
 57: 
 58:     /**
 59:      * Initialize connection and define main table
 60:      *
 61:      */
 62:     protected function _construct()
 63:     {
 64:         $this->_init('bundle/price_index', 'entity_id');
 65:     }
 66: 
 67:     /**
 68:      * Retrieve attribute object
 69:      *
 70:      * @param string $attributeCode
 71:      * @return Mage_Catalog_Model_Resource_Eav_Attribute
 72:      */
 73:     protected function _getAttribute($attributeCode)
 74:     {
 75:         if (!isset($this->_attributes[$attributeCode])) {
 76:             $this->_attributes[$attributeCode] = Mage::getSingleton('catalog/config')
 77:                 ->getAttribute(Mage_Catalog_Model_Product::ENTITY, $attributeCode);
 78:         }
 79:         return $this->_attributes[$attributeCode];
 80:     }
 81: 
 82:     /**
 83:      * Retrieve websites collection array
 84:      *
 85:      * @return array
 86:      */
 87:     protected function _getWebsites()
 88:     {
 89:         if (is_null($this->_websites)) {
 90:             $this->_websites = Mage::app()->getWebsites(false);
 91:         }
 92:         return $this->_websites;
 93:     }
 94: 
 95:     /**
 96:      * Retrieve customer groups collection array
 97:      *
 98:      * @return array
 99:      */
100:     protected function _getCustomerGroups()
101:     {
102:         if (is_null($this->_customerGroups)) {
103:             $this->_customerGroups = array();
104:             foreach (Mage::getModel('customer/group')->getCollection() as $group) {
105:                 $this->_customerGroups[$group->getId()] = $group;
106:             }
107:         }
108:         return $this->_customerGroups;
109:     }
110: 
111:     /**
112:      * Retrieve product ids array by product condition
113:      *
114:      * @param Mage_Core_Model_Product|Mage_Catalog_Model_Product_Condition_Interface|array|int $product
115:      * @param int $lastEntityId
116:      * @param int $limit
117:      * @return array
118:      */
119:     public function getProducts($product = null, $lastEntityId = 0, $limit = 100)
120:     {
121: 
122:         $select = $this->_getReadAdapter()->select()
123:             ->from(
124:                 array('e' => $this->getTable('catalog/product')),
125:                 array('entity_id')
126:             )
127:             ->where('e.type_id=?', Mage_Catalog_Model_Product_Type::TYPE_BUNDLE);
128:         if ($product instanceof Mage_Catalog_Model_Product) {
129:             $select->where('e.entity_id=?', $product->getId());
130:         } elseif ($product instanceof Mage_Catalog_Model_Product_Condition_Interface) {
131:             $value = new Zend_Db_Expr($product->getIdsSelect($this->_getReadAdapter()));
132:             $select->where('e.entity_id IN(?)', $value);
133:         } elseif (is_numeric($product) || is_array($product)) {
134:             $select->where('e.entity_id IN(?)', $product);
135:         }
136: 
137:         $priceType = $this->_getAttribute('price_type');
138:         $priceTypeAlias = 't_' . $priceType->getAttributeCode();
139:         $joinConds = array(
140:             $priceTypeAlias . '.attribute_id=:attribute_id',
141:             $priceTypeAlias . '.store_id=0',
142:             $priceTypeAlias . '.entity_id=e.entity_id'
143:         );
144: 
145:         $select->joinLeft(
146:             array($priceTypeAlias => $priceType->getBackend()->getTable()),
147:             join(' AND ', $joinConds),
148:             array('price_type' => $priceTypeAlias . '.value')
149:         );
150: 
151:         $select->where('e.entity_id>:last_entity_id', $lastEntityId)
152:             ->order('e.entity_id')
153:             ->limit($limit);
154:         $bind = array(
155:             'attribute_id'   => $priceType->getAttributeId(),
156:             'last_entity_id' => $lastEntityId
157:         );
158:         return $this->_getReadAdapter()->fetchPairs($select, $bind);
159:     }
160: 
161:     /**
162:      * Reindex Bundle product Price Index
163:      *
164:      * @param Mage_Core_Model_Product|Mage_Catalog_Model_Product_Condition_Interface|array|int $products
165:      * @return Mage_Bundle_Model_Resource_Price_Index
166:      */
167:     public function reindex($products = null)
168:     {
169:         $lastEntityId = 0;
170:         while (true) {
171:             $productsData = $this->getProducts($products, $lastEntityId);
172:             if (!$productsData) {
173:                 break;
174:             }
175: 
176:             foreach ($productsData as $productId => $priceType) {
177:                 $this->_reindexProduct($productId, $priceType);
178:                 $lastEntityId = $productId;
179:             }
180:         }
181: 
182:         return $this;
183:     }
184: 
185:     /**
186:      * Reindex product price
187:      *
188:      * @param int $productId
189:      * @param int $priceType
190:      * @return Mage_Bundle_Model_Resource_Price_Index
191:      */
192:     protected function _reindexProduct($productId, $priceType)
193:     {
194:         $options = $this->getSelections($productId);
195:         $selectionProducts = array();
196:         foreach ($options as $option) {
197:             foreach ($option['selections'] as $selection) {
198:                 $selectionProducts[$selection['product_id']] = $selection['product_id'];
199:             }
200:         }
201: 
202:         $priceIndex = array();
203:         if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) {
204:             // load selection product prices from index for dynamic bundle
205:             $priceIndex = $this->getProductsPriceFromIndex($selectionProducts);
206:         }
207: 
208:         foreach ($this->_getWebsites() as $website) {
209:             if (!$website->getDefaultStore()) {
210:                 continue;
211:             }
212:             $salableStatus = $this->getProductsSalableStatus($selectionProducts, $website);
213:             $priceData = $this->getProductsPriceData($productId, $website);
214:             $priceData = $priceData[$productId];
215: 
216:             /* @var $website Mage_Core_Model_Website */
217:             foreach ($this->_getCustomerGroups() as $group) {
218:                 /* @var $group Mage_Customer_Model_Group */
219:                 if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) {
220:                     $basePrice     = $this->_getBasePrice($productId, $priceData, $website, $group);
221:                     $customOptions = $this->getCustomOptions($productId, $website);
222:                 } elseif ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) {
223:                     $basePrice = 0;
224:                 }
225: 
226:                 list($minPrice, $maxPrice) = $this->_calculateBundleSelections($options, $salableStatus,
227:                     $productId, $priceType, $basePrice, $priceData, $priceIndex, $website, $group
228:                 );
229: 
230:                 if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) {
231:                     list($minPrice, $maxPrice) =
232:                         $this->_calculateCustomOptions($customOptions, $basePrice, $minPrice, $maxPrice);
233:                 }
234: 
235:                 $this->_savePriceIndex($productId, $website->getId(), $group->getId(), $minPrice, $maxPrice);
236:             }
237:         }
238: 
239:         return $this;
240:     }
241: 
242:     /**
243:      * Save price index
244:      *
245:      * @param int $productId
246:      * @param int $websiteId
247:      * @param int $groupId
248:      * @param float $minPrice
249:      * @param float $maxPrice
250:      * @return Mage_Bundle_Model_Resource_Price_Index
251:      */
252:     protected function _savePriceIndex($productId, $websiteId, $groupId, $minPrice, $maxPrice)
253:     {
254:         $adapter = $this->_getWriteAdapter();
255:         $adapter->beginTransaction();
256:         $bind = array($productId, $websiteId, $groupId, $minPrice, $maxPrice);
257:         $adapter->insertOnDuplicate($this->getMainTable(), $bind, array('min_price', 'max_price'));
258:         $adapter->commit();
259: 
260:         return $this;
261:     }
262: 
263:     /**
264:      * Retrieve bundle options with selections and prices by product
265:      *
266:      * @param int $productId
267:      * @return array
268:      */
269:     public function getSelections($productId)
270:     {
271:         $options = array();
272:         $read = $this->_getReadAdapter();
273:         $select = $read->select()
274:             ->from(
275:                 array('option_table' => $this->getTable('bundle/option')),
276:                 array('option_id', 'required', 'type')
277:             )
278:             ->join(
279:                 array('selection_table' => $this->getTable('bundle/selection')),
280:                 'selection_table.option_id=option_table.option_id',
281:                 array('selection_id', 'product_id', 'selection_price_type',
282:                     'selection_price_value', 'selection_qty', 'selection_can_change_qty')
283:             )
284:             ->join(
285:                 array('e' => $this->getTable('catalog/product')),
286:                 'e.entity_id=selection_table.product_id AND e.required_options=0',
287:                 array()
288:             )
289:             ->where('option_table.parent_id=:product_id');
290: 
291:         $query = $read->query($select, array('product_id' => $productId));
292:         while ($row = $query->fetch()) {
293:             if (!isset($options[$row['option_id']])) {
294:                 $options[$row['option_id']] = array(
295:                     'option_id'     => $row['option_id'],
296:                     'required'      => $row['required'],
297:                     'type'          => $row['type'],
298:                     'selections'    => array()
299:                 );
300:             }
301:             $options[$row['option_id']]['selections'][$row['selection_id']] = array(
302:                 'selection_id'      => $row['selection_id'],
303:                 'product_id'        => $row['product_id'],
304:                 'price_type'        => $row['selection_price_type'],
305:                 'price_value'       => $row['selection_price_value'],
306:                 'qty'               => $row['selection_qty'],
307:                 'can_change_qty'    => $row['selection_can_change_qty']
308:             );
309:         }
310: 
311:         return $options;
312:     }
313: 
314:     /**
315:      * Retrieve salable product statuses
316:      *
317:      * @param int|array $products
318:      * @param Mage_Core_Model_Website $website
319:      * @return array
320:      */
321:     public function getProductsSalableStatus($products, Mage_Core_Model_Website $website)
322:     {
323:         $read = $this->_getReadAdapter();
324:         $productsData = array();
325:         $select = $read->select()
326:             ->from(array('e' => $this->getTable('catalog/product')), 'entity_id')
327:             ->where('e.entity_id IN(?)', $products);
328:         // add belong to website
329:         $select->joinLeft(
330:             array('pw' => $this->getTable('catalog/product_website')),
331:             'e.entity_id=pw.product_id AND pw.website_id=:website_id',
332:             array('pw.website_id')
333:         );
334: 
335:         $store = $website->getDefaultStore();
336: 
337:         // add product status
338:         $status = $this->_getAttribute('status');
339:         if ($status->isScopeGlobal()) {
340:             $select->join(
341:                 array('t_status' => $status->getBackend()->getTable()),
342:                 'e.entity_id=t_status.entity_id'
343:                 . ' AND t_status.attribute_id=:status_attribute_id'
344:                 . ' AND t_status.store_id=0',
345:                 array('status' => 't_status.value')
346:             );
347:         } else {
348: 
349:             $statusField = $read->getCheckSql(
350:                 't2_status.value_id > 0',
351:                 't2_status.value',
352:                 't1_status.value'
353:             );
354: 
355:             $statusTable = $status->getBackend()->getTable();
356:             $select->join(
357:                 array('t1_status' => $statusTable),
358:                 'e.entity_id=t1_status.entity_id'
359:                 . ' AND t1_status.attribute_id=:status_attribute_id'
360:                 . ' AND t1_status.store_id=0',
361:                 array('status' => $statusField)
362:             )
363:             ->joinLeft(
364:                 array('t2_status' => $statusTable),
365:                 't1_status.entity_id = t2_status.entity_id'
366:                 . ' AND t1_status.attribute_id = t2_status.attribute_id'
367:                 . ' AND t2_status.store_id=:store_id',
368:                 array()
369:             );
370:         }
371: 
372:         $bind = array(
373:             'status_attribute_id' => $status->getAttributeId(),
374:             'website_id'   => $website->getId(),
375:             'store_id'     => $store->getId()
376:         );
377: 
378:         Mage::dispatchEvent('catalog_product_prepare_index_select', array(
379:             'website'   => $website,
380:             'select'    => $select,
381:             'bind'      => $bind
382:         ));
383: 
384:         $query = $read->query($select, $bind);
385:         while ($row = $query->fetch()) {
386:             $salable = isset($row['salable']) ? $row['salable'] : true;
387:             $website = $row['website_id'] > 0 ? true : false;
388:             $status  = $row['status'];
389: 
390:             $productsData[$row['entity_id']] = $salable && $status && $website;
391:         }
392: 
393:         return $productsData;
394:     }
395: 
396:     /**
397:      * Retrieve Selection Product price from Price Index
398:      * Return index key {entity_id}-{website_id}-{customer_group_id}
399:      *
400:      * @param int|array $productIds
401:      * @return array
402:      */
403:     public function getProductsPriceFromIndex($productIds)
404:     {
405:         $price  = $this->_getAttribute('price');
406:         $read = $this->_getReadAdapter();
407:         $key = $read->getConcatSql(array('entity_id', 'customer_group_id', 'website_id'), '-');
408: 
409:         $select = $read->select()
410:             ->from(
411:                 array('price_index' => $this->getTable('catalogindex/price')),
412:                 array('index_key' => $key, 'value')
413:             )
414:             ->where('entity_id IN(?)', $productIds)
415:             ->where('attribute_id= :attribute_id');
416:         $index = $read->fetchPairs($select, array('attribute_id' => $price->getAttributeId()));
417:         return $index;
418:     }
419: 
420:     /**
421:      * Retrieve product(s) price data
422:      *
423:      * @param int|array $products
424:      * @param Mage_Core_Model_Website $website
425:      * @return array
426:      */
427:     public function getProductsPriceData($products, Mage_Core_Model_Website $website)
428:     {
429:         $productsData = array();
430:         $read = $this->_getReadAdapter();
431:         $select = $read->select()
432:             ->from(array('e' => $this->getTable('catalog/product')), 'entity_id')
433:             ->where('e.entity_id IN(?)', $products);
434: 
435:         $this->_addAttributeDataToSelect($select, 'price', $website);
436:         $this->_addAttributeDataToSelect($select, 'special_price', $website);
437:         $this->_addAttributeDataToSelect($select, 'special_from_date', $website);
438:         $this->_addAttributeDataToSelect($select, 'special_to_date', $website);
439: 
440:         $query = $read->query($select);
441:         while ($row = $query->fetch()) {
442:             $productsData[$row['entity_id']] = array(
443:                 'price'             => $row['price'],
444:                 'special_price'     => $row['special_price'],
445:                 'special_from_date' => $row['special_from_date'],
446:                 'special_to_date'   => $row['special_to_date']
447:             );
448:         }
449: 
450:         return $productsData;
451:     }
452: 
453:     /**
454:      * Add attribute data to select
455:      *
456:      * @param Varien_Db_Select $select
457:      * @param string $attributeCode
458:      * @param Mage_Core_Model_Website $website
459:      * @return Mage_Bundle_Model_Resource_Price_Index
460:      */
461:     protected function _addAttributeDataToSelect(Varien_Db_Select $select, $attributeCode,
462:         Mage_Core_Model_Website $website)
463:     {
464:         $attribute  = $this->_getAttribute($attributeCode);
465:         $store      = $website->getDefaultStore();
466:         if ($attribute->isScopeGlobal()) {
467:             $table = 't_' . $attribute->getAttributeCode();
468:             $select->joinLeft(
469:                 array($table => $attribute->getBackend()->getTable()),
470:                 "e.entity_id={$table}.entity_id"
471:                 . " AND {$table}.attribute_id={$attribute->getAttributeId()}"
472:                 . " AND {$table}.store_id=0",
473:                 array($attribute->getAttributeCode() => $table . '.value')
474:             );
475:         } else {
476:             $tableName   = $attribute->getBackend()->getTable();
477:             $tableGlobal = 't1_' . $attribute->getAttributeCode();
478:             $tableStore  = 't2_' . $attribute->getAttributeCode();
479: 
480:             $attributeCond = $this->getReadConnection()->getCheckSql(
481:                 $tableStore . '.value_id > 0',
482:                 $tableStore . '.value',
483:                 $tableGlobal . '.value'
484:             );
485:             $select->joinLeft(
486:                 array($tableGlobal => $tableName),
487:                 "e.entity_id = {$tableGlobal}.entity_id"
488:                 . " AND {$tableGlobal}.attribute_id = {$attribute->getAttributeId()}"
489:                 . " AND {$tableGlobal}.store_id = 0",
490:                 array($attribute->getAttributeCode() => $attributeCond)
491:             )
492:             ->joinLeft(
493:                 array($tableStore => $tableName),
494:                 "{$tableGlobal}.entity_id = {$tableStore}.entity_id"
495:                 . " AND {$tableGlobal}.attribute_id = {$tableStore}.attribute_id"
496:                 . " AND {$tableStore}.store_id = " . $store->getId(),
497:                 array()
498:             );
499:         }
500:         return $this;
501:     }
502: 
503:     /**
504:      * Retrieve fixed bundle base price (with special price and rules)
505:      *
506:      * @param int $productId
507:      * @param array $priceData
508:      * @param Mage_Core_Model_Website $website
509:      * @param Mage_Customer_Model_Group $customerGroup
510:      * @return float
511:      */
512:     protected function _getBasePrice($productId, array $priceData, $website, $customerGroup)
513:     {
514:         $store          = $website->getDefaultStore();
515:         $storeTimeStamp = Mage::app()->getLocale()->storeTimeStamp($store);
516:         $finalPrice     = $this->_calculateSpecialPrice($priceData['price'], $priceData, $website);
517: 
518:         $rulePrice = Mage::getResourceModel('catalogrule/rule')
519:             ->getRulePrice($storeTimeStamp, $website->getId(), $customerGroup->getId(), $productId);
520: 
521:         if ($rulePrice !== null && $rulePrice !== false) {
522:             $finalPrice = min($finalPrice, $rulePrice);
523:         }
524: 
525:         return $finalPrice;
526:     }
527: 
528:     /**
529:      * Retrieve custom options for product
530:      *
531:      * @param int $productId
532:      * @param Mage_Core_Model_Website $website
533:      * @return array
534:      */
535:     public function getCustomOptions($productId, Mage_Core_Model_Website $website)
536:     {
537:         $options = array();
538:         $store   = $website->getDefaultStore();
539:         $price   = $this->_getAttribute('price');
540:         $adapter = $this->_getReadAdapter();
541: 
542:         $bind = array(
543:             ':product_id' => $productId,
544:             ':store_id'   => $store->getId(),
545: 
546:         );
547:         $select = $adapter->select()
548:             ->from(
549:                 array('option_table' => $this->getTable('catalog/product_option')),
550:                 array('option_id', 'is_require', 'type')
551:             )
552:             ->where('option_table.product_id=:product_id');
553: 
554:         if ($price->isScopeGlobal()) {
555:             $select->join(
556:                 array('price_table' => $this->getTable('catalog/product_option_price')),
557:                 'option_table.option_id = price_table.option_id' .
558:                 ' AND price_table.store_id = 0',
559:                 array('value_id' => 'option_price_id', 'price', 'price_type')
560:             );
561:         } else {
562:             $valueIdCond = $adapter->getCheckSql(
563:                 'price_store_table.option_price_id IS NOT NULL',
564:                 'price_store_table.option_price_id',
565:                 'price_global_table.option_price_id'
566:             );
567:             $priceCond = $adapter->getCheckSql(
568:                 'price_store_table.price IS NOT NULL',
569:                 'price_store_table.price',
570:                 'price_global_table.price'
571:             );
572:             $priceTypeCond = $adapter->getCheckSql(
573:                 'price_store_table.price_type IS NOT NULL',
574:                 'price_store_table.price_type',
575:                 'price_global_table.price_type'
576:             );
577: 
578:             $select
579:                 ->join(
580:                     array('price_global_table' => $this->getTable('catalog/product_option_price')),
581:                     'option_table.option_id=price_global_table.option_id' .
582:                     ' AND price_global_table.store_id=0',
583:                     array(
584:                         'value_id'   => $valueIdCond,
585:                         'price'      => $priceCond,
586:                         'price_type' => $priceTypeCond
587:                     ))
588:                 ->joinLeft(
589:                     array('price_store_table' => $this->getTable('catalog/product_option_price')),
590:                     'option_table.option_id = price_store_table.option_id' .
591:                     ' AND price_store_table.store_id=:store_id',
592:                     array()
593:                 );
594:         }
595: 
596:         $query = $adapter->query($select, $bind);
597:         while ($row = $query->fetch()) {
598:             if (!isset($options[$row['option_id']])) {
599:                 $options[$row['option_id']] = array(
600:                     'option_id'     => $row['option_id'],
601:                     'is_require'    => $row['is_require'],
602:                     'type'          => $row['type'],
603:                     'values'        => array()
604:                 );
605:             }
606:             $options[$row['option_id']]['values'][$row['value_id']] = array(
607:                 'price_type'        => $row['price_type'],
608:                 'price_value'       => $row['price']
609:             );
610:         }
611: 
612:         $select = $adapter->select()
613:             ->from(
614:                 array('option_table' => $this->getTable('catalog/product_option')),
615:                 array('option_id', 'is_require', 'type')
616:             )
617:             ->join(
618:                 array('type_table' => $this->getTable('catalog/product_option_type_value')),
619:                 'option_table.option_id=type_table.option_id',
620:                 array()
621:             )
622:             ->where('option_table.product_id=:product_id');
623: 
624:         if ($price->isScopeGlobal()) {
625:             $select->join(
626:                 array('price_table' => $this->getTable('catalog/product_option_type_price')),
627:                 'type_table.option_type_id=price_table.option_type_id' .
628:                 ' AND price_table.store_id=0',
629:                 array('value_id' => 'option_type_id', 'price', 'price_type')
630:             );
631:         } else {
632:             $select
633:                 ->join(
634:                     array('price_global_table' => $this->getTable('catalog/product_option_type_price')),
635:                     'type_table.option_type_id=price_global_table.option_type_id' .
636:                     ' AND price_global_table.store_id=0',
637:                     array(
638:                         'value_id'   => $valueIdCond,
639:                         'price'      => $priceCond,
640:                         'price_type' => $priceTypeCond
641:                     )
642:                 )
643:                 ->joinLeft(
644:                     array('price_store_table' => $this->getTable('catalog/product_option_type_price')),
645:                     'type_table.option_type_id=price_store_table.option_type_id' .
646:                     ' AND price_store_table.store_id=:store_id',
647:                     array()
648:                 );
649:         }
650: 
651:         $query = $adapter->query($select, $bind);
652:         while ($row = $query->fetch()) {
653:             if (!isset($options[$row['option_id']])) {
654:                 $options[$row['option_id']] = array(
655:                     'option_id'     => $row['option_id'],
656:                     'is_require'    => $row['is_require'],
657:                     'type'          => $row['type'],
658:                     'values'        => array()
659:                 );
660:             }
661:             $options[$row['option_id']]['values'][$row['value_id']] = array(
662:                 'price_type'        => $row['price_type'],
663:                 'price_value'       => $row['price']
664:             );
665:         }
666: 
667:         return $options;
668:     }
669: 
670:     /**
671:      * Calculate custom options price
672:      * Return array with indexes(0 -> min_price, 1 -> max_price)
673:      *
674:      * @param array $options
675:      * @param float $basePrice
676:      * @param float $minPrice
677:      * @param float $maxPrice
678:      * @return array
679:      */
680:     public function _calculateCustomOptions(array $options, $basePrice, $minPrice, $maxPrice)
681:     {
682:         foreach ($options as $option) {
683:             $optionPrices = array();
684:             foreach ($option['values'] as $value) {
685:                 if ($value['price_type'] == 'percent') {
686:                     $valuePrice = $basePrice * $value['price_value'] / 100;
687:                 } else {
688:                     $valuePrice = $value['price_value'];
689:                 }
690:                 $optionPrices[] = $valuePrice;
691:             }
692:             if ($option['is_require']) {
693:                 $minPrice += min($optionPrices);
694:             }
695:             $multiTypes = array(
696:                 Mage_Catalog_Model_Product_Option::OPTION_TYPE_DROP_DOWN,
697:                 Mage_Catalog_Model_Product_Option::OPTION_TYPE_CHECKBOX,
698:                 Mage_Catalog_Model_Product_Option::OPTION_TYPE_MULTIPLE
699:             );
700:             if ($optionPrices) {
701:                 if (in_array($option['type'], $multiTypes)) {
702:                     $maxPrice += array_sum($optionPrices);
703:                 } else {
704:                     $maxPrice += max($optionPrices);
705:                 }
706:             }
707:         }
708: 
709:         return array($minPrice, $maxPrice);
710:     }
711: 
712:     /**
713:      * Calculate minimal and maximal price for bundle selections
714:      * Return array with prices (0 -> min_price, 1 -> max_price)
715:      *
716:      * @param array $options
717:      * @param array $salableStatus
718:      * @param int $productId
719:      * @param int $priceType
720:      * @param float $basePrice
721:      * @param array $priceData
722:      * @param array $priceIndex
723:      * @param Mage_Core_Model_Website $website
724:      * @param Mage_Customer_Model_Group $group
725:      * @return array
726:      */
727:     public function _calculateBundleSelections(array $options, array $salableStatus, $productId, $priceType, $basePrice,
728:         $priceData, $priceIndex, $website, $group)
729:     {
730:         $minPrice = $maxPrice = $basePrice;
731:         $optPrice = 0;
732: 
733:         foreach ($options as $option) {
734:             $optionPrices = array();
735:             foreach ($option['selections'] as $selection) {
736:                 if (!$selection['product_id']) {
737:                     continue;
738:                 }
739: 
740:                 if (!$salableStatus[$selection['product_id']]) {
741:                     continue;
742:                 }
743: 
744:                 if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_FIXED) {
745:                     $basePrice = $this->_getBasePrice($productId, $priceData, $website, $group);
746:                 }
747: 
748:                 // calculate selection price
749:                 if ($priceType == Mage_Bundle_Model_Product_Price::PRICE_TYPE_DYNAMIC) {
750:                     $priceIndexKey = join('-', array(
751:                         $selection['product_id'],
752:                         $website->getId(),
753:                         $group->getId()
754:                     ));
755: 
756:                     $selectionPrice = isset($priceIndex[$priceIndexKey]) ? $priceIndex[$priceIndexKey] : 0;
757:                     $selectionPrice = $this->_calculateSpecialPrice($selectionPrice, $priceData, $website);
758:                 } else {
759:                     if ($selection['price_type']) { // percent
760:                         $selectionPrice = $basePrice * $selection['price_value'] / 100;
761:                     } else {
762:                         $selectionPrice = $this->_calculateSpecialPrice($selection['price_value'],
763:                         $priceData, $website);
764:                     }
765:                 }
766: 
767:                 // calculate selection qty
768:                 if ($selection['can_change_qty'] && $option['type'] != 'multi' && $option['type'] != 'checkbox') {
769:                     $qty = 1;
770:                 } else {
771:                     $qty = $selection['qty'];
772:                 }
773: 
774:                 $selectionPrice = $selectionPrice * $qty;
775:                 $optionPrices[$selection['selection_id']] = $selectionPrice;
776:             }
777: 
778:             if ($optionPrices) {
779:                 if ($option['required']) {
780:                     $minPrice += min($optionPrices);
781:                 } else {
782:                     $optPrice = $optPrice && $optPrice < min($optionPrices) ? $optPrice : min($optionPrices);
783:                 }
784:                 if (in_array($option['type'], array('multi', 'checkbox'))) {
785:                     $maxPrice += array_sum($optionPrices);
786:                 } else {
787:                     $maxPrice += max($optionPrices);
788:                 }
789:             }
790:         }
791: 
792:         if ($minPrice == 0) {
793:             $minPrice = $optPrice;
794:         }
795:         return array($minPrice, $maxPrice);
796:     }
797: 
798:     /**
799:      * Apply special price
800:      *
801:      * @param float $finalPrice
802:      * @param array $priceData
803:      * @param Mage_Core_Model_Website $website
804:      * @return float
805:      */
806:     public function _calculateSpecialPrice($finalPrice, array $priceData, Mage_Core_Model_Website $website)
807:     {
808:         $store              = $website->getDefaultStore();
809:         $specialPrice       = $priceData['special_price'];
810: 
811:         if (!is_null($specialPrice) && $specialPrice != false) {
812:             if (Mage::app()->getLocale()->isStoreDateInInterval($store, $priceData['special_from_date'],
813:             $priceData['special_to_date'])) {
814:                 $specialPrice   = ($finalPrice * $specialPrice) / 100;
815:                 $finalPrice     = min($finalPrice, $specialPrice);
816:             }
817:         }
818: 
819:         return $finalPrice;
820:     }
821: 
822:     /**
823:      * Retrieve price index for products
824:      *
825:      * @param int|array $productIds
826:      * @param int $websiteId
827:      * @param int $groupId
828:      * @return array
829:      */
830:     public function loadPriceIndex($productIds, $websiteId, $groupId)
831:     {
832:         $prices = array();
833:         $adapter = $this->_getReadAdapter();
834:         $select = $adapter->select()
835:             ->from(
836:                 array('pi' => $this->getMainTable()),
837:                 array('entity_id', 'min_price', 'max_price')
838:             )
839:             ->where('entity_id IN(?)', $productIds)
840:             ->where('website_id=:website_id')
841:             ->where('customer_group_id=:group_id');
842:         $bind = array(
843:             'website_id' => $websiteId,
844:             'group_id'   => $groupId
845:         );
846:         $query = $adapter->query($select, $bind);
847:         while ($row = $query->fetch()) {
848:             $prices[$row['entity_id']] = array(
849:                 'min_price' => $row['min_price'],
850:                 'max_price' => $row['max_price']
851:             );
852:         }
853: 
854:         return $prices;
855:     }
856: }
857: 
Magento 1.7.0.2 API documentation generated by ApiGen 2.8.0