Как получить актуальные значения реквизитов и дополнительных реквизитов в подписке на событие ПриЗаписи

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

При разработке интеграций через веб-сервисы программисты часто сталкиваются со странным поведением платформы 1С: в обработчике события ПриЗаписи (или в соответствующей подписке) данные объекта кажутся «старыми», хотя запись уже инициирована. Особенно остро эта проблема проявляется при работе с дополнительными реквизитами, реализуемыми через Библиотеку стандартных подсистем (БСП). Рассмотрим подробнее, почему так происходит, и как правильно организовать получение актуальных данных.

Различие между объектом и ссылкой в контексте транзакции

Для начала разберем фундаментальное различие между переменной типа СправочникОбъект (или ДокументОбъект) и его ссылкой. Проанализируем ситуацию: когда мы находимся внутри модуля объекта или подписки на событие, у нас есть доступ к Источнику. Источник — это экземпляр объекта в оперативной памяти сервера.

Важно понимать следующие моменты:

  1. Объект в памяти (Источник): Это набор данных, с которыми вы работаете в коде прямо сейчас. Если вы изменили реквизит, но не вызвали Записать(), в базе данных изменений еще нет, а в объекте они уже есть.
  2. Ссылка (Источник.Ссылка): Это всего лишь уникальный идентификатор (GUID). Сама по себе ссылка не содержит данных реквизитов. Как только вы обращаетесь к Источник.Ссылка.МойРеквизит, платформа выполняет чтение из базы данных или из транзакционного кэша.

Выясним причину конфликта: почему при записи из веб-сервиса Объект.Ссылка.Реквизит может вернуть новое значение, а Объект.Реквизит — старое? Это происходит из-за того, что запись данных в базу данных уже произошла (физически в таблицу СУБД), но текущий экземпляр объекта в памяти не был «перечитан» после записи.

Особенности работы дополнительных реквизитов (БСП)

В большинстве современных конфигураций дополнительные реквизиты хранятся в табличной части ДополнительныеРеквизиты. Однако программная установка этих свойств часто выполняется через механизмы БСП, например, функцией УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(). Посмотрим на типовую ошибку в логике веб-сервиса:


// Код в веб-сервисе
ДоговорОбъект = Справочники.ДоговорыКонтрагентов.НайтиПоКоду(Код).ПолучитьОбъект();
ДоговорОбъект.Записать(); // Первая запись объекта

// Установка доп. реквизитов через БСП
Свойства = Новый Структура("МойРеквизит", НовоеЗначение);
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(ДоговорОбъект.Ссылка, Свойства); 

Проанализируем ситуацию: метод ЗаписатьСвойстваУОбъекта принимает Ссылку. Внутри этого метода БСП создает свой собственный экземпляр объекта этого договора, заполняет его табличную часть новыми значениями и вызывает метод Записать().

В итоге в базе данных значения обновились, но ваш первоначальный экземпляр ДоговорОбъект, который «живет» в памяти вашего веб-сервиса, об этом ничего не знает. Его табличная часть ДополнительныеРеквизиты осталась пустой или со старыми значениями. Когда срабатывает подписка ПриЗаписи, вызванная из метода БСП, она видит именно тот объект, который создала БСП, а не ваш исходный.

Почему поведение отличается при записи «руками» из формы?

При работе в интерактивном режиме (через форму) платформа сама управляет кэшированием и обновлением данных. Когда пользователь нажимает «Записать», объект в памяти формы является единственным источником истины. В случае же с веб-сервисом мы часто создаем несколько экземпляров одного и того же объекта в рамках одной транзакции, что и приводит к путанице.

Как гарантированно передать данные в подписку?

Если нам нужно, чтобы подписка ПриЗаписи отреагировала на изменения, внесенные внешним кодом (веб-сервисом), самым надежным способом является использование свойства ДополнительныеСвойства. Это структура, которая существует «внутри» объекта в памяти и не записывается в базу данных, но доступна во всех обработчиках событий (ПередЗаписью, ПриЗаписи).

Рассмотрим правильный алгоритм работы в веб-сервисе:


// В модуле веб-сервиса
ДоговорОбъект = Справочники.ДоговорыКонтрагентов.НайтиПоКоду(Код).ПолучитьОбъект();

// Вместо того чтобы ждать записи БСП, подготовим данные заранее
// Помещаем флаг или само значение в ДополнительныеСвойства
ДоговорОбъект.ДополнительныеСвойства.Вставить("ОбновленноеЗначениеРеквизита", НовоеЗначение);

// Если мы меняем доп. реквизиты БСП, лучше делать это напрямую в ТЧ объекта перед записью
// Либо учитывать этот флаг в подписке
ДоговорОбъект.Записать();

Теперь разберем, как должна выглядеть подписка на событие ПриЗаписи:


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

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

Рекомендации по работе с транзакционным кэшем

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

Посмотрим на пример с использованием Прочитать():


// В веб-сервисе
УправлениеСвойствами.ЗаписатьСвойстваУОбъекта(ДоговорОбъект.Ссылка, Свойства); 
// После этого вызова ДоговорОбъект в памяти устарел!
ДоговорОбъект.Прочитать(); // Теперь ТЧ ДополнительныеРеквизиты обновлена

Подведем итог: странное поведение при записи через веб-сервисы чаще всего связано с тем, что программист работает с одним экземпляром объекта, а системные механизмы (БСП) создают другой экземпляр для записи в ту же таблицу СУБД. Использование ДополнительныеСвойства и работа напрямую с коллекциями Источника в подписках позволяют избежать этих проблем и обеспечить целостность данных.

← На главную