Есть такой класс компонента в Битрикс24.
Компонент собственный, не стандартный.
<?php

use Bitrix\Crm\Category\PermissionEntityTypeHelper;
use Bitrix\Crm\CompanyTable;
use Bitrix\Crm\ContactTable;
use Bitrix\Crm\Service\Container;
use Bitrix\Crm\Service\UserPermissions;
use Bitrix\Crm\Settings\LayoutSettings;
use Bitrix\Main\Grid\Options;
use Bitrix\Main\Loader;
use Bitrix\Main\Localization\Loc;
use Bitrix\Main\ORM\Fields\ExpressionField;
use Bitrix\Main\ORM\Fields\Relations\Reference;
use Bitrix\Main\ORM\Query\Join;
use Bitrix\Main\ORM\Query\Query;
use Bitrix\Main\Type\Date;
use Bitrix\Main\UI\PageNavigation;
use Citfact\SiteCore\Service\CrmEnumService;
use Citfact\SiteCore\Tools\Company\CompanyRepository;
use Citfact\SiteCore\Tools\Contact\ContactRepository;
use Citfact\SiteCore\Tools\Element\UserFieldEnumTable;

defined('B_PROLOG_INCLUDED') || die;

class CrmAgentsListComponents extends CBitrixComponent
{
    const GRID_ID = 'crm_agents_list';

    protected string $navParamName = 'page';
    protected array $defaultGridSort = [
        'ID' => 'desc'
    ];

    public function executeComponent()
    {
        Loader::requireModule('crm');

        $sExportType = !empty($this->arParams['EXPORT_TYPE']) ?
            strval($this->arParams['EXPORT_TYPE']) : (!empty($_REQUEST['type']) ? strval($_REQUEST['type']) : '');

        $currentPage = $this->request->get('page');
        $currentPage = (is_numeric($currentPage) && $currentPage > 0) ? $currentPage : 1;

        $gridOptions = new Options(self::GRID_ID, $this->getFilterPresets());
        $filterOptions = new \Bitrix\Main\UI\Filter\Options(self::GRID_ID . '_FILTER');

        $navParams = $gridOptions->getNavParams(['nPageSize' => 20]);
        $pageSize = (int)$navParams['nPageSize'];

        $pageNavigation = new PageNavigation($this->navParamName);
        $pageNavigation->allowAllRecords(false)->setPageSize($pageSize)->setCurrentPage($currentPage)->initFromUri();

        $filterFields = $this->getFilter();
        $sort = $gridOptions->GetSorting(array(
            'sort' => array('ID' => 'asc'),
            'vars' => array('by' => 'by', 'order' => 'order')
        ));

        $query = $this->getAgentsQuery($filterOptions, $pageNavigation);

        $pageNavigation->setRecordCount($this->getAgentsCount($query));

        foreach ($sort['sort'] as $field => $order) {
            $query->addUnionOrder($field, $order);
        }

        $this->arResult = [
            'GRID_ID' => self::GRID_ID,
            'FILTER' => $filterFields,
            'FILTER_PRESETS' => $this->getFilterPresets(),
            'COLUMNS' => $this->getGridColumns($sExportType ? $gridOptions->getVisibleColumns() : []),
            'HEADERS_SECTIONS' => $this->getHeaderSections(),
            'ROWS' => [],
            'NAV' => $pageNavigation,
            'BUTTONS' => $this->getToolbarItems(),
            'EXPORT_EXCEL_PARAMS' => $this->getExportExcelParams()
        ];

        if (!empty($sExportType)) {
            $this->updateExportNavigation($pageNavigation);
        }

        $agentsPositions = [];

        $agentsIdentifiers = [
            CCrmOwnerType::Contact => [],
            CCrmOwnerType::Company => [],
        ];

        $result = $query
            ->setUnionLimit($pageNavigation->getLimit())
            ->setUnionOffset($pageNavigation->getOffset())
            ->exec();
        while ($item = $result->fetch()) {
            $entityId = $item['ID'];
            $entityTypeId = $item['TYPE'];

            $agentsIdentifiers[$entityTypeId][] = $entityId;
            $agentsPositions[] = sprintf('%s_%s', $entityTypeId, $entityId);
        }

        $agentsPositions = array_flip($agentsPositions);

        $items = $this->getItems($agentsIdentifiers, $gridOptions);
        usort($items, static function ($a, $b) use ($agentsPositions) {
           $keyA = sprintf('%s_%s', $a['TYPE'], $a['ID']);
           $keyB = sprintf('%s_%s', $b['TYPE'], $b['ID']);

           return $agentsPositions[$keyA] <=> $agentsPositions[$keyB];
        });

        $this->arResult['ROWS'] = $this->getGridRows($items);
        if (!empty($sExportType)) {
            return $this->processExport($sExportType);
        }


        $this->includeComponentTemplate();
    }

    public function updateExportNavigation(PageNavigation $navigation) {
        $currentPage = !empty($this->arParams['PAGE_NUMBER']) && $this->arParams['PAGE_NUMBER'] > 1 ? $this->arParams['PAGE_NUMBER'] : 1;
        $pageSize = !empty($this->arParams['STEXPORT_PAGE_SIZE']) ? $this->arParams['STEXPORT_PAGE_SIZE'] : $navigation->getRecordCount();

        $navigation
            ->setCurrentPage($currentPage)
            ->setPageSize($pageSize);
    }

    public function processExport(string $sExportType)
    {
        global $APPLICATION;

        $isStExport = false;    // Step-by-step export mode
        if (!empty($sExportType))
        {
            $sExportType = mb_strtolower(trim($sExportType));
            switch ($sExportType)
            {
                case 'csv':
                case 'excel':
                    $isStExport = (isset($this->arParams['STEXPORT_MODE']) && $this->arParams['STEXPORT_MODE'] === 'Y');
                    break;
                default:
                    $sExportType = '';
            }
        }

        /** @var PageNavigation $navigation */
        $navigation = $this->arResult['NAV'];

        $this->arResult['STEXPORT_MODE'] = $isStExport ? 'Y' : 'N';
        $this->arResult['STEXPORT_IS_FIRST_PAGE'] = $navigation->getCurrentPage() === 1 ? 'Y' : 'N';
        $this->arResult['STEXPORT_IS_LAST_PAGE'] =  $navigation->getCurrentPage() < $navigation->getPageCount() ? 'N' : 'Y';

        if ($isStExport) {
            $this->__templateName = '.default';

            $this->IncludeComponentTemplate($sExportType);

            return [
                'PROCESSED_ITEMS' => count($this->arResult['ROWS']),
                'TOTAL_ITEMS' => $navigation->getRecordCount(),
                'LAST_EXPORTED_ID' => -1
            ];
        } else {
            $APPLICATION->RestartBuffer();
            // hack. any '.default' customized template should contain 'excel' page
            $this->__templateName = '.default';

            if ($sExportType === 'excel')
            {
                Header('Content-Type: application/vnd.ms-excel');
                Header('Content-Disposition: attachment;filename=quotes.xls');
            }
            Header('Content-Type: application/octet-stream');
            Header('Content-Transfer-Encoding: binary');

            // add UTF-8 BOM marker
            if (defined('BX_UTF') && BX_UTF)
                echo chr(239).chr(187).chr(191);

            $this->IncludeComponentTemplate($sExportType);

            die();
        }
    }

    public function getEntityFieldName(int $entityTypeId, string $columnId): ?string
    {
        $data = [
            CCrmOwnerType::Contact => [
                'TITLE' => 'FULL_NAME',
                'CITY' => ContactRepository::FIELD_CITY,
                'CLIENT_REQUEST' => ContactRepository::FIELD_CLIENT_REQUEST,
                'INQUIRY_PURPOSE' => ContactRepository::FIELD_INQUIRY_PURPOSE,
                'LEAD_SOURCE' => ContactRepository::FIELD_LEAD_SOURCE,
                'AGENT_DIRECTION' => ContactRepository::FIELD_AGENT_DIRECTION,
                'AGENT_TYPE' => ContactRepository::FIELD_AGENT_TYPE,
                'AGENT_STATUS' => ContactRepository::FIELD_AGENT_STATUS,
                'AGENT_VIP' => ContactRepository::FIELD_AGENT_VIP,
                'LAST_REALIZATION_DATE' => ContactRepository::FIELD_LAST_REALIZATION_DATE,
                'LAST_CONTACT_DATE' => ContactRepository::FIELD_LAST_CONTACT_DATE,
                'CONTRACT_DATE' => ContactRepository::FIELD_CONTRACT_DATE,
                'REGION' => ContactRepository::FIELD_REGION,
                'AGENT_INN' => ContactRepository::FIELD_AGENT_INN,
                'NEWSLETTER_CONSENT' => ContactRepository::FIELD_NEWSLETTER_CONSENT,
                'AGENT_CONTRACT_NUM' => ContactRepository::FIELD_AGENT_CONTRACT_NUM,
                'CALL_RESULT' => ContactRepository::FIELD_CALL_RESULT,
                'CALL_CREATED_AT' => ContactRepository::FIELD_CALL_CREATED_AT,
                'TOPIC_SUBJECT' => ContactRepository::FIELD_TOPIC_SUBJECT,
                'AGENT_PERSON_TYPE' => ContactRepository::FIELD_AGENT_PERSON_TYPE,
                'AGENT_CITY' => ContactRepository::FIELD_AGENT_CITY,
                'RQ_INN' => 'RQ.RQ_INN',
                'DEAL_COUNT_TOTAL' => 'UF_CRM_1710918157',
                'RETAIL_DEAL_COUNT' => 'UF_CRM_1710918216',
                'DKT_SOURCE' => 'UF_CRM_DCT_SOURCE',
                'CONTACT_NAME' => 'NAME',
                'CONTACT_LAST_NAME' => 'LAST_NAME',
                'CONTACT_SECOND_NAME' => 'SECOND_NAME',
                'CONTACT_BIRTHDATE' => 'BIRTHDATE',
                'CONTACT_COMPANY_ID' => 'COMPANY_ID',
                'CONTACT_SOURCE_ID' => 'SOURCE_ID',
                'CONTACT_EXPORT' => 'EXPORT',
                'CONTACT_PARTNER_1C_GUID' => ContactRepository::FIELD_PARTNER_1C_GUID,
                'CONTACT_USER_ID_SHOP' => ContactRepository::FIELD_USER_ID_SHOP,
                'CONTACT_POLICY_CONSENT' => ContactRepository::FIELD_POLICY_AGREEMENT,
                'PARTNER_1C_ID' => ContactRepository::FIELD_PARTNER_1C_ID,
            ],
            CCrmOwnerType::Company => [
                'CITY' => CompanyRepository::FIELD_CITY,
                'CLIENT_REQUEST' => CompanyRepository::FIELD_CLIENT_REQUEST,
                'INQUIRY_PURPOSE' => CompanyRepository::FIELD_INQUIRY_PURPOSE,
                'LEAD_SOURCE' => CompanyRepository::FIELD_LEAD_SOURCE,
                'AGENT_DIRECTION' => CompanyRepository::FIELD_AGENT_DIRECTION,
                'AGENT_TYPE' => CompanyRepository::FIELD_AGENT_TYPE,
                'AGENT_STATUS' => CompanyRepository::FIELD_AGENT_STATUS,
                'AGENT_VIP' => CompanyRepository::FIELD_AGENT_VIP,
                'LAST_REALIZATION_DATE' => CompanyRepository::FIELD_LAST_REALIZATION_DATE,
                'LAST_CONTACT_DATE' => CompanyRepository::FIELD_LAST_CONTACT_DATE,
                'CONTRACT_DATE' => CompanyRepository::FIELD_CONTRACT_DATE,
                'REGION' => CompanyRepository::FIELD_REGION,
                'AGENT_INN' => CompanyRepository::FIELD_AGENT_INN,
                'NEWSLETTER_CONSENT' => CompanyRepository::FIELD_NEWSLETTER_CONSENT,
                'AGENT_CONTRACT_NUM' => CompanyRepository::FIELD_AGENT_CONTRACT_NUM,
                'CALL_RESULT' => CompanyRepository::FIELD_CALL_RESULT,
                'CALL_CREATED_AT' => CompanyRepository::FIELD_CALL_CREATED_AT,
                'TOPIC_SUBJECT' => CompanyRepository::FIELD_TOPIC_SUBJECT,
                'AGENT_PERSON_TYPE' => CompanyRepository::FIELD_AGENT_PERSON_TYPE,
                'AGENT_CITY' => CompanyRepository::FIELD_AGENT_CITY,
                'RQ_INN' => 'RQ.RQ_INN',
                'DEAL_COUNT_TOTAL' => 'UF_CRM_1711693267',
                'RETAIL_DEAL_COUNT' => 'UF_CRM_1711693370',
                'DKT_SOURCE' => 'UF_CRM_65DF3C69A59A6',
                'COMPANY_HEAD_COMPANY' => CompanyRepository::FIELD_HEAD_COMPANY,
                'COMPANY_INDUSTRY' => 'INDUSTRY',
                'COMPANY_PARTNER' => CompanyRepository::FIELD_PARTNER,
                'COMPANY_DO_ID' => CompanyRepository::FIELD_DO_ID,
                'COMPANY_RQ_COMPANY_NAME' => 'RQ.RQ_COMPANY_NAME',
                'PARTNER_ID_1C' => CompanyRepository::FIELD_PARTNER_1C_ID
            ]
        ];

        return $data[$entityTypeId][$columnId];
    }

    public function getEntityFields(int $entityTypeId): array
    {
        static $entityFields = [];
        if (isset($entityFields[$entityTypeId])) {
            return $entityFields[$entityTypeId];
        }

        $entityTypeName = CCrmOwnerType::ResolveName($entityTypeId);
        $entityFields[$entityTypeId] = [];

        foreach ($this->getGridColumns() as $column) {
            if ($column['runtime']) {
                continue;
            }

            $sectionId = $column['section_id'];
            if ($sectionId && $sectionId !== $entityTypeName && $sectionId !== 'GENERAL') {
                continue;
            }

            if ($entityFieldName = $this->getEntityFieldName($entityTypeId, $column['id'])) {
                $entityFields[$entityTypeId][$column['id']] = $entityFieldName;
            } else {
                $entityFields[$entityTypeId][] = $column['id'];
            }
        }

        return $entityFields[$entityTypeId];
    }

    public function getEntityFieldAlias(int $entityTypeId, string $fieldName): ?string
    {
        $flippedEntityFields = array_flip($this->getEntityFields($entityTypeId));
        if (is_numeric($flippedEntityFields[$fieldName])) {
            return null;
        }

        return $flippedEntityFields[$fieldName] ?? null;
    }

    public function getEntitySelectedFields(string $entityName, array $visibleColumns): array
    {
        $selectedFields = [];

        $columns = $this->getGridColumns();

        foreach ($this->getEntityFields($entityName) as $alias => $fieldName) {
            $hasAlias = !is_int($alias);
            $gridColumnId = $hasAlias ? $alias : $fieldName;
            if (
                (empty($visibleColumns) && !$columns[$gridColumnId]['default'])
                || (!empty($visibleColumns) && !in_array($gridColumnId, $visibleColumns))
            ) {
                continue;
            }

            if ($hasAlias) {
                $selectedFields[$gridColumnId] = $fieldName;
            } else {
                $selectedFields[] = $fieldName;
            }
        }

        $dependencyFields = [
            'CREATED_BY_ID' => $this->getUserNameFields('CREATED_BY'),
            'ASSIGNED_BY_ID' => $this->getUserNameFields('ASSIGNED_BY'),
            'CONTACT_COMPANY_ID' => ['CONTACT_COMPANY_NAME' => 'COMPANY.TITLE'],
            'COMPANY_HEAD_COMPANY' => ['COMPANY_HEAD_COMPANY_TITLE' => 'CP.TITLE'],
            'COMPANY_PARTNER' =>  ['COMPANY_PARTNER_TITLE' => 'CP.TITLE'],
        ];
        foreach ($dependencyFields as $field => $fields) {
            if (isset($selectedFields[$field]) || in_array($field, $selectedFields)) {
                $selectedFields = array_merge($selectedFields, $fields);
            }
        }

        return $selectedFields;
    }

    protected function getGridColumns(array $visibleColumns = []): array
    {
        $columns = [
            [
                'id' => 'ID',
                'name' => 'ID',
                'sort' => 'ID',
                'first_order' => 'desc',
                'width' => 60,
                'type' => 'int',
                'class' => 'minimal',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'DATE_CREATE',
                'name' => Loc::getMessage('GRID_COLUMN_CREATED_AT'),
                'sort' => 'DATE_CREATE',
                'first_order' => 'desc',
                'type' => 'date',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CREATED_BY_ID',
                'name' => Loc::getMessage('GRID_COLUMN_CREATED_BY_ID'),
                'sort' => 'CREATED_BY_ID',
                'first_order' => 'desc',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'SUMMARY',
                'name' => Loc::getMessage('GRID_COLUMN_TYPE'),
                'sort' => 'TITLE',
                'width' => 200,
                'default' => true,
                'section_id' => 'GENERAL',
                'runtime' => true,
                'ignore_filter' => true
            ],
            [
                'id' => 'TITLE',
                'name' => Loc::getMessage('GRID_COLUMN_TITLE'),
                'sort' => 'TITLE',
                'width' => 200,
                'default' => false,
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'PHONE',
                'name' => Loc::getMessage('GRID_COLUMN_PHONE'),
                'width' => 180,
                'type' => 'custom',
                'default' => true,
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'EMAIL',
                'name' => Loc::getMessage('GRID_COLUMN_EMAIL'),
                'width' => 180,
                'type' => 'custom',
                'default' => true,
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'COMMENTS',
                'name' => Loc::getMessage('GRID_COLUMN_COMMENTS'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'ASSIGNED_BY_ID',
                'name' => Loc::getMessage('GRID_COLUMN_ASSIGNED_BY_ID'),
                'sort' => 'ASSIGNED_BY_NAME',
                'class' => 'username',
                'default' => true,
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'WEBFORM_ID',
                'name' => Loc::getMessage('GRID_COLUMN_WEBFORM_ID'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CITY',
                'name' => Loc::getMessage('GRID_COLUMN_CITY'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CLIENT_REQUEST',
                'name' => Loc::getMessage('GRID_COLUMN_CLIENT_REQUEST'),
                'type' => 'text',
                'section_id' => 'GENERAL',
                'ignore_filter' => true
            ],
            [
                'id' => 'INQUIRY_PURPOSE',
                'name' => Loc::getMessage('GRID_COLUMN_INQUIRY_PURPOSE'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'LEAD_SOURCE',
                'name' => Loc::getMessage('GRID_COLUMN_LEAD_SOURCE'),
                'type' => 'text',
                'section_id' => 'GENERAL',
                'ignore_filter' => true
            ],
            [
                'id' => 'AGENT_DIRECTION',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_DIRECTION'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_TYPE',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_TYPE'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_STATUS',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_STATUS'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_VIP',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_VIP'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'LAST_REALIZATION_DATE',
                'name' => Loc::getMessage('GRID_COLUMN_LAST_REALIZATION_DATE'),
                'type' => 'date',
                'section_id' => 'GENERAL',
                'ignore_filter' => true
            ],
            [
                'id' => 'LAST_CONTACT_DATE',
                'name' => Loc::getMessage('GRID_COLUMN_LAST_CONTACT_DATE'),
                'type' => 'date',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CONTRACT_DATE',
                'name' => Loc::getMessage('GRID_COLUMN_CONTRACT_DATE'),
                'type' => 'date',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'REGION',
                'name' => Loc::getMessage('GRID_COLUMN_REGION'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_INN',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_INN'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'NEWSLETTER_CONSENT',
                'name' => Loc::getMessage('GRID_COLUMN_NEWSLETTER_CONSENT'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_CONTRACT_NUM',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_CONTRACT_NUM'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CALL_RESULT',
                'name' => Loc::getMessage('GRID_COLUMN_CALL_RESULT'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'CALL_CREATED_AT',
                'name' => Loc::getMessage('GRID_COLUMN_CALL_CREATED_AT'),
                'type' => 'date',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'TOPIC_SUBJECT',
                'name' => Loc::getMessage('GRID_COLUMN_TOPIC_SUBJECT'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_PERSON_TYPE',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_PERSON_TYPE'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'AGENT_CITY',
                'name' => Loc::getMessage('GRID_COLUMN_AGENT_CITY'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'RQ_INN',
                'name' => Loc::getMessage('GRID_COLUMN_RQ_INN'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'DEAL_COUNT_TOTAL',
                'name' => Loc::getMessage('GRID_COLUMN_DEAL_COUNT_TOTAL'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'RETAIL_DEAL_COUNT',
                'name' => Loc::getMessage('GRID_COLUMN_RETAIL_DEAL_COUNT'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'DKT_SOURCE',
                'name' => Loc::getMessage('GRID_COLUMN_DKT_SOURCE'),
                'type' => 'text',
                'section_id' => 'GENERAL',
            ],
            [
                'id' => 'PARTNER_1C_ID',
                'name' => Loc::getMessage('GRID_COLUMN_PARTNER_1C_ID'),
                'type' => 'text',
                'section_id' => 'GENERAL'
            ],
            [
                'id' => 'CONTACT_NAME',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_NAME'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_LAST_NAME',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_LAST_NAME'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_SECOND_NAME',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_SECOND_NAME'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_BIRTHDATE',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_BIRTHDATE'),
                'type' => 'date',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_COMPANY_ID',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_COMPANY_ID'),
                'type' => 'text',
                'section_id' => 'CONTACT',
                'ignore_filter' => true
            ],
            [
                'id' => 'CONTACT_SOURCE_ID',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_SOURCE_ID'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_EXPORT',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_EXPORT'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_PARTNER_1C_GUID',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_PARTNER_1C_GUID'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_USER_ID_SHOP',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_USER_ID_SHOP'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'CONTACT_POLICY_CONSENT',
                'name' => Loc::getMessage('GRID_COLUMN_CONTACT_POLICY_CONSENT'),
                'type' => 'text',
                'section_id' => 'CONTACT'
            ],
            [
                'id' => 'COMPANY_HEAD_COMPANY',
                'name' => Loc::getMessage('GRID_COLUMN_COMPANY_HEAD_COMPANY'),
                'type' => 'text',
                'section_id' => 'COMPANY',
                'ignore_filter' => true
            ],
            [
                'id' => 'COMPANY_INDUSTRY',
                'name' => Loc::getMessage('GRID_COLUMN_COMPANY_INDUSTRY'),
                'type' => 'text',
                'section_id' => 'COMPANY'
            ],
            [
                'id' => 'COMPANY_PARTNER',
                'name' => Loc::getMessage('GRID_COLUMN_COMPANY_PARTNER'),
                'type' => 'text',
                'section_id' => 'COMPANY'
            ],
            [
                'id' => 'COMPANY_DO_ID',
                'name' => Loc::getMessage('GRID_COLUMN_COMPANY_DO_ID'),
                'type' => 'text',
                'section_id' => 'COMPANY'
            ],
            [
                'id' => 'COMPANY_RQ_COMPANY_NAME',
                'name' => Loc::getMessage('GRID_COLUMN_COMPANY_RQ_COMPANY_NAME'),
                'type' => 'text',
                'section_id' => 'COMPANY'
            ],
        ];

        $mappedColumns = [];
        foreach ($columns as $column) {
            $mappedColumns[$column['id']] = $column;
        }

        if (!empty($visibleColumns)) {
            $flippedVisibleColumns = array_flip($visibleColumns);

            $mappedColumns = array_intersect_key($mappedColumns, $flippedVisibleColumns);
            uasort($mappedColumns, function ($a, $b) use ($flippedVisibleColumns) {
                return $flippedVisibleColumns[$a['id']] <=> $flippedVisibleColumns[$b['id']];
            });
        }

        return $mappedColumns;
    }

    protected function getHeaderSections(): array
    {
        Container::getInstance()->getLocalization()->loadMessages();

        return [
            'GENERAL' => [
                'id' => 'GENERAL',
                'name' => Loc::getMessage('GRID_SECTION_GENERAL'),
                'default' => true,
                'selected' => true
            ],
            'CONTACT' => [
                'id' => 'CONTACT',
                'name' => Loc::getMessage('GRID_SECTION_CONTACT'),
                'selected' => true
            ],
            'COMPANY' => [
                'id' => 'COMPANY',
                'name' => Loc::getMessage('GRID_SECTION_COMPANY'),
                'selected' => true
            ]
        ];
    }

    protected function getFilter(): array
    {
        Container::getInstance()->getLocalization()->loadMessages();

        $fields = [
            'ID' => [
                'id' => 'ID',
                'name' => 'ID',
                'default' => false
            ],
            'TYPE' => [
                'id' => 'TYPE',
                'name' => Loc::getMessage('GRID_COLUMN_TYPE'),
                'default' => true,
                'type' => 'list',
                'items' => [
                    CCrmOwnerType::Contact => Loc::getMessage('CRM_COMMON_CONTACT'),
                    CCrmOwnerType::Company => Loc::getMessage('CRM_COMMON_COMPANY'),
                ]
            ],
            'PHONE' => [
                'id' => 'PHONE',
                'type' => 'string',
                'name' => Loc::getMessage('GRID_COLUMN_PHONE'),
                'default' => true
            ],
            'EMAIL' => [
                'id' => 'EMAIL',
                'type' => 'string',
                'name' => Loc::getMessage('GRID_COLUMN_EMAIL'),
                'default' => true
            ],
            'CREATED_BY_ID' => [
                'id' => 'CREATED_BY_ID',
                'name' => Loc::getMessage('GRID_COLUMN_CREATED_BY_ID'),
                'default' => false,
                'type' => 'dest_selector',
                'params' => [
                    'context' => 'CRM_EVENT_FILTER_CREATED_BY_ID',
                    'multiple' => 'Y',
                    'contextCode' => 'U',
                    'enableAll' => 'N',
                    'enableSonetgroups' => 'N',
                    'allowEmailInvitation' => 'N',
                    'allowSearchEmailUsers' => 'N',
                    'departmentSelectDisable' => 'Y',
                    'isNumeric' => 'Y',
                    'prefix' => 'U',
                ]
            ],
            'ASSIGNED_BY_ID' => [
                'id' => 'ASSIGNED_BY_ID',
                'name' => Loc::getMessage('GRID_COLUMN_ASSIGNED_BY_ID'),
                'default' => true,
                'type' => 'dest_selector',
                'params' => [
                    'context' => 'CRM_EVENT_FILTER_ASSIGNED_BY_ID',
                    'multiple' => 'Y',
                    'contextCode' => 'U',
                    'enableAll' => 'N',
                    'enableSonetgroups' => 'N',
                    'allowEmailInvitation' => 'N',
                    'allowSearchEmailUsers' => 'N',
                    'departmentSelectDisable' => 'Y',
                    'isNumeric' => 'Y',
                    'prefix' => 'U',
                ]
            ],
            'DATE_CREATE' => [
                'id' => 'DATE_CREATE',
                'name' => Loc::getMessage('GRID_COLUMN_CREATED_AT'),
                'default' => true,
                'type' => 'date'
            ],
            'DEAL_COUNT_TOTAL' => [
                'id' => 'DEAL_COUNT_TOTAL',
                'type' => 'number',
                'name' => Loc::getMessage('GRID_COLUMN_DEAL_COUNT_TOTAL'),
            ],
            'RETAIL_DEAL_COUNT' => [
                'id' => 'RETAIL_DEAL_COUNT',
                'type' => 'number',
                'name' => Loc::getMessage('GRID_COLUMN_RETAIL_DEAL_COUNT'),
            ],
            'DKT_SOURCE' => [
                'id' => 'DKT_SOURCE',
                'type' => 'string',
                'name' => Loc::getMessage('GRID_COLUMN_DKT_SOURCE')
            ],
            'PARTNER_1C_ID' => [
                'id' => 'PARTNER_1C_ID',
                'type' => 'string',
                'name' => Loc::getMessage('GRID_COLUMN_PARTNER_1C_ID')
            ],
            'LAST_REALIZATION_DATE' => [
                'id' => 'LAST_REALIZATION_DATE',
                'type' => 'date',
                'name' => Loc::getMessage('GRID_COLUMN_LAST_REALIZATION_DATE')
            ]
        ];

        $fieldsInfo = $this->getFieldsInfo();

        foreach ($this->getGridColumns() as $column) {
            $isGeneral = !$column['section_id'] || $column['section_id'] === 'GENERAL';

            if ($column['ignore_filter'] || isset($fields[$column['id']])) {
                continue;
            }

            $entityTypeId = CCrmOwnerType::ResolveID($column['section_id']);

            $field = [
                'id' => $column['id'],
                'name' => $column['name'],
                'default' => false,
                'sectionId' => $column['section_id'],
            ];

            $entityAttachments = $entityTypeId ? [$entityTypeId] : [CCrmOwnerType::Contact, CCrmOwnerType::Company];
            foreach ($entityAttachments as $entityTypeId) {
                $fieldData = $fieldsInfo[$entityTypeId][$column['id']];
                if (!$fieldData) {
                    continue;
                }

                if (in_array($fieldData['type'], ['iblock_element', 'enumeration'])) {
                    $field['type'] = 'list';

                    $values = $isGeneral ? array_combine($fieldData['value'], $fieldData['value']) : $fieldData['value'];
                    $field['items'] = array_merge($field['items'] ?? [], $values);
                } elseif ($fieldData['type'] === 'boolean') {
                    $field['type'] = 'checkbox';
                } elseif ($fieldData['type'] === 'date') {
                    $field['type'] = 'date';
                }
            }

            $fields[$field['id']] = $field;
        }

        return $fields;
    }

    protected function getFilterPresets(): array
    {
        $currentUserID = CCrmSecurityHelper::GetCurrentUserID();
        $currentUserName = CCrmViewHelper::GetFormattedUserName($currentUserID);

        return [
            'filter_assigned_self' => [
                'name' => Loc::getMessage('GRID_PRESET_ASSIGNED_SELF'),
                'fields' => ['ASSIGNED_BY_ID' => $currentUserID, 'ASSIGNED_BY_ID_name' => $currentUserName]
            ]
        ];
    }

    protected function getItems(array $agentsIdentifiers, Options $gridOptions): array
    {
        $items = [];

        $entitiesData = [
            CCrmOwnerType::Contact => [
                'dataManager' => ContactTable::class,
                'selectedFields' => ['ID', 'TITLE' => 'FULL_NAME']
            ],
            CCrmOwnerType::Company => [
                'dataManager' => CompanyTable::class,
                'selectedFields' => ['ID', 'TITLE']
            ]
        ];

        /**
         * @var int $entityTypeId
         * @var array $dataManagerClass
         */
        foreach ($entitiesData as $entityTypeId => $entityData) {
            if (empty($agentsIdentifiers[$entityTypeId])) {
                continue;
            }

            $selectedFields = array_merge(
                $this->getEntitySelectedFields($entityTypeId, $gridOptions->getVisibleColumns()),
                $entityData['selectedFields']
            );

            $query = $entityData['dataManager']::query()
                ->setSelect($selectedFields)
                ->whereIn('ID', $agentsIdentifiers[$entityTypeId]);

            $this->injectRuntimeFields($entityTypeId, $query);

            $result = $query->exec();
            while ($item = $result->fetch()) {
                $item['TYPE'] = $entityTypeId;

                $items[] = $item;
            }
        }

        return $items;
    }

    protected function getGridRows(array $agents): array
    {
        $now = time() + CTimeZone::GetOffset();
        $timeFormat = $this->getTimeFormat();

        Container::getInstance()->getLocalization()->loadMessages();

        return array_map(function(array $item) use ($now, $timeFormat) {
            $entityTypeName = CCrmOwnerType::ResolveName($item['TYPE']);

            $columns = [];
            foreach ($item as $key => $value) {
                if ($value instanceof Date) {
                    $columns[$key] = FormatDate($timeFormat, MakeTimeStamp($value), $now);
                } else {
                    $columns[$key] = $this->getFieldTextValue($item['TYPE'], $key, $value);
                }
            }

            if ($item['PHONE']) {
                $item['~PHONE_WORK'][] = $item['PHONE'];
            }

            if ($item['EMAIL']) {
                $item['~EMAIL_WORK'][] = $item['EMAIL'];
            }

            $columns['NAME'] = $columns['TITLE'] ?? '';
            $columns['COMMENTS'] = htmlspecialcharsback($item['COMMENTS'] ?? '');
            $columns['CREATED_BY_FORMATTED_NAME'] = $columns['CREATED_BY_ID'] ? $this->getUserFormatName($item, 'CREATED_BY') : '';
            $columns['ASSIGNED_BY_FORMATTED_NAME'] = $columns['ASSIGNED_BY_ID'] ? $this->getUserFormatName($item, 'ASSIGNED_BY') : '';

            \Bitrix\Crm\Tracking\UI\Grid::appendRows($item['TYPE'], $item['ID'], $columns);
            unset($columns['PHONE'], $columns['EMAIL'], $columns['WEB'], $columns['IM'], $columns['LINK']);

            return [
                'id' => CCrmOwnerType::ResolveName($item['TYPE']) . '_' . $item['ID'],
                'data' => $item,
                'columns' => $columns + CCrmViewHelper::RenderListMultiFields(
                        $item, "{$entityTypeName}_{$item['ID']}_", array('ENABLE_SIP' => true, 'SIP_PARAMS' => array('ENTITY_TYPE' => 'CRM_'.$entityTypeName, 'ENTITY_ID' => $item['ID'])))
            ];
        }, $agents);
    }

    protected function getFieldTextValue(int $entityTypeId, string $fieldName, $value)
    {
        $fields = $this->getEntityFieldsInfo($entityTypeId);
        if (!is_scalar($value) && !is_array($value)) {
            return $value;
        }

        $field = $fields[$fieldName];
        if (!$field) {
            return $value;
        }

        if (is_array($value) && $field['type'] === 'enumeration') {
            array_walk($value, function (&$item) use ($field) {
                $item = $field['value'][$item];
            });
            return implode(', ', $value);
        } elseif (isset($fields[$fieldName]['value'][$value])) {
            return $fields[$fieldName]['value'][$value];
        } elseif ($field['type'] === 'boolean') {
            if (in_array($value, ['Y', 'N'])) {
                $value = ($value === 'Y');
            }
            return filter_var($value, FILTER_VALIDATE_BOOL)  ? Loc::getMessage('GRID_FIELD_VALUE_YES') : Loc::getMessage('GRID_FIELD_VALUE_NO');
        }

        return $value;
    }

    protected function getEntityFieldsInfo(int $entityTypeId): array
    {
        return $this->getFieldsInfo()[$entityTypeId] ?? [];
    }

    protected function getEntityFieldInfo(int $entityTypeId, string $fieldName): ?array
    {
        return $this->getEntityFieldsInfo($entityTypeId)[$fieldName];
    }

    protected function getFieldsInfo(): array
    {
        static $fields = null;
        if (!is_null($fields)) {
            return $fields;
        }

        $fields = [];

        $contactFields = $this->getEntityFields(CCrmOwnerType::Contact);
        $companyFields = $this->getEntityFields(CCrmOwnerType::Company);

        $userFieldNames = array_merge(
            array_values($contactFields),
            array_values($companyFields)
        );

        $entityIdMap = [
            'CRM_CONTACT' => CCrmOwnerType::Contact,
            'CRM_COMPANY' => CCrmOwnerType::Company,
        ];

        $result = \Bitrix\Main\UserFieldTable::query()
            ->setSelect(['ID', 'ENTITY_ID', 'FIELD_NAME', 'USER_TYPE_ID', 'ENUM_ID' => 'ENUM.ID', 'ENUM_VALUE' => 'ENUM.VALUE'])
            ->whereIn('ENTITY_ID', array_keys($entityIdMap))
            ->whereIn('FIELD_NAME', $userFieldNames)
            ->registerRuntimeField(null, (new Reference(
                'ENUM',
                UserFieldEnumTable::getEntity(),
                Join::on('this.ID', 'ref.USER_FIELD_ID')
            ))->configureJoinType(Join::TYPE_LEFT))
            ->exec();

        while ($item = $result->fetch()) {
            $entityTypeId = $entityIdMap[$item['ENTITY_ID']];
            $alias = $this->getEntityFieldAlias($entityTypeId, $item['FIELD_NAME']) ?: $item['FIELD_NAME'];

            if (!isset($fields[$entityTypeId][$alias])) {
                $fields[$entityTypeId][$alias] = [
                    'name' => $item['FIELD_NAME'],
                    'alias' => $alias,
                    'type' => $item['USER_TYPE_ID'],
                ];
            }

            if ($item['USER_TYPE_ID'] === 'enumeration') {
                $fields[$entityTypeId][$alias]['value'][$item['ENUM_ID']] = $item['ENUM_VALUE'];
            }
        }

        $crmService = new CrmEnumService();

        $booleanFields = [
            'CONTACT_EXPORT'
        ];

        $enumFieldsData = [
            'CITY' => $crmService->getCities(),
            'REGION' => $crmService->getRegions(),
            'WEBFORM_ID' => $crmService->getWebformList(),
            'CONTACT_SOURCE_ID' => $crmService->getSources(),
            'COMPANY_INDUSTRY' => $crmService->getIndustries()
        ];

        foreach ($entityIdMap as $entityTypeId) {
            foreach ($booleanFields as $fieldName) {
                $fields[$entityTypeId][$fieldName] = [
                    'name' => $fieldName,
                    'alias' => $fieldName,
                    'type' => 'boolean',
                ];
            }

            foreach ($enumFieldsData as $fieldName => $valuesList) {

                    if (!isset($fields[$entityTypeId][$fieldName])) {
                        $fields[$entityTypeId][$fieldName] = [
                            'name' => $fieldName,
                            'alias' => $fieldName,
                            'type' => 'enumeration',
                        ];
                    }

                    $fields[$entityTypeId][$fieldName]['value'] = $valuesList;
            }
        }

        return $fields;
    }

    protected function getAgentsQuery(\Bitrix\Main\UI\Filter\Options $filterOptions, PageNavigation $navigation): Query
    {
        $userPermissions = Container::getInstance()->getUserPermissions();

        $securityOptions = (\Bitrix\Crm\Security\QueryBuilder\OptionsBuilder::makeFromArray([]))
            ->setOperations((array) UserPermissions::OPERATION_READ)
            ->setSkipCheckOtherEntityTypes(false);

        $query = $userPermissions->createListQueryBuilder(
            (new PermissionEntityTypeHelper(CCrmOwnerType::Contact))->getAllPermissionEntityTypesForEntity(),
            (clone $securityOptions)->setAliasPrefix('CT')->build()
        );
        $permissionSql = $query->build()->getSql();

        $contactQuery = ContactTable::query()
            ->setSelect(['ID', 'DATE_CREATE', 'TITLE' => 'FULL_NAME', 'TYPE', 'PHONE', 'EMAIL'])
            ->setCustomBaseTableAlias('CT')
            ->where(ContactRepository::FIELD_TYPE_ID, ContactRepository::TYPE_SUPPLIER)
            ->registerRuntimeField('TYPE', new ExpressionField('TYPE', sprintf('"%s"', CCrmOwnerType::Contact)))
            ->where(Query::filter()->whereExpr($permissionSql, []));

        $query = $userPermissions->createListQueryBuilder(
            (new PermissionEntityTypeHelper(CCrmOwnerType::Company))->getAllPermissionEntityTypesForEntity(),
            (clone $securityOptions)->setReadAllAllowed(true)->setAliasPrefix('CN')->build()
        );
        $permissionSql = $query->build()->getSql();

        $companyQuery = CompanyTable::query()
            ->setSelect(['ID', 'DATE_CREATE', 'TITLE', 'TYPE', 'PHONE', 'EMAIL'])
            ->setCustomBaseTableAlias('CN')
            ->where('COMPANY_TYPE', CompanyRepository::TYPE_OTHER)
            ->registerRuntimeField('TYPE', new ExpressionField('TYPE', sprintf('"%s"', CCrmOwnerType::Company)))
            ->where(Query::filter()->whereExpr($permissionSql, []));

        /** @var Query $query */
        foreach ([CCrmOwnerType::Contact => $contactQuery, CCrmOwnerType::Company => $companyQuery] as $entityTypeId => $query) {
            $this->injectFilterToQuery($entityTypeId, $query, $filterOptions);
        }

        $contactQuery
            ->union($companyQuery);

        return $contactQuery;
    }

    protected function getAgentsCount(Query $query): int
    {
        return (new Query($query))
            ->countTotal(true)
            ->exec()
            ->getCount();
    }

    protected function injectFilterToQuery(int $entityTypeId, Query $query, \Bitrix\Main\UI\Filter\Options $filterOptions): void
    {
        $filterFields = $this->getFilter();

        $filterParams = $filterOptions->getFilter($filterFields);
        if (empty($filterParams)) {
            return;
        }

        if (!empty($filterOptions->getSearchString())) {
            $searchString = str_replace('%', '%%', $filterOptions->getSearchString());
            $query->whereLike('TITLE', '%' . $searchString . '%');
        }

        if (is_numeric($filterParams['ID'])) {
            $query->where('ID', $filterParams['ID']);
        }

        $entityTypeName = CCrmOwnerType::ResolveName($entityTypeId);

        foreach ($filterFields as $field) {
            $isGeneral = !$field['sectionId'] || $field['sectionId'] === 'GENERAL';
            if (!$isGeneral && $field['sectionId'] !== $entityTypeName) {
                continue;
            }

            $columnId = $field['id'];
            $fieldId = $this->getEntityFieldName($entityTypeId, $columnId) ?: $columnId;
            $value = $filterParams[$columnId];

            if ($fieldId === 'TYPE' && !empty($value)) {
                $query->where('TYPE', $value);
                continue;
            } elseif ($fieldId === 'PHONE') {
                $phone = $filterParams['PHONE'] ? preg_replace('/\D/', '', $filterParams['PHONE']) : null;
                if ($phone) {
                    $query->whereLike('PHONE', '%' . $phone . '%');
                }
                continue;
            } elseif ($fieldId === 'EMAIL') {
                $email = $filterParams['EMAIL'] ? trim($filterParams['EMAIL']) : null;
                if ($email) {
                    $query->whereLike('EMAIL', '%' . $email . '%');
                }
                continue;
            }

            if ($field['type'] === 'date') {
                if (isset($filterParams[$columnId . '_from'])) {
                    if ($dateTime = \Bitrix\Main\Type\DateTime::tryParse($filterParams[$columnId . '_from'])) {
                        $query->where($fieldId, '>=', $dateTime);
                    }
                }
                if ($filterParams[$columnId . '_to']) {
                    if ($dateTime = \Bitrix\Main\Type\DateTime::tryParse($filterParams[$columnId . '_to'])) {
                        $query->where($fieldId, '<=', $dateTime);
                    }
                }
            } elseif ($field['type'] === 'number') {
                if (is_numeric($filterParams[$columnId . '_from'])) {
                    $query->where($fieldId, '>=', $filterParams[$columnId . '_to']);
                }

                if (is_numeric($filterParams[$columnId . '_to'])) {
                    $query->where($fieldId, '<=', $filterParams[$columnId . '_to']);
                }
            }

            if (is_null($value)) {
                continue;
            } elseif ($field['type'] === 'string') {
                $query->whereLike($fieldId, '%' . $value . '%');
            } elseif ($isGeneral && $field['type'] === 'list') {
                $entityFieldsInfo = $this->getEntityFieldInfo($entityTypeId, $columnId);
                if (!$entityFieldsInfo) {
                    continue;
                }

                $valueId = array_search($value, $entityFieldsInfo['value']);
                if ($valueId !== false) {
                    $query->where($fieldId, $valueId);
                }

            } elseif (!is_array_assoc($value)) {
                $query->whereIn($fieldId, $value);
            } else {
                $query->where($fieldId, $value);
            }
        }
    }

    protected function injectRuntimeFields(int $entityTypeId, Query $query): void
    {
        $companyReferences = [
            new Reference(
                'COMPANY_HEAD',
                CompanyTable::getEntity(),
                Join::on('this.' . CompanyRepository::FIELD_HEAD_COMPANY, 'ref.ID')
            ),
            new Reference(
                'CP',
                CompanyTable::getEntity(),
                Join::on('this.' . CompanyRepository::FIELD_PARTNER, 'ref.ID')
            )
        ];

        $query
            ->registerRuntimeField(null, new Reference(
                'RQ',
                \Bitrix\Crm\RequisiteTable::getEntity(),
                Join::on('this.ID', 'ref.ENTITY_ID')
                    ->where('ref.ENTITY_TYPE_ID', $entityTypeId)
            ));

        $references = [];
        switch ($entityTypeId) {
            case CCrmOwnerType::Company:
                $references = $companyReferences;
        }

        foreach ($references as $reference) {
            $query->registerRuntimeField(null, $reference);
        }
    }

    protected function getTimeFormat()
    {
        if (LayoutSettings::getCurrent()->isSimpleTimeFormatEnabled()) {
            return [
                'tommorow' => 'tommorow',
                's' => 'sago',
                'i' => 'iago',
                'H3' => 'Hago',
                'today' => 'today',
                'yesterday' => 'yesterday',
                //'d7' => 'dago',
                '-' => \Bitrix\Main\Type\DateTime::convertFormatToPhp(FORMAT_DATE)
            ];
        }

        return preg_replace('/:s$/', '', \Bitrix\Main\Type\DateTime::convertFormatToPhp(FORMAT_DATETIME));
    }

    protected function getToolbarItems(): array
    {
        return [
            [
                'TITLE' => Loc::getMessage('CRM_AGENT_EXPORT_EXCEL_TITLE'),
                'TEXT' => Loc::getMessage('CRM_AGENT_EXPORT_EXCEL'),
                'ONCLICK' => ($lockScript ?? "BX.UI.StepProcessing.ProcessManager.get('EXPORT_AGENT_EXCEL').showDialog()"),
                'ICON' => 'btn-export'
            ]
        ];
    }

    protected function getExportExcelParams(): array
    {
        $stExportId = 'EXPORT_AGENT';
        $componentName = 'citfact:crm.agent.list';
        return  [
            'id' => $stExportId. '_EXCEL',
            'controller' => 'citfact:sitecore.api.crm.export',
            'queue' => [
                [
                    'action' => 'dispatcher',
                ],
            ],
            'params' => [
                'SITE_ID' => SITE_ID,
                'ENTITY_TYPE' => 'AGENT',
                'EXPORT_TYPE' => 'excel',
                'COMPONENT_NAME' => $componentName,
                'signedParameters' => \Bitrix\Main\Component\ParameterSigner::signParameters(
                    $componentName,
                    [
                        'NAME_TEMPLATE' => $this->arParams['NAME_TEMPLATE'] ?? '',
                        'NAVIGATION_CONTEXT_ID' => 'AGENT'
                    ]
                ),
            ],
            'messages' => [
                'DialogTitle' => Loc::getMessage('AGENT_EXPORT_CSV_TITLE_MSGVER_1'),
                'DialogSummary' => Loc::getMessage('AGENT_STEXPORT_SUMMARY_MSGVER_1'),
            ],
            'dialogMaxWidth' => 650,
        ];
    }

    protected function getUserNameFields(string $prefix = '')
    {
        return [
            $prefix . '_LOGIN' => $prefix. '.LOGIN',
            $prefix . '_NAME' => $prefix. '.NAME',
            $prefix . '_LAST_NAME' => $prefix. '.LAST_NAME',
            $prefix . '_SECOND_NAME' => $prefix. '.SECOND_NAME',
        ];
    }

    protected function getUserFormatName(array $user, string $prefix = '',)
    {
        return CUser::FormatName(
            $this->arParams['NAME_TEMPLATE'] ?: CSite::GetNameFormat(),
            [
                'LOGIN' => $user[$prefix.'_LOGIN'] ?? null,
                'NAME' => $user[$prefix.'_NAME'] ?? null,
                'LAST_NAME' => $user[$prefix.'_LAST_NAME'] ?? null,
                'SECOND_NAME' => $user[$prefix.'_SECOND_NAME'] ?? null,
            ],
            true,
            false
        );
    }
}

Если сортировать таблицу по полям ASSIGNED_BY_ID или CREATED_BY_ID, то появляется ошибка:
UNCAUGHT_EXCEPTION - [Error]
Call to a member function getAlias() on null (0)
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/bitrix/modules/main/lib/orm/query/query.php:2554
#0: Bitrix\Main\ORM\Query\Query->buildQuery
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/bitrix/modules/main/lib/orm/query/query.php:942
#1: Bitrix\Main\ORM\Query\Query->exec
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/local/components/citfact/crm.agent.list/class.php:93
#2: CrmAgentsListComponents->executeComponent
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/bitrix/modules/main/classes/general/component.php:668
#3: CBitrixComponent->includeComponent
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/bitrix/modules/main/classes/general/main.php:1188
#4: CAllMain->IncludeComponent
/home/bitrix/ext_www/acorl.kerama-marazzi.ru/crm/agent/index.php:12

Как можно исправить ошибку?