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