Как в ячейке табличной части обычных форм 1С отобразить реквизит объекта вместо его наименования, сохранив ссылку?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке интерфейсов на платформе 1С:Предприятие 8 (особенно в Обычных формах) часто возникает классическая задача: необходимо, чтобы в ячейке табличного поля отображалось не стандартное наименование элемента справочника, а значение одного из его дополнительных реквизитов (например, артикул, внутренний код, числовой коэффициент или специальный текстовый идентификатор). При этом критически важно сохранить саму ссылку на объект в этой ячейке, чтобы пользователь мог в любой момент открыть карточку элемента или перевыбрать значение стандартным способом.

В этой статье мы подробно разберем, как решить эту задачу наиболее эффективными способами. Мы проанализируем механику работы платформы 1С с табличными полями, рассмотрим правильное использование события ПриПолученииДанных, а также разберем альтернативные архитектурные подходы, которые помогут оптимизировать производительность вашей информационной базы.

Почему стандартное «Основное представление» нам не подходит

У любого справочника в 1С есть свойство «Основное представление», которое может принимать значения «В виде наименования» или «В виде кода». Если мы изменим это свойство в конфигураторе, то во всех списках и полях ввода вместо наименования начнет отображаться код. Однако этот подход имеет два существенных недостатка:

  1. Изменение действует глобально по всей конфигурации, что чаще всего недопустимо, так как в других документах и отчетах пользователям по-прежнему нужно видеть стандартное наименование.
  2. В качестве основного представления можно выбрать только стандартные реквизиты: Код или Наименование. Если нам нужно вывести произвольный реквизит (например, созданный вручную реквизит «АртикулПоставщика» со значением вида «12,54»), стандартный механизм конфигуратора нам ничем не поможет.

Для локального решения этой задачи на конкретной форме нам потребуется программное управление выводом данных.

Решение №1: Оптимальный метод через событие «ПриПолученииДанных»

Наиболее гибким и правильным с точки зрения платформы 1С решением является использование обработчика события ПриПолученииДанных табличного поля. Это событие вызывается системой тогда, когда платформа подготовила порцию данных для отображения на экране пользователя.

Важное предупреждение: главная ошибка начинающих разработчиков при работе с обычными формами — обращение к реквизитам связанного справочника напрямую через точку в цикле оформления строк (например, ОформлениеСтроки.ДанныеСтроки.СсылкаНаСправочник.ИмяРеквизита). Такое обращение порождает неявный SQL-запрос к базе данных для каждой отображаемой строки. При прокрутке таблицы (скроллинге) интерфейс начнет сильно зависать, так как система будет выполнять сотни мелких запросов к СУБД (проблема N+1 запросов).

Чтобы избежать проблем с производительностью, мы применим метод пакетной выборки данных через один оптимизированный запрос. Разберем этот алгоритм по шагам:

  1. Создадим обработчик события ПриПолученииДанных для нашего табличного поля.
  2. Соберем все уникальные ссылки из отображаемых в данный момент строк в массив.
  3. Выполним один запрос к базе данных, выгрузив нужные реквизиты для всех полученных ссылок.
  4. Поместим результат запроса в объект Соответствие для мгновенного поиска.
  5. Обойдем строки повторно и установим нужный текст в ячейках.

Рассмотрим пример реализации этого механизма на встроенном языке 1С:


Процедура ТабличнаяЧастьПриПолученииДанных(Элемент, ОформленияСтрок)
    
    // Шаг 1. Собираем только уникальные и непустые ссылки из видимых строк
    МассивСсылок = Новый Массив;
    Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
        // Получаем данные текущей строки табличной части
        ДанныеСтроки = ОформлениеСтроки.ДанныеСтроки;
        Если ДанныеСтроки <> Неопределено Тогда
            СсылкаНаОбъект = ДанныеСтроки.Номенклатура; // Замените на имя вашей колонки
            Если ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
                МассивСсылок.Добавить(СсылкаНаОбъект);
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;
    
    Если МассивСсылок.Количество() = 0 Тогда
        Возврат;
    КонецЕсли;
    
    // Шаг 2. Выполняем один запрос для получения нужного реквизита по всем ссылкам
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.ДополнительныйКод КАК ОтображаемыйКод
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура
        |ГДЕ
        |   Номенклатура.Ссылка В (&МассивСсылок)";
        
    Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
    ТаблицаРезультата = Запрос.Выполнить().Выгрузить();
    
    // Шаг 3. Заполняем Соответствие для быстрой выборки (поиск в ОЗУ за O(1))
    СоответствиеКодов = Новый Соответствие;
    Для Каждого СтрокаРезультата Из ТаблицаРезультата Цикл
        СоответствиеКодов.Вставить(СтрокаРезультата.Ссылка, СтрокаРезультата.OтображаемыйКод);
    КонецЦикла;
    
    // Шаг 4. Устанавливаем текст в ячейках табличного поля
    Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
        ДанныеСтроки = ОформлениеСтроки.ДанныеСтроки;
        Если ДанныеСтроки <> Неопределено Тогда
            СсылкаНаОбъект = ДанныеСтроки.Номенклатура;
            Если ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
                // Ищем значение реквизита в нашем соответствии
                НайденныйКод = СоответствиеКодов.Получить(СсылкаНаОбъект);
                Если НайденныйКод <> Неопределено Тогда
                    // Устанавливаем именно ТЕКСТ ячейки, при этом значение-ссылка сохраняется
                    ОформлениеСтроки.Ячейки.Номенклатура.УстановитьТекст(Строка(НайденныйКод));
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Этот метод работает чрезвычайно быстро, так как обращение к базе данных происходит ровно один раз для всей порции видимых строк (обычно это 20-30 строк на экране), а сопоставление данных в оперативной памяти через объект Соответствие выполняется мгновенно.

Альтернатива №1: Кэширование данных при открытии формы

Если справочник, реквизиты которого необходимо отображать, содержит небольшое количество элементов (например, до 2000–3000 записей) и эти данные практически не изменяются в процессе работы пользователей, мы можем полностью отказаться от запросов во время прокрутки списка.

Для этого мы можем закэшировать все необходимые данные в переменную модуля формы при ее инициализации. Проанализируем, как это реализовать:

  1. В самом верху модуля формы объявим переменную, которая будет хранить наше соответствие.
  2. В обработчике события формы ПриОткрытии заполним эту переменную одним запросом ко всему справочнику.
  3. В событии ПриПолученииДанных будем просто брать готовые текстовые значения из этой переменной по ссылке-ключу.

Посмотрим на пример реализации данного подхода:


Перем мКэшКодовЭкземпляров; // Переменная доступна во всех процедурах модуля формы

Процедура ПриОткрытии()
    
    мКэшКодовЭкземпляров = Новый Соответствие;
    
    // Запрашиваем данные по всему справочнику один раз
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   Номенклатура.Ссылка КАК Ссылка,
        |   Номенклатура.ДополнительныйКод КАК ОтображаемыйКод
        |ИЗ
        |   Справочник.Номенклатура КАК Номенклатура";
        
    Выборка = Запрос.Выполнить().Выбрать();
    Пока Выборка.Следующий() Цикл
        мКэшКодовЭкземпляров.Вставить(Выборка.Ссылка, Строка(Выборка.ОтображаемыйКод));
    КонецЦикла;

КонецПроцедуры

Процедура ТабличнаяЧастьПриПолученииДанных(Элемент, ОформленияСтрок)
    
    Для Каждого ОформлениеСтроки Из ОформленияСтрок Цикл
        ДанныеСтроки = ОформлениеСтроки.ДанныеСтроки;
        Если ДанныеСтроки <> Неопределено Тогда
            СсылкаНаОбъект = ДанныеСтроки.Номенклатура;
            Если ЗначениеЗаполнено(СсылкаНаОбъект) Тогда
                // Получаем значение напрямую из кэша в памяти, без обращений к БД
                НайденныйКод = мКэшКодовЭкземпляров.Получить(СсылкаНаОбъект);
                Если НайденныйКод <> Неопределено Тогда
                    ОформлениеСтроки.Ячейки.Номенклатура.УстановитьТекст(НайденныйКод);
                КонецЕсли;
            КонецЕсли;
        КонецЕсли;
    КонецЦикла;

КонецПроцедуры

Преимущество: абсолютная плавность интерфейса. Прокрутка таблицы будет происходить мгновенно, так как нагрузка на сервер базы данных при скроллинге равна нулю.

Недостаток: если база данных очень большая (сотни тысяч элементов), кэширование потребует слишком много оперативной памяти. Также, если другой пользователь изменит код в справочнике, текущая форма не узнает об этом до перезапуска.

Альтернатива №2: Создание вспомогательной колонки в табличной части

Еще один архитектурно красивый и надежный метод заключается в денормализации данных: мы добавляем вспомогательный текстовый реквизит непосредственно в саму табличную часть нашего документа или обработки (например, реквизит КодДляОтображения с типом Строка).

Разберем по шагам логику этого решения:

  1. В конфигураторе добавляем в табличную часть реквизит КодДляОтображения.
  2. На форме размещаем колонку, связанную с этим реквизитом, и делаем ее доступной только для чтения (свойство ТолькоПросмотр = Истина).
  3. Для колонки с самой ссылкой (которая теперь может быть скрыта или находиться рядом) мы настраиваем событие ПриИзменении.
  4. При выборе пользователем нового элемента справочника мы программно считываем его код и записываем его в строку нашей табличной части.

Посмотрим на пример кода для обработчика изменения ячейки:


Процедура ТабличнаяЧастьНоменклатураПриИзменении(Элемент)
    
    // Получаем текущую строку табличной части
    СтрокаТЧ = ЭлементыФормы.ТабличнаяЧасть.ТекущиеДанные;
    Если СтрокаТЧ <> Неопределено Тогда
        Если ЗначениеЗаполнено(СтрокаТЧ.Номенклатура) Тогда
            // Записываем код связанного объекта напрямую в реквизит строки
            СтрокаТЧ.КодДляОтображения = СтрокаТЧ.Номенклатура.ДополнительныйКод;
        Иначе
            СтрокаТЧ.КодДляОтображения = "";
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

Преимущества метода:

Альтернатива №3: Строковое поле с программным вызовом формы выбора

В случаях, когда нам физически не нужно хранить в базе данных ссылку на справочник, а требуется сохранять только строковое значение кода, но при этом предоставить пользователю удобный интерфейс интерактивного выбора, можно использовать программный перехват события начала выбора.

Разберем, как это реализовать:

  1. Тип реквизита нашей табличной части оставляем строго текстовым (Строка).
  2. В свойствах элемента управления ячейки на форме включаем свойство КнопкаВыбора.
  3. Создаем обработчик события НачалоВыбора для этого поля.

Проанализируем пример программного вызова формы выбора:


Процедура ТабличнаяЧастьКодНачалоВыбора(Элемент, СтандартнаяОбработка)
    
    // Отменяем стандартное поведение системы
    СтандартнаяОбработка = Ложь;
    
    // Программно открываем форму выбора нужного справочника
    ФормаВыбора = Справочники.Номенклатура.ПолучитьФормуВыбора("ФормаВыбора", Элемент, Элемент);
    ВыбранноеЗначение = ФормаВыбора.ОткрытьМодально();
    
    // Если пользователь выбрал элемент, извлекаем его реквизит и записываем в ячейку строку
    Если ВыбранноеЗначение <> Неопределено И НЕ ВыбранноеЗначение.Пустая() Тогда
        СтрокаТЧ = ЭлементыФормы.ТабличнаяЧасть.ТекущиеДанные;
        Если СтрокаТЧ <> Неопределено Тогда
            СтрокаТЧ.Код = ВыбранноеЗначение.ДополнительныйКод;
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

При таком подходе в базе данных хранится чистая текстовая строка, но для конечного пользователя процесс заполнения выглядит точно так же, как выбор элемента из справочника.

Почему не стоит использовать событие «ПриВыводеСтроки» для этой задачи

В Обычных формах 1С существует еще одно событие табличного поля — ПриВыводеСтроки. На первый взгляд оно кажется проще в реализации, так как в его параметрах сразу доступен объект оформления конкретной строки. Однако использовать его для запросов к базе данных категорически запрещено.

Событие ПриВыводеСтроки вызывается платформой непрерывно: при малейшем движении мыши по таблице, при изменении фокуса, при изменении размеров окна и при любом скроллинге. Если поместить туда запрос или обращение через точку к реквизитам ссылки, то приложение мгновенно начнет тормозить даже на мощных серверах. Данное событие предназначено исключительно для легких визуальных операций оформления (например, закрасить фон строки серым цветом, если в текущих данных строки стоит флаг удаления).

Резюме

Для реализации отображения реквизита вместо ссылки в Обычных формах 1С рекомендуем придерживаться следующих правил:

← На главную