30 private static array $changeActive = [];
31 private static array $currentActive = [];
33 private static ?
bool $separateSkuMode =
null;
35 private static int $deferredCalculation = -1;
37 private static bool $calculateAvailable =
false;
38 private static array $calculatePriceTypes = [];
40 private static array $deferredSku = [];
41 private static array $deferredOffers = [];
42 private static array $deferredUnknown = [];
44 private static array $skuExist = [];
45 private static array $skuAvailable = [];
46 private static array $offersIds = [];
47 private static array $offersMap = [];
48 private static array $skuPrices = [];
50 private static ?
string $queryElementTimestamp =
null;
59 self::$allowUpdateAvailable++;
69 self::$allowUpdateAvailable--;
79 return (self::$allowUpdateAvailable >= 0);
89 self::$deferredCalculation++;
99 self::$deferredCalculation--;
109 return (self::$deferredCalculation >= 0);
136 'TYPE' => $productIblock
137 ? Catalog\ProductTable::TYPE_EMPTY_SKU
138 : Catalog\ProductTable::TYPE_PRODUCT
140 'AVAILABLE' => Catalog\ProductTable::STATUS_NO,
142 'QUANTITY_TRACE' => Catalog\ProductTable::STATUS_YES,
143 'CAN_BUY_ZERO' => Catalog\ProductTable::STATUS_NO
148 'TYPE' => Catalog\ProductTable::TYPE_SKU,
149 'AVAILABLE' => Catalog\ProductTable::STATUS_NO,
151 'QUANTITY_TRACE' => Catalog\ProductTable::STATUS_YES,
152 'CAN_BUY_ZERO' => Catalog\ProductTable::STATUS_NO
157 'TYPE' => Catalog\ProductTable::TYPE_SKU,
158 'AVAILABLE' => Catalog\ProductTable::STATUS_YES,
160 'QUANTITY_TRACE' => Catalog\ProductTable::STATUS_NO,
161 'CAN_BUY_ZERO' => Catalog\ProductTable::STATUS_YES,
185 public static function updateAvailable($productId, $iblockId = 0, array $productFields = array())
187 if (!self::allowedUpdateAvailable())
189 static::disableUpdateAvailable();
198 $productId = (int)$productId;
206 $iblockId = (int)$iblockId;
208 $iblockId = (int)\CIBlockElement::GetIBlockByID($productId);
218 $iblockData = \CCatalogSku::GetInfoByIBlock($iblockId);
219 if (empty($iblockData))
228 switch ($iblockData[
'CATALOG_TYPE'])
230 case \CCatalogSku::TYPE_PRODUCT:
231 $fields = (self::isSeparateSkuMode()
232 ? static::getParentDataAsProduct($productId, $productFields)
233 : static::getParentProductFieldsByState(static::getOfferState($productId, $iblockId))
236 case \CCatalogSku::TYPE_FULL:
237 $offerState = static::getOfferState($productId, $iblockId);
238 if ($offerState !== self::OFFERS_ERROR)
244 $fields = (self::isSeparateSkuMode()
245 ? static::getParentDataAsProduct($productId, $productFields)
246 : static::getParentProductFieldsByState($offerState)
250 $product = Catalog\Model\Product::getCacheItem($productId,
true);
251 if (!empty($product))
253 switch ($product[
'TYPE'])
255 case Catalog\ProductTable::TYPE_SKU:
256 $fields = static::getDefaultParentSettings($offerState,
false);
258 case Catalog\ProductTable::TYPE_EMPTY_SKU:
259 $fields = static::getParentProductFieldsByState($offerState);
261 case Catalog\ProductTable::TYPE_PRODUCT:
262 case Catalog\ProductTable::TYPE_SET:
263 $fields[
'AVAILABLE'] = Catalog\ProductTable::calculateAvailable($product);
274 case \CCatalogSku::TYPE_OFFERS:
275 $parent = \CCatalogSku::getProductList($productId, $iblockId);
276 if (!isset($parent[$productId]))
279 'TYPE' => Catalog\ProductTable::TYPE_FREE_OFFER,
285 'TYPE' => Catalog\ProductTable::TYPE_OFFER,
287 $parentId = $parent[$productId][
'ID'];
288 $parentIblockId = $parent[$productId][
'IBLOCK_ID'];
290 $fields = array_merge(
291 self::getProductAvailable($productId, $productFields),
295 case \CCatalogSku::TYPE_CATALOG:
296 $fields = self::getProductAvailable($productId, $productFields);
306 $updateResult = Catalog\ProductTable::update($productId, $fields);
307 if (!$updateResult->isSuccess())
312 unset($updateResult);
318 && $parentIblockId > 0
321 $result = self::updateParentAvailable($parentId, $parentIblockId);
325 unset($parentIblockId, $parentId, $fields, $iblockData);
327 static::enableUpdateAvailable();
343 if (!self::allowedUpdateAvailable())
353 || $type == Catalog\ProductTable::TYPE_SET
357 if ($iblockId !==
null)
359 $iblockId = (int)$iblockId;
366 case Catalog\ProductTable::TYPE_SKU:
367 case Catalog\ProductTable::TYPE_EMPTY_SKU:
368 self::setCalculateData(self::$deferredSku, $id, $iblockId);
370 case Catalog\ProductTable::TYPE_OFFER:
371 self::setCalculateData(self::$deferredOffers, $id, $iblockId);
374 if (isset(self::$deferredSku[$id]))
375 self::setCalculateData(self::$deferredSku, $id, $iblockId);
376 elseif (isset(self::$deferredOffers[$id]))
377 self::setCalculateData(self::$deferredOffers, $id, $iblockId);
379 self::setCalculateData(self::$deferredUnknown, $id, $iblockId);
382 if (!self::usedDeferredCalculation())
396 public static function calculatePrice($id, $iblockId =
null, $type =
null, array $priceTypes = [])
398 if (!self::allowedUpdateAvailable())
401 if (self::isSeparateSkuMode())
415 if ($iblockId !==
null)
417 $iblockId = (int)$iblockId;
424 case Catalog\ProductTable::TYPE_SKU:
425 case Catalog\ProductTable::TYPE_EMPTY_SKU:
426 self::setCalculatePriceTypes(self::$deferredSku, $id, $iblockId, $priceTypes);
428 case Catalog\ProductTable::TYPE_OFFER:
429 self::setCalculatePriceTypes(self::$deferredOffers, $id, $iblockId, $priceTypes);
432 if (isset(self::$deferredSku[$id]))
433 self::setCalculatePriceTypes(self::$deferredSku, $id, $iblockId, $priceTypes);
434 elseif (isset(self::$deferredOffers[$id]))
435 self::setCalculatePriceTypes(self::$deferredOffers, $id, $iblockId, $priceTypes);
437 self::setCalculatePriceTypes(self::$deferredUnknown, $id, $iblockId, $priceTypes);
441 if (!self::usedDeferredCalculation())
452 if (!self::allowedUpdateAvailable())
455 static::disableUpdateAvailable();
457 self::updateDeferredSkuList();
459 if (!empty(self::$deferredSku))
461 self::clearStepData();
463 self::$calculatePriceTypes = array_keys(self::$calculatePriceTypes);
464 if (!empty(self::$calculatePriceTypes))
465 sort(self::$calculatePriceTypes);
467 self::loadProductIblocks();
469 $list = array_keys(self::$deferredSku);
472 foreach (array_chunk($list, 100) as $pageIds)
474 self::loadProductData($pageIds);
475 self::updateProductData($pageIds);
476 self::updateElements($pageIds);
477 self::updateProductFacetIndex($pageIds);
479 unset($pageIds, $list);
481 self::clearStepData();
483 self::$deferredSku = array();
486 self::$calculateAvailable =
false;
487 self::$calculatePriceTypes = array();
489 static::enableUpdateAvailable();
500 static::disablePropertyHandler();
512 static::enablePropertyHandler();
525 static::disablePropertyHandler();
527 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($newFields[
'IBLOCK_ID']);
528 if (empty($iblockData))
531 if (isset($newFields[
'ACTIVE']) && $newFields[
'ACTIVE'] != $oldFields[
'ACTIVE'])
532 self::$changeActive[$newFields[
'ID']] = $newFields[
'ACTIVE'];
533 self::$currentActive[$newFields[
'ID']] = $oldFields[
'ACTIVE'];
546 $modifyActive =
false;
547 $modifyProperty =
false;
551 if (!$fields[
'RESULT'])
554 $elementId = $fields[
'ID'];
558 $modifyActive = isset(self::$changeActive[$elementId]);
560 isset(self::$offers[$elementId])
561 && self::$offers[$elementId][
'CURRENT_PRODUCT'] != self::$offers[$elementId][
'NEW_PRODUCT']
563 $process = $modifyActive || $modifyProperty;
568 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($fields[
'IBLOCK_ID']);
569 $process = !empty($iblockData);
574 if ($modifyActive && !isset(self::$offers[$elementId]))
576 $parent = \CCatalogSku::getProductList($elementId, $fields[
'IBLOCK_ID']);
577 if (!empty($parent[$elementId]))
578 self::$offers[$elementId] = array(
579 'CURRENT_PRODUCT' => $parent[$elementId][
'ID'],
580 'NEW_PRODUCT' => $parent[$elementId][
'ID'],
581 'PRODUCT_IBLOCK_ID' => $parent[$elementId][
'IBLOCK_ID']
586 if (isset(self::$offers[$elementId]))
588 $offerDescr = self::$offers[$elementId];
590 if ($offerDescr[
'CURRENT_PRODUCT'] > 0)
592 if ($modifyActive || $modifyProperty)
595 $offerDescr[
'CURRENT_PRODUCT'],
596 $iblockData[
'PRODUCT_IBLOCK_ID'],
601 if ($offerDescr[
'NEW_PRODUCT'] > 0)
605 ? self::$changeActive[$elementId]
606 : self::$currentActive[$elementId]
608 if ($modifyProperty && $elementActive ==
'Y')
611 $offerDescr[
'NEW_PRODUCT'],
612 $iblockData[
'PRODUCT_IBLOCK_ID'],
618 if ($offerDescr[
'CURRENT_PRODUCT'] == 0 || $offerDescr[
'NEW_PRODUCT'] == 0)
621 $offerDescr[
'NEW_PRODUCT'] > 0
622 ? Catalog\ProductTable::TYPE_OFFER
623 : Catalog\ProductTable::TYPE_FREE_OFFER
626 $result = Catalog\Model\Product::update($elementId, array(
'TYPE' => $type));
642 if (isset(self::$offers[$elementId]))
643 unset(self::$offers[$elementId]);
644 if (isset(self::$currentActive[$elementId]))
645 unset(self::$currentActive[$elementId]);
646 if (isset(self::$changeActive[$elementId]))
647 unset(self::$changeActive[$elementId]);
648 static::enablePropertyHandler();
661 if ((
int)$elementData[
'WF_PARENT_ELEMENT_ID'] > 0)
664 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($elementData[
'IBLOCK_ID']);
665 if (empty($iblockData))
668 $parent = \CCatalogSku::getProductList($elementId, $elementData[
'IBLOCK_ID']);
669 if (!empty($parent[$elementId]))
670 self::$offers[$elementId] = array(
671 'CURRENT_PRODUCT' => $parent[$elementId][
'ID'],
673 'PRODUCT_IBLOCK_ID' => $parent[$elementId][
'IBLOCK_ID']
687 $elementId = $elementData[
'ID'];
688 if (!isset(self::$offers[$elementId]))
692 self::$offers[$elementId][
'CURRENT_PRODUCT'],
693 self::$offers[$elementId][
'PRODUCT_IBLOCK_ID'],
697 if (isset(self::$offers[$elementId]))
698 unset(self::$offers[$elementId]);
722 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($iblockId);
723 if (empty($iblockData))
726 $skuPropertyId = $iblockData[
'SKU_PROPERTY_ID'];
727 if (!isset($propertyList[$skuPropertyId]))
729 $skuPropertyCode = (string)$propertyList[$skuPropertyId][
'CODE'];
730 if ($skuPropertyCode ===
'')
731 $skuPropertyCode = (string)$skuPropertyId;
735 if ($propertyIdentifyer)
737 if (is_int($propertyIdentifyer))
739 $propertyId = $propertyIdentifyer;
743 $propertyId = (int)$propertyIdentifyer;
744 if ($propertyId.
'' != $propertyIdentifyer)
745 $propertyId = ($skuPropertyCode == $propertyIdentifyer ? $skuPropertyId : 0);
747 if ($propertyId == $skuPropertyId)
749 $skuValue = $newValues;
756 if (array_key_exists($skuPropertyId, $newValues))
758 $skuValue = $newValues[$skuPropertyId];
761 elseif (array_key_exists($skuPropertyCode, $newValues))
763 $skuValue = $newValues[$skuPropertyCode];
771 $newSkuPropertyValue = 0;
772 if (!empty($skuValue))
774 if (!is_array($skuValue))
776 $newSkuPropertyValue = (int)$skuValue;
780 $skuValue = current($skuValue);
781 if (!is_array($skuValue))
782 $newSkuPropertyValue = (int)$skuValue;
783 elseif (!empty($skuValue[
'VALUE']))
784 $newSkuPropertyValue = (int)$skuValue[
'VALUE'];
788 if ($newSkuPropertyValue < 0)
789 $newSkuPropertyValue = 0;
791 $currentSkuPropertyValue = 0;
792 if (!empty($currentValues[$skuPropertyId]) && is_array($currentValues[$skuPropertyId]))
794 $currentSkuProperty = current($currentValues[$skuPropertyId]);
795 if (!empty($currentSkuProperty[
'VALUE']))
796 $currentSkuPropertyValue = (int)$currentSkuProperty[
'VALUE'];
797 unset($currentSkuProperty);
799 if ($currentSkuPropertyValue < 0)
800 $currentSkuPropertyValue = 0;
803 if (!static::allowedPropertyHandler() || ($currentSkuPropertyValue != $newSkuPropertyValue))
805 self::$offers[$elementId] = array(
806 'CURRENT_PRODUCT' => $currentSkuPropertyValue,
807 'NEW_PRODUCT' => $newSkuPropertyValue,
808 'PRODUCT_IBLOCK_ID' => $iblockData[
'PRODUCT_IBLOCK_ID']
830 if (!static::allowedPropertyHandler())
833 self::calculateOfferChange((
int)$elementId, (
int)$iblockId);
855 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($iblockId);
856 if (empty($iblockData))
859 $skuPropertyId = $iblockData[
'SKU_PROPERTY_ID'];
860 if (!isset($propertyList[$skuPropertyId]))
862 $skuPropertyCode = (string)$propertyList[$skuPropertyId][
'CODE'];
863 if ($skuPropertyCode ===
'')
864 $skuPropertyCode = (string)$skuPropertyId;
868 if (array_key_exists($skuPropertyId, $newValues))
870 $skuValue = $newValues[$skuPropertyId];
873 elseif (array_key_exists($skuPropertyCode, $newValues))
875 $skuValue = $newValues[$skuPropertyCode];
882 $newSkuPropertyValue = 0;
883 if (!empty($skuValue))
885 if (!is_array($skuValue))
887 $newSkuPropertyValue = (int)$skuValue;
891 if (array_key_exists(
'VALUE', $skuValue))
893 $newSkuPropertyValue = (int)$skuValue[
'VALUE'];
897 foreach($skuValue as $row)
900 $newSkuPropertyValue = (
int)$row;
901 elseif (array_key_exists(
'VALUE', $row))
902 $newSkuPropertyValue = (int)$row[
'VALUE'];
909 if ($newSkuPropertyValue < 0)
910 $newSkuPropertyValue = 0;
912 $currentSkuPropertyValue = 0;
913 if (!empty($currentValues[$skuPropertyId]) && is_array($currentValues[$skuPropertyId]))
915 $currentSkuProperty = current($currentValues[$skuPropertyId]);
916 if (!empty($currentSkuProperty[
'VALUE']))
917 $currentSkuPropertyValue = (int)$currentSkuProperty[
'VALUE'];
918 unset($currentSkuProperty);
920 if ($currentSkuPropertyValue < 0)
921 $currentSkuPropertyValue = 0;
923 if (!static::allowedPropertyHandler() || ($currentSkuPropertyValue != $newSkuPropertyValue))
925 self::$offers[$elementId] = [
926 'CURRENT_PRODUCT' => $currentSkuPropertyValue,
927 'NEW_PRODUCT' => $newSkuPropertyValue,
928 'PRODUCT_IBLOCK_ID' => $iblockData[
'PRODUCT_IBLOCK_ID']
950 self::calculateOfferChange((
int)$elementId, (
int)$iblockId);
966 $productId = (int)$productId;
969 $iblockId = (int)$iblockId;
971 $iblockId = (int)\CIBlockElement::GetIBlockByID($productId);
976 $offerList = \CCatalogSku::getOffersList($productId, $iblockId, array(), array(
'ID',
'ACTIVE'));
977 if (!empty($offerList[$productId]))
980 $activeOffers = array_filter($offerList[$productId],
'\Bitrix\Catalog\Product\Sku::filterActive');
981 if (!empty($activeOffers))
983 $existOffers = Catalog\ProductTable::getList(array(
984 'select' => array(
'ID',
'AVAILABLE'),
988 if (!empty($existOffers))
992 unset($activeOffers);
1011 $productId = (int)$productId;
1012 if ($productId <= 0)
1034 $offerId = (int)$offerId;
1038 static::disableUpdateAvailable();
1039 $updateResult = Catalog\Model\Product::update($offerId, array(
'TYPE' => $type));
1040 $result = $updateResult->isSuccess();
1041 static::enableUpdateAvailable();
1052 self::$allowPropertyHandler++;
1062 self::$allowPropertyHandler--;
1072 return (self::$allowPropertyHandler >= 0);
1084 return (isset($row[
'ACTIVE']) && $row[
'ACTIVE'] ==
'Y');
1093 private static function isSeparateSkuMode(): bool
1095 if (self::$separateSkuMode ===
null)
1097 self::$separateSkuMode = Main\Config\Option::get(
'catalog',
'show_catalog_tab_with_offers') ===
'Y';
1100 return self::$separateSkuMode;
1112 private static function getParentDataAsProduct($productId, array $productFields = array())
1114 if (!isset($productFields[
'QUANTITY'])
1115 || !isset($productFields[
'QUANTITY_TRACE'])
1116 || !isset($productFields[
'CAN_BUY_ZERO'])
1118 $productFields = array_merge(Catalog\Model\
Product::getCacheItem($productId,
true), $productFields);
1135 private static function getProductAvailable($productId, array $productFields)
1139 if (isset($productFields[
'AVAILABLE']))
1143 isset($productFields[
'QUANTITY'])
1144 || isset($productFields[
'QUANTITY_TRACE'])
1145 || isset($productFields[
'CAN_BUY_ZERO'])
1149 !isset($productFields[
'QUANTITY'])
1150 || !isset($productFields[
'QUANTITY_TRACE'])
1151 || !isset($productFields[
'CAN_BUY_ZERO'])
1153 $productFields = array_merge(Catalog\Model\
Product::getCacheItem($productId,
true), $productFields);
1154 $fields[
'AVAILABLE'] = Catalog\ProductTable::calculateAvailable($productFields);
1169 private static function updateParentAvailable(
int $parentId,
int $parentIblockId): bool
1171 $parentIBlock = \CCatalogSku::GetInfoByIblock($parentIblockId);
1173 empty($parentIBlock)
1174 || (self::isSeparateSkuMode() && $parentIBlock[
'CATALOG_TYPE'] === \CCatalogSku::TYPE_FULL)
1180 $parentFields = static::getDefaultParentSettings(static::getOfferState(
1186 $iterator = Catalog\Model\Product::getList([
1194 $row = $iterator->fetch();
1197 $updateResult = Catalog\Model\Product::update($parentId, $parentFields);
1201 $parentFields[
'ID'] = $parentId;
1202 $updateResult = Catalog\Model\Product::add($parentFields);
1205 $result = $updateResult->isSuccess();
1206 unset($updateResult);
1221 private static function updateDeferredSkuList()
1223 if (!empty(self::$deferredUnknown))
1225 $list = array_keys(self::$deferredUnknown);
1227 foreach (array_chunk($list, 500) as $pageIds)
1229 $iterator = Catalog\ProductTable::getList(array(
1230 'select' => array(
'ID',
'TYPE'),
1236 while ($row = $iterator->fetch())
1238 $row[
'ID'] = (int)$row[
'ID'];
1240 self::migrateCalculateData(self::$deferredUnknown, self::$deferredSku, $row[
'ID']);
1242 self::migrateCalculateData(self::$deferredUnknown, self::$deferredOffers, $row[
'ID']);
1245 unset($row, $iterator, $pageIds, $list);
1247 self::$deferredUnknown = array();
1249 if (!empty(self::$deferredOffers))
1251 $productList = \CCatalogSku::getProductList(array_keys(self::$deferredOffers));
1252 if (!empty($productList))
1254 foreach ($productList as $id => $row)
1255 self::transferCalculationData(self::$deferredOffers, self::$deferredSku, $id, $row[
'ID'], $row[
'IBLOCK_ID']);
1258 unset($productList);
1260 self::$deferredOffers = array();
1274 private static function setCalculateData(array &$list, $id, $iblockId)
1276 static $priceTypes =
null,
1279 self::$calculateAvailable =
true;
1281 if ($priceTypes ===
null)
1284 $priceTypeKeys = array_fill_keys($priceTypes,
true);
1286 self::$calculatePriceTypes = $priceTypeKeys;
1288 if ($iblockId ===
null)
1290 if (isset($list[$id]))
1292 $iblockId = $list[$id][
'IBLOCK_ID'];
1297 'IBLOCK_ID' => $iblockId,
1298 self::ACTION_AVAILABLE =>
true,
1299 self::ACTION_PRICE => self::$calculatePriceTypes,
1300 self::ACTION_ELEMENT_TIMESTAMP =>
true,
1315 private static function setCalculatePriceTypes(array &$list, $id, $iblockId, array $priceTypes)
1317 static $allPriceTypes =
null;
1319 if ($allPriceTypes ===
null)
1324 if (empty($priceTypes))
1326 $priceTypes = $allPriceTypes;
1329 foreach ($priceTypes as $typeId)
1331 self::$calculatePriceTypes[$typeId] =
true;
1334 if ($iblockId ===
null)
1336 if (isset($list[$id]))
1338 $iblockId = $list[$id][
'IBLOCK_ID'];
1342 if (!isset($list[$id]))
1345 'IBLOCK_ID' => $iblockId,
1346 self::ACTION_PRICE => array_fill_keys($priceTypes,
true),
1347 self::ACTION_ELEMENT_TIMESTAMP =>
true,
1350 elseif (!isset($list[$id][self::ACTION_PRICE]))
1352 if ($iblockId !==
null)
1354 $list[$id][
'IBLOCK_ID'] = $iblockId;
1361 if ($iblockId !==
null)
1363 $list[$id][
'IBLOCK_ID'] = $iblockId;
1365 foreach ($priceTypes as $typeId)
1385 private static function migrateCalculateData(array &$source, array &$destination, $id)
1387 if (!isset($source[$id]))
1390 if (isset($destination[$id]))
1392 if (isset($source[$id][self::ACTION_AVAILABLE]))
1393 self::setCalculateData($destination, $id, $source[$id][
'IBLOCK_ID']);
1394 elseif (isset($source[$id][self::ACTION_PRICE]))
1395 self::setCalculatePriceTypes($destination, $id, $source[$id][
'IBLOCK_ID'], array_keys($source[$id][self::ACTION_PRICE]));
1399 $destination[$id] = $source[$id];
1401 unset($source[$id]);
1416 private static function transferCalculationData(array &$source, array &$destination, $sourceId, $destinationId, $iblockId)
1418 if (!isset($source[$sourceId]))
1421 if (isset($destination[$destinationId]))
1423 if (isset($source[$sourceId][self::ACTION_AVAILABLE]))
1424 self::setCalculateData($destination, $destinationId, $iblockId);
1425 elseif (isset($source[$sourceId][self::ACTION_PRICE]))
1426 self::setCalculatePriceTypes($destination, $destinationId, $iblockId, array_keys($source[$sourceId][self::ACTION_PRICE]));
1430 $destination[$destinationId] = $source[$sourceId];
1431 $destination[$destinationId][
'IBLOCK_ID'] = $iblockId;
1433 unset($source[$sourceId]);
1442 private static function clearStepData()
1444 self::$skuExist = array();
1445 self::$skuAvailable = array();
1446 self::$offersIds = array();
1447 self::$offersMap = array();
1448 self::$skuPrices = array();
1457 private static function loadProductIblocks()
1460 foreach (array_keys(self::$deferredSku) as $id)
1462 if (self::$deferredSku[$id][
'IBLOCK_ID'] ===
null)
1466 if (!empty($listIds))
1468 $data = \CIBlockElement::GetIBlockByIDList($listIds);
1469 foreach ($data as $id => $iblockId)
1471 self::$deferredSku[$id][
'IBLOCK_ID'] = $iblockId;
1473 unset($id, $iblockId);
1486 private static function loadProductData(array $listIds)
1488 $iterator = Catalog\Model\Product::getList(array(
1489 'select' => array(
'ID'),
1490 'filter' => array(
'@ID' => $listIds)
1492 while ($row = $iterator->fetch())
1494 $row[
'ID'] = (int)$row[
'ID'];
1495 self::$skuExist[$row[
'ID']] =
true;
1497 unset($row, $iterator);
1498 $offers = \CCatalogSku::getOffersList(
1502 array(
'ID',
'ACTIVE',
'AVAILABLE')
1504 foreach ($listIds as $id)
1511 $allOffers = array();
1512 $availableOffers = array();
1513 foreach (
$offers[$id] as $offerId => $row)
1515 $allOffers[] = $offerId;
1516 if ($row[
'ACTIVE'] !=
'Y' || $row[
'AVAILABLE'] !=
'Y')
1519 $availableOffers[] = $offerId;
1521 self::$skuPrices[$id] = array();
1522 if (self::$skuAvailable[$id] == self::OFFERS_AVAILABLE)
1524 foreach ($availableOffers as $offerId)
1526 self::$offersMap[$offerId] = $id;
1527 self::$offersIds[] = $offerId;
1532 foreach ($allOffers as $offerId)
1534 self::$offersMap[$offerId] = $id;
1535 self::$offersIds[] = $offerId;
1539 unset($offerId, $availableOffers, $allOffers, $id);
1541 if (!self::isSeparateSkuMode())
1542 self::loadProductPrices();
1553 private static function updateProductData(array $listIds): void
1555 $separateMode = self::isSeparateSkuMode();
1556 if (self::$calculateAvailable)
1558 $iblockData =
false;
1560 foreach ($listIds as $id)
1562 if (empty(self::$deferredSku[$id][self::ACTION_AVAILABLE]))
1564 if (empty(self::$deferredSku[$id][
'IBLOCK_ID']))
1566 if (!isset(self::$skuAvailable[$id]))
1569 if ($iblockId !== self::$deferredSku[$id][
'IBLOCK_ID'])
1571 $iblockId = self::$deferredSku[$id][
'IBLOCK_ID'];
1572 $iblockData = \CCatalogSku::GetInfoByIBlock(self::$deferredSku[$id][
'IBLOCK_ID']);
1574 if (empty($iblockData))
1580 self::$skuAvailable[$id],
1581 $iblockData[
'CATALOG_TYPE'] == \CCatalogSku::TYPE_PRODUCT
1592 'TYPE' => $fields[
'TYPE'],
1596 if (isset(self::$skuExist[$id]))
1598 $result = Catalog\Model\Product::update($id, $fields);
1599 unset(self::$skuExist[$id]);
1603 $fields[
'ID'] = $id;
1604 $result = Catalog\Model\Product::add($fields);
1607 unset($result, $id);
1612 self::updateProductPrices($listIds);
1624 private static function loadProductPrices()
1626 if (empty(self::$calculatePriceTypes) || empty(self::$offersIds))
1629 sort(self::$offersIds);
1630 foreach (array_chunk(self::$offersIds, 500) as $pageOfferIds)
1632 $filter = Main\Entity\Query::filter();
1633 $filter->whereIn(
'PRODUCT_ID', $pageOfferIds);
1634 $filter->whereIn(
'CATALOG_GROUP_ID', self::$calculatePriceTypes);
1635 $filter->where(Main\Entity\Query::filter()->logic(
'or')->where(
'QUANTITY_FROM',
'<=', 1)->whereNull(
'QUANTITY_FROM'));
1636 $filter->where(Main\Entity\Query::filter()->logic(
'or')->where(
'QUANTITY_TO',
'>=', 1)->whereNull(
'QUANTITY_TO'));
1638 $iterator = Catalog\PriceTable::getList(array(
1640 'PRODUCT_ID',
'CATALOG_GROUP_ID',
'PRICE',
'CURRENCY',
1641 'PRICE_SCALE',
'TMP_ID'
1643 'filter' => $filter,
1644 'order' => array(
'PRODUCT_ID' =>
'ASC',
'CATALOG_GROUP_ID' =>
'ASC')
1646 while ($row = $iterator->fetch())
1654 $typeId = (int)$row[
'CATALOG_GROUP_ID'];
1655 $offerId = (int)$row[
'PRODUCT_ID'];
1657 if (!isset(self::$deferredSku[$productId][self::ACTION_PRICE][$typeId]))
1659 unset($row[
'PRODUCT_ID']);
1661 if (!isset(self::$skuPrices[$productId][$typeId]))
1662 self::$skuPrices[
$productId][$typeId] = $row;
1663 elseif (self::$skuPrices[$productId][$typeId][
'PRICE_SCALE'] > $row[
'PRICE_SCALE'])
1664 self::$skuPrices[
$productId][$typeId] = $row;
1666 unset($row, $iterator);
1683 private static function updateProductPrices(array $listIds)
1685 if (empty(self::$calculatePriceTypes))
1690 if (!empty(self::$skuPrices))
1692 $existIds = array();
1693 $existIdsByType = array();
1694 $iterator = Catalog\PriceTable::getList(array(
1695 'select' => array(
'ID',
'CATALOG_GROUP_ID',
'PRODUCT_ID'),
1696 'filter' => array(
'@PRODUCT_ID' => $listIds,
'@CATALOG_GROUP_ID' => self::$calculatePriceTypes),
1697 'order' => array(
'ID' =>
'ASC')
1699 while ($row = $iterator->fetch())
1701 $row[
'ID'] = (int)$row[
'ID'];
1702 $priceTypeId = (int)$row[
'CATALOG_GROUP_ID'];
1704 $existIds[$row[
'ID']] = $row[
'ID'];
1705 if (!isset($existIdsByType[$productId]))
1706 $existIdsByType[$productId] = array();
1707 if (!isset($existIdsByType[$productId][$priceTypeId]))
1708 $existIdsByType[$productId][$priceTypeId] = array();
1709 $existIdsByType[
$productId][$priceTypeId][] = $row[
'ID'];
1711 unset($row, $iterator);
1712 foreach ($listIds as $productId)
1714 if (!isset(self::$skuPrices[$productId]))
1717 foreach (array_keys(self::$skuPrices[$productId]) as $resultPriceType)
1720 $row = self::$skuPrices[
$productId][$resultPriceType];
1721 if (!empty($existIdsByType[$productId][$resultPriceType]))
1723 $rowId = array_shift($existIdsByType[$productId][$resultPriceType]);
1724 unset($existIds[$rowId]);
1726 if ($rowId ===
null)
1729 $row[
'CATALOG_GROUP_ID'] = $resultPriceType;
1730 $rowResult = Catalog\PriceTable::add($row);
1734 $rowResult = Catalog\PriceTable::update($rowId, $row);
1736 if (!$rowResult->isSuccess())
1743 unset($row, $rowResult, $resultPriceType);
1745 unset($existIdsByType);
1749 if (!empty($existIds))
1751 $conn = Main\Application::getConnection();
1752 $helper = $conn->getSqlHelper();
1754 foreach (array_chunk($existIds, 500) as $pageIds)
1756 $conn->queryExecute(
1757 'delete from '.$tableName.
' where '.$helper->quote(
'ID').
' in ('.implode(
',', $pageIds).
')'
1761 unset($helper, $conn);
1768 $conn = Main\Application::getConnection();
1769 $helper = $conn->getSqlHelper();
1770 $conn->queryExecute(
1772 ' where '.$helper->quote(
'PRODUCT_ID').
' in ('.implode(
',', $listIds).
')'.
1773 ' and '.$helper->quote(
'CATALOG_GROUP_ID').
' in ('.implode(
',', self::$calculatePriceTypes).
')'
1775 unset($helper, $conn);
1779 private static function updateElements(array $listIds): void
1781 if (self::isSeparateSkuMode())
1785 $conn = \Bitrix\Main\Application::getConnection();
1786 if (self::$queryElementTimestamp ===
null)
1788 $helper = $conn->getSqlHelper();
1790 .
' set ' . $helper->quote(
'TIMESTAMP_X') .
' = ' . $helper->getCurrentDateTimeFunction()
1791 .
' where ' . $helper->quote(
'ID') .
'=';
1793 foreach ($listIds as $id)
1795 if (empty(self::$deferredSku[$id][self::ACTION_ELEMENT_TIMESTAMP]))
1797 $conn->queryExecute(self::$queryElementTimestamp . $id);
1809 private static function updateProductFacetIndex(array $listIds): void
1811 foreach ($listIds as $id)
1813 if (empty(self::$deferredSku[$id][
'IBLOCK_ID']))
1815 if (!isset(self::$skuAvailable[$id]))
1817 Iblock\PropertyIndex\Manager::updateElementIndex(
1818 self::$deferredSku[$id][
'IBLOCK_ID'],
1832 private static function calculateOfferChange(
int $elementId,
int $iblockId): void
1834 if (!isset(self::$offers[$elementId]))
1837 $iblockData = \CCatalogSku::GetInfoByOfferIBlock($iblockId);
1838 if (!empty($iblockData))
1840 $offerDescr = self::$offers[$elementId];
1841 $existCurrentProduct = ($offerDescr[
'CURRENT_PRODUCT'] > 0);
1842 $existNewProduct = ($offerDescr[
'NEW_PRODUCT'] > 0);
1843 if ($existCurrentProduct)
1846 $offerDescr[
'CURRENT_PRODUCT'],
1847 $iblockData[
'PRODUCT_IBLOCK_ID'],
1851 if ($existNewProduct)
1854 $offerDescr[
'NEW_PRODUCT'],
1855 $iblockData[
'PRODUCT_IBLOCK_ID'],
1859 if (!$existCurrentProduct || !$existNewProduct)
1864 ? Catalog\ProductTable::TYPE_OFFER
1865 : Catalog\ProductTable::TYPE_FREE_OFFER
1867 $result = Catalog\Model\Product::update($elementId, array(
'TYPE' => $type));
1871 unset($existNewProduct, $existCurrentProduct);
1874 unset(self::$offers[$elementId]);