29 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_IBLOCK_ID')
38 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_CATALOG')
46 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_EMPTY_FIELDS')
53 if (empty($allowedTypes))
56 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_FIELDS')
60 $filter[
'=TYPE'] = $allowedTypes;
64 if (empty($sectionElements))
69 $sectionIdList = array_keys($sectionElements);
77 '@ID' => $sectionIdList,
80 'order' => [
'ID' =>
'ASC'],
84 $row[
'ID'] = (int)$row[
'ID'];
85 $sectionNames[$row[
'ID']] = $row[
'NAME'];
89 foreach ($sectionIdList as $sectionId)
91 $elementResult = static::updateElementList(
93 $sectionElements[$sectionId],
96 if (!$elementResult->isSuccess())
100 'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_PRODUCTS_UPDATE',
101 [
'#ID#' => $sectionId,
'#NAME#' => $sectionNames[$sectionId]]
108 unset($sectionNames, $sectionIdList, $sectionElements);
120 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_IBLOCK_ID')
125 if (empty($elementIds))
128 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_EMPTY_ELEMENTS')
135 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_EMPTY_FIELDS')
143 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_CATALOG')
149 if (empty($allowedTypes))
152 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_FIELDS')
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' => [
179 foreach ($existProducts as $id)
182 if (!$elementResult->isSuccess())
185 implode(
'; ', $elementResult->getErrorMessages()),
190 foreach ($newProducts as $id)
192 $data[
'fields'][
'ID'] = $id;
194 if (!$elementResult->isSuccess())
197 implode(
'; ', $elementResult->getErrorMessages()),
202 unset($elementResult, $id,
$data);
203 unset($newProducts, $existProducts);
213 if (!(isset(
$USER) &&
$USER instanceof \CUser))
218 if (!AccessController::getCurrent()->check(ActionDictionary::ACTION_PRODUCT_EDIT))
228 if (empty($sections))
234 $filter[
'INCLUDE_SUBSECTIONS'] =
'Y';
235 $filter[
'CHECK_PERMISSIONS'] =
'Y';
236 $filter[
'MIN_PERMISSION'] =
'R';
240 foreach ($sections as $sectionId)
244 $filter[
'SECTION_ID'] = $sectionId;
254 $id = (int)$row[
'ID'];
255 if (isset($dublicates[$id]))
259 $dublicates[$id] =
true;
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]))
319 'PURCHASING_CURRENCY',
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'
333 $list = array_fill_keys($list, $baseTypes);
340 $list += Catalog\Product\SystemField::getAllowedProductTypes();
344 foreach (array_keys(
$fields) as $fieldName)
346 if (!isset($list[$fieldName]))
365 $check = array_intersect($check, $row);
368 $result = array_values($check);
404 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_IBLOCK_ID')
414 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_CATALOG')
425 if (empty($sectionElements))
430 $sectionIdList = array_keys($sectionElements);
432 $iterator = Iblock\SectionTable::getList([
438 '@ID' => $sectionIdList,
441 'order' => [
'ID' =>
'ASC'],
445 $row[
'ID'] = (int)$row[
'ID'];
446 $sectionNames[$row[
'ID']] = $row[
'NAME'];
450 foreach ($sectionIdList as $sectionId)
452 $elementResult = self::convertTypeElementList(
454 $sectionElements[$sectionId],
457 if (!$elementResult->isSuccess())
462 ?
'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_PRODUCTS_CONVERT'
463 :
'BX_CATALOG_PRODUCT_ACTION_ERR_SECTION_SERVICES_CONVERT'
466 '#ID#' => $sectionId,
467 '#NAME#' => $sectionNames[$sectionId],
475 unset($sectionNames, $sectionIdList, $sectionElements);
480 private static function convertTypeElementList(
int $iblockId,
array $elementIds,
int $type): Main\Result
487 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_IBLOCK_ID')
497 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_BAD_CATALOG')
503 Main\Type\Collection::normalizeArrayValuesByInt($elementIds,
true);
504 if (empty($elementIds))
507 Loc::getMessage(
'BX_CATALOG_PRODUCT_ACTION_ERR_EMPTY_ELEMENTS')
513 $productResult = self::getProductListByTypeForConversion($elementIds,
$type);
514 $data = $productResult->getData();
515 $products =
$data[
'PRODUCT_IDS'] ?? [];
517 if (!$productResult->isSuccess())
519 $result->addErrors($productResult->getErrors());
521 unset($productResult);
522 if (empty($products))
527 $inventoryResult = self::checkInventoryDocumentByProducts($products);
528 $data = $inventoryResult->getData();
529 $products =
$data[
'PRODUCT_IDS'] ?? [];
531 if (!$inventoryResult->isSuccess())
533 $result->addErrors($inventoryResult->getErrors());
535 unset($inventoryResult);
536 if (empty($products))
541 $convertResult = self::convertCatalogType($products,
$type);
542 if (!$convertResult->isSuccess())
544 $result->addErrors($convertResult->getErrors());
550 private static function getProductListByTypeForModify(
array $elementIds,
array $types): Main\Result
554 $types = array_fill_keys($types,
true);
557 $newList = array_fill_keys($elementIds,
true);
560 foreach (array_chunk($elementIds, 500) as $pageIds)
562 $iterator = Catalog\ProductTable::getList([
573 $row[
'ID'] = (int)$row[
'ID'];
574 $row[
'TYPE'] = (int)$row[
'TYPE'];
575 unset($newList[$row[
'ID']]);
576 if (isset($types[$row[
'TYPE']]))
578 $existList[] = $row[
'ID'];
582 $errorList[] = $row[
'ID'];
589 'EXIST_PRODUCT_IDS' => $existList,
590 'NEW_PRODUCT_IDS' => array_values($newList),
592 unset($existList, $newList);
594 if (!empty($errorList))
597 $iterator = Iblock\ElementTable::getList([
608 $names[] =
'[' . $row[
'ID'] .
'] ' . $row[
'NAME'];
613 'BX_CATALOG_PRODUCT_ACTION_ERR_CANNOT_SET_FIELD_BY_TYPE',
615 '#NAMES#' => implode(
', ', $names),
626 private static function getProductListByTypeForConversion(
array $elementIds,
int $type): Main\Result
633 foreach (array_chunk($elementIds, 500) as $pageIds)
635 $iterator = Catalog\ProductTable::getList([
646 $row[
'ID'] = (int)$row[
'ID'];
647 $row[
'TYPE'] = (int)$row[
'TYPE'];
648 if ($row[
'TYPE'] ===
$type)
650 $validList[] = $row[
'ID'];
654 $errorList[] = $row[
'ID'];
660 $result->setData([
'PRODUCT_IDS' => $validList]);
662 if (!empty($errorList))
665 $iterator = Iblock\ElementTable::getList([
676 $names[] =
'[' . $row[
'ID'] .
'] ' . $row[
'NAME'];
683 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_NOT_SIMPLE_PRODUCT',
684 [
'#NAMES#' => implode(
', ', $names)]
692 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_NOT_SERVICE',
693 [
'#NAMES#' => implode(
', ', $names)]
704 private static function checkInventoryDocumentByProducts(
array $elementIds): Main\Result
707 if (!Catalog\Config\State::isUsedInventoryManagement())
709 $result->setData([
'PRODUCT_IDS' => $elementIds]);
714 $validList = array_fill_keys($elementIds,
true);
718 $query->setDistinct(
true);
721 'NAME' =>
'ELEMENT.NAME'
723 $query->setFilter([
'=DOCUMENT.STATUS' =>
'Y']);
725 foreach (array_chunk($elementIds, 500) as $pageIds)
727 $query->addFilter(
'@ELEMENT_ID', $pageIds);
731 $id = (int)$row[
'ELEMENT_ID'];
732 unset($validList[$id]);
733 $errorList[] =
'[' . $row[
'ELEMENT_ID'] .
'] ' . $row[
'NAME'];
740 $result->setData([
'PRODUCT_IDS' => array_keys($validList)]);
741 if (!empty($errorList))
745 'BX_CATALOG_PRODUCT_ACTION_ERR_SELECTED_INVENTORY_PRODUCTS',
746 [
'#NAMES#' => implode(
', ', $errorList)]
754 private static function convertCatalogType(
array $elementIds,
int $type): Main\Result
765 $sqlMain .=
' set ' . $helper->quote(
'TYPE') .
' = ' . Catalog\ProductTable::TYPE_SERVICE
766 .
', ' . $helper->quote(
'QUANTITY') .
' = ('
767 .
'CASE WHEN ' . $helper->quote(
'AVAILABLE') .
' = \'Y\' THEN 1 ELSE 0 END'
768 .
'), ' . $helper->quote(
'QUANTITY_RESERVED') .
' = 0'
769 .
', ' . $helper->quote(
'QUANTITY_TRACE') .
' = \'N\''
770 .
', ' . $helper->quote(
'CAN_BUY_ZERO') .
' = \'Y\''
771 .
', ' . $helper->quote(
'NEGATIVE_AMOUNT_TRACE') .
' = \'Y\''
776 $available = Catalog\ProductTable::calculateAvailable([
781 $sqlMain .=
' set ' . $helper->quote(
'TYPE') .
' = ' . Catalog\ProductTable::TYPE_PRODUCT
782 . (Catalog\Config\State::isUsedInventoryManagement()
783 ?
', ' . $helper->quote(
'AVAILABLE') .
' = \'' . $helper->forSql($available) .
'\',
'
784 . $helper->quote('QUANTITY
') . ' = 0
'
787 . ',
' . $helper->quote('QUANTITY_TRACE
') . ' = \
'D\''
788 .
', ' . $helper->quote(
'CAN_BUY_ZERO') .
' = \'D\''
789 .
', ' . $helper->quote(
'NEGATIVE_AMOUNT_TRACE') .
' = \'D\''
793 $sqlMain .=
', ' . $helper->quote(
'TIMESTAMP_X') .
' = ' . $helper->quote(
'TIMESTAMP_X')
798 .
' where PRODUCT_ID in ('
801 foreach (array_chunk($elementIds, 500) as $pageIds)
803 $list = implode(
',', $pageIds);
846 private static function convert(
array $productIdList,
int $catalogType):
Main\
Result
850 if (
Main\Loader::includeModule(
'crm'))
852 $convertCrmProductResult = self::convertCrmProducts($productIdList, $catalogType);
853 if (!$convertCrmProductResult->isSuccess())
855 $result->addErrors($convertCrmProductResult->getErrors());
859 if (
Main\Loader::includeModule(
'sale'))
861 $convertSaleProductResult = self::convertSaleProducts($productIdList, $catalogType);
862 if (!$convertSaleProductResult->isSuccess())
864 $result->addErrors($convertSaleProductResult->getErrors());
871 private static function convertCrmProducts(
array $productIdList,
int $type): Main\Result
876 $productIdSql = $helper->forSql(implode(
',', $productIdList));
878 'UPDATE %s SET TYPE = %d WHERE PRODUCT_ID IN (%s)',
879 $helper->quote(Crm\ProductRowTable::getTableName()),
888 catch (Main\DB\SqlQueryException $exception)
890 return (
new Main\Result())->addError(
new Main\
Error($exception->getMessage()));
893 return new Main\Result();
896 private static function convertSaleProducts(
array $productIdList,
int $type): Main\Result
898 $saleType = Sale\Internals\Catalog\ProductTypeMapper::getType(
$type);
903 $productIdSql = $helper->forSql(implode(
',', $productIdList));
905 'UPDATE %s SET TYPE = %s WHERE PRODUCT_ID IN (%s)',
906 $helper->quote(Sale\Internals\BasketTable::getTableName()),
907 $saleType ?: $helper->convertToDb($saleType),
915 catch (Main\DB\SqlQueryException $exception)
917 return (
new Main\Result())->addError(
new Main\
Error($exception->getMessage()));
920 return new Main\Result();