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

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

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

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

Разбор типичной ошибки: почему нельзя присваивать «Результат» полю строки

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


Результат = Запрос.Выполнить().Выгрузить();
Для Каждого ЭлементРезультата Из Результат Цикл
    НоваяСтрокаТЧ = Счета.Добавить();
    НоваяСтрокаТЧ.НомерСтроки = Результат;
    НоваяСтрокаТЧ.Счет = Результат;
КонецЦикла;

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

Важные моменты:

  1. Типизация: Поле табличной части имеет конкретный тип (например, ДокументСсылка.СчетНаОплатуПокупателю). Ему нужно присваивать конкретное значение из колонки текущей строки выборки, а не всю таблицу.
  2. Системные поля: Поле НомерСтроки управляется платформой автоматически. Прямое присваивание ему значений вручную (особенно всей таблицы значений) является грубой ошибкой.

Способ 1: Классический обход выборки запроса

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

Разберем по шагам реализацию этого метода:


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

Запрос.УстановитьПараметр("Период", ТекущаяДата());

// 2. Выполняем запрос
РезультатЗапроса = Запрос.Выполнить();

// 3. Используем выборку для обхода данных
Выборка = РезультатЗапроса.Выбрать();

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

В данном примере мы используем метод Выборка.Следующий(), который позволяет последовательно обращаться к каждой строке результата. Обратите внимание, что мы обращаемся к Выборка.Счет — это именно то значение (ссылка на документ), которое нам нужно.

Способ 2: Использование метода Загрузить() — максимально быстрый путь

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

Главное условие: имена колонок в таблице значений (результате запроса) должны полностью совпадать с именами реквизитов табличной части документа.

Разберем пример реализации:


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

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

// Метод Загрузить очищает ТЧ и заполняет её данными из ТЗ
Объект.Счета.Загрузить(ТЗ_Результат);

Этот метод идеален, когда логика документа предполагает полное обновление списка счетов. Если же счета нужно дописывать к уже существующим в таблице, используйте цикл из первого способа.

Особенности работы с регистром «РезервыНоменклатуры»

Проанализируем ситуацию с точки зрения бизнес-логики. Документ «Снятие с резерва» должен оперировать только актуальными данными. Поэтому при написании запроса важно учитывать следующие нюансы:

  1. Работа с остатками: Нужно обращаться именно к виртуальной таблице Остатки. Если обращаться к основной таблице регистра или таблице оборотов, вы можете получить документы, по которым резерв уже давно снят.
  2. Фильтрация: Всегда передавайте в параметры виртуальной таблицы актуальную дату (границу), чтобы видеть остатки именно на момент формирования документа.
  3. Уникальность: В табличную часть «Счета» обычно требуется вывести список самих документов-оснований без дублей (даже если по одному счету зарезервировано 10 позиций номенклатуры). Для этого используйте ключевое слово ВЫБРАТЬ РАЗЛИЧНЫЕ в тексте запроса.

Контекст выполнения кода: Клиент или Сервер?

Выясним, где именно должен располагаться данный код. Работа с запросами и объектами базы данных в управляемом приложении возможна только на сервере.

Если вы создаете кнопку «Заполнить» на форме документа, цепочка вызовов должна быть следующей:

  1. Пользователь нажимает кнопку (Клиентская процедура &НаКлиенте).
  2. Вызывается серверная процедура (&НаСервере).
  3. Внутри серверной процедуры происходит работа с данными формы (через реквизит Объект).

Пример правильной структуры в модуле формы:


&НаСервере
Процедура ЗаполнитьСчетаНаСервере()
    // Здесь размещаем код запроса
    // ...
    Объект.Счета.Загрузить(РезультатЗапроса.Выгрузить());
КонецПроцедуры

&НаКлиенте
Процедура Заполнить(Команда)
    ЗаполнитьСчетаНаСервере();
КонецПроцедуры

Дополнительные проверки при заполнении

Для обеспечения стабильности работы программы рекомендуем добавить дополнительные проверки перед добавлением строк:

Пример расширенного условия в запросе:


Запрос.Текст = 
    "ВЫБРАТЬ РАЗЛИЧНЫЕ
    |	Резервы.ДокументРезерва КАК Счет
    |ИЗ
    |	РегистрНакопления.РезервыНоменклатуры.Остатки КАК Резервы
    |ГДЕ
    |	Резервы.ДокументРезерва ССЫЛКА Документ.СчетНаОплатуПокупателю
    |	И НЕ Резервы.ДокументРезерва.ПометкаУдаления";

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

← На главную