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