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