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