При разработке интерфейсов на обычных формах в 1С:Предприятии программисты часто сталкиваются с задачей отображения связанных данных. Типичный пример — вывод в форме элемента справочника (например, "Контрагенты") списка записей из регистра сведений, которые относятся исключительно к текущему элементу. Однако при настройке табличного поля разработчики нередко путают два принципиально разных типа данных, выступающих в роли источника: РегистрСведенийСписок и РегистрСведенийНаборЗаписей. Это приводит к тому, что отборы либо не применяются вовсе, либо данные на форме не обновляются.
В этой статье мы подробно разберем, в чем заключается разница между этими типами данных, как правильно настраивать отборы для каждого из них, почему возникают популярные ошибки компиляции и как их оперативно исправить.
Прежде чем переходить к написанию кода, давайте разберем внутреннюю логику работы платформы 1С с этими объектами. От этого напрямую зависит способ программного управления ими.
РегистрСведенийСписок — это динамический список. Его главная задача — отображать данные из базы данных порциями (по мере прокрутки пользователем). Он работает напрямую с таблицей базы данных. Когда пользователь редактирует строку в таком списке, изменения записываются в базу данных немедленно в момент окончания редактирования строки. Этот тип данных идеально подходит, если вам нужно просто показать историю записей или предоставить интерфейс для быстрого добавления/удаления строк без привязки к транзакции записи основного документа или справочника.
РегистрСведенийНаборЗаписей — это объектная сущность, представляющая собой коллекцию записей в оперативной памяти. Он считывает из базы данных сразу весь объем информации, соответствующий установленному отбору. Изменения, вносимые пользователем в таблицу на форме, связанную с набором записей, не записываются в базу данных автоматически. Они сохраняются в памяти до тех пор, пока вы явно не вызовете метод Записать(). Это незаменимо, когда логика работы требует, чтобы изменения в регистре сведений сохранялись только одновременно с сохранением самого элемента справочника или документа (например, при нажатии кнопки "ОК" или "Записать" в форме).
Теперь, понимая эту разницу, рассмотрим пошагово реализацию отбора для обоих вариантов.
Представим ситуацию: мы добавили на форму справочника табличное поле, связали его с реквизитом формы СтатьиДекларации, имеющим тип РегистрСведенийНаборЗаписей.ИмяВашегоРегистра. Нам нужно отфильтровать записи по текущему контрагенту (владельцу формы).
Если мы попытаемся использовать свойство ОтборСтрок у табличного поля на форме, мы столкнемся с тем, что строки не будут отбираться при открытии, либо визуально таблица останется пустой. Правильный алгоритм работы с набором записей выглядит совершенно иначе: мы должны настраивать отбор непосредственно у самого объекта данных (реквизита формы), а затем принудительно считывать данные из базы.
Разберем этот процесс по шагам.
Шаг 1. Установка отбора и чтение данных
В обработчике события формы ПриОткрытии (или ПередОткрытием) нам необходимо написать следующий код:
// 1. Сначала обращаемся к свойству Отбор реквизита формы (набора записей)
// и устанавливаем фильтр по нужному измерению.
// Метод Установить() очищает предыдущие значения отбора по этому полю,
// задает новое значение и сразу активирует использование отбора.
СтатьиДекларации.Отбор.Контрагент.Установить(Ссылка);
// 2. Теперь необходимо дать платформе команду прочитать данные из базы данных
// с учетом только что наложенного отбора.
СтатьиДекларации.Прочитать();
Обратите внимание: как только выполнится метод Прочитать(), связанное с этим реквизитом табличное поле на форме обновится автоматически. Никаких методов перерисовки интерфейса вызывать не требуется.
Шаг 2. Запись измененных данных
Поскольку это набор записей, данные при изменении в таблице формы остаются "висеть" в оперативной памяти. Чтобы изменения попали в базу данных, нам нужно переопределить стандартное сохранение формы. В обработчике события формы ПослеЗаписи напишем:
Процедура ПослеЗаписи(Отказ)
// Записываем накопленные в наборе изменения в базу данных.
// Платформа автоматически перезапишет в БД только те строки,
// которые соответствуют установленному ранее отбору.
СтатьиДекларации.Записать();
КонецПроцедуры
Важное ограничение для Набора Записей: Отбор в объекте типа РегистрСведенийНаборЗаписей можно устанавливать только по измерениям регистра и только на равенство (вид сравнения ВидСравнения.Равно). Если вы попытаетесь отфильтровать набор записей по ресурсу или реквизиту регистра, платформа выдаст ошибку во время выполнения.
В процессе обсуждения на форумах часто упоминается проблема: при вызове метода Прочитать() система выдает ошибку о недостатке параметров. Выясним, почему это происходит и как этого избежать.
Метод Прочитать() у объекта РегистрСведенийНаборЗаписей действительно не принимает никаких параметров. Ошибка чаще всего возникает из-за следующих ошибок разработчика:
ЭлементыФормы.СтатьиДекларации.Прочитать(). У элемента формы нет такого метода, либо вызывается метод другого объекта с похожим именем, требующий параметров. Обращаться нужно напрямую к реквизиту формы: СтатьиДекларации.Прочитать().Прочитать() ошибочно пишется метод Получить(). Метод Получить(ИндексСтроки) у коллекции записей требует передачи индекса строки. Его вызов без параметров как раз и генерирует ошибку "Недостаточно фактических параметров".СтатьиДекларации.Прочитать(Ссылка). Это также приводит к синтаксической ошибке.Если специфика вашей задачи не требует транзакционной записи набора данных вместе с владельцем формы, проще и эффективнее использовать динамический список РегистрСведенийСписок. В этом случае данные будут записываться сразу по окончании редактирования каждой строки.
Рассмотрим, как правильно настроить отбор для этого типа данных. В обработчике ПриОткрытии пишем следующий код:
// Устанавливаем отбор по измерению Контрагент для динамического списка.
// Метод Установить() также подходит для списков.
СтатьиДекларации.Отбор.Контрагент.Установить(Ссылка);
Если в процессе работы формы ссылка на контрагента изменилась (например, при первом создании элемента, когда ссылка была пустой, а затем объект был записан и получил реальную ссылку), динамический список нужно обновить программно. Для этого используется метод Обновить() самого списка:
// Принудительно заставляем динамический список перечитать данные из БД
СтатьиДекларации.Обновить();
Важно: Не путайте метод обновления источника данных Обновить() с методом визуального интерфейса ЭлементыФормы.СтатьиДекларации.ОбновитьСтроки(). Метод ОбновитьСтроки() лишь заставляет видеосистему перерисовать уже загруженные в табличное поле строки на экране монитора, но он не отправляет запрос в базу данных для получения актуальных отфильтрованных строк!
Если вам требуется сложная логика фильтрации (например, отбор по ресурсам, реквизитам регистра, использование сложных видов сравнения вроде "В списке" или "Не равно"), использовать напрямую РегистрСведенийНаборЗаписей становится крайне неудобно из-за его жестких ограничений.
В таких случаях мы рекомендуем использовать универсальный паттерн проектирования через временную таблицу значений на форме. Проанализируем этот алгоритм:
ТЗ_Статьи) и связываем с ней наше табличное поле на форме.ГДЕ запроса мы можем указать абсолютно любые условия и отборы, не ограничиваясь только измерениями регистра.
// Заполняем таблицу значений на форме данными из запроса
ТЗ_Статьи = РезультатЗапроса.Выгрузить();
ТЗ_Статьи и вызываем метод Записать().Этот подход гарантирует максимальную гибкость и избавляет от жестких ограничений платформы, накладываемых на стандартные отборы наборов записей.