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/modules/catalog/lib/component/

Upload File :
current_dir [ Writeable] document_root [ Writeable]

 

Command :


[ Back ]     

Current File : /home/bitrix/ext_www/cvetdv.ru/bitrix/modules/catalog/lib/component/gridvariationform.php
<?php

namespace Bitrix\Catalog\Component;

use Bitrix\Catalog;
use Bitrix\Catalog\Access\AccessController;
use Bitrix\Catalog\Access\ActionDictionary;
use Bitrix\Catalog\Config\State;
use Bitrix\Catalog\v2\Property\Property;
use Bitrix\Currency\CurrencyManager;
use Bitrix\Currency\Integration\IblockMoneyProperty;
use Bitrix\Iblock\ElementTable;
use Bitrix\Iblock\PropertyTable;
use Bitrix\Main\Engine\Response\AjaxJson;
use Bitrix\Main\Grid\Editor\Types;
use Bitrix\Main\Grid\Options;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\Text\HtmlFilter;

class GridVariationForm extends VariationForm
{
	/** @var Catalog\v2\Sku\BaseSku */
	protected $entity;

	protected static ?array $usedHeaders = null;
	protected static ?array $headers = null;

	protected function prepareFieldName(string $name): string
	{
		$name = parent::prepareFieldName($name);

		return static::formatFieldName($name);
	}

	public static function formatFieldName($name): string
	{
		return BaseForm::GRID_FIELD_PREFIX.parent::formatFieldName($name);
	}

	public static function preparePropertyName(string $name = ''): string
	{
		$name = parent::preparePropertyName($name);

		return static::formatFieldName($name);
	}

	public function isReadOnly(): bool
	{
		if (State::isExternalCatalog())
		{
			return true;
		}

		return !$this->isAllowedEditFields();
	}

	public static function getGridCardSettingsItems(): array
	{
		$result = [];
		$result['VAT_INCLUDED'] = [
			'ITEMS' => [
				static::formatFieldName('VAT_ID'),
				static::formatFieldName('VAT_INCLUDED'),
			],
			'TITLE' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_TITLE_VAT_INCLUDED'),
			'DESCRIPTION' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_DESC_VAT_INCLUDED'),
		];
		if (AccessController::getCurrent()->check(ActionDictionary::ACTION_PRODUCT_PURCHASE_INFO_VIEW))
		{
			$result['PURCHASING_PRICE_FIELD'] = [
				'ITEMS' => [
					static::formatFieldName('PURCHASING_PRICE_FIELD'),
				],
				'TITLE' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_TITLE_PURCHASING_PRICE_FIELD'),
				'DESCRIPTION' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_DESC_PURCHASING_PRICE_FIELD'),
			];
		}
		$result['MEASUREMENTS'] = [
			'ITEMS' => [
				static::formatFieldName('HEIGHT'),
				static::formatFieldName('LENGTH'),
				static::formatFieldName('WIDTH'),
				static::formatFieldName('WEIGHT'),
			],
			'TITLE' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_TITLE_MEASUREMENTS'),
			'DESCRIPTION' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_DESC_MEASUREMENTS'),
		];
		$result['MEASURE_RATIO'] = [
			'ITEMS' => [
				static::formatFieldName('MEASURE_RATIO'),
			],
			'TITLE' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_TITLE_MEASURE_RATIO'),
			'DESCRIPTION' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_SETTINGS_DESC_MEASURE_RATIO_MSGVER_1'),
		];

		return $result;
	}

	public function setGridSettings(string $settingId, $selected, array $currentHeaders = []): AjaxJson
	{
		$headers = static::getHeaderIdsBySettingId($settingId);

		if (!empty($headers))
		{
			$options = new Options($this->getVariationGridId());
			$allUsedColumns = $options->getUsedColumns();

			if (empty($allUsedColumns))
			{
				$allUsedColumns = $currentHeaders;
			}

			if ($selected === 'true')
			{
				// sort new columns by default grid column sort
				$defaultHeaders = array_column($this->getGridHeaders(), 'id');
				$currentHeadersInDefaultPosition = array_values(
					array_intersect($defaultHeaders, array_merge($allUsedColumns, $headers))
				);
				$headers = array_values(array_intersect($defaultHeaders, $headers));

				foreach ($headers as $header)
				{
					$insertPosition = array_search($header, $currentHeadersInDefaultPosition, true);
					array_splice($allUsedColumns, $insertPosition, 0, $header);
				}
			}
			else
			{
				$allUsedColumns = array_diff($allUsedColumns, $headers);
			}

			$options->setColumns(implode(',', $allUsedColumns));
			$options->save();
		}

		return AjaxJson::createSuccess();
	}

	protected static function getHeaderIdsBySettingId(string $settingId): array
	{
		$headers = [];
		switch ($settingId)
		{
			case 'MEASUREMENTS':
				$headers = [
					'WEIGHT',
					'WIDTH',
					'LENGTH',
					'HEIGHT',
				];
				break;
			case 'PURCHASING_PRICE_FIELD':
				if (AccessController::getCurrent()->check(ActionDictionary::ACTION_PRODUCT_PURCHASE_INFO_VIEW))
				{
					$headers = [
						'PURCHASING_PRICE_FIELD',
					];
				}
				break;
			case 'MEASURE_RATIO':
				$headers = [
					'MEASURE_RATIO',
				];
				break;
			case 'VAT_INCLUDED':
				$headers = [
					'VAT_INCLUDED',
					'VAT_ID'
				];
				break;
		}

		foreach ($headers as &$id)
		{
			$id = static::formatFieldName($id);
		}
		unset($id);

		return $headers;
	}

	protected function buildDescriptions(): array
	{
		return array_merge(
			parent::buildDescriptions(),
			$this->getCommonQuantityDescription(),
			$this->getBarcodeDescription()
		);
	}

	protected function getPropertyDescription(Property $property): array
	{
		$description = parent::getPropertyDescription($property);

		if ($description['editable'])
		{
			switch ($description['type'])
			{
				case 'multilist':
					$dropdownItems = [];

					if (!empty($description['data']['items']) && is_array($description['data']['items']))
					{
						$dropdownItems = $description['data']['items'];
					}

					$description['editable'] = [
						'TYPE' => Types::MULTISELECT,
						'DATA' => [
							'ITEMS' => $dropdownItems,
						]
					];
					break;
				case 'list':
					$dropdownItems = [];

					if (!$description['required'])
					{
						$dropdownItems[] = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_NOT_SELECTED');
					}

					if (!empty($description['data']['items']) && is_array($description['data']['items']))
					{
						foreach ($description['data']['items'] as $item)
						{
							$dropdownItems[$item['VALUE']] = $item['NAME'];
						}
					}
					$description['editable'] = [
						'TYPE' => Types::DROPDOWN,
						'items' => $dropdownItems,
					];
					break;
				case 'custom':
					if ($description['multiple'] === true && $description['propertyCode'] !== 'MORE_PHOTO')
					{
						$description['editable'] = false;
					}
					else
					{
						$description['editable'] = [
							'TYPE' => Types::CUSTOM,
							'NAME' => $description['data']['edit'] ?? $description['name'],
							// 'HTML' => $description['data']['edit'] ?? $description['name'],
						];
					}
					break;
				case 'boolean':
					$description['editable'] = ['TYPE' => Types::CHECKBOX];
					break;
				case 'datetime':
					$description['editable'] = ['TYPE' => Types::DATE];
					break;
				case 'money':
					$description['editable'] = [
						'TYPE' => Types::MONEY,
						'CURRENCY_LIST' => CurrencyManager::getSymbolList(),
						'HTML_ENTITY' => true,
					];
					break;
				default:
					$description['editable'] = ['TYPE' => Types::TEXT];
			}

			$nonEditableUserTypes = [
				'ElementXmlID',
				'employee',
				'map_yandex',
				'map_google',
				'ECrm',
				'video',
				'HTML',
			];
			if (
				$description['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_ELEMENT
				|| $description['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_SECTION
				|| in_array($description['settings']['USER_TYPE'], $nonEditableUserTypes, true)
			)
			{
				$description['editable'] = false;
			}
		}

		return $description;
	}

	protected function getUnavailableUserTypes(): array
	{
		return [
			'DiskFile',
			'TopicID',
		];
	}

	public function getColumnValues(bool $allowDefaultValues = true): array
	{
		$values = $this->getShowedValues($allowDefaultValues);

		foreach ($this->getGridHeaders() as $description)
		{
			$name = $description['id'];
			if (!isset($values[$name]))
			{
				continue;
			}

			$currentValue = $values[$name];

			switch ($description['type'])
			{
				case 'string':
				case 'text':
					if (!empty($values[$name]))
					{
						$values[$name] = HtmlFilter::encode($values[$name]);
					}
					break;
				case 'number':
					$values[$name] = (float)($values[$name] ?: 0);
					break;
				case 'multilist':
					if (is_array($currentValue))
					{
						$formatted = [];
						$items = [];

						foreach ($description['data']['items'] as $item)
						{
							$items[$item['VALUE']] = $item['HTML'] ?? HtmlFilter::encode($item['NAME']);
						}

						foreach ($currentValue as $multipleItemValue)
						{
							$formatted[] = $items[$multipleItemValue];
						}

						$values[$name] = $formatted;
					}
					break;
				case 'boolean':
					if (
						$description['id'] === static::formatFieldName('ACTIVE')
						|| $description['id'] === static::formatFieldName('AVAILABLE')
						|| $description['id'] === static::formatFieldName('VAT_INCLUDED')
					)
					{
						$code = $currentValue === 'Y' ? 'YES' : 'NO';
					}
					else
					{
						$code = ($currentValue !== '') ? 'YES' : 'NO';
					}
					$values[$name] = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_VALUE_' . $code);
					break;
				case 'list':
					if (isset($description['editable']['items']))
					{
						$values[$name] = HtmlFilter::encode($description['editable']['items'][$currentValue] ?? '');
						break;
					}
					foreach ($description['data']['items'] as $item)
					{
						if ($currentValue === $item['VALUE'])
						{
							$values[$name] = HtmlFilter::encode($item['NAME'] ?? '');
							break;
						}
					}
					break;
				case 'custom':
					$values[$name] = $values[$description['data']['view']];
					break;
				case 'user':
					$values[$name] = HtmlFilter::encode($values[$name . '_FORMATTED_NAME'] ?? '');
					break;
				case 'readOnlyVat':
					$currentVat = (int)$values[$name];
					$values[$name] = '';
					if ($currentVat > 0)
					{
						foreach ($this->getVats() as $vat)
						{
							if ((int)$vat['ID'] === $currentVat)
							{
								$values[$name] = htmlspecialcharsbx($vat['NAME']);
								break;
							}
						}
					}
					break;
				case 'barcode':
					$barcodes =
						is_array($values[$name])
							? array_column($values[$name], 'BARCODE')
							: null
					;

					$values[$name] = $barcodes ? htmlspecialcharsbx(implode(', ', $barcodes)) : '';
					break;
				case 'money':
					if (isset($description['data']['isProductProperty']) && $description['data']['isProductProperty'])
					{
						$separatedValues = IblockMoneyProperty::getSeparatedValues($values[$name]);
						$amount = (float)($separatedValues['AMOUNT'] . '.' . $separatedValues['DECIMALS']);
						$currency = $separatedValues['CURRENCY'];
						$values[$name] = \CCurrencyLang::CurrencyFormat($amount, $currency, true);
					}
					break;
			}

			if (is_array($values[$name]))
			{
				$values[$name] = implode(', ', $values[$name]);
			}
		}

		return $values;
	}

	public function loadGridHeaders(): array
	{
		$defaultWidth = 130;

		$headerName = static::getHeaderName('NAME');

		$headers = [
			[
				'id' => static::formatFieldName('NAME'),
				'name' => $headerName['NAME'],
				'title' => $headerName['TITLE'],
				'sort' => 'NAME',
				'type' => 'string',
				'editable' =>
					$this->isAllowedEditFields()
						? [
							'TYPE' => Types::TEXT,
							'PLACEHOLDER' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_NEW_VARIATION_PLACEHOLDER_MSGVER_1'),
						]
						: false
				,
				'width' => $defaultWidth,
				'default' => false,
			],
		];

		$productFields = [
			'ACTIVE',
			'QUANTITY_COMMON',
			'MEASURE',
			'MEASURE_RATIO',
		];
		if (State::isUsedInventoryManagement())
		{
			$productFields[] = 'BARCODE';
		}
		$headers = array_merge(
			$headers,
			$this->getIblockPropertiesHeaders(),
			$this->getProductFieldHeaders(
				$productFields,
				$defaultWidth
			),
			$this->getPurchasingPriceHeaders($defaultWidth),
			$this->getPricesHeaders($defaultWidth),
			$this->getProductFieldHeaders(
				[
					'AVAILABLE', 'VAT_ID', 'VAT_INCLUDED', 'QUANTITY', 'QUANTITY_RESERVED',
					'QUANTITY_TRACE', 'CAN_BUY_ZERO', // 'SUBSCRIBE',
					'WEIGHT', 'WIDTH', 'LENGTH', 'HEIGHT',
					'SHOW_COUNTER', 'CODE', 'TIMESTAMP_X', 'MODIFIED_BY',
					'DATE_CREATE', 'XML_ID',
					// 'BAR_CODE', 'TAGS', 'DISCOUNT', 'STORE', 'PRICE_TYPE',
				],
				$defaultWidth
			)
		);

		self::$headers = $headers;

		return $headers;
	}

	public function getGridHeaders(): array
	{
		if (self::$headers)
		{
			return self::$headers;
		}

		return $this->loadGridHeaders();
	}

	/**
	 * Columns that are sent in the grid request.
	 *
	 * @return array
	 */
	public function getGridSupportedAjaxColumns(): array
	{
		$columns = array_fill_keys(
			array_column($this->getGridHeaders(), 'id'),
			true
		);

		foreach ($this->getIblockPropertiesDescriptions() as $property)
		{
			$name = $property['name'];

			// files are not supported because new files are not sent in the request
			$isFile = $property['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_FILE;
			if ($isFile)
			{
				unset($columns[$name]);
			}
		}

		return array_keys($columns);
	}

	protected function getProductFieldHeaders(array $fields, int $defaultWidth): array
	{
		$headers = [];

		$numberFields = ['QUANTITY', 'QUANTITY_RESERVED', 'QUANTITY_COMMON', 'MEASURE_RATIO', 'WEIGHT', 'WIDTH', 'LENGTH', 'HEIGHT'];
		$numberFields = array_fill_keys($numberFields, true);

		$immutableFields = ['TIMESTAMP_X', 'MODIFIED_BY', 'DATE_CREATE', 'CREATED_USER_NAME', 'AVAILABLE'];
		$immutableFields = array_fill_keys($immutableFields, true);

		$defaultFields = ['QUANTITY', 'MEASURE', 'NAME'];
		if (State::isUsedInventoryManagement())
		{
			$defaultFields[] = 'BARCODE';
		}
		$defaultFields = array_fill_keys($defaultFields, true);

		$sortableFields = [
			'QUANTITY' =>'QUANTITY',
			'AVAILABLE' =>'AVAILABLE',
			'WEIGHT' =>'WEIGHT',
			'ACTIVE' =>'ACTIVE',
			'MEASURE' =>'MEASURE',
			'TIMESTAMP_X' => 'TIMESTAMP_X',
			'MODIFIED_BY' => 'MODIFIED_BY',
			'DATE_CREATE' => 'CREATED',
			'CREATED_USER_NAME' => 'CREATED_BY',
			'CODE' => 'CODE',
			'EXTERNAL_ID' => 'EXTERNAL_ID',
			'XML_ID' => 'XML_ID',
			'TAGS' => 'TAGS',
			'SHOW_COUNTER' => 'SHOW_COUNTER',
			'SHOW_COUNTER_START' => 'SHOW_COUNTER_START',
			'PREVIEW_PICTURE' => 'HAS_PREVIEW_PICTURE',
			'DETAIL_PICTURE' => 'HAS_DETAIL_PICTURE',
		];

		foreach ($fields as $code)
		{
			$type = isset($numberFields[$code]) ? 'number' : 'string';

			switch ($code)
			{
				case 'AVAILABLE':
				case 'ACTIVE':
				case 'VAT_INCLUDED':
					$type = 'boolean';
					break;

				case 'MODIFIED_BY':
					$type = 'user';
					break;

				case 'VAT_ID':
				case 'MEASURE':
				case 'QUANTITY_TRACE':
				case 'CAN_BUY_ZERO':
				case 'SUBSCRIBE':
					$type = 'list';
					break;
			}

			$editable = false;

			if (!isset($immutableFields[$code]))
			{
				$editable = [
					'TYPE' => Types::TEXT,
				];

				switch ($code)
				{
					case 'ACTIVE':
						$editable = [
							'TYPE' => Types::CHECKBOX,
						];
						break;
					case 'VAT_INCLUDED':
						$editable =
							$this->isPricesEditable()
								? ['TYPE' => Types::CHECKBOX]
								: false
						;
						break;

					case 'VAT_ID':
						if (!$this->isPricesEditable())
						{
							$editable = false;
							$type = 'readOnlyVat';

							break;
						}

						$defaultVat = $this->getDefaultVat();
						$vatList = [
							$defaultVat['ID'] => $defaultVat['NAME'],
						];

						if ($defaultVat['ID'] !== static::NOT_SELECTED_VAT_ID_VALUE && !Loader::includeModule('bitrix24'))
						{
							$vatList[static::NOT_SELECTED_VAT_ID_VALUE] = Loc::getMessage("CATALOG_PRODUCT_CARD_VARIATION_GRID_NOT_SELECTED");
						}

						foreach ($this->getVats() as $vat)
						{
							if ($vat['RATE'] === $defaultVat['RATE'] && $vat['EXCLUDE_VAT'] === $defaultVat['EXCLUDE_VAT'])
							{
								continue;
							}

							$vatList[$vat['ID']] = htmlspecialcharsbx($vat['NAME']);
						}
						$editable = [
							'TYPE' => Types::DROPDOWN,
							'items' => $vatList,
						];
						break;

					case 'MEASURE':
						$measureList = [];

						foreach ($this->getMeasures() as $measure)
						{
							$measureList[$measure['ID']] = htmlspecialcharsbx($measure['MEASURE_TITLE']);
							if (empty($measureList[$measure['ID']]))
							{
								$measureList[$measure['ID']] = \CCatalogMeasureClassifier::getMeasureTitle(
									$measure["CODE"],
									'MEASURE_TITLE'
								);
							}
						}
						$editable = [
							'TYPE' => Types::DROPDOWN,
							'items' => $measureList,
						];
						break;

					case 'QUANTITY':
					case 'QUANTITY_RESERVED':
					case 'QUANTITY_COMMON':
						if (State::isUsedInventoryManagement())
						{
							$editable = false;
						}
						else
						{
							$editable = [
								'TYPE' => Types::NUMBER,
								'PLACEHOLDER' => 0,
							];
						}
						break;

					case 'MEASURE_RATIO':
						$editable = [
							'TYPE' => Types::NUMBER,
							'PLACEHOLDER' => 0,
						];
						break;

					case 'BARCODE':
						$editable = [
							'TYPE' => Types::CUSTOM,
						];
						$type = 'barcode';
						break;

					case 'QUANTITY_TRACE':
					case 'CAN_BUY_ZERO':
					case 'SUBSCRIBE':
						$items = [];
						foreach ($this->getCatalogEnumFields($code) as $field)
						{
							$items[$field['VALUE']] = $field['NAME'];
						}

						$editable = [
							'TYPE' => Types::DROPDOWN,
							'items' => $items,
						];
						break;
				}
			}

			$headerName =
				$code === 'MODIFIED_BY'
					? static::getHeaderName('USER_NAME')
					: static::getHeaderName($code)
			;

			$sortField = $sortableFields[$code] ?? false;

			$headers[] = [
				'id' => static::formatFieldName($code),
				'name' => $headerName['NAME'],
				'title' => $headerName['TITLE'],
				'sort' => $sortField,
				'locked' => false,
				'headerHint' => null,
				'type' => $type,
				'align' => $type === 'number' ? 'right' : 'left',
				'editable' => $editable,
				'width' => $defaultWidth,
				'default' => isset($defaultFields[$code]),
			];
		}

		return $headers;
	}

	/**
	 * Returns grid header's description for iblock properties.
	 *
	 * @return array
	 */
	protected function getIblockPropertiesHeaders(): array
	{
		$headers = [];

		foreach ($this->getIblockPropertiesDescriptions() as $property)
		{
			$isDirectory =
				$property['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_STRING
				&& $property['settings']['USER_TYPE'] === 'directory'
			;

			$sortField = 'PROPERTY_' . $property['propertyId'];
			if (
				$property['multiple']
				|| $property['propertyCode'] === 'CML2_LINK'
				|| $property['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_FILE
			)
			{
				$sortField = false;
			}

			$header = [
				'id' => $property['name'],
				'name' => $property['title'],
				'title' => $property['title'],
				'type' => $property['type'],
				'align' => $property['type'] === 'number' ? 'right' : 'left',
				'sort' => $sortField,
				'default' => $property['propertyCode'] === self::MORE_PHOTO,
				'data' => $property['data'],
				'width' => $isDirectory ? 160 : null,
				'editable' => $property['editable'],
			];
			if (!empty($property['isEnabledOfferTree']))
			{
				$header['hint'] = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_OFFER_TREE_HINT_MSGVER_1');
			}
			if ($property['propertyCode'] === self::MORE_PHOTO)
			{
				$header['hint'] = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_MORE_PHOTO_SIZE');
				$header['hintHtml'] = true;
			}

			if (
				$property['settings']['PROPERTY_TYPE'] === PropertyTable::TYPE_FILE
				&& $property['multiple'] === true
				&& $property['propertyCode'] !== 'MORE_PHOTO'
			)
			{
				$header['hint'] = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_FILE_MULTIPLE_HINT_MSGVER_1');
			}

			$headers[] = $header;
		}

		return $headers;
	}

	/**
	 * Returns list with purchasing price grid header.
	 *
	 * @param int|null $defaultWidth
	 * @return array
	 */
	protected function getPurchasingPriceHeaders(?int $defaultWidth): array
	{
		$headers = [];

		if ($this->isPurchasingPriceAllowed())
		{
			$headerName = static::getHeaderName('PURCHASING_PRICE');

			$headers[] = [
				'id' => static::formatFieldName('PURCHASING_PRICE_FIELD'),
				'name' => $headerName['NAME'],
				'title' => $headerName['TITLE'],
				'sort' => 'PURCHASING_PRICE',
				'type' => 'money',
				'align' => 'right',
				'editable' =>
					!State::isUsedInventoryManagement() && $this->isAllowedEditFields()
						? [
							'TYPE' => Types::MONEY,
							'CURRENCY_LIST' => CurrencyManager::getSymbolList(),
							'HTML_ENTITY' => true,
						]
						: false
				,
				'width' => $defaultWidth,
				'default' => false,
			];
		}

		return $headers;
	}

	/**
	 * Returns grid headers list for price types.
	 *
	 * @param int|null $defaultWidth
	 * @return array
	 */
	protected function getPricesHeaders(?int $defaultWidth): array
	{
		$headers = [];

		$currencyList = CurrencyManager::getSymbolList();

		foreach (Catalog\GroupTable::getTypeList() as $priceType)
		{
			$columnName = $priceType['NAME_LANG'] ?? $priceType['NAME'];

			$priceId = static::formatFieldName(BaseForm::PRICE_FIELD_PREFIX.$priceType['ID'].'_FIELD');
			$headers[] = [
				'id' => $priceId,
				'name' => $columnName,
				'title' => $columnName,
				'sort' => 'SCALED_PRICE_'.$priceType['ID'],
				'type' => 'money',
				'align' => 'right',
				'editable' =>
					$this->isPricesEditable()
						? [
							'TYPE' => Types::MONEY,
							'CURRENCY_LIST' => $currencyList,
							'HTML_ENTITY' => true,
						]
						: false
				,
				'locked' => !$this->isPricesEditable(),
				'headerHint' =>
					$this->isPricesEditable()
						? null
						: Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_PRICE_EDIT_RESTRICTED_HINT')
				,
				'base' => $priceType['BASE'] === 'Y',
				'width' => $defaultWidth,
				'default' => $priceType['BASE'] === 'Y',
			];
		}

		return $headers;
	}

	private function getShowedValues(bool $allowDefaultValues = true): array
	{
		if (!self::$usedHeaders)
		{
			$options = new Options($this->getVariationGridId());
			self::$usedHeaders = $options->getUsedColumns();

			if (!self::$usedHeaders)
			{
				$defaultHeaders = array_filter(
					$this->getGridHeaders(),
					static function ($header)
					{
						return ($header['default'] === true);
					}
				);

				self::$usedHeaders = array_column($defaultHeaders, 'id');
			}
		}

		$usedHeaders = self::$usedHeaders;
		$filteredDescriptions = array_filter(
			$this->getDescriptions(),
			static function ($description) use ($usedHeaders)
			{
				return in_array($description['name'], $usedHeaders, true);
			}
		);

		return parent::getValues($allowDefaultValues, $filteredDescriptions);
	}

	public function getValues(bool $allowDefaultValues = true, array $descriptions = null): array
	{
		$values = $this->getShowedValues($allowDefaultValues);

		foreach ($this->getDescriptions() as $description)
		{
			$name = $description['name'];

			if (!isset($values[$name]))
			{
				continue;
			}

			switch ($description['type'])
			{
				case 'custom':
					$values[$name] = $values[$description['data']['view']];
					break;
				case 'money':
					$descriptionData = $description['data'];
					$values[$name] = [
						'PRICE' => [
							'NAME' => $descriptionData['amount'],
							'VALUE' => $values[$descriptionData['amount']],
						],
						'CURRENCY' => [
							'NAME' => $descriptionData['currency']['name'],
							'VALUE' => $values[$descriptionData['currency']['name']],
						],
					];
					break;
				case 'boolean':
					$descriptionData = $description['data'] ?? [];
					$variants = $descriptionData['items'] ?? [];
					foreach ($variants as $variant)
					{
						if ($values[$name] === $variant['ID'])
						{
							$values[$name] = $variant['NAME'];
							break;
						}
					}
					break;
			}
		}

		return $values;
	}

	/**
	 * Leaves only the values of the grid fields used.
	 *
	 * For price fields, converts values to `['PRICE' => '...', 'CURRENCY' => '...']` format.
	 *
	 * @param array $dirtyValues
	 *
	 * @return array with fields values, and additional fields `PRICES` and `PROPERTIES`.
	 */
	public function prepareFieldsValues(array $dirtyValues): array
	{
		$result = [
			'PROPERTIES' => [],
			'BARCODES' => [],
			'PRICES' => [],
		];

		$pricePrefix = self::GRID_FIELD_PREFIX . self::PRICE_FIELD_PREFIX;
		$purchacingPricePrefix = self::GRID_FIELD_PREFIX . 'PURCHASING_PRICE';

		foreach ($this->getDescriptions() as $description)
		{
			$name = $description['name'];
			$value = $dirtyValues[$name] ?? null;
			if (!isset($value))
			{
				continue;
			}

			if (isset($description['propertyId']))
			{
				$type = $description['type'] ?? null;
				if ($type === 'multilist' && empty($value))
				{
					$value = [];
				}

				$propertyId = (int)$description['propertyId'];
				$result['PROPERTIES'][$propertyId] = $value;
			}
			elseif (mb_strpos($name, $pricePrefix) === 0)
			{
				if (
					is_array($value)
					&& isset($value['PRICE']['NAME'], $value['PRICE']['VALUE'], $value['CURRENCY']['VALUE'])
				)
				{
					$priceGroupId = str_replace($pricePrefix, '', $value['PRICE']['NAME']);
					if ($priceGroupId)
					{
						$result['PRICES'][$priceGroupId] = [
							'PRICE' => (float)$value['PRICE']['VALUE'],
							'CURRENCY' => (string)$value['CURRENCY']['VALUE'],
						];
					}
				}
			}
			elseif (mb_strpos($name, $purchacingPricePrefix) === 0)
			{
				if (is_array($value) && isset($value['PRICE']['VALUE'], $value['CURRENCY']['VALUE']))
				{
					$result['PURCHASING_PRICE'] = (float)$value['PRICE']['VALUE'];
					$result['PURCHASING_CURRENCY'] = (string)$value['CURRENCY']['VALUE'];
				}
			}
			elseif (isset($description['originalName']))
			{
				$name = $description['originalName'];
				$result[$name] = $value;
			}
			elseif (isset($description['entity']) && $description['entity'] === 'barcode')
			{
				if (is_array($value))
				{
					array_push($result['BARCODES'], ...$value);
				}
				else
				{
					$result['BARCODES'][] = $value;
				}
			}
			elseif (isset($description['entity']) && $description['entity'] === 'measure_ratio')
			{
				$result['MEASURE_RATIO'] = $value;
			}
		}

		return $result;
	}

	protected function getAdditionalValues(array $values, array $descriptions = null): array
	{
		$additionalValues = parent::getAdditionalValues($values, $descriptions);

		$numberFields = ['MEASURE_RATIO', 'QUANTITY'];
		foreach ($numberFields as $fieldName)
		{
			$fieldName = self::formatFieldName($fieldName);
			if (isset($values[$fieldName]) && $values[$fieldName] == 0)
			{
				$additionalValues[$fieldName] = null;
			}
		}

		return $additionalValues;
	}

	protected function getPropertySettings(Property $property): array
	{
		$settings = parent::getPropertySettings($property);
		$settings['GRID_MODE'] = true;

		return $settings;
	}

	protected function getImagePropertyViewHtml($value): string
	{
		$fileCount = 0;

		// single scalar property
		if (!empty($value) && !is_array($value))
		{
			$value = [$value];
		}

		if (is_array($value))
		{
			$fileCount = min(count($value), 3);
			$value = reset($value);
		}

		$imageSrc = '';

		if (!empty($value))
		{
			$image = \CFile::GetFileArray($value);
			if ($image)
			{
				$imageSrc = $image['SRC'];
			}
		}

		$multipleClass = match ($fileCount)
		{
			3 => ' ui-image-input-img-block-multiple',
			2 => ' ui-image-input-img-block-double',
			0 => ' ui-image-input-img-block-empty',
			default => '',
		};

		if ($imageSrc)
		{
			$imageSrc = ' src="' . $imageSrc . '"';
		}

		return <<<HTML
<div class="ui-image-input-img-block{$multipleClass}">
	<div class="ui-image-input-img-block-inner">
		<div class="ui-image-input-img-item">
			<img class="ui-image-input-img"{$imageSrc}>
		</div>
	</div>
</div>
HTML;
	}

	protected function getFilePropertyInputName(array $property): string
	{
		$inputName = $property['name'] ?? '';

		if (isset($property['settings']['MULTIPLE']) && $property['settings']['MULTIPLE'] === 'Y')
		{
			$inputName .= '_n#IND#';
		}

		return $inputName;
	}

	protected function getFieldValue(array $field)
	{
		if ($field['entity'] === 'money')
		{
			return $this->getMoneyFieldValue($field);
		}

		if ($field['entity'] === 'barcode')
		{
			return $this->getBarcodeValue();
		}

		if (isset($field['originalName']) && $field['originalName'] === 'QUANTITY_COMMON')
		{
			return $this->getCommonQuantityFieldValue();
		}

		return parent::getFieldValue($field);
	}

	protected function getMoneyFieldValue(array $field)
	{
		if ($field['priceTypeId'] === 'PURCHASING_PRICE')
		{
			$price = $this->entity->getField('PURCHASING_PRICE');
			$currency = $this->entity->getField('PURCHASING_CURRENCY');
		}
		else
		{
			$priceItem = $this->entity
				->getPriceCollection()
				->findByGroupId($field['priceTypeId'])
			;
			$price = $priceItem?->getPrice();
			$currency = $priceItem?->getCurrency();
		}

		$currency = $currency ?? CurrencyManager::getBaseCurrency();

		return \CCurrencyLang::CurrencyFormat($price, $currency, true);
	}

	/**
	 * Return description of common quantity field
	 * @return array
	 */
	protected function getCommonQuantityDescription(): array
	{
		$commonQuantityName = 'QUANTITY_COMMON';
		return [
			[
				'entity' => 'product',
				'name' => $this->prepareFieldName($commonQuantityName),
				'originalName' => $commonQuantityName,
				'title' => Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_DESCRIPTION_COMMON_QUANTITY_TITLE'),
				'type' => 'number',
				'editable' => false,
				'required' => false,
				'placeholders' => null,
				'defaultValue' => null,
				'optionFlags' => 1, // showAlways
			]
		];
	}

	/**
	 * Return value of common quantity of the variation ( Available quantity + Reserved Quantity )
	 * @return float
	 */
	protected function getCommonQuantityFieldValue(): float
	{
		$quantity = (float)$this->entity->getField('QUANTITY');
		$quantityReserved = (float)$this->entity->getField('QUANTITY_RESERVED');
		return $quantity + $quantityReserved;
	}

	protected function getBarcodeDescription(): array
	{
		if (!State::isUsedInventoryManagement())
		{
			return [];
		}

		$headerName = static::getHeaderName('BARCODE');

		return [
			[
				'entity' => 'barcode',
				'name' => static::formatFieldName('BARCODE'),
				'title' => $headerName['NAME'],
				'type' => 'barcode',
				'multiple' => true,
				'editable' => $this->isAllowedEditFields(),
				'required' => false,
			],
		];
	}

	/**
	 * @return array
	 */
	protected function getBarcodeValue(): array
	{
		$barcodes = [];
		foreach ($this->entity->getBarcodeCollection() as $barcodeItem)
		{
			$barcodes[] = [
				'ID' => $barcodeItem->getId(),
				'BARCODE' => $barcodeItem->getBarcode(),
			];
		}

		return $barcodes;
	}

	protected function getElementTableMap(): array
	{
		return ElementTable::getMap();
	}

	protected static function getHeaderName(string $code): array
	{
		$headerName = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_HEADER_NAME_' . $code);
		$headerTitle = Loc::getMessage('CATALOG_PRODUCT_CARD_VARIATION_GRID_HEADER_TITLE_' . $code);

		return [
			'NAME' => $headerName,
			'TITLE' => $headerTitle ?? $headerName,
		];
	}
}

Youez - 2016 - github.com/yon3zu
LinuXploit