Как прервать проведение документа, но при этом сохранить внесенные изменения в 1С

Программист 1С v8.3 (Обычные формы)
← На главную

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

Анализируем проблему: Почему Отказ = Истина в ОбработкеПроведения стирает изменения?

Разберем по шагам, что происходит, когда пользователь нажимает кнопку "Провести". Платформа открывает одну общую транзакцию. Внутри этой транзакции последовательно вызываются события: ПередЗаписью (на сервере), собственно запись объекта в базу данных, и затем — ОбработкаПроведения. Если в процедуре ОбработкаПроведения мы устанавливаем флаг Отказ = Истина, система воспринимает это как сигнал к полной отмене транзакции. Согласно принципу атомарности (ACID), либо все действия внутри транзакции выполняются успешно, либо все они отменяются. В итоге запись документа в базу данных аннулируется, и все изменения, внесенные пользователем перед нажатием кнопки, пропадают.

Проанализируем ситуацию, в которой оказался автор темы. Он пытался вызвать метод Записать(РежимЗаписиДокумента.Запись) прямо внутри процедуры проведения. Это классическая ошибка, так как объект уже находится в состоянии записи, и повторный вызов записи того же экземпляра объекта внутри транзакции либо проигнорируется, либо приведет к ошибке блокировки, но главное — последующий Отказ = Истина все равно откатит всё назад.

Решение 1: Перенос логики в событие ПередЗаписью

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

Посмотрим на пример реализации в модуле объекта:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    
    // Проверяем, что пользователь нажал именно кнопку "Провести"
    Если РежимЗаписи = РежимЗаписиДокумента.Проведение Тогда
        
        // Выполняем нашу проверку (например, соответствие ТЧ перемещению)
        ТчСоответствует = ПроверитьСоответствиеТабличнойЧасти();
        
        Если НЕ ТчСоответствует Тогда
            // Если условие не выполнено, мы НЕ ставим Отказ = Истина,
            // так как это отменит запись. Вместо этого мы меняем РЕЖИМ записи.
            
            РежимЗаписи = РежимЗаписиДокумента.Запись;
            
            #Если Клиент Тогда
                Сообщить("Документ записан, но не проведен: данные ТЧ некорректны!");
            #КонецЕсли
        КонецЕсли;
        
    КонецЕсли;

КонецПроцедуры

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

Решение 2: Использование флагов и ДополнительныхСвойств

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

В ПередЗаписью мы помечаем объект специальным флагом, а в ОбработкаПроведения принимаем решение. Однако, помните: если мы хотим именно записать изменения и не провести, нам всё равно придется менять РежимЗаписи в ПередЗаписью, как показано выше. Если же мы всё-таки попали в ОбработкаПроведения, то единственный способ не делать движений, но оставить документ "проведенным" (с синей галочкой) — это просто выйти из процедуры.

Рассмотрим пример кода в ОбработкаПроведения:


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

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

Решение 3: Работа в модуле формы (перехват на клиенте)

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

  1. В обработчике ПередЗаписью на форме проверяем ПараметрыЗаписи.РежимЗаписи.
  2. Вызываем серверную функцию для проверки условий проведения.
  3. Если проверка не прошла, спрашиваем пользователя: "Проведение невозможно. Записать документ без проведения?".
  4. При согласии — меняем ПараметрыЗаписи.РежимЗаписи на Запись.

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

Технические нюансы и "подводные камни"

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

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

2. Блокировки. При попытке программной записи объекта из самого себя (например, вызов ЭтотОбъект.Записать() внутри события записи) платформа 8.3 выдаст ошибку. Никогда не вызывайте метод Записать() внутри обработчиков событий записи или проведения текущего объекта.

3. Фоновые задания. В сообщении 26 предлагали использовать фоновое задание для записи. Проанализируем это: фоновое задание работает в другой транзакции. Если основная транзакция еще не завершена (а она не завершена, пока работает ОбработкаПроведения), фоновое задание наткнется на объектную блокировку и не сможет ничего записать. Этот метод избыточен и сложен в реализации для данной задачи.

Итоги

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

← На главную