Как автоматически заполнить табличную часть документа данными из регистра сведений в 1С?

Программист Управленческий учет IT и автоматизация бизнеса
← На главную

В процессе разработки часто возникает задача: при создании нового документа необходимо автоматически подтянуть актуальные значения из регистра сведений (например, цены, нормативы или показатели) в его табличную часть. Начинающие разработчики часто сталкиваются с ошибками контекста или выбирают не совсем подходящие события для реализации этой логики. Разберем по шагам, как правильно организовать этот процесс, исправим типовые ошибки и оптимизируем код.

Анализ основной ошибки: «Переменная не определена»

Рассмотрим ситуацию, когда при попытке выполнить запрос в процедуре ПриСозданииНаСервере возникает ошибка «Переменная не определена (Показатель)». Выясним причину: в программном коде внутри процедуры вы пытаетесь передать в параметр запроса переменную, которая не была объявлена локально и не является реквизитом формы или объекта. Проанализируем пример кода:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    Запрос = Новый Запрос;
    Запрос.УстановитьПараметр("ДатаСреза", ТекущаяДата()); // Используем функцию ТекущаяДата()
    // Ошибка здесь: программа не знает, что такое «Показатель»
    Запрос.УстановитьПараметр("Показатель", Показатель); 
    ...
КонецПроцедуры

Чтобы исправить это, необходимо понимать, откуда должен взяться этот Показатель. Если мы хотим получить все актуальные записи из регистра, то условие в запросе по конкретному показателю вообще не нужно. Если же мы хотим получить значение конкретного реквизита документа, необходимо обращаться к нему через контекст Объект.ИмяРеквизита.

Выбор правильного события: почему ОбработкаЗаполнения лучше?

Проанализируем архитектурный подход. Использование события ПриСозданииНаСервере в модуле формы — это распространенная ошибка. Почему это плохо:

  1. Затирание данных: Это событие срабатывает каждый раз при открытии формы. Если пользователь создаст документ, изменит данные в табличной части вручную и сохранит его, то при следующем открытии ваш код снова выполнит запрос и очистит (перезаполнит) таблицу, уничтожив ручные правки пользователя.
  2. Лишняя нагрузка: Нет смысла выполнять тяжелый запрос к базе данных при каждом открытии существующего документа, если данные в нем уже сохранены.

Правильным решением будет использование обработчика ОбработкаЗаполнения в модуле объекта документа. Рассмотрим преимущества этого метода:

Реализация заполнения в модуле объекта

Разберем, как правильно написать код в модуле объекта. Мы будем использовать виртуальную таблицу СрезПоследних для получения самых свежих данных из регистра на момент создания документа.


Процедура ОбработкаЗаполнения(ДанныеЗаполнения, ТекстЗаполнения, СтандартнаяОбработка)
    
    // Очищаем табличную часть перед заполнением
    ДинамикаПоказателей.Очистить();
    
    Запрос = Новый Запрос;
    // Используем дату документа, если она заполнена, иначе - текущую дату
    ДатаЗапроса = ?(ЗначениеЗаполнено(Дата), Дата, ТекущаяДата());
    
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   АктуальныеПоказателиСрезПоследних.Показатель КАК Показатель,
        |   АктуальныеПоказателиСрезПоследних.Размерность КАК Размерность,
        |   АктуальныеПоказателиСрезПоследних.ЦелевоеЗначение КАК ЦелевоеЗначение
        |ИЗ
        |   РегистрСведений.АктуальныеПоказатели.СрезПоследних(&ДатаЗапроса, ) КАК АктуальныеПоказателиСрезПоследних";
    
    Запрос.УстановитьПараметр("ДатаЗапроса", ДатаЗапроса);
    
    РезультатЗапроса = Запрос.Выполнить();
    
    // Быстрая загрузка данных, если имена полей запроса и ТЧ совпадают
    Если Не РезультатЗапроса.Пустой() Тогда
        ДинамикаПоказателей.Загрузить(РезультатЗапроса.Выгрузить());
    КонецЕсли;

КонецПроцедуры

Оптимизация работы с данными

Посмотрим на важные нюансы, которые помогут сделать код более профессиональным и быстрым:

  1. Метод Загрузить(): Вместо того чтобы обходить выборку в цикле Пока ... Цикл и добавлять строки по одной методом Добавить(), мы используем РезультатЗапроса.Выгрузить() и метод ТЧ Загрузить(). Это работает в разы быстрее, так как данные переносятся в память одним массивом. Главное условие — псевдонимы полей в запросе (после слова КАК) должны строго совпадать с именами колонок табличной части.
  2. Параметры виртуальной таблицы: Всегда передавайте условие или дату первым параметром в СрезПоследних(&Дата, <Условие>). Это критически важно для производительности. Если вы напишете условие в секции ГДЕ, СУБД сначала выберет все записи регистра, а потом отфильтрует их. Если в параметрах ВТ — СУБД отсечет лишнее сразу.
  3. Функция ЕСТЬNULL: Если в регистре могут быть пустые значения ресурсов, рекомендуется в запросе оборачивать поля в функцию ЕСТЬNULL(Поле, 0), чтобы в документе не появлялись пустые ячейки там, где ожидаются числа.

Добавление кнопки «Заполнить» для пользователя

Иногда пользователю нужно обновить данные в табличной части уже после того, как документ был создан (например, если значения в регистре изменились). Выясним, как это реализовать. Лучше всего вынести логику заполнения в отдельную экспортную процедуру модуля объекта, чтобы ее можно было вызвать и из ОбработкаЗаполнения, и из команды формы.

В модуле объекта:


Процедура ЗаполнитьПоказателиИзРегистра() Экспорт
    // Код запроса и загрузки данных, описанный выше
    ...
КонецПроцедуры

В модуле формы (при нажатии на кнопку):


&НаСервере
Процедура ЗаполнитьНаСервере()
    // Создаем объект из реквизита формы, чтобы вызвать экспортный метод
    ДокОбъект = РеквизитФормыВЗначение("Объект");
    ДокОбъект.ЗаполнитьПоказателиИзРегистра();
    // Возвращаем измененный объект обратно в форму
    ЗначениеВРеквизитФормы(ДокОбъект, "Объект");
КонецПроцедуры

Проанализировав эти подходы, мы видим, что правильное распределение кода между модулем формы и модулем объекта избавляет от ошибок определения переменных и гарантирует корректное поведение программы при работе пользователя с документами.

← На главную