При разработке интерфейсов на платформе 1С:Предприятие 8.3 программисты часто сталкиваются с необходимостью отображения в форме списка не только основных реквизитов документа, но и специфических данных из его табличных частей. Типичный пример — вывод комментария из самой первой строки списка работ или состава товаров. Попытка решить эту задачу «в лоб» через программное обращение к индексу коллекции в пути к данным часто приводит к неудаче. В этой статье мы подробно разберем, почему так происходит, и проанализируем три эффективных способа, как реализовать программное изменение форм для решения этой задачи — для этого подойдут инструменты для настройки и оптимизации списков.
Рассмотрим ситуацию, с которой столкнулся автор темы. Он попытался программно добавить поле в форму списка и указать путь к данным следующим образом: Список.Ссылка.Работы[0].Комментарий. На первый взгляд логика понятна: мы обращаемся к ссылке текущей строки, переходим к табличной части Работы, выбираем первую строку по индексу 0 и берем поле Комментарий.
Однако выясним причину, по которой этот код не будет работать. Динамический список в 1С — это не массив объектов в оперативной памяти, а механизм, который генерирует SQL-запрос к базе данных. Система компоновки данных (СКД), лежащая в основе списка, транслирует ваши инструкции в запросы к СУБД. В языке SQL не существует понятия «индекс строки табличной части» в том виде, в котором мы привыкли видеть его в объектной технике 1С. Табличная часть — это отдельная физическая таблица, связанная с основной таблицей документа по полю Ссылка. Чтобы получить данные из нее, необходимо выполнить полноценное соединение таблиц — для этого подойдёт вывод оплат и отгрузок в списках заказов.
Проанализируем три подхода к решению этой проблемы: от «костыльных» до профессиональных.
Самый правильный и производительный метод — это модификация самого текста запроса, на котором строится список — поможет инструменты разработчика для работы с запросами. Вместо того чтобы пытаться «вытащить» данные через точку от ссылки, мы добавим нужное поле непосредственно в выборку основного запроса. Рассмотрим, как это сделать программно в процедуре ПриСозданииНаСервере.
Нам потребуется использовать вложенный запрос, который найдет минимальный номер строки для каждого документа, а затем соединить этот результат с основной таблицей. Такое добавление реквизитов и элементов на управляемые формы позволяет сохранить гибкость интерфейса. Разберем пример кода:
Процедура УР_ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
// 1. Модифицируем текст запроса динамического списка
// Добавляем левое соединение с подзапросом, который выбирает первую строку ТЧ
ТекстЗапроса = Список.ТекстЗапроса;
// Подготовим новый текст с использованием псевдонима ПерваяСтрокаКомментарий
НовыйТекстЗапроса =
"ВЫБРАТЬ
| ОсновнаяТаблица.Ссылка КАК Ссылка,
| ОсновнаяТаблица.Номер КАК Номер,
| ОсновнаяТаблица.Дата КАК Дата,
| ТЧ_ПерваяСтрока.Комментарий КАК ПерваяСтрокаКомментарий
|ИЗ
| Документ.НазваниеВашегоДокумента КАК ОсновнаяТаблица
| ЛЕВОЕ СОЕДИНЕНИЕ (
| ВЫБРАТЬ
| ТЧ.Ссылка КАК Ссылка,
| МИНИМУМ(ТЧ.НомерСтроки) КАК НомерСтроки
| ИЗ
| Документ.НазваниеВашегоДокумента.Работы КАК ТЧ
| ГРУППИРОВАТЬ ПО
| ТЧ.Ссылка
| ) КАК ТЧ_МинНомер НА ОсновнаяТаблица.Ссылка = ТЧ_МинНомер.Ссылка
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.НазваниеВашегоДокумента.Работы КАК ТЧ_ПерваяСтрока
| НА ТЧ_МинНомер.Ссылка = ТЧ_ПерваяСтрока.Ссылка
| И ТЧ_МинНомер.НомерСтроки = ТЧ_ПерваяСтрока.НомерСтроки";
Список.ТекстЗапроса = НовыйТекстЗапроса;
// 2. Программно добавляем элемент формы
НЭлемент = Элементы.Добавить("УР_Описание", Тип("ПолеФормы"), Элементы.Список);
НЭлемент.Вид = ВидПоляФормы.ПолеВвода;
НЭлемент.ПутьКДанным = "Список.ПерваяСтрокаКомментарий"; // Ссылаемся на поле из запроса
КонецПроцедуры
Преимущества этого метода: данные выбираются одним запросом, обеспечивается высокая скорость прокрутки списка, нет избыточности данных в базе.
Если логика выбора строки очень сложная (например, нужно выбрать не просто первую строку, а первую строку с определенным статусом или содержащую определенный текст), можно воспользоваться обработчиком ПриПолученииДанныхНаСервере. Похожий механизм используется, когда нужно реализовать флажок в динамическом списке — есть готовый индикатор прикрепленных файлов в журналах документов.
В этом событии мы получаем доступ к уже отобранным строкам, которые система готовит к выводу на экран. Мы можем обойти их и программно заполнить значения в оформлении строк.
Алгоритм действий:
ПриПолученииДанныхНаСервере собираем все ссылки документов, попавших в выборку.Строки.Рассмотрим пример кода для этого события:
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Строки)
МассивСсылок = Строки.ПолучитьКлючи();
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТЧ.Ссылка КАК Ссылка,
| ТЧ.Комментарий КАК Комментарий
|ИЗ
| Документ.НазваниеДокумента.Работы КАК ТЧ
|ГДЕ
| ТЧ.Ссылка В(&МассивСсылок)
| И ТЧ.НомерСтроки = 1"; // Упрощенно для первой строки
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
ТекущаяСтрокаСписок = Строки[Выборка.Ссылка];
ТекущаяСтрокаСписок.Данные["УР_Описание"] = Выборка.Комментарий;
КонецЦикла;
КонецПроцедуры
Важно помнить: этот метод может замедлить работу интерфейса при быстрой прокрутке, так как код выполняется каждый раз при обновлении состава строк на экране.
Проанализируем решение, к которому в итоге пришел автор вопроса. Он добавил в расширении дополнительный реквизит документа (например, УР_ПервыйКомментарий), который заполняется в момент записи документа значением из табличной части. После этого в списке просто выводится этот реквизит.
Рассмотрим, как это реализовать:
УР_ПервыйКомментарий (строка, неограниченная длина или нужной длины).ПередЗаписью пишем код:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Работы.Количество() > 0 Тогда
УР_ПервыйКомментарий = Работы[0].Комментарий;
Иначе
УР_ПервыйКомментарий = "";
КонецЕсли;
КонецПроцедуры
Посмотрим на этот метод критически. С одной стороны, это обеспечивает максимальную производительность формы списка, так как платформе не нужно делать никаких соединений. С другой стороны, мы получаем избыточность данных. Если табличная часть будет изменена программно с отключенными обработчиками событий, данные в списке станут неактуальными. Также это решение требует обновления всех существующих документов в базе (обработки), чтобы заполнить новый реквизит.
Мы выяснили, что прямое обращение к строкам табличной части через ПутьКДанным в динамических списках невозможно из-за специфики работы SQL-запросов. Для решения задачи программного создания поля мы рекомендуем использовать изменение текста запроса (Способ 1), так как это наиболее сбалансированный вариант между сложностью разработки и производительностью системы.
При программном создании элемента формы всегда убеждайтесь, что поле, указанное в НЭлемент.ПутьКДанным, действительно присутствует в колонках динамического списка. Иногда после добавления требуется выполнить программное перемещение колонки динамического списка, чтобы данные отображались в удобном для пользователя месте. Использование префикса Список. обязательно при программном добавлении элементов, связанных с данными динамического списка.