При настройке бизнес-процессов в 1С периодически возникает специфическая задача: жестко заблокировать любые изменения уже проведенного документа для всех категорий пользователей, включая администраторов с полными правами. При этом стандартная попытка перехватить событие ПриЗаписи часто приводит к ошибке в логике: при нажатии кнопки «Отменить проведение» проверка перестает работать, и документ спокойно переходит в непроведенное состояние.
Разберем детально, почему так происходит, выясним причину такого поведения платформы и рассмотрим правильные варианты реализации запрета.
Проанализируем механику работы платформы при записи объектов. Когда пользователь нажимает «Отменить проведение» (интерактивно из формы или списка, либо при вызове метода программно), платформа выполняет определенную цепочку действий:
Проведен в оперативной памяти сразу же сбрасывается в значение Ложь.ПередЗаписью, очищаются движения по регистрам, отрабатывает ОбработкаУдаленияПроведения, и только затем вызывается событие ПриЗаписи.Когда в обработчике ПриЗаписи проверяется свойство объекта через Источник.Проведен, платформа сообщает, что флаг проведения уже снят. В итоге условие проверки не срабатывает, транзакция фиксируется, и проведение успешно отменяется.
Кроме того, событие ПриЗаписи срабатывает уже после того, как документ физически записан в базу данных (хоть транзакция еще и открыта), что делает проверку в этом событии неоптимальной с точки зрения архитектуры.
Рассмотрим правильный подход к глобальному контролю состояния документа. Чтобы определить реальный статус документа до начала текущей транзакции записи, необходимо обратиться к его состоянию, сохраненному непосредственно в базе данных. Для этого используется обращение через ссылку: Источник.Ссылка.Проведен.
Перенесем проверку в событие ПередЗаписью. Это позволит прерывать транзакцию в самом начале, не выполняя лишних действий по записи данных и удалению движений.
Создадим подписку на события в конфигурации со следующими параметрами:
ДокументОбъект.ИмяВашегоДокумента (или несколько документов).ПередЗаписью.Посмотрим на пример кода процедуры-обработчика подписки:
Процедура ЗапретитьИзменениеПроведенныхПередЗаписью(Источник, Отказ, РежимЗаписи, РежимПроведения) Экспорт
// Если документ новый, в базе данных его еще нет, проверять ссылку не имеет смысла
Если Источник.ЭтоНовый() Тогда
Возврат;
КонецЕсли;
// Проверяем статус проведения документа непосредственно в базе данных
Если Источник.Ссылка.Проведен Тогда
Отказ = Истина;
ВызватьИсключение "Редактирование, перепроведение и отмена проведения данного документа запрещены!";
КонецЕсли;
КонецПроцедуры
Разберем по шагам, как отрабатывает данный алгоритм в разных сценариях:
Источник.ЭтоНовый() возвращает Истина, проверка пропускается, документ успешно проводится первый раз.Источник.Ссылка.Проведен = Истина). Срабатывает отказ, запись отменяется.Источник.Проведен уже равно Ложь, однако в базе данных документ всё еще числится проведенным (Источник.Ссылка.Проведен = Истина). Платформа устанавливает Отказ = Истина, транзакция откатывается, и отмена проведения блокируется.Если мы полностью запретили перезапись и отмену проведения, документ все еще могут попытаться удалить напрямую (например, через удаление помеченных объектов или программно вызовом метода Удалить()). При этом событие ПередЗаписью не вызывается.
Для полной изоляции документа создадим вторую подписку на событие ПередУдалением:
Процедура ЗапретитьУдалениеПроведенныхПередУдалением(Источник, Отказ) Экспорт
Если Источник.Проведен Тогда
Отказ = Истина;
ВызватьИсключение "Удаление проведенного документа запрещено!";
КонецЕсли;
КонецПроцедуры
Если блокировку требуется реализовать только для конкретного вида документа без создания общих подписок, можно задействовать предопределенные процедуры модуля объекта документа.
Для перехвата именно операции отмены проведения в модуле объекта существует специальный обработчик ОбработкаУдаленияПроведения. Посмотрим на пример кода:
Процедура ОбработкаУдаленияПроведения(Отказ)
// Блокируем отмену проведения документа
Отказ = Истина;
Сообщить("Отмена проведения для данного документа запрещена правилами учета.");
КонецПроцедуры
Важно помнить: одна лишь процедура ОбработкаУдаленияПроведения защищает только от снятия с проведения. Чтобы пользователь не мог изменить данные внутри проведенного документа или перепровести его с новыми суммами, ее обязательно нужно дополнять проверкой в процедуре ПередЗаписью того же модуля объекта:
Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
Если Не ЭтоНовый() И Ссылка.Проведен Тогда
Отказ = Истина;
Сообщить("Изменение проведенного документа запрещено.");
КонецЕсли;
КонецПроцедуры
При внедрении жестких запретов на уровне кода необходимо учитывать следующие ограничения платформы 1С:
ОбменДанными.Загрузка: если в коде подписки присутствует типовая конструкция Если Источник.ОбменДанными.Загрузка Тогда Возврат; КонецЕсли;, любой программист или пользователь с правами запуска внешних обработок сможет обойти данный запрет, установив этот флаг в Истина перед записью. Если требуется абсолютная блокировка, эту проверку вставлять нельзя (но это сделает невозможным обновление документа через штатные планы обмена).Источник.Ссылка.Проведен неявно выполняет запрос к СУБД для считывания значения поля из таблицы документа. При массовом перепроведении или пакетной обработке документов это вызовет дополнительное чтение из базы.