При работе с системой компоновки данных (СКД) программисты часто сталкиваются с необходимостью динамического формирования отборов. Одна из типичных задач — создание сложной иерархии условий (например, вложенные группы "И" или "ИЛИ"). Однако при попытке программно изменить родителя уже существующей группы или элемента отбора, разработчик получает ошибку: «Поле объекта недоступно для записи (Родитель)» (разобраться в причинах поможет консоль кода). В этой статье мы подробно разберем, почему так происходит и как правильно манипулировать структурой отбора.
Проанализируем архитектуру платформы 1С:Предприятие 8. Свойство Родитель у объектов типа ГруппаЭлементовОтбораКомпоновкиДанных и ЭлементОтбораКомпоновкиДанных предназначено исключительно для получения информации о том, в какую коллекцию входит данный элемент. В СКД иерархия определяется физическим нахождением объекта в коллекции Элементы конкретной группы или самого отбора.
Важный момент: вы не можете просто «перевесить» элемент из одной группы в другую, изменив ссылку на родителя. Единственный верный способ — это создание нового элемента в целевой коллекции и копирование в него свойств исходного элемента (или удаление старого и добавление нового).
Рассмотрим ситуацию, когда мы формируем структуру отбора «с нуля». Чтобы один элемент стал дочерним по отношению к другому, его нужно добавлять не в общий корень отбора, а в коллекцию Элементы родительской группы. Посмотрим на пример кода, который создает вложенную структуру:
// Получаем коллекцию элементов отбора из компоновщика настроек
Отбор = КомпоновщикНастроек.Настройки.Отбор;
// Создаем родительскую группу (например, группа "И")
ГруппаРодитель = Отбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаРодитель.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
ГруппаРодитель.Использование = Истина;
ГруппаРодитель.Представление = "Основное условие";
// Создаем дочернюю группу (например, группа "ИЛИ") внутри родительской
ГруппаДочерняя = ГруппаРодитель.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаДочерняя.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
ГруппаДочерняя.Использование = Истина;
В данном примере ГруппаДочерняя автоматически получит в свойстве Родитель ссылку на ГруппаРодитель, так как она была создана через метод Добавить() коллекции элементов родителя.
Если у вас уже есть готовая группа с элементами на верхнем уровне, и вы хотите «обернуть» ее в другую группу (сделать уровнем выше), нам придется прибегнуть к рекурсивному копированию. Разберем по шагам, как это реализовать:
Для упрощения этой задачи в Библиотеке стандартных подсистем (БСП) существует общий модуль КомпоновкаДанныхКлиентСервер. Метод СкопироватьЭлементы позволяет рекурсивно перенести структуру из одного источника в другой.
Рассмотрим пример реализации:
// 1. Сохраняем текущий отбор
СтарыйОтбор = КомпоновщикНастроек.Настройки.Отбор;
// 2. Создаем временную структуру настроек
НовыеНастройки = Новый НастройкиКомпоновкиДанных;
НовыйОтбор = НовыеНастройки.Отбор;
// 3. Создаем новую «супер-группу» на верхнем уровне
ГлавнаяГруппа = НовыйОтбор.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГлавнаяГруппа.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
ГлавнаяГруппа.Использование = Истина;
// 4. Используем механизм БСП для копирования элементов из старого отбора в новую группу
КомпоновкаДанныхКлиентСервер.СкопироватьЭлементы(ГлавнаяГруппа, СтарыйОтбор, Ложь);
// 5. Загружаем новые настройки обратно в компоновщик
КомпоновщикНастроек.ЗагрузитьНастройки(НовыеНастройки);
Если в вашей конфигурации интегрирована БСП, мы рекомендуем использовать стандартные функции из модуля ОбщегоНазначенияКлиентСервер. Это сделает код более чистым и защищенным от ошибок типизации. Выясним, как это выглядит на практике:
// Создаем группу "ИЛИ" с помощью БСП
ПредставлениеГруппы = "Группа по контрагентам";
ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
// Функция СоздатьГруппуЭлементовОтбора возвращает объект созданной группы
НоваяГруппа = ОбщегоНазначенияКлиентСервер.СоздатьГруппуЭлементовОтбора(
КомпоновщикНастроек.Настройки.Отбор,
ПредставлениеГруппы,
ТипГруппы
);
// Теперь мы можем добавлять элементы уже в эту группу
Элемент = НоваяГруппа.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
Элемент.ЛевоеЗначение = Новый ПолеКомпоновкиДанных("Контрагент");
Элемент.ВидСравнения = ВидСравненияКомпоновкиДанных.Равно;
Элемент.ПравоеЗначение = Объект.Контрагент;
Элемент.Использование = Истина;
Проанализируем несколько технических тонкостей, которые помогут избежать ошибок при программном управлении отборами:
КомпоновщикНастроек.Настройки.Отбор могут не отобразиться мгновенно. После программных манипуляций часто требуется вызвать КомпоновщикНастроек.ЗагрузитьПользовательскиеНастройки() или работать напрямую с коллекцией КомпоновщикНастроек.ПользовательскиеНастройки.ИдентификаторПользовательскойНастройки. Если вы создадите два элемента с одинаковым идентификатором, платформа выдаст ошибку при попытке вывода отбора на форму.ПолеКомпоновкиДанных для установки свойства ЛевоеЗначение. Простое указание строки с именем поля приведет к некорректной работе отбора.Рассмотрим сложный пример формирования двойной группировки вида: ( (Условие1 И Условие2) ИЛИ (Условие3 И Условие4) ).
КоллекцияЭлементов = КомпоновщикНастроек.Настройки.Отбор.Элементы;
// Внешняя группа ИЛИ
ГруппаИЛИ = КоллекцияЭлементов.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИЛИ.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИЛИ;
// Первая вложенная группа И
ГруппаИ_1 = ГруппаИЛИ.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИ_1.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
// Вторая вложенная группа И
ГруппаИ_2 = ГруппаИЛИ.Элементы.Добавить(Тип("ГруппаЭлементовОтбораКомпоновкиДанных"));
ГруппаИ_2.ТипГруппы = ТипГруппыЭлементовОтбораКомпоновкиДанных.ГруппаИ;
// Наполняем ГруппаИ_1 и ГруппаИ_2 элементами аналогично предыдущим примерам
Таким образом, мы выяснили, что программная установка родителя в СКД реализуется через правильный выбор коллекции, в которую добавляется объект. Использование методов БСП значительно ускоряет этот процесс и минимизирует количество «костылей» в коде.