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