Price.php 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724
  1. <?php
  2. /**
  3. * Copyright © Magento, Inc. All rights reserved.
  4. * See COPYING.txt for license details.
  5. */
  6. namespace Magento\Bundle\Model\ResourceModel\Indexer;
  7. use Magento\Catalog\Api\Data\ProductInterface;
  8. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\BasePriceModifier;
  9. use Magento\Framework\Indexer\DimensionalIndexerInterface;
  10. use Magento\Framework\EntityManager\MetadataPool;
  11. use Magento\Catalog\Model\Indexer\Product\Price\TableMaintainer;
  12. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructureFactory;
  13. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\IndexTableStructure;
  14. use Magento\Catalog\Model\ResourceModel\Product\Indexer\Price\Query\JoinAttributeProcessor;
  15. use Magento\Customer\Model\Indexer\CustomerGroupDimensionProvider;
  16. use Magento\Store\Model\Indexer\WebsiteDimensionProvider;
  17. use Magento\Catalog\Model\Product\Attribute\Source\Status;
  18. /**
  19. * Bundle products Price indexer resource model
  20. *
  21. * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
  22. */
  23. class Price implements DimensionalIndexerInterface
  24. {
  25. /**
  26. * @var IndexTableStructureFactory
  27. */
  28. private $indexTableStructureFactory;
  29. /**
  30. * @var TableMaintainer
  31. */
  32. private $tableMaintainer;
  33. /**
  34. * @var MetadataPool
  35. */
  36. private $metadataPool;
  37. /**
  38. * @var \Magento\Framework\App\ResourceConnection
  39. */
  40. private $resource;
  41. /**
  42. * @var bool
  43. */
  44. private $fullReindexAction;
  45. /**
  46. * @var string
  47. */
  48. private $connectionName;
  49. /**
  50. * @var \Magento\Framework\DB\Adapter\AdapterInterface
  51. */
  52. private $connection;
  53. /**
  54. * Mapping between dimensions and field in database
  55. *
  56. * @var array
  57. */
  58. private $dimensionToFieldMapper = [
  59. WebsiteDimensionProvider::DIMENSION_NAME => 'pw.website_id',
  60. CustomerGroupDimensionProvider::DIMENSION_NAME => 'cg.customer_group_id',
  61. ];
  62. /**
  63. * @var BasePriceModifier
  64. */
  65. private $basePriceModifier;
  66. /**
  67. * @var JoinAttributeProcessor
  68. */
  69. private $joinAttributeProcessor;
  70. /**
  71. * @var \Magento\Framework\Event\ManagerInterface
  72. */
  73. private $eventManager;
  74. /**
  75. * @var \Magento\Framework\Module\Manager
  76. */
  77. private $moduleManager;
  78. /**
  79. * @param IndexTableStructureFactory $indexTableStructureFactory
  80. * @param TableMaintainer $tableMaintainer
  81. * @param MetadataPool $metadataPool
  82. * @param \Magento\Framework\App\ResourceConnection $resource
  83. * @param BasePriceModifier $basePriceModifier
  84. * @param JoinAttributeProcessor $joinAttributeProcessor
  85. * @param \Magento\Framework\Event\ManagerInterface $eventManager
  86. * @param \Magento\Framework\Module\Manager $moduleManager
  87. * @param bool $fullReindexAction
  88. * @param string $connectionName
  89. *
  90. * @SuppressWarnings(PHPMD.ExcessiveParameterList)
  91. */
  92. public function __construct(
  93. IndexTableStructureFactory $indexTableStructureFactory,
  94. TableMaintainer $tableMaintainer,
  95. MetadataPool $metadataPool,
  96. \Magento\Framework\App\ResourceConnection $resource,
  97. BasePriceModifier $basePriceModifier,
  98. JoinAttributeProcessor $joinAttributeProcessor,
  99. \Magento\Framework\Event\ManagerInterface $eventManager,
  100. \Magento\Framework\Module\Manager $moduleManager,
  101. $fullReindexAction = false,
  102. $connectionName = 'indexer'
  103. ) {
  104. $this->indexTableStructureFactory = $indexTableStructureFactory;
  105. $this->tableMaintainer = $tableMaintainer;
  106. $this->connectionName = $connectionName;
  107. $this->metadataPool = $metadataPool;
  108. $this->resource = $resource;
  109. $this->fullReindexAction = $fullReindexAction;
  110. $this->basePriceModifier = $basePriceModifier;
  111. $this->joinAttributeProcessor = $joinAttributeProcessor;
  112. $this->eventManager = $eventManager;
  113. $this->moduleManager = $moduleManager;
  114. }
  115. /**
  116. * {@inheritdoc}
  117. * @param array $dimensions
  118. * @param \Traversable $entityIds
  119. * @throws \Exception
  120. */
  121. public function executeByDimensions(array $dimensions, \Traversable $entityIds)
  122. {
  123. $this->tableMaintainer->createMainTmpTable($dimensions);
  124. $temporaryPriceTable = $this->indexTableStructureFactory->create([
  125. 'tableName' => $this->tableMaintainer->getMainTmpTable($dimensions),
  126. 'entityField' => 'entity_id',
  127. 'customerGroupField' => 'customer_group_id',
  128. 'websiteField' => 'website_id',
  129. 'taxClassField' => 'tax_class_id',
  130. 'originalPriceField' => 'price',
  131. 'finalPriceField' => 'final_price',
  132. 'minPriceField' => 'min_price',
  133. 'maxPriceField' => 'max_price',
  134. 'tierPriceField' => 'tier_price',
  135. ]);
  136. $entityIds = iterator_to_array($entityIds);
  137. $this->prepareTierPriceIndex($dimensions, $entityIds);
  138. $this->prepareBundlePriceTable();
  139. $this->prepareBundlePriceByType(
  140. \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED,
  141. $dimensions,
  142. $entityIds
  143. );
  144. $this->prepareBundlePriceByType(
  145. \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC,
  146. $dimensions,
  147. $entityIds
  148. );
  149. $this->calculateBundleOptionPrice($temporaryPriceTable, $dimensions);
  150. $this->basePriceModifier->modifyPrice($temporaryPriceTable, $entityIds);
  151. }
  152. /**
  153. * Retrieve temporary price index table name for fixed bundle products
  154. *
  155. * @return string
  156. */
  157. private function getBundlePriceTable()
  158. {
  159. return $this->getTable('catalog_product_index_price_bundle_tmp');
  160. }
  161. /**
  162. * Retrieve table name for temporary bundle selection prices index
  163. *
  164. * @return string
  165. */
  166. private function getBundleSelectionTable()
  167. {
  168. return $this->getTable('catalog_product_index_price_bundle_sel_tmp');
  169. }
  170. /**
  171. * Retrieve table name for temporary bundle option prices index
  172. *
  173. * @return string
  174. */
  175. private function getBundleOptionTable()
  176. {
  177. return $this->getTable('catalog_product_index_price_bundle_opt_tmp');
  178. }
  179. /**
  180. * Prepare temporary price index table for fixed bundle products
  181. *
  182. * @return $this
  183. */
  184. private function prepareBundlePriceTable()
  185. {
  186. $this->getConnection()->delete($this->getBundlePriceTable());
  187. return $this;
  188. }
  189. /**
  190. * Prepare table structure for temporary bundle selection prices index
  191. *
  192. * @return $this
  193. */
  194. private function prepareBundleSelectionTable()
  195. {
  196. $this->getConnection()->delete($this->getBundleSelectionTable());
  197. return $this;
  198. }
  199. /**
  200. * Prepare table structure for temporary bundle option prices index
  201. *
  202. * @return $this
  203. */
  204. private function prepareBundleOptionTable()
  205. {
  206. $this->getConnection()->delete($this->getBundleOptionTable());
  207. return $this;
  208. }
  209. /**
  210. * Prepare temporary price index data for bundle products by price type
  211. *
  212. * @param int $priceType
  213. * @param array $dimensions
  214. * @param int|array $entityIds the entity ids limitation
  215. * @return void
  216. * @throws \Exception
  217. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  218. */
  219. private function prepareBundlePriceByType($priceType, array $dimensions, $entityIds = null)
  220. {
  221. $connection = $this->getConnection();
  222. $select = $connection->select()->from(
  223. ['e' => $this->getTable('catalog_product_entity')],
  224. ['entity_id']
  225. )->joinInner(
  226. ['cg' => $this->getTable('customer_group')],
  227. array_key_exists(CustomerGroupDimensionProvider::DIMENSION_NAME, $dimensions)
  228. ? sprintf(
  229. '%s = %s',
  230. $this->dimensionToFieldMapper[CustomerGroupDimensionProvider::DIMENSION_NAME],
  231. $dimensions[CustomerGroupDimensionProvider::DIMENSION_NAME]->getValue()
  232. ) : '',
  233. ['customer_group_id']
  234. )->joinInner(
  235. ['pw' => $this->getTable('catalog_product_website')],
  236. 'pw.product_id = e.entity_id',
  237. ['pw.website_id']
  238. )->joinInner(
  239. ['cwd' => $this->getTable('catalog_product_index_website')],
  240. 'pw.website_id = cwd.website_id',
  241. []
  242. );
  243. $select->joinLeft(
  244. ['tp' => $this->getTable('catalog_product_index_tier_price')],
  245. 'tp.entity_id = e.entity_id AND tp.website_id = pw.website_id' .
  246. ' AND tp.customer_group_id = cg.customer_group_id',
  247. []
  248. )->where(
  249. 'e.type_id=?',
  250. \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE
  251. );
  252. foreach ($dimensions as $dimension) {
  253. if (!isset($this->dimensionToFieldMapper[$dimension->getName()])) {
  254. throw new \LogicException(
  255. 'Provided dimension is not valid for Price indexer: ' . $dimension->getName()
  256. );
  257. }
  258. $select->where($this->dimensionToFieldMapper[$dimension->getName()] . ' = ?', $dimension->getValue());
  259. }
  260. $this->joinAttributeProcessor->process($select, 'status', Status::STATUS_ENABLED);
  261. if ($this->moduleManager->isEnabled('Magento_Tax')) {
  262. $taxClassId = $this->joinAttributeProcessor->process($select, 'tax_class_id');
  263. } else {
  264. $taxClassId = new \Zend_Db_Expr('0');
  265. }
  266. if ($priceType == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC) {
  267. $select->columns(['tax_class_id' => new \Zend_Db_Expr('0')]);
  268. } else {
  269. $select->columns(
  270. ['tax_class_id' => $connection->getCheckSql($taxClassId . ' IS NOT NULL', $taxClassId, 0)]
  271. );
  272. }
  273. $this->joinAttributeProcessor->process($select, 'price_type', $priceType);
  274. $price = $this->joinAttributeProcessor->process($select, 'price');
  275. $specialPrice = $this->joinAttributeProcessor->process($select, 'special_price');
  276. $specialFrom = $this->joinAttributeProcessor->process($select, 'special_from_date');
  277. $specialTo = $this->joinAttributeProcessor->process($select, 'special_to_date');
  278. $currentDate = new \Zend_Db_Expr('cwd.website_date');
  279. $specialFromDate = $connection->getDatePartSql($specialFrom);
  280. $specialToDate = $connection->getDatePartSql($specialTo);
  281. $specialFromExpr = "{$specialFrom} IS NULL OR {$specialFromDate} <= {$currentDate}";
  282. $specialToExpr = "{$specialTo} IS NULL OR {$specialToDate} >= {$currentDate}";
  283. $specialExpr = "{$specialPrice} IS NOT NULL AND {$specialPrice} > 0 AND {$specialPrice} < 100"
  284. . " AND {$specialFromExpr} AND {$specialToExpr}";
  285. $tierExpr = new \Zend_Db_Expr('tp.min_price');
  286. if ($priceType == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED) {
  287. $specialPriceExpr = $connection->getCheckSql(
  288. $specialExpr,
  289. 'ROUND(' . $price . ' * (' . $specialPrice . ' / 100), 4)',
  290. 'NULL'
  291. );
  292. $tierPrice = $connection->getCheckSql(
  293. $tierExpr . ' IS NOT NULL',
  294. 'ROUND((1 - ' . $tierExpr . ' / 100) * ' . $price . ', 4)',
  295. 'NULL'
  296. );
  297. $finalPrice = $connection->getLeastSql([
  298. $price,
  299. $connection->getIfNullSql($specialPriceExpr, $price),
  300. $connection->getIfNullSql($tierPrice, $price),
  301. ]);
  302. } else {
  303. $finalPrice = new \Zend_Db_Expr('0');
  304. $tierPrice = $connection->getCheckSql($tierExpr . ' IS NOT NULL', '0', 'NULL');
  305. }
  306. $select->columns(
  307. [
  308. 'price_type' => new \Zend_Db_Expr($priceType),
  309. 'special_price' => $connection->getCheckSql($specialExpr, $specialPrice, '0'),
  310. 'tier_percent' => $tierExpr,
  311. 'orig_price' => $connection->getIfNullSql($price, '0'),
  312. 'price' => $finalPrice,
  313. 'min_price' => $finalPrice,
  314. 'max_price' => $finalPrice,
  315. 'tier_price' => $tierPrice,
  316. 'base_tier' => $tierPrice,
  317. ]
  318. );
  319. if ($entityIds !== null) {
  320. $select->where('e.entity_id IN(?)', $entityIds);
  321. }
  322. /**
  323. * Add additional external limitation
  324. */
  325. $this->eventManager->dispatch(
  326. 'catalog_product_prepare_index_select',
  327. [
  328. 'select' => $select,
  329. 'entity_field' => new \Zend_Db_Expr('e.entity_id'),
  330. 'website_field' => new \Zend_Db_Expr('pw.website_id'),
  331. 'store_field' => new \Zend_Db_Expr('cwd.default_store_id')
  332. ]
  333. );
  334. $query = $select->insertFromSelect($this->getBundlePriceTable());
  335. $connection->query($query);
  336. }
  337. /**
  338. * Calculate fixed bundle product selections price
  339. *
  340. * @param IndexTableStructure $priceTable
  341. * @param array $dimensions
  342. *
  343. * @return void
  344. * @throws \Exception
  345. */
  346. private function calculateBundleOptionPrice($priceTable, $dimensions)
  347. {
  348. $connection = $this->getConnection();
  349. $this->prepareBundleSelectionTable();
  350. $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED);
  351. $this->calculateBundleSelectionPrice($dimensions, \Magento\Bundle\Model\Product\Price::PRICE_TYPE_DYNAMIC);
  352. $this->prepareBundleOptionTable();
  353. $select = $connection->select()->from(
  354. $this->getBundleSelectionTable(),
  355. ['entity_id', 'customer_group_id', 'website_id', 'option_id']
  356. )->group(
  357. ['entity_id', 'customer_group_id', 'website_id', 'option_id']
  358. );
  359. $minPrice = $connection->getCheckSql('is_required = 1', 'price', 'NULL');
  360. $tierPrice = $connection->getCheckSql('is_required = 1', 'tier_price', 'NULL');
  361. $select->columns(
  362. [
  363. 'min_price' => new \Zend_Db_Expr('MIN(' . $minPrice . ')'),
  364. 'alt_price' => new \Zend_Db_Expr('MIN(price)'),
  365. 'max_price' => $connection->getCheckSql('group_type = 0', 'MAX(price)', 'SUM(price)'),
  366. 'tier_price' => new \Zend_Db_Expr('MIN(' . $tierPrice . ')'),
  367. 'alt_tier_price' => new \Zend_Db_Expr('MIN(tier_price)'),
  368. ]
  369. );
  370. $query = $select->insertFromSelect($this->getBundleOptionTable());
  371. $connection->query($query);
  372. $this->getConnection()->delete($priceTable->getTableName());
  373. $this->applyBundlePrice($priceTable);
  374. $this->applyBundleOptionPrice($priceTable);
  375. }
  376. /**
  377. * Calculate bundle product selections price by product type
  378. *
  379. * @param array $dimensions
  380. * @param int $priceType
  381. * @return void
  382. * @throws \Exception
  383. * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
  384. */
  385. private function calculateBundleSelectionPrice($dimensions, $priceType)
  386. {
  387. $connection = $this->getConnection();
  388. if ($priceType == \Magento\Bundle\Model\Product\Price::PRICE_TYPE_FIXED) {
  389. $selectionPriceValue = $connection->getCheckSql(
  390. 'bsp.selection_price_value IS NULL',
  391. 'bs.selection_price_value',
  392. 'bsp.selection_price_value'
  393. );
  394. $selectionPriceType = $connection->getCheckSql(
  395. 'bsp.selection_price_type IS NULL',
  396. 'bs.selection_price_type',
  397. 'bsp.selection_price_type'
  398. );
  399. $priceExpr = new \Zend_Db_Expr(
  400. $connection->getCheckSql(
  401. $selectionPriceType . ' = 1',
  402. 'ROUND(i.price * (' . $selectionPriceValue . ' / 100),4)',
  403. $connection->getCheckSql(
  404. 'i.special_price > 0 AND i.special_price < 100',
  405. 'ROUND(' . $selectionPriceValue . ' * (i.special_price / 100),4)',
  406. $selectionPriceValue
  407. )
  408. ) . '* bs.selection_qty'
  409. );
  410. $tierExpr = $connection->getCheckSql(
  411. 'i.base_tier IS NOT NULL',
  412. $connection->getCheckSql(
  413. $selectionPriceType . ' = 1',
  414. 'ROUND(i.base_tier - (i.base_tier * (' . $selectionPriceValue . ' / 100)),4)',
  415. $connection->getCheckSql(
  416. 'i.tier_percent > 0',
  417. 'ROUND((1 - i.tier_percent / 100) * ' . $selectionPriceValue . ',4)',
  418. $selectionPriceValue
  419. )
  420. ) . ' * bs.selection_qty',
  421. 'NULL'
  422. );
  423. $priceExpr = $connection->getLeastSql([
  424. $priceExpr,
  425. $connection->getIfNullSql($tierExpr, $priceExpr),
  426. ]);
  427. } else {
  428. $price = 'idx.min_price * bs.selection_qty';
  429. $specialExpr = $connection->getCheckSql(
  430. 'i.special_price > 0 AND i.special_price < 100',
  431. 'ROUND(' . $price . ' * (i.special_price / 100), 4)',
  432. $price
  433. );
  434. $tierExpr = $connection->getCheckSql(
  435. 'i.tier_percent IS NOT NULL',
  436. 'ROUND((1 - i.tier_percent / 100) * ' . $price . ', 4)',
  437. 'NULL'
  438. );
  439. $priceExpr = $connection->getLeastSql([
  440. $specialExpr,
  441. $connection->getIfNullSql($tierExpr, $price),
  442. ]);
  443. }
  444. $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
  445. $linkField = $metadata->getLinkField();
  446. $select = $connection->select()->from(
  447. ['i' => $this->getBundlePriceTable()],
  448. ['entity_id', 'customer_group_id', 'website_id']
  449. )->join(
  450. ['parent_product' => $this->getTable('catalog_product_entity')],
  451. 'parent_product.entity_id = i.entity_id',
  452. []
  453. )->join(
  454. ['bo' => $this->getTable('catalog_product_bundle_option')],
  455. "bo.parent_id = parent_product.$linkField",
  456. ['option_id']
  457. )->join(
  458. ['bs' => $this->getTable('catalog_product_bundle_selection')],
  459. 'bs.option_id = bo.option_id',
  460. ['selection_id']
  461. )->joinLeft(
  462. ['bsp' => $this->getTable('catalog_product_bundle_selection_price')],
  463. 'bs.selection_id = bsp.selection_id AND bsp.website_id = i.website_id',
  464. ['']
  465. )->join(
  466. ['idx' => $this->getMainTable($dimensions)],
  467. 'bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id' .
  468. ' AND i.website_id = idx.website_id',
  469. []
  470. )->join(
  471. ['e' => $this->getTable('catalog_product_entity')],
  472. 'bs.product_id = e.entity_id AND e.required_options=0',
  473. []
  474. )->where(
  475. 'i.price_type=?',
  476. $priceType
  477. )->columns(
  478. [
  479. 'group_type' => $connection->getCheckSql("bo.type = 'select' OR bo.type = 'radio'", '0', '1'),
  480. 'is_required' => 'bo.required',
  481. 'price' => $priceExpr,
  482. 'tier_price' => $tierExpr,
  483. ]
  484. );
  485. $query = $select->insertFromSelect($this->getBundleSelectionTable());
  486. $connection->query($query);
  487. }
  488. /**
  489. * Prepare percentage tier price for bundle products
  490. *
  491. * @param array $dimensions
  492. * @param array $entityIds
  493. * @return void
  494. * @throws \Exception
  495. */
  496. private function prepareTierPriceIndex($dimensions, $entityIds)
  497. {
  498. $connection = $this->getConnection();
  499. $metadata = $this->metadataPool->getMetadata(ProductInterface::class);
  500. $linkField = $metadata->getLinkField();
  501. // remove index by bundle products
  502. $select = $connection->select()->from(
  503. ['i' => $this->getTable('catalog_product_index_tier_price')],
  504. null
  505. )->join(
  506. ['e' => $this->getTable('catalog_product_entity')],
  507. "i.entity_id=e.entity_id",
  508. []
  509. )->where(
  510. 'e.type_id=?',
  511. \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE
  512. );
  513. $query = $select->deleteFromSelect('i');
  514. $connection->query($query);
  515. $select = $connection->select()->from(
  516. ['tp' => $this->getTable('catalog_product_entity_tier_price')],
  517. ['e.entity_id']
  518. )->join(
  519. ['e' => $this->getTable('catalog_product_entity')],
  520. "tp.{$linkField} = e.{$linkField}",
  521. []
  522. )->join(
  523. ['cg' => $this->getTable('customer_group')],
  524. 'tp.all_groups = 1 OR (tp.all_groups = 0 AND tp.customer_group_id = cg.customer_group_id)',
  525. ['customer_group_id']
  526. )->join(
  527. ['pw' => $this->getTable('store_website')],
  528. 'tp.website_id = 0 OR tp.website_id = pw.website_id',
  529. ['website_id']
  530. )->where(
  531. 'pw.website_id != 0'
  532. )->where(
  533. 'e.type_id=?',
  534. \Magento\Bundle\Ui\DataProvider\Product\Listing\Collector\BundlePrice::PRODUCT_TYPE
  535. )->columns(
  536. new \Zend_Db_Expr('MIN(tp.value)')
  537. )->group(
  538. ['e.entity_id', 'cg.customer_group_id', 'pw.website_id']
  539. );
  540. if (!empty($entityIds)) {
  541. $select->where('e.entity_id IN(?)', $entityIds);
  542. }
  543. foreach ($dimensions as $dimension) {
  544. if (!isset($this->dimensionToFieldMapper[$dimension->getName()])) {
  545. throw new \LogicException(
  546. 'Provided dimension is not valid for Price indexer: ' . $dimension->getName()
  547. );
  548. }
  549. $select->where($this->dimensionToFieldMapper[$dimension->getName()] . ' = ?', $dimension->getValue());
  550. }
  551. $query = $select->insertFromSelect($this->getTable('catalog_product_index_tier_price'));
  552. $connection->query($query);
  553. }
  554. /**
  555. * Create bundle price.
  556. *
  557. * @param IndexTableStructure $priceTable
  558. * @return void
  559. */
  560. private function applyBundlePrice($priceTable): void
  561. {
  562. $select = $this->getConnection()->select();
  563. $select->from(
  564. $this->getBundlePriceTable(),
  565. [
  566. 'entity_id',
  567. 'customer_group_id',
  568. 'website_id',
  569. 'tax_class_id',
  570. 'orig_price',
  571. 'price',
  572. 'min_price',
  573. 'max_price',
  574. 'tier_price',
  575. ]
  576. );
  577. $query = $select->insertFromSelect($priceTable->getTableName());
  578. $this->getConnection()->query($query);
  579. }
  580. /**
  581. * Make insert/update bundle option price.
  582. *
  583. * @return void
  584. * @param IndexTableStructure $priceTable
  585. */
  586. private function applyBundleOptionPrice($priceTable): void
  587. {
  588. $connection = $this->getConnection();
  589. $subSelect = $connection->select()->from(
  590. $this->getBundleOptionTable(),
  591. [
  592. 'entity_id',
  593. 'customer_group_id',
  594. 'website_id',
  595. 'min_price' => new \Zend_Db_Expr('SUM(min_price)'),
  596. 'alt_price' => new \Zend_Db_Expr('MIN(alt_price)'),
  597. 'max_price' => new \Zend_Db_Expr('SUM(max_price)'),
  598. 'tier_price' => new \Zend_Db_Expr('SUM(tier_price)'),
  599. 'alt_tier_price' => new \Zend_Db_Expr('MIN(alt_tier_price)'),
  600. ]
  601. )->group(
  602. ['entity_id', 'customer_group_id', 'website_id']
  603. );
  604. $minPrice = 'i.min_price + ' . $connection->getIfNullSql('io.min_price', '0');
  605. $tierPrice = 'i.tier_price + ' . $connection->getIfNullSql('io.tier_price', '0');
  606. $select = $connection->select()->join(
  607. ['io' => $subSelect],
  608. 'i.entity_id = io.entity_id AND i.customer_group_id = io.customer_group_id' .
  609. ' AND i.website_id = io.website_id',
  610. []
  611. )->columns(
  612. [
  613. 'min_price' => $connection->getCheckSql("{$minPrice} = 0", 'io.alt_price', $minPrice),
  614. 'max_price' => new \Zend_Db_Expr('io.max_price + i.max_price'),
  615. 'tier_price' => $connection->getCheckSql("{$tierPrice} = 0", 'io.alt_tier_price', $tierPrice),
  616. ]
  617. );
  618. $query = $select->crossUpdateFromSelect(['i' => $priceTable->getTableName()]);
  619. $connection->query($query);
  620. }
  621. /**
  622. * Get main table
  623. *
  624. * @param array $dimensions
  625. * @return string
  626. */
  627. private function getMainTable($dimensions)
  628. {
  629. if ($this->fullReindexAction) {
  630. return $this->tableMaintainer->getMainReplicaTable($dimensions);
  631. }
  632. return $this->tableMaintainer->getMainTable($dimensions);
  633. }
  634. /**
  635. * Get connection
  636. *
  637. * return \Magento\Framework\DB\Adapter\AdapterInterface
  638. * @throws \DomainException
  639. */
  640. private function getConnection(): \Magento\Framework\DB\Adapter\AdapterInterface
  641. {
  642. if ($this->connection === null) {
  643. $this->connection = $this->resource->getConnection($this->connectionName);
  644. }
  645. return $this->connection;
  646. }
  647. /**
  648. * Get table
  649. *
  650. * @param string $tableName
  651. * @return string
  652. */
  653. private function getTable($tableName)
  654. {
  655. return $this->resource->getTableName($tableName, $this->connectionName);
  656. }
  657. }