1414use Magento \Catalog \Model \ResourceModel \Product \Indexer \Price \Query \JoinAttributeProcessor ;
1515use Magento \Framework \App \ResourceConnection ;
1616use Magento \Framework \DB \Adapter \AdapterInterface ;
17+ use Magento \Framework \EntityManager \EntityMetadataInterface ;
1718use Magento \Framework \EntityManager \MetadataPool ;
1819use Magento \Framework \Event \ManagerInterface ;
1920use Magento \Framework \Module \Manager ;
@@ -45,6 +46,11 @@ class PriceTest extends TestCase
4546 */
4647 private $ priceModel ;
4748
49+ /**
50+ * @var MetadataPool
51+ */
52+ private $ metadataPool ;
53+
4854 /**
4955 * @inheritdoc
5056 */
@@ -64,7 +70,7 @@ protected function setUp(): void
6470 /** @var TableMaintainer|MockObject $tableMaintainer */
6571 $ tableMaintainer = $ this ->createMock (TableMaintainer::class);
6672 /** @var MetadataPool|MockObject $metadataPool */
67- $ metadataPool = $ this ->createMock (MetadataPool::class);
73+ $ this -> metadataPool = $ this ->createMock (MetadataPool::class);
6874 /** @var BasePriceModifier|MockObject $basePriceModifier */
6975 $ basePriceModifier = $ this ->createMock (BasePriceModifier::class);
7076 /** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
@@ -78,7 +84,7 @@ protected function setUp(): void
7884 $ this ->priceModel = new Price (
7985 $ indexTableStructureFactory ,
8086 $ tableMaintainer ,
81- $ metadataPool ,
87+ $ this -> metadataPool ,
8288 $ this ->resourceMock ,
8389 $ basePriceModifier ,
8490 $ joinAttributeProcessor ,
@@ -89,6 +95,123 @@ protected function setUp(): void
8995 );
9096 }
9197
98+ /**
99+ * @throws \ReflectionException
100+ */
101+ public function testCalculateDynamicBundleSelectionPrice (): void
102+ {
103+ $ entity = 'entity_id ' ;
104+ $ price = 'idx.min_price * bs.selection_qty ' ;
105+ //@codingStandardsIgnoreStart
106+ $ selectQuery = "SELECT `i`.`entity_id`,
107+ `i`.`customer_group_id`,
108+ `i`.`website_id`,
109+ `bo`.`option_id`,
110+ `bs`.`selection_id`,
111+ IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
112+ `bo`.`required` AS `is_required`,
113+ LEAST(IF(i.special_price > 0 AND i.special_price < 100,
114+ ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
115+ IFNULL((IF(i.tier_percent IS NOT NULL,
116+ ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
117+ bs.selection_qty)) AS `price`,
118+ IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
119+ NULL) AS `tier_price`
120+ FROM `catalog_product_index_price_bundle_temp` AS `i`
121+ INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
122+ (parent_product.created_in <= 1 AND parent_product.updated_in > 1)
123+ INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
124+ INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
125+ INNER JOIN `catalog_product_index_price_replica` AS `idx`
126+ ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
127+ i.website_id = idx.website_id
128+ INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
129+ WHERE (i.price_type = 0)
130+ AND (si.stock_status = 1)
131+ ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
132+ `customer_group_id` = VALUES(`customer_group_id`),
133+ `website_id` = VALUES(`website_id`),
134+ `option_id` = VALUES(`option_id`),
135+ `selection_id` = VALUES(`selection_id`),
136+ `group_type` = VALUES(`group_type`),
137+ `is_required` = VALUES(`is_required`),
138+ `price` = VALUES(`price`),
139+ `tier_price` = VALUES(`tier_price`) " ;
140+ $ processedQuery = "INSERT INTO `catalog_product_index_price_bundle_sel_temp` (,,,,,,,,) SELECT `i`.`entity_id`,
141+ `i`.`customer_group_id`,
142+ `i`.`website_id`,
143+ `bo`.`option_id`,
144+ `bs`.`selection_id`,
145+ IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
146+ `bo`.`required` AS `is_required`,
147+ LEAST(IF(i.special_price > 0 AND i.special_price < 100,
148+ ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
149+ IFNULL((IF(i.tier_percent IS NOT NULL,
150+ ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
151+ bs.selection_qty)) AS `price`,
152+ IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
153+ NULL) AS `tier_price`
154+ FROM `catalog_product_index_price_bundle_temp` AS `i`
155+ INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
156+ (parent_product.created_in <= 1 AND parent_product.updated_in > 1)
157+ INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
158+ INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
159+ INNER JOIN `catalog_product_index_price_replica` AS `idx` USE INDEX (PRIMARY)
160+ ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
161+ i.website_id = idx.website_id
162+ INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
163+ WHERE (i.price_type = 0)
164+ AND (si.stock_status = 1)
165+ ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
166+ `customer_group_id` = VALUES(`customer_group_id`),
167+ `website_id` = VALUES(`website_id`),
168+ `option_id` = VALUES(`option_id`),
169+ `selection_id` = VALUES(`selection_id`),
170+ `group_type` = VALUES(`group_type`),
171+ `is_required` = VALUES(`is_required`),
172+ `price` = VALUES(`price`),
173+ `tier_price` = VALUES(`tier_price`) ON DUPLICATE KEY UPDATE = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES() " ;
174+ //@codingStandardsIgnoreEnd
175+ $ this ->connectionMock ->expects ($ this ->exactly (3 ))
176+ ->method ('getCheckSql ' )
177+ ->withConsecutive (
178+ [
179+ 'i.special_price > 0 AND i.special_price < 100 ' ,
180+ 'ROUND( ' . $ price . ' * (i.special_price / 100), 4) ' ,
181+ $ price
182+ ],
183+ [
184+ 'i.tier_percent IS NOT NULL ' ,
185+ 'ROUND((1 - i.tier_percent / 100) * ' . $ price . ', 4) ' ,
186+ 'NULL '
187+ ],
188+ ["bo.type = 'select' OR bo.type = 'radio' " , '0 ' , '1 ' ]
189+ );
190+
191+ $ select = $ this ->createMock (\Magento \Framework \DB \Select::class);
192+ $ select ->expects ($ this ->once ())->method ('from ' )->willReturn ($ select );
193+ $ select ->expects ($ this ->exactly (5 ))->method ('join ' )->willReturn ($ select );
194+ $ select ->expects ($ this ->exactly (2 ))->method ('where ' )->willReturn ($ select );
195+ $ select ->expects ($ this ->once ())->method ('columns ' )->willReturn ($ select );
196+ $ select ->expects ($ this ->any ())->method ('__toString ' )->willReturn ($ selectQuery );
197+
198+ $ this ->connectionMock ->expects ($ this ->once ())->method ('getIfNullSql ' );
199+ $ this ->connectionMock ->expects ($ this ->once ())->method ('getLeastSql ' );
200+ $ this ->connectionMock ->expects ($ this ->any ())
201+ ->method ('select ' )
202+ ->willReturn ($ select );
203+ $ this ->connectionMock ->expects ($ this ->exactly (9 ))->method ('quoteIdentifier ' );
204+ $ this ->connectionMock ->expects ($ this ->once ())->method ('query ' )->with ($ processedQuery );
205+
206+ $ pool = $ this ->createMock (EntityMetadataInterface::class);
207+ $ pool ->expects ($ this ->once ())->method ('getLinkField ' )->willReturn ($ entity );
208+ $ this ->metadataPool ->expects ($ this ->once ())
209+ ->method ('getMetadata ' )
210+ ->willReturn ($ pool );
211+
212+ $ this ->invokeMethodViaReflection ('calculateDynamicBundleSelectionPrice ' , []);
213+ }
214+
92215 /**
93216 * Tests create Bundle Price temporary table
94217 */
@@ -147,16 +270,18 @@ public function testGetBundleOptionTable(): void
147270 * Invoke private method via reflection
148271 *
149272 * @param string $methodName
273+ * @param array $args
150274 * @return string
275+ * @throws \ReflectionException
151276 */
152- private function invokeMethodViaReflection (string $ methodName ): string
277+ private function invokeMethodViaReflection (string $ methodName, array $ args = [] ): string
153278 {
154279 $ method = new \ReflectionMethod (
155280 Price::class,
156281 $ methodName
157282 );
158283 $ method ->setAccessible (true );
159284
160- return (string )$ method ->invoke ($ this ->priceModel );
285+ return (string )$ method ->invoke ($ this ->priceModel , $ args );
161286 }
162287}
0 commit comments