AnonSec Shell
Server IP : 85.193.89.191  /  Your IP : 3.144.242.40
Web Server : Apache
System : Linux 956367-cx40159.tmweb.ru 3.10.0-1160.105.1.el7.x86_64 #1 SMP Thu Dec 7 15:39:45 UTC 2023 x86_64
User : bitrix ( 600)
PHP Version : 8.1.27
Disable Function : NONE
MySQL : OFF  |  cURL : OFF  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /home/bitrix/www/bitrix/modules/catalog/lib/controller/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME ]     

Current File : /home/bitrix/www/bitrix/modules/catalog/lib/controller/product.php
<?php

namespace Bitrix\Catalog\Controller;

use Bitrix\Catalog\Component\UseStore;
use Bitrix\Catalog\Access\ActionDictionary;
use Bitrix\Catalog\Controller\Product\SkuDeferredCalculations;
use Bitrix\Catalog\Model\Event;
use Bitrix\Iblock;
use Bitrix\Main\Engine;
use Bitrix\Main\Engine\Response\DataType\Page;
use Bitrix\Main\Error;
use Bitrix\Main\ORM\Data\DataManager;
use Bitrix\Main\Result;
use Bitrix\Main\Engine\ActionFilter\Scope;
use Bitrix\Main\UI\PageNavigation;
use Bitrix\Rest\Event\EventBindInterface;
use Bitrix\Rest\RestException;

class Product extends Controller implements EventBindInterface
{
	use SkuDeferredCalculations;

	/**
	 * @inheritDoc
	 */
	public function configureActions()
	{
		return [
			'addProperty' => [
				'+prefilters' => [new Scope(Scope::AJAX)]
			],
			'getSkuTreeProperties' => [
				'+prefilters' => [new Scope(Scope::AJAX)]
			],
		];
	}

	/**
	 * @inheritDoc
	 */
	protected function processBeforeAction(Engine\Action $action)
	{
		$r = new Result();

		if ($action->getName() === 'add')
		{
			$r = $this->processBeforeAdd($action);
		}
		else if ($action->getName() === 'update')
		{
			$r = $this->processBeforeUpdate($action);
		}

		if (!$r->isSuccess())
		{
			$this->addErrors($r->getErrors());

			return null;
		}

		if ($this->isActionWithDefferedCalculation($action))
		{
			$this->processBeforeDeferredCalculationAction();
		}

		return parent::processBeforeAction($action);
	}

	/**
	 * @inheritDoc
	 *
	 * @param Engine\Action $action
	 * @param mixed $result
	 *
	 * @return void
	 */
	protected function processAfterAction(Engine\Action $action, $result)
	{
		if ($this->isActionWithDefferedCalculation($action))
		{
			$this->processAfterDeferredCalculationAction();
		}

		return parent::processAfterAction($action, $result);
	}

	/**
	 * @throws \Bitrix\Main\ArgumentException
	 * @throws \Bitrix\Main\ObjectPropertyException
	 * @throws \Bitrix\Main\SystemException
	 * @throws \Bitrix\Main\NotImplementedException
	 */
	protected function processBeforeUpdate(Engine\Action $action): Result
	{
		$r = new Result();

		$arguments = $action->getArguments();

		$fields = $arguments['fields'];
		$productId = $arguments['id'];

		$iblockId = $this->getProductIblockId($productId);
		$iblockIdOrigin = $fields['iblockId'] ?? null;
		if ($iblockIdOrigin !== null)
		{
			$iblockIdOrigin = (int)$iblockIdOrigin;
		}

		if ($iblockIdOrigin && $iblockIdOrigin !== $iblockId)
		{
			$r->addError(
				new Error(
					sprintf(
						'Product - %d is not exists in catalog - %d', $productId , $iblockIdOrigin
					)
				)
			);
		}

		return $r;
	}

	protected function processBeforeAdd(Engine\Action $action): Result
	{
		return new Result();
	}

	//region Actions
	public function getFieldsByFilterAction($filter): ?array
	{
		/** @var \Bitrix\Catalog\RestView\Product $view */
		$view = $this->getViewManager()
			->getView($this);
		$r = $view->getFieldsByFilter($filter);

		if(!$r->isSuccess())
		{
			$this->addErrors($r->getErrors());
			return null;
		}
		else
		{
			return [$this->getServiceItemName() =>$view->prepareFieldInfos(
				$r->getData()
			)];
		}
	}

	static protected function perfGetList(array $select, array $filter, array $order, $pageNavigation = null): array
	{
		$rawRows = [];
		$elementIds = [];

		$rsData = \CIBlockElement::GetList(
			$order,
			$filter,
			false,
			$pageNavigation ?? false,
			array('ID', 'IBLOCK_ID')
		);
		while($row = $rsData->Fetch())
		{
			$rawRows[$row['ID']] = $row;
			$elementIds[] = $row['ID'];
		}

		foreach (array_chunk($elementIds, \IRestService::LIST_LIMIT) as $pageIds)
		{
			$elementFilter = [
				'IBLOCK_ID' => $filter['IBLOCK_ID'],
				'ID' => $pageIds,
			];
			$iterator = \CIBlockElement::GetList([], $elementFilter, false, false, $select);
			while ($row = $iterator->Fetch())
			{
				$rawRows[$row['ID']] += $row;
			}
		}

		return $rawRows;
	}

	/**
	 * @param $select
	 * @param $filter
	 * @param $order
	 * @param PageNavigation $pageNavigation
	 * @return Page|null
	 */
	public function listAction(PageNavigation $pageNavigation, array $select = [], array $filter = [], array $order = []): ?Page
	{
		$r = $this->checkPermissionIBlockElementList($filter['IBLOCK_ID']);
		if($r->isSuccess())
		{
			$result = [];

			$select = empty($select)? array_merge(['*'], $this->getAllowedFieldsProduct()):$select;
			$order = empty($order)? ['ID'=>'ASC']:$order;

			$groupFields = $this->splitFieldsByEntity(
				array_flip($select)
			);
			$allProperties = isset($groupFields['elementFields']['PROPERTY_*']);
			if ($allProperties)
			{
				unset($groupFields['elementFields']['PROPERTY_*']);
			}

			$productFields = array_keys($groupFields['productFields']);
			$elementFields = array_keys($groupFields['elementFields']);
			$propertyFields = $groupFields['propertyFields'];

			$propertyFields = $this->preparePropertyFields($propertyFields);
			$propertyIds = array_keys($propertyFields);
			$list = self::perfGetList(array_merge($productFields, $elementFields), $filter, $order, self::getNavData($pageNavigation->getOffset()));

			if (!empty($list))
			{
				if ($allProperties || !empty($propertyIds))
				{
					$this->attachPropertyValues($list, (int)$filter['IBLOCK_ID'], $propertyIds);
				}

				foreach ($list as $row)
				{
					$result[] = $row;
				}
			}

			return new Page($this->getServiceListName(), $result, function() use ($filter)
			{
				return (int)\CIBlockElement::GetList([], $filter, []);
			});
		}
		else
		{
			$this->addErrors($r->getErrors());
			return null;
		}
	}

	public function getAction($id)
	{
		$r = $this->checkPermissionIBlockElementGet($id);
		if($r->isSuccess())
		{
			$r = $this->exists($id);
			if($r->isSuccess())
			{
				return [$this->getServiceItemName() => $this->get($id)];
			}
		}

		if($r->isSuccess() === false)
		{
			$this->addErrors($r->getErrors());
			return null;
		}
	}

	public function addAction(array $fields): ?array
	{
		$r = $this->checkPermissionAdd($fields['IBLOCK_ID']);
		if($r->isSuccess())
		{
			if (isset($fields['IBLOCK_SECTION_ID']) && (int)$fields['IBLOCK_SECTION_ID'] > 0)
			{
				$r = $this->checkPermissionIBlockElementSectionBindUpdate($fields['IBLOCK_SECTION_ID']);
			}
		}

		if($r->isSuccess())
		{
			$id = 0;
			$element = new \CIBlockElement();

			$r = $this->addValidate($fields);
			if($r->isSuccess())
			{
				$groupFields = $this->splitFieldsByEntity($fields);

				$productFields = $groupFields['productFields'];
				$propertyFields = $groupFields['propertyFields'];
				$elementFields = $groupFields['elementFields'];

				$productFields = $this->prepareProductFields($productFields);
				$propertyFields = $this->preparePropertyFields($propertyFields);
				$elementFieldsAdd = count($propertyFields)>0 ? array_merge($elementFields, ['PROPERTY_VALUES'=>$propertyFields]):$elementFields;

				$id = $element->Add($elementFieldsAdd);
				if($element->LAST_ERROR<>'')
				{
					$r->addError(new Error($element->LAST_ERROR));
				}
				else
				{
					$productFields['ID'] = $id;

					$r = \Bitrix\Catalog\Model\Product::add($productFields);
					if($r->isSuccess() === false)
					{
						$element::Delete($id);
					}
				}
			}
		}

		if(!$r->isSuccess())
		{
			$this->addErrors($r->getErrors());
			return null;
		}
		else
		{
			return ['ELEMENT'=>$this->get($id)];
		}
	}

	public function updateAction(int $id, array $fields): ?array
	{
		$fields['IBLOCK_ID'] ??= $this->getProductIblockId($id);
		$r = $this->checkPermissionUpdate($id);
		if($r->isSuccess())
		{
			if (isset($fields['IBLOCK_SECTION_ID']) && (int)$fields['IBLOCK_SECTION_ID'] > 0)
			{
				$r = $this->checkPermissionIBlockElementSectionBindUpdate($fields['IBLOCK_SECTION_ID']);
			}
		}

		if($r->isSuccess())
		{
			$element = new \CIBlockElement();

			$groupFields = $this->splitFieldsByEntity($fields);

			$productFields = $groupFields['productFields'];
			$propertyFields = $groupFields['propertyFields'];
			$elementFields = $groupFields['elementFields'];

			$productFields = $this->prepareProductFields($productFields);
			$propertyFields = $this->preparePropertyFields($propertyFields);

			$propertyFields = $this->fillPropertyFieldsDefaultPropertyValues($id, $fields['IBLOCK_ID'], $propertyFields);
			$propertyFields = $this->preparePropertyFieldsUpdate($propertyFields);

			$elementFieldsUpdate = count($propertyFields)>0 ? array_merge($elementFields, ['PROPERTY_VALUES'=>$propertyFields]):$elementFields;

			$r = $this->exists($id);
			if($r->isSuccess())
			{
				$r = $this->updateValidate($elementFieldsUpdate+['ID'=>$id]);
				if($r->isSuccess())
				{
					$element->Update($id, $elementFieldsUpdate);
					if($element->LAST_ERROR<>'')
					{
						$r->addError(new Error($element->LAST_ERROR));
					}
					elseif (!empty($productFields))
					{
						$r = \Bitrix\Catalog\Model\Product::update($id, $productFields);
					}
				}
			}
		}

		if($r->isSuccess())
		{
			return ['ELEMENT'=>$this->get($id)];
		}
		else
		{
			$this->addErrors($r->getErrors());
			return null;
		}
	}

	public function deleteAction(int $id): ?bool
	{
		$r = $this->checkPermissionDelete($id);
		if($r->isSuccess())
		{
			$r = $this->exists($id);
		}
		if($r->isSuccess())
		{
			if (!\CIBlockElement::Delete($id))
			{
				if ($ex = self::getApplication()->GetException())
					$r->addError(new Error($ex->GetString(), $ex->GetId()));
				else
					$r->addError(new Error('delete iBlockElement error'));
			}
		}

		if($r->isSuccess())
		{
			return true;
		}
		else
		{
			$this->addErrors($r->getErrors());
			return null;
		}
	}

	public function downloadAction(array $fields): ?Engine\Response\BFile
	{
		$productId = $fields['PRODUCT_ID'];
		$fieldName = $fields['FIELD_NAME'];
		$id = $fields['FILE_ID'];
		$file = [];

		$r = $this->exists($productId);
		if($r->isSuccess())
		{
			$iblockId = $this->get($productId)['IBLOCK_ID'];

			if($this->checkFieldsDownload(['NAME'=>$fieldName, 'IBLOCK_ID'=>$iblockId]) == true)
			{
				$files = [];
				$iBlock = \CIBlock::GetArrayByID($iblockId);

				if ($productId > 0)
				{
					$element = \CIBlockElement::GetList(
						array(),
						array(
							"CATALOG_ID" => $iBlock["ID"],
							"=ID" => $productId,
							"CHECK_PERMISSIONS" => "N",
						),
						false,
						false,
						array("ID", $fieldName)
					);
					while ($res = $element->GetNext())
					{
						if (isset($res[$fieldName]))
						{
							$files[] = $res[$fieldName];
						}
						elseif (isset($res[$fieldName."_VALUE"]))
						{
							if (is_array($res[$fieldName."_VALUE"]))
							{
								$files = array_merge($files, $res[$fieldName."_VALUE"]);
							}
							else
							{
								$files[] = $res[$fieldName."_VALUE"];
							}
						}
					}
				}

				if (!in_array($id, $files))
				{
					$r->addError(new Error('Product file wrong'));
				}
				else
				{
					$file = \CFile::GetFileArray($id);
					if (is_array($file) == false)
					{
						$r->addError(new Error('Product is empty'));
					}
				}
			}
			else
			{
				$r->addError(new Error('Name file field is not available'));
			}
		}

		if($r->isSuccess())
		{
			return \Bitrix\Main\Engine\Response\BFile::createByFileId($file['ID']);
		}
		else
		{
			$this->addErrors($r->getErrors());
			return null;
		}
	}
	//endregion Actions

	protected function getEntityTable()
	{
		return \Bitrix\Catalog\Model\Product::getTabletClassName();
	}

	protected function splitFieldsByEntity($fields): array
	{
		$productFields = [];
		$elementFields = [];
		$propertyFields = [];

		foreach($fields as $name=>$value)
		{
			if(in_array($name, $this->getAllowedFieldsProduct()))
			{
				$productFields[$name] = $value;
			}
			else
			{
				if (preg_match('/^(PROPERTY_\d+)$/', $name))
				{
					$propertyFields[$name] = $value;
				}
				else
				{
					$elementFields[$name] = $value;
				}
			}
		}

		return [
			'productFields'=>$productFields,
			'propertyFields'=>$propertyFields,
			'elementFields'=>$elementFields
		];
	}

	protected function prepareProductFields(array $fields): array
	{
		$result = $fields;

		if (UseStore::isUsed())
		{
			unset($result['QUANTITY_TRACE']);
		}

		return $result;
	}

	protected function preparePropertyFields($fields)
	{
		$result = [];
		$matches = [];

		foreach($fields as $name=>$value)
		{
			if (preg_match('/^(PROPERTY_)(\d+)$/', $name, $matches))
			{
				$result[$matches[2]] = $value;
			}
		}
		return $result;
	}

	protected function preparePropertyFieldsUpdate($fields): array
	{
		$result = [];

		if(count($fields)>0)
		{
			foreach ($fields as $propertyId=>$value)
			{
				$property = [];
				// single
				if(isset($value['VALUE']))
				{
					if(isset($value['VALUE_ID']))
					{
						//update
						$valueId=$value['VALUE_ID'];
						unset($value['VALUE_ID']);
						$property[$valueId]=$value;

					}
					else
					{
						//replace
						$property[]=$value;
					}
				}
				// multi
				else
				{
					if(is_array($value) && count($value)>0)
					{
						foreach ($value as $item)
						{
							if(isset($item['VALUE_ID']))
							{
								//update
								$valueId = $item['VALUE_ID'];
								unset($item['VALUE_ID']);
								$property[$valueId]=$item;
							}
							else
							{
								//replace
								$property[]=$item;
							}
						}
					}
				}

				if(count($property)>0)
				{
					$result[$propertyId]=$property;
				}
			}
		}
		return $result;
	}

	protected function fillPropertyFieldsDefaultPropertyValues($id, $iblockId, $propertyValues)
	{
		$fields = $propertyValues;

		if ($id > 0 && $iblockId > 0 && !empty($propertyValues))
		{
			$r = \CIBlockElement::GetProperty(
				$iblockId,
				$id,
				'SORT',
				'ASC',
				[
					'CHECK_PERMISSIONS' => 'N',
				]
			);
			while ($property = $r->Fetch())
			{
				if (
					$property['PROPERTY_TYPE'] !== Iblock\PropertyTable::TYPE_FILE
					&& !array_key_exists($property['ID'], $propertyValues)
				)
				{
					$fields[$property['ID']] ??= [];

					$fields[$property['ID']][] = [
						'VALUE_ID' => $property['PROPERTY_VALUE_ID'],
						'VALUE' => $property['VALUE'],
						'DESCRIPTION' => $property['DESCRIPTION'],
					];
				}
			}
			unset($property, $r);
		}

		return $fields;
	}

	protected function exists($id)
	{
		$r = new Result();
		if (isset($this->get($id)['ID']) == false)
		{
			$r->addError(new Error('Product is not exists'));
		}

		return $r;
	}

	protected function get($id)
	{
		$row = \CIBlockElement::getList(
			[],
			['ID' => $id],
			false,
			false,
			[
				'*',
				...$this->getAllowedFieldsProduct()
			]
		)->fetch();

		if (!$row)
		{
			return [];
		}

		$result = [
			$row['ID'] => $row,
		];

		$this->attachPropertyValues($result, (int)$row['IBLOCK_ID']);

		return $result[$row['ID']];
	}

	protected function addValidate($fields)
	{
		return $this->checkFields($fields);
	}

	protected function updateValidate($fields)
	{
		return $this->checkFields($fields);
	}

	protected function checkFields($fields)
	{
		$r = new Result();

		if (isset($fields['SECTION_ID']))
		{
			$section = \CIBlockSection::GetByID($fields['SECTION_ID'])->Fetch();
			if (!isset($section['ID']))
			{
				$r->addError(new Error('Section is not exists'));
			}
		}
		if (isset($fields['MODIFIED_BY']))
		{
			$user = \CUser::GetByID($fields['MODIFIED_BY'])->Fetch();
			if (!isset($user['ID']))
			{
				$r->addError(new Error('User modifiedBy is not exists'));
			}
		}
		if (isset($fields['CREATED_BY']))
		{
			$user = \CUser::GetByID($fields['CREATED_BY'])->Fetch();
			if (!isset($user['ID']))
			{
				$r->addError(new Error('User createdBy is not exists'));
			}
		}
		if (isset($fields['PURCHASING_CURRENCY']))
		{
			$currency = \CCurrency::GetByID($fields['PURCHASING_CURRENCY']);
			if (!isset($currency['CURRENCY']))
			{
				$r->addError(new Error('Currency purchasingCurrency is not exists'));
			}
		}
		if (isset($fields['VAT_ID']))
		{
			$user = \CCatalogVat::GetByID($fields['VAT_ID'])->Fetch();
			if (!isset($user['ID']))
			{
				$r->addError(new Error('VAT vatId is not exists'));
			}
		}

		return $r;
	}

	/**
	 * @param array &$result
	 * @param int $iblockId
	 * @param array $propertyIds
	 * @return void
	 */
	protected function attachPropertyValues(array &$result, int $iblockId, array $propertyIds = []): void
	{
		if ($iblockId <= 0)
		{
			return;
		}

		$propertyFilter = !empty($propertyIds) ? ['ID' => $propertyIds] : [];

		$propertyValues = [];
		\CIBlockElement::getPropertyValuesArray(
			$propertyValues,
			$iblockId,
			['ID' => array_keys($result)],
			$propertyFilter,
			['USE_PROPERTY_ID' => 'Y']
		);

		foreach ($result as $k => $v)
		{
			if (isset($propertyValues[$k]))
			{
				foreach ($propertyValues[$k] as $propId => $fields)
				{
					$value = null;

					if (isset($fields['PROPERTY_VALUE_ID']))
					{
						if ($fields['PROPERTY_TYPE'] === Iblock\PropertyTable::TYPE_LIST)
						{
							if ($fields['MULTIPLE'] === 'Y')
							{
								if (is_array($fields['PROPERTY_VALUE_ID']))
								{
									foreach ($fields['PROPERTY_VALUE_ID'] as $i => $item)
									{
										$value[] = [
											'VALUE' => $fields['VALUE_ENUM_ID'][$i],
											'VALUE_ENUM' => $fields['VALUE_ENUM'][$i],
											'VALUE_ID' => $fields['PROPERTY_VALUE_ID'][$i],
										];
									}
								}
							}
							else
							{
								$value = [
									'VALUE' => $fields['VALUE_ENUM_ID'],
									'VALUE_ENUM' => $fields['VALUE_ENUM'],
									'VALUE_ID' => $fields['PROPERTY_VALUE_ID']
								];
							}
						}
						else
						{
							if ($fields['MULTIPLE'] === 'Y')
							{
								if (is_array($fields['PROPERTY_VALUE_ID']))
								{
									foreach ($fields['PROPERTY_VALUE_ID'] as $i => $item)
									{
										$value[] = [
											'VALUE' => $fields['VALUE'][$i],
											'VALUE_ID' => $fields['PROPERTY_VALUE_ID'][$i]
										];
									}
								}
							}
							else
							{
								$value = [
									'VALUE' => $fields['VALUE'],
									'VALUE_ID' => $fields['PROPERTY_VALUE_ID']
								];
							}
						}
					}

					$result[$k]['PROPERTY_' . $propId] = $value;
				}
			}
			elseif (!empty($propertyIds))
			{
				/**
				 * if property values are empty $propertyValues is empty
				 */

				foreach ($propertyIds as $propId)
				{
					$result[$k]['PROPERTY_' . $propId] = null;
				}
			}
		}
	}

	protected function checkPermissionEntity($name, $arguments=[])
	{
		$name = mb_strtolower($name); //for ajax mode

		if($name == 'getfieldsbyfilter'
			|| $name == 'download'
		)
		{
			$r = $this->checkReadPermissionEntity();
		}
		else
		{
			$r = parent::checkPermissionEntity($name);
		}

		return $r;
	}

	/**
	 * @return string[]
	 */
	protected function getAllowedFieldsProduct(): array
	{
		$result = [
			'TYPE',
			'AVAILABLE',
			'BUNDLE',
			'QUANTITY',
			'QUANTITY_RESERVED',
			'QUANTITY_TRACE',
			'CAN_BUY_ZERO',
			'SUBSCRIBE',
			'VAT_ID',
			'VAT_INCLUDED',
			'BARCODE_MULTI',
			'WEIGHT',
			'LENGTH',
			'WIDTH',
			'HEIGHT',
			'MEASURE',
			'RECUR_SCHEME_LENGTH',
			'RECUR_SCHEME_TYPE',
			'TRIAL_PRICE_ID',
			'WITHOUT_ORDER',
			'QUANTITY_TRACE_RAW',
			'PAYMENT_TYPE',
			'SUBSCRIBE_RAW',
			'CAN_BUY_ZERO_RAW'
		];

		if ($this->accessController->check(ActionDictionary::ACTION_PRODUCT_PURCHASE_INFO_VIEW))
		{
			array_push($result, 'PURCHASING_PRICE', 'PURCHASING_CURRENCY');
		}

		return $result;
	}

	protected function checkFieldsDownload($fields)
	{
		$name = $fields['NAME'];
		$iblockId = $fields['IBLOCK_ID'];

		if ($name === "DETAIL_PICTURE")
			return true;
		elseif ($name === "PREVIEW_PICTURE")
			return true;
		elseif ($name === "PICTURE")
			return true;
		elseif (!preg_match("/^PROPERTY_(.+)\$/", $name, $match))
			return false;
		else
		{
			$db_prop = \CIBlockProperty::GetPropertyArray($match[1], $iblockId);
			if(is_array($db_prop) && $db_prop["PROPERTY_TYPE"] === "F")
				return true;
		}
		return false;
	}

	public function addPropertyAction($fields)
	{
		$r = $this->checkPermissionIBlockModify($fields['IBLOCK_ID']);
		if(!$r->isSuccess())
		{
			$this->addErrors($r->getErrors());
			return null;
		}

		$iblockProperty = new \CIBlockProperty();

		$propertyFields = array(
			'ACTIVE' => 'Y',
			'IBLOCK_ID' => $fields['IBLOCK_ID'],
			'NAME' => $fields['NAME'],
			'SORT' => $fields['SORT'] ?? 100,
			'CODE' => $fields['CODE'] ?? '',
			'MULTIPLE' => ($fields['MULTIPLE'] === 'Y') ? 'Y' : 'N',
			'IS_REQUIRED'=> ($fields['IS_REQUIRED'] === 'Y') ? 'Y' : 'N',
			'SECTION_PROPERTY'=> 'N',
		);

		$newID = (int)($iblockProperty->Add($propertyFields));
		if ($newID === 0)
		{
			$this->addError(new \Bitrix\Main\Error($iblockProperty->LAST_ERROR));
			return null;
		}

		return [
			'ID' => $newID,
			'CONTROL_ID' => 'PROPERTY_'.$newID
		];
	}

	//region checkPermissionController
	protected function checkModifyPermissionEntity()
	{
		return $this->checkReadPermissionEntity();
	}

	protected function checkReadPermissionEntity()
	{
		$r = new Result();

		if (
			!$this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
			&& !$this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT)
			&& !$this->accessController->check(ActionDictionary::ACTION_CATALOG_VIEW)
		)
		{
			$r->addError(new Error('Access Denied', 200040300010));
		}
		return $r;
	}
	//endregion checkPermissionController

	//region checkPermissionIBlock
	protected function existsIblock(int $iblockId): Result
	{
		$result = new Result();

		$arIBlock = \CIBlock::GetArrayByID($iblockId, 'NAME');
		if (empty($arIBlock))
		{
			$result->addError(new Error('Iblock Not Found', 200040300000));
		}

		return $result;
	}

	protected function checkPermissionAdd(int $iblockId): Result
	{
		$result = new Result();

		$result->addErrors(
			$this->checkPermissionIBlockElementAdd($iblockId)->getErrors()
		);
		$result->addErrors(
			$this->checkPermissionCatalogProductAdd()->getErrors()
		);

		return $result;
	}

	protected function checkPermissionCatalogProductAdd(): Result
	{
		$result = new Result();

		if (!$this->accessController->check(ActionDictionary::ACTION_PRODUCT_ADD))
		{
			$result->addError(new Error('Access Denied', 200040300040));
		}

		return $result;
	}

	protected function checkPermissionIBlockElementAdd(int $iblockId): Result
	{
		//return $this->checkPermissionIBlockElementList($iblockId);
		return $this->checkPermissionIBlockElementModify($iblockId, 0);
	}

	protected function checkPermissionUpdate(int $elementId): Result
	{
		$result = new Result();

		$result->addErrors(
			$this->checkPermissionIBlockElementUpdate($elementId)->getErrors()
		);
		$result->addErrors(
			$this->checkPermissionCatalogProductUpdate($elementId)->getErrors()
		);

		return $result;
	}

	protected function checkPermissionCatalogProductUpdate(int $elementId): Result
	{
		$result = new Result();

		if (!$this->accessController->check(ActionDictionary::ACTION_PRODUCT_EDIT))
		{
			$result->addError(new Error('Access Denied', 200040300040));
		}

		return $result;
	}

	protected function checkPermissionIBlockElementUpdate(int $elementId): Result
	{
		$iblockId = \CIBlockElement::GetIBlockByID($elementId);

		return $this->checkPermissionIBlockElementModify($iblockId, $elementId);
	}

	protected function checkPermissionIBlockModify($iblockId): Result
	{
		$iblockId = (int)$iblockId;

		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if (!\CIBlockRights::UserHasRightTo($iblockId, $iblockId, self::IBLOCK_EDIT))
		{
			$r->addError(new Error('Access Denied', 200040300040));
		}

		return $r;
	}

	protected function checkPermissionIBlockElementModify($iblockId, $elementId): Result
	{
		$iblockId = (int)$iblockId;

		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if ($elementId > 0)
		{
			$bBadBlock = !\CIBlockElementRights::UserHasRightTo($iblockId, $elementId, self::IBLOCK_ELEMENT_EDIT); //access edit
		}
		else
		{
			$bBadBlock = !\CIBlockRights::UserHasRightTo($iblockId, $iblockId, self::IBLOCK_ELEMENT_EDIT);
		}

		if($bBadBlock)
		{
			$r->addError(new Error('Access Denied', 200040300043));
		}

		return $r;
	}

	protected function checkPermissionDelete(int $elementId): Result
	{
		$result = new Result();

		$result->addErrors(
			$this->checkPermissionIBlockElementDelete($elementId)->getErrors()
		);
		$result->addErrors(
			$this->checkPermissionCatalogProductDelete($elementId)->getErrors()
		);

		return $result;
	}

	protected function checkPermissionCatalogProductDelete(int $elementId): Result
	{
		$result = new Result();

		if (!$this->accessController->check(ActionDictionary::ACTION_PRODUCT_DELETE))
		{
			$result->addError(new Error('Access Denied', 200040300040));
		}

		return $result;
	}

	protected function checkPermissionIBlockElementDelete(int $elementId): Result
	{
		$iblockId = (int)\CIBlockElement::GetIBlockByID($elementId);

		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if (!\CIBlockElementRights::UserHasRightTo($iblockId, $elementId, self::IBLOCK_ELEMENT_DELETE)) //access delete
		{
			$r->addError(new Error('Access Denied', 200040300040));
		}

		return $r;
	}

	protected function checkPermissionIBlockElementGet($elementId): Result
	{
		$iblockId = (int)\CIBlockElement::GetIBlockByID($elementId);
		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if (!\CIBlockElementRights::UserHasRightTo($iblockId, $elementId, self::IBLOCK_ELEMENT_READ)) //access read
		{
			$r->addError(new Error('Access Denied', 200040300040));
		}

		return $r;
	}

	protected function checkPermissionIBlockElementList($iblockId): Result
	{
		$iblockId = (int)$iblockId;
		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if (!\CIBlockRights::UserHasRightTo($iblockId, $iblockId, self::IBLOCK_READ))
		{
			$r->addError(new Error('Access Denied', 200040300030));
		}

		return $r;
	}

	protected function checkPermissionIBlockElementSectionBindModify($iblockId, $iblockSectionId): Result
	{
		$iblockId = (int)$iblockId;
		$r = $this->existsIblock($iblockId);
		if (!$r->isSuccess())
		{
			return $r;
		}

		if (!\CIBlockSectionRights::UserHasRightTo(
			$iblockId,
			$iblockSectionId,
			self::IBLOCK_ELEMENT_SECTION_BIND
		)) //access update
		{
			$r->addError(new Error('Access Denied', 200040300050));
		}

		return $r;
	}

	protected function checkPermissionIBlockElementSectionBindUpdate($iblockSectionId): Result
	{
		$iblockId = $this->getIBlockBySectionId($iblockSectionId);

		return $this->checkPermissionIBlockElementSectionBindModify($iblockId, $iblockSectionId);
	}

	protected function getIBlockBySectionId($id): int
	{
		$section = \CIBlockSection::GetList(
			[],
			[
				'ID' => (int)$id,
			],
			false,
			[
				'ID',
				'IBLOCK_ID',
			]
		);
		$res = $section->Fetch();
		unset($section);

		return (int)($res['IBLOCK_ID'] ?? 0);
	}
	//endregion

	// rest-event region
	/**
	 * @inheritDoc
	 */
	public static function getCallbackRestEvent(): array
	{
		return [self::class, 'processItemEvent'];
	}

	public static function processItemEvent(array $arParams, array $arHandler): array
	{
		$id = null;
		$event = $arParams[0] ?? null;

		if (!$event)
		{
			throw new RestException('event object not found trying to process event');
		}

		if($event instanceof Event) // update, add
		{
			$id = $event->getParameter('id');
		}
		else if($event instanceof \Bitrix\Main\ORM\Event) // delete
		{
			$item = $event->getParameter('id');
			$id = is_array($item) ? $item['ID']: $item;
		}

		if (!$id)
		{
			throw new RestException('id not found trying to process event');
		}

		$product = \Bitrix\Catalog\Model\Product::getCacheItem($id);

		$type = $product['TYPE']  ?? null;

		if (!$type)
		{
			throw new RestException('type is not specified trying to process event');
		}

		return [
			'FIELDS' => [
				'ID' => $id,
				'TYPE' => $type
			],
		];
	}

	protected static function getBindings(): array
	{
		$entity = (new static())->getEntity();
		$class = $entity->getNamespace() . $entity->getName();
		$model = \Bitrix\Catalog\Model\Product::class;

		return [
			Event::makeEventName($model,DataManager::EVENT_ON_AFTER_ADD) => $entity->getModule().'.'.$entity->getName().'.on.add',
			Event::makeEventName($model,DataManager::EVENT_ON_AFTER_UPDATE) => $entity->getModule().'.'.$entity->getName().'.on.update',
			Event::makeEventName($class,DataManager::EVENT_ON_DELETE) => $entity->getModule().'.'.$entity->getName().'.on.delete',
		];
	}
	// endregion

	// region Internal tools

	/**
	 * Returns iblock id for product, if exists.
	 *
	 * @param int $productId
	 * @return int|null
	 */
	protected static function getProductIblockId(int $productId): ?int
	{
		$iblockId = \CIBlockElement::GetIBlockByID($productId);

		return
			$iblockId === false
				? null
				: $iblockId
		;
	}

	// endRegion
}

Anon7 - 2022
AnonSec Team