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

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

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

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

Постановка задачи и анализ ограничений платформы

Рассмотрим практический сценарий. В документе программно формируется табличная часть КонтактнаяИнформация. При открытии формы в ней всегда присутствует определенный набор строк:

  1. Адрес (тип строки — Адрес)
  2. Телефон (тип строки — Телефон)
  3. Информация (тип строки — Информация)

Наша цель: сделать так, чтобы в строке Информация колонка Данные становилась недоступной для редактирования (или открывалась только на просмотр), если в строке Телефон колонка Данные не заполнена.

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

Разбор типичной ошибки: Поле объекта не обнаружено (ТекущаяКолонка)

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

Поле объекта не обнаружено (текущаяколонка)

Выясним причину. В обычных формах элемент управления табличной частью на форме имел тип ТабличноеПоле, у которого действительно было свойство ТекущаяКолонка. В управляемых формах таблица представлена объектом ТаблицаФормы. У этого объекта нет свойства ТекущаяКолонка. Вместо него для определения активной колонки необходимо использовать свойство ТекущийЭлемент, которое возвращает элемент формы (поле ввода), связанный с колонкой:

ИмяТекущейКолонки = Элемент.ТекущийЭлемент.Имя;

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

Способ 1. Технический реквизит строки + Условное оформление (Рекомендуемый)

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

Шаг 1. Создание реквизита

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

Шаг 2. Расчет флага доступности

Нам потребуется написать процедуру, которая будет обходить табличную часть и устанавливать этот флаг для строк с типом "Информация". Эту процедуру нужно вызывать при создании формы на сервере, а также при изменении поля "Телефон" на клиенте.


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

Шаг 3. Настройка Условного оформления

Теперь настроим само условное оформление. Это можно сделать интерактивно в свойствах формы (вкладка "Условное оформление") или программно в процедуре ПриСозданииНаСервере:


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

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

Способ 2. Динамическое управление свойством «ТолькоПросмотр» при активизации строки

Если по каким-то причинам вы не можете или не хотите добавлять новые реквизиты в табличную часть (даже экранные), можно управлять поведением колонки динамически "на лету".

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

Для реализации этого метода используем клиентское событие таблицы ПриАктивизацииСтроки:


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

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

Способ 3. Запрет редактирования через событие «ПередНачаломИзменения»

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

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


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

Подведем итоги

Каждый из рассмотренных методов имеет свои преимущества:

Используйте данные шаблоны в своих проектах, комбинируйте их в зависимости от требований к интерфейсу ваших конфигураций на платформе 1С:Предприятие!

← На главную