28 $result->addError(
new Main\
Error(
34 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
37 $result->addError(
new Main\
Error(
45 $result->addError(
new Main\
Error(
52 $allowedTypes = static::getAllowedProductTypes($catalog, $fields);
53 if (empty($allowedTypes))
55 $result->addError(
new Main\
Error(
60 $filter[
'=TYPE'] = $allowedTypes;
64 if (empty($sectionElements))
69 $sectionIdList = array_keys($sectionElements);
71 $iterator = Iblock\SectionTable::getList([
77 '@ID' => $sectionIdList,
78 '=IBLOCK_ID' => $iblockId,
80 'order' => [
'ID' =>
'ASC'],
82 while ($row = $iterator->fetch())
84 $row[
'ID'] = (int)$row[
'ID'];
85 $sectionNames[$row[
'ID']] = $row[
'NAME'];
87 unset($row, $iterator);
89 foreach ($sectionIdList as $sectionId)
91 $elementResult = static::updateElementList(
93 $sectionElements[$sectionId],
96 if (!$elementResult->isSuccess())
98 $result->addError(
new Main\
Error(
100 'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_PRODUCTS_UPDATE',
101 [
'#ID#' => $sectionId,
'#NAME#' => $sectionNames[$sectionId]]
108 unset($sectionNames, $sectionIdList, $sectionElements);
119 $result->addError(
new Main\
Error(
124 Main\Type\Collection::normalizeArrayValuesByInt($elementIds,
true);
125 if (empty($elementIds))
127 $result->addError(
new Main\
Error(
134 $result->addError(
new Main\
Error(
139 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
142 $result->addError(
new Main\
Error(
148 $allowedTypes = static::getAllowedProductTypes($catalog, $fields);
149 if (empty($allowedTypes))
151 $result->addError(
new Main\
Error(
157 $productResult = self::getProductListByTypeForModify($elementIds, $allowedTypes);
158 $data = $productResult->getData();
159 $newProducts = $data[
'NEW_PRODUCT_IDS'] ?? [];
160 $existProducts = $data[
'EXIST_PRODUCT_IDS'] ?? [];
162 if (!$productResult->isSuccess())
164 $result->addErrors($productResult->getErrors());
166 unset($productResult);
168 if (empty($newProducts) && empty($existProducts))
175 'external_fields' => [
176 'IBLOCK_ID' => $iblockId
179 foreach ($existProducts as $id)
181 $elementResult = Catalog\Model\Product::update($id, $data);
182 if (!$elementResult->isSuccess())
184 $result->addError(
new Main\
Error(
185 implode(
'; ', $elementResult->getErrorMessages()),
190 foreach ($newProducts as $id)
192 $data[
'fields'][
'ID'] = $id;
193 $elementResult = Catalog\Model\Product::add($data);
194 if (!$elementResult->isSuccess())
196 $result->addError(
new Main\
Error(
197 implode(
'; ', $elementResult->getErrorMessages()),
202 unset($elementResult, $id, $data);
203 unset($newProducts, $existProducts);
204 unset($blackList, $catalog);
213 if (!(isset($USER) && $USER instanceof \CUser))
218 if (!AccessController::getCurrent()->check(ActionDictionary::ACTION_PRODUCT_EDIT))
227 Main\Type\Collection::normalizeArrayValuesByInt($sections,
false);
228 if (empty($sections))
233 $filter[
'IBLOCK_ID'] = $iblockId;
234 $filter[
'INCLUDE_SUBSECTIONS'] =
'Y';
235 $filter[
'CHECK_PERMISSIONS'] =
'Y';
236 $filter[
'MIN_PERMISSION'] =
'R';
240 foreach ($sections as $sectionId)
242 $result[$sectionId] = [];
244 $filter[
'SECTION_ID'] = $sectionId;
245 $iterator = \CIBlockElement::GetList(
252 while ($row = $iterator->fetch())
254 $id = (int)$row[
'ID'];
255 if (isset($dublicates[$id]))
259 $dublicates[$id] =
true;
262 unset($id, $row, $iterator);
264 if (!empty($elements))
266 $operations = \CIBlockElementRights::UserHasRightTo(
270 \CIBlockRights::RETURN_OPERATIONS
272 foreach ($elements as $elementId)
275 isset($operations[$elementId][
'element_edit'])
276 && isset($operations[$elementId][
'element_edit_price'])
279 $result[$sectionId][] = $elementId;
287 if (empty($result[$sectionId]))
289 unset($result[$sectionId]);
295 return (!empty($result) ? $result :
null);
319 'PURCHASING_CURRENCY',
322 Catalog\ProductTable::TYPE_PRODUCT,
323 Catalog\ProductTable::TYPE_OFFER,
324 CATALOG\ProductTable::TYPE_FREE_OFFER,
327 $catalog[
'CATALOG_TYPE'] === \CCatalogSku::TYPE_FULL
328 && Main\
Config\Option::get(
'catalog',
'show_catalog_tab_with_offers') ===
'Y'
331 $baseTypes[] = Catalog\ProductTable::TYPE_SKU;
333 $list = array_fill_keys($list, $baseTypes);
336 $list[
'VAT_INCLUDED'][] = Catalog\ProductTable::TYPE_SET;
337 $list[
'VAT_ID'][] = Catalog\ProductTable::TYPE_SET;
338 $list[
'SUBSCRIBE'][] = Catalog\ProductTable::TYPE_SET;
340 $list += Catalog\Product\SystemField::getAllowedProductTypes();
344 foreach (array_keys($fields) as $fieldName)
346 if (!isset($list[$fieldName]))
351 $result[] = $list[$fieldName];
356 if (count($result) === 1)
358 $result = reset($result);
362 $check = array_shift($result);
363 foreach ($result as $row)
365 $check = array_intersect($check, $row);
368 $result = array_values($check);
397 private static function convertTypeSectionList(
int $iblockId, array $sections,
int $type): Main\
Result
403 $result->addError(
new Main\
Error(
410 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
413 $result->addError(
new Main\
Error(
425 if (empty($sectionElements))
430 $sectionIdList = array_keys($sectionElements);
432 $iterator = Iblock\SectionTable::getList([
438 '@ID' => $sectionIdList,
439 '=IBLOCK_ID' => $iblockId,
441 'order' => [
'ID' =>
'ASC'],
443 while ($row = $iterator->fetch())
445 $row[
'ID'] = (int)$row[
'ID'];
446 $sectionNames[$row[
'ID']] = $row[
'NAME'];
448 unset($row, $iterator);
450 foreach ($sectionIdList as $sectionId)
452 $elementResult = self::convertTypeElementList(
454 $sectionElements[$sectionId],
457 if (!$elementResult->isSuccess())
459 $result->addError(
new Main\
Error(
462 ?
'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_PRODUCTS_CONVERT'
463 :
'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_SERVICES_CONVERT'
473 $data = $elementResult->getData();
474 if (isset($data[
'CONVERT_COMPLETE']))
476 $result->setData([
'CONVERT_COMPLETE' =>
'Y']);
480 unset($sectionNames, $sectionIdList, $sectionElements);
485 private static function convertTypeElementList(
int $iblockId, array $elementIds,
int $type): Main\Result
487 $result =
new Main\Result();
491 $result->addError(
new Main\
Error(
498 $catalog = \CCatalogSku::GetInfoByIBlock($iblockId);
501 $result->addError(
new Main\
Error(
508 Main\Type\Collection::normalizeArrayValuesByInt($elementIds,
true);
509 if (empty($elementIds))
511 $result->addError(
new Main\
Error(
518 $productResult = self::getProductListByTypeForConversion($elementIds, $type);
519 $data = $productResult->getData();
520 $products = $data[
'PRODUCT_IDS'] ?? [];
522 if (!$productResult->isSuccess())
524 $result->addErrors($productResult->getErrors());
526 unset($productResult);
527 if (empty($products))
532 $inventoryResult = self::checkInventoryDocumentByProducts($products);
533 $data = $inventoryResult->getData();
534 $products = $data[
'PRODUCT_IDS'] ?? [];
536 if (!$inventoryResult->isSuccess())
538 $result->addErrors($inventoryResult->getErrors());
540 unset($inventoryResult);
541 if (empty($products))
546 $convertResult = self::convertCatalogType($products, $type);
547 if (!$convertResult->isSuccess())
549 $result->addErrors($convertResult->getErrors());
551 $data = $convertResult->getData();
552 if (isset($data[
'CONVERT_COMPLETE']))
554 $result->setData([
'CONVERT_COMPLETE' =>
'Y']);
560 private static function getProductListByTypeForModify(array $elementIds, array $types): Main\Result
562 $result =
new Main\Result();
564 $types = array_fill_keys($types,
true);
567 $newList = array_fill_keys($elementIds,
true);
570 foreach (array_chunk($elementIds, 500) as $pageIds)
572 $iterator = Catalog\ProductTable::getList([
581 while ($row = $iterator->fetch())
583 $row[
'ID'] = (int)$row[
'ID'];
584 $row[
'TYPE'] = (int)$row[
'TYPE'];
585 unset($newList[$row[
'ID']]);
586 if (isset($types[$row[
'TYPE']]))
588 $existList[] = $row[
'ID'];
592 $errorList[] = $row[
'ID'];
595 unset($row, $iterator);
599 'EXIST_PRODUCT_IDS' => $existList,
600 'NEW_PRODUCT_IDS' => array_values($newList),
602 unset($existList, $newList);
604 if (!empty($errorList))
607 $iterator = Iblock\ElementTable::getList([
616 while ($row = $iterator->fetch())
618 $names[] =
'[' . $row[
'ID'] .
'] ' . $row[
'NAME'];
620 unset($row, $iterator);
621 $result->addError(
new Main\
Error(
623 'BX_CATALOG_PRODUCT_ACTION_ERR_CANNOT_SET_FIELD_BY_TYPE',
625 '#NAMES#' => implode(
', ', $names),
636 private static function getProductListByTypeForConversion(array $elementIds,
int $type): Main\Result
638 $result =
new Main\Result();
643 foreach (array_chunk($elementIds, 500) as $pageIds)
645 $iterator = Catalog\ProductTable::getList([
654 while ($row = $iterator->fetch())
656 $row[
'ID'] = (int)$row[
'ID'];
657 $row[
'TYPE'] = (int)$row[
'TYPE'];
658 if ($row[
'TYPE'] === $type)
660 $validList[] = $row[
'ID'];
664 $errorList[] = $row[
'ID'];
667 unset($row, $iterator);
670 $result->setData([
'PRODUCT_IDS' => $validList]);
672 if (!empty($errorList))
675 $iterator = Iblock\ElementTable::getList([
684 while ($row = $iterator->fetch())
686 $names[] =
'[' . $row[
'ID'] .
'] ' . $row[
'NAME'];
688 unset($row, $iterator);
691 $result->addError(
new Main\
Error(
693 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_NOT_SIMPLE_PRODUCT',
694 [
'#NAMES#' => implode(
', ', $names)]
700 $result->addError(
new Main\
Error(
702 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_NOT_SERVICE',
703 [
'#NAMES#' => implode(
', ', $names)]
714 private static function checkInventoryDocumentByProducts(array $elementIds): Main\Result
716 $result =
new Main\Result();
717 if (!Catalog\
Config\State::isUsedInventoryManagement())
719 $result->setData([
'PRODUCT_IDS' => $elementIds]);
724 $validList = array_fill_keys($elementIds,
true);
728 $query->setDistinct(
true);
731 'NAME' =>
'ELEMENT.NAME'
733 $query->setFilter([
'=DOCUMENT.STATUS' =>
'Y']);
735 foreach (array_chunk($elementIds, 500) as $pageIds)
737 $query->addFilter(
'@ELEMENT_ID', $pageIds);
738 $iterator = $query->exec();
739 while ($row = $iterator->fetch())
741 $id = (int)$row[
'ELEMENT_ID'];
742 unset($validList[$id]);
743 $errorList[] =
'[' . $row[
'ELEMENT_ID'] .
'] ' . $row[
'NAME'];
745 unset($row, $iterator);
750 $result->setData([
'PRODUCT_IDS' => array_keys($validList)]);
751 if (!empty($errorList))
753 $result->addError(
new Main\
Error(
755 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_INVENTORY_PRODUCTS',
756 [
'#NAMES#' => implode(
', ', $errorList)]
764 private static function convertCatalogType(array $elementIds,
int $type): Main\Result
766 $result =
new Main\Result();
768 $connection = Main\Application::getConnection();
769 $helper = $connection->getSqlHelper();
775 $sqlMain .=
' set ' . $helper->quote(
'TYPE') .
' = ' . Catalog\ProductTable::TYPE_SERVICE
776 .
', ' . $helper->quote(
'QUANTITY') .
' = ('
777 .
'CASE WHEN ' . $helper->quote(
'AVAILABLE') .
' = \'Y\' THEN 1 ELSE 0 END'
778 .
'), ' . $helper->quote(
'QUANTITY_RESERVED') .
' = 0'
779 .
', ' . $helper->quote(
'QUANTITY_TRACE') .
' = \'N\''
780 .
', ' . $helper->quote(
'CAN_BUY_ZERO') .
' = \'Y\''
781 .
', ' . $helper->quote(
'NEGATIVE_AMOUNT_TRACE') .
' = \'Y\''
786 $available = Catalog\ProductTable::calculateAvailable([
791 $sqlMain .=
' set ' . $helper->quote(
'TYPE') .
' = ' . Catalog\ProductTable::TYPE_PRODUCT
792 . (Catalog\Config\State::isUsedInventoryManagement()
793 ?
', ' . $helper->quote(
'AVAILABLE') .
' = \'' . $helper->forSql($available) .
'\',
'
794 . $helper->quote('QUANTITY
') . ' = 0
'
797 . ',
' . $helper->quote('QUANTITY_TRACE
') . ' = \
'D\''
798 .
', ' . $helper->quote(
'CAN_BUY_ZERO') .
' = \'D\''
799 .
', ' . $helper->quote(
'NEGATIVE_AMOUNT_TRACE') .
' = \'D\''
803 $sqlMain .=
', ' . $helper->quote(
'TIMESTAMP_X') .
' = ' . $helper->quote(
'TIMESTAMP_X')
808 .
' where PRODUCT_ID in ('
811 foreach (array_chunk($elementIds, 500) as $pageIds)
813 $list = implode(
',', $pageIds);
814 $connection->query($sqlMain . $list .
')');
818 $connection->query($sqlStores . $list .
')');
825 if (!$result->isSuccess())
831 $result->setData([
'CONVERT_COMPLETE' =>
'Y']);
858 private static function convert(array $productIdList,
int $catalogType): Main\
Result
862 if (Main\Loader::includeModule(
'crm'))
864 $convertCrmProductResult = self::convertCrmProducts($productIdList, $catalogType);
865 if (!$convertCrmProductResult->isSuccess())
867 $result->addErrors($convertCrmProductResult->getErrors());
871 if (Main\Loader::includeModule(
'sale'))
873 $convertSaleProductResult = self::convertSaleProducts($productIdList, $catalogType);
874 if (!$convertSaleProductResult->isSuccess())
876 $result->addErrors($convertSaleProductResult->getErrors());
883 private static function convertCrmProducts(array $productIdList,
int $type): Main\Result
885 $connection = Main\Application::getConnection();
886 $helper = $connection->getSqlHelper();
888 $productIdSql = $helper->forSql(implode(
',', $productIdList));
890 'UPDATE %s SET TYPE = %d WHERE PRODUCT_ID IN (%s)',
891 $helper->quote(Crm\ProductRowTable::getTableName()),
898 $connection->query($sql);
900 catch (Main\DB\SqlQueryException $exception)
902 return (
new Main\Result())->addError(
new Main\
Error($exception->getMessage()));
905 return new Main\Result();
908 private static function convertSaleProducts(array $productIdList,
int $type): Main\Result
910 $saleType = Sale\Internals\Catalog\ProductTypeMapper::getType($type);
912 $connection = Main\Application::getConnection();
913 $helper = $connection->getSqlHelper();
915 $productIdSql = $helper->forSql(implode(
',', $productIdList));
917 'UPDATE %s SET TYPE = %s WHERE PRODUCT_ID IN (%s)',
918 $helper->quote(
Sale\Internals\BasketTable::getTableName()),
919 $saleType ?: $helper->convertToDb($saleType),
925 $connection->query($sql);
927 catch (Main\DB\SqlQueryException $exception)
929 return (
new Main\Result())->addError(
new Main\
Error($exception->getMessage()));
932 return new Main\Result();