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

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Каждый разработчик 1С рано или поздно сталкивается с бизнес-требованием: заблокировать документ от изменений после того, как он перешел в определенный статус (например, «Согласован», «Оплачен» или «Услуга оказана»). При этом важно сохранить возможность редактирования для администраторов (пользователей с полными правами) и оставить доступными для обычных пользователей некоторые отдельные поля или кнопки (например, кнопку отправки на согласование или поле для комментариев).

В рамках этой статьи мы подробно разберем, как решить эту задачу на управляемых формах в платформе «1С:Предприятие 8.3». Мы рассмотрим несколько подходов: от простых и быстрых до профессиональных и максимально безопасных.

Шаг 1. Безопасность данных: Защита на уровне сервера

Перед тем как настраивать интерфейс управляемой формы, мы должны уяснить важнейшее правило разработки в 1С: интерфейсная блокировка формы не гарантирует безопасность данных! Если мы просто отключим элементы на форме, опытный пользователь или сторонний разработчик сможет изменить статус и любые другие реквизиты документа в обход формы. Это можно сделать через обработку «Групповое изменение реквизитов», внешние обработки, API (Web/HTTP-сервисы) или программно из другого кода.

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

Проанализируем пример кода, который нужно разместить в модуле объекта документа:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)

    // Предотвращаем бесконечный цикл и проверяем, что документ не новый
    Если ЭтоНовый() Тогда
        Возврат;
    КонецЕсли;

    // Если у пользователя есть полные права, проверка не требуется
    Если РольДоступна("ПолныеПрава") Тогда
        Возврат;
    КонецЕсли;

    // Получаем текущее значение статуса непосредственно из базы данных
    СтарыйСтатус = Ссылка.Статус;

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

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

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

Шаг 2. Интерфейсные решения: Блокировка формы на клиенте

Теперь, когда наши данные защищены на сервере, перейдем к реализации удобного интерфейса для пользователя. Пользователь не должен заполнять поля документа, чтобы в самом конце при нажатии кнопки «Записать» получить отказ. Наша задача — наглядно показать ему, что документ закрыт для редактирования.

Рассмотрим два основных подхода к блокировке элементов управляемой формы.

Вариант А. Использование свойства ТолькоПросмотр всей формы

Самый простой способ закрыть форму — установить системное свойство формы ТолькоПросмотр. Мы можем сделать это при открытии формы или при изменении статуса документа.

Давайте посмотрим, как реализовать это в модуле управляемой формы:


&НаСервере
Процедура ПриСозданииНаСервере(Отказ, СтандартнаяОбработка)
    
    УстановитьДоступностьФормыНаСервере();
    
КонецПроцедуры

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

Важная деталь: Несмотря на свою простоту, метод ЭтаФорма.ТолькоПросмотр = Истина имеет существенный недостаток. Платформа 1С полностью блокирует интерактивность всей формы. Это означает, что пользователи больше не смогут нажимать на кнопки выбора и открытия («лупы» или гиперссылки) в полях ввода. Они лишатся возможности «провалиться» в карточку контрагента, открыть связанный договор или просмотреть прикрепленные файлы. Пользователи начнут жаловаться на крайнее неудобство работы.

Вариант Б. Поэлементная блокировка с сохранением навигации

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

Разберем эффективный алгоритм обхода элементов формы в модуле управляемой формы:


&НаКлиенте
Процедура ПриОткрытии(Отказ)
    УстановитьДоступностьЭлементов();
EndПроцедуры

&НаСервере
Функция ПользовательИмеетДоступ()
    // Проверяем роли через БСП или напрямую
    Возврат РольДоступна("ПолныеПрава") ИЛИ РольДоступна("РедактированиеУтвержденныхДокументов");
КонецФункции

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

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

Шаг 3. Оптимизация формы с помощью группировки элементов

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

Для оптимизации процесса мы рекомендуем использовать группировку элементов формы. Объединяйте логически связанные поля в группы элементов (например, ГруппаОсновное, ГруппаТабличныеЧасти). На управляемой форме свойства доступности наследуются подчиненными элементами от родительского контейнера. Вместо обхода сотен полей достаточно изменить свойства всего нескольких родительских групп:


// Пример быстрой блокировки целых разделов формы
Элементы.ГруппаОсновное.ТолькоПросмотр = Истина;
Элементы.ГруппаТабличныеЧасти.ТолькоПросмотр = Истина;
Элементы.ГруппаКнопкиДействий.Доступность = Ложь;

Этот метод не только повышает скорость работы интерфейса, но и делает программный код значительно чище, лаконичнее и проще для дальнейшей поддержки другими разработчиками.

Шаг 4. Использование стандартного механизма БСП

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

Преимущество БСП заключается в том, что для пользователей с полными правами в меню «Ещё» автоматически выводится стандартная кнопка «Разрешить редактирование реквизитов», которая безопасно разблокирует поля формы после предупреждения о возможных последствиях.

Разберем шаги по интеграции этого механизма:

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

Использование встроенных механизмов БСП — это наиболее предпочтительный, правильный путь разработки в современных конфигурациях 1С, соответствующий стандартам фирмы «1С».

← На главную