При разработке в расширениях 1С программисты часто сталкиваются с задачей: необходимо выполнить определенное действие (например, выгрузить остатки во внешний файл или отправить данные в API) строго после того, как документ изменил движения в регистрах. Однако использование стандартной директивы &После для процедур ОбработкаПроведения или ОбработкаУдаленияПроведения часто приводит к тому, что запросы к виртуальным таблицам остатков выдают старые данные. Проанализируем, почему это происходит, и разберем эффективные методы решения этой задачи.
Прежде всего, нам необходимо понять механику работы платформы 1С. Когда выполняется процедура ОбработкаПроведения, вся работа происходит внутри одной транзакции. Несмотря на то что код расширения с директивой &После отрабатывает после основного кода модуля объекта, транзакция в этот момент еще не зафиксирована. Это означает следующее:
Рассмотрим подробнее способы обхода этого ограничения.
Самый быстрый и простой способ увидеть изменения в регистрах внутри транзакции — это принудительно вызвать метод Записать() для коллекции движений. Рассмотрим на примере кода в расширении:
&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
// Если документ не должен быть проведен, выходим
Если Отказ Тогда
Возврат;
КонецЕсли;
// Принудительно записываем движения в базу данных (внутри транзакции)
ЭтотОбъект.Движения.Записать();
// Теперь остатки в регистрах физически изменены, и запрос их увидит
еГ_РаботаСXML.СоздатьФайлСОстатками(ЭтотОбъект);
КонецПроцедуры
Важный нюанс: при использовании этого метода нужно быть осторожным. Если в основной конфигурации или других расширениях также вызывается принудительная запись, это может привести к избыточной нагрузке на СУБД или блокировкам. Однако для небольших систем это рабочее решение.
Если действие должно выполняться только при интерактивном проведении пользователем из формы, мы можем использовать событие ПослеЗаписиНаСервере. Но как понять, что документ именно проводился, а не просто записывался? Для этого воспользуемся коллекцией ДополнительныеСвойства.
Разберем по шагам:
Пример кода в модуле объекта:
&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
ЭтотОбъект.ДополнительныеСвойства.Вставить("ДокументПроведен", Истина);
КонецПроцедуры
Пример кода в модуле формы:
&После("ПослеЗаписиНаСервере")
Процедура еГ_ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
Если ТекущийОбъект.ДополнительныеСвойства.Свойство("ДокументПроведен") Тогда
// Здесь транзакция уже завершена, и остатки гарантированно актуальны
еГ_РаботаСXML.СоздатьФайлСОстатками(ТекущийОбъект);
КонецЕсли;
КонецПроцедуры
Этот метод хорош тем, что он выполняется вне основной транзакции проведения, что исключает блокировки базы данных при длительном формировании файла.
Проанализируем ситуацию, когда формирование файла или расчет остатков занимает значительное время. Заставлять пользователя ждать завершения транзакции — плохая практика. Лучше всего запустить фоновое задание.
Посмотрим на пример реализации:
&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
ПараметрыЗадания = Новый Массив;
ПараметрыЗадания.Добавить(ЭтотОбъект.Ссылка);
// Запускаем фоновое задание, которое выполнится сразу после фиксации транзакции
ФоновыеЗадания.Выполнить("еГ_РаботаСXML.СформироватьФайлФоновымЗаданием", ПараметрыЗадания);
КонецПроцедуры
Фоновое задание «подождет», пока основная транзакция завершится, и начнет работу уже с полностью актуальными данными в базе.
Начиная с версии платформы 8.3.10, мы можем создавать собственные объекты ПодпискаНаСобытие прямо в расширении. Это более профессиональный подход, так как он позволяет:
Создадим подписку на событие ОбработкаПроведения для нужных документов и укажем в качестве обработчика процедуру в общем модуле расширения. Логика получения остатков будет аналогична Решению 1 или Решению 3.
Если конфигурация «тяжелая» (как в случае с решениями на базе УТ), лучшим архитектурным решением будет использование регистра-очереди. Рассмотрим этот алгоритм:
еГ_ОчередьВыгрузки с единственным измерением — ссылкой на документ или номенклатуру.&После("ОбработкаПроведения") и &После("ОбработкаУдаленияПроведения") просто записываем ссылку в этот регистр. Это происходит мгновенно и не блокирует работу.Это решение гарантирует 100% точность остатков, так как данные читаются заведомо после завершения всех транзакций, и никак не влияет на скорость работы пользователей в программе.
Выбор метода зависит от ваших требований к скорости обновления данных. Если файл нужен мгновенно и он формируется быстро — используйте Движения.Записать(). Если важна производительность системы — используйте фоновые задания или схему с очередью в регистре сведений.