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