1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
step_operations.php
См. документацию.
1<?php
2
3use Bitrix\Main\localization\Loc;
8
10{
11 public const DEFAULT_SESSION_PREFIX = 'CC';
12
13 protected $sessID = '';
14 protected $errorCounter = 0;
15 protected $errors = array();
16 protected $stepErrors = array();
17 protected $maxExecutionTime = 0;
18 protected $maxOperationCounter = 0;
19 protected $startOperationTime = 0;
20 protected $lastID = 0;
21 protected $allCounter = 0;
22 protected $allOperationCounter = 0;
23 protected $finishOperation = false;
24 protected $defaultProgressTemplate = '#PROGRESS_BAR#';
25 protected $progressTemplate = '#PROGRESS_BAR#';
26 protected $errorTemplate = '';
27 protected $params = null;
28
30 {
31 $sessID = (string)$sessID;
32 if ($sessID == '')
33 $sessID = self::DEFAULT_SESSION_PREFIX.time();
34 $this->sessID = $sessID;
35 $this->errorCounter = 0;
36 $this->errors = array();
37 $this->stepErrors = array();
39 if ($maxExecutionTime < 0)
41 $this->maxExecutionTime = $maxExecutionTime;
45 $this->maxOperationCounter = $maxOperationCounter;
46 $this->startOperationTime = time();
47 $this->finishOperation = false;
48 $this->progressTemplate = Loc::getMessage('BX_STEP_OPERATION_PROGRESS_TEMPLATE').$this->defaultProgressTemplate;
49 }
50
51 public function __destruct()
52 {
53 if ($this->sessID != '' && isset($_SESSION[$this->sessID]))
54 unset($_SESSION[$this->sessID]);
55 }
56
57 public function setParams($params)
58 {
59 if (!empty($params) && is_array($params))
60 $this->params = $params;
61 }
62
63 public function initStep($allCount, $allOperationCount, $lastID)
64 {
65 if (isset($_SESSION[$this->sessID]) && is_array($_SESSION[$this->sessID]))
66 {
67 if (isset($_SESSION[$this->sessID]['ERRORS_COUNTER']) && (int)$_SESSION[$this->sessID]['ERRORS_COUNTER'] > 0)
68 $this->errorCounter = (int)$_SESSION[$this->sessID]['ERRORS_COUNT'];
69 }
70 $this->stepErrors = array();
71 $lastID = (int)$lastID;
72 if ($lastID < 0)
73 $lastID = 0;
74 $this->lastID = $lastID;
75 $allCount = (int)$allCount;
76 if ($allCount < 0)
77 $allCount = 0;
78 $this->allCounter = $allCount;
79 $allOperationCount = (int)$allOperationCount;
80 if ($allOperationCount < 0)
81 $allOperationCount = 0;
82 $this->allOperationCounter = $allOperationCount;
83 }
84
85 public function saveStep()
86 {
87 if (!isset($_SESSION[$this->sessID]) || !is_array($_SESSION[$this->sessID]))
88 $_SESSION[$this->sessID] = array();
89 if ($this->errorCounter > 0)
90 {
91 if (!empty($this->stepErrors))
92 $this->errors = $this->stepErrors;
93 $_SESSION[$this->sessID]['ERRORS_COUNTER'] = $this->errorCounter;
94 }
95
97
98 return array(
99 'sessID' => $this->sessID,
100 'maxExecutionTime' => $this->maxExecutionTime,
101 'maxOperationCounter' => $this->maxOperationCounter,
102 'lastID' => $this->lastID,
103 'allCounter' => $this->allCounter,
104 'counter' => $this->allCounter,
105 'allOperationCounter' => $this->allOperationCounter,
106 'operationCounter' => $this->allOperationCounter,
107 'errorCounter' => $this->errorCounter,
108 'errors' => (!empty($this->stepErrors) ? '<p>'.implode('</p><p>', $this->stepErrors).'</p>' : ''),
109 'finishOperation' => $this->finishOperation,
110 'message' => $this->getMessage()
111 );
112 }
113
114 public function startOperation()
115 {
116
117 }
118
119 public function finalOperation()
120 {
121
122 }
123
124 public function runOperation()
125 {
126
127 }
128
129 public function run()
130 {
131 $this->startOperation();
132 $this->runOperation();
133 $this->finalOperation();
134 }
135
137 {
138 $template = (string)$template;
139 if ($template !== '')
140 $this->progressTemplate = $template.$this->defaultProgressTemplate;
141 }
142
143 public function getMessage()
144 {
145 $messageParams = array(
146 'MESSAGE' => '',
147 'PROGRESS_TOTAL' => $this->allCounter,
148 'PROGRESS_VALUE' => $this->allOperationCounter,
149 'TYPE' => 'PROGRESS',
150 'DETAILS' => str_replace(array('#ALL#', '#COUNT#'), array($this->allCounter, $this->allOperationCounter), $this->progressTemplate),
151 'HTML' => true
152 );
153 $message = new CAdminMessage($messageParams);
154 return $message->Show();
155 }
156
157 public static function getAllCounter()
158 {
159 return 0;
160 }
161
162 public static function getDefaultExecutionTime()
163 {
164 $executionTime = (int)ini_get('max_execution_time');
165 if ($executionTime <= 0)
166 $executionTime = 60;
167 return (int)(2*$executionTime/3);
168 }
169
170 protected function setLastId($lastId)
171 {
172 $this->allOperationCounter++;
173 $this->lastID = $lastId;
174 }
175
176 protected function isStopOperation()
177 {
178 return ($this->maxExecutionTime > 0 && (time() - $this->startOperationTime > $this->maxExecutionTime));
179 }
180
181 protected function setFinishOperation($finish)
182 {
183 $this->finishOperation = ($finish === true);
184 }
185
186 protected function calculateNextOperationCounter()
187 {
188 if (!$this->finishOperation)
189 {
190 $period = time() - $this->startOperationTime;
191 if ($this->maxExecutionTime > 2*$period)
192 $this->maxOperationCounter = $this->maxOperationCounter*2;
193 elseif ($period >= $this->maxExecutionTime)
194 $this->maxOperationCounter = (int)(($this->maxOperationCounter*2)/3);
195 unset($period);
196 if ($this->maxOperationCounter < 10)
197 $this->maxOperationCounter = 10;
198 }
199 }
200
201 protected function addError($error)
202 {
203 $error = (string)$error;
204 if ($error === '')
205 return;
206 $this->stepErrors[] = $error;
207 $this->errorCounter++;
208 }
209}
210
212{
213 const SESSION_PREFIX = 'PSA';
214
216 {
217 $sessID = (string)$sessID;
218 if ($sessID == '')
219 $sessID = self::SESSION_PREFIX.time();
220 parent::__construct($sessID, $maxExecutionTime, $maxOperationCounter);
221 }
222
223 public function runOperation()
224 {
225 global $DB;
226
227 $tableName = '';
228 switch (mb_strtoupper($DB->type))
229 {
230 case 'MYSQL':
231 $tableName = 'b_catalog_product_sets';
232 break;
233 case 'MSSQL':
234 $tableName = 'B_CATALOG_PRODUCT_SETS';
235 break;
236 case 'ORACLE':
237 $tableName = 'B_CATALOG_PRODUCT_SETS';
238 break;
239 }
240 if ($tableName == '')
241 return;
242
243 $emptyList = true;
244 CTimeZone::Disable();
245 $filter = array('TYPE' => CCatalogProductSet::TYPE_SET, 'SET_ID' => 0);
246 if ($this->lastID > 0)
247 $filter['>ID'] = $this->lastID;
248 $topCount = ($this->maxOperationCounter > 0 ? array('nTopCount' => $this->maxOperationCounter) : false);
249 $productSetsIterator = CCatalogProductSet::getList(
250 array('ID' => 'ASC'),
251 $filter,
252 false,
253 $topCount,
254 array('ID', 'OWNER_ID', 'ITEM_ID', 'MODIFIED_BY', 'TIMESTAMP_X')
255 );
256 while ($productSet = $productSetsIterator->Fetch())
257 {
258 $emptyList = false;
259 $productSet['MODIFIED_BY'] = (int)$productSet['MODIFIED_BY'];
260 if ($productSet['MODIFIED_BY'] == 0)
261 $productSet['MODIFIED_BY'] = false;
262 CCatalogProductSet::recalculateSet($productSet['ID'], $productSet['ITEM_ID']);
263 $arTimeFields = array(
264 '~TIMESTAMP_X' => $DB->CharToDateFunction($productSet['TIMESTAMP_X'], "FULL"),
265 'MODIFIED_BY' => $productSet['MODIFIED_BY']
266 );
267 $strUpdate = $DB->PrepareUpdate($tableName, $arTimeFields);
268 if (!empty($strUpdate))
269 {
270 $strQuery = "update ".$tableName." set ".$strUpdate." where ID = ".$productSet['ID'];
271 $DB->Query($strQuery);
272 }
273 $this->setLastId($productSet['ID']);
274 if ($this->isStopOperation())
275 break;
276 }
277 CTimeZone::Enable();
278 $this->setFinishOperation($emptyList);
279 }
280
281 public static function getAllCounter()
282 {
283 return (int)CCatalogProductSet::getList(
284 array(),
285 array('TYPE' => CCatalogProductSet::TYPE_SET, 'SET_ID' => 0),
286 array()
287 );
288 }
289}
290
292{
293 const SESSION_PREFIX = 'PA';
294
295 protected $config = array();
296
297 protected $preloadTooLong = false;
298
299 protected $iblockData = null;
300 protected $productList = array();
301 protected $currentList = array();
302 protected $currentIdsList = array();
303 private $offersMap = array();
304 private $offersIds = array();
305 private $prices = array();
306 private $calculatePrices = array();
307 private $existPriceIds = array();
308 private $existIdsByType = array();
309 private $measureRatios = array();
310 private $currencyReference = array();
311 private $measureIds = [
312 'DEFAULT' => null,
313 'BASE' => null
314 ];
315
317 protected $useSets = false;
319 protected $separateSkuMode = false;
321 protected $extendedMode = false;
322
324 {
325 $sessID = (string)$sessID;
326 if ($sessID == '')
327 $sessID = self::SESSION_PREFIX.time();
328 parent::__construct($sessID, $maxExecutionTime, $maxOperationCounter);
329 $this->preloadTooLong = false;
330 $this->initConfig();
331 $this->setOldConfig();
332 $this->initReferences();
333 }
334
335 public function isUseSets()
336 {
337 return $this->config['CHECK_SETS'];
338 }
339
340 public function isSeparateSkuMode()
341 {
342 return $this->config['SEPARATE_SKU_MODE'];
343 }
344
345 public function runOperation()
346 {
347 if (!isset($this->params['IBLOCK_ID']))
348 return;
349 $this->params['IBLOCK_ID'] = (int)$this->params['IBLOCK_ID'];
350 if ($this->params['IBLOCK_ID'] <= 0)
351 return;
352 $this->iblockData = CCatalogSku::GetInfoByIBlock($this->params['IBLOCK_ID']);
353 if (empty($this->iblockData))
354 return;
355
356 $this->currentList = array();
357 $this->currentIdsList = array();
358 $productIterator = $this->getProductIterator(array(), array());
359 while ($product = $productIterator->fetch())
360 {
361 $product['PRODUCT_ID'] = (int)$product['PRODUCT_ID'];
362 $product['PRODUCT_EXISTS'] = $product['PRODUCT_ID'] > 0;
363 $this->currentList[$product['ID']] = $product;
364 $this->currentIdsList[] = $product['ID'];
365 }
366 unset($product, $productIterator);
367
368 if (!empty($this->currentList))
369 {
370 switch ($this->iblockData['CATALOG_TYPE'])
371 {
372 case CCatalogSku::TYPE_CATALOG:
373 $this->loadProductPrices();
374 break;
375 case CCatalogSku::TYPE_OFFERS:
376 $this->loadOffersData();
377 $this->loadProductPrices();
378 break;
379 case CCatalogSku::TYPE_FULL:
380 case CCatalogSku::TYPE_PRODUCT:
381 $this->loadSkuData();
382 $this->loadProductPrices();
383 $this->loadSkuPrices();
384 break;
385 }
386 $this->loadProductSets();
387 $this->loadMeasureRatios();
388 if ($this->checkPreloadTime())
389 $this->updateProductData();
390 }
391
392 $this->setFinishOperation(empty($this->currentIdsList));
393 $this->currentList = array();
394 $this->currentIdsList = array();
395 }
396
397 public function getMessage()
398 {
399 if (empty($this->iblockData))
400 return parent::getMessage();
401
402 $title = '';
403
404 switch ($this->iblockData['CATALOG_TYPE'])
405 {
406 case CCatalogSku::TYPE_CATALOG:
408 'BX_STEP_OPERATION_CATALOG_TITLE',
409 array(
410 '#ID#' => $this->iblockData['IBLOCK_ID'],
411 '#NAME#' => htmlspecialcharsbx(CIBlock::GetArrayByID($this->iblockData['IBLOCK_ID'], 'NAME'))
412 )
413 );
414 break;
415 case CCatalogSku::TYPE_OFFERS:
417 'BX_STEP_OPERATION_OFFERS_TITLE',
418 array(
419 '#ID#' => $this->iblockData['PRODUCT_IBLOCK_ID'],
420 '#NAME#' => htmlspecialcharsbx(CIBlock::GetArrayByID($this->iblockData['PRODUCT_IBLOCK_ID'], 'NAME'))
421 )
422 );
423 break;
424 case CCatalogSku::TYPE_PRODUCT:
425 case CCatalogSku::TYPE_FULL:
427 'BX_STEP_OPERATION_CATALOG_TITLE',
428 array(
429 '#ID#' => $this->iblockData['PRODUCT_IBLOCK_ID'],
430 '#NAME#' => htmlspecialcharsbx(CIBlock::GetArrayByID($this->iblockData['PRODUCT_IBLOCK_ID'], 'NAME'))
431 )
432 );
433 break;
434 }
435
436 $messageParams = array(
437 'MESSAGE' => $title,
438 'PROGRESS_TOTAL' => $this->allCounter,
439 'PROGRESS_VALUE' => $this->allOperationCounter,
440 'TYPE' => 'PROGRESS',
441 'DETAILS' => str_replace(array('#ALL#', '#COUNT#'), array($this->allCounter, $this->allOperationCounter), $this->progressTemplate),
442 'HTML' => true
443 );
444 $message = new CAdminMessage($messageParams);
445 return $message->Show();
446 }
447
448 public static function getIblockList($iblockId)
449 {
450 $result = array();
451 $iblockId = (int)$iblockId;
452 if ($iblockId <= 0)
453 return $result;
454
455 $iblockData = CCatalogSku::GetInfoByIBlock($iblockId);
456 if (empty($iblockData))
457 return $result;
458
459 switch ($iblockData['CATALOG_TYPE'])
460 {
461 case CCatalogSku::TYPE_CATALOG:
462 $iblockName = CIBlock::GetArrayByID($iblockData['IBLOCK_ID'], 'NAME');
463 $result[] = array(
464 'ID' => $iblockData['IBLOCK_ID'],
465 'NAME' => $iblockName,
466 'TITLE' => Loc::getMessage(
467 'BX_STEP_OPERATION_CATALOG_TITLE',
468 array(
469 '#ID#' => $iblockData['IBLOCK_ID'],
470 '#NAME#' => $iblockName
471 )
472 ),
473 'COUNT' => static::getIblockCounter($iblockData['IBLOCK_ID'])
474 );
475 unset($iblockName);
476 break;
477 case CCatalogSku::TYPE_OFFERS:
478 $result[] = array(
479 'ID' => $iblockData['IBLOCK_ID'],
480 'NAME' => CIBlock::GetArrayByID($iblockData['IBLOCK_ID'], 'NAME'),
481 'TITLE' => Loc::getMessage(
482 'BX_STEP_OPERATION_OFFERS_TITLE',
483 array(
484 '#ID#' => $iblockData['PRODUCT_IBLOCK_ID'],
485 '#NAME#' => CIBlock::GetArrayByID($iblockData['PRODUCT_IBLOCK_ID'], 'NAME')
486 )
487 ),
488 'COUNT' => static::getIblockCounter($iblockData['IBLOCK_ID'])
489 );
490 break;
491 case CCatalogSku::TYPE_PRODUCT:
492 case CCatalogSku::TYPE_FULL:
493 $iblockName = CIBlock::GetArrayByID($iblockData['PRODUCT_IBLOCK_ID'], 'NAME');
494 $result[] = array(
495 'ID' => $iblockData['IBLOCK_ID'],
496 'NAME' => CIBlock::GetArrayByID($iblockData['IBLOCK_ID'], 'NAME'),
497 'TITLE' => Loc::getMessage(
498 'BX_STEP_OPERATION_OFFERS_TITLE',
499 array(
500 '#ID#' => $iblockData['PRODUCT_IBLOCK_ID'],
501 '#NAME#' => $iblockName
502 )
503 ),
504 'COUNT' => static::getIblockCounter($iblockData['IBLOCK_ID'])
505 );
506 $result[] = array(
507 'ID' => $iblockData['PRODUCT_IBLOCK_ID'],
508 'NAME' => $iblockName,
509 'TITLE' => Loc::getMessage(
510 'BX_STEP_OPERATION_CATALOG_TITLE',
511 array(
512 '#ID#' => $iblockData['PRODUCT_IBLOCK_ID'],
513 '#NAME#' => $iblockName
514 )
515 ),
516 'COUNT' => static::getIblockCounter($iblockData['PRODUCT_IBLOCK_ID'])
517 );
518 unset($iblockName);
519 break;
520 }
521 unset($iblockData);
522
523 return $result;
524 }
525
526 protected function checkPreloadTime()
527 {
528 $this->preloadTooLong = ($this->maxExecutionTime > 0 && (time() - $this->startOperationTime > ($this->maxExecutionTime/2)));
529 return !$this->preloadTooLong;
530 }
531
532 protected function calculateNextOperationCounter()
533 {
534 if (!$this->finishOperation)
535 {
536 if ($this->preloadTooLong)
537 {
538 $this->maxOperationCounter = (int)(($this->maxOperationCounter*2)/3);
539 return;
540 }
541 $period = time() - $this->startOperationTime;
542 if ($this->maxExecutionTime > 2*$period)
543 $this->maxOperationCounter = $this->maxOperationCounter*2;
544 elseif ($period >= $this->maxExecutionTime)
545 $this->maxOperationCounter = (int)(($this->maxOperationCounter*2)/3);
546 unset($period);
547 if ($this->maxOperationCounter < 10)
548 $this->maxOperationCounter = 10;
549 elseif($this->maxOperationCounter > 500)
550 $this->maxOperationCounter = 500;
551 }
552 }
553
554 protected function initConfig()
555 {
556 $this->config = array(
557 'SEPARATE_SKU_MODE' => (string)Main\Config\Option::get('catalog', 'show_catalog_tab_with_offers') == 'Y',
558 'CHECK_AVAILABLE' => true,
559 'CHECK_SKU_PRICES' => true,
560 'CHECK_PRICES' => false,
561 'CHECK_SETS' => Catalog\Config\Feature::isProductSetsEnabled(),
562 'CHECK_MEASURE_RATIO' => false,
563 'CHECK_MEASURE' => false,
564 'UPDATE_ONLY' => false
565 );
566 }
567
568 protected function setOldConfig()
569 {
570 $this->useSets = $this->config['CHECK_SETS'];
571 $this->separateSkuMode = $this->config['SEPARATE_SKU_MODE'];
572 $this->extendedMode = false;
573 }
574
575 protected function initReferences()
576 {
577 $this->initCurrencyReference();
578 $this->initMeasures();
579 }
580
581 private function initCurrencyReference()
582 {
583 $this->currencyReference = [];
584 if (!$this->config['CHECK_PRICES'])
585 return;
586 $iterator = Currency\CurrencyTable::getList([
587 'select' => ['CURRENCY', 'CURRENT_BASE_RATE']
588 ]);
589 while ($row = $iterator->fetch())
590 $this->currencyReference[$row['CURRENCY']] = (float)$row['CURRENT_BASE_RATE'];
591 unset($row, $iterator);
592 }
593
594 private function initMeasures()
595 {
597 if (!empty($measure))
598 {
599 if ($measure['ID'] > 0)
600 $this->measureIds['DEFAULT'] = $measure['ID'];
601 }
603 array(),
604 array('=CODE' => CCatalogMeasure::DEFAULT_MEASURE_CODE),
605 false,
606 false,
607 array('ID', 'CODE')
608 );
609 $measure = $iterator->Fetch();
610 unset($iterator);
611 if (!empty($measure))
612 {
613 $this->measureIds['BASE'] = $measure['ID'];
614 }
615 unset($measure);
616 }
617
619 protected function runOperationFullCatalog(){}
620
622 protected function runOperationProductIblock(){}
623
625 protected function runOperationCatalog(){}
626
628 protected function runOperationOfferIblock(){}
629
636 protected function runExtendedOperation(array $product){}
637
638 protected function getProductIterator($filter, $select)
639 {
640 $select[] = 'ID';
641 $select[] = 'IBLOCK_ID';
642 $select[] = 'ACTIVE';
643 $select[] = 'NAME';
644 $select['PRODUCT_ID'] = 'PRODUCT.ID';
645 $select['QUANTITY'] = 'PRODUCT.QUANTITY';
646 $select['QUANTITY_TRACE'] = 'PRODUCT.QUANTITY_TRACE';
647 $select['CAN_BUY_ZERO'] = 'PRODUCT.CAN_BUY_ZERO';
648 $select['TYPE'] = 'PRODUCT.TYPE';
649 $select['MEASURE'] = 'PRODUCT.MEASURE';
650
651 if ($this->lastID > 0)
652 $filter['>ID'] = $this->lastID;
653 $filter['=IBLOCK_ID'] = $this->params['IBLOCK_ID'];
654 $filter['=WF_PARENT_ELEMENT_ID'] = null;
655 if ($this->config['UPDATE_ONLY'])
656 $filter['!==PRODUCT.ID'] = null;
657
659 'select' => $select,
660 'filter' => $filter,
661 'order' => array('ID' => 'ASC'),
662 'runtime' => array(
663 'PRODUCT' => new Main\Entity\ReferenceField(
664 'PRODUCT',
665 'Bitrix\Catalog\Product',
666 array('=this.ID' => 'ref.ID'),
667 array('join_type' => 'LEFT')
668 )
669 )
670 );
671 if ($this->maxOperationCounter > 0)
674 }
675
676 protected static function getIblockCounter($iblockId)
677 {
678 $iblockId = (int)$iblockId;
679 if ($iblockId <= 0)
680 return 0;
681
683 '=IBLOCK_ID' => $iblockId,
684 '=WF_PARENT_ELEMENT_ID' => null
685 ));
686 }
687
688 private function loadSkuData()
689 {
690 if (empty($this->currentList))
691 return;
692 $offers = \CCatalogSku::getOffersList(
693 $this->currentIdsList,
694 $this->params['IBLOCK_ID'],
695 array(),
696 array('ID', 'ACTIVE', 'AVAILABLE')
697 );
698 foreach ($this->currentIdsList as $id)
699 {
700 $this->currentList[$id]['SKU_STATE'] = Catalog\Product\Sku::OFFERS_NOT_EXIST;
701 $this->currentList[$id]['SET_EXISTS'] = false;
702 $this->currentList[$id]['BUNDLE_EXISTS'] = false;
703 if (empty($offers[$id]))
704 continue;
705
706 $this->currentList[$id]['SKU_STATE'] = Catalog\Product\Sku::OFFERS_NOT_AVAILABLE;
707 $allOffers = array();
708 $availableOffers = array();
709 foreach ($offers[$id] as $offerId => $row)
710 {
711 $allOffers[] = $offerId;
712 if ($row['ACTIVE'] != 'Y' || $row['AVAILABLE'] != 'Y')
713 continue;
714 $this->currentList[$id]['SKU_STATE'] = Catalog\Product\Sku::OFFERS_AVAILABLE;
715 $availableOffers[] = $offerId;
716 }
717
718 $this->calculatePrices[$id] = [];
719 if ($this->config['CHECK_SKU_PRICES'] && !$this->config['SEPARATE_SKU_MODE'])
720 {
721 if ($this->currentList[$id]['SKU_STATE'] == Catalog\Product\Sku::OFFERS_AVAILABLE)
722 {
723 foreach ($availableOffers as $offerId)
724 {
725 $this->offersMap[$offerId] = $id;
726 $this->offersIds[] = $offerId;
727 }
728 }
729 else
730 {
731 foreach ($allOffers as $offerId)
732 {
733 $this->offersMap[$offerId] = $id;
734 $this->offersIds[] = $offerId;
735 }
736 }
737 }
738 }
739 unset($offerId, $availableOffers, $allOffers, $id);
740 }
741
742 private function loadProductPrices()
743 {
744 if (empty($this->currentList))
745 return;
746
747 $this->prices = [];
748 $this->existPriceIds = [];
749 $this->existIdsByType = [];
750
751 if (!$this->config['CHECK_PRICES'] && !$this->config['CHECK_SKU_PRICES'])
752 return;
753
754 foreach (array_chunk($this->currentIdsList, 500) as $pageIds)
755 {
756 $iterator = Catalog\PriceTable::getList([
757 'select' => ['ID', 'PRODUCT_ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY', 'PRICE_SCALE'],
758 'filter' => ['@PRODUCT_ID' => $pageIds],
759 'order' => ['PRODUCT_ID' => 'ASC', 'CATALOG_GROUP_ID' => 'ASC']
760 ]);
761 while ($row = $iterator->fetch())
762 {
763 $id = (int)$row['ID'];
764 $row['PRICE'] = (float)$row['PRICE'];
765 $row['PRICE_SCALE'] = (float)$row['PRICE_SCALE'];
766 $productId = (int)$row['PRODUCT_ID'];
767 $priceTypeId = (int)$row['CATALOG_GROUP_ID'];
768
769 if (!isset($this->prices[$productId]))
770 $this->prices[$productId] = [];
771 $this->prices[$productId][$id] = $row;
772
773 if (!isset($this->existPriceIds[$productId]))
774 $this->existPriceIds[$productId] = [];
775 $this->existPriceIds[$productId][$id] = $id;
776
777 if (!isset($this->existIdsByType[$productId]))
778 $this->existIdsByType[$productId] = [];
779 if (!isset($this->existIdsByType[$productId][$priceTypeId]))
780 $this->existIdsByType[$productId][$priceTypeId] = [];
781 $this->existIdsByType[$productId][$priceTypeId][] = $id;
782 }
783 unset($priceTypeId, $productId, $id, $row, $iterator);
784 }
785 unset($pageIds);
786 }
787
788 private function loadSkuPrices()
789 {
790 if (empty($this->currentList))
791 return;
792
793/* $this->existPriceIds = array();
794 $this->existIdsByType = array(); */
795
796 if (!$this->config['CHECK_SKU_PRICES'] || $this->config['SEPARATE_SKU_MODE'])
797 return;
798
799/* foreach (array_chunk($this->currentIdsList, 500) as $pageIds)
800 {
801 $iterator = Catalog\PriceTable::getList(array(
802 'select' => array('ID', 'CATALOG_GROUP_ID', 'PRODUCT_ID'),
803 'filter' => array('@PRODUCT_ID' => $pageIds),
804 'order' => array('ID' => 'ASC')
805 ));
806 while ($row = $iterator->fetch())
807 {
808 $row['ID'] = (int)$row['ID'];
809 $priceTypeId = (int)$row['CATALOG_GROUP_ID'];
810 $productId = (int)$row['PRODUCT_ID'];
811 if (!isset($this->existPriceIds[$productId]))
812 $this->existPriceIds[$productId] = array();
813 $this->existPriceIds[$productId][$row['ID']] = $row['ID'];
814 if (!isset($this->existIdsByType[$productId]))
815 $this->existIdsByType[$productId] = array();
816 if (!isset($this->existIdsByType[$productId][$priceTypeId]))
817 $this->existIdsByType[$productId][$priceTypeId] = array();
818 $this->existIdsByType[$productId][$priceTypeId][] = $row['ID'];
819 }
820 unset($row, $iterator);
821 }
822 unset($pageIds); */
823
824 if (empty($this->offersIds))
825 return;
826
827 sort($this->offersIds);
828 foreach (array_chunk($this->offersIds, 500) as $pageOfferIds)
829 {
830 $filter = Main\Entity\Query::filter();
831 $filter->whereIn('PRODUCT_ID', $pageOfferIds);
832 $filter->where(Main\Entity\Query::filter()->logic('or')->where('QUANTITY_FROM', '<=', 1)->whereNull('QUANTITY_FROM'));
833 $filter->where(Main\Entity\Query::filter()->logic('or')->where('QUANTITY_TO', '>=', 1)->whereNull('QUANTITY_TO'));
834
835 $iterator = Catalog\PriceTable::getList(array(
836 'select' => array(
837 'PRODUCT_ID', 'CATALOG_GROUP_ID', 'PRICE', 'CURRENCY',
838 'PRICE_SCALE', 'TMP_ID'
839 ),
840 'filter' => $filter,
841 'order' => array('PRODUCT_ID' => 'ASC', 'CATALOG_GROUP_ID' => 'ASC')
842 ));
843 while ($row = $iterator->fetch())
844 {
845 $typeId = (int)$row['CATALOG_GROUP_ID'];
846 $offerId = (int)$row['PRODUCT_ID'];
847 $productId = $this->offersMap[$offerId];
848 unset($row['PRODUCT_ID']);
849
850 if (!isset($this->calculatePrices[$productId][$typeId]))
851 $this->calculatePrices[$productId][$typeId] = $row;
852 elseif ($this->calculatePrices[$productId][$typeId]['PRICE_SCALE'] > $row['PRICE_SCALE'])
853 $this->calculatePrices[$productId][$typeId] = $row;
854 }
855 unset($row, $iterator);
856 unset($filter);
857 }
858 }
859
860 private function loadOffersData()
861 {
862 if (empty($this->currentList))
863 return;
864
865 $productList = \CCatalogSku::getProductList(
866 $this->currentIdsList,
867 $this->params['IBLOCK_ID']
868 );
869 if (!is_array($productList))
871
872 foreach ($this->currentIdsList as $id)
873 $this->currentList[$id]['PARENT_EXISTS'] = isset($productList[$id]);
874 unset($id, $productList);
875 }
876
877 private function loadProductSets()
878 {
879 if (!$this->config['CHECK_SETS'])
880 return;
881 if (empty($this->currentIdsList))
882 return;
883
884 foreach ($this->currentIdsList as $id)
885 {
886 $this->currentList[$id]['SET_EXISTS'] = false;
887 $this->currentList[$id]['BUNDLE_EXISTS'] = false;
888 }
889 unset($id);
890
891 //TODO: replace sql to api
892 $conn = Main\Application::getConnection();
893 $helper = $conn->getSqlHelper();
894 $tableName = $helper->quote('b_catalog_product_sets');
895 $iterator = $conn->query(
896 'select '.$helper->quote('OWNER_ID').', '.$helper->quote('TYPE').' from '.$tableName.
897 ' where '.$helper->quote('OWNER_ID').' in ('.implode(',', $this->currentIdsList).') and '.
898 $helper->quote('OWNER_ID').' = '.$helper->quote('ITEM_ID')
899 );
900 while ($row = $iterator->fetch())
901 {
902 $id = (int)$row['OWNER_ID'];
903 if ($row['TYPE'] == \CCatalogProductSet::TYPE_SET)
904 $this->currentList[$id]['SET_EXISTS'] = true;
905 if ($row['TYPE'] == \CCatalogProductSet::TYPE_GROUP)
906 $this->currentList[$id]['BUNDLE_EXISTS'] = true;
907 }
908 unset($row, $iterator);
909 unset($tableName, $helper, $conn);
910 }
911
912 private function loadMeasureRatios()
913 {
914 $this->measureRatios = array();
915 if (!$this->config['CHECK_MEASURE_RATIO'])
916 return;
917 if (empty($this->currentIdsList))
918 return;
919
920 $this->measureRatios = array_fill_keys(
921 $this->currentIdsList,
922 array(
923 'RATIOS' => array(),
924 'DEFAULT_EXISTS' => false,
925 'DEFAULT_RATIO_ID' => null,
926 'DOUBLES' => array()
927 )
928 );
929 foreach (array_chunk($this->currentIdsList, 500) as $pageIds)
930 {
931 $iterator = Catalog\MeasureRatioTable::getList(array(
932 'select' => array('*'),
933 'filter' => array('@PRODUCT_ID' => $pageIds),
934 'order' => array('PRODUCT_ID' => 'ASC', 'RATIO' => 'ASC')
935 ));
936 while ($row = $iterator->fetch())
937 {
938 $productId = (int)$row['PRODUCT_ID'];
939 $this->measureRatios[$productId]['RATIOS'][$row['ID']] = $row;
940 if ($row['IS_DEFAULT'] == 'Y')
941 {
942 if ($this->measureRatios[$productId]['DEFAULT_EXISTS'])
943 {
944 $this->measureRatios[$productId]['DOUBLES'][] = $row['ID'];
945 }
946 else
947 {
948 $this->measureRatios[$productId]['DEFAULT_EXISTS'] = true;
949 $this->measureRatios[$productId]['DEFAULT_RATIO_ID'] = $row['ID'];
950 }
951 }
952 }
953 }
954 unset($productId, $row, $iterator, $pageIds);
955 }
956
957 private function updateProductData()
958 {
959 if (empty($this->currentIdsList))
960 return;
961
962 $checkMeasure = (
963 $this->config['CHECK_MEASURE']
964 && $this->iblockData['CATALOG_TYPE'] != CCatalogSku::TYPE_PRODUCT
965 && (isset($this->iblockData['SUBSCRIPTION']) && $this->iblockData['SUBSCRIPTION'] != 'Y')
966 );
967
968 foreach ($this->currentIdsList as $id)
969 {
970 $product = $this->currentList[$id];
971 $product['SUCCESS'] = true;
972 if ($this->config['CHECK_AVAILABLE'])
973 {
974 switch ($this->iblockData['CATALOG_TYPE'])
975 {
977 $fields = $this->getCatalogItem($product);
978 break;
980 $fields = $this->getOfferIblockItem($product);
981 break;
983 $fields = $this->getFullCatalogItem($product);
984 break;
986 $fields = $this->getProductIblockItem($product);
987 break;
988 default:
989 $fields = array();
990 break;
991 }
992
993 if ($this->config['CHECK_SETS'])
994 {
995 $fields['BUNDLE'] = ($product['BUNDLE_EXISTS']
996 ? Catalog\ProductTable::STATUS_YES
997 : Catalog\ProductTable::STATUS_NO
998 );
999 }
1000 if ($checkMeasure)
1001 {
1002 if ($fields['TYPE'] == Catalog\ProductTable::TYPE_SET)
1003 {
1004 if ($this->measureIds['BASE'] !== null)
1005 {
1006 $fields['MEASURE'] = $this->measureIds['BASE'];
1007 }
1008 }
1009 else
1010 {
1011 if ((int)$product['MEASURE'] <= 0 && $this->measureIds['DEFAULT'] !== null)
1012 {
1013 $fields['MEASURE'] = $this->measureIds['DEFAULT'];
1014 }
1015 }
1016 }
1017
1018 if ($product['PRODUCT_EXISTS'])
1019 {
1020 $productResult = Catalog\ProductTable::update($product['ID'], $fields);
1021 }
1022 else
1023 {
1024 $fields['ID'] = $product['ID'];
1025 $productResult = Catalog\ProductTable::add($fields);
1026 $fields['PRODUCT_ID'] = $fields['ID'];
1027 unset($fields['ID']);
1028 }
1029
1030 if ($productResult->isSuccess())
1031 {
1032 $product = array_merge($product, $fields);
1033 }
1034 else
1035 {
1036 $product['SUCCESS'] = false;
1037 $errorId = 'BX_CATALOG_REINDEX_ERR_PRODUCT_UPDATE_FAIL_EXT';
1038 if (
1039 $product['TYPE'] == Catalog\ProductTable::TYPE_OFFER
1040 || $product['TYPE'] == Catalog\ProductTable::TYPE_FREE_OFFER
1041 )
1042 $errorId = 'BX_CATALOG_REINDEX_ERR_OFFER_UPDATE_FAIL_EXT';
1043 $this->addError(Loc::getMessage(
1044 $errorId,
1045 [
1046 '#ID#' => $id,
1047 '#NAME#' => $product['NAME'],
1048 '#ERROR#' => implode('; ', $productResult->getErrorMessages())
1049 ]
1050 ));
1051 unset($errorId);
1052 }
1053 unset($productResult, $fields);
1054 }
1055
1056 if ($product['SUCCESS'])
1057 {
1058 if ($this->config['CHECK_PRICES'])
1059 $this->updateProductPrices($id, $product);
1060 if ($this->config['CHECK_SKU_PRICES'])
1061 {
1062 $this->updateSkuPrices($id, $product);
1063 if ($product['TYPE'] == Catalog\ProductTable::TYPE_SKU)
1064 Iblock\PropertyIndex\Manager::updateElementIndex($this->params['IBLOCK_ID'], $id);
1065 }
1066 if ($this->config['CHECK_MEASURE_RATIO'])
1067 $this->updateMeasureRatios($id, $product);
1068 }
1069
1070 unset($product);
1071 $this->setLastId($id);
1072 if ($this->isStopOperation())
1073 break;
1074 }
1075 unset($id);
1076 }
1077
1078 private function updateProductPrices($id, array $product)
1079 {
1080 if (!$this->config['CHECK_PRICES'])
1081 return;
1082
1083 if ($product['TYPE'] == Catalog\ProductTable::TYPE_SKU && !$this->config['SEPARATE_SKU_MODE'])
1084 return;
1085
1086 if (empty($this->prices[$id]))
1087 return;
1088
1089 if ($product['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
1090 {
1091 unset($this->prices[$id]);
1092 unset($this->existIdsByType[$id]);
1093 unset($this->existPriceIds[$id]);
1094
1095 $conn = Main\Application::getConnection();
1096 $helper = $conn->getSqlHelper();
1097 $conn->queryExecute(
1098 'delete from '.$helper->quote(Catalog\PriceTable::getTableName()).
1099 ' where '.$helper->quote('PRODUCT_ID').' = '.$id
1100 );
1101 unset($helper, $conn);
1102 return;
1103 }
1104
1105 $success = true;
1106 $errorMessage = [];
1107 foreach (array_keys($this->prices[$id]) as $rowId)
1108 {
1109 $row = $this->prices[$id][$rowId];
1110 if (
1111 !isset($this->currencyReference[$row['CURRENCY']])
1112 || $this->currencyReference[$row['CURRENCY']] == 0
1113 )
1114 continue;
1115 $baseRate = $this->currencyReference[$row['CURRENCY']];
1116 $newScale = $row['PRICE'] * $baseRate;
1117 if ($newScale == $row['PRICE_SCALE'])
1118 continue;
1119 $rowResult = Catalog\PriceTable::update($rowId, ['PRICE_SCALE' => $newScale]);
1120 if (!$rowResult->isSuccess())
1121 {
1122 $success = false;
1123 $errorMessage = $rowResult->getErrorMessages();
1124 break;
1125 }
1126 }
1127 unset($rowResult, $newScale, $baseRate, $row, $rowId);
1128
1129 unset($this->prices[$id]);
1130
1131 if (!$success)
1132 {
1133 $errorId = 'BX_CATALOG_REINDEX_ERR_PRODUCT_PRICE_UPDATE_FAIL_EXT';
1134 if (
1135 $product['TYPE'] == Catalog\ProductTable::TYPE_OFFER
1136 || $product['TYPE'] == Catalog\ProductTable::TYPE_FREE_OFFER
1137 )
1138 $errorId = 'BX_CATALOG_REINDEX_ERR_OFFER_PRICE_UPDATE_FAIL_EXT';
1139 $this->addError(Loc::getMessage(
1140 $errorId,
1141 [
1142 '#ID#' => $id,
1143 '#NAME#' => $product['NAME'],
1144 '#ERROR#' => implode('; ', $errorMessage)
1145 ]
1146 ));
1147 unset($errorId);
1148 }
1149 unset($errorMessage, $success);
1150 }
1151
1152 private function updateSkuPrices($id, array $product)
1153 {
1154 if ($product['TYPE'] != Catalog\ProductTable::TYPE_SKU)
1155 return;
1156 if (!$this->config['CHECK_SKU_PRICES'] || $this->config['SEPARATE_SKU_MODE'])
1157 return;
1158
1159 $success = true;
1160 $errorMessage = [];
1161 if (!empty($this->calculatePrices[$id]))
1162 {
1163 foreach (array_keys($this->calculatePrices[$id]) as $resultPriceType)
1164 {
1165 $rowId = null;
1166 $row = $this->calculatePrices[$id][$resultPriceType];
1167 if (!empty($this->existIdsByType[$id][$resultPriceType]))
1168 {
1169 $rowId = array_shift($this->existIdsByType[$id][$resultPriceType]);
1170 unset($this->existPriceIds[$id][$rowId]);
1171 unset($this->prices[$id][$rowId]);
1172 }
1173 if ($rowId === null)
1174 {
1175 $row['PRODUCT_ID'] = $id;
1176 $row['CATALOG_GROUP_ID'] = $resultPriceType;
1177 $rowResult = Catalog\PriceTable::add($row);
1178 }
1179 else
1180 {
1181 $rowResult = Catalog\PriceTable::update($rowId, $row);
1182 }
1183 if (!$rowResult->isSuccess())
1184 {
1185 $success = false;
1186 $errorMessage = $rowResult->getErrorMessages();
1187 break;
1188 }
1189 }
1190 }
1191 unset($this->calculatePrices[$id]);
1192
1193 if ($success && !empty($this->existPriceIds[$id]))
1194 {
1195 $conn = Main\Application::getConnection();
1196 $helper = $conn->getSqlHelper();
1197 $conn->queryExecute(
1198 'delete from '.$helper->quote(Catalog\PriceTable::getTableName()).
1199 ' where '.$helper->quote('ID').' in ('.implode(',', $this->existPriceIds[$id]).')'
1200 );
1201 unset($helper, $conn);
1202 }
1203
1204 if (isset($this->existPriceIds[$id]))
1205 unset($this->existPriceIds[$id]);
1206
1207 if (!$success)
1208 {
1209 $this->addError(Loc::getMessage(
1210 'BX_CATALOG_REINDEX_ERR_PRODUCT_UPDATE_FAIL_EXT',
1211 [
1212 '#ID#' => $id,
1213 '#NAME#' => $product['NAME'],
1214 '#ERROR#' => implode('; ', $errorMessage)
1215 ]
1216 ));
1217 }
1218 unset($errorMessage, $success);
1219 }
1220
1221 private function updateMeasureRatios($id, array $product)
1222 {
1223 if (!$this->config['CHECK_MEASURE_RATIO'])
1224 return;
1225
1226 if (!isset($this->measureRatios[$id]))
1227 return;
1228
1229 $action = '';
1230 if (isset($this->iblockData['SUBSCRIPTION']) && $this->iblockData['SUBSCRIPTION'] == 'Y')
1231 {
1232 if (!empty($this->measureRatios[$id]['RATIOS']))
1233 $action = 'set';
1234 else
1235 $action = 'create';
1236 }
1237 elseif (
1238 $product['TYPE'] == Catalog\ProductTable::TYPE_PRODUCT
1239 || $product['TYPE'] == Catalog\ProductTable::TYPE_OFFER
1240 || $product['TYPE'] == Catalog\ProductTable::TYPE_FREE_OFFER
1241 )
1242 {
1243 if (!empty($this->measureRatios[$id]['RATIOS']))
1244 $action = 'check';
1245 else
1246 $action = 'create';
1247 }
1248 elseif ($product['TYPE'] == Catalog\ProductTable::TYPE_SET)
1249 {
1250 if (!empty($this->measureRatios[$id]['RATIOS']))
1251 $action = 'set';
1252 else
1253 $action = 'create';
1254 }
1255 elseif ($product['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU)
1256 {
1257 if (!empty($this->measureRatios[$id]['RATIOS']))
1258 $action = 'remove';
1259 }
1260 elseif ($product['TYPE'] == Catalog\ProductTable::TYPE_SKU)
1261 {
1262 if ($this->config['SEPARATE_SKU_MODE'])
1263 {
1264 if (!empty($this->measureRatios[$id]['RATIOS']))
1265 $action = 'check';
1266 else
1267 $action = 'create';
1268 }
1269 else
1270 {
1271 if (!empty($this->measureRatios[$id]['RATIOS']))
1272 $action = 'remove';
1273 }
1274 }
1275
1276 switch ($action)
1277 {
1278 case 'create':
1279 $ratioResult = Catalog\MeasureRatioTable::add(array(
1280 'PRODUCT_ID' => $id,
1281 'RATIO' => 1,
1282 'IS_DEFAULT' => 'Y'
1283 ));
1284 unset($ratioResult);
1285 break;
1286 case 'check':
1287 if (!$this->measureRatios[$id]['DEFAULT_EXISTS'])
1288 {
1289 $firstRatio = reset($this->measureRatios[$id]['RATIOS']);
1290 $ratioResult = Catalog\MeasureRatioTable::update($firstRatio['ID'], array('IS_DEFAULT' => 'Y'));
1291 unset($ratioResult, $firstRatio);
1292 }
1293 if (!empty($this->measureRatios[$id]['DOUBLES']))
1294 {
1295 foreach ($this->measureRatios[$id]['DOUBLES'] as $ratioId)
1296 {
1297 $ratioResult = Catalog\MeasureRatioTable::update($ratioId, array('IS_DEFAULT' => 'N'));
1298 }
1299 unset($ratioResult, $ratioId);
1300 }
1301 break;
1302 case 'set':
1303 $ratioId = null;
1304 foreach ($this->measureRatios[$id]['RATIOS'] as $row)
1305 {
1306 if ($row['RATIO'] == 1)
1307 {
1308 $ratioId = $row['ID'];
1309 break;
1310 }
1311 }
1312 unset($row);
1313 if ($ratioId === null && $this->measureRatios[$id]['DEFAULT_RATIO_ID'] !== null)
1314 $ratioId = $this->measureRatios[$id]['DEFAULT_RATIO_ID'];
1315 if ($ratioId === null)
1316 {
1317 $firstRatio = reset($this->measureRatios[$id]['RATIOS']);
1318 $ratioId = $firstRatio['ID'];
1319 unset($firstRatio);
1320 }
1321 foreach ($this->measureRatios[$id]['RATIOS'] as $row)
1322 {
1323 if ($row['ID'] == $ratioId)
1324 {
1325 $ratioResult = Catalog\MeasureRatioTable::update(
1326 $row['ID'],
1327 array('RATIO' => 1, 'IS_DEFAULT' => 'Y')
1328 );
1329 }
1330 else
1331 {
1332 $ratioResult = Catalog\MeasureRatioTable::delete($row['ID']);
1333 }
1334 }
1335 unset($ratioResult, $row, $ratioId);
1336
1337 break;
1338 case 'remove':
1339 Catalog\MeasureRatioTable::deleteByProduct($id);
1340 break;
1341 default:
1342 break;
1343 }
1344
1345 unset($action);
1346 unset($this->measureRatios[$id]);
1347 }
1348
1349 private function getFullCatalogItem(array $product): array
1350 {
1351 $fields = []; // only for phpDoc
1352 switch ($product['SKU_STATE'])
1353 {
1354 case Catalog\Product\Sku::OFFERS_AVAILABLE:
1355 case Catalog\Product\Sku::OFFERS_NOT_AVAILABLE:
1356 if ($this->config['SEPARATE_SKU_MODE'])
1357 {
1358 $fields = [
1359 'AVAILABLE' => Catalog\ProductTable::calculateAvailable($product),
1360 'TYPE' => Catalog\ProductTable::TYPE_SKU,
1361 ];
1362 }
1363 else
1364 {
1365 $fields = Catalog\Product\Sku::getDefaultParentSettings($product['SKU_STATE']);
1366 }
1367 break;
1368 case Catalog\Product\Sku::OFFERS_NOT_EXIST:
1369 switch ($product['TYPE'])
1370 {
1371 case Catalog\ProductTable::TYPE_SKU:
1372 case Catalog\ProductTable::TYPE_EMPTY_SKU:
1373 $fields = Catalog\Product\Sku::getDefaultParentSettings($product['SKU_STATE']);
1374 break;
1375 case Catalog\ProductTable::TYPE_PRODUCT:
1376 case Catalog\ProductTable::TYPE_SET:
1377 $fields['AVAILABLE'] = Catalog\ProductTable::calculateAvailable($product);
1378 $fields['TYPE'] = (int)$product['TYPE'];
1379 break;
1380 case Catalog\ProductTable::TYPE_SERVICE:
1381 $fields['TYPE'] = (int)$product['TYPE'];
1382 break;
1383 default:
1384 $fields = Catalog\ProductTable::getDefaultAvailableSettings();
1385 $fields['TYPE'] = Catalog\ProductTable::TYPE_PRODUCT;
1386 break;
1387 }
1388 break;
1389 }
1390
1391 return $this->prepareSetState($fields, $product);
1392 }
1393
1394 private function getProductIblockItem(array $product): array
1395 {
1396 return Catalog\Product\Sku::getParentProductFieldsByState(
1397 $product['SKU_STATE']
1398 );
1399 }
1400
1401 private function getCatalogItem(array $product): array
1402 {
1403 if ($product['PRODUCT_EXISTS'])
1404 {
1405 switch ($product['TYPE'])
1406 {
1407 case Catalog\ProductTable::TYPE_PRODUCT:
1408 case Catalog\ProductTable::TYPE_SET:
1409 $fields['AVAILABLE'] = Catalog\ProductTable::calculateAvailable($product);
1410 $fields['TYPE'] = (int)$product['TYPE'];
1411 break;
1412 case Catalog\ProductTable::TYPE_SERVICE:
1413 $fields['TYPE'] = (int)$product['TYPE'];
1414 break;
1415 default:
1416 $fields = array(
1417 'AVAILABLE' => Catalog\ProductTable::calculateAvailable($product),
1418 'TYPE' => Catalog\ProductTable::TYPE_PRODUCT,
1419 );
1420 break;
1421 }
1422 }
1423 else
1424 {
1425 $fields = Catalog\ProductTable::getDefaultAvailableSettings();
1426 $fields['TYPE'] = Catalog\ProductTable::TYPE_PRODUCT;
1427 }
1428
1429 return $this->prepareSetState($fields, $product);
1430 }
1431
1432 private function getOfferIblockItem(array $product): array
1433 {
1434 return array(
1435 'AVAILABLE' => Catalog\ProductTable::calculateAvailable($product),
1436 'TYPE' => ($product['PARENT_EXISTS'] ? Catalog\ProductTable::TYPE_OFFER : Catalog\ProductTable::TYPE_FREE_OFFER)
1437 );
1438 }
1439
1440 private function prepareSetState(array $fields, array $product): array
1441 {
1442 if (!$this->config['CHECK_SETS'])
1443 {
1444 return $fields;
1445 }
1446
1447 if ($fields['TYPE'] == Catalog\ProductTable::TYPE_SET && !$product['SET_EXISTS'])
1448 {
1449 $fields['TYPE'] = Catalog\ProductTable::TYPE_PRODUCT;
1450 }
1451 elseif ($fields['TYPE'] == Catalog\ProductTable::TYPE_PRODUCT && $product['SET_EXISTS'])
1452 {
1453 $fields['TYPE'] = Catalog\ProductTable::TYPE_SET;
1454 }
1455
1456 return $fields;
1457 }
1458}
1459
1461{
1462 const SESSION_PREFIX = 'PS';
1463 const SETS_ID = 'SETS';
1464
1466 {
1467 $sessID = (string)$sessID;
1468 if ($sessID == '')
1469 $sessID = self::SESSION_PREFIX.time();
1470 parent::__construct($sessID, $maxExecutionTime, $maxOperationCounter);
1471 }
1472
1473 public function runOperation()
1474 {
1475 if (!isset($this->params['IBLOCK_ID']))
1476 return;
1477 if ($this->params['IBLOCK_ID'] == self::SETS_ID)
1478 $this->runOperationSets();
1479 else
1480 parent::runOperation();
1481 }
1482
1483 public function getMessage()
1484 {
1485 if ($this->params['IBLOCK_ID'] == self::SETS_ID)
1486 {
1487 $messageParams = array(
1488 'MESSAGE' => Loc::getMessage('BX_STEP_OPERATION_SETS_TITLE'),
1489 'PROGRESS_TOTAL' => $this->allCounter,
1490 'PROGRESS_VALUE' => $this->allOperationCounter,
1491 'TYPE' => 'PROGRESS',
1492 'DETAILS' => str_replace(array('#ALL#', '#COUNT#'), array($this->allCounter, $this->allOperationCounter), $this->progressTemplate),
1493 'HTML' => true
1494 );
1495 $message = new CAdminMessage($messageParams);
1496 return $message->Show();
1497 }
1498 return parent::getMessage();
1499 }
1500
1501 public static function getCatalogList()
1502 {
1503 $result = array();
1504
1505 $catalogList = array();
1506 $iblockList = array();
1507
1509 'select' => array('IBLOCK_ID', 'PRODUCT_IBLOCK_ID'),
1510 'order' => array('IBLOCK_ID' => 'ASC')
1511 ));
1512 while ($catalog = $catalogIterator->fetch())
1513 {
1514 $iblockId = (int)$catalog['IBLOCK_ID'];
1515 $parentIblockID = (int)$catalog['PRODUCT_IBLOCK_ID'];
1516 $iblockList[$iblockId] = ($parentIblockID > 0 ? $parentIblockID : $iblockId);
1517 unset($parentIblockID, $iblockId);
1518 }
1519 unset($catalog, $catalogIterator);
1520 if (empty($iblockList))
1521 return $result;
1522
1523 $iblockIterator = Catalog\ProductTable::getList(array(
1524 'select' => array('IBLOCK_ID' => 'IBLOCK_ELEMENT.IBLOCK_ID', new Main\Entity\ExpressionField('CNT', 'COUNT(*)')),
1525 'filter' => array(static::getProductFilter()),
1526 'group' => array('IBLOCK_ID'),
1527 'order' => array('IBLOCK_ID' => 'ASC')
1528 ));
1529 while ($iblock = $iblockIterator->fetch())
1530 {
1531 $iblockId = (int)$iblock['IBLOCK_ID'];
1532 if (isset($iblockList[$iblockId]))
1533 {
1534 $catalogId = $iblockList[$iblockId];
1535 $catalogList[$catalogId] = $catalogId;
1536 unset($catalogId);
1537 }
1538 unset($iblockId);
1539 }
1540 unset($iblock, $iblockIterator);
1541 unset($iblockList);
1542
1543 if (empty($catalogList))
1544 return $result;
1545
1546 foreach ($catalogList as &$catalogId)
1547 {
1548 $iblockList = static::getIblockList($catalogId);
1549 if (!empty($iblockList))
1550 $result = array_merge($result, $iblockList);
1551 unset($iblockList);
1552 }
1553 unset($catalogId);
1554
1555 if (Catalog\Config\Feature::isProductSetsEnabled())
1556 static::addSetDescription($result);
1557
1558 return $result;
1559 }
1560
1561 protected function initConfig()
1562 {
1563 parent::initConfig();
1564 $this->config['UPDATE_ONLY'] = true;
1565 }
1566
1567 protected function runOperationSets()
1568 {
1569 global $DB;
1570
1571 $tableName = '';
1572 switch (mb_strtoupper($DB->type))
1573 {
1574 case 'MYSQL':
1575 $tableName = 'b_catalog_product_sets';
1576 break;
1577 case 'MSSQL':
1578 $tableName = 'B_CATALOG_PRODUCT_SETS';
1579 break;
1580 case 'ORACLE':
1581 $tableName = 'B_CATALOG_PRODUCT_SETS';
1582 break;
1583 }
1584 if ($tableName == '')
1585 return;
1586
1588 $emptyList = true;
1589 CTimeZone::Disable();
1590 $filter = array('TYPE' => CCatalogProductSet::TYPE_SET, 'SET_ID' => 0);
1591 if ($this->lastID > 0)
1592 $filter['>ID'] = $this->lastID;
1593 $topCount = ($this->maxOperationCounter > 0 ? array('nTopCount' => $this->maxOperationCounter) : false);
1594 $productSetsIterator = CCatalogProductSet::getList(
1595 array('ID' => 'ASC'),
1596 $filter,
1597 false,
1598 $topCount,
1599 array('ID', 'OWNER_ID', 'ITEM_ID', 'MODIFIED_BY', 'TIMESTAMP_X')
1600 );
1601 while ($productSet = $productSetsIterator->Fetch())
1602 {
1603 $emptyList = false;
1604 $productSet['MODIFIED_BY'] = (int)$productSet['MODIFIED_BY'];
1605 if ($productSet['MODIFIED_BY'] == 0)
1606 $productSet['MODIFIED_BY'] = false;
1607 CCatalogProductSet::recalculateSet($productSet['ID'], $productSet['ITEM_ID']);
1608 $arTimeFields = array(
1609 '~TIMESTAMP_X' => $DB->CharToDateFunction($productSet['TIMESTAMP_X'], "FULL"),
1610 'MODIFIED_BY' => $productSet['MODIFIED_BY']
1611 );
1612 $strUpdate = $DB->PrepareUpdate($tableName, $arTimeFields);
1613 if (!empty($strUpdate))
1614 {
1615 $strQuery = "update ".$tableName." set ".$strUpdate." where ID = ".$productSet['ID'];
1616 $DB->Query($strQuery);
1617 }
1618 $this->setLastId($productSet['ID']);
1619 if ($this->isStopOperation())
1620 break;
1621 }
1622 CTimeZone::Enable();
1623 $this->setFinishOperation($emptyList);
1625 }
1626
1627 protected static function addSetDescription(array &$iblockList)
1628 {
1630 array(),
1631 array('TYPE' => CCatalogProductSet::TYPE_SET, 'SET_ID' => 0),
1632 array()
1633 );
1634 if ($counter <= 0)
1635 return;
1636 $iblockList[] = array(
1637 'ID' => self::SETS_ID,
1638 'NAME' => Loc::getMessage('BX_STEP_OPERATION_SETS_TITLE'),
1639 'TITLE' => Loc::getMessage('BX_STEP_OPERATION_SETS_TITLE'),
1640 'COUNT' => $counter
1641 );
1642 }
1643
1644 protected static function getProductFilter($iblockFilter = false)
1645 {
1646 if ($iblockFilter)
1647 {
1648 return array(
1649 'LOGIC' => 'OR',
1650 '=PRODUCT.QUANTITY_TRACE_ORIG' => Catalog\ProductTable::STATUS_DEFAULT,
1651 '=PRODUCT.CAN_BUY_ZERO_ORIG' => Catalog\ProductTable::STATUS_DEFAULT
1652 );
1653 }
1654 return array(
1655 'LOGIC' => 'OR',
1656 '=QUANTITY_TRACE_ORIG' => Catalog\ProductTable::STATUS_DEFAULT,
1657 '=CAN_BUY_ZERO_ORIG' => Catalog\ProductTable::STATUS_DEFAULT
1658 );
1659 }
1660}
1661
1663{
1664 const NOTIFY_ID = 'CATALOG_REINDEX';
1665
1667 {
1668 parent::__construct($sessID, $maxExecutionTime, $maxOperationCounter);
1669 }
1670
1671 public static function removeNotify()
1672 {
1673 // old message from 16.0
1674 $iterator = \CAdminNotify::GetList([], ['MODULE_ID' => 'catalog', 'TAG' => 'CATALOG_16']);
1675 while ($row = $iterator->Fetch())
1676 {
1677 \CAdminNotify::Delete($row['ID']);
1678 }
1679
1680 $iterator = \CAdminNotify::GetList([], ['MODULE_ID' => 'catalog', 'TAG' => self::NOTIFY_ID]);
1681 while ($row = $iterator->Fetch())
1682 {
1683 \CAdminNotify::Delete($row['ID']);
1684 }
1685 unset($row, $iterator);
1686 }
1687
1688 public static function showNotify()
1689 {
1691
1693 'select' => ['CNT'],
1694 'runtime' => [
1695 new Main\Entity\ExpressionField('CNT', 'COUNT(*)')
1696 ]
1697 ])->fetch();
1698 $catalogCount = (isset($catalogData['CNT']) ? (int)$catalogData['CNT'] : 0);
1699 unset($catalogData);
1700 if ($catalogCount == 0)
1701 return;
1702
1703 $defaultLang = '';
1704 $messages = [];
1706 'select' => ['ID', 'DEF'],
1707 'filter' => ['=ACTIVE' => 'Y']
1708 ]);
1709 while ($row = $iterator->fetch())
1710 {
1711 if ($defaultLang == '')
1712 $defaultLang = $row['ID'];
1713 if ($row['DEF'] == 'Y')
1714 $defaultLang = $row['ID'];
1715 $languageId = $row['ID'];
1717 __FILE__,
1718 $languageId
1719 );
1720 $messages[$languageId] = Loc::getMessage(
1721 'BX_CATALOG_REINDEX_NOTIFY',
1722 ['#LINK#' => '/bitrix/admin/settings.php?lang='.$languageId.'&mid=catalog&mid_menu=1'],
1723 $languageId
1724 );
1725 }
1726 unset($languageId, $row, $iterator);
1727
1728 if (empty($messages))
1729 return;
1730
1732 'MODULE_ID' => 'catalog',
1733 'TAG' => self::NOTIFY_ID,
1734 'ENABLE_CLOSE' => 'Y',
1735 'NOTIFY_TYPE' => \CAdminNotify::TYPE_NORMAL,
1736 'MESSAGE' => $messages[$defaultLang],
1737 'LANG' => $messages
1738 ]);
1739 }
1740
1741 public static function execAgent()
1742 {
1744
1745 return '';
1746 }
1747
1748 protected function initConfig()
1749 {
1750 parent::initConfig();
1751 $this->config['CHECK_MEASURE_RATIO'] = true;
1752 $this->config['CHECK_MEASURE'] = true;
1753 $this->config['CHECK_PRICES'] = true;
1754 }
1755
1756 protected function setOldConfig()
1757 {
1758 parent::setOldConfig();
1759 $this->extendedMode = true;
1760 }
1761
1770 protected function runExtendedOperation(array $product)
1771 {
1772 if (!isset($product['TYPE']))
1773 return;
1774 if (
1775 $product['TYPE'] == Catalog\ProductTable::TYPE_EMPTY_SKU
1776 || ($product['TYPE'] == Catalog\ProductTable::TYPE_SKU && !$this->isSeparateSkuMode())
1777 )
1778 return;
1779
1780 $ratios = array();
1781 $defaultExist = false;
1782 $defaultDoubles = array();
1783 $iterator = Catalog\MeasureRatioTable::getList(array(
1784 'select' => array('*'),
1785 'filter' => array('=PRODUCT_ID' => $product['PRODUCT_ID']),
1786 'order' => array('RATIO' => 'ASC')
1787 ));
1788 while ($row = $iterator->fetch())
1789 {
1790 if ($row['IS_DEFAULT'] == 'Y')
1791 {
1792 if (!$defaultExist)
1793 $defaultExist = true;
1794 else
1795 $defaultDoubles[] = $row['ID'];
1796 }
1797 $ratios[$row['ID']] = $row;
1798 }
1799 unset($row, $iterator);
1800
1801 if (empty($ratios))
1802 {
1803 $ratioResult = Catalog\MeasureRatioTable::add(array(
1804 'PRODUCT_ID' => $product['PRODUCT_ID'],
1805 'RATIO' => 1,
1806 'IS_DEFAULT' => 'Y'
1807 ));
1808 unset($ratioResult);
1809 }
1810 else
1811 {
1812 if (!$defaultExist)
1813 {
1814 reset($ratios);
1815 $firstRatio = current($ratios);
1816 $ratioResult = Catalog\MeasureRatioTable::update($firstRatio['ID'], array('IS_DEFAULT' => 'Y'));
1817 unset($ratioResult);
1818 unset($firstRatio);
1819 }
1820 if (!empty($defaultDoubles))
1821 {
1822 foreach ($defaultDoubles as $ratioId)
1823 {
1824 $ratioResult = Catalog\MeasureRatioTable::update($ratioId, array('IS_DEFAULT' => 'N'));
1825 unset($ratioResult);
1826 }
1827 unset($ratioId);
1828 }
1829 }
1830 unset($defaultDoubles, $defaultExist, $ratios);
1831 }
1832}
$catalogData
Определения options.php:2236
while($arIBlock=$rsIBlocks->Fetch()) $catalogIterator
Определения options.php:1934
$catalogCount
Определения options.php:2242
$catalogList
Определения catalog_reindex.php:128
static enableUpdateAvailable()
Определения sku.php:57
static disableUpdateAvailable()
Определения sku.php:67
const OFFERS_NOT_EXIST
Определения sku.php:17
const OFFERS_NOT_AVAILABLE
Определения sku.php:18
const OFFERS_AVAILABLE
Определения sku.php:19
const TYPE_EMPTY_SKU
Определения product.php:75
const TYPE_SKU
Определения product.php:72
const STATUS_DEFAULT
Определения product.php:68
static get($moduleId, $name, $default="", $siteId=false)
Определения option.php:30
static loadLanguageFile($file, $language=null, $normalize=true)
Определения loc.php:225
static getMessage($code, $replace=null, $language=null)
Определения loc.php:30
static getList(array $parameters=array())
Определения datamanager.php:431
static getCount($filter=array(), array $cache=array())
Определения datamanager.php:516
Определения admin_lib.php:2166
static Delete($ID)
Определения admin_notify.php:113
const TYPE_NORMAL
Определения admin_notify.php:7
static Add($arFields)
Определения admin_notify.php:22
static GetList($arSort=[], $arFilter=[])
Определения admin_notify.php:207
const NOTIFY_ID
Определения step_operations.php:1664
__construct($sessID, $maxExecutionTime, $maxOperationCounter)
Определения step_operations.php:1666
static execAgent()
Определения step_operations.php:1741
static removeNotify()
Определения step_operations.php:1671
static showNotify()
Определения step_operations.php:1688
runExtendedOperation(array $product)
Определения step_operations.php:1770
static getDefaultMeasure($getStub=false, $getExt=false)
Определения measure.php:185
static getList($arOrder=array(), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelectFields=array())
Определения measure.php:15
const SESSION_PREFIX
Определения step_operations.php:293
static getIblockCounter($iblockId)
Определения step_operations.php:676
runOperationOfferIblock()
Определения step_operations.php:628
calculateNextOperationCounter()
Определения step_operations.php:532
__construct($sessID, $maxExecutionTime, $maxOperationCounter)
Определения step_operations.php:323
runOperationProductIblock()
Определения step_operations.php:622
runOperationFullCatalog()
Определения step_operations.php:619
static getIblockList($iblockId)
Определения step_operations.php:448
getProductIterator($filter, $select)
Определения step_operations.php:638
runExtendedOperation(array $product)
Определения step_operations.php:636
const TYPE_SET
Определения product_set.php:9
const TYPE_GROUP
Определения product_set.php:10
static recalculateSet($setID, $productID=0)
Определения product_set.php:211
static getAllCounter()
Определения step_operations.php:281
__construct($sessID, $maxExecutionTime, $maxOperationCounter)
Определения step_operations.php:215
static getList($arOrder=array(), $arFilter=array(), $arGroupBy=false, $arNavStartParams=false, $arSelect=array())
Определения product_set.php:191
const SESSION_PREFIX
Определения step_operations.php:1462
static getCatalogList()
Определения step_operations.php:1501
static addSetDescription(array &$iblockList)
Определения step_operations.php:1627
__construct($sessID, $maxExecutionTime, $maxOperationCounter)
Определения step_operations.php:1465
static getProductFilter($iblockFilter=false)
Определения step_operations.php:1644
const TYPE_OFFERS
Определения catalog_sku.php:22
const TYPE_PRODUCT
Определения catalog_sku.php:21
const TYPE_FULL
Определения catalog_sku.php:23
const TYPE_CATALOG
Определения catalog_sku.php:20
addError($error)
Определения step_operations.php:201
static getDefaultExecutionTime()
Определения step_operations.php:162
static getAllCounter()
Определения step_operations.php:157
const DEFAULT_SESSION_PREFIX
Определения step_operations.php:11
setProgressTemplates($template)
Определения step_operations.php:136
setLastId($lastId)
Определения step_operations.php:170
setParams($params)
Определения step_operations.php:57
initStep($allCount, $allOperationCount, $lastID)
Определения step_operations.php:63
calculateNextOperationCounter()
Определения step_operations.php:186
__construct($sessID, $maxExecutionTime, $maxOperationCounter)
Определения step_operations.php:29
setFinishOperation($finish)
Определения step_operations.php:181
$defaultProgressTemplate
Определения step_operations.php:24
if(!\Bitrix\Main\Loader::includeModule('clouds')) $lastId
Определения sync.php:68
$template
Определения file_edit.php:49
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
$iblockId
Определения iblock_catalog_edit.php:30
$catalog
Определения iblock_catalog_edit.php:135
if(! $catalogEdit->isSuccess()) $iblock
Определения iblock_catalog_edit.php:38
$getListParams
Определения iblock_catalog_list.php:210
$select
Определения iblock_catalog_list.php:194
$filter
Определения iblock_catalog_list.php:54
$iblockList
Определения iblock_catalog_list.php:271
while($arParentIBlockProperty=$dbParentIBlockProperty->Fetch()) $errorMessage
global $DB
Определения cron_frame.php:29
$success
Определения mail_entry.php:69
htmlspecialcharsbx($string, $flags=ENT_COMPAT, $doubleEncode=true)
Определения tools.php:2701
Определения ufield.php:9
$message
Определения payment.php:8
$counter
Определения options.php:5
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$messages
Определения template.php:8
$title
Определения pdf.php:123
$error
Определения subscription_card_product.php:20
$action
Определения file_dialog.php:21
$iterator
Определения yandex_run.php:610
$fields
Определения yandex_run.php:501