14
14
use Magento \Catalog \Model \ResourceModel \Product \Indexer \Price \Query \JoinAttributeProcessor ;
15
15
use Magento \Framework \App \ResourceConnection ;
16
16
use Magento \Framework \DB \Adapter \AdapterInterface ;
17
+ use Magento \Framework \EntityManager \EntityMetadataInterface ;
17
18
use Magento \Framework \EntityManager \MetadataPool ;
18
19
use Magento \Framework \Event \ManagerInterface ;
19
20
use Magento \Framework \Module \Manager ;
@@ -45,6 +46,11 @@ class PriceTest extends TestCase
45
46
*/
46
47
private $ priceModel ;
47
48
49
+ /**
50
+ * @var MetadataPool
51
+ */
52
+ private $ metadataPool ;
53
+
48
54
/**
49
55
* @inheritdoc
50
56
*/
@@ -64,7 +70,7 @@ protected function setUp(): void
64
70
/** @var TableMaintainer|MockObject $tableMaintainer */
65
71
$ tableMaintainer = $ this ->createMock (TableMaintainer::class);
66
72
/** @var MetadataPool|MockObject $metadataPool */
67
- $ metadataPool = $ this ->createMock (MetadataPool::class);
73
+ $ this -> metadataPool = $ this ->createMock (MetadataPool::class);
68
74
/** @var BasePriceModifier|MockObject $basePriceModifier */
69
75
$ basePriceModifier = $ this ->createMock (BasePriceModifier::class);
70
76
/** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
@@ -78,7 +84,7 @@ protected function setUp(): void
78
84
$ this ->priceModel = new Price (
79
85
$ indexTableStructureFactory ,
80
86
$ tableMaintainer ,
81
- $ metadataPool ,
87
+ $ this -> metadataPool ,
82
88
$ this ->resourceMock ,
83
89
$ basePriceModifier ,
84
90
$ joinAttributeProcessor ,
@@ -89,6 +95,123 @@ protected function setUp(): void
89
95
);
90
96
}
91
97
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
+
92
215
/**
93
216
* Tests create Bundle Price temporary table
94
217
*/
@@ -147,16 +270,18 @@ public function testGetBundleOptionTable(): void
147
270
* Invoke private method via reflection
148
271
*
149
272
* @param string $methodName
273
+ * @param array $args
150
274
* @return string
275
+ * @throws \ReflectionException
151
276
*/
152
- private function invokeMethodViaReflection (string $ methodName ): string
277
+ private function invokeMethodViaReflection (string $ methodName, array $ args = [] ): string
153
278
{
154
279
$ method = new \ReflectionMethod (
155
280
Price::class,
156
281
$ methodName
157
282
);
158
283
$ method ->setAccessible (true );
159
284
160
- return (string )$ method ->invoke ($ this ->priceModel );
285
+ return (string )$ method ->invoke ($ this ->priceModel , $ args );
161
286
}
162
287
}
0 commit comments