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