В процессе разработки в конфигурации УПП 1.3 (или любой другой на обычных формах) часто возникает задача: выполнить определенную логику только в тот момент, когда документ записывается впервые. На первый взгляд задача кажется тривиальной, но у платформы 1С есть свои особенности работы с транзакциями и ссылками, которые могут привести к ошибкам, если не знать механизмов работы событий ПередЗаписью и ПриЗаписи.
В этой статье мы подробно разберем, как правильно определить статус "новости" объекта, рассмотрим подводные камни с установкой ссылок вручную и научимся передавать состояние объекта между разными стадиями записи.
Самый простой и очевидный способ проверить, был ли объект уже сохранен в базе данных — это вызвать метод ЭтоНовый(). Однако результат работы этого метода напрямую зависит от того, в каком обработчике события модуля объекта он вызывается.
Разберем ситуацию в событии ПередЗаписью. В этот момент объект еще не зафиксирован в таблицах базы данных. Даже если системе уже известен уникальный идентификатор (GUID) будущей записи, физически данных в базе еще нет. Поэтому метод ЭтоНовый() вернет значение Истина.
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ЭтоНовый() Тогда
// Этот блок выполнится только при первой записи документа
Сообщить("Документ создается впервые!");
КонецЕсли;
КонецПроцедуры
Важный нюанс: как только выполнение кода переходит в процедуру ПриЗаписи, ситуация меняется. В этот момент запись в базу данных уже фактически произведена (хотя транзакция еще не зафиксирована окончательно). Метод ЭтоНовый() в ПриЗаписи всегда будет возвращать Ложь. Проанализируем, как обойти это ограничение.
Часто логика требует совершить действия (например, создать связанные записи в регистрах) именно в событии ПриЗаписи, когда у объекта уже точно есть ссылка. Но как узнать, был ли он новым секунду назад? Для этих целей мы рекомендуем использовать коллекцию ДополнительныеСвойства.
Это специальная структура, которая "живет" вместе с экземпляром объекта в оперативной памяти на протяжении всего цикла записи, но не сохраняется в базу данных. Рассмотрим пример реализации:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
// Если объект загружается через обмен (РИБ),
// проверка на "новость" может иметь другую логику
Если ОбменДанными.Загрузка Тогда
Возврат;
КонецЕсли;
// Сохраняем статус "новости" во внутреннюю структуру объекта
ДополнительныеСвойства.Вставить("ЭтоНовыйОбъект", ЭтоНовый());
КонецПроцедуры
Процедура ПриЗаписи(Отказ)
ПризнакНовизны = Ложь;
// Проверяем, есть ли наше свойство в структуре
Если ДополнительныеСвойства.Свойство("ЭтоНовыйОбъект", ПризнакНовизны) Тогда
Если ПризнакНовизны Тогда
// Теперь мы точно знаем, что документ новый, даже в ПриЗаписи
ВыполнитьДействияДляНовогоДокумента();
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Этот подход считается эталонным в типовых конфигурациях (УПП, ERP, БП 3.0), так как он надежно работает и в модулях объекта, и в подписках на события.
Многие начинающие разработчики пытаются проверить "новость" документа через условие Если Ссылка.Пустая() Тогда. Рассмотрим, почему это плохая практика.
Существует метод УстановитьСсылкуНового(). Он позволяет программисту назначить документу конкретную ссылку (GUID) еще до того, как кнопка "Записать" будет нажата. Это часто используется при программном заполнении связанных табличных частей. В такой ситуации:
Ссылка.Пустая() вернет Ложь (так как ссылка уже заполнена).ЭтоНовый() при этом честно вернет Истина.Таким образом, всегда отдавайте предпочтение методу ЭтоНовый() или проверке существования ссылки в базе данных через запрос.
Если вам нужно проверить объект "со стороны" (например, в какой-то внешней обработке, имея на руках только ссылку), метод ЭтоНовый() вам не поможет, так как он доступен только для Объекта, а не для Ссылки. Выясним, как поступить в таком случае.
Самый быстрый способ — использовать функцию из общего модуля типовой конфигурации (если она есть) или написать небольшой запрос. Проверим наличие записи в таблице базы данных:
Функция ДокументСуществуетВБазе(СсылкаНаДокумент)
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| 1
|ИЗ
| Документ.РеализацияТоваровУслуг КАК Реализация
|ГДЕ
| Реализация.Ссылка = &Ссылка";
Запрос.УстановитьПараметр("Ссылка", СсылкаНаДокумент);
Возврат Не Запрос.Выполнить().Пустой();
КонецФункции
В типовых решениях УПП 1.3 аналогичную роль выполняет функция ОбщегоНазначения.СсылкаСуществует(). Она возвращает Ложь, если объект еще ни разу не сохранялся.
Проанализируем ситуацию с распределенными базами данных. Если в вашу базу "прилетает" новый документ из другого филиала, для вашей текущей базы он технически является новым. Метод ЭтоНовый() вернет Истина.
Однако, как правило, при загрузке данных из обмена не нужно выполнять проверки заполнения или создавать какие-то дополнительные записи, так как они должны прийти вместе с объектом. Поэтому всегда добавляйте проверку ОбменДанными.Загрузка:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если ОбменДанными.Загрузка Тогда
Возврат; // Не выполняем логику "нового" при загрузке данных
КонецЕсли;
// Ваша логика здесь
КонецПроцедуры
Иногда под "новым" документом понимают только тот, что был создан с нуля, а не копированием. Если вам нужно разделить эти состояния, используйте обработчик ПриКопировании. В нем вы также можете установить флаг в ДополнительныеСвойства, чтобы в дальнейшем отличить "чисто новый" объект от "копии".
Подведем итог: для надежной проверки в 1С 8.3 используйте метод ЭтоНовый() в событии ПередЗаписью, а если данные нужны позже — кэшируйте их в ДополнительныеСвойства. Это обеспечит стабильную работу вашего кода даже при использовании сложных механизмов записи и обмена данными.