Почему при ошибке перепроведения документ в 1С 7.7 остается проведенным и как это исправить?

Программист 1С v7.7 Управленческий учет Торговля и дистрибуция
← На главную

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

Механизм транзакций: почему платформа оставляет документ проведенным

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

  1. Когда пользователь или программный код инициирует проведение, платформа автоматически открывает внутреннюю неявную транзакцию базы данных.
  2. Если документ уже был проведен, в рамках этой же единой транзакции происходит очистка старых движений по регистрам и бухгалтерским счетам, запись измененных реквизитов документа и последующий вызов предопределенной процедуры ОбработкаПроведения.
  3. Если внутри процедуры ОбработкаПроведения возникает программная ошибка, вызывается оператор СтатусВозврата(0) или метод НеПроводитьДокумент(), платформа фиксирует аварийное завершение процесса и инициирует полный откат транзакции (Rollback).
  4. Откат транзакции возвращает абсолютно все данные в состояние, которое предшествовало нажатию кнопки. Это означает, что отменяются изменения реквизитов, восстанавливаются прежние движения по регистрам, а признак документа возвращается к исходному значению — «Проведен».

Таким образом, попытка вызвать внутри модуля документа команду отмены проведения не дает желаемого эффекта: любая ошибка проведения лишь откатывает базу к предыдущему стабильному состоянию.

Посмотрим на типовой код проверки из модуля документа конфигурации «Торговля и Склад»:


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

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

    // Далее следует проведение по регистрам...
    Если (ПустоеЗначение(ВидыДвижений) = 1) ИЛИ (Найти(ВидыДвижений, "Регистр") <> 0) Тогда
        ПроведениеПоРегистрам();
    КонецЕсли;
КонецПроцедуры

Функция глНеПроводить внутри себя содержит оператор СтатусВозврата(0). При повторном проведении документа с неверным складом процедура прерывается, платформа откатывает транзакцию, и в базе восстанавливаются старые, еще корректные движения документа. Для платформы перепроведение — это атомарная операция, а не последовательность из двух независимых действий «снять с проведения» и «провести заново».

Решение 1. Разделение перепроведения на две независимые транзакции через форму

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

Метод СделатьНеПроведенным() нельзя вызывать в модуле документа или внутри транзакции проведения. Его следует выполнять из внешней среды или из модуля формы документа. Разберем пример переопределения кнопки проведения в форме документа:


Процедура ПриНажатииКнопкиПровести()
    // Проверяем, проведен ли документ в текущий момент
    Если Проведен() = 1 Тогда
        // Запоминаем ссылку и закрываем текущую форму для снятия блокировок
        ДокСсылка = ТекущийДокумент();
        Форма.Закрыть(0);

        ДокОбъект = СоздатьОбъект("Документ." + Вид());
        Если ДокОбъект.НайтиДокумент(ДокСсылка) = 1 Тогда
            // Первая транзакция: делаем документ непроведенным
            ДокОбъект.СделатьНеПроведенным();

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

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

Решение 2. Перенос проверок заполнения в предопределенную процедуру ПриЗаписи

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

Для этого перенесем валидацию в предопределенную процедуру формы ПриЗаписи():


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

    Если Склад.РозничныйСклад = 1 Тогда
        Предупреждение("Документ нельзя оформлять с розничного склада!");
        СтатусВозврата(0);
        Возврат;
    КонецЕсли;
КонецПроцедуры

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

Решение 3. Запрет редактирования проведенных документов

С точки зрения методологии бухгалтерского и складского учета свободное редактирование проведенных документов «на лету» несет высокие риски искажения себестоимости и последовательности партионного учета. Наиболее надежный способ защиты базы — запретить прямое редактирование проведенного документа.

Разберем, как включить блокировку в модуле формы документа в процедуре ПриОткрытии:


Процедура ПриОткрытии()
    // Если документ уже проведен и у пользователя нет полных административных прав
    Если (Проведен() = 1) И (НазваниеНабораПрав() <> "Администратор") Тогда
        Форма.ТолькоПросмотр(1);
        Предупреждение("Проведенный документ доступен только для чтения!
                       |Для редактирования сначала отмените проведение через журнал.");
    КонецЕсли;
КонецПроцедуры

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

← На главную