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