Как ограничить выбор значения в табличной части так, чтобы можно было только перевыбрать текущее значение?

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

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

Рассмотрим подробнее, почему стандартные механизмы, такие как процедура ПриИзменении, не подходят для этой задачи, и разберем по шагам несколько эффективных способов реализации данного ограничения.

Проблема использования события ПриИзменении

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

Способ 1. Использование события ОбработкаВыбора

Этот метод является наиболее близким к тому, что реализовал автор темы. Событие ОбработкаВыбора срабатывает у элемента формы (поля ввода), когда пользователь уже выбрал значение в форме списка или из выпадающего списка, но оно еще не «приземлилось» в реквизит.

Разберем пример кода для этого случая:


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

В этом сценарии, если пользователь выберет другой документ, флаг СтандартнаяОбработка = Ложь предотвратит запись этого значения в реквизит, и старое значение останется нетронутым.

Способ 2. Ограничение через НачалоВыбора (полная блокировка)

Если логика задачи подразумевает, что при определенном условии менять значение нельзя совсем, проще всего запретить само открытие формы выбора. Для этого используется событие НачалоВыбора.

Выясним причину, по которой этот метод считается более «чистым» с точки зрения UX: пользователь даже не тратит время на поиск документа в списке, так как система сразу сообщает о невозможности изменения.


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

Способ 3. Программная фильтрация формы выбора

Иногда требуется, чтобы пользователь всё-таки мог открыть форму выбора (например, чтобы убедиться в правильности данных), но видел там только один допустимый документ — тот, который уже выбран. Для этого в процедуре НачалоВыбора мы можем программно установить отбор для открываемой формы.

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


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

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

Способ 4. Кэширование значения (универсальный метод)

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

  1. Создаем переменную модуля формы, например, КэшЗначения.
  2. В событии ПриНачалеРедактирования записываем текущее значение в эту переменную.
  3. В событии ПриИзменении сравниваем новое значение с кэшем.

&НаКлиенте
Перем КэшЗначения;

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

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

Дополнительные меры безопасности

Важно помнить, что любые проверки на клиенте (в форме) можно обойти. Поэтому обязательно дублируйте проверку неизменности реквизитов на сервере в модуле объекта в процедуре ПередЗаписью. Проанализируйте коллекцию Ссылка.ИмяТабличнойЧасти (данные в базе) и сравните их с текущей коллекцией ЭтотОбъект.ИмяТабличнойЧасти (данные, которые пытаются записать). Если обнаружены расхождения в запрещенных для изменения колонках, вызывайте Отказ = Истина.

Также не забудьте про события Очистка и АвтоПодбор. Если вы ограничили только выбор через форму, пользователь может просто нажать кнопку «X» (очистить) или ввести часть номера документа с клавиатуры. В этих событиях также следует установить СтандартнаяОбработка = Ложь, если изменение данных не допускается.

← На главную