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