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 ;
22
23
23
24
/**
24
25
* Class to test Bundle products Price indexer resource model
26
+ *
27
+ * @SuppressWarnings(PHPMD.CouplingBetweenObjects)
25
28
*/
26
29
class PriceTest extends TestCase
27
30
{
@@ -45,6 +48,11 @@ class PriceTest extends TestCase
45
48
*/
46
49
private $ priceModel ;
47
50
51
+ /**
52
+ * @var MetadataPool
53
+ */
54
+ private $ metadataPool ;
55
+
48
56
/**
49
57
* @inheritdoc
50
58
*/
@@ -64,7 +72,7 @@ protected function setUp(): void
64
72
/** @var TableMaintainer|MockObject $tableMaintainer */
65
73
$ tableMaintainer = $ this ->createMock (TableMaintainer::class);
66
74
/** @var MetadataPool|MockObject $metadataPool */
67
- $ metadataPool = $ this ->createMock (MetadataPool::class);
75
+ $ this -> metadataPool = $ this ->createMock (MetadataPool::class);
68
76
/** @var BasePriceModifier|MockObject $basePriceModifier */
69
77
$ basePriceModifier = $ this ->createMock (BasePriceModifier::class);
70
78
/** @var JoinAttributeProcessor|MockObject $joinAttributeProcessor */
@@ -78,7 +86,7 @@ protected function setUp(): void
78
86
$ this ->priceModel = new Price (
79
87
$ indexTableStructureFactory ,
80
88
$ tableMaintainer ,
81
- $ metadataPool ,
89
+ $ this -> metadataPool ,
82
90
$ this ->resourceMock ,
83
91
$ basePriceModifier ,
84
92
$ joinAttributeProcessor ,
@@ -89,6 +97,124 @@ protected function setUp(): void
89
97
);
90
98
}
91
99
100
+ /**
101
+ * @throws \ReflectionException
102
+ * @SuppressWarnings(PHPMD.ExcessiveMethodLength)
103
+ */
104
+ public function testCalculateDynamicBundleSelectionPrice (): void
105
+ {
106
+ $ entity = 'entity_id ' ;
107
+ $ price = 'idx.min_price * bs.selection_qty ' ;
108
+ //@codingStandardsIgnoreStart
109
+ $ selectQuery = "SELECT `i`.`entity_id`,
110
+ `i`.`customer_group_id`,
111
+ `i`.`website_id`,
112
+ `bo`.`option_id`,
113
+ `bs`.`selection_id`,
114
+ IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
115
+ `bo`.`required` AS `is_required`,
116
+ LEAST(IF(i.special_price > 0 AND i.special_price < 100,
117
+ ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
118
+ IFNULL((IF(i.tier_percent IS NOT NULL,
119
+ ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
120
+ bs.selection_qty)) AS `price`,
121
+ IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
122
+ NULL) AS `tier_price`
123
+ FROM `catalog_product_index_price_bundle_temp` AS `i`
124
+ INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
125
+ (parent_product.created_in <= 1 AND parent_product.updated_in > 1)
126
+ INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
127
+ INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
128
+ INNER JOIN `catalog_product_index_price_replica` AS `idx`
129
+ ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
130
+ i.website_id = idx.website_id
131
+ INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
132
+ WHERE (i.price_type = 0)
133
+ AND (si.stock_status = 1)
134
+ ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
135
+ `customer_group_id` = VALUES(`customer_group_id`),
136
+ `website_id` = VALUES(`website_id`),
137
+ `option_id` = VALUES(`option_id`),
138
+ `selection_id` = VALUES(`selection_id`),
139
+ `group_type` = VALUES(`group_type`),
140
+ `is_required` = VALUES(`is_required`),
141
+ `price` = VALUES(`price`),
142
+ `tier_price` = VALUES(`tier_price`) " ;
143
+ $ processedQuery = "INSERT INTO `catalog_product_index_price_bundle_sel_temp` (,,,,,,,,) SELECT `i`.`entity_id`,
144
+ `i`.`customer_group_id`,
145
+ `i`.`website_id`,
146
+ `bo`.`option_id`,
147
+ `bs`.`selection_id`,
148
+ IF(bo.type = 'select' OR bo.type = 'radio', 0, 1) AS `group_type`,
149
+ `bo`.`required` AS `is_required`,
150
+ LEAST(IF(i.special_price > 0 AND i.special_price < 100,
151
+ ROUND(idx.min_price * bs.selection_qty * (i.special_price / 100), 4), idx.min_price * bs.selection_qty),
152
+ IFNULL((IF(i.tier_percent IS NOT NULL,
153
+ ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4), NULL)), idx.min_price *
154
+ bs.selection_qty)) AS `price`,
155
+ IF(i.tier_percent IS NOT NULL, ROUND((1 - i.tier_percent / 100) * idx.min_price * bs.selection_qty, 4),
156
+ NULL) AS `tier_price`
157
+ FROM `catalog_product_index_price_bundle_temp` AS `i`
158
+ INNER JOIN `catalog_product_entity` AS `parent_product` ON parent_product.entity_id = i.entity_id AND
159
+ (parent_product.created_in <= 1 AND parent_product.updated_in > 1)
160
+ INNER JOIN `catalog_product_bundle_option` AS `bo` ON bo.parent_id = parent_product.row_id
161
+ INNER JOIN `catalog_product_bundle_selection` AS `bs` ON bs.option_id = bo.option_id
162
+ INNER JOIN `catalog_product_index_price_replica` AS `idx` USE INDEX (PRIMARY)
163
+ ON bs.product_id = idx.entity_id AND i.customer_group_id = idx.customer_group_id AND
164
+ i.website_id = idx.website_id
165
+ INNER JOIN `cataloginventory_stock_status` AS `si` ON si.product_id = bs.product_id
166
+ WHERE (i.price_type = 0)
167
+ AND (si.stock_status = 1)
168
+ ON DUPLICATE KEY UPDATE `entity_id` = VALUES(`entity_id`),
169
+ `customer_group_id` = VALUES(`customer_group_id`),
170
+ `website_id` = VALUES(`website_id`),
171
+ `option_id` = VALUES(`option_id`),
172
+ `selection_id` = VALUES(`selection_id`),
173
+ `group_type` = VALUES(`group_type`),
174
+ `is_required` = VALUES(`is_required`),
175
+ `price` = VALUES(`price`),
176
+ `tier_price` = VALUES(`tier_price`) ON DUPLICATE KEY UPDATE = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES(), = VALUES() " ;
177
+ //@codingStandardsIgnoreEnd
178
+ $ this ->connectionMock ->expects ($ this ->exactly (3 ))
179
+ ->method ('getCheckSql ' )
180
+ ->withConsecutive (
181
+ [
182
+ 'i.special_price > 0 AND i.special_price < 100 ' ,
183
+ 'ROUND( ' . $ price . ' * (i.special_price / 100), 4) ' ,
184
+ $ price
185
+ ],
186
+ [
187
+ 'i.tier_percent IS NOT NULL ' ,
188
+ 'ROUND((1 - i.tier_percent / 100) * ' . $ price . ', 4) ' ,
189
+ 'NULL '
190
+ ],
191
+ ["bo.type = 'select' OR bo.type = 'radio' " , '0 ' , '1 ' ]
192
+ );
193
+
194
+ $ select = $ this ->createMock (\Magento \Framework \DB \Select::class);
195
+ $ select ->expects ($ this ->once ())->method ('from ' )->willReturn ($ select );
196
+ $ select ->expects ($ this ->exactly (5 ))->method ('join ' )->willReturn ($ select );
197
+ $ select ->expects ($ this ->exactly (2 ))->method ('where ' )->willReturn ($ select );
198
+ $ select ->expects ($ this ->once ())->method ('columns ' )->willReturn ($ select );
199
+ $ select ->expects ($ this ->any ())->method ('__toString ' )->willReturn ($ selectQuery );
200
+
201
+ $ this ->connectionMock ->expects ($ this ->once ())->method ('getIfNullSql ' );
202
+ $ this ->connectionMock ->expects ($ this ->once ())->method ('getLeastSql ' );
203
+ $ this ->connectionMock ->expects ($ this ->any ())
204
+ ->method ('select ' )
205
+ ->willReturn ($ select );
206
+ $ this ->connectionMock ->expects ($ this ->exactly (9 ))->method ('quoteIdentifier ' );
207
+ $ this ->connectionMock ->expects ($ this ->once ())->method ('query ' )->with ($ processedQuery );
208
+
209
+ $ pool = $ this ->createMock (EntityMetadataInterface::class);
210
+ $ pool ->expects ($ this ->once ())->method ('getLinkField ' )->willReturn ($ entity );
211
+ $ this ->metadataPool ->expects ($ this ->once ())
212
+ ->method ('getMetadata ' )
213
+ ->willReturn ($ pool );
214
+
215
+ $ this ->invokeMethodViaReflection ('calculateDynamicBundleSelectionPrice ' , []);
216
+ }
217
+
92
218
/**
93
219
* Tests create Bundle Price temporary table
94
220
*/
@@ -147,16 +273,18 @@ public function testGetBundleOptionTable(): void
147
273
* Invoke private method via reflection
148
274
*
149
275
* @param string $methodName
276
+ * @param array $args
150
277
* @return string
278
+ * @throws \ReflectionException
151
279
*/
152
- private function invokeMethodViaReflection (string $ methodName ): string
280
+ private function invokeMethodViaReflection (string $ methodName, array $ args = [] ): string
153
281
{
154
282
$ method = new \ReflectionMethod (
155
283
Price::class,
156
284
$ methodName
157
285
);
158
286
$ method ->setAccessible (true );
159
287
160
- return (string )$ method ->invoke ($ this ->priceModel );
288
+ return (string )$ method ->invoke ($ this ->priceModel , $ args );
161
289
}
162
290
}
0 commit comments