99use Magento \Catalog \Model \Product ;
1010use Magento \Catalog \Model \ProductFactory ;
1111use Magento \Catalog \Model \ResourceModel \Indexer \ActiveTableSwitcher ;
12+ use Magento \CatalogRule \Model \Indexer \IndexBuilder \ProductLoader ;
13+ use Magento \CatalogRule \Model \Indexer \IndexerTableSwapperInterface as TableSwapper ;
1214use Magento \CatalogRule \Model \ResourceModel \Rule \Collection as RuleCollection ;
1315use Magento \CatalogRule \Model \ResourceModel \Rule \CollectionFactory as RuleCollectionFactory ;
1416use Magento \CatalogRule \Model \Rule ;
1719use Magento \Framework \App \ResourceConnection ;
1820use Magento \Framework \Exception \LocalizedException ;
1921use Magento \Framework \Pricing \PriceCurrencyInterface ;
20- use Magento \CatalogRule \Model \Indexer \IndexBuilder \ProductLoader ;
21- use Magento \CatalogRule \Model \Indexer \IndexerTableSwapperInterface as TableSwapper ;
2222use Magento \Framework \Stdlib \DateTime ;
23+ use Magento \Framework \Stdlib \DateTime \TimezoneInterface ;
24+ use Magento \Store \Model \ScopeInterface ;
2325use Magento \Store \Model \StoreManagerInterface ;
2426use Psr \Log \LoggerInterface ;
2527
@@ -143,11 +145,26 @@ class IndexBuilder
143145 */
144146 private $ pricesPersistor ;
145147
148+ /**
149+ * @var TimezoneInterface|mixed
150+ */
151+ private $ localeDate ;
152+
153+ /**
154+ * @var ActiveTableSwitcher|mixed
155+ */
156+ private $ activeTableSwitcher ;
157+
146158 /**
147159 * @var TableSwapper
148160 */
149161 private $ tableSwapper ;
150162
163+ /**
164+ * @var ProductLoader|mixed
165+ */
166+ private $ productLoader ;
167+
151168 /**
152169 * @param RuleCollectionFactory $ruleCollectionFactory
153170 * @param PriceCurrencyInterface $priceCurrency
@@ -168,6 +185,7 @@ class IndexBuilder
168185 * @param ActiveTableSwitcher|null $activeTableSwitcher
169186 * @param ProductLoader|null $productLoader
170187 * @param TableSwapper|null $tableSwapper
188+ * @param TimezoneInterface|null $localeDate
171189 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
172190 * @SuppressWarnings(PHPMD.UnusedFormalParameter)
173191 */
@@ -190,7 +208,8 @@ public function __construct(
190208 RuleProductPricesPersistor $ pricesPersistor = null ,
191209 ActiveTableSwitcher $ activeTableSwitcher = null ,
192210 ProductLoader $ productLoader = null ,
193- TableSwapper $ tableSwapper = null
211+ TableSwapper $ tableSwapper = null ,
212+ TimezoneInterface $ localeDate = null
194213 ) {
195214 $ this ->resource = $ resource ;
196215 $ this ->connection = $ resource ->getConnection ();
@@ -222,8 +241,16 @@ public function __construct(
222241 $ this ->pricesPersistor = $ pricesPersistor ?? ObjectManager::getInstance ()->get (
223242 RuleProductPricesPersistor::class
224243 );
244+ $ this ->activeTableSwitcher = $ activeTableSwitcher ?? ObjectManager::getInstance ()->get (
245+ ActiveTableSwitcher::class
246+ );
247+ $ this ->productLoader = $ productLoader ?? ObjectManager::getInstance ()->get (
248+ ProductLoader::class
249+ );
225250 $ this ->tableSwapper = $ tableSwapper ??
226251 ObjectManager::getInstance ()->get (TableSwapper::class);
252+ $ this ->localeDate = $ localeDate ??
253+ ObjectManager::getInstance ()->get (TimezoneInterface::class);
227254 }
228255
229256 /**
@@ -237,11 +264,19 @@ public function __construct(
237264 public function reindexById ($ id )
238265 {
239266 try {
240- $ this ->doReindexByIds ([$ id ]);
267+ $ this ->cleanProductIndex ([$ id ]);
268+
269+ $ products = $ this ->productLoader ->getProducts ([$ id ]);
270+ $ activeRules = $ this ->getActiveRules ();
271+ foreach ($ products as $ product ) {
272+ $ this ->applyRules ($ activeRules , $ product );
273+ }
274+
275+ $ this ->reindexRuleGroupWebsite ->execute ();
241276 } catch (\Exception $ e ) {
242277 $ this ->critical ($ e );
243278 throw new LocalizedException (
244- __ (" Catalog rule indexing failed. See details in exception log. " )
279+ __ (' Catalog rule indexing failed. See details in exception log. ' )
245280 );
246281 }
247282 }
@@ -369,6 +404,68 @@ protected function cleanByIds($productIds)
369404 $ this ->cleanProductPriceIndex ($ productIds );
370405 }
371406
407+ /**
408+ * Assign product to rule
409+ *
410+ * @param Rule $rule
411+ * @param int $productEntityId
412+ * @param array $websiteIds
413+ * @return void
414+ */
415+ private function assignProductToRule (Rule $ rule , int $ productEntityId , array $ websiteIds ): void
416+ {
417+ $ ruleId = (int ) $ rule ->getId ();
418+ $ ruleProductTable = $ this ->getTable ('catalogrule_product ' );
419+ $ this ->connection ->delete (
420+ $ ruleProductTable ,
421+ [
422+ 'rule_id = ? ' => $ ruleId ,
423+ 'product_id = ? ' => $ productEntityId ,
424+ ]
425+ );
426+
427+ $ customerGroupIds = $ rule ->getCustomerGroupIds ();
428+ $ sortOrder = (int )$ rule ->getSortOrder ();
429+ $ actionOperator = $ rule ->getSimpleAction ();
430+ $ actionAmount = $ rule ->getDiscountAmount ();
431+ $ actionStop = $ rule ->getStopRulesProcessing ();
432+
433+ $ rows = [];
434+ foreach ($ websiteIds as $ websiteId ) {
435+ $ scopeTz = new \DateTimeZone (
436+ $ this ->localeDate ->getConfigTimezone (ScopeInterface::SCOPE_WEBSITE , $ websiteId )
437+ );
438+ $ fromTime = $ rule ->getFromDate ()
439+ ? (new \DateTime ($ rule ->getFromDate (), $ scopeTz ))->getTimestamp ()
440+ : 0 ;
441+ $ toTime = $ rule ->getToDate ()
442+ ? (new \DateTime ($ rule ->getToDate (), $ scopeTz ))->getTimestamp () + IndexBuilder::SECONDS_IN_DAY - 1
443+ : 0 ;
444+ foreach ($ customerGroupIds as $ customerGroupId ) {
445+ $ rows [] = [
446+ 'rule_id ' => $ ruleId ,
447+ 'from_time ' => $ fromTime ,
448+ 'to_time ' => $ toTime ,
449+ 'website_id ' => $ websiteId ,
450+ 'customer_group_id ' => $ customerGroupId ,
451+ 'product_id ' => $ productEntityId ,
452+ 'action_operator ' => $ actionOperator ,
453+ 'action_amount ' => $ actionAmount ,
454+ 'action_stop ' => $ actionStop ,
455+ 'sort_order ' => $ sortOrder ,
456+ ];
457+
458+ if (count ($ rows ) == $ this ->batchCount ) {
459+ $ this ->connection ->insertMultiple ($ ruleProductTable , $ rows );
460+ $ rows = [];
461+ }
462+ }
463+ }
464+ if ($ rows ) {
465+ $ this ->connection ->insertMultiple ($ ruleProductTable , $ rows );
466+ }
467+ }
468+
372469 /**
373470 * Apply rule
374471 *
@@ -392,6 +489,28 @@ protected function applyRule(Rule $rule, $product)
392489 return $ this ;
393490 }
394491
492+ /**
493+ * Apply rules
494+ *
495+ * @param RuleCollection $ruleCollection
496+ * @param Product $product
497+ * @return void
498+ */
499+ private function applyRules (RuleCollection $ ruleCollection , Product $ product ): void
500+ {
501+ foreach ($ ruleCollection as $ rule ) {
502+ if (!$ rule ->validate ($ product )) {
503+ continue ;
504+ }
505+
506+ $ websiteIds = array_intersect ($ product ->getWebsiteIds (), $ rule ->getWebsiteIds ());
507+ $ this ->assignProductToRule ($ rule , $ product ->getId (), $ websiteIds );
508+ }
509+
510+ $ this ->cleanProductPriceIndex ([$ product ->getId ()]);
511+ $ this ->reindexRuleProductPrice ->execute ($ this ->batchCount , $ product ->getId ());
512+ }
513+
395514 /**
396515 * Retrieve table name
397516 *
0 commit comments