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