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