File manager - Edit - /home/c14075/dragmet-ural.ru/www/UI.tar
Back
PropertyProduct.php 0000644 00000016653 15133137061 0010455 0 ustar 00 <?php namespace Bitrix\Catalog\UI; use Bitrix\Main\Config\Option; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Catalog; use Bitrix\Crm; use Bitrix\Iblock; class PropertyProduct { private const PRICE_PRECISION = 2; /** * Get readable properties of a product variation. Fields are not properties - but included here. * * @param int $iblockId * @param int $skuId * * @return array in format ['PROPERTY_CODE' => 'readable value'] */ public static function getSkuProperties(int $iblockId, int $skuId): array { $properties = self::getIblockProperties($iblockId, $skuId); $product = Catalog\ProductTable::getRow([ 'select' => [ 'SKU_NAME' => 'IBLOCK_ELEMENT.NAME', 'SKU_DESCRIPTION' => 'IBLOCK_ELEMENT.DETAIL_TEXT', 'PURCHASING_PRICE', 'PURCHASING_CURRENCY', 'LENGTH', 'WIDTH', 'HEIGHT', 'WEIGHT', ], 'filter' => ['=ID' => $skuId], ]); $properties['SKU_ID'] = $skuId; if ($product !== null) { $properties['PURCHASING_PRICE'] = round((float)$product['PURCHASING_PRICE'], self::PRICE_PRECISION); $properties['PURCHASING_PRICE_FORMATTED'] = \CCrmCurrency::MoneyToString( $product['PURCHASING_PRICE'], $product['PURCHASING_CURRENCY'] ); $properties['LENGTH'] = $product['LENGTH']; $properties['WEIGHT'] = $product['WEIGHT']; $properties['WIDTH'] = $product['WIDTH']; $properties['HEIGHT'] = $product['HEIGHT']; $properties['SKU_NAME'] = htmlspecialcharsbx($product['SKU_NAME']); $properties['SKU_DESCRIPTION'] = (new \CBXSanitizer())->SanitizeHtml($product['SKU_DESCRIPTION']); } else { $properties['PURCHASING_PRICE'] = 0; $properties['PURCHASING_PRICE_FORMATTED'] = ''; $properties['LENGTH'] = null; $properties['WEIGHT'] = null; $properties['WIDTH'] = null; $properties['HEIGHT'] = null; $properties['SKU_NAME'] = ''; $properties['SKU_DESCRIPTION'] = ''; } return $properties; } /** * Get readable properties of a product. * * @param int $iblockId * @param int $productId * * @return array in format ['PROPERTY_CODE' => 'readable value'] */ public static function getIblockProperties(int $iblockId, int $productId): array { $result = []; $props = \CIBlockElement::GetProperty($iblockId, $productId, 'id', 'asc'); while ($prop = $props->GetNext()) { if (empty($prop['VALUE']) && !($prop['PROPERTY_TYPE'] === 'L' && $prop['LIST_TYPE'] === 'C') ) { continue; } $code = 'PROPERTY_' . $prop['ID']; switch ($prop['PROPERTY_TYPE']) { case 'S': case 'N': if ($prop['USER_TYPE'] === 'directory' && isset($prop['USER_TYPE_SETTINGS']['TABLE_NAME']) && \CModule::IncludeModule('highloadblock') ) { $value = self::getDirectoryValue($prop); } else if ($prop['USER_TYPE'] === 'HTML') { $value = (new \CBXSanitizer())->SanitizeHtml($prop['~VALUE']['TEXT']); } else { $value = $prop['VALUE']; } if (!isset($result[$code])) { $result[$code] = $value; } else { $result[$code] .= ', ' . $value; } break; case 'L': if ($prop['LIST_TYPE'] === 'C') { switch ($prop['VALUE_ENUM']) { case 'Y': $value = Loc::getMessage('CRM_ENTITY_PRODUCT_LIST_COLUMN_CHECKBOX_YES'); break; case 'N': case '': $value = Loc::getMessage('CRM_ENTITY_PRODUCT_LIST_COLUMN_CHECKBOX_NO'); break; default: $value = htmlspecialcharsbx($prop['VALUE_ENUM']); } $result[$code] = $value; break; } if ($prop['MULTIPLE'] !== 'Y') { $result[$code] = $prop['VALUE_ENUM']; break; } if (!isset($result[$code])) { $result[$code] = $prop['VALUE_ENUM']; } else { $result[$code] .= ', ' . $prop['VALUE_ENUM']; } break; case 'F': $listImageSize = Option::get('iblock', 'list_image_size'); $minImageSize = [ 'W' => 1, 'H' => 1, ]; $maxImageSize = [ 'W' => $listImageSize, 'H' => $listImageSize, ]; $result[$code] ??= ''; $result[$code] .= \CFileInput::Show( 'NO_FIELDS[' . $productId . ']', $prop['VALUE'], [ 'IMAGE' => 'Y', 'PATH' => 'Y', 'FILE_SIZE' => 'Y', 'DIMENSIONS' => 'Y', 'IMAGE_POPUP' => 'N', 'MAX_SIZE' => $maxImageSize, 'MIN_SIZE' => $minImageSize, ], [ 'upload' => false, 'medialib' => false, 'file_dialog' => false, 'cloud' => false, 'del' => false, 'description' => false, ] ); break; default: $result[$code] = htmlspecialcharsbx($prop['VALUE']); } } return $result; } /** * Get readable string value of directory property. * * @param array $prop * * @return string|null */ private static function getDirectoryValue(array $prop): ?string { $hlblock = \Bitrix\Highloadblock\HighloadBlockTable::getRow([ 'filter' => [ '=TABLE_NAME' => $prop['USER_TYPE_SETTINGS']['TABLE_NAME'], ], ]); if ($hlblock) { $entity = \Bitrix\Highloadblock\HighloadBlockTable::compileEntity($hlblock); $entityClass = $entity->getDataClass(); $row = $entityClass::getRow([ 'filter' => [ 'UF_XML_ID' => $prop['VALUE'], ], ]); if (isset($row['UF_NAME'])) { return htmlspecialcharsbx($row['UF_NAME']); } return null; } return null; } /** * All column codes for crm.entity.product.list template * * @return array */ public static function getColumnNames(): array { $result = []; $iterator = Iblock\PropertyTable::getList([ 'select' => ['ID'], 'filter' => [ '=IBLOCK_ID' => self::getIblockIds(), 'ACTIVE' => 'Y', [ 'LOGIC' => 'OR', '==USER_TYPE' => null, '=USER_TYPE' => '', '@USER_TYPE' => self::getAllowedPropertyUserTypes(), ], '!@PROPERTY_TYPE' => self::getRestrictedPropertyTypes(), '!@CODE' => self::getRestrictedProperties(), ], 'order' => ['IBLOCK_ID' => 'ASC', 'SORT' => 'ASC', 'NAME' => 'ASC'], ]); while ($prop = $iterator->fetch()) { $result[] = 'PROPERTY_' . $prop['ID']; } $skuFields = [ 'SKU_ID', 'SKU_NAME', 'SKU_DESCRIPTION', 'LENGTH', 'WIDTH', 'HEIGHT', 'WEIGHT', ]; return array_merge($result, $skuFields); } /** * Restricted property types for hiding in product grid of deal. * For \CCrmEntityProductListComponent::getIblockColumnsDescription * * @return array */ public static function getRestrictedPropertyTypes(): array { return [ Iblock\PropertyTable::TYPE_ELEMENT, Iblock\PropertyTable::TYPE_SECTION, ]; } /** * Restricted properties for hiding in product grid of deal. * For \CCrmEntityProductListComponent::getIblockColumnsDescription * * @return array */ public static function getRestrictedProperties(): array { return [ 'MORE_PHOTO', 'BLOG_POST_ID', 'BLOG_COMMENTS_CNT', ]; } /** * Supported user types of properties in product grid of deal. * For \CCrmEntityProductListComponent::getIblockColumnsDescription * * @return array */ public static function getAllowedPropertyUserTypes(): array { return [ 'Date', 'DateTime', 'directory', 'HTML', ]; } /** * @return array */ private static function getIblockIds(): array { if (Loader::includeModule('crm')) { return [ Crm\Product\Catalog::getDefaultId(), Crm\Product\Catalog::getDefaultOfferId(), ]; } return []; } } FileUploader/DocumentController.php 0000644 00000001742 15133137061 0013456 0 ustar 00 <?php namespace Bitrix\Catalog\UI\FileUploader; use Bitrix\Catalog\Access\AccessController; use Bitrix\Catalog\Access\ActionDictionary; use Bitrix\Main\Engine\CurrentUser; use Bitrix\UI\FileUploader\FileOwnershipCollection; use Bitrix\UI\FileUploader\Configuration; use Bitrix\UI\FileUploader\UploaderController; class DocumentController extends UploaderController { public function __construct(array $options) { parent::__construct($options); } public function isAvailable(): bool { return AccessController::getCurrent()->check(ActionDictionary::ACTION_CATALOG_READ); } public function getConfiguration(): Configuration { return new Configuration(); } public function canUpload(): bool { return AccessController::getCurrent()->check(ActionDictionary::ACTION_STORE_VIEW); } public function verifyFileOwner(FileOwnershipCollection $files): void { } public function canView(): bool { return false; } public function canRemove(): bool { return false; } } FileUploader/ProductController.php 0000644 00000006217 15133137061 0013322 0 ustar 00 <?php namespace Bitrix\Catalog\UI\FileUploader; use Bitrix\Catalog\Access\AccessController; use Bitrix\Catalog\Access\ActionDictionary; use Bitrix\Catalog\v2\Image\MorePhotoImage; use Bitrix\Catalog\v2\IoC\ServiceContainer; use Bitrix\Iblock\PropertyTable; use Bitrix\Main\ArgumentException; use Bitrix\Main\Engine\CurrentUser; use Bitrix\UI\FileUploader\FileOwnershipCollection; use Bitrix\UI\FileUploader\Configuration; use Bitrix\UI\FileUploader\UploaderController; class ProductController extends UploaderController { private ?array $property; public function __construct(array $options = []) { $options['productId'] ??= 0; $options['productId'] = is_numeric($options['productId']) ? (int)$options['productId'] : 0; $options['iblockId'] ??= 0; $options['iblockId'] = (int)$options['iblockId']; if (empty($options['iblockId']) && !empty($options['productId'])) { $options['iblockId'] = (int)\CIBlockElement::GetIBlockByID($options['productId']); } if (empty($options['iblockId'])) { throw new ArgumentException('Parameter "iblockId" must be defined in options.'); } $iblockInfo = ServiceContainer::getIblockInfo($options['iblockId']); if (!$iblockInfo) { throw new ArgumentException("Iblock {{$options['iblockId']}} is not supported."); } $options['fieldName'] ??= MorePhotoImage::CODE; $options['fieldName'] = (string)$options['fieldName']; if ($options['fieldName'] === '') { throw new ArgumentException('Parameter "fieldName" must be defined in options.'); } $this->property = $this->loadProperty($options['iblockId'], $options['fieldName']); parent::__construct($options); } private function loadProperty(int $iblockId, string $fieldName): ?array { return PropertyTable::getRow([ 'select' => ['ID', 'FILE_TYPE'], 'filter' => [ '=IBLOCK_ID' => $iblockId, '=ACTIVE' => 'Y', '=PROPERTY_TYPE' => PropertyTable::TYPE_FILE, '=CODE' => $fieldName, ], ]); } public function isAvailable(): bool { return AccessController::getCurrent()->check(ActionDictionary::ACTION_CATALOG_READ); } public function getConfiguration(): Configuration { $configuration = new Configuration(); $acceptedFileTypes = []; if (!empty($this->property['FILE_TYPE'])) { $propertyFileTypes = $this->prepareAcceptedFileTypes($this->property['FILE_TYPE']); if (!empty($propertyFileTypes)) { $acceptedFileTypes = array_intersect(Configuration::getImageExtensions(), $propertyFileTypes); } } if (!empty($acceptedFileTypes)) { $configuration->setAcceptedFileTypes($acceptedFileTypes); } else { $configuration->acceptOnlyImages(); } return $configuration; } private function prepareAcceptedFileTypes(string $fileTypes): array { $imageExtensions = explode(',', $fileTypes); return array_map(static fn ($extension) => '.' . trim($extension), $imageExtensions); } public function canUpload(): bool { return CurrentUser::get()->canDoOperation(ActionDictionary::ACTION_STORE_VIEW); } public function verifyFileOwner(FileOwnershipCollection $files): void { } public function canView(): bool { return false; } public function canRemove(): bool { return false; } } EntitySelector/StoreProvider.php 0000644 00000012232 15150200436 0013035 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Catalog\ProductTable; use Bitrix\Catalog\StoreProductTable; use Bitrix\Catalog\StoreTable; use Bitrix\Iblock\Component\Tools; use Bitrix\Main\Localization\Loc; use Bitrix\Main\Text\HtmlFilter; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; class StoreProvider extends BaseProvider { private const STORE_LIMIT = 10; private const ENTITY_ID = 'store'; public function __construct(array $options = []) { $this->options['searchDisabledStores'] = $options['searchDisabledStores'] ?? true; $this->options['useAddressAsTitle'] = $options['useAddressAsTitle'] ?? true; $this->options['productId'] = (int)$options['productId']; if ($this->options['productId'] > 0) { $product = ProductTable::getRow([ 'filter' => ['=ID' => $this->options['productId']], 'select' => ['MEASURE'] ]); $this->options['measureSymbol'] = $this->getMeasureSymbol((int)$product['MEASURE']); } parent::__construct(); } private function getMeasureSymbol(int $measureId = null): string { $measureResult = \CCatalogMeasure::getList( array('CODE' => 'ASC'), array(), false, array(), array('CODE', 'SYMBOL_RUS', 'SYMBOL_INTL', 'IS_DEFAULT', 'ID') ); $default = ''; while ($measureFields = $measureResult->Fetch()) { $symbol = $measureFields['SYMBOL_RUS'] ?? $measureFields['SYMBOL_INTL']; if ($measureId === (int)$measureFields['ID']) { return HtmlFilter::encode($symbol); } if ($measureFields['IS_DEFAULT'] === 'Y') { $default = $symbol; } } return HtmlFilter::encode($default); } protected function isSearchDisabledStores(): bool { return $this->getOptions()['searchDisabledStores']; } protected function isUseAddressAsTitle(): bool { return $this->getOptions()['useAddressAsTitle']; } protected function getProductId(): int { return $this->getOptions()['productId']; } public function isAvailable(): bool { return $GLOBALS["USER"]->IsAuthorized(); } public function getItems(array $ids): array { return $this->getStores(['ID' => $ids]); } public function getSelectedItems(array $ids): array { return $this->getStores(['=ID' => $ids]); } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $searchQuery->setCacheable(false); $query = $searchQuery->getQuery(); $filter = [ [ '%TITLE' => $query, '%ADDRESS' => $query, 'LOGIC' => 'OR', ] ]; $items = $this->getStores($filter); $dialog->addItems($items); } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItemsCount = count($dialog->getRecentItems()->getEntityItems(self::ENTITY_ID)); if ($recentItemsCount < self::STORE_LIMIT) { foreach ($this->getStores() as $store) { $dialog->addRecentItem($store); } } } private function getStores(array $filter = []): array { $filter['=ACTIVE'] = 'Y'; $storeProducts = []; if ($this->getProductId() > 0) { $storeProductRaw = StoreProductTable::getList([ 'filter' => ['=PRODUCT_ID' => $this->getProductId()], 'select' => ['STORE_ID', 'AMOUNT'], ]); while ($storeProduct = $storeProductRaw->fetch()) { $storeProducts[$storeProduct['STORE_ID']] = $storeProduct['AMOUNT']; } } $storeRaw = StoreTable::getList([ 'select' => ['ID', 'TITLE', 'ADDRESS', 'IMAGE_ID'], 'filter' => $filter, ]); $stores = []; while ($store = $storeRaw->fetch()) { $store['PRODUCT_AMOUNT'] = 0; if (isset($storeProducts[$store['ID']])) { $store['PRODUCT_AMOUNT'] = $storeProducts[$store['ID']]; } $store['IMAGE'] = null; if ($store['IMAGE_ID'] > 0) { $store['IMAGE'] = $this->getImageSource($store['IMAGE_ID']); } $stores[] = $store; } if ($storeProducts) { usort( $stores, static function ($first, $second) { return ($first['PRODUCT_AMOUNT'] > $second['PRODUCT_AMOUNT']) ? -1 : 1; } ); } $items = []; foreach ($stores as $key => $store) { $store['SORT'] = 100 * $key; $items[] = $this->makeItem($store); } return $items; } private function getImageSource(int $id): ?string { if ($id <= 0) { return null; } $file = \CFile::GetFileArray($id); if (!$file) { return null; } return Tools::getImageSrc($file, true) ?: null; } private function makeItem($store): Item { $title = $store['TITLE']; if ($title === '') { $title = ($this->isUseAddressAsTitle()) ? $store['ADDRESS'] : Loc::getMessage('STORE_SELECTOR_EMPTY_TITLE') ; } $item = new Item([ 'id' => $store['ID'], 'sort' => $store['SORT'], 'entityId' => self::ENTITY_ID, 'title' => $title, 'subtitle' => $store['ADDRESS'], 'avatar' => $store['IMAGE'], 'caption' => [ 'text' => $store['PRODUCT_AMOUNT'] > 0 ? $store['PRODUCT_AMOUNT'] . ' ' . $this->getOptions()['measureSymbol'] : '' , 'type' => 'html', ], ]); return $item; } } EntityEditor/StoreDocumentProvider.php 0000644 00000044235 15150200436 0014212 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntityEditor; use Bitrix\Catalog\ContractorTable; use Bitrix\Catalog\StoreDocumentFileTable; use Bitrix\Catalog\StoreDocumentTable; use Bitrix\Currency\CurrencyManager; use Bitrix\Currency\CurrencyTable; use Bitrix\Main; use Bitrix\Main\Engine\CurrentUser; use Bitrix\Main\Loader; use Bitrix\Main\Localization\Loc; use Bitrix\Main\UserTable; use Bitrix\Sale\PriceMaths; use Bitrix\UI\EntityEditor\BaseProvider; use CCatalogStoreDocsElement; use CCurrencyLang; class StoreDocumentProvider extends BaseProvider { protected const DEFAULT_TYPE = StoreDocumentTable::TYPE_ARRIVAL; protected const GUID_PREFIX = 'STORE_DOCUMENT_DETAIL_'; protected const ENTITY_TYPE_NAME = 'store_document'; protected const PATH_TO_USER_PROFILE = '/company/personal/user/#user_id#/'; protected $document; protected $config; private function __construct(array $documentFields, array $config = []) { $this->document = $documentFields; $this->config = $config; } /** * @param array $documentFields * @param array $config * @return static */ public static function createByArray(array $documentFields, array $config = []): self { return new static($documentFields, $config); } /** * @param int $id * @param array $config * @return static */ public static function createById(int $id, array $config = []): self { $provider = new static(['ID' => $id], $config); $provider->loadDocument(); return $provider; } /** * @param string $type * @param array $config * @return static */ public static function createByType(string $type, array $config = []): self { return new static(['DOC_TYPE' => $type], $config); } protected function getDocumentId(): ?int { return $this->document['ID'] ?? null; } protected function getDocumentType(): string { return $this->document['DOC_TYPE'] ?? static::DEFAULT_TYPE; } protected function isNewDocument(): bool { return $this->getDocumentId() === null; } protected function loadDocument(): void { if (!$this->isNewDocument()) { $document = StoreDocumentTable::getById($this->getDocumentId())->fetch(); $this->document = $document ?: []; } } public function getGUID(): string { return static::GUID_PREFIX . $this->getDocumentType(); } public function getEntityId(): ?int { return $this->getDocumentId(); } public function getEntityTypeName(): string { return static::ENTITY_TYPE_NAME; } public function getEntityFields(): array { static $fields = []; $documentType = $this->getDocumentType(); if (!isset($fields[$documentType])) { $documentTypeFields = $this->getDocumentFields(); $fields[$documentType] = $this->getAdditionalFieldKeys($documentTypeFields); } return $fields[$documentType]; } protected function getDocumentFields(): array { return array_merge($this->getDocumentCommonFields(), $this->getDocumentSpecificFields()); } protected function getDocumentCommonFields(): array { return [ [ 'name' => 'ID', 'title' => static::getFieldTitle('ID'), 'type' => 'number', 'editable' => false, 'required' => false, ], [ 'name' => 'TITLE', 'title' => static::getFieldTitle('TITLE'), 'type' => 'text', 'editable' => true, 'required' => false, 'isHeading' => true, 'visibilityPolicy' => 'edit', 'placeholders' => [ 'creation' => $this->getDefaultDocumentTitle(), ], ], [ 'name' => 'DATE_CREATE', 'title' => static::getFieldTitle('DATE_CREATE'), 'type' => 'datetime', 'editable' => false, 'visibilityPolicy' => 'view', ], [ 'name' => 'CREATED_BY', 'title' => static::getFieldTitle('CREATED_BY'), 'type' => 'user', 'editable' => false, ], [ 'name' => 'RESPONSIBLE_ID', 'title' => static::getFieldTitle('RESPONSIBLE_ID'), 'type' => 'user', 'editable' => true, 'required' => true, ], [ 'name' => 'TOTAL_WITH_CURRENCY', 'title' => static::getFieldTitle('TOTAL_WITH_CURRENCY'), 'type' => 'money', 'data' => [ 'largeFormat' => true, 'affectedFields' => ['CURRENCY', 'TOTAL'], 'amount' => 'TOTAL', 'currency' => [ 'name' => 'CURRENCY', 'items' => $this->prepareCurrencyList(), ], 'formatted' => 'FORMATTED_TOTAL', 'formattedWithCurrency' => 'FORMATTED_TOTAL_WITH_CURRENCY', ], 'editable' => in_array($this->getDocumentType(), [StoreDocumentTable::TYPE_ARRIVAL, StoreDocumentTable::TYPE_STORE_ADJUSTMENT]), ], [ 'name' => 'DATE_MODIFY', 'title' => static::getFieldTitle('DATE_MODIFY'), 'type' => 'datetime', 'editable' => false, 'visibilityPolicy' => 'view', ], [ 'name' => 'MODIFIED_BY', 'title' => static::getFieldTitle('MODIFIED_BY'), 'type' => 'user', 'editable' => false, 'visibilityPolicy' => 'view', ], [ 'name' => 'DATE_STATUS', 'title' => static::getFieldTitle('DATE_STATUS'), 'type' => 'datetime', 'editable' => false, 'visibilityPolicy' => 'view', ], [ 'name' => 'STATUS_BY', 'title' => static::getFieldTitle('STATUS_BY'), 'type' => 'user', 'editable' => false, 'visibilityPolicy' => 'view', ], [ 'name' => 'DOCUMENT_PRODUCTS', 'title' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_DOCUMENT_PRODUCTS_' . $this->getDocumentType()) ?: Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_DOCUMENT_PRODUCTS'), 'type' => 'product_row_summary', 'editable' => false, ], ]; } protected function getDocumentSpecificFields(): array { $fields = []; switch ($this->getDocumentType()) { case StoreDocumentTable::TYPE_ARRIVAL: $fields = [ [ 'name' => 'DOC_NUMBER', 'title' => static::getFieldTitle('DOC_NUMBER'), 'type' => 'text', 'editable' => true, 'showAlways' => true, ], [ 'name' => 'DATE_DOCUMENT', 'title' => static::getFieldTitle('DATE_DOCUMENT'), 'type' => 'datetime', 'editable' => true, 'data' => [ 'enableTime' => false, ], ], [ 'name' => 'CONTRACTOR_ID', 'title' => static::getFieldTitle('CONTRACTOR_ID'), 'type' => 'contractor', 'editable' => true, 'required' => true, 'data' => [ 'contractorName' => 'CONTRACTOR_NAME', ], ], [ 'name' => 'ITEMS_ORDER_DATE', 'title' => static::getFieldTitle('ITEMS_ORDER_DATE'), 'type' => 'datetime', 'editable' => true, 'data' => [ 'enableTime' => false, ], ], [ 'name' => 'ITEMS_RECEIVED_DATE', 'title' => static::getFieldTitle('ITEMS_RECEIVED_DATE'), 'type' => 'datetime', 'editable' => true, 'data' => [ 'enableTime' => false, ], ], [ 'name' => 'DOCUMENT_FILES', 'title' => static::getFieldTitle('DOCUMENT_FILES'), 'type' => 'file', 'editable' => true, 'showAlways' => true, 'data' => [ 'multiple' => true, 'maxFileSize' => \CUtil::Unformat(ini_get('upload_max_filesize')), ] ], ]; break; case StoreDocumentTable::TYPE_DEDUCT: $fields = [ [ 'name' => 'DOC_NUMBER', 'title' => static::getFieldTitle('DOC_NUMBER'), 'type' => 'text', 'editable' => true, 'showAlways' => false, ], [ 'name' => 'DATE_DOCUMENT', 'title' => static::getFieldTitle('DATE_DOCUMENT'), 'type' => 'datetime', 'editable' => true, 'showAlways' => false, 'data' => [ 'enableTime' => false, ], ], ]; break; case StoreDocumentTable::TYPE_MOVING: $fields = [ [ 'name' => 'DOC_NUMBER', 'title' => static::getFieldTitle('DOC_NUMBER'), 'type' => 'text', 'editable' => true, 'showAlways' => false, ], [ 'name' => 'DATE_DOCUMENT', 'title' => static::getFieldTitle('DATE_DOCUMENT'), 'type' => 'datetime', 'editable' => true, 'showAlways' => false, 'data' => [ 'enableTime' => false, ], ], ]; break; } return $fields; } protected function getDefaultDocumentTitle(string $documentNumber = '') { return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_TITLE_DEFAULT_NAME_' . $this->getDocumentType(), ['%DOCUMENT_NUMBER%' => $documentNumber]); } protected function getContractorName(): string { if (!empty($this->config['data'])) { $data = $this->config['data']; if (!empty($data['CONTRACTOR_COMPANY'])) { return $data['CONTRACTOR_COMPANY']; } if (!empty($data['CONTRACTOR_PERSON_NAME'])) { return $data['CONTRACTOR_PERSON_NAME']; } } $contractorId = $this->document['CONTRACTOR_ID']; if (!$contractorId) { return ''; } $contractor = ContractorTable::getById($contractorId)->fetch(); if ($contractor['COMPANY']) { return $contractor['COMPANY']; } if ($contractor['PERSON_NAME']) { return $contractor['PERSON_NAME']; } return ''; } protected function getAdditionalFieldKeys($fields): array { $resultFields = []; foreach ($fields as $field) { $fieldName = $field['name']; $fieldType = $field['type']; if ($fieldType === 'user') { $field['data'] = [ 'enableEditInView' => $field['editable'], 'formated' => $fieldName . '_FORMATTED_NAME', 'photoUrl' => $fieldName . '_PHOTO_URL', 'showUrl' => 'PATH_TO_' . $fieldName, 'pathToProfile' => static::PATH_TO_USER_PROFILE, ]; } $resultFields[] = $field; } return $resultFields; } public function getEntityConfig(): array { $sectionElements = [ [ 'name' => 'main', 'title' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_MAIN_SECTION'), 'type' => 'section', 'elements' => $this->getMainSectionElements(), 'data' => [ 'isRemovable' => 'false', ], 'sort' => 100, ], ]; $sectionElements[] = [ 'name' => 'products', 'title' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_PRODUCTS_SECTION'), 'type' => 'section', 'elements' => [ ['name' => 'DOCUMENT_PRODUCTS'], ], 'data' => [ 'isRemovable' => 'false', ], 'sort' => 200, ]; $sectionElements[] = [ 'name' => 'extra', 'title' => Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_EXTRA_SECTION'), 'type' => 'section', 'elements' => [ ['name' => 'RESPONSIBLE_ID'], ], 'data' => [ 'isRemovable' => 'false', ], 'sort' => 300, ]; Main\Type\Collection::sortByColumn($sectionElements, ['sort' => SORT_ASC]); return [ [ 'name' => 'left', 'type' => 'column', 'data' => [ 'width' => 40, ], 'elements' => $sectionElements, ], ]; } public function getMainSectionElements() { switch ($this->getDocumentType()) { case StoreDocumentTable::TYPE_ARRIVAL: return [ ['name' => 'TITLE'], ['name' => 'TOTAL_WITH_CURRENCY'], ['name' => 'CONTRACTOR_ID'], ['name' => 'DOC_NUMBER'], ['name' => 'DATE_DOCUMENT'], ['name' => 'ITEMS_RECEIVED_DATE'], ['name' => 'DOCUMENT_FILES'], ]; case StoreDocumentTable::TYPE_STORE_ADJUSTMENT: return [ ['name' => 'TITLE'], ['name' => 'TOTAL_WITH_CURRENCY'], ]; case StoreDocumentTable::TYPE_MOVING: return [ ['name' => 'TITLE'], ['name' => 'TOTAL_WITH_CURRENCY'], ]; case StoreDocumentTable::TYPE_DEDUCT: return [ ['name' => 'TITLE'], ['name' => 'TOTAL_WITH_CURRENCY'], ]; default: return []; } } public function getEntityData(): array { if ($this->isNewDocument()) { $document = array_fill_keys(array_column($this->getEntityFields(), 'name'), null); $document = array_merge($document, [ 'DOC_TYPE' => $this->document['DOC_TYPE'], 'RESPONSIBLE_ID' => CurrentUser::get()->getId(), ]); } else { $document = $this->document; } $currency = $this->document['CURRENCY']; if (!$currency) { $currency = CurrencyManager::getBaseCurrency(); } if (!isset($document['TOTAL'])) { $document['TOTAL'] = 0; $document['CURRENCY'] = $currency; } $document['FORMATTED_TOTAL'] = CCurrencyLang::CurrencyFormat($document['TOTAL'], $currency, false); $document['FORMATTED_TOTAL_WITH_CURRENCY'] = CCurrencyLang::CurrencyFormat($document['TOTAL'], $currency); if (empty($this->config['skipProducts'])) { $document['DOCUMENT_PRODUCTS'] = $this->getDocumentProductsPreview($document); } if (empty($this->config['skipFiles'])) { $document['DOCUMENT_FILES'] = $this->getDocumentFiles($document); } $dateFields = ['DATE_DOCUMENT', 'ITEMS_ORDER_DATE', 'ITEMS_RECEIVED_DATE']; foreach ($dateFields as $dateField) { if (isset($document[$dateField]) && $document[$dateField] instanceof Main\Type\DateTime) { $document[$dateField] = new Main\Type\Date($document[$dateField]); } } return $this->getAdditionalDocumentData($document); } protected function getDocumentFiles(array $document) { if ($this->isNewDocument()) { return []; } $files = StoreDocumentFileTable::getList(['select' => ['FILE_ID'], 'filter' => ['DOCUMENT_ID' => $this->document['ID']]])->fetchAll(); return array_column($files, 'FILE_ID'); } protected function getDocumentProductsPreview(array $document): array { if (!Loader::includeModule('sale')) { return []; } $documentProducts = []; if (!$this->isNewDocument()) { $documentProductsRes = CCatalogStoreDocsElement::getList( ['ID' => 'ASC'], ['DOC_ID' => $this->getDocumentId()], false, false, ['ELEMENT_ID', 'ELEMENT_NAME', 'AMOUNT', 'PURCHASING_PRICE', 'BASE_PRICE'] ); while ($elementRow = $documentProductsRes->Fetch()) { $documentProducts[] = $elementRow; } } $result = [ 'count' => count($documentProducts), 'total' => [ 'amount' => $document['TOTAL'], 'currency' => $document['CURRENCY'], ], 'items' => [], ]; $priceType = 'PURCHASING_PRICE'; foreach ($documentProducts as $product) { $productSum = PriceMaths::roundPrecision((float)$product[$priceType] * (float)$product['AMOUNT']); $result['items'][] = [ 'PRODUCT_NAME' => $product['ELEMENT_NAME'], 'SUM' => CCurrencyLang::CurrencyFormat($productSum, $document['CURRENCY']), ]; } return $result; } protected function getAdditionalDocumentData(array $document): array { $userFields = []; foreach ($this->getEntityFields() as $field) { $fieldName = $field['name']; $fieldType = $field['type']; if ($fieldType === 'user') { $userId = $document[$field['name']]; if (!$userId && $fieldName === 'CREATED_BY') { $userId = CurrentUser::get()->getId(); } $userFields[$fieldName] = $userId; } } $document['PATH_TO_USER_PROFILE'] = static::PATH_TO_USER_PROFILE; $document['CONTRACTOR_NAME'] = $this->getContractorName(); $uniqueUserIds = array_filter(array_unique(array_values($userFields))); if (!empty($uniqueUserIds) && empty($this->config['skipUsers'])) { $document = $this->getAdditionalUserData($document, $userFields, $this->getUsersInfo($uniqueUserIds)); } elseif(!empty($uniqueUserIds) && !empty($document['USER_INFO'])) { $document = $this->getAdditionalUserData($document, $userFields, $document['USER_INFO']); } return $document; } protected function getUsersInfo(array $userIds): array { $usersInfo = []; $userIds = array_filter(array_unique(array_values($userIds))); if (!empty($userIds)) { $userList = UserTable::getList([ 'filter' => ['=ID' => $userIds], 'select' => [ 'ID', 'LOGIN', 'PERSONAL_PHOTO', 'NAME', 'SECOND_NAME', 'LAST_NAME', 'WORK_POSITION', ], ]); while ($user = $userList->fetch()) { $usersInfo[$user['ID']] = $user; } } return $usersInfo; } protected function getAdditionalUserData(array $document, array $userFields, array $usersInfo): array { foreach ($userFields as $fieldName => $userId) { if (!$userId) { continue; } $user = $usersInfo[$userId]; $document['PATH_TO_' . $fieldName] = \CComponentEngine::MakePathFromTemplate( static::PATH_TO_USER_PROFILE, ['user_id' => $user['ID']] ); $document[$fieldName . '_FORMATTED_NAME'] = \CUser::FormatName( \CSite::GetNameFormat(false), [ 'LOGIN' => $user['LOGIN'], 'NAME' => $user['NAME'], 'LAST_NAME' => $user['LAST_NAME'], 'SECOND_NAME' => $user['SECOND_NAME'], ], true, false ); if ((int)$user['PERSONAL_PHOTO'] > 0) { $fileInfo = \CFile::ResizeImageGet( (int)$user['PERSONAL_PHOTO'], [ 'width' => 60, 'height' => 60, ], BX_RESIZE_IMAGE_EXACT ); if (isset($fileInfo['src'])) { $document[$fieldName . '_PHOTO_URL'] = $fileInfo['src']; } } } return $document; } public function getEntityControllers(): array { return [ [ 'name' => 'PRODUCT_LIST_CONTROLLER', 'type' => 'product_list', 'config' => [], ], [ 'name' => 'DOCUMENT_CARD_CONTROLLER', 'type' => 'document_card', 'config' => [], ], ]; } public function isReadOnly(): bool { return isset($this->document['STATUS']) && $this->document['STATUS'] === 'Y'; } protected function prepareCurrencyList(): array { $result = []; $existingCurrencies = CurrencyTable::getList([ 'select' => ['CURRENCY', 'FULL_NAME' => 'CURRENT_LANG_FORMAT.FULL_NAME', 'SORT'], 'order' => ['SORT' => 'ASC', 'CURRENCY' => 'ASC'], ])->fetchAll(); foreach ($existingCurrencies as $currency) { $result[] = [ 'NAME' => $currency['FULL_NAME'], 'VALUE' => $currency['CURRENCY'], ]; } return $result; } public static function getFieldTitle($fieldName) { switch ($fieldName) { case 'ID': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_ID'); case 'TITLE': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_TITLE_ID'); case 'TOTAL_WITH_CURRENCY': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_TOTAL'); case 'ITEMS_ORDER_DATE': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_ITEMS_ORDER_DATE_DOCUMENT'); case 'ITEMS_RECEIVED_DATE': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_ITEMS_RECEIVED_DATE_DOCUMENT'); case 'DOCUMENT_FILES': return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_DOCUMENT_FILES_2'); default: return Loc::getMessage('CATALOG_STORE_DOCUMENT_DETAIL_FIELD_' . $fieldName); } } } ViewedProducts/Repository.php 0000644 00000003063 15152315315 0012406 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\ViewedProducts; use Bitrix\Catalog; use Bitrix\Main\Loader; /** * Class Repository * * @package Bitrix\Catalog\v2\Integration\UI\ViewedProducts * * * !!! This API is in alpha stage and is not stable. This is subject to change at any time without notice. * @internal */ final class Repository { /** @var Repository */ private static $instance; public const DEFAULT_GET_LIST_LIMIT = 10; /** * @return Repository */ public static function getInstance(): Repository { if (is_null(static::$instance)) { static::$instance = new static(); } return static::$instance; } /** * @param array $options * @return Catalog\v2\Sku\BaseSku[] */ public function getList(array $options = []): array { $result = []; if (!Loader::includeModule('sale')) { return $result; } $limit = $options['limit'] ?? self::DEFAULT_GET_LIST_LIMIT; $viewedProductsList = Catalog\CatalogViewedProductTable::getList( [ 'filter' => [ '=FUSER_ID' => (int)\CSaleBasket::GetBasketUserID( !Catalog\Product\Basket::isNotCrawler() ), '=SITE_ID' => SITE_ID, ], 'select' => [ 'ELEMENT_ID', 'PRODUCT_ID', ], 'order' => [ 'DATE_VISIT' => 'DESC', ], 'limit' => $limit, ] ); while ($viewedProduct = $viewedProductsList->fetch()) { $sku = Catalog\v2\IoC\ServiceContainer::getRepositoryFacade() ->loadVariation((int)$viewedProduct['PRODUCT_ID']) ; if (!$sku) { continue; } $result[] = $sku; } return $result; } } EntitySelector/SectionProvider.php 0000644 00000006510 15152315315 0013354 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; class SectionProvider extends BaseProvider { private const SECTION_LIMIT = 20; private const SECTION_ENTITY_ID = 'section'; public function __construct(array $options = []) { parent::__construct(); $this->options = $options; } public function isAvailable(): bool { return $GLOBALS['USER']->isAuthorized(); } public function getItems(array $ids): array { $items = []; $filter = !empty($ids) ? ['ID' => $ids] : []; foreach ($this->getActiveSections($filter) as $section) { $items[] = $this->makeItem($section); } return $items; } public function getSelectedItems(array $ids): array { $selectedItems = []; $filter = !empty($ids) ? ['ID' => $ids] : []; foreach ($this->getSections($filter) as $section) { $selectedItems[] = $this->makeItem($section); } return $selectedItems; } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItemsCount = count($dialog->getRecentItems()->getEntityItems(self::SECTION_ENTITY_ID)); if ($recentItemsCount < self::SECTION_LIMIT) { foreach ($this->getActiveSections() as $section) { $dialog->addRecentItem( $this->makeItem($section) ); } } } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $filter = []; $query = $searchQuery->getQuery(); if ($query !== '') { $filter['%NAME'] = $query; } foreach ($this->getActiveSections($filter) as $section) { $dialog->addItem( $this->makeItem($section) ); } if ($dialog->getItemCollection()->count() >= self::SECTION_LIMIT) { $searchQuery->setCacheable(false); } } protected function getActiveSections(array $additionalFilter = []): array { return $this->getSections(array_merge(['=ACTIVE' => 'Y'], $additionalFilter)); } protected function getSections(array $additionalFilter = []): array { $sections = []; $filter = $this->getDefaultFilter(); if (!empty($additionalFilter)) { $filter = array_merge($filter, $additionalFilter); } if (!empty($filter)) { $sectionData = \CIBlockSection::GetList( [], $filter, false, ['ID', 'NAME', 'PICTURE'], [ 'nTopCount' => self::SECTION_LIMIT, ] ); while ($section = $sectionData->fetch()) { if (!empty($section['PICTURE'])) { $section['PICTURE'] = \CFile::resizeImageGet( $section['PICTURE'], [ 'width' => 100, 'height' => 100, ], BX_RESIZE_IMAGE_EXACT, false )['src']; } $sections[] = $section; } } return $sections; } protected function makeItem(array $section): Item { return new Item([ 'id' => $section['ID'], 'entityId' => self::SECTION_ENTITY_ID, 'title' => $section['NAME'], 'avatar' => $section['PICTURE'], ]); } private function getDefaultFilter(): array { $filter = []; $iblockId = (int)($this->getOptions()['iblockId'] ?? 0); if (!empty($iblockId)) { $filter['IBLOCK_ID'] = $iblockId; } return $filter; } } EntitySelector/BarcodeProvider.php 0000644 00000004113 15152315315 0013304 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\Catalog\StoreBarcodeTable; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; class BarcodeProvider extends ProductProvider { protected const ENTITY_ID = 'barcode'; public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $searchQuery->setCacheable(false); $productIds = $this->getProductIdsByBarcode($searchQuery->getQuery()); if (!$productIds) { return; } $productIds = array_unique($productIds); $products = $this->getProductsByIds($productIds); $elementMap = []; foreach ($products as $key => $product) { $elementMap[$product['ID']][] = $key; } if (!empty($products)) { $barcodeRaw = \Bitrix\Catalog\StoreBarcodeTable::getList([ 'filter' => [ '=PRODUCT_ID' => $productIds, 'BARCODE' => $searchQuery->getQuery() . '%' ], 'select' => ['BARCODE', 'PRODUCT_ID'] ]); while ($barcode = $barcodeRaw->fetch()) { $productId = $barcode['PRODUCT_ID']; if (!isset($elementMap[$productId])) { continue; } foreach ($elementMap[$productId] as $key) { $products[$key]['BARCODE'] = $barcode['BARCODE']; } } foreach ($products as $product) { $dialog->addItem( $this->makeItem($product) ); } } } public function handleBeforeItemSave(Item $item): void { $item->setSaveable(false); } private function getProductIdsByBarcode(string $barcodeString = ''): array { $barcodes = []; $elementRaw = \CIBlockElement::GetList( [], [ 'ACTIVE' => 'Y', 'CHECK_PERMISSIONS' => 'Y', 'PRODUCT_BARCODE' => $barcodeString . '%', ], false, false, ['ID'] ); while ($element = $elementRaw->Fetch()) { $barcodes[] = $element['ID']; } return $barcodes; } } EntitySelector/ContractorProvider.php 0000644 00000005203 15152315315 0014064 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Catalog\ContractorTable; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; class ContractorProvider extends BaseProvider { private const MAX_ITEMS_IN_RECENT = 10; private const ENTITY_ID = 'contractor'; public function __construct(array $options = []) { parent::__construct(); } public function isAvailable(): bool { return $GLOBALS["USER"]->IsAuthorized(); } public function getItems(array $ids): array { return $this->getContractorItemsByFilter(['ID' => $ids]); } public function getSelectedItems(array $ids): array { return $this->getContractorItemsByFilter(['ID' => $ids]); } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $searchQuery->setCacheable(false); $query = $searchQuery->getQuery(); $filter = [ 'LOGIC' => 'OR', ['%PERSON_NAME' => $query], ['%COMPANY' => $query], ]; $items = $this->getContractorItemsByFilter($filter); $dialog->addItems($items); } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItemsCount = count($dialog->getRecentItems()->getEntityItems(self::ENTITY_ID)); if ($recentItemsCount < self::MAX_ITEMS_IN_RECENT) { $amountToSelect = self::MAX_ITEMS_IN_RECENT - $recentItemsCount; $excludedIds = array_keys($dialog->getRecentItems()->getEntityItems(self::ENTITY_ID)); $contractors = ContractorTable::getList([ 'select' => ['ID', 'PERSON_TYPE', 'PERSON_NAME', 'COMPANY'], 'order' => ['ID' => 'ASC'], 'filter' => ['!ID' => $excludedIds], 'limit' => $amountToSelect, ])->fetchAll(); foreach ($contractors as $contractor) { $dialog->addRecentItem($this->makeItem($contractor)); } } } private function getContractorItemsByFilter($filter) { $contractors = ContractorTable::getList([ 'select' => ['ID', 'PERSON_TYPE', 'PERSON_NAME', 'COMPANY'], 'filter' => $filter, ])->fetchAll(); $items = []; foreach ($contractors as $contractor) { $items[] = $this->makeItem($contractor); } return $items; } private function makeItem($contractor) { if ((int)$contractor['PERSON_TYPE'] === CONTRACTOR_INDIVIDUAL) { $title = $contractor['PERSON_NAME']; } else { $title = $contractor['COMPANY']; } return new Item([ 'id' => $contractor['ID'], 'entityId' => self::ENTITY_ID, 'title' => $title, ]); } } EntitySelector/ProductProvider.php 0000644 00000047726 15152315315 0013406 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Catalog\PriceTable; use Bitrix\Catalog\Product\PropertyCatalogFeature; use Bitrix\Catalog\ProductTable; use Bitrix\Catalog\v2\Iblock\IblockInfo; use Bitrix\Catalog\v2\IoC\ServiceContainer; use Bitrix\Iblock\Component\Tools; use Bitrix\Iblock\PropertyTable; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\RecentItem; use Bitrix\UI\EntitySelector\SearchQuery; class ProductProvider extends BaseProvider { protected const PRODUCT_LIMIT = 20; protected const ENTITY_ID = 'product'; public function __construct(array $options = []) { parent::__construct(); $this->options['iblockId'] = (int)($options['iblockId'] ?? 0); $this->options['basePriceId'] = (int)($options['basePriceId'] ?? 0); $this->options['currency'] = $options['currency'] ; $this->options['restrictedProductTypes'] = is_array($options['restrictedProductTypes']) ? $options['restrictedProductTypes'] : null ; $this->options['showPriceInCaption'] = (bool)($options['showPriceInCaption'] ?? true); } public function isAvailable(): bool { global $USER; if ( !$USER->isAuthorized() || !($USER->canDoOperation('catalog_read') || $USER->canDoOperation('catalog_view')) ) { return false; } if ($this->getIblockId() <= 0 || !$this->getIblockInfo()) { return false; } return true; } public function getItems(array $ids): array { $items = []; foreach ($this->getProductsByIds($ids) as $product) { $items[] = $this->makeItem($product); } return $items; } public function getSelectedItems(array $ids): array { return $this->getItems($ids); } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItems = $dialog->getRecentItems()->getEntityItems(self::ENTITY_ID); $recentItemsCount = count($recentItems); if ($this->options['restrictedProductTypes'] !== null && $recentItemsCount > 0) { $ids = []; foreach ($recentItems as $recentItem) { $ids[] = $recentItem->getId(); } $products = $this->getProductsByIds($ids); /** @var RecentItem $recentItem */ foreach ($recentItems as $recentItem) { if (!isset($products[$recentItem->getId()])) { $recentItem->setAvailable(false); $recentItemsCount--; } } } $recentItemsCount = count($dialog->getRecentItems()->getEntityItems(self::ENTITY_ID)); if ($recentItemsCount < self::PRODUCT_LIMIT) { foreach ($this->getProducts() as $product) { $dialog->addRecentItem($this->makeItem($product)); } } } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $searchQuery->setCacheable(false); $products = $this->getProductsBySearchString($searchQuery->getQuery()); if (!empty($products)) { foreach ($products as $product) { $dialog->addItem( $this->makeItem($product) ); } if ($this->shouldDisableCache($products)) { $searchQuery->setCacheable(false); } } } protected function makeItem(array $product): Item { $customData = array_filter(array_intersect_key($product, [ 'SKU_PROPERTIES' => true, 'SEARCH_PROPERTIES' => true, 'PREVIEW_TEXT' => true, 'DETAIL_TEXT' => true, 'PARENT_NAME' => true, 'PARENT_SEARCH_PROPERTIES' => true, 'PARENT_PREVIEW_TEXT' => true, 'PARENT_DETAIL_TEXT' => true, 'BARCODE' => true, ])); return new Item([ 'id' => $product['ID'], 'entityId' => static::ENTITY_ID, 'title' => $product['NAME'], 'supertitle' => $product['SKU_PROPERTIES'], 'subtitle' => $this->getSubtitle($product), 'caption' => $this->getCaption($product), 'avatar' => $product['IMAGE'], 'customData' => $customData, ]); } protected function getSubtitle(array $product): string { return $product['BARCODE'] ?? ''; } protected function getCaption(array $product): array { if (!$this->shouldDisplayPriceInCaption()) { return []; } return [ 'text' => $product['PRICE'], 'type' => 'html', ]; } protected function getIblockId() { return $this->getOptions()['iblockId']; } private function getBasePriceId() { return $this->getOptions()['basePriceId']; } private function getCurrency() { return $this->getOptions()['currency']; } private function shouldDisplayPriceInCaption() { return $this->getOptions()['showPriceInCaption']; } protected function getIblockInfo(): ?IblockInfo { static $iblockInfo = null; if ($iblockInfo === null) { $iblockInfo = ServiceContainer::getIblockInfo($this->getIblockId()); } return $iblockInfo; } private function getImageSource(int $id): ?string { if ($id <= 0) { return null; } $file = \CFile::GetFileArray($id); if (!$file) { return null; } return Tools::getImageSrc($file, true) ?: null; } protected function getProductsByIds(array $ids): array { [$productIds, $offerIds] = $this->separateOffersFromProducts($ids); $products = $this->getProducts([ 'filter' => ['=ID' => $productIds], 'offer_filter' => ['=ID' => $offerIds], 'sort' => [], ]); // sort $products by $productIds return $this->sortProductsByIds($products, $productIds); } private function separateOffersFromProducts(array $ids): array { $iblockInfo = $this->getIblockInfo(); if (!$iblockInfo) { return [[], []]; } $productIds = $ids; $offerIds = []; if ($iblockInfo->canHaveSku()) { $productList = \CCatalogSku::getProductList($ids, $iblockInfo->getSkuIblockId()); if (!empty($productList)) { $productIds = []; $counter = 0; foreach ($ids as $id) { if ($counter >= self::PRODUCT_LIMIT) { break; } if (isset($productList[$id])) { $productId = $productList[$id]['ID']; if (isset($productIds[$productId])) { continue; } $offerIds[] = $id; $productIds[$productId] = $productId; } else { $productIds[$id] = $id; } $counter++; } $productIds = array_values($productIds); } } return [$productIds, $offerIds]; } private function sortProductsByIds(array $products, array $ids): array { $sorted = []; foreach ($ids as $id) { if (isset($products[$id])) { $sorted[$id] = $products[$id]; } } return $sorted; } protected function getProductsBySearchString(string $searchString = ''): array { $iblockInfo = $this->getIblockInfo(); if (!$iblockInfo) { return []; } $productFilter = []; $offerFilter = []; if ($searchString !== '') { $simpleProductFilter = [ [ 'LOGIC' => 'OR', '*SEARCHABLE_CONTENT' => $searchString, 'PRODUCT_BARCODE' => $searchString . '%', ] ]; if ($iblockInfo->canHaveSku()) { $productFilter[] = [ 'LOGIC' => 'OR', '*SEARCHABLE_CONTENT' => $searchString, '=ID' => \CIBlockElement::SubQuery('PROPERTY_' . $iblockInfo->getSkuPropertyId(), [ 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', 'ACTIVE' => 'Y', 'ACTIVE_DATE' => 'Y', 'IBLOCK_ID' => $iblockInfo->getSkuIblockId(), '*SEARCHABLE_CONTENT' => $searchString, ]), 'PRODUCT_BARCODE' => $searchString . '%', ]; $offerFilter = $simpleProductFilter; } else { $productFilter[] = $simpleProductFilter; } } return $this->getProducts([ 'filter' => $productFilter, 'offer_filter' => $offerFilter, 'searchString' => $searchString, ]); } protected function getProducts(array $parameters = []): array { $iblockInfo = $this->getIblockInfo(); if (!$iblockInfo) { return []; } $productFilter = (array)($parameters['filter'] ?? []); $offerFilter = (array)($parameters['offer_filter'] ?? []); $shouldLoadOffers = (bool)($parameters['load_offers'] ?? true); $additionalProductFilter = ['IBLOCK_ID' => $iblockInfo->getProductIblockId()]; if ($this->options['restrictedProductTypes'] !== null) { $filteredTypes = array_intersect( $this->options['restrictedProductTypes'], [ \Bitrix\Catalog\ProductTable::TYPE_PRODUCT, \Bitrix\Catalog\ProductTable::TYPE_SET, \Bitrix\Catalog\ProductTable::TYPE_SKU, \Bitrix\Catalog\ProductTable::TYPE_OFFER, \Bitrix\Catalog\ProductTable::TYPE_FREE_OFFER, \Bitrix\Catalog\ProductTable::TYPE_EMPTY_SKU, ] ); if (count($filteredTypes) > 0) { $additionalProductFilter['!=TYPE'] = $filteredTypes; } } $products = $this->loadElements([ 'filter' => array_merge($productFilter, $additionalProductFilter), 'limit' => self::PRODUCT_LIMIT, ]); if (empty($products)) { return []; } $products = $this->loadProperties($products, $iblockInfo->getProductIblockId(), $iblockInfo); if ($shouldLoadOffers && $iblockInfo->canHaveSku()) { $products = $this->loadOffers($products, $iblockInfo, $offerFilter); } $products = $this->loadPrices($products); if ($parameters['searchString']) { $products = $this->loadBarcodes($products, $parameters['searchString']); } return $products; } private function loadElements(array $parameters = []): array { $elements = []; $additionalFilter = (array)($parameters['filter'] ?? []); $limit = (int)($parameters['limit'] ?? 0); $filter = [ 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', 'ACTIVE' => 'Y', 'ACTIVE_DATE' => 'Y', ]; $selectFields = array_filter(array_unique(array_merge( [ 'ID', 'NAME', 'IBLOCK_ID', 'TYPE', 'PREVIEW_PICTURE', 'DETAIL_PICTURE', 'PREVIEW_TEXT', 'PREVIEW_TEXT_TYPE', 'DETAIL_TEXT', 'DETAIL_TEXT_TYPE', ], array_keys($additionalFilter) ))); $navParams = false; if ($limit > 0) { $navParams = [ 'nTopCount' => $limit, ]; } $elementIterator = \CIBlockElement::GetList( ['ID' => 'DESC'], array_merge($filter, $additionalFilter), false, $navParams, $selectFields ); while ($element = $elementIterator->Fetch()) { $element['ID'] = (int)$element['ID']; $element['IBLOCK_ID'] = (int)$element['IBLOCK_ID']; $element['TYPE'] = (int)$element['TYPE']; $element['IMAGE'] = null; $element['PRICE'] = null; $element['SKU_PROPERTIES'] = null; if (!empty($element['PREVIEW_PICTURE'])) { $element['IMAGE'] = $this->getImageSource((int)$element['PREVIEW_PICTURE']); } if (empty($element['IMAGE']) && !empty($element['DETAIL_PICTURE'])) { $element['IMAGE'] = $this->getImageSource((int)$element['DETAIL_PICTURE']); } if (!empty($element['PREVIEW_TEXT']) && $element['PREVIEW_TEXT_TYPE'] === 'html') { $element['PREVIEW_TEXT'] = HTMLToTxt($element['PREVIEW_TEXT']); } if (!empty($element['DETAIL_TEXT']) && $element['DETAIL_TEXT_TYPE'] === 'html') { $element['DETAIL_TEXT'] = HTMLToTxt($element['DETAIL_TEXT']); } $elements[$element['ID']] = $element; } return $elements; } private function loadOffers(array $products, IblockInfo $iblockInfo, array $additionalFilter = []): array { $productsWithOffers = $this->filterProductsWithOffers($products); if (empty($productsWithOffers)) { return $products; } $skuPropertyId = 'PROPERTY_' . $iblockInfo->getSkuPropertyId(); $offerFilter = [ 'IBLOCK_ID' => $iblockInfo->getSkuIblockId(), $skuPropertyId => array_keys($productsWithOffers), ]; if (!empty($additionalFilter)) { $offerFilter = array_merge($offerFilter, $additionalFilter); } // first - load offers with coincidence in searchable content or by offer ids $offers = $this->loadElements(['filter' => $offerFilter]); $offers = array_column($offers, null, $skuPropertyId . '_VALUE'); $productsStillWithoutOffers = array_diff_key($productsWithOffers, $offers); if (!empty($productsStillWithoutOffers)) { // second - load any offer for product if have no coincidences in searchable content $additionalOffers = $this->loadElements([ 'filter' => [ 'IBLOCK_ID' => $iblockInfo->getSkuIblockId(), $skuPropertyId => array_keys($productsStillWithoutOffers), ], ]); $additionalOffers = array_column($additionalOffers, null, $skuPropertyId . '_VALUE'); $offers = array_merge($offers, $additionalOffers); } if (!empty($offers)) { $offers = array_column($offers, null, 'ID'); $offers = $this->loadProperties($offers, $iblockInfo->getSkuIblockId(), $iblockInfo); $products = $this->matchProductOffers($products, $offers, $skuPropertyId . '_VALUE'); } return $products; } private function loadPrices(array $elements): array { if (empty($elements)) { return []; } $variationToProductMap = []; foreach ($elements as $id => $element) { $variationToProductMap[$element['ID']] = $id; } $priceTableResult = PriceTable::getList([ 'select' => ['PRICE', 'CURRENCY', 'PRODUCT_ID'], 'filter' => [ 'PRODUCT_ID' => array_keys($variationToProductMap), '=CATALOG_GROUP_ID' => $this->getBasePriceId(), [ 'LOGIC' => 'OR', '<=QUANTITY_FROM' => 1, '=QUANTITY_FROM' => null, ], [ 'LOGIC' => 'OR', '>=QUANTITY_TO' => 1, '=QUANTITY_TO' => null, ], ], ]); while ($price = $priceTableResult->fetch()) { $productId = $variationToProductMap[$price['PRODUCT_ID']]; $priceValue = $price['PRICE']; $currency = $price['CURRENCY']; if (!empty($this->getCurrency()) && $this->getCurrency() !== $currency) { $priceValue = \CCurrencyRates::ConvertCurrency($priceValue, $currency, $this->getCurrency()); $currency = $this->getCurrency(); } $formattedPrice = \CCurrencyLang::CurrencyFormat($priceValue, $currency, true); $elements[$productId]['PRICE'] = $formattedPrice; } return $elements; } private function loadBarcodes(array $elements, string $searchString): array { if (empty($elements)) { return []; } $variationToProductMap = []; foreach ($elements as $id => $element) { $element['BARCODE'] = null; $variationToProductMap[$element['ID']] = $id; } $variationIds = array_keys($variationToProductMap); if (empty($variationIds)) { return $elements; } $barcodeRaw = \Bitrix\Catalog\StoreBarcodeTable::getList([ 'filter' => [ '=PRODUCT_ID' => $variationIds, 'BARCODE' => $searchString . '%' ], 'select' => ['BARCODE', 'PRODUCT_ID'] ]); while ($barcode = $barcodeRaw->fetch()) { $variationId = $barcode['PRODUCT_ID']; $productId = $variationToProductMap[$variationId]; if (!isset($productId)) { continue; } $elements[$productId]['BARCODE'] = $barcode['BARCODE']; } return $elements; } private function matchProductOffers(array $products, array $offers, string $productLinkProperty): array { foreach ($offers as $offer) { $productId = $offer[$productLinkProperty] ?? null; if ($productId && isset($products[$productId])) { $fieldsToSafelyMerge = [ 'ID', 'NAME', 'PREVIEW_PICTURE', 'DETAIL_PICTURE', 'PREVIEW_TEXT', 'DETAIL_TEXT', 'SEARCH_PROPERTIES', ]; foreach ($fieldsToSafelyMerge as $field) { $products[$productId]['PARENT_' . $field] = $products[$productId][$field] ?? null; $products[$productId][$field] = $offer[$field] ?? null; } if (!empty($offer['IMAGE'])) { $products[$productId]['IMAGE'] = $offer['IMAGE']; } if (!empty($offer['SKU_PROPERTIES'])) { $products[$productId]['SKU_PROPERTIES'] = $offer['SKU_PROPERTIES']; } } } return $products; } private function filterProductsWithOffers(array $products): array { return array_filter($products, static function ($item) { return $item['TYPE'] === ProductTable::TYPE_SKU; }); } private function loadProperties(array $elements, int $iblockId, IblockInfo $iblockInfo): array { if (empty($elements)) { return []; } $propertyIds = []; $skuTreeProperties = null; if ($iblockInfo->getSkuIblockId() === $iblockId) { $skuTreeProperties = array_map( 'intval', PropertyCatalogFeature::getOfferTreePropertyCodes($iblockId) ?? [] ); if (!empty($skuTreeProperties)) { $propertyIds = array_merge($propertyIds, $skuTreeProperties); } } $morePhotoId = $this->getMorePhotoPropertyId($iblockId); if ($morePhotoId) { $propertyIds[] = $morePhotoId; } $searchProperties = $this->getSearchPropertyIds($iblockId); if (!empty($searchProperties)) { $propertyIds = array_merge($propertyIds, $searchProperties); } if (empty($propertyIds)) { return $elements; } $elementPropertyValues = array_fill_keys(array_keys($elements), []); $offersFilter = [ 'IBLOCK_ID' => $iblockId, 'ID' => array_column($elements, 'ID'), ]; $propertyFilter = [ 'ID' => array_unique($propertyIds), ]; \CIBlockElement::GetPropertyValuesArray($elementPropertyValues, $iblockId, $offersFilter, $propertyFilter); foreach ($elementPropertyValues as $elementId => $elementProperties) { if (empty($elementProperties)) { continue; } $currentElement =& $elements[$elementId]; foreach ($elementProperties as $property) { $propertyId = (int)$property['ID']; if ($propertyId === $morePhotoId) { if (empty($currentElement['IMAGE'])) { $propertyValue = is_array($property['VALUE']) ? reset($property['VALUE']) : $property['VALUE']; if ((int)$propertyValue > 0) { $currentElement['IMAGE'] = $this->getImageSource((int)$propertyValue); } } } else { $isArray = is_array($property['~VALUE']); if ( ($isArray && !empty($property['~VALUE'])) || (!$isArray && (string)$property['~VALUE'] !== '') ) { $propertyValue = $this->getPropertyDisplayValue($property); if ($propertyValue !== '') { if ($skuTreeProperties !== null && in_array($propertyId, $skuTreeProperties, true)) { $currentElement['SKU_PROPERTIES'][$propertyId] = $propertyValue; } else { $currentElement['SEARCH_PROPERTIES'][$propertyId] = $propertyValue; } } } } } $currentElement['SKU_PROPERTIES'] = !empty($currentElement['SKU_PROPERTIES']) ? implode(', ', $currentElement['SKU_PROPERTIES']) : null; $currentElement['SEARCH_PROPERTIES'] = !empty($currentElement['SEARCH_PROPERTIES']) ? implode(', ', $currentElement['SEARCH_PROPERTIES']) : null; } unset($currentElement); return $elements; } private function getPropertyDisplayValue(array $property): string { if (!empty($property['USER_TYPE'])) { $userType = \CIBlockProperty::GetUserType($property['USER_TYPE']); $searchMethod = $userType['GetSearchContent'] ?? null; if ($searchMethod && is_callable($searchMethod)) { $value = $searchMethod($property, ['VALUE' => $property['~VALUE']], []); } else { $value = ''; } } else { $value = $property['~VALUE'] ?? ''; } if (is_array($value)) { $value = implode(', ', $value); } $value = trim((string)$value); return $value; } private function getMorePhotoPropertyId(int $iblockId): ?int { $iterator = PropertyTable::getList([ 'select' => ['ID'], 'filter' => [ '=IBLOCK_ID' => $iblockId, '=CODE' => \CIBlockPropertyTools::CODE_MORE_PHOTO, '=ACTIVE' => 'Y', ], ]); if ($row = $iterator->fetch()) { return (int)$row['ID']; } return null; } private function getSearchPropertyIds(int $iblockId): array { $properties = PropertyTable::getList([ 'select' => ['ID'], 'filter' => [ '=IBLOCK_ID' => $iblockId, '=ACTIVE' => 'Y', '=SEARCHABLE' => 'Y', '@PROPERTY_TYPE' => [ PropertyTable::TYPE_STRING, PropertyTable::TYPE_NUMBER, PropertyTable::TYPE_LIST, ], ], 'order' => ['SORT' => 'ASC', 'ID' => 'ASC'], ])->fetchAll() ; return array_map('intval', array_column($properties, 'ID')); } protected function shouldDisableCache(array $products): bool { if (count($products) >= self::PRODUCT_LIMIT) { return true; } foreach ($products as $product) { if (!empty($product['PARENT_ID'])) { return true; } } return false; } } EntitySelector/IblockElementProvider.php 0000644 00000011105 15152315315 0014461 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Iblock\Component\Tools; use Bitrix\Iblock\PropertyTable; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\SearchQuery; class IblockElementProvider extends BaseProvider { protected const ENTITY_ID = 'iblock-element'; private const ELEMENTS_LIMIT = 20; public function __construct(array $options = []) { parent::__construct(); $this->options = $options; } public function isAvailable(): bool { return $GLOBALS['USER']->isAuthorized(); } public function getItems(array $ids): array { $items = []; $filter = !empty($ids) ? ['ID' => $ids] : []; foreach ($this->getElements($filter) as $element) { $items[] = $this->makeItem($element); } return $items; } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItems = $dialog->getRecentItems()->getEntityItems(self::ENTITY_ID); $recentItemsCount = count($recentItems); if ($recentItemsCount < self::ELEMENTS_LIMIT) { foreach ($this->getElements() as $element) { $dialog->addRecentItem($this->makeItem($element)); } } } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $filter = []; $query = $searchQuery->getQuery(); if ($query !== '') { $filter = $this->getQueryFilter($query); } $elements = $this->getElements($filter); foreach ($elements as $element) { $dialog->addItem( $this->makeItem($element) ); } } public function getPreselectedItems(array $ids): array { return $this->getItems($ids); } private function getQueryFilter(string $query): array { return [ '*SEARCHABLE_CONTENT' => $query, ]; } protected function getElements(array $additionalFilter = []): array { $elements = []; $filter = $this->getDefaultFilter(); if (!empty($additionalFilter)) { $filter = array_merge($filter, $additionalFilter); } $navParams = ['nTopCount' => self::ELEMENTS_LIMIT]; $selectFields = [ 'ID', 'NAME', 'DETAIL_TEXT', 'PREVIEW_PICTURE', 'IBLOCK_ID', 'XML_ID', ]; if (!empty($filter)) { $elementData = \CIBlockElement::GetList( [], $filter, false, $navParams, $selectFields ); while ($element = $elementData->fetch()) { if (empty($element['PREVIEW_PICTURE'])) { $element['PREVIEW_PICTURE'] = $this->getElementImage($element); } $elements[] = $element; } } return $elements; } protected function makeItem(array $element): Item { $itemParams = [ 'id' => $element['ID'] ?? null, 'entityId' => self::ENTITY_ID, 'title' => $element['NAME'] ?? null, 'subtitle' => $element['ID'] ?? null, 'description' => $element['DETAIL_TEXT'] ?? null, 'avatar' => $element['PREVIEW_PICTURE'] ?? null, 'customData' => [ 'xmlId' => $element['XML_ID'] ?? null, ], ]; return new Item($itemParams); } private function getElementImage(array $element): ?string { $iblockId = $element['IBLOCK_ID'] ?? null; if (!$iblockId) { return ''; } $photoPropertyId = $this->getMorePhotoPropertyId($iblockId); if (!$photoPropertyId) { return ''; } $propertyFilter = [ 'ID' => $photoPropertyId, ]; $result = \CIBlockElement::GetProperty($iblockId, $element['ID'], 'sort', 'asc', $propertyFilter)->Fetch(); if (empty($result['VALUE'])) { return ''; } if (is_array($result['VALUE'])) { $imageId = (int)$result['VALUE'][0]; } else { $imageId = (int)$result['VALUE']; } return $this->getImageSource($imageId); } private function getDefaultFilter() { $filter = [ 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', ]; $iblockId = (int)($this->getOption('iblockId', 0)); if (!empty($iblockId)) { $filter['IBLOCK_ID'] = $iblockId; } return $filter; } private function getMorePhotoPropertyId(int $iblockId): ?int { $iterator = PropertyTable::getList([ 'select' => ['ID'], 'filter' => [ '=IBLOCK_ID' => $iblockId, '=CODE' => \CIBlockPropertyTools::CODE_MORE_PHOTO, '=ACTIVE' => 'Y', ], ]); if ($row = $iterator->fetch()) { return (int)$row['ID']; } return null; } private function getImageSource(int $id): ?string { if ($id <= 0) { return null; } $file = \CFile::GetFileArray($id); if (!$file) { return null; } return Tools::getImageSrc($file, false) ?: null; } } EntitySelector/IblockElementXmlProvider.php 0000644 00000002412 15152315315 0015143 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; class IblockElementXmlProvider extends IblockElementProvider { protected const ENTITY_ID = 'iblock-element-xml'; public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $filter = []; $query = $searchQuery->getQuery(); if ($query !== '') { $filter = $this->getQueryFilter($query); } $elements = $this->getElements($filter); foreach ($elements as $element) { $dialog->addItem( $this->makeItem($element) ); } } private function getQueryFilter(string $query): array { return [ [ 'LOGIC' => 'OR', '%XML_ID' => $query, '*SEARCHABLE_CONTENT' => $query, ], ]; } protected function makeItem(array $element, string $propertyType = ''): Item { $itemParams = [ 'id' => $element['ID'] ?? null, 'entityId' => self::ENTITY_ID, 'title' => $element['NAME'] ?? null, 'subtitle' => $element['XML_ID'] ?? null, 'description' => $element['DETAIL_TEXT'] ?? null, 'avatar' => $element['PREVIEW_PICTURE'] ?? null, 'customData' => [ 'xmlId' => $element['XML_ID'] ?? null, ], ]; return new Item($itemParams); } } EntitySelector/ProductVariationProvider.php 0000644 00000003446 15152315315 0015252 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\UI\EntitySelector\Dialog; class ProductVariationProvider extends ProductProvider { protected const ENTITY_ID = 'product_variation'; public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); } protected function getProductsBySearchString(string $searchString = ''): array { if (trim($searchString) === '') { return []; } $iblockInfo = $this->getIblockInfo(); if (!$iblockInfo) { return []; } $productFilter = [ [ 'LOGIC' => 'OR', '*SEARCHABLE_CONTENT' => $searchString, 'PRODUCT_BARCODE' => $searchString . '%', ] ]; $products = $this->getProducts([ 'filter' => $productFilter, 'searchString' => $searchString, 'load_offers' => false, ]); if ($iblockInfo->canHaveSku()) { $subQueryPropererties = [ 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', 'ACTIVE' => 'Y', 'ACTIVE_DATE' => 'Y', 'IBLOCK_ID' => $iblockInfo->getSkuIblockId(), '*SEARCHABLE_CONTENT' => $searchString, ]; $productFilter = [ [ 'LOGIC' => 'OR', '*SEARCHABLE_CONTENT' => $searchString, 'PRODUCT_BARCODE' => $searchString . '%', '=ID' => \CIBlockElement::SubQuery('PROPERTY_' . $iblockInfo->getSkuPropertyId(), $subQueryPropererties), ] ]; if (!empty($products)) { $productFilter[] = [ '!=ID' => array_keys($products), ]; } $offersFilter = [ [ 'LOGIC' => 'OR', '*SEARCHABLE_CONTENT' => $searchString, 'PRODUCT_BARCODE' => $searchString . '%', ] ]; $offers = $this->getProducts([ 'filter' => $productFilter, 'offer_filter' => $offersFilter, 'searchString' => $searchString, ]); array_push($products, ...$offers); } return $products; } } EntitySelector/BrandProvider.php 0000644 00000007431 15152315315 0013001 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Main\Loader; use Bitrix\UI\EntitySelector\BaseProvider; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; use Bitrix\UI\EntitySelector\SearchQuery; use Bitrix\Iblock\PropertyTable; use Bitrix\Highloadblock as HL; class BrandProvider extends BaseProvider { private const BRAND_LIMIT = 20; private const BRAND_ENTITY_ID = 'brand'; private const BRAND_CODE = 'BRAND_REF'; public function __construct(array $options = []) { parent::__construct(); $this->options['iblockId'] = (int)($options['iblockId'] ?? 0); } public function isAvailable(): bool { return $GLOBALS['USER']->isAuthorized(); } public function getItems(array $ids): array { $items = []; $filter = !empty($ids) ? ['=ID' => $ids] : []; foreach ($this->getBrands($filter) as $section) { $items[] = $this->makeItem($section); } return $items; } public function getSelectedItems(array $ids): array { return $this->getItems($ids); } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItemsCount = count($dialog->getRecentItems()->getEntityItems(self::BRAND_ENTITY_ID)); if ($recentItemsCount < self::BRAND_LIMIT) { foreach ($this->getBrands() as $brand) { $dialog->addRecentItem( $this->makeItem($brand) ); } } } public function doSearch(SearchQuery $searchQuery, Dialog $dialog): void { $filter = []; $query = $searchQuery->getQuery(); if ($query !== '') { $filter['%UF_NAME'] = $query; } foreach ($this->getBrands($filter) as $brand) { $dialog->addItem( $this->makeItem($brand) ); } if ($dialog->getItemCollection()->count() >= self::BRAND_LIMIT) { $searchQuery->setCacheable(false); } } protected function getBrands(array $filter = []): array { if (!Loader::includeModule('highloadblock') || !Loader::includeModule('iblock')) { return []; } $catalogId = $this->options['iblockId']; if ($catalogId <= 0) { return []; } $propertySettings = PropertyTable::getList([ 'select' => ['ID', 'USER_TYPE_SETTINGS'], 'filter' => [ '=IBLOCK_ID' => $catalogId, '=ACTIVE' => 'Y', '=CODE' => self::BRAND_CODE, ], 'limit' => 1, ]) ->fetch() ; if (!$propertySettings) { return []; } $propertySettings['USER_TYPE_SETTINGS'] = ( $userTypeSettings = CheckSerializedData($propertySettings['USER_TYPE_SETTINGS']) ? unserialize($propertySettings['USER_TYPE_SETTINGS'], ['allowed_classes' => false]) : [] ); if (empty($userTypeSettings['TABLE_NAME'])) { return []; } $table = HL\HighloadBlockTable::getList([ 'select' => ['TABLE_NAME', 'NAME', 'ID'], 'filter' => ['=TABLE_NAME' => $userTypeSettings['TABLE_NAME']], ]) ->fetch() ; $brandEntity = HL\HighloadBlockTable::compileEntity($table); $brandEntityClass = $brandEntity->getDataClass(); $parameters = [ 'select' => ['UF_XML_ID', 'UF_FILE', 'UF_NAME'], ]; if (!empty($filter)) { $parameters['filter'] = $filter; } $brands = []; $brandsRaw = $brandEntityClass::getList($parameters); while ($brand = $brandsRaw->fetch()) { if (!empty($brand['UF_FILE'])) { $brand['LOGO'] = \CFile::resizeImageGet( $brand['UF_FILE'], [ 'width' => 100, 'height' => 100, ], BX_RESIZE_IMAGE_EXACT, false )['src']; } $brands[] = $brand; } return $brands; } protected function makeItem(array $brand): Item { return new Item([ 'id' => $brand['UF_XML_ID'], 'entityId' => self::BRAND_ENTITY_ID, 'title' => $brand['UF_NAME'], 'avatar' => $brand['LOGO'], ]); } } EntitySelector/VariationProvider.php 0000644 00000006114 15152315315 0013704 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntitySelector; use Bitrix\Catalog\v2\Iblock\IblockInfo; use Bitrix\Catalog\v2\IoC\ServiceContainer; use Bitrix\Main\Loader; use Bitrix\UI\EntitySelector\Dialog; use Bitrix\UI\EntitySelector\Item; class VariationProvider extends ProductProvider { protected const ENTITY_ID = 'variation'; public function __construct(array $options = []) { parent::__construct(); if (Loader::includeModule('crm')) { $defaultIblockId = \Bitrix\Crm\Product\Catalog::getDefaultOfferId(); } else { $defaultIblockId = 0; } if (isset($options['iblockId']) && (int)$options['iblockId'] > 0) { $iblockId = (int)$options['iblockId']; } else { $iblockId = $defaultIblockId; } $this->options['iblockId'] = $iblockId; } public function fillDialog(Dialog $dialog): void { $dialog->loadPreselectedItems(); if ($dialog->getItemCollection()->count() > 0) { foreach ($dialog->getItemCollection() as $item) { $dialog->addRecentItem($item); } } $recentItems = $dialog->getRecentItems()->getEntityItems(self::ENTITY_ID); /** * @var Item $recentItem */ $ids = []; foreach ($recentItems as $recentItem) { $ids[] = $recentItem->getId(); } $filter = [ 'ID' => $ids, ]; $offers = $this->getProducts([ 'filter' => $filter, ]); foreach ($offers as $offer) { $dialog->addRecentItem($this->makeItem($offer)); } } protected function getProductsBySearchString(string $searchString = ''): array { if (trim($searchString) === '') { return []; } $filter = [ '*SEARCHABLE_CONTENT' => $searchString, ]; return $this->getProducts([ 'filter' => $filter, 'searchString' => $searchString, ]); } public function getItems(array $ids): array { if (empty($ids)) { return []; } $filter = [ 'ID' => $ids, ]; $offers = $this->getProducts([ 'filter' => $filter, ]); $items = []; foreach ($offers as $offer) { $items[] = $this->makeItem($offer); } return $items; } public function getSelectedItems(array $ids): array { return $this->getItems($ids); } protected function getProducts(array $parameters = []): array { $iblockInfo = $this->getIblockInfo(); if (!$iblockInfo) { return []; } $filter = $this->getDefaultFilter(); $filter['IBLOCK_ID'] = $iblockInfo->getSkuIblockId(); $additionalFilter = $parameters['filter']; $filter = array_merge($filter, $additionalFilter); $offers = $this->loadElements([ 'filter' => $filter, 'limit' => self::PRODUCT_LIMIT, ]); $offers = $this->loadProperties($offers, $iblockInfo->getSkuIblockId(), $iblockInfo); $offers = $this->loadPrices($offers); if (isset($parameters['searchString'])) { $offers = $this->loadBarcodes($offers, $parameters['searchString']); } return $offers; } protected function getIblockInfo(): ?IblockInfo { return ServiceContainer::getIblockInfo($this->getIblockId()); } private function getDefaultFilter(): array { return [ 'CHECK_PERMISSIONS' => 'Y', 'MIN_PERMISSION' => 'R', 'ACTIVE' => 'Y', 'ACTIVE_DATE' => 'Y', ]; } } EntityEditor/StoreProvider.php 0000644 00000012646 15152315315 0012521 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntityEditor; use Bitrix\Catalog\Access\AccessController; use Bitrix\Catalog\Access\ActionDictionary; use Bitrix\Catalog\StoreTable; use Bitrix\Main\Config\Ini; use Bitrix\Main\Localization\Loc; use Bitrix\UI\EntityEditor\BaseProvider; use Bitrix\UI\EntityEditor\ProviderWithUserFieldsTrait; /** * Provider for store (warehouse) entity. */ class StoreProvider extends BaseProvider { public const USER_FIELD_TYPE = 'CAT_STORE'; use ProviderWithUserFieldsTrait; /** * @inheritDoc */ public function getUfEntityId(): string { return self::USER_FIELD_TYPE; } /** * Entity field values. * * @var array */ private array $entity; /** * @param array $entityFields */ public function __construct(array $entityFields) { $this->entity = $entityFields; } /** * @inheritDoc */ public function getEntityId(): ?int { return $this->entity['ID'] ?? null; } /** * @inheritDoc */ public function getGUID(): string { $id = $this->getEntityId() ?? 0; return "store_{$id}_details"; } /** * @inheritDoc */ public function getEntityTypeName(): string { return 'store'; } /** * @inheritDoc */ public function getEntityFields(): array { $isDefaultStore = isset($this->entity['IS_DEFAULT']) && $this->entity['IS_DEFAULT'] === 'Y'; $fields = [ [ 'name' => 'TITLE', 'title' => static::getFieldTitle('TITLE'), 'type' => 'text', 'showAlways' => true, ], [ 'name' => 'CODE', 'title' => static::getFieldTitle('CODE'), 'type' => 'text', 'visibilityPolicy' => 'edit', ], [ 'name' => 'ADDRESS', 'title' => static::getFieldTitle('ADDRESS'), 'type' => 'textarea', 'required' => true, 'showAlways' => true, ], [ 'name' => 'DESCRIPTION', 'title' => static::getFieldTitle('DESCRIPTION'), 'type' => 'textarea', 'visibilityPolicy' => 'edit', ], [ 'name' => 'PHONE', 'title' => static::getFieldTitle('PHONE'), 'type' => 'text', 'showAlways' => true, ], [ 'name' => 'SCHEDULE', 'title' => static::getFieldTitle('SCHEDULE'), 'type' => 'text', 'showAlways' => true, ], [ 'name' => 'EMAIL', 'title' => static::getFieldTitle('EMAIL'), 'type' => 'email', 'visibilityPolicy' => 'edit', ], [ 'name' => 'GPS_N', 'title' => static::getFieldTitle('GPS_N'), 'type' => 'text', 'visibilityPolicy' => 'edit', ], [ 'name' => 'GPS_S', 'title' => static::getFieldTitle('GPS_S'), 'type' => 'text', 'visibilityPolicy' => 'edit', ], [ 'name' => 'XML_ID', 'title' => static::getFieldTitle('XML_ID'), 'type' => 'text', 'visibilityPolicy' => 'edit', ], [ 'name' => 'SORT', 'title' => static::getFieldTitle('SORT'), 'type' => 'number', 'default_value' => 100, 'visibilityPolicy' => 'edit', ], [ 'name' => 'ACTIVE', 'title' => static::getFieldTitle('ACTIVE'), 'type' => 'boolean', 'default_value' => 'Y', 'editable' => !$isDefaultStore, 'showAlways' => true, ], [ 'name' => 'ISSUING_CENTER', 'title' => static::getFieldTitle('ISSUING_CENTER'), 'type' => 'boolean', 'visibilityPolicy' => 'edit', ], [ 'name' => 'IMAGE_ID', 'title' => static::getFieldTitle('IMAGE_ID'), 'type' => 'file', 'showAlways' => true, 'data' => [ 'multiple' => false, 'maxFileSize' => Ini::unformatInt(ini_get('upload_max_filesize')), ], ], ]; $fields = $this->fillUfEntityFields($fields); return $fields; } /** * @inheritDoc */ public function getEntityConfig(): array { $elements = []; foreach ($this->getEntityFields() as $item) { $elements[] = [ 'name' => $item['name'], ]; } $sectionElements = [ [ 'name' => 'main', 'title' => Loc::getMessage('CATALOG_STORE_DETAIL_MAIN_SECTION'), 'type' => 'section', 'elements' => $elements, 'data' => [ 'isRemovable' => 'false', ], 'sort' => 100, ], ]; return [ [ 'name' => 'left', 'type' => 'column', 'elements' => $sectionElements, ], ]; } /** * @inheritDoc */ public function getEntityData(): array { $result = []; foreach ($this->getEntityFields() as $item) { $field = $item['name']; $type = $item['type'] ?? 'text'; $value = $this->entity[$field] ?? $item['default_value'] ?? null; $result[$field] = $this->prepareValue($type, $value); } $result = $this->fillUfEntityData($result); return $result; } /** * @inheritDoc */ public function getEntityControllers(): array { return []; } /** * @inheritDoc */ public function isReadOnly(): bool { return ! AccessController::getCurrent()->check(ActionDictionary::ACTION_STORE_MODIFY); } /** * Get field title for editor. * * @param string $fieldName * * @return string */ private static function getFieldTitle(string $fieldName): string { static $managerFields; if (!isset($managerFields)) { $managerFields = []; foreach (StoreTable::getMap() as $field) { /** * @var \Bitrix\Main\ORM\Fields\Field $field */ $name = $field->getName(); $managerFields[$name] = Loc::getMessage("CATALOG_STORE_DETAIL_FIELD_TITLE_{$name}") ?? $field->getTitle() ; } } return $managerFields[$fieldName] ?? $fieldName; } private function prepareValue(string $type, $value) { if (!isset($value)) { return null; } return (string)$value; } } EntityEditor/Product/StoreDocumentProductPositionRepository.php 0000644 00000007643 15152315315 0021314 0 ustar 00 <?php namespace Bitrix\Catalog\v2\Integration\UI\EntityEditor\Product; use Bitrix\Catalog\Url\InventoryBuilder; use Bitrix\Catalog\v2\Helpers\PropertyValue; use Bitrix\Catalog\v2\IoC\ServiceContainer; use Bitrix\Catalog\Config\State; use Bitrix\Iblock\Url\AdminPage\BuilderManager; use Bitrix\Main\Loader; use Bitrix\Sale\PriceMaths; /** * Class StoreDocumentProductPositionRepository * * @package Bitrix\Catalog\v2\Integration\UI\EntityEditor\Product * * * !!! This API is in alpha stage and is not stable. This is subject to change at any time without notice. * @internal */ final class StoreDocumentProductPositionRepository { protected const PRODUCT_PRICE_TYPE = 'PURCHASING_PRICE'; /** @var StoreDocumentProductPositionRepository */ private static $instance; private array $documentProductPositionListCollection = []; private int $catalogId; /** * @return StoreDocumentProductPositionRepository */ public static function getInstance(): StoreDocumentProductPositionRepository { if (is_null(static::$instance)) { static::$instance = new static(); } return static::$instance; } /** * Get array of product positions in document * * @param int $documentId * @param int $limit * @return array */ public function getList(int $documentId, int $limit = 10): array { $productPositionList = $this->fetchDocumentProductPositionList($documentId); return array_slice($productPositionList, 0, $limit); } /** * Get product positions count of document * * @param int $documentId * @return int */ public function getCount(int $documentId): int { return count($this->fetchDocumentProductPositionList($documentId)); } private function fetchDocumentProductPositionList(int $documentId): array { if (!Loader::includeModule('sale')) { return []; } if (!empty($this->documentProductPositionListCollection[$documentId])) { return $this->documentProductPositionListCollection[$documentId]; } $documentProductListData = \CCatalogStoreDocsElement::getList( ['ID' => 'ASC'], ['DOC_ID' => $documentId], false, false, ['ELEMENT_ID', 'ELEMENT_NAME', 'AMOUNT', 'PURCHASING_PRICE', 'BASE_PRICE'] ); $documentProductList = []; while ($product = $documentProductListData->Fetch()) { $documentProductList[] = $this->formProductPositionData($product); } $this->documentProductPositionListCollection[$documentId] = $documentProductList; return $documentProductList; } private function formProductPositionData(array $product): array { $productPositionData = [ 'PRODUCT_NAME' => $product['ELEMENT_NAME'], 'SUM' => PriceMaths::roundPrecision((float)$product[self::PRODUCT_PRICE_TYPE] * (float)$product['AMOUNT']), ]; $productId = $product['ELEMENT_ID']; $productVariation = ServiceContainer::getRepositoryFacade()->loadVariation($product['ELEMENT_ID']); if ($productVariation) { $image = $productVariation->getFrontImageCollection()->getFrontImage(); $productPositionData['PHOTO_URL'] = $image ? $image->getSource() : null; $productPositionData['VARIATION_INFO'] = PropertyValue::getSkuPropertyDisplayValues($productVariation); if (!State::isProductCardSliderEnabled()) { $productId = $productVariation->getParent() ? $productVariation->getParent()->getId() : $productId; } } $productPositionData['URL'] = $this->buildPositionUrl($productId); return $productPositionData; } private function buildPositionUrl(int $productId): string { $urlBuilder = BuilderManager::getInstance()->getBuilder(InventoryBuilder::TYPE_ID); if ($urlBuilder) { $urlBuilder->setIblockId($this->getCatalogId()); return $urlBuilder->getElementDetailUrl($productId); } return ''; } private function getCatalogId(): int { if (!isset($this->catalogId)) { $this->catalogId = 0; if (\Bitrix\Main\Loader::includeModule('crm')) { $this->catalogId = \Bitrix\Crm\Product\Catalog::getDefaultId() ?? 0; } } return $this->catalogId; } }
| ver. 1.4 |
Github
|
.
| PHP 7.4.33 | Generation time: 0.24 |
proxy
|
phpinfo
|
Settings