Как сохранить данные из документа в справочник при записи в 1С?

Программист 1С v8.3 (Управляемые формы) Управленческий учет
← На главную

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

Анализ исходной задачи и выбор метода

Рассмотрим ситуацию: у нас есть документ ПриобретениеНедвижимости и справочник ОбъектыНедвижимости. Состав реквизитов у них во многом совпадает (Адрес, Площадь, Стоимость и т.д.). Наша цель — при записи документа перенести эти данные в новый элемент справочника.

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

Использование функции ЗаполнитьЗначенияСвойств

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

Рассмотрим базовый код, который можно разместить в модуле объекта документа:


Процедура ПриЗаписи(Отказ)
    // Создаем новый объект справочника
    НовыйОбъектСпр = Справочники.ОбъектыНедвижимости.СоздатьЭлемент();
    
    // Копируем данные из документа (ЭтотОбъект) в справочник
    ЗаполнитьЗначенияСвойств(НовыйОбъектСпр, ЭтотОбъект);
    
    // Если в справочнике есть реквизиты, которые называются иначе,
    // заполняем их вручную
    НовыйОбъектСпр.Наименование = "Объект: " + ЭтотОбъект.ПолноеНаименование;
    
    // Записываем справочник
    НовыйОбъектСпр.Записать();
КонецПроцедуры

Важный нюанс: если имена реквизитов отличаются хотя бы на одну букву (например, «Адерс» вместо «Адрес»), автоматического заполнения не произойдет. Проанализируем также типы данных: если в документе адрес — это Строка, а в справочнике — ссылка на другой справочник, данные не будут перенесены. Выясним причину отсутствия данных в справочнике в первую очередь через проверку типов и имен в Конфигураторе.

Исправление ошибок с запросами и итераторами

Если же вы все-таки решили использовать запрос (например, для массовой обработки или сложного сбора данных), важно правильно обрабатывать результат. Ошибка «Итератор для значения не определен» возникает при попытке использовать цикл Для Каждого напрямую к методу Следующий() или к результату запроса, который еще не выгружен.

Разберем два правильных способа обхода данных:

  1. Выгрузка в Таблицу Значений (ТЗ): подходит, если данных немного и их нужно обрабатывать несколько раз.
  2. Использование выборки: более производительный способ, так как данные читаются последовательно.

Пример правильного цикла с выборкой:


РезультатЗапроса = Запрос.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

Пока Выборка.Следующий() Цикл
    НовыйЭлемент = Справочники.ОбъектыНедвижимости.СоздатьЭлемент();
    ЗаполнитьЗначенияСвойств(НовыйЭлемент, Выборка);
    НовыйЭлемент.Записать();
КонецЦикла;

Обратите внимание на установку параметров в запросе. Если вы работаете внутри документа, параметром должна выступать ссылка: Запрос.УстановитьПараметр("Ссылка", ЭтотОбъект.Ссылка);. Использование типа Объект в параметрах запроса недопустимо.

Предотвращение дублирования данных

Одной из главных проблем кода, представленного в обсуждении, является создание нового элемента справочника при каждой записи документа. Если пользователь откроет документ и нажмет «Записать» пять раз — в справочнике появится пять одинаковых объектов. Чтобы этого избежать, реализуем проверку.

Рассмотрим пошагово алгоритм «умной» записи:

  1. Добавим в документ реквизит ОбъектНедвижимостиСсылка (тип СправочникСсылка.ОбъектыНедвижимости).
  2. При записи будем проверять: если реквизит пустой — создаем новый элемент, если заполнен — редактируем существующий.

Посмотрим на пример реализации в модуле объекта:


Процедура ПриЗаписи(Отказ)
    
    // Проверяем, был ли уже создан объект справочника
    Если ЗначениеЗаполнено(ЭтотОбъект.ОбъектНедвижимостиСсылка) Тогда
        ОбъектСпр = ЭтотОбъект.ОбъектНедвижимостиСсылка.ПолучитьОбъект();
    Иначе
        ОбъектСпр = Справочники.ОбъектыНедвижимости.СоздатьЭлемент();
    КонецЕсли;
    
    // Заполняем реквизиты
    ЗаполнитьЗначенияСвойств(ОбъектСпр, ЭтотОбъект);
    
    // Обязательно заполняем Наименование, иначе в списке будет пусто
    ОбъектСпр.Наименование = ЭтотОбъект.АдресМестанахожджения;
    
    Попытка
        ОбъектСпр.Записать();
        
        // Если это был новый элемент, сохраняем ссылку в документе
        Если Не ЗначениеЗаполнено(ЭтотОбъект.ОбъектНедвижимостиСсылка) Тогда
            ЭтотОбъект.ОбъектНедвижимостиСсылка = ОбъектСпр.Ссылка;
            // Используем ОбновитьЭтотОбъект, если платформа позволяет, 
            // или записываем ссылку без бесконечного цикла
            ЭтотОбъект.Записать(); 
        КонецЕсли;
    Исключение
        Сообщить("Не удалось сохранить данные в справочник: " + ОписаниеОшибки());
    КонецПопытки;
    
КонецПроцедуры

Место размещения кода и транзакции

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

Проанализируем ситуацию с обновлением интерфейса. Пользователи часто жалуются, что после записи документа они открывают список справочника и не видят новой записи, пока не нажмут кнопку «Обновить». В управляемых формах для решения этой задачи используйте механизм оповещений:


// В форме документа после записи
Оповестить("ЗаписанОбъектНедвижимости", Объект.ОбъектНедвижимостиСсылка);

В форме списка справочника можно подписаться на это событие и выполнить метод Элементы.Список.Обновить(), что обеспечит мгновенный отклик интерфейса.

Итоги и рекомендации

Мы рассмотрели процесс переноса данных из документа в справочник и выяснили, что ключ к успеху лежит в использовании встроенных функций платформы и контроле уникальности данных.

Основные правила, которые мы выделили:

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

← На главную