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