Как получить измененный дополнительный реквизит (свойство) в подписке «ПриЗаписи» в 1С:УПП 1.3?

Программист 1С v8.3 (Обычные формы) IT и автоматизация бизнеса
← На главную

При разработке в конфигурации 1С:Управление производственным предприятием (УПП) редакции 1.3 (на обычных формах) разработчики часто сталкиваются с задачей контроля или обработки дополнительных реквизитов объектов в момент их сохранения. Однако попытка получить актуальное значение свойства в подписке на событие ПриЗаписи объекта справочника или документа стандартным запросом к регистру сведений возвращает старые данные.

В этой статье мы подробно разберем, почему так происходит, проанализируем внутренние механизмы платформы 1С и рассмотрим несколько способов решения этой задачи — от простых «обходных путей» до правильной архитектурной реализации.

Выясним причину: почему в подписке видны старые данные?

Давайте проанализируем последовательность событий, которая происходит, когда пользователь нажимает кнопку «ОК» или «Записать» в форме элемента справочника УПП:

  1. Начинается транзакция записи объекта.
  2. Выполняется обработчик ПередЗаписью на уровне модуля объекта.
  3. Выполняется непосредственная запись объекта в базу данных.
  4. Срабатывает обработчик ПриЗаписи модуля объекта и все связанные с ним подписки на события. На этом этапе сам справочник уже записан, но транзакция еще не завершена.
  5. Управление возвращается в форму элемента. И вот здесь, в обработчике формы ПриЗаписи, вызывается специальная обработка ЗначенияСвойствОбъекта, которая берет измененные пользователем свойства из табличной части формы и записывает их в независимый регистр сведений ЗначенияСвойствОбъектов.
  6. Транзакция фиксируется.

Таким образом, когда срабатывает ваша подписка на событие ПриЗаписи для справочника, новые значения дополнительных свойств еще физически не записаны в регистр сведений ЗначенияСвойствОбъектов. Они все еще находятся на форме в оперативной памяти клиента. Именно поэтому ваш запрос к регистру сведений возвращает старые значения, которые хранились там до начала перезаписи.

Ниже приведен типичный код запроса, который в данный момент времени не вернет новые данные:


// Этот запрос в подписке "ПриЗаписи" объекта вернет СТАРЫЕ значения свойств
Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |	ЗначенияСвойствОбъектов.Значение КАК Значение
    |ИЗ
    |	РегистрСведений.ЗначенияСвойствОбъектов КАК ЗначенияСвойствОбъектов
    |ГДЕ
    |	ЗначенияСвойствОбъектов.Объект = &Объект
    |	И ЗначенияСвойствОбъектов.Свойство = &Свойство";

Запрос.УстановитьПараметр("Объект", Источник.Ссылка);
Запрос.УстановитьПараметр("Свойство", НужноеСвойство);
Выборка = Запрос.Выполнить().Выбрать();

Теперь рассмотрим, как мы можем обойти это поведение платформы и получить корректные измененные данные.

Способ 1. Использование подписки на запись регистра сведений (Рекомендуемый метод)

Поскольку свойства хранятся отдельно от самого объекта, наиболее правильным и архитектурно чистым решением будет создание подписки на событие ПриЗаписи для набора записей регистра сведений ЗначенияСвойствОбъектов.

Этот способ гарантирует стабильную работу системы как при записи вручную пользователем из формы, так и при программном изменении свойств (например, через групповую обработку или при обмене данными).

Разберем реализацию этого метода по шагам:

  1. Создаем новую подписку на событие.
  2. В качестве источника выбираем РегистрСведенийНаборЗаписей.ЗначенияСвойствОбъектов.
  3. Событие — ПриЗаписи.
  4. В обработчике подписки пишем следующий код:

Процедура ПриЗаписиНабораЗначенийСвойств(Источник, Отказ, Замещение) Экспорт

    // Определяем объект, свойства которого изменяются
    Если Источник.Отбор.Объект.Использование Тогда
        СсылкаНаОбъект = Источник.Отбор.Объект.Значение;
        
        // Нас интересует конкретный тип объекта, например, ДоговорыКонтрагентов
        Если ТипЗнч(СсылкаНаОбъект) = Тип("СправочникСсылка.ДоговорыКонтрагентов") Тогда
            
            // Обходим новые (записываемые) свойства из набора записей
            Для Каждого Запись Из Источник Цикл
                
                // Проверяем изменение нужного нам свойства
                Если Запись.Свойство = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00000000140") Тогда
                    
                    НовоеЗначение = Запись.Значение;
                    
                    // Здесь выполняем необходимую бизнес-логику
                    // Например, проверку заполнения или запись сопутствующих данных
                    Если Не ЗначениеЗаполнено(НовоеЗначение) Тогда
                        // Ваш код...
                    КонецЕсли;
                    
                КонецЕсли;
                
            КонецЦикла;
            
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

Преимущества: Полная независимость от интерфейса клиента, высокая производительность, корректная обработка при любых видах записи (программная, интерактивная, фоновые задания).

Способ 2. Получение данных напрямую из открытой формы объекта (Быстрый хак)

Если логику проверки обязательно нужно выполнить именно в контексте записи самого справочника и только при работе пользователя в интерфейсе, можно применить получение данных из элементов активной формы.

Рассмотрим пример кода, реализующий этот подход в подписке на событие ПриЗаписи для объекта справочника:


Процедура ПолучитьДопРеквизитИзФормы(Источник, Отказ) Экспорт

    // Для новых (еще не записанных ранее) объектов свойства из формы прочитать не удастся
    Если Источник.ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;

    // Пытаемся получить форму элемента справочника
    Форма = Источник.Ссылка.ПолучитьФорму("ФормаЭлемента");
    
    // Проверяем, открыта ли она пользователем в данный момент
    Если Форма.Открыта() Тогда
        
        // Находим нужное нам свойство (замените поиск по коду на ваш метод предопределенных настроек)
        СвойствоМенеджер = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00000000140");
        
        // Обходим элементы табличной части обработки свойств на форме
        Для Каждого СтрокаСвойства Из Форма.ЭлементыФормы.Свойства.Значение Цикл
            Если СтрокаСвойства.Свойство = СвойствоМенеджер Тогда
                
                АктуальноеЗначение = СтрокаСвойства.Значение;
                
                // Выполняем требуемые действия с актуальным значением из формы
                // ...
                Прервать;
                
            КонецЕсли;
        КонецЦикла;
        
    КонецЕсли;

КонецПроцедуры

Важное предупреждение! Использование метода ПолучитьФорму() внутри серверного кода или обработчиков событий объекта — это крайне нерекомендуемая практика (антипаттерн).

Проанализируем ключевые недостатки данного подхода:

Способ 3. Передача данных через структуру «ДополнительныеСвойства»

Если вам необходимо передать новые значения свойств из формы в модуль объекта (или подписку) «чистым» путем без прямого обращения к элементам формы из подписки, задействуйте встроенный механизм платформы — структуру ДополнительныеСвойства.

Для этого в модуле формы элемента справочника в обработчике ПередЗаписью добавьте код, который поместит актуальную таблицу свойств в свойства самого объекта:


// В модуле формы элемента справочника
Процедура ПередЗаписью(Отказ)
    
    // Передаем измененную таблицу свойств в объект перед началом записи
    ТаблицаСвойств = ЭлементыФормы.Свойства.Значение.Выгрузить();
    ЭтотОбъект.ДополнительныеСвойства.Вставить("НовыеЗначенияСвойств", ТаблицаСвойств);
    
КонецПроцедуры

Теперь в вашей подписке на событие ПриЗаписи для справочника вы сможете легко прочитать эти данные, минуя прямое обращение к интерфейсу:


// В модуле подписки на событие "ПриЗаписи" объекта
Процедура ПриЗаписиОбъекта(Источник, Отказ) Экспорт

    Если Источник.ДополнительныеСвойства.Свойство("НовыеЗначенияСвойств") Тогда
        ТаблицаСвойств = Источник.ДополнительныеСвойства.НовыеЗначенияСвойств;
        
        СвойствоМенеджер = ПланыВидовХарактеристик.СвойстваОбъектов.НайтиПоКоду("00000000140");
        СтрокаСвойства = ТаблицаСвойств.Найти(СвойствоМенеджер, "Свойство");
        
        Если СтрокаСвойства <> Неопределено Тогда
            НовоеЗначение = СтрокаСвойства.Значение;
            // Обрабатываем полученное значение
        КонецЕсли;
    КонецЕсли;

КонецПроцедуры

Этот способ намного безопаснее прямого обращения к форме через ПолучитьФорму(), так как при программной записи свойство просто не будет передано в структуре, и подписка отработает без ошибок.

Резюме

Мы детально изучили проблему несовпадения времени записи объекта и его дополнительных характеристик в УПП 1.3. При выборе решения всегда отдавайте предпочтение Способу №1 (Подписка на регистр сведений), так как он обеспечивает максимальную надежность кода и соответствует стандартам разработки фирмы «1С». Использовать передачу через ДополнительныеСвойства (Способ №3) целесообразно в тех случаях, когда алгоритм жестко завязан на транзакцию записи основного объекта.

← На главную