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