Как заполнить табличную часть документа или реквизит формы данными из запроса, используя метод ЗаполнитьЗначенияСвойств()?

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

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

Исходная проблема: Почему структура не заполняет табличную часть?

Представим, что у нас есть задача: при выборе номенклатуры в одной табличной части формы (например, "Товары") необходимо получить информацию об ее остатках на складах и заполнить ими другую табличную часть формы (например, "ОстаткиНаСкладах"). Автор столкнулся с проблемой, когда его код не заполнял табличную часть.

Давайте рассмотрим исходный код автора, чтобы выявить основные причины, по которым механизм не работал так, как ожидалось:


&НаСервереБезКонтекста Функция ТоварыНоменклатураАвтоПодборНаСервере(СсылкаНом)
    ДанныеОстатки = Новый Структура; // Проблема №1
    Запрос = Новый Запрос("ВЫБРАТЬ 
                         | ТоварыНаСкладахОстатки.Склад КАК Склад, 
                         | ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ОстатокНаСкладе, 
                         | ТоварыНаСкладахОстатки.КОтгрузкеОстаток КАК РезервНаСкладе 
                         |ИЗ 
                         | РегистрНакопления.ТоварыНаСкладах.Остатки КАК ТоварыНаСкладахОстатки 
                         |ГДЕ 
                         | ТоварыНаСкладахОстатки.Номенклатура = &Номенклатура");
    Запрос.УстановитьПараметр("Номенклатура", СсылкаНом);
    Результат = Запрос.Выполнить;
    Если Результат.Пустой Тогда
        Сообщить ("Нет данных по остаткам выбранной номенклатуры на складе");
    Иначе
        ТЗ = Результат.Выбрать;
        Если ТЗ.Следующий Тогда
            ЗаполнитьЗначенияСвойств(ДанныеОстатки, ТЗ); // Проблема №2
        КонецЕсли;
    КонецЕсли;
    Возврат ДанныеОстатки;
КонецФункции

&НаКлиенте
Процедура ТоварыНоменклатураАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
    ТекДанные = Элементы.Товары.ТекущиеДанные;
    ДанныеОстатки = ТоварыНоменклатураАвтоПодборНаСервере(ТекДанные.Номенклатура);
    ЗаполнитьЗначенияСвойств(Элементы.ОстаткиНаСкладах, ДанныеОстатки); // Проблема №3
КонецПроцедуры

Проанализируем выявленные проблемы:

  1. Проблема №1: Создание пустой структуры без полей. Строка ДанныеОстатки = Новый Структура; создает структуру, которая изначально не содержит никаких полей. Метод ЗаполнитьЗначенияСвойств() предназначен для копирования значений в уже существующие свойства объекта-приемника. Если у приемника нет таких свойств, метод ничего не заполнит.
  2. Проблема №2: Заполнение одной структуры и отсутствие итерации. Даже если бы структура ДанныеОстатки имела поля, код Если ТЗ.Следующий Тогда ЗаполнитьЗначенияСвойств(ДанныеОстатки, ТЗ); КонецЕсли; получает и обрабатывает только первую строку результата запроса. Если запрос возвращает несколько строк (например, остатки на разных складах), они будут проигнорированы.
  3. Проблема №3: Некорректное обращение к реквизиту формы и попытка заполнения коллекции одной структурой.
    • Некорректное обращение: Элементы.ОстаткиНаСкладах — это элемент управления формы, который отображает данные. Сами данные (реквизит формы типа ТаблицаЗначений) доступны по своему имени, то есть ОстаткиНаСкладах. Попытка заполнить элемент управления методом ЗаполнитьЗначенияСвойств() приводит к ошибке, так как этот метод работает с объектами данных, а не с элементами интерфейса.
    • Попытка заполнения коллекции одной структурой: Даже если бы обращение было правильным, ДанныеОстатки на момент возврата с сервера является одиночной структурой (из-за проблемы №2). Табличная часть — это коллекция строк, и ее нельзя заполнить одной структурой, необходимо построчно добавлять элементы.

Разбираем основные ошибки и их решения

Ошибка 1: Непонимание работы метода ЗаполнитьЗначенияСвойств()

Метод ЗаполнитьЗначенияСвойств(Приемник, Источник) — это мощный инструмент для быстрого копирования данных. Однако он работает по принципу "копировать только то, что совпадает". Это означает, что в объекте-приемнике должны уже существовать свойства с такими же именами, как и в объекте-источнике.

В нашем случае, ДанныеОстатки = Новый Структура; создает абсолютно пустую структуру. Когда мы вызываем ЗаполнитьЗначенияСвойств(ДанныеОстатки, ТЗ);, у структуры ДанныеОстатки нет полей "Склад", "ОстатокНаСкладе", "РезервНаСкладе", поэтому метод ничего не может заполнить. Чтобы ЗаполнитьЗначенияСвойств() работала с новой структурой, ее поля должны быть явно созданы, например, так:


СтруктураМассива = Новый Структура("Склад,ОстатокНаСкладе,РезервНаСкладе");
ЗаполнитьЗначенияСвойств(СтруктураМассива, ТЗ); // Теперь сработает

Однако для табличных частей подход будет немного отличаться, поскольку они оперируют строками, которые уже имеют заранее определенный набор колонок (свойств). Когда мы создаем новую строку табличной части (МояТаблица.Добавить()), эта строка уже является объектом типа СтрокаТаблицыЗначений с соответствующими колонками, и ЗаполнитьЗначенияСвойств() прекрасно работает с ней, копируя данные из выборки запроса или другой структуры.

Ошибка 2: Неправильный подход к заполнению табличных частей

Табличные части документов и реквизиты формы типа ТаблицаЗначений являются коллекциями строк. Это означает, что для их заполнения данными из запроса, который обычно возвращает несколько строк, нам необходимо:

  1. Итерировать по каждой строке результата запроса.
  2. Для каждой полученной строки из запроса создать новую строку в целевой табличной части (или ТаблицеЗначений).
  3. Затем заполнить эту новую строку данными из текущей строки запроса, используя ЗаполнитьЗначенияСвойств().

Вот как это выглядит концептуально:


// В серверной функции, если возвращаем ТаблицуЗначений или МассивСтруктур
Пока ВыборкаЗапроса.Следующий() Цикл
    НоваяСтрока = МояТаблицаЗначений.Добавить(); // Или создаем структуру и добавляем в массив
    ЗаполнитьЗначенияСвойств(НоваяСтрока, ВыборкаЗапроса);
КонецЦикла;
Возврат МояТаблицаЗначений;

// В клиентской процедуре
Для Каждого СтрокаСерверныхДанных Из ПолученныеДанныеССервера Цикл
    НоваяСтрокаФормы = ОстаткиНаСкладах.Добавить();
    ЗаполнитьЗначенияСвойств(НоваяСтрокаФормы, СтрокаСерверныхДанных);
КонецЦикла;

Ошибка 3: Некорректное обращение к реквизиту формы

Это была ключевая ошибка, которую автор в итоге самостоятельно выявил и исправил. В управляемых формах 1С мы различаем:

Для манипуляций с данными (добавление строк, изменение значений, очистка) необходимо обращаться именно к реквизиту формы напрямую, по его имени, а не через коллекцию Элементы. Таким образом, вместо Элементы.ОстаткиНаСкладах.Добавить() или ЗаполнитьЗначенияСвойств(Элементы.ОстаткиНаСкладах, ...) следует использовать ОстаткиНаСкладах.Добавить() и ЗаполнитьЗначенияСвойств(НоваяСтрока, ...).

Шаги к правильному решению: Комплексный подход

Чтобы эффективно решить задачу заполнения табличной части, мы рекомендуем следующий подход:

  1. На серверной стороне получаем необходимые данные с помощью запроса.
  2. Формируем коллекцию объектов (например, Массив из Структур или ТаблицуЗначений), которая будет передана на клиент. Это позволяет передать все строки данных за один вызов.
  3. На клиентской стороне принимаем эту коллекцию.
  4. Очищаем целевую табличную часть формы.
  5. Итерируем по полученной с сервера коллекции и для каждой ее строки добавляем новую строку в табличную часть формы, заполняя ее с помощью ЗаполнитьЗначенияСвойств().

Реализация серверной функции для получения данных

Мы модифицируем серверную функцию таким образом, чтобы она возвращала Массив структур, каждая из которых будет представлять одну строку остатков. Также учтем оптимизацию запроса для виртуальных таблиц регистров, переместив условие отбора по номенклатуре непосредственно в параметры виртуальной таблицы Остатки.


&НаСервереБезКонтекста
Функция ТоварыНоменклатураАвтоПодборНаСервере(СсылкаНом)
    // Создаем массив, в который будем собирать структуры с данными
    ДанныеОстаткиМассив = Новый Массив; 

    Запрос = Новый Запрос("ВЫБРАТЬ 
                         | ТоварыНаСкладахОстатки.Склад КАК Склад, 
                         | ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ОстатокНаСкладе, 
                         | ТоварыНаСкладахОстатки.КОтгрузкеОстаток КАК РезервНаСкладе 
                         |ИЗ 
                         | РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки");
    // Условие по номенклатуре теперь установлено в параметрах виртуальной таблицы
    Запрос.УстановитьПараметр("Номенклатура", СсылкаНом);

    Результат = Запрос.Выполнить();
    Выборка = Результат.Выбрать();

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

Реализация клиентской процедуры для заполнения табличной части

На клиентской стороне мы вызовем нашу серверную функцию, получим Массив структур. Затем, прежде чем заполнять табличную часть формы "ОстаткиНаСкладах", мы очистим ее от предыдущих данных. После этого в цикле пройдемся по каждой структуре из полученного массива, добавим новую строку в табличную часть формы и заполним ее значениями из структуры.


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

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

Альтернативные подходы

Использование метода Запрос.Выполнить().Выгрузить()

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

Плюсы:

Минусы:

Вот как это могло бы выглядеть с использованием Выгрузить():


&НаСервереБезКонтекста
Функция ТоварыНоменклатураАвтоПодборНаСервере(СсылкаНом)
    Запрос = Новый Запрос("ВЫБРАТЬ 
                         | ТоварыНаСкладахОстатки.Склад КАК Склад, 
                         | ТоварыНаСкладахОстатки.ВНаличииОстаток КАК ОстатокНаСкладе, 
                         | ТоварыНаСкладахОстатки.КОтгрузкеОстаток КАК РезервНаСкладе 
                         |ИЗ 
                         | РегистрНакопления.ТоварыНаСкладах.Остатки(, Номенклатура = &Номенклатура) КАК ТоварыНаСкладахОстатки");
    Запрос.УстановитьПараметр("Номенклатура", СсылкаНом);

    Результат = Запрос.Выполнить();
    
    // Выгружаем результат запроса сразу в ТаблицуЗначений
    ТаблицаОстатков = Результат.Выгрузить();
    
    Возврат ТаблицаОстатков; // Возвращаем ТаблицуЗначений
КонецФункции

&НаКлиенте
Процедура ТоварыНоменклатураАвтоПодбор(Элемент, Текст, ДанныеВыбора, ПараметрыПолученияДанных, Ожидание, СтандартнаяОбработка)
    ТекДанные = Элементы.Товары.ТекущиеДанные;
    
    Если ТекДанные.Номенклатура = Неопределено Тогда
        ОстаткиНаСкладах.Очистить();
        Возврат;
    КонецЕсли;

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

Метод Загрузить() в этом случае является наиболее эффективным способом, так как он выполняет массовое копирование данных, значительно опережая построчное добавление в цикле для больших таблиц. Он автоматически сопоставляет колонки по именам.

Важные рекомендации и отладка

Чтобы избежать подобных проблем в будущем, рекомендуем:

  1. Используйте отладчик: Отладка — ваш лучший друг. Пошаговое выполнение кода на сервере и клиенте позволяет точно увидеть значения переменных, тип объектов и момент возникновения ошибки. Если отладка на сервере недоступна, временно используйте Сообщить() для вывода ключевых значений переменных в код серверных процедур, чтобы понять, какие данные приходят и как они обрабатываются.
  2. Внимательно проверяйте имена полей: Убедитесь, что имена полей в запросе точно совпадают с именами колонок в целевой табличной части (или свойствами структуры/строки).
  3. Проверяйте тип возвращаемых и принимаемых значений: Убедитесь, что серверная функция возвращает тип данных, который клиентская процедура ожидает и может корректно обработать (например, Массив, ТаблицаЗначений).
  4. Обрабатывайте пустые результаты: Всегда предусматривайте сценарии, когда запрос не возвращает данных, и информируйте пользователя о таких ситуациях.
  5. Различайте элементы формы и реквизиты формы: Помните, что для работы с данными реквизита формы обращайтесь к нему по имени, а не через коллекцию Элементы.

Надеемся, что этот подробный разбор поможет вам успешно заполнять табличные части и реквизиты форм в ваших разработках на 1С.

← На главную