Как получить актуальные остатки регистров после проведения или отмены проведения в расширении 1С?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Выясняем причину: транзакция и видимость данных

Прежде всего, нам необходимо понять механику работы платформы 1С. Когда выполняется процедура ОбработкаПроведения, вся работа происходит внутри одной транзакции. Несмотря на то что код расширения с директивой &После отрабатывает после основного кода модуля объекта, транзакция в этот момент еще не зафиксирована. Это означает следующее:

  1. Изменения в регистрах еще не «видны» для стандартных запросов, если уровень изоляции транзакций на сервере СУБД настроен стандартным образом.
  2. Если в коде происходит ошибка, транзакция откатится, и записи в регистрах не появятся.
  3. Получение остатков через виртуальные таблицы запросом может вернуть данные без учета текущего документа, так как платформа еще не успела обновить итоги.

Рассмотрим подробнее способы обхода этого ограничения.

Решение 1. Принудительная запись движений

Самый быстрый и простой способ увидеть изменения в регистрах внутри транзакции — это принудительно вызвать метод Записать() для коллекции движений. Рассмотрим на примере кода в расширении:


&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
    // Если документ не должен быть проведен, выходим
    Если Отказ Тогда
        Возврат;
    КонецЕсли;

    // Принудительно записываем движения в базу данных (внутри транзакции)
    ЭтотОбъект.Движения.Записать();
    
    // Теперь остатки в регистрах физически изменены, и запрос их увидит
    еГ_РаботаСXML.СоздатьФайлСОстатками(ЭтотОбъект);
КонецПроцедуры

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

Решение 2. Использование событий формы и ДополнительныеСвойства

Если действие должно выполняться только при интерактивном проведении пользователем из формы, мы можем использовать событие ПослеЗаписиНаСервере. Но как понять, что документ именно проводился, а не просто записывался? Для этого воспользуемся коллекцией ДополнительныеСвойства.

Разберем по шагам:

  1. В модуле объекта (в расширении) устанавливаем флаг проведения.
  2. В модуле формы проверяем этот флаг.

Пример кода в модуле объекта:


&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
    ЭтотОбъект.ДополнительныеСвойства.Вставить("ДокументПроведен", Истина);
КонецПроцедуры

Пример кода в модуле формы:


&После("ПослеЗаписиНаСервере")
Процедура еГ_ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    Если ТекущийОбъект.ДополнительныеСвойства.Свойство("ДокументПроведен") Тогда
        // Здесь транзакция уже завершена, и остатки гарантированно актуальны
        еГ_РаботаСXML.СоздатьФайлСОстатками(ТекущийОбъект);
    КонецЕсли;
КонецПроцедуры

Этот метод хорош тем, что он выполняется вне основной транзакции проведения, что исключает блокировки базы данных при длительном формировании файла.

Решение 3. Фоновые задания (асинхронный подход)

Проанализируем ситуацию, когда формирование файла или расчет остатков занимает значительное время. Заставлять пользователя ждать завершения транзакции — плохая практика. Лучше всего запустить фоновое задание.

Посмотрим на пример реализации:


&После("ОбработкаПроведения")
Процедура еГ_ОбработкаПроведения(Отказ, РежимПроведения)
    ПараметрыЗадания = Новый Массив;
    ПараметрыЗадания.Добавить(ЭтотОбъект.Ссылка);
    
    // Запускаем фоновое задание, которое выполнится сразу после фиксации транзакции
    ФоновыеЗадания.Выполнить("еГ_РаботаСXML.СформироватьФайлФоновымЗаданием", ПараметрыЗадания);
КонецПроцедуры

Фоновое задание «подождет», пока основная транзакция завершится, и начнет работу уже с полностью актуальными данными в базе.

Решение 4. Использование подписок на события в расширении

Начиная с версии платформы 8.3.10, мы можем создавать собственные объекты ПодпискаНаСобытие прямо в расширении. Это более профессиональный подход, так как он позволяет:

Создадим подписку на событие ОбработкаПроведения для нужных документов и укажем в качестве обработчика процедуру в общем модуле расширения. Логика получения остатков будет аналогична Решению 1 или Решению 3.

Решение 5. Очередь событий через Регистр Сведений

Если конфигурация «тяжелая» (как в случае с решениями на базе УТ), лучшим архитектурным решением будет использование регистра-очереди. Рассмотрим этот алгоритм:

  1. В расширении создаем новый Регистр Сведений еГ_ОчередьВыгрузки с единственным измерением — ссылкой на документ или номенклатуру.
  2. В событиях &После("ОбработкаПроведения") и &После("ОбработкаУдаленияПроведения") просто записываем ссылку в этот регистр. Это происходит мгновенно и не блокирует работу.
  3. Создаем регламентное задание, которое раз в 5–10 минут читает данные из этого регистра, формирует нужные файлы и очищает очередь.

Это решение гарантирует 100% точность остатков, так как данные читаются заведомо после завершения всех транзакций, и никак не влияет на скорость работы пользователей в программе.

Резюме

Выбор метода зависит от ваших требований к скорости обновления данных. Если файл нужен мгновенно и он формируется быстро — используйте Движения.Записать(). Если важна производительность системы — используйте фоновые задания или схему с очередью в регистре сведений.

← На главную