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