Как отменить изменение ячейки табличной части в управляемой форме 1С при событии ПриИзменении?

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

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

Сложность заключается в том, что в обработчике события ПриИзменении для элемента формы табличной части отсутствует стандартный параметр Отказ, который есть, к примеру, в событиях ПередЗаписью или ПередНачаломДобавления. К моменту вызова процедуры ПриИзменении новые данные уже записаны в реквизиты формы. В этой статье мы подробно разберем несколько профессиональных способов решения этой задачи: от простого «отката» булевых полей до универсального кэширования значений и использования механизмов условного оформления.

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


Способ 1. Быстрое «инвертирование» (для булевых реквизитов)

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

Проанализируем программный код, который реализует это поведение:


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

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

Преимущества метода:

  1. Предельно простой код, не требующий создания дополнительных переменных или усложнения логики формы.
  2. Быстрое исполнение на клиенте.

Недостатки метода:

  1. Неуниверсальность: данный подход абсолютно неприменим для полей других типов (числа, строки, даты, ссылки на справочники или документы). Вы не сможете «инвертировать» ссылку на склад или введенную сумму.
  2. Проблемы с интерфейсом (UX): пользователь видит, как галочка сначала физически ставится в ячейке, а затем, после предупреждения, резко исчезает. Визуально это выглядит как микродерганье интерфейса.

Способ 2. Универсальный кэш старого значения (Рекомендуемый программный метод)

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

Выясним, как работает этот алгоритм:

  1. Когда пользователь активизирует ячейку и начинает ввод данных, срабатывает событие ПриНачалеРедактирования. В этот момент в данных строки еще хранится старое (предыдущее) значение.
  2. Мы запоминаем это значение в локальную переменную модуля формы на клиенте.
  3. В событии ПриИзменении мы выполняем проверку прав или корректности данных. Если проверка завершается неудачно, мы восстанавливаем значение ячейки из сохраненной переменной.

Посмотрим на пример практической реализации:


&НаКлиенте
Перем ПредыдущееЗначениеЯчейки; // Переменная доступна во всех клиентских процедурах модуля

&НаКлиенте
Процедура СтруктурныеПодразделенияПриНачалеРедактирования(Элемент, НоваяСтрока, Копирование)
    
    // Событие срабатывает при активизации режима ввода в строке
    ТекущаяСтрока = Элементы.СтруктурныеПодразделения.ТекущиеДанные;
    Если ТекущаяСтрока <> Неопределено Тогда
        // Сохраняем исходное значение колонки "ВРаботе" до изменения
        ПредыдущееЗначениеЯчейки = ТекущаяСтрока.ВРаботе;
    КонецЕсли;
    
КонецПроцедуры

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

Этот метод лишен недостатков предыдущего варианта. Он отлично работает со справочниками (например, если пользователь ошибочно выбрал запрещенный Склад), числами и строками.


Способ 3. Превентивный запрет редактирования через Условное Оформление (Идеальный UX)

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

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

Разберем программную настройку по шагам:

  1. Добавим в табличную часть скрытый служебный реквизит типа Булево, например, ДоступноРедактирование.
  2. При заполнении табличной части на сервере (или при открытии документа) для каждой строки рассчитаем права доступа текущего пользователя и запишем результат в колонку ДоступноРедактирование (Истина/Ложь).
  3. Настроим правило условного оформления: если ДоступноРедактирование равно Ложь, то для колонок ВРаботе и Выполнен устанавливается свойство ТолькоПросмотр = Истина.

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


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

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


Способ 4. Использование метода ЗакончитьРедактированиеСтроки()

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

Синтаксис метода:

Элементы.<ИмяТабличнойЧасти>.ЗакончитьРедактированиеСтроки(ОтменаРедактирования)

Если передать в качестве параметра значение Истина, платформа принудительно прекратит редактирование строки и вернет абсолютно все её реквизиты к состоянию, в котором они находились до начала редактирования.


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

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


Важнейшее правило: Всегда дублируйте проверку на сервере при записи!

Любые проверки на уровне интерфейса управляемой формы (события элементов формы, скрытие колонок, условное оформление) служат исключительно для создания удобного и понятного пользовательского интерфейса (UI/UX). Они защищают от случайных ошибок, но не гарантируют безопасность данных.

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

Рассмотрим пример надежной серверной проверки:


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

Использование связки из превентивного условного оформления на клиенте (для удобства работы) и жесткой транзакционной проверки в модуле объекта ПередЗаписью (для безопасности) является золотым стандартом разработки на платформе 1С:Предприятие 8.3.

← На главную