Как выполнить операцию только после завершения предыдущей и гарантированно дождаться записи в регистры

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

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

Причина проблемы: Транзакционная модель 1С

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

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

Способ 1. Модификация коллекции Движения (Рекомендуемый)

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

Рассмотрим пример кода, который можно разместить сразу после вызова основной логики проведения в модуле документа:


// Внутри процедуры ОбработкаПроведения
ПроведениеДокументов.ОбработкаПроведенияДокумента(ЭтотОбъект, Отказ);

Если Не Отказ Тогда
    // Обращаемся к коллекции движений, которая уже заполнена в памяти
    Для Каждого Запись Из Движения.ИмяВашегоРегистра Цикл
        // Устанавливаем нужные параметры, например, период
        Запись.Период = НужнаяДата;
    КонецЦикла;
    
    // Устанавливаем флаг, что движения нужно записать в конце транзакции
    Движения.ИмяВашегоРегистра.Записывать = Истина;
КонецЕсли;

Этот подход является наиболее правильным согласно стандартам разработки 1С, так как он не нагружает базу данных повторными операциями чтения и записи. Платформа сама сбросит измененные данные в базу в момент завершения транзакции.

Способ 2. Использование обработчика ПослеЗаписиНаСервере в форме

Если логика требует, чтобы записи уже гарантированно существовали в физических таблицах (например, вам нужно запустить внешнюю обработку, которая делает запрос к этим данным), то работу следует перенести в модуль формы. Рассмотрим событие ПослеЗаписиНаСервере.

Это событие примечательно тем, что оно вызывается сразу после того, как основная транзакция записи и проведения успешно завершена. В этот момент данные уже "лежать" в базе.


&НаСервере
Процедура ПослеЗаписиНаСервере(ТекущийОбъект, ПараметрыЗаписи)
    // В этот момент транзакция проведения завершена.
    // Мы можем создать набор записей и прочитать его из базы.
    Набор = РегистрыНакопления.ИмяРегистра.СоздатьНаборЗаписей();
    Набор.Отбор.Регистратор.Установить(ТекущийОбъект.Ссылка);
    Набор.Прочитать();
    
    Для Каждого Запись Из Набор Цикл
        Запись.Период = ТекущийОбъект.Дата; // Пример корректировки
    КонецЦикла;
    
    Набор.Записать();
КонецПроцедуры

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

Способ 3. Расширения и директива &После

Разберем ситуацию, когда вы работаете с типовой конфигурацией и не хотите менять основной код. В 1С:Предприятие 8.3 существует механизм расширений. Мы можем создать процедуру-обертку, которая выполнится после основной обработки проведения, но все еще внутри транзакции.

Для этого в расширении создается процедура с аннотацией &После("ОбработкаПроведения"). Однако помните, что внутри этой процедуры данные все еще не зафиксированы в базе (см. Способ 1). Вы все еще работаете с объектом ЭтотОбъект.Движения.

Способ 4. Принудительная запись внутри транзакции

Существует метод Движения.Записать(). Если вызвать его внутри процедуры проведения, данные будут отправлены в базу немедленно. Посмотрим на пример:


ПроведениеДокументов.ОбработкаПроведенияДокумента(ЭтотОбъект, Отказ);
// Принудительно сбрасываем движения в БД
Движения.ИмяРегистра.Записать();

// Теперь запрос к базе данных внутри этой же транзакции увидит эти записи
Запрос = Новый Запрос("ВЫБРАТЬ * ИЗ РегистрНакопления.ИмяРегистра ГДЕ Регистратор = &Регистратор");
Запрос.УстановитьПараметр("Регистратор", ЭтотОбъект.Ссылка);
// ... выполнение запроса

Внимание: Использование явной записи Записать() внутри транзакции крайне не рекомендуется (кроме исключительных случаев). Это создает избыточные блокировки таблиц базы данных и может существенно замедлить работу системы при одновременной работе многих пользователей.

Асинхронность и фоновые задания

Проанализируем упоминание "асинхронности" в обсуждении. Важно понимать различие: в 1С серверный код (запись в базу) всегда выполняется синхронно. Если вам нужно выполнить тяжелую операцию после проведения, не заставляя пользователя ждать, рассмотрите механизм Фоновых заданий.

В обработчике ПослеЗаписи на клиенте или сервере вы можете инициировать запуск фонового задания, которое в отдельном потоке выполнит необходимые расчеты на основе уже проведенного документа. Это позволит "разнести" процессы по времени и избежать конфликтов блокировок.

Итоги и рекомендации

Подведем итог нашему разбору:

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

Выяснив эти технические подробности, вы сможете строить надежную и быструю архитектуру ваших решений в 1С, избегая "фантомных" ошибок отсутствия данных.

← На главную