Как заполнить табличную часть документа данными из запроса в 1С v8.3

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

В процессе разработки в среде «1С:Предприятие» часто возникает задача автоматического заполнения табличных частей (ТЧ) документов на основе уже существующих данных. Например, когда необходимо собрать различных ответственных лиц (автора документа, текущего менеджера, исполнителей из других табличных частей) и поместить их в единый список уведомлений прямо внутри этого же документа.

В данной статье мы подробно разберем, как правильно реализовать этот функционал на сервере. Мы изучим метод прямого запроса к базе данных с использованием операторов объединения, проанализируем частые ошибки начинающих разработчиков и рассмотрим альтернативный способ заполнения «на лету» для документов, которые еще не были сохранены в базу данных.

Постановка задачи и анализ исходных данных

Представим сценарий: у нас есть документ ЗаказКлиента. В его шапке определены реквизиты АвторЗаказа и ТекущийМенеджер (тип ссылка на справочник Пользователи). Также в документе присутствует табличная часть ЭтапыВыполнения, где в каждой строке указан свой Менеджер.

Наша цель — заполнить другую табличную часть этого же документа, которая называется Уведомляемые. В нее должны попасть все вышеперечисленные лица без дублирования и пустых значений, чтобы в дальнейшем система могла отправить им сервисные сообщения.

Способ 1. Заполнение табличной части через запрос с объединением (Рекомендуемый)

Если документ уже существует в базе данных (то есть он записан), самым быстрым и производительным решением будет использование запроса к СУБД с последующей загрузкой результата в табличную часть.

Давайте проанализируем по шагам структуру такого запроса. Мы будем использовать оператор ОБЪЕДИНИТЬ (без ключевого слова ВСЕ). Это позволит СУБД автоматически исключить дублирующиеся записи сотрудников еще на этапе выполнения запроса.

Рассмотрим готовый код серверной процедуры:


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

Давайте разберем критически важные аспекты этого кода:

  1. Обязательное наличие условий отбора ГДЕ: Начинающие разработчики часто забывают указать отбор по конкретной ссылке. Если убрать конструкцию ГДЕ ЗаказКлиента.Ссылка = &ТекущийДокумент, система выберет данные изо всех существующих заказов в базе и попытается загрузить этот колоссальный объем в один документ, что приведет к зависанию программы.
  2. Исключение пустых ссылок: Использование конструкции <> ЗНАЧЕНИЕ(Справочник.Пользователи.ПустаяСсылка) гарантирует, что если какое-то поле (например, текущий менеджер) не заполнено, в табличную часть Уведомляемые не запишется пустая строка.
  3. Совпадение имен колонок: Обратите внимание на первый запрос пакета объединения: ЗаказКлиента.АвторЗаказа КАК Уведомляемый. Имя псевдонима колонки в запросе должно символ в символ совпадать с именем реквизита вашей табличной части. Если в ТЧ колонка называется Уведомляемый, то и в первой выборке запроса псевдоним должен быть аналогичным. В последующих частях объединения имена колонок автоматически сопоставляются по порядку их следования.

Важные нюансы работы метода Загрузить()

Использование метода Объект.Уведомляемые.Загрузить(ТаблицаРезультата) — это наиболее быстрый и лаконичный способ перенести данные из запроса. Однако помните о следующих особенностях:


Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
    // Ищем, нет ли уже такого пользователя в ТЧ, чтобы не дублировать
    ПараметрыОтбора = Новый Структура("Уведомляемый", Выборка.Уведомляемый);
    НайденныеСтроки = Объект.Уведомляемые.НайтиСтроки(ПараметрыОтбора);
    
    Если НайденныеСтроки.Количество() = 0 Тогда
        НоваяСтрока = Объект.Уведомляемые.Добавить();
        ЗаполнитьЗначенияСвойств(НоваяСтрока, Выборка);
    КонецЕсли;
КонецЦикла;

Способ 2. Заполнение табличной части «на лету» без обращения к базе данных

Рассмотрим важную проблему: что делать, если пользователь только что создал документ на форме, заполнил реквизиты, но еще ни разу его не сохранил (кнопка «Записать» не нажималась)?

В этой ситуации обращение к базе данных через запрос не принесет результата. В СУБД физически нет записи о новом документе, и запрос вернет пустой результат. Для таких интерактивных сценариев правильнее обрабатывать данные непосредственно в оперативной памяти сервера приложений, работая напрямую с реквизитами формы объекта.

Посмотрим на пример реализации такого алгоритма с использованием коллекции Соответствие, которая автоматически решает проблему уникальности записей (исключает дубли):


&НаСервере
Процедура ЗаполнитьИзДанныхФормыНаСервере()
    
    // Используем Соответствие для автоматического отсеивания дублей
    УникальныеУведомляемые = Новый Соответствие;
    
    // Анализируем автора заказа
    Если ЗначениеЗаполнено(Объект.АвторЗаказа) Тогда
        УникальныеУведомляемые.Вставить(Объект.АвторЗаказа, Истина);
    КонецЕсли;
    
    // Анализируем текущего менеджера
    Если ЗначениеЗаполнено(Объект.ТекущийМенеджер) Тогда
        УникальныеУведомляемые.Вставить(Объект.ТекущийМенеджер, Истина);
    КонецЕсли;
    
    // Обходим табличную часть этапов выполнения, которая находится в памяти
    Для Каждого СтрокаЭтапа Из Объект.ЭтапыВыполнения Цикл
        Если ЗначениеЗаполнено(СтрокаЭтапа.Mенеджер) Тогда
            УникальныеУведомляемые.Вставить(СтрокаЭтапа.Mенеджер, Истина);
        КонецЕсли;
    КонецЦикла;
    
    // Переносим собранные уникальные данные в целевую табличную часть
    Объект.Уведомляемые.Очистить();
    Для Каждого КлючИЗначение Из УникальныеУведомляемые Цикл
        НоваяСтрока = Объект.Уведомляемые.Добавить();
        НоваяСтрока.Уведомляемый = КлючИЗначение.Ключ;
    КонецЦикла;
    
КонецПроцедуры

Данный подход работает мгновенно, не нагружает СУБД лишними транзакциями и идеально подходит для вызова при изменении реквизитов формы прямо во время интерактивной работы пользователя.

Разбор распространенных ошибок начинающих разработчиков

Разберем две классические ошибки, с которыми сталкиваются программисты при попытке реализовать подобную задачу.

Ошибка 1: Неопознанный оператор "Выбрать" на сервере. Часто при написании кода разработчики пытаются объявить текст запроса прямо в модуле, забывая, что встроенный язык 1С и язык запросов СУБД — это разные вещи. Текст запроса всегда должен находиться внутри строкового литерала (в кавычках):


// ТАК ДЕЛАТЬ НЕЛЬЗЯ (вызовет ошибку компиляции модулей):
Запрос.Текст = ВЫБРАТЬ АвторЗаказа ИЗ Документ.ЗаказКлиента;

// ПРАВИЛЬНЫЙ ВАРИАНТ:
Запрос.Текст = "ВЫБРАТЬ АвторЗаказа ИЗ Документ.ЗаказКлиента";

Ошибка 2: Присвоение таблицы значений реквизиту строки. Иногда разработчики пытаются присвоить выгруженный результат запроса (который является целой таблицей значений) в качестве значения конкретной ячейки строки ТЧ:


// ОШИБОЧНЫЙ КОД:
НоваяСтрока = Объект.Уведомляемые.Добавить();
НоваяСтрока.Уведомляемый = Запрос.Выполнить().Выгрузить(); // Ошибка! Попытка записать таблицу в поле ссылки.

Помните, что ячейка строки ТЧ хранит атомарное значение (в нашем примере — ссылку на элемент справочника Пользователи). Результат запроса — это двумерный массив данных (таблица), который необходимо либо перебирать построчно в цикле, либо загружать целиком с помощью специализированного метода Загрузить() всего объекта ТЧ.

← На главную