Как управлять движениями документа и правильно использовать флаг Отказ в 1С

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

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

Разница между флагом Отказ и оператором Возврат

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

Флаг Отказ = Истина: Это сигнал системе о том, что возникла критическая ошибка. Установка этого флага в значение Истина инициирует откат всей транзакции. Это означает, что:

  1. Документ не получит статус "Проведен".
  2. Никакие движения ни по одному регистру не будут записаны в базу данных.
  3. Все изменения других объектов, сделанные в этой транзакции, будут аннулированы.

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

Сценарий 1: Документ проведен, но движения не формируются

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


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

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

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

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

Сценарий 2: Запрет проведения при нарушении условий

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


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

Без оператора Возврат код продолжит выполняться до конца процедуры, тратя процессорное время и память на расчет движений, которые в итоге все равно будут отменены из-за Отказ = Истина.

Управление движениями через свойство Записывать

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

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


// Регистр Деньги пишем всегда
Движения.Деньги.Записывать = Истина;
Движение = Движения.Деньги.Добавить();
// ... заполнение

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

Критическая ошибка: Поиск по наименованию в коде

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

Вместо этого рекомендуется использовать предопределенные элементы (если они возможны), либо хранить ссылку на "Нашу фирму" в Константах или Дополнительных реквизитах организации.

Архитектурные рекомендации: Вынос логики в Общие модули

Хотя процедура ОбработкаПроведения всегда должна находиться в модуле объекта документа, саму логику расчета движений правильно выносить в Общие модули. Рассмотрим, как это выглядит:


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

// В общем модуле ПроведениеСервер (с флагом Сервер)
Процедура ВыполнитьДвиженияОказанияУслуг(ДокументОбъект, Отказ) Экспорт
    // Здесь вся логика расчетов
    // Флаг Отказ передается по ссылке, его изменение здесь повлияет на транзакцию документа
КонецПроцедуры

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

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

← На главную