Как в 1С провести один документ из другого, чтобы второй документ увидел движения первого?

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

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

Выясним причину проблемы: как работают транзакции в 1С

Разберем по шагам, что происходит при проведении документа. Процесс записи и проведения выполняется внутри общего транзакционного контекста СУБД:

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

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

Решение 1. Явная запись движений перед проведением второго документа

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

Рассмотрим пример кода в модуле объекта первого документа или в подписке на событие:


// Формируем движения первого документа
Движение = Движения.ОстаткиНоменклатуры.Добавить();
Движение.ВидДвижения = ВидДвиженияНакопления.Приход;
Движение.Период = Дата;
Движение.Номенклатура = СсылкаНаТовар;
Движение.Количество = 10;

// Явно записываем движения первого документа в базу данных в текущей транзакции
Движения.Записать();

// Создаем или получаем второй документ
Док2Объект = СоздаваемыйДокумент.ПолучитьОбъект();
Док2Объект.Дата = Дата + 1; // Смещение на 1 секунду

// Проводим второй документ — теперь он увидит движения первого документа
Док2Объект.Записать(РежимЗаписиДокумента.Проведение);

Вызов метода Движения.Записать() скидывает накопившиеся в памяти наборы движений в таблицы БД в рамках еще не завершенной транзакции. Благодаря этому запросы из процедур проведения Документ2 смогут считать актуальные данные по регистрам.

Архитектурные риски и почему так делать не рекомендуется

Несмотря на то, что явный вызов Движения.Записать() решает задачу "в лоб", этот подход несёт в себе серьезные архитектурные риски:

  1. Взаимные блокировки (Deadlocks): Каскадное проведение документов существенно увеличивает длительность транзакции и количество блокируемых таблиц. В многопользовательской среде это часто приводит к ошибкам превышения времени ожидания блокировок.
  2. Сложности при отмене проведения и перепроведении: При изменении или пометке на удаление Документ1 потребуется писать дополнительный код для отмены проведения или перепроведения Документ2.
  3. Ошибки контроля остатков: Если второй документ проводится внутри транзакции первого, проверка остатков может сработать некорректно при параллельном проведении других документов разными пользователями.

Альтернативные и безопасные способы решения

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

Способ A. Передача данных в памяти без запроса к БД

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

Способ B. Асинхронное проведение через очередь и фоновое задание

Для исключения блокировок проведение второго документа выносят за пределы транзакции первого:

  1. При проведении Документ1 в транзакции фиксируется запись в специальный регистр сведений (очередь отложенного проведения).
  2. После успешного завершения транзакции (например, в событии формы или через механизм фоновых задач) запускается фоновое задание с помощью ФоновыеЗадания.Выполнить().
  3. Фоновое задание выполняется в отдельном сеансе, где транзакция первого документа гарантированно закрыта, и проводит Документ2.

Способ C. Проведение из формы после фиксации транзакции

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


&НаКлиенте
Процедура ПослеЗаписи(ПараметрыЗаписи)
    // Транзакция первого документа уже полностью зафиксирована
    ПровестиСвязанныйДокументНаСервере(Объект.Ссылка);
КонецПроцедуры

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

← На главную