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