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