В практике разработки на платформе 1С:Предприятие 8.3 часто возникает задача отобразить данные в виде "развертки" или матрицы. Например, у нас есть список документов (заказов или задач), и мы хотим видеть в колонках не просто реквизиты самого документа, а наличие в них определенных услуг или сотрудников. Классический пример: в строках — документы, в колонках — фамилии сотрудников, а на пересечении — флажок, указывающий, участвует ли данный сотрудник в этой задаче.
Стандартный функционал динамического списка не предполагает автоматического "разворота" данных (Pivot), поэтому нам необходимо реализовать это программно. Разберем по шагам два основных подхода к решению этой задачи.
Этот метод считается наиболее правильным и производительным, так как он позволяет пользователю использовать стандартные функции платформы: сортировку, поиск и отбор по созданным колонкам. Суть метода заключается в том, чтобы при создании формы динамически изменить текст запроса динамического списка, добавив в него вычисляемые поля для каждого объекта (например, сотрудника).
Проанализируем алгоритм реализации этого метода в обработчике ПриСозданииНаСервере:
Шаг 1. Получение данных для колонок
Сначала нам нужно определить, какие именно колонки мы хотим видеть. Для этого выберем список сотрудников (или услуг) из справочника.
// Получаем список сотрудников, которые станут нашими колонками
ЗапросСотрудники = Новый Запрос;
ЗапросСотрудники.Текст =
"ВЫБРАТЬ
| Сотрудники.Ссылка КАК Ссылка,
| Сотрудники.Наименование КАК ФИО
|ИЗ
| Справочник.Сотрудники КАК Сотрудники
|ГДЕ
| НЕ Сотрудники.ПометкаУдаления";
РезультатЗапроса = ЗапросСотрудники.Выполнить();
Выборка = РезультатЗапроса.Выбрать();
Шаг 2. Добавление реквизитов формы
Чтобы динамический список "понял", куда помещать данные из нового запроса, мы должны добавить новые реквизиты в объект ДинамическийСписок. Для этого используется метод ИзменитьРеквизиты().
НовыеРеквизиты = Новый Массив;
ДобавляемыеКолонки = Новый СписокЗначений;
Пока Выборка.Следующий() Цикл
ИмяПоля = "Сотрудник_" + СтрЗаменить(Строка(Выборка.Ссылка.УникальныйИдентификатор()), "-", "_");
НовыеРеквизиты.Добавить(Новый РеквизитФормы(ИмяПоля, Новый ОписаниеТипов("Булево"), "Список"));
ДобавляемыеКолонки.Добавить(Выборка.Ссылка, ИмяПоля);
КонецЦикла;
ИзменитьРеквизиты(НовыеРеквизиты);
Шаг 3. Модификация текста запроса
Теперь мы должны достроить текст запроса динамического списка. Рассмотрим пример, где мы проверяем вхождение документа в массив ссылок, где этот сотрудник фигурирует. Либо, что более эффективно, используем вложенные ВЫБОР КОГДА.
ТекстЗапроса = Список.Текст; // Берем исходный текст из настроек ДС
Для Каждого ЭлементСписка Из ДобавляемыеКолонки Цикл
ИмяПоля = ЭлементСписка.Представление;
ТекстЗапроса = ТекстЗапроса + ", ВЫБОР КОГДА &Список_" + ИмяПоля + " СОДЕРЖИТ Ссылка ТОГДА ИСТИНА ИНАЧЕ ЛОЖЬ КОНЕЦ КАК " + ИмяПоля;
КонецЦикла;
// Важное замечание: Использование конструкции "В (&Параметр)" для каждой колонки
// может потребовать предварительного сбора массивов ссылок документов для каждого сотрудника.
Шаг 4. Создание элементов интерфейса
После того как данные подготовлены в запросе, их нужно вывести на форму. Создаем элементы типа ПолеФормы.
Для Каждого ЭлементСписка Из ДобавляемыеКолонки Цикл
ИмяПоля = ЭлементСписка.Представление;
НовыйЭлемент = Элементы.Добавить(ИмяПоля, Тип("ПолеФормы"), Элементы.Список);
НовыйЭлемент.Вид = ВидПоляФормы.ПолеФлажка;
НовыйЭлемент.ПутьКДанным = "Список." + ИмяПоля;
НовыйЭлемент.Заголовок = Строка(ЭлементСписка.Значение);
КонецЦикла;
Разберем альтернативный вариант. Если количество колонок невелико, а логика их заполнения слишком сложна для SQL-запроса, можно использовать событие ПриПолученииДанныхНаСервере. Этот метод позволяет "дорисовать" значения в колонки прямо перед отправкой данных на клиент.
Проанализируем особенности этого метода:
ЛОЖЬ КАК Сотрудник1, ЛОЖЬ КАК Сотрудник2.ПриПолученииДанныхНаСервере вы обходите коллекцию НастройкиПриПолученииДанных и заполняете нужные колонки.
&НаСервере
Процедура СписокПриПолученииДанныхНаСервере(ИмяЭлемента, Настройки, Данные)
// Получаем массив ссылок документов, которые сейчас отображаются на экране
МассивСсылок = Данные.ПолучитьКлючи();
// Выполняем один запрос к базе, чтобы понять, какие сотрудники есть в этих документах
Запрос = Новый Запрос;
Запрос.Текст = "ВЫБРАТЬ Сотрудник, Ссылка ИЗ Документ.Задачи.Сотрудники ГДЕ Ссылка В (&МассивСсылок)";
Запрос.УстановитьПараметр("МассивСсылок", МассивСсылок);
ТаблицаСвязей = Запрос.Выполнить().Выгрузить();
Для Каждого СтрокаДанных Из Данные Цикл
Для Каждого Колонка Из ДобавляемыеКолонки Цикл
// Ищем связь в таблице и устанавливаем Истина/Ложь
// СтрокаДанных.Значение.Установить(Колонка.Представление, РезультатПоиска);
КонецЦикла;
КонецЦикла;
КонецПроцедуры
Важно: Помните, что при использовании этого метода стандартная сортировка и фильтрация 1С по этим колонкам работать не будет, так как данные не извлекаются из базы данных основным запросом списка.
Выясним причину, почему первый способ считается профессиональным стандартом. Платформа 1С при работе с динамическим списком стремится минимизировать объем передаваемых данных. Модификация текста запроса позволяет СУБД (SQL Server, PostgreSQL) самостоятельно рассчитать значения флажков, используя индексы. Это критично, если в списке тысячи документов.
Рассмотрим несколько советов по оптимизации:
УсловноеОформление для списка. Например, можно выделять цветом фона ячейки, где стоит "Истина".УникальныйИдентификатор) следите за тем, чтобы имя начиналось с буквы и не содержало спецсимволов, иначе система выдаст ошибку.Подведем итог: для реализации "горизонтального" списка услуг или сотрудников в динамическом списке наиболее эффективным является сочетание программного изменения структуры реквизитов формы и динамической сборки текста запроса. Это сохраняет интерактивность списка и обеспечивает высокую скорость работы интерфейса.