Как в 1С вывести сообщение с привязкой к конкретной ячейке табличной части?

Программист 1С v8.3 (Управляемые формы) 1С:Управление торговлей Управленческий учет Торговля и дистрибуция
← На главную

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

Проанализируем типичную ситуацию: необходимо проверить, что в каждой строке табличной части "Товары" заполнено поле "ВидЦены". Если поле пустое, нужно отменить проведение документа и подсветить проблемную ячейку.

Почему не работает простой способ?

На первый взгляд, задача кажется простой. Можно перебрать строки табличной части и при выполнении условия вывести сообщение, указав в свойстве Поле нужный реквизит. Посмотрим на первоначальный, нерабочий вариант кода:


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

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

Решение 1: Формируем путь к ячейке вручную

Чтобы система поняла, какую именно ячейку мы хотим подсветить, нужно передать ей путь в определенном формате: ИмяТабличнойЧасти[ИндексСтроки].ИмяКолонки.

Разберем по шагам, как его сформировать:

  1. Имя табличной части: В нашем случае это Товары.
  2. Индекс строки: Это порядковый номер строки в коллекции. Важный момент: индексация коллекций в 1С начинается с нуля. Однако у строки табличной части есть свойство НомерСтроки, которое возвращает ее номер в том виде, как его видит пользователь (начиная с единицы). Поэтому для получения правильного индекса нужно всегда вычитать единицу: Строка.НомерСтроки - 1.
  3. Имя колонки: В нашем примере это ВидЦены.

Теперь соберем все вместе с помощью конкатенации (сложения) строк. Получится рабочий код:


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

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

Решение 2: Используем функции общих модулей (БСП) — рекомендованный подход

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

Рассмотрим две ключевые функции:

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


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

Преимущества этого подхода:

Дополнительное замечание по оптимизации кода

В некоторых примерах можно встретить предварительную проверку на количество строк перед циклом:


Если ЭтотОбъект.Товары.Количество() > 0 Тогда
    Для каждого Строка из ЭтотОбъект.Товары Цикл
        // ...
    КонецЦикла; 
КонецЕсли;

Такая проверка является избыточной. Конструкция цикла Для каждого ... Цикл устроена так, что если коллекция (в нашем случае ЭтотОбъект.Товары) пуста, то тело цикла просто не выполнится ни разу. Код можно и нужно писать без лишней проверки, делая его короче и чище.

← На главную