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