Как программно провести один документ при проведении другого в 1С

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

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

Метод 1: Прямое проведение из модуля объекта

Самый простой и очевидный способ — вызвать процедуру проведения подчиненного документа непосредственно в процедуре ОбработкаПроведения основного документа. Разберем этот процесс по шагам.

Допустим, в нашем документе Расход_денег есть реквизит ДоговорПоставки или прямая ссылка на накладную. Нам необходимо получить объект этого документа и вызвать метод Записать с указанием соответствующего режима записи.

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


Процедура ОбработкаПроведения(Отказ, РежимПроведения)
    // 1. Находим ссылку на документ, который нужно провести
    // Допустим, ссылка хранится в реквизите ОбъектОплаты
    Если ЗначениеЗаполнено(ОбъектОплаты) И ТипЗнч(ОбъектОплаты) = Тип("ДокументСсылка.ПриходнаяНакладная") Тогда
        
        // 2. Получаем объект из ссылки для возможности редактирования и проведения
        ДокументОбъект = ОбъектОплаты.ПолучитьОбъект();
        
        // 3. Проверяем, не проведен ли он уже, чтобы избежать лишней нагрузки
        Если НЕ ДокументОбъект.Проведен Тогда
            Попытка
                // 4. Выполняем программное проведение
                ДокументОбъект.Записать(РежимЗаписиДокумента.Проведение);
            Исключение
                // 5. Если возникла ошибка, прерываем проведение основного документа
                Отказ = Истина;
                Сообщение = Новый СообщениеПользователю;
                Сообщение.Текст = "Не удалось провести связанную накладную: " + КраткоеОписаниеОшибки(ИнформацияОбОшибке());
                Сообщение.Сообщить();
            КонецПопытки;
        КонецЕсли;
        
    КонецЕсли;
КонецПроцедуры

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

Метод 2: Использование подписок на события

Если логика конфигурации сложная, лучше не загромождать модули документов и использовать Подписки на события. Это делает код более чистым и облегчает поддержку системы. Рассмотрим ситуацию, когда при проведении любого документа оплаты нужно «подтягивать» и проводить соответствующие первичные документы.

Для реализации этого метода:

  1. Создаем новый объект метаданных Подписка на событие.
  2. В качестве источника выбираем ДокументОбъект.Расход_денег.
  3. Событие — ОбработкаПроведения.
  4. В обработчике (общем модуле) прописываем аналогичную логику поиска и записи объекта.

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

Транзакционная целостность и риски рекурсии

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

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

Посмотрим на пример безопасного проведения:


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

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

Альтернативный подход: Формирование движений без проведения

Существует еще один способ, который позволяет избежать вызова тяжелого метода проведения. Мы можем оставить «Приходную накладную» в статусе «не проведена», но документ «Расход денег» сам запишет данные в регистры от имени этой накладной.

Разберем, как это работает через наборы записей:

  1. В документе Расход_денег получаем ссылку на накладную.
  2. Создаем набор записей нужного регистра (например, РегистрНакопления.ТоварыНаСкладах).
  3. Устанавливаем отбор по Регистратору, где регистратором будет выступать ссылка на Приходную накладную.
  4. Заполняем таблицу движений вручную.
  5. Вызываем НаборЗаписей.Записать(Ложь).

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

Особенности оперативного проведения и блокировок

Проанализируем ситуацию с блокировками данных (Deadlocks). При программном проведении сразу нескольких документов нагрузка на СУБД возрастает. Если два пользователя одновременно проводят два разных «Расхода денег», которые ссылаются на одну и ту же «Приходную накладную», может возникнуть конфликт блокировок.

Для минимизации таких случаев рекомендуем:

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

Итоги

Мы рассмотрели основной способ программного взаимодействия документов в 1С. Наиболее надежным решением является вызов метода Записать(РежимЗаписиДокумента.Проведение) с обязательной обработкой исключений через блок Попытка...Исключение. Это гарантирует, что либо оба документа будут успешно проведены, либо (в случае ошибки) база данных останется в исходном состоянии без противоречивых данных.

← На главную