403Webshell
Server IP : 80.87.202.40  /  Your IP : 216.73.216.169
Web Server : Apache
System : Linux rospirotorg.ru 5.14.0-539.el9.x86_64 #1 SMP PREEMPT_DYNAMIC Thu Dec 5 22:26:13 UTC 2024 x86_64
User : bitrix ( 600)
PHP Version : 8.2.27
Disable Function : NONE
MySQL : OFF |  cURL : ON |  WGET : ON |  Perl : ON |  Python : OFF |  Sudo : ON |  Pkexec : ON
Directory :  /home/bitrix/ext_www/cvetdv.ru/bitrix/components/bitrix/catalog.store.document.detail/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/components/bitrix/catalog.store.document.detail/class.php
<?php

use Bitrix\Catalog;
use Bitrix\Catalog\Access\AccessController;
use Bitrix\Catalog\Access\ActionDictionary;
use Bitrix\Catalog\Access\Model\StoreDocumentElement;
use Bitrix\Catalog\Document\DocumentFieldsManager;
use Bitrix\Catalog\Document\StoreDocumentTableManager;
use Bitrix\Catalog\Config\Feature;
use Bitrix\Catalog\Config\State;
use Bitrix\Catalog\StoreDocumentBarcodeTable;
use Bitrix\Catalog\StoreDocumentElementTable;
use Bitrix\Catalog\StoreDocumentTable;
use Bitrix\Catalog\Url\InventoryManagementSourceBuilder;
use Bitrix\Catalog\v2\Integration\UI\EntityEditor\StoreDocumentProvider;
use Bitrix\Catalog\v2\IoC\ServiceContainer;
use Bitrix\Main\Engine\Contract\Controllerable;
use Bitrix\Main\Engine\Response\AjaxJson;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Web\Uri;
use Bitrix\Sale\PriceMaths;
use Bitrix\Main;
use Bitrix\UI;
use Bitrix\Crm\Integration\DocumentGeneratorManager;
use Bitrix\Crm\Integration\DocumentGenerator\DataProvider\StoreDocumentArrival;
use Bitrix\Crm\Integration\DocumentGenerator\DataProvider\StoreDocumentStoreAdjustment;
use Bitrix\Crm\Integration\DocumentGenerator\DataProvider\StoreDocumentMoving;
use Bitrix\Crm\Integration\DocumentGenerator\DataProvider\StoreDocumentDeduct;
use Bitrix\Catalog\v2\Contractor;
use Bitrix\Crm\Settings\EntityEditSettings;
use Bitrix\Main\Config\Option;

if (!defined('B_PROLOG_INCLUDED') || B_PROLOG_INCLUDED !== true)
{
	die();
}

Main\Loader::includeModule('catalog');
Main\Loader::includeModule('currency');
Main\Loader::includeModule('sale');

class CatalogStoreDocumentDetailComponent extends CBitrixComponent implements Controllerable
{
	public const COLLECT_RIGHT_COLUMN_EVENT = 'DocumentCard:onCollectRightColumnContent';
	public const CONDUCT_FAILURE_AFTER_SAVE_EVENT = 'DocumentCard:onConductFailureAfterSave';

	/** @var null|int $documentId */
	private ?int $documentId;

	private ?string $documentType = null;
	/** @var array $document */
	private $document;
	/** @var AccessController */
	private $accessController;
	private bool $canSelectDocumentType = true;

	/** @var Contractor\Provider\IProvider|null */
	private ?Contractor\Provider\IProvider $contractorsProvider;

	public function __construct($component = null)
	{
		parent::__construct($component);

		$this->accessController = AccessController::getCurrent();
		$this->contractorsProvider = Contractor\Provider\Manager::getActiveProvider(
			Contractor\Provider\Manager::PROVIDER_STORE_DOCUMENT
		);
	}

	public function onPrepareComponentParams($arParams)
	{
		if (isset($arParams['DOCUMENT_ID']))
		{
			$this->documentId = (int)$arParams['DOCUMENT_ID'] ?: null;
		}
		if (isset($arParams['DOCUMENT_TYPE']))
		{
			$this->documentType = $arParams['DOCUMENT_TYPE'];

			if (
				$this->isNew()
				&& in_array($this->documentType, [StoreDocumentTable::TYPE_ARRIVAL, StoreDocumentTable::TYPE_STORE_ADJUSTMENT], true)
			)
			{
				$this->replaceDocumentTypeToAccessibleType();
			}
		}
		if (!isset($arParams['PATH_TO']))
		{
			$arParams['PATH_TO'] = [];
		}

		return parent::onPrepareComponentParams($arParams);
	}

	/**
	 * Replace selected type in params of component to accessible document type.
	 *
	 * Checks access only `modify` right, because the type change is relevant only at creation.
	 *
	 * @return void
	 */
	private function replaceDocumentTypeToAccessibleType(): void
	{
		$replaces = [
			StoreDocumentTable::TYPE_ARRIVAL => StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
			StoreDocumentTable::TYPE_STORE_ADJUSTMENT => StoreDocumentTable::TYPE_ARRIVAL,
		];

		foreach ($replaces as $baseType => $anotherType)
		{
			$can = $this->accessController->checkByValue(ActionDictionary::ACTION_STORE_DOCUMENT_MODIFY, $baseType);
			if (!$can)
			{
				$this->canSelectDocumentType = false;

				// change current type
				if ($this->documentType === $baseType)
				{
					$can = $this->accessController->checkByValue(ActionDictionary::ACTION_STORE_DOCUMENT_MODIFY, $anotherType);
					if ($can)
					{
						$this->documentType = $anotherType;
					}
				}
			}
		}
	}

	protected function listKeysSignedParameters()
	{
		return [
			'DOCUMENT_ID',
			'DOCUMENT_TYPE',
			'PATH_TO',
		];
	}

	public function executeComponent()
	{
		$this->checkParams();
		$this->loadDocument();
		if (!empty($this->arResult['ERROR_MESSAGES']))
		{
			$this->includeComponentTemplate();
			return;
		}
		$this->initializeDocumentFields();

		$this->arResult['INCLUDE_CRM_ENTITY_EDITOR'] = Contractor\Provider\Manager::isActiveProviderByModule(Contractor\Provider\Manager::PROVIDER_STORE_DOCUMENT, 'crm');
		$this->arResult['GUID'] = $this->arResult['FORM']['GUID'];
		$this->arResult['IS_MAIN_CARD_READ_ONLY'] = $this->arResult['FORM']['READ_ONLY'];
		$this->arResult['DOCUMENT_TYPE'] = $this->getDocumentType();
		$this->arResult['FOCUSED_TAB'] = $this->request->get('focusedTab');

		$this->setDropdownTypes();

		// region uf
		$tableClass = StoreDocumentTableManager::getTableClassByType($this->getDocumentType());
		$this->arResult['USER_FIELD_ENTITY_ID'] = $tableClass ? $tableClass::getUfId() : '';
		// endregion

		$this->getAdditionalEntityEditorActions();

		$this->collectRightColumnContent();

		$this->checkIfInventoryManagementIsDisabled();

		$this->checkIfInventoryManagementIsUsed();

		$this->arResult['CRM_DOCUMENT_BUTTON_CONFIG'] = $this->getDocumentButtonConfig();

		$this->arResult['INVENTORY_MANAGEMENT_SOURCE'] =
			InventoryManagementSourceBuilder::getInstance()->getInventoryManagementSource()
		;

		$this->arResult['IS_PRODUCT_BATCH_METHOD_SELECTED'] = State::isProductBatchMethodSelected();

		$this->includeComponentTemplate();
	}

	private function getDocumentButtonConfig(): ?array
	{
		$result = null;

		$documentType2ProviderMap = [
			StoreDocumentTable::TYPE_ARRIVAL => StoreDocumentArrival::class,
			StoreDocumentTable::TYPE_STORE_ADJUSTMENT => StoreDocumentStoreAdjustment::class,
			StoreDocumentTable::TYPE_MOVING => StoreDocumentMoving::class,
			StoreDocumentTable::TYPE_DEDUCT => StoreDocumentDeduct::class,
		];

		$isDocumentButtonAvailable = (
			Main\Loader::includeModule('crm')
			&& DocumentGeneratorManager::getInstance()->isDocumentButtonAvailable()
			&& isset($this->arResult['DOCUMENT']['ID'])
			&& (int)$this->arResult['DOCUMENT']['ID'] > 0
			&& isset($this->arResult['DOCUMENT']['DOC_TYPE'])
			&& isset($documentType2ProviderMap[$this->arResult['DOCUMENT']['DOC_TYPE']])
		);
		if ($isDocumentButtonAvailable)
		{
			$result = DocumentGeneratorManager::getInstance()->getDocumentButtonParameters(
				$documentType2ProviderMap[$this->arResult['DOCUMENT']['DOC_TYPE']],
				$this->arResult['DOCUMENT']['ID']
			);
			if (empty($result))
			{
				$result = null;
			}
		}

		return $result;
	}

	private function checkParams()
	{
		if (!$this->documentId && !$this->documentType)
		{
			$this->arResult['ERROR_MESSAGES'][] = Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_DOCUMENT_TYPE_NOT_SPECIFIED_ERROR');
		}
	}

	private function getDocumentType(): ?string
	{
		if ($this->documentType)
		{
			return $this->documentType;
		}

		$this->loadDocument();

		return $this->document['DOC_TYPE'] ?? null;
	}

	private function initializeDocumentFields(): void
	{
		$this->arResult['DOCUMENT'] = $this->document;

		$editorProvider = $this->getEditorProvider();
		$this->arResult['FORM'] = $editorProvider->getFields();
		$this->addPreloadedFields();
	}

	private function addPreloadedFields(): void
	{
		$preloadedDocumentFields = $this->arParams['PRELOADED_FIELDS']['DOCUMENT_FIELDS'] ?? null;
		if (!is_array($preloadedDocumentFields) || empty($preloadedDocumentFields))
		{
			return;
		}

		if (isset($preloadedDocumentFields['TOTAL'], $preloadedDocumentFields['CURRENCY']))
		{
			$total = $preloadedDocumentFields['TOTAL'];
			$currency = $preloadedDocumentFields['CURRENCY'];
			$preloadedDocumentFields['FORMATTED_TOTAL'] = CCurrencyLang::CurrencyFormat($total, $currency, false);
			$preloadedDocumentFields['FORMATTED_TOTAL_WITH_CURRENCY'] = CCurrencyLang::CurrencyFormat($total, $currency);
		}

		$this->arResult['FORM']['ENTITY_DATA'] = array_merge($this->arResult['FORM']['ENTITY_DATA'], $preloadedDocumentFields);
	}

	private function getEditorProvider(): StoreDocumentProvider
	{
		$createUfUrl = '';
		$tableClass = StoreDocumentTableManager::getTableClassByType($this->getDocumentType());
		if ($tableClass)
		{
			$url = new Uri($this->arParams['PATH_TO']['UF']);
			$url->addParams(['entityId' => $tableClass::getUfId()]);

			$createUfUrl = $url->getUri();
		}

		if ($this->document)
		{
			$provider = StoreDocumentProvider::createByArray($this->document);
		}
		else
		{
			$provider = StoreDocumentProvider::createByType($this->getDocumentType());
		}

		$provider->setCreateUfUrl($createUfUrl);

		return $provider;
	}

	private function loadDocument(): void
	{
		if (!$this->checkDocumentBaseRights())
		{
			if (Main\Loader::includeModule('crm'))
			{
				$this->arResult['ERROR_MESSAGES'][] = [
					'TITLE' => Loc::getMessage('CATALOG_STORE_DOCUMENT_ERR_ACCESS_DENIED'),
					'HELPER_CODE' => 15955386,
					'LESSON_ID' => 25010,
					'COURSE_ID' => 48,
				];
			}
			else
			{
				$this->arResult['ERROR_MESSAGES'][] = Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_VIEW_RIGHTS_ERROR');
			}

			return;
		}

		if (!$this->documentId)
		{
			return;
		}

		$documentType = StoreDocumentTable::getRow(['select' => ['DOC_TYPE'], 'filter' => ['=ID' => $this->documentId]]);
		if (!$documentType)
		{
			$this->arResult['ERROR_MESSAGES'][] = Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_DOCUMENT_NOT_FOUND_ERROR');

			return;
		}

		$documentType = $documentType['DOC_TYPE'];

		$tableClass = StoreDocumentTableManager::getTableClassByType($documentType) ?: StoreDocumentTable::class;
		$document = $tableClass::getRow([
			'select' => [
				'*',
				'UF_*',
				'CONTRACTOR_REF_' => 'CONTRACTOR',
			],
			'filter' => [
				'=ID' => $this->documentId,
			],
		]);
		if ($document)
		{
			$document = $this->fillDefaultDocumentFields($document);
			$this->document = $document;
			$this->documentType = $document['DOC_TYPE'];
		}
		else
		{
			$this->arResult['ERROR_MESSAGES'][] = Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_DOCUMENT_NOT_FOUND_ERROR');
		}

		if (!$this->checkDocumentReadRights())
		{
			$this->arResult['ERROR_MESSAGES'][] = Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_VIEW_RIGHTS_ERROR');
		}
	}

	private function fillDefaultDocumentFields($document)
	{
		$resultDocument = $document;
		if (!isset($resultDocument['TITLE']))
		{
			$resultDocument['TITLE'] = StoreDocumentTable::getTypeList(true)[$resultDocument['DOC_TYPE']];
		}

		return $resultDocument;
	}

	private function checkDocumentBaseRights(): bool
	{
		return
			$this->accessController->check(ActionDictionary::ACTION_CATALOG_READ)
			&& $this->accessController->check(ActionDictionary::ACTION_INVENTORY_MANAGEMENT_ACCESS)
			;
	}

	private function checkDocumentReadRights(): bool
	{
		$documentType = $this->getDocumentType();
		if ($documentType === null)
		{
			return false;
		}

		return
			$this->checkDocumentBaseRights()
			&& $this->accessController->checkByValue(
				ActionDictionary::ACTION_STORE_DOCUMENT_VIEW,
				$documentType
			)
		;
	}

	private function checkDocumentWriteRights(): bool
	{
		$documentType = $this->getDocumentType();
		if ($documentType === null)
		{
			return false;
		}

		return
			$this->checkDocumentBaseRights()
			&& $this->accessController->checkByValue(
				ActionDictionary::ACTION_STORE_DOCUMENT_MODIFY,
				$documentType
			)
		;
	}

	private function checkDocumentCardEditRights(): bool
	{
		return
			$this->checkDocumentBaseRights()
			&& AccessController::getCurrent()->check(ActionDictionary::ACTION_STORE_DOCUMENT_CARD_EDIT)
		;
	}

	private function checkDocumentConductRights(): bool
	{
		$documentType = $this->getDocumentType();
		if ($documentType === null)
		{
			return false;
		}

		return
			$this->checkDocumentBaseRights()
			&& $this->accessController->checkByValue(
				ActionDictionary::ACTION_STORE_DOCUMENT_CONDUCT,
				$documentType
			)
		;
	}

	private function checkDocumentCancelRights(): bool
	{
		$documentType = $this->getDocumentType();
		if ($documentType === null)
		{
			return false;
		}

		return
			$this->checkDocumentBaseRights()
			&& $this->accessController->checkByValue(
				ActionDictionary::ACTION_STORE_DOCUMENT_CANCEL,
				$documentType
			)
		;
	}

	private function checkEditPurchasePriceRights(): bool
	{
		return $this->accessController->check(ActionDictionary::ACTION_PRODUCT_PURCHASE_INFO_VIEW);
	}

	private function checkEditPriceRights(): bool
	{
		return $this->accessController->check(ActionDictionary::ACTION_PRICE_EDIT);
	}

	private function checkEditExtraPriceRights(): bool
	{
		return $this->accessController->check(ActionDictionary::ACTION_PRODUCT_PRICE_EXTRA_EDIT);
	}

	public function changeRequiredAction(string $documentType, string $fieldName, string $required): AjaxJson
	{
		$result = new Bitrix\Main\Result();
		if (
			!in_array(
				$documentType,
				[
					StoreDocumentTable::TYPE_ARRIVAL,
					StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
					StoreDocumentTable::TYPE_MOVING,
					StoreDocumentTable::TYPE_DEDUCT
				],
				true
			)
		)
		{
			$result->addError(
				new Main\Error(Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_DOC_TYPE_ERROR'))
			);

			return AjaxJson::createError($result->getErrorCollection());
		}
		$this->documentType = $documentType;

		$result = $this->validateRequestBeforeAction();
		if (!$result->isSuccess())
		{
			return AjaxJson::createError($result->getErrorCollection());
		}

		if (!$this->checkDocumentCardEditRights())
		{
			$result->addError(
				new Main\Error(Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_CARD_NO_WRITE_RIGHTS_ERROR'))
			);

			return AjaxJson::createError($result->getErrorCollection());
		}

		$result =
			$required === 'Y'
				? DocumentFieldsManager::addRequiredField($documentType, $fieldName)
				: DocumentFieldsManager::deleteRequiredField($documentType, $fieldName)
		;

		return $result->isSuccess() ? AjaxJson::createSuccess() : AjaxJson::createError($result->getErrorCollection());
	}

	public function saveAction($fields = []): array
	{
		$actionValidateResult = $this->validateRequestBeforeAction();
		if (!$actionValidateResult->isSuccess())
		{
			return [
				'ERROR' => implode('<br>', $actionValidateResult->getErrorMessages()),
			];
		}

		if (!$this->checkDocumentWriteRights())
		{
			return [
				'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_WRITE_RIGHTS_ERROR'),
			];
		}

		if (!$fields)
		{
			$fields = $this->request->get('data') ?: [];
		}

		$saveResult = $this->saveDocument($fields);

		if (!$saveResult->isSuccess())
		{
			$errorMessage = implode('<br>', $saveResult->getErrorMessages());
			return [
				'ERROR' => $errorMessage,
			];
		}

		$entityId = $saveResult->getData()['ENTITY_ID'];
		if ($entityId)
		{
			$result = [
				'ENTITY_ID' => $entityId,
			];

			if ($this->isNew())
			{
				$result['REDIRECT_URL'] = $this->getUrlToDocumentDetail($entityId);
			}
			else
			{
				$result['ENTITY_DATA'] = $this->getEntityDataForResponse();
			}

			return $result;
		}

		return [
			'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_SAVE_ERROR'),
		];
	}

	public function saveAndConductAction($fields = []): array
	{
		$actionValidateResult = $this->validateRequestBeforeAction();
		if (!$actionValidateResult->isSuccess())
		{
			return [
				'ERROR' => implode('<br>', $actionValidateResult->getErrorMessages()),
			];
		}

		if (!$this->checkDocumentWriteRights() || !$this->checkDocumentConductRights())
		{
			return [
				'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_WRITE_RIGHTS_ERROR'),
			];
		}

		if (!$fields)
		{
			$fields = $this->request->get('data') ?: [];
		}

		$saveResult = $this->saveDocument($fields);

		if (!$saveResult->isSuccess())
		{
			$errorMessage = implode('<br>', $saveResult->getErrorMessages());
			return [
				'ERROR' => $errorMessage,
			];
		}

		if (
			$this->getDocumentType() === StoreDocumentTable::TYPE_ARRIVAL
			|| $this->getDocumentType() ===	StoreDocumentTable::TYPE_STORE_ADJUSTMENT
		)
		{
			$decodedProducts = $this->decodeProducts($fields['DOCUMENT_PRODUCTS'] ?? null);
			if (is_array($decodedProducts))
			{
				$this->updateBarcodes($decodedProducts);
			}
		}

		$entityId = $saveResult->getData()['ENTITY_ID'];

		if ($entityId)
		{
			$closeSliderOnSave = true;

			$userId = Main\Engine\CurrentUser::get()->getId();

			global $APPLICATION;
			$APPLICATION->ResetException();

			$isConducted = CCatalogDocs::conductDocument($entityId, $userId);

			if (!$isConducted)
			{
				if ($this->isNew())
				{
					$closeSliderOnSave = false;
					$eventParameters = [
						'DOCUMENT_ID' => $entityId,
						'USER_ID' => $userId,
						'ERROR_MESSAGE' =>
							$APPLICATION->GetException()
								? $APPLICATION->GetException()->GetString()
								: Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_CONDUCT_ERROR')
						,
					];

					$event = new Main\Event(
						'catalog',
						self::CONDUCT_FAILURE_AFTER_SAVE_EVENT,
						$eventParameters
					);
					$event->send();
				}
				else
				{
					$errorMessage = $APPLICATION->GetException() ? $APPLICATION->GetException()->GetString() : '';
					if ($errorMessage)
					{
						return [
							'ERROR' => $errorMessage,
						];
					}

					return [
						'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_CONDUCT_ERROR'),
					];
				}
			}

			return [
				'ENTITY_ID' => $entityId,
				'REDIRECT_URL' => $this->getUrlToDocumentDetail($entityId, $closeSliderOnSave),
				'EVENT_PARAMS' => [
					'showNotificationOnClose' => $isConducted ? 'Y' : 'N',
				],
			];
		}

		return [
			'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_SAVE_ERROR'),
		];
	}

	public function conductAction(): array
	{
		$actionValidateResult = $this->validateRequestBeforeAction();
		if (!$actionValidateResult->isSuccess())
		{
			return [
				'ERROR' => implode('<br>', $actionValidateResult->getErrorMessages()),
			];
		}

		if (!$this->checkDocumentConductRights())
		{
			return [
				'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_WRITE_RIGHTS_ERROR'),
			];
		}

		$userId = Main\Engine\CurrentUser::get()->getId();

		global $APPLICATION;
		$APPLICATION->ResetException();

		if (
			$this->getDocumentType() === StoreDocumentTable::TYPE_ARRIVAL
			|| $this->getDocumentType() ===	StoreDocumentTable::TYPE_STORE_ADJUSTMENT
		)
		{
			$documentProducts = StoreDocumentBarcodeTable::getList([
				'filter' => [
					'=DOCUMENT_ELEMENT.DOC_ID' => $this->documentId,
				],
				'select' => ['BARCODE', 'SKU_ID' => 'DOCUMENT_ELEMENT.ELEMENT_ID']
			])->fetchAll();


			$this->updateBarcodes($documentProducts);
		}

		$isConducted = CCatalogDocs::conductDocument($this->documentId, $userId);

		if (!$isConducted)
		{
			$errorMessage = $APPLICATION->GetException() ? $APPLICATION->GetException()->GetString() : '';
			if ($errorMessage)
			{
				return [
					'ERROR' => $errorMessage,
				];
			}

			return [
				'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_CONDUCT_ERROR'),
			];
		}

		return [
			'REDIRECT_URL' => $this->getUrlToDocumentDetail($this->documentId, true),
			'EVENT_PARAMS' => [
				'showNotificationOnClose' => 'Y',
			],
		];
	}

	public function cancelConductAction(): array
	{
		$actionValidateResult = $this->validateRequestBeforeAction();
		if (!$actionValidateResult->isSuccess())
		{
			return [
				'ERROR' => implode('<br>', $actionValidateResult->getErrorMessages()),
			];
		}

		if (!$this->checkDocumentCancelRights())
		{
			return [
				'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_WRITE_RIGHTS_ERROR'),
			];
		}

		global $APPLICATION;
		$APPLICATION->ResetException();

		$isCancelled = CCatalogDocs::cancellationDocument($this->documentId);

		if (!$isCancelled && $APPLICATION->GetException())
		{
			return [
				'ERROR' => $APPLICATION->GetException()->GetString(),
			];
		}

		if ($isCancelled)
		{
			return [
				'REDIRECT_URL' => $this->getUrlToDocumentDetail($this->documentId),
			];
		}

		return [
			'ERROR' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_CANCEL_ERROR'),
		];
	}

	private function validateRequestBeforeAction(): \Bitrix\Main\Result
	{
		$result = new Bitrix\Main\Result();

		if (!Feature::isInventoryManagementEnabled())
		{
			$result->addError(new Main\Error(Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_NO_INVENTORY_MANAGEMENT_ENABLED_ERROR')));
		}

		return $result;
	}

	/**
	 * @param $fields
	 * @return Main\Result
	 */
	private function saveDocument($fields): Main\Result
	{
		$result = new Main\Result();
		$contractorProviderSaveResult = null;
		if ($this->contractorsProvider)
		{
			$contractorProviderAccessResult = $this->contractorsProvider::checkAccessRights(
				(int)$this->documentId,
				$fields,
			);
			if (!$contractorProviderAccessResult->isSuccess())
			{
				return $result->addErrors($contractorProviderAccessResult->getErrors());
			}

			$contractorProviderSaveResult = $this->contractorsProvider::onBeforeDocumentSave($fields);
		}

		$entityId = null;
		if ($this->isNew())
		{
			$addDocumentResult = $this->addDocument($fields);
			if ($addDocumentResult->isSuccess())
			{
				$entityId = (int)$addDocumentResult->getData()['ENTITY_ID'];
			}
			else
			{
				$result->addErrors($addDocumentResult->getErrors());
			}
		}
		else
		{
			$updateDocumentResult = $this->updateDocument($fields);
			if ($updateDocumentResult->isSuccess())
			{
				$entityId = (int)$updateDocumentResult->getData()['ENTITY_ID'];
			}
			else
			{
				$result->addErrors($updateDocumentResult->getErrors());
			}
		}

		if (!$result->isSuccess())
		{
			if ($this->contractorsProvider)
			{
				$this->contractorsProvider::onAfterDocumentSaveFailure($entityId, $contractorProviderSaveResult);
			}

			return $result;
		}

		if ($this->contractorsProvider)
		{
			$this->contractorsProvider::onAfterDocumentSaveSuccess(
				$entityId,
				$contractorProviderSaveResult,
				[
					'entityEditorSettings' => new EntityEditSettings($this->getEditorProvider()->getConfigId()),
				]
			);
		}

		if ($result->isSuccess())
		{
			$result->setData(['ENTITY_ID' => $entityId]);
		}

		return $result;
	}

	private function addDocument($fields): Main\Result
	{
		global $APPLICATION;
		$result = new Main\Result();

		$prepareFieldsResult = $this->prepareFieldsToAdd($fields);
		if (!$prepareFieldsResult->isSuccess())
		{
			$result->addErrors($prepareFieldsResult->getErrors());
			return $result;
		}
		$fieldsToSave = $prepareFieldsResult->getData()['PREPARED_FIELDS'];

		$APPLICATION->ResetException();
		$entityId = CCatalogDocs::add($fieldsToSave);
		if ($APPLICATION->GetException())
		{
			$result->addError(new Main\Error($APPLICATION->GetException()->GetString()));
			return $result;
		}

		$result->setData(['ENTITY_ID' => $entityId]);
		return $result;
	}

	private function updateDocument($fields): Main\Result
	{
		global $APPLICATION;
		$result = new Main\Result();

		$prepareFieldsResult = $this->prepareFieldsToUpdate($fields);
		if (!$prepareFieldsResult->isSuccess())
		{
			$result->addErrors($prepareFieldsResult->getErrors());
			return $result;
		}
		$fieldsToUpdate = $prepareFieldsResult->getData()['PREPARED_FIELDS'];

		$APPLICATION->ResetException();
		CCatalogDocs::update($this->documentId, $fieldsToUpdate['GENERAL']);
		if ($APPLICATION->GetException())
		{
			$result->addError(new Main\Error($APPLICATION->GetException()->GetString()));
			return $result;
		}

		if (array_key_exists('ELEMENT', $fieldsToUpdate))
		{
			$this->updateElements($fieldsToUpdate['ELEMENT']);
		}

		$result->setData(['ENTITY_ID' => $this->documentId]);

		return $result;
	}

	private function updateBarcodes(array $fields): void
	{
		$barcodeElementMap = [];
		foreach ($fields as $field)
		{
			$barcode = null;
			if (isset($field['DOC_BARCODE']) && !empty($field['DOC_BARCODE']))
			{
				$barcode = $field['DOC_BARCODE'];
			}
			elseif (isset($field['BARCODE']) && !empty($field['BARCODE']))
			{
				$barcode = $field['BARCODE'];
			}
			$skuId = (int)($field['SKU_ID']);
			if ($skuId > 0 && $barcode)
			{
				$barcodeElementMap[$barcode] = $skuId;
			}
		}

		if (empty($barcodeElementMap))
		{
			return;
		}

		$existedBarcodeRaw = Catalog\StoreBarcodeTable::getList([
			'filter' => ['=BARCODE' => array_keys($barcodeElementMap)],
			'select' => ['BARCODE']
		]);

		while ($barcodeData = $existedBarcodeRaw->fetch())
		{
			$barcode = $barcodeData['BARCODE'];
			if (isset($barcodeElementMap[$barcode]))
			{
				unset($barcodeElementMap[$barcode]);
			}
		}

		foreach ($barcodeElementMap as $barcode => $skuId)
		{
			CCatalogStoreBarCode::add([
				'PRODUCT_ID' => $skuId,
				'BARCODE' => $barcode,
				'STORE_ID' => 0,
				'CREATED_BY' => Main\Engine\CurrentUser::get()->getId(),
				'MODIFIED_BY' => Main\Engine\CurrentUser::get()->getId(),
			]);
		}
	}

	private function prepareFieldsToAdd(array $fields): Main\Result
	{
		$result = new Main\Result();
		$preparedFields = [];

		foreach ($fields as $fieldName => $value)
		{
			$prepareFieldResult = $this->prepareFieldByName($fieldName, $value);
			if ($prepareFieldResult->isSuccess() && isset($prepareFieldResult->getData()['PREPARED_VALUE']))
			{
				$preparedFields[$fieldName] = $prepareFieldResult->getData()['PREPARED_VALUE'];
			}
			else
			{
				$result->addErrors($prepareFieldResult->getErrors());
			}
		}

		if (!$result->isSuccess())
		{
			return $result;
		}

		if (isset($preparedFields['TOTAL']))
		{
			unset($preparedFields['TOTAL']);
		}

		if (empty($preparedFields['CURRENCY']))
		{
			if ($this->isNew() && isset($preparedFields['TOTAL_WITH_CURRENCY']))
			{
				$preparedFields['CURRENCY'] = $preparedFields['TOTAL_WITH_CURRENCY'];
			}
			else
			{
				$preparedFields['CURRENCY'] = \Bitrix\Currency\CurrencyManager::getBaseCurrency();
			}
		}
		$preparedFields['DOC_TYPE'] = $this->getDocumentType();
		$preparedFields['SITE_ID'] = $this->getSiteId();
		$preparedFields['CREATED_BY'] = Main\Engine\CurrentUser::get()->getId();
		$preparedFields['MODIFIED_BY'] = Main\Engine\CurrentUser::get()->getId();

		if (array_key_exists('DOCUMENT_PRODUCTS', $fields))
		{
			$products = $this->decodeProducts($fields['DOCUMENT_PRODUCTS']);
			$element = [];
			$prepareElementResult = $this->prepareElementField($products);
			if ($prepareElementResult->isSuccess())
			{
				$element = $prepareElementResult->getData()['ELEMENTS'];
			}
			else
			{
				$result->addErrors($prepareElementResult->getErrors());
				return $result;
			}
			$preparedFields['ELEMENT'] = $element;
			$preparedFields['TOTAL'] = $this->calculateDocumentTotalFromElement($element);
		}

		$userFieldValues = $this->extractUserFieldValues($preparedFields);

		$validFieldNames = array_keys(StoreDocumentTable::getEntity()->getFields());
		$validFieldNames = array_merge($validFieldNames, ['ELEMENT', 'DOCUMENT_FILES']);
		$preparedFields = array_intersect_key($preparedFields, array_flip($validFieldNames));

		$preparedFields = $this->prepareFilesToUpdate($preparedFields);

		$preparedFields += $userFieldValues;

		$result->setData(['PREPARED_FIELDS' => $preparedFields]);

		return $result;
	}

	private function prepareFieldsToUpdate(array $fields): Main\Result
	{
		$result = new Main\Result();
		$preparedFields = [];
		$generalFields = [];

		foreach ($fields as $fieldName => $value)
		{
			$prepareFieldResult = $this->prepareFieldByName($fieldName, $value);
			if ($prepareFieldResult->isSuccess() && isset($prepareFieldResult->getData()['PREPARED_VALUE']))
			{
				$generalFields[$fieldName] = $prepareFieldResult->getData()['PREPARED_VALUE'];
			}
			else
			{
				$result->addErrors($prepareFieldResult->getErrors());
			}
		}

		if (!$result->isSuccess())
		{
			return $result;
		}

		if (isset($generalFields['TOTAL']))
		{
			unset($generalFields['TOTAL']);
		}

		$generalFields['MODIFIED_BY'] = Main\Engine\CurrentUser::get()->getId();

		if (array_key_exists('DOCUMENT_PRODUCTS', $fields))
		{
			$products = $this->decodeProducts($fields['DOCUMENT_PRODUCTS']);
			$element = [];
			$prepareElementResult = $this->prepareElementField($products);
			if ($prepareElementResult->isSuccess())
			{
				$element = $prepareElementResult->getData()['ELEMENTS'];
			}
			else
			{
				$result->addErrors($prepareElementResult->getErrors());
				return $result;
			}
			$preparedFields['ELEMENT'] = $element;
			unset($generalFields['DOCUMENT_PRODUCTS']);
			$generalFields['TOTAL'] = $this->calculateDocumentTotalFromElement($element);
		}

		$generalFields = $this->prepareFilesToUpdate($generalFields);
		$userFieldValues = $this->extractUserFieldValues($generalFields);

		$validFieldNames = array_keys(StoreDocumentTable::getEntity()->getFields());
		$validFieldNames = array_merge($validFieldNames, ['ELEMENT', 'DOCUMENT_FILES']);
		$generalFields = array_intersect_key($generalFields, array_flip($validFieldNames));

		$generalFields += $userFieldValues;

		$preparedFields['GENERAL'] = $generalFields;

		$result->setData(['PREPARED_FIELDS' => $preparedFields]);

		return $result;
	}

	private function extractUserFieldValues(array $fields): array
	{
		global $USER_FIELD_MANAGER;

		$tableClass = StoreDocumentTableManager::getTableClassByType($this->getDocumentType());

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

		$userFieldValues = [];
		$USER_FIELD_MANAGER->EditFormAddFields(
			$tableClass::getUfId(),
			$userFieldValues,
			[ 'FORM' => $fields ]
		);

		return $userFieldValues;
	}

	private function prepareFilesToUpdate(array $fields): array
	{
		$filesExists = isset($fields['DOCUMENT_FILES']) && is_array($fields['DOCUMENT_FILES']);
		$filesDelete = isset($fields['DOCUMENT_FILES_del']) && is_array($fields['DOCUMENT_FILES_del']);
		if ($filesExists || $filesDelete)
		{
			$result = [];
			if ($filesExists)
			{
				$fileList = $fields['DOCUMENT_FILES'];
				Main\Type\Collection::normalizeArrayValuesByInt($fileList, false);
				$fileList = \Bitrix\Main\UI\FileInputUtility::instance()->checkFiles(
					'document_files_uploader',
					$fileList
				);
				foreach ($fileList as $id)
				{
					$result[$id] = (string)$id;
				}
			}

			if ($filesDelete)
			{
				$deleteList = $fields['DOCUMENT_FILES_del'];
				Main\Type\Collection::normalizeArrayValuesByInt($deleteList, false);
				foreach ($deleteList as $id)
				{
					$result[$id] = 'delete' . $id;
				}
			}

			$fields['DOCUMENT_FILES'] = array_values($result);
			unset($result);
		}

		return $fields;
	}

	private function prepareFieldByName($fieldName, $value): Main\Result
	{
		// $result = $value;
		$result = new Main\Result();
		$preparedValue = $value;
		if (!empty($preparedValue))
		{
			switch ($fieldName)
			{
				case 'DATE_DOCUMENT':
				case 'ITEMS_ORDER_DATE':
				case 'ITEMS_RECEIVED_DATE':
					if (Main\Type\DateTime::isCorrect($value))
					{
						$preparedValue = Main\Type\DateTime::createFromUserTime($value);
					}
					else
					{
						$result->addError(
							new Main\Error(
								Loc::getMessage(
									'CATALOG_STORE_DOCUMENT_DETAIL_INCORRECT_VALUE',
									['#FIELD_NAME#' => StoreDocumentProvider::getFieldTitle($fieldName)]
								)
							)
						);
						return $result;
					}
			}
		}

		$result->setData(['PREPARED_VALUE' => $preparedValue]);

		return $result;
	}

	private function prepareBarcodes($products): array
	{
		$productIds = array_unique(array_column($products, 'SKU_ID'));
		$existingBarcodes = [];
		$existingBarcodesRes = \Bitrix\Catalog\StoreBarcodeTable::getList([
			'select' => ['BARCODE', 'PRODUCT_ID'],
			'filter' => ['PRODUCT_ID' => $productIds],
		]);
		while ($existingBarcode = $existingBarcodesRes->fetch())
		{
			$productId = $existingBarcode['PRODUCT_ID'];
			if (!isset($existingBarcodes[$productId]))
			{
				$existingBarcodes[$productId] = [];
			}
			$existingBarcodes[$productId][] = $existingBarcode['BARCODE'];
		}

		$result = [];

		foreach ($products as $product)
		{
			$productId = $product['SKU_ID'];
			$newBarcode = $product['BARCODE'];
			if (is_array($existingBarcodes[$productId]) && in_array($newBarcode, $existingBarcodes[$productId]))
			{
				continue;
			}

			if (!isset($result[$productId]))
			{
				$result[$productId] = [];
			}
			$result[$productId][] = $newBarcode;
		}

		return $result;
	}

	public function configureActions()
	{
		return [];
	}

	private function isNew(): bool
	{
		return $this->documentId === null;
	}

	private function setDropdownTypes()
	{
		$dropdownRequiringTypes = [
			StoreDocumentTable::TYPE_ARRIVAL,
			StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
		];
		if (!$this->canSelectDocumentType || !in_array($this->getDocumentType(), $dropdownRequiringTypes))
		{
			$this->arResult['DROPDOWN_TYPES'] = [];
			return;
		}

		switch ($this->getDocumentType())
		{
			case StoreDocumentTable::TYPE_ARRIVAL:
			case StoreDocumentTable::TYPE_STORE_ADJUSTMENT:
				$this->arResult['DROPDOWN_TYPES'] = [
					StoreDocumentTable::TYPE_ARRIVAL,
					StoreDocumentTable::TYPE_STORE_ADJUSTMENT,
				];
				return;
			default:
				$this->arResult['DROPDOWN_TYPES'] = [];
				return;
		}
	}

	/**
	 * Products of document.
	 *
	 * @return array
	 */
	private function getDocumentProducts(): array
	{
		if (!$this->documentId)
		{
			return [];
		}

		$result = [];

		$rows = StoreDocumentElementTable::getList([
			'filter' => [
				'=DOC_ID' => $this->documentId,
			],
		]);
		foreach ($rows as $row)
		{
			$id = (int)$row['ID'];
			$result[$id] = $row;
		}

		return $result;
	}

	/**
	 * Checks whether the fields of the element have changed.
	 *
	 * @param array $old
	 * @param array $new
	 *
	 * @return bool
	 */
	private function isChangedElement(array $old, array $new): bool
	{
		$checkedFields = [
			'STORE_FROM' => 'intval',
			'STORE_TO' => 'intval',
			'ELEMENT_ID' => 'intval',
			'AMOUNT' => 'floatval',
			'PURCHASING_PRICE' => 'floatval',
			'BASE_PRICE' => 'floatval',
			'BASE_PRICE_EXTRA' => 'floatval',
			'BASE_PRICE_EXTRA_RATE' => 'strval',
			'COMMENT' => 'strval',
		];

		foreach ($checkedFields as $name => $typeCallback)
		{
			$oldValue =  call_user_func($typeCallback, $old[$name] ?? null);
			$newValue =  call_user_func($typeCallback, $new[$name] ?? null);

			if ($oldValue !== $newValue)
			{
				return true;
			}
		}

		return false;
	}

	private function decodeProducts($encodedProducts)
	{
		if ($encodedProducts === null || $encodedProducts === '')
		{
			return null;
		}

		return CUtil::JsObjectToPhp($encodedProducts);
	}

	private function prepareElementField($products): Main\Result
	{
		$result = new Main\Result();

		$elements = [];
		$documentBarcodesMap = [];
		$existElements = $this->getDocumentProducts();

		if (!isset($products))
		{
			$products = [];
		}

		$hasEditPurchasePriceRights = $this->checkEditPurchasePriceRights();
		$hasEditPriceRights = $this->checkEditPriceRights();
		$hasEditExtraPriceRights = $this->checkEditExtraPriceRights();

		foreach ($products as $product)
		{
			if (!$product['SKU_ID'])
			{
				$result->addError(new Main\Error(Loc::getMessage('CATALOG_STORE_DOCUMENT_NO_PRODUCT')));

				return $result;
			}

			$existElement = $existElements[$product['ID']] ?? null;
			$skuEntity = null;
			if (!$hasEditPurchasePriceRights)
			{
				if ($existElement)
				{
					unset($product['PURCHASING_PRICE']);
				}
				else
				{
					$skuEntity = $skuEntity ?? ServiceContainer::getRepositoryFacade()->loadVariation((int)$product['SKU_ID']);
					$product['PURCHASING_PRICE'] = $skuEntity ? $skuEntity->getField('PURCHASING_PRICE') : 0;
				}
			}
			if (!$hasEditPriceRights)
			{
				if ($existElement)
				{
					unset($product['BASE_PRICE']);
				}
				else
				{
					$skuEntity = $skuEntity ?? ServiceContainer::getRepositoryFacade()->loadVariation((int)$product['SKU_ID']);
					$basePriceEntity = $skuEntity ? $skuEntity->getPriceCollection()->findBasePrice() : null;
					$fields['BASE_PRICE'] = $basePriceEntity ? $basePriceEntity->getPrice() : null;
				}
			}
			if (!$hasEditExtraPriceRights)
			{
				if ($existElement)
				{
					unset($product['BASE_PRICE_EXTRA'], $product['BASE_PRICE_EXTRA_RATE']);
				}
				else
				{
					$product['BASE_PRICE_EXTRA'] = 0;
					$product['BASE_PRICE_EXTRA_RATE'] = StoreDocumentElementTable::EXTRA_RATE_PERCENTAGE;
				}
			}

			$elementFields = [
				'ID' => is_numeric($product['ID']) ? $product['ID'] : null,
				'AMOUNT' => $product['AMOUNT'] ?? null,
				'ELEMENT_ID' => $product['SKU_ID'],
				'PURCHASING_PRICE' => $product['PURCHASING_PRICE'],
				'BASE_PRICE' => $product['BASE_PRICE'],
				'BASE_PRICE_EXTRA' => $product['BASE_PRICE_EXTRA'] ?? null,
				'BASE_PRICE_EXTRA_RATE' => $product['BASE_PRICE_EXTRA_RATE'],
				'COMMENT' => (string)($product['COMMENT'] ?? ''),
			];

			if (isset($product['DOC_BARCODE']) && !empty($product['DOC_BARCODE']))
			{
				$elementFields['BARCODE'] = [
					$product['DOC_BARCODE'],
				];
				$documentBarcodesMap[] = [
					'SKU_ID' => (int)$product['SKU_ID'],
					'BARCODE' => $product['DOC_BARCODE'],
				];
			}
			elseif (isset($product['BARCODE']) && !empty($product['BARCODE']))
			{
				$elementFields['BARCODE'] = [
					$product['BARCODE'],
				];
				$documentBarcodesMap[] = [
					'SKU_ID' => (int)$product['SKU_ID'],
					'BARCODE' => $product['BARCODE'],
				];
			}

			if (!$this->isNew())
			{
				$elementFields['DOC_ID'] = $this->documentId;
			}

			$elementFields['STORE_TO'] = null;
			$elementFields['STORE_FROM'] = null;
			switch ($this->getDocumentType())
			{
				case StoreDocumentTable::TYPE_ARRIVAL:
				case StoreDocumentTable::TYPE_STORE_ADJUSTMENT:
					$elementFields['STORE_TO'] = $product['STORE_TO'];
					break;
				case StoreDocumentTable::TYPE_MOVING:
					$elementFields['STORE_FROM'] = $product['STORE_FROM'];
					$elementFields['STORE_TO'] = $product['STORE_TO'];
					break;
				case StoreDocumentTable::TYPE_DEDUCT:
					$elementFields['STORE_FROM'] = $product['STORE_FROM'];
					break;
			}

			if (
				!$existElement
				|| $this->isChangedElement($existElement, $elementFields)
			)
			{
				$can = $this->accessController->check(
					ActionDictionary::ACTION_STORE_VIEW,
					StoreDocumentElement::createFromArray($elementFields)
				);
				if (!$can)
				{
					$message = Loc::getMessage('CATALOG_STORE_DOCUMENT_ELEMENT_STORE_ACCESS_DENIED', [
						'#PRODUCT_NAME#' => $product['NAME'] ?? $product['ELEMENT_ID'],
					]);
					$result->addError(
						new Main\Error($message)
					);
					continue;
				}
			}

			$elements[] = $elementFields;
		}

		if (!$result->isSuccess())
		{
			return $result;
		}

		$barcodeFilter = array_unique(array_column($documentBarcodesMap, 'BARCODE'));
		if ($barcodeFilter)
		{
			$productBarcodes = [];
			$barcodeProductRaw = \Bitrix\Catalog\StoreBarcodeTable::getList([
				'filter' => ['=BARCODE' => $barcodeFilter],
				'select' => ['PRODUCT_ID', 'BARCODE'],
			]);

			while ($productBarcode = $barcodeProductRaw->fetch())
			{
				$productBarcodes[$productBarcode['BARCODE']] = (int)$productBarcode['PRODUCT_ID'];
			}

			$existedBarcodes = [];
			foreach ($documentBarcodesMap as $documentBarcode)
			{
				$barcode = $documentBarcode['BARCODE'];
				if (
					!empty($barcode)
					&& isset($productBarcodes[$barcode])
					&& $productBarcodes[$barcode] > 0
					&& $documentBarcode['SKU_ID'] !== $productBarcodes[$barcode]
				)
				{
					$existedBarcodes[] = $barcode;
				}
			}

			$existedBarcodes = array_unique($existedBarcodes);
			foreach ($existedBarcodes as $existedBarcode)
			{
				$result->addError(
					new Main\Error(
						Loc::getMessage(
							'CATALOG_STORE_DOCUMENT_BARCODE_EXIST_ERROR',
							['#BARCODE#' => htmlspecialcharsbx($existedBarcode)]
						)
					)
				);
			}
		}

		$result->setData(['ELEMENTS' => $elements]);

		return $result;
	}

	private function calculateDocumentTotalFromElement(array $element): float
	{
		$result = 0.0;

		$priceType = 'PURCHASING_PRICE';
		foreach ($element as $product)
		{
			$result += PriceMaths::roundPrecision((float)$product[$priceType] * (float)$product['AMOUNT']);
		}

		return PriceMaths::roundPrecision($result);
	}

	private function getUrlToDocumentDetail($documentId, $addCloseOnSaveParam = false): string
	{
		$pathToDocumentDetailTemplate = $this->arParams['PATH_TO']['DOCUMENT'] ?? '';
		if ($pathToDocumentDetailTemplate === '')
		{
			return $pathToDocumentDetailTemplate;
		}

		$pathToDocumentDetail = str_replace('#DOCUMENT_ID#', $documentId, $pathToDocumentDetailTemplate);

		if ($addCloseOnSaveParam)
		{
			$pathToDocumentDetail .= '?closeOnSave=Y';
		}

		return
			InventoryManagementSourceBuilder::getInstance()->addInventoryManagementSourceParam($pathToDocumentDetail)
			;
	}

	private function clearElementsForDocument(array $elementsToUpdate)
	{
		$elementsToUpdateIds = array_map(static fn($elementToUpdate) => (int)$elementToUpdate['ID'], $elementsToUpdate);

		$elements = \Bitrix\Catalog\StoreDocumentElementTable::getList([
			'select' => ['ID'],
			'filter' => ['DOC_ID' => $this->documentId]
		])->fetchAll();
		foreach ($elements as $element)
		{
			$barcodesDb = StoreDocumentBarcodeTable::getList(['select' => ['ID'], 'filter' => ['DOC_ELEMENT_ID' => $element['ID']]]);
			while ($barcode = $barcodesDb->fetch())
			{
				CCatalogStoreDocsBarcode::delete($barcode['ID']);
			}

			if (!in_array((int)$element['ID'], $elementsToUpdateIds, true))
			{
				CCatalogStoreDocsElement::delete((int)$element['ID']);
			}
		}
	}

	private function updateElements($elementsToUpdate)
	{
		$this->clearElementsForDocument($elementsToUpdate);

		foreach ($elementsToUpdate as $element)
		{
			$docElementId = $element['ID'];
			unset($element['ID']);
			if ($docElementId)
			{
				unset($element['ID']);
				CCatalogStoreDocsElement::update($docElementId, $element);
			}
			else
			{
				$docElementId = CCatalogStoreDocsElement::add($element);
			}

			if (!empty($element['BARCODE']))
			{
				$this->updateBarcodesForDocsElement($docElementId, $element['DOC_ID'], $element['BARCODE']);
			}
		}
	}

	private function updateBarcodesForDocsElement($docElementId, $docId, $barcodes)
	{
		foreach($barcodes as $barcode)
		{
			CCatalogStoreDocsBarcode::add(['BARCODE' => $barcode, 'DOC_ID' => $docId, 'DOC_ELEMENT_ID' => $docElementId]);
		}
	}

	private function collectRightColumnContent()
	{
		$eventParameters = [
			'DOCUMENT_ID' => $this->documentId,
			'DOCUMENT_TYPE' => $this->getDocumentType(),
			'DOCUMENT_FIELDS' => $this->document,
		];

		$event = new Main\Event(
			'catalog',
			self::COLLECT_RIGHT_COLUMN_EVENT,
			$eventParameters
		);
		$event->send();

		if ($event->getResults())
		{
			$this->arResult['RIGHT_COLUMN'] = [];
			foreach ($event->getResults() as $eventResult)
			{
				$componentInfo = $eventResult->getParameters();
				$this->arResult['RIGHT_COLUMN']['COMPONENT_NAME'] = $componentInfo['COMPONENT_NAME'];
				$this->arResult['RIGHT_COLUMN']['COMPONENT_TEMPLATE'] = $componentInfo['COMPONENT_TEMPLATE'];
				$this->arResult['RIGHT_COLUMN']['COMPONENT_PARAMS'] = $componentInfo['COMPONENT_PARAMS'];

				break;
			}
		}
	}

	private function getEntityDataForResponse(): array
	{
		$this->loadDocument();
		$entityData = $this->getEditorProvider()->getEntityData();

		foreach ($entityData as $key => $field)
		{
			if (is_null($field))
			{
				unset($entityData[$key]);
				continue;
			}

			if ($field instanceof Main\Type\Date)
			{
				$entityData[$key] = $field->toString();
			}
		}

		return $entityData;
	}

	private function checkIfInventoryManagementIsUsed()
	{
		$this->arResult['IS_CONDUCT_LOCKED'] = !State::isUsedInventoryManagement();
		if ($this->arResult['IS_CONDUCT_LOCKED'])
		{
			$sliderPath = \CComponentEngine::makeComponentPath('bitrix:catalog.store.enablewizard');
			$sliderPath = getLocalPath('components' . $sliderPath . '/slider.php');
			$this->arResult['MASTER_SLIDER_URL'] = $sliderPath;
		}
		else
		{
			$this->arResult['MASTER_SLIDER_URL'] = null;
		}
	}

	private function checkIfInventoryManagementIsDisabled(): void
	{
		$this->arResult['IS_INVENTORY_MANAGEMENT_DISABLED'] = !Feature::isInventoryManagementEnabled();
		if ($this->arResult['IS_INVENTORY_MANAGEMENT_DISABLED'])
		{
			$this->arResult['INVENTORY_MANAGEMENT_FEATURE_SLIDER_CODE'] = Feature::getInventoryManagementHelpLink()['FEATURE_CODE'] ?? null;
		}
		else
		{
			$this->arResult['INVENTORY_MANAGEMENT_FEATURE_SLIDER_CODE'] = null;
		}
	}

	public function sendAnalyticsAction()
	{
		return null;
	}

	private function getAdditionalEntityEditorActions()
	{
		$additionalActions = [];
		$customToolPanelButtons = [];

		if ($this->checkDocumentConductRights())
		{
			if ($this->checkDocumentWriteRights())
			{
				$additionalActions[] = [
					'ID' => 'SAVE_AND_CONDUCT',
					'ACTION' => 'saveAndConduct',
					'ACTION_TYPE' => UI\EntityEditor\Action::ACTION_TYPE_SAVE,
				];

				$customToolPanelButtons[] = [
					'ID' => 'SAVE_AND_CONDUCT',
					'TEXT' => Loc::getMessage('CATALOG_STORE_DOCUMENT_SAVE_AND_CONDUCT_BUTTON'),
					'ACTION_ID' => 'SAVE_AND_CONDUCT',
					'CLASS' => 'ui-btn-light-border',
				];
			}

			$additionalActions[] = [
				'ID' => 'CONDUCT',
				'ACTION' => 'conduct',
				'ACTION_TYPE' => UI\EntityEditor\Action::ACTION_TYPE_DIRECT,
			];

			$customToolPanelButtons[] = [
				'ID' => 'CONDUCT',
				'TEXT' => Loc::getMessage('CATALOG_STORE_DOCUMENT_CONDUCT_BUTTON'),
				'ACTION_ID' => 'CONDUCT',
				'CLASS' => 'ui-btn-light-border',
			];
		}

		if ($this->checkDocumentCancelRights())
		{
			$additionalActions[] = [
				'ID' => 'CANCEL_CONDUCT',
				'ACTION' => 'cancelConduct',
				'ACTION_TYPE' => UI\EntityEditor\Action::ACTION_TYPE_DIRECT,
			];

			$customToolPanelButtons[] = [
				'ID' => 'CANCEL_CONDUCT',
				'TEXT' => Loc::getMessage('CATALOG_STORE_DOCUMENT_CANCEL_CONDUCT_BUTTON'),
				'ACTION_ID' => 'CANCEL_CONDUCT',
				'CLASS' => 'ui-btn-light-border',
			];
		}

		$this->arResult['FORM']['ENABLE_TOOL_PANEL'] = !empty($customToolPanelButtons) || $this->checkDocumentWriteRights();
		$this->arResult['FORM']['ADDITIONAL_ACTIONS'] = $additionalActions;
		$this->arResult['FORM']['CUSTOM_TOOL_PANEL_BUTTONS'] = $customToolPanelButtons;

		$viewButtons = $this->document && $this->document['STATUS'] === 'Y' ? ['CANCEL_CONDUCT'] : ['CONDUCT'];

		$this->arResult['FORM']['TOOL_PANEL_BUTTONS_ORDER'] = [
			'VIEW' => $viewButtons,
			'EDIT' => [
				UI\EntityEditor\Action::DEFAULT_ACTION_BUTTON_ID, 'SAVE_AND_CONDUCT', 'CANCEL',
			],
		];
	}

	public static function getTypeByDocumentId(int $documentId): ?string
	{
		if ($documentId <= 0)
		{
			return null;
		}
		$row = StoreDocumentTable::getRow([
			'select' => [
				'ID',
				'DOC_TYPE',
			],
			'filter' => [
				'=ID' => $documentId,
			],
		]);

		return $row['DOC_TYPE'] ?? null;
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit