При работе со старыми конфигурациями, такими как Управление торговлей 10.3, Комплексная автоматизация 1.1 или УПП 1.3, разработчики и пользователи часто сталкиваются с досадным ограничением «Универсального отчета». Проблема проявляется следующим образом: если пользователь устанавливает несколько условий на один и тот же реквизит (например, Контрагент В Группе "Оптовики" И Контрагент НЕ В Списке "Заблокированные"), то при сохранении и последующем открытии настроек остается только последнее условие. Второе просто исчезает.
В этой статье мы подробно разберем, почему это происходит, проанализируем внутреннее устройство механизма ПостроительОтчета и рассмотрим несколько эффективных способов решения этой задачи.
Разберем техническую сторону вопроса. «Универсальный отчет» в толстом клиенте базируется на объекте платформы ПостроительОтчета. Для сохранения настроек обычно используются стандартные методы ПолучитьНастройки() и УстановитьНастройки().
Когда платформа выполняет метод ПолучитьНастройки(), она сериализует коллекцию отборов в структуру. В этой структуре ключом является имя поля (путь к данным). Поскольку в структуре (или соответствии) ключ должен быть уникальным, каждое последующее условие по тому же полю перезаписывает предыдущее. Таким образом, если у вас три строки отбора по полю Номенклатура, в сохраненный файл или базу данных попадет только информация из последней строки.
В отличие от устаревшего построителя, современная Система компоновки данных (СКД) хранит элементы отбора как коллекцию объектов, где каждый элемент имеет свой уникальный идентификатор, что полностью снимает эту проблему. Однако, если переписывать отчет на СКД долго или невозможно, рассмотрим альтернативные варианты.
Проанализируем самый быстрый способ, который не требует глубокого переписывания механизмов сохранения настроек. Если у нас есть доступ к тексту запроса, который формирует данные для построителя, мы можем просто продублировать поля.
Рассмотрим пример. Вместо стандартного выбора поля Контрагент, добавим его дубль с другим псевдонимом:
ВЫБРАТЬ
Продажи.Контрагент КАК Контрагент,
Продажи.Контрагент КАК КонтрагентДубль,
Продажи.СуммаОборот КАК Сумма
ИЗ
РегистрНакопления.Продажи.Обороты(&ДатаНач, &ДатаКон) КАК Продажи
Теперь в настройках отчета пользователь сможет выбрать основное поле Контрагент для условия «В группе» и поле КонтрагентДубль для условия «Не в списке». Поскольку для платформы это разные ключи (Контрагент и КонтрагентДубль), механизм сохранения отработает корректно и восстановит оба значения.
Если нам нужно «честное» решение, позволяющее добавлять неограниченное количество отборов по одному полю без правки запроса, придется вмешаться в код сохранения настроек. Рассмотрим, как это реализовать программно.
Нам необходимо отказаться от стандартного метода ПостроительОтчета.ПолучитьНастройки() в пользу ручного обхода коллекции. Посмотрим на логику реализации:
Отбор.ХранилищеЗначения и сохраним в соответствующий регистр сведений.Добавить().Проанализируем пример кода для сохранения настроек:
// Создаем структуру для хранения
ТаблицаОтборов = Новый ТаблицаЗначений;
ТаблицаОтборов.Колонки.Добавить("Имя");
ТаблицаОтборов.Колонки.Добавить("Использование");
ТаблицаОтборов.Колонки.Добавить("ВидСравнения");
ТаблицаОтборов.Колонки.Добавить("Значение");
// Обходим отбор построителя
Для Каждого ЭлементОтбора Из ПостроительОтчета.Отбор Цикл
НоваяСтрока = ТаблицаОтборов.Добавить();
НоваяСтрока.Имя = ЭлементОтбора.Имя;
НоваяСтрока.Использование = ЭлементОтбора.Использование;
НоваяСтрока.ВидСравнения = ЭлементОтбора.ВидСравнения;
НоваяСтрока.Значение = ЭлементОтбора.Значение;
КонецЦикла;
// Сохраняем в хранилище (условно в переменную или регистр)
НастройкиДляСохранения = Новый ХранилищеЗначения(ТаблицаОтборов);
При восстановлении настроек важно использовать метод Добавить(), так как он позволяет создавать несколько элементов для одного пути к данным в объекте Отбор:
// Восстановление настроек
ТаблицаОтборов = НастройкиДляСохранения.Получить();
Если ТипЗнч(ТаблицаОтборов) = Тип("ТаблицаЗначений") Тогда
Для Каждого Стр Из ТаблицаОтборов Цикл
// Ищем или добавляем элемент отбора
Попытка
НовыйЭлемент = ПостроительОтчета.Отбор.Добавить(Стр.Имя);
НовыйЭлемент.Использование = Стр.Использование;
НовыйЭлемент.ВидСравнения = Стр.ВидСравнения;
НовыйЭлемент.Значение = Стр.Значение;
Исключение
// Обработка случая, если поле более недоступно
КонецПопытки;
КонецЦикла;
КонецЕсли;
Разберем еще один способ, который часто выручает в типовых конфигурациях. Если пользователю нужно сделать отбор по группе и одновременно исключить конкретные элементы, можно использовать связанные поля. Например, для Контрагента это может быть поле Договор.Владелец.
Рассмотрим ситуацию:
Контрагент В Группе "Розничные покупатели".ДоговорКонтрагента.Владелец НЕ В Списке "Исключения".Фактически, оба условия фильтруют список контрагентов. Но поскольку пути к данным разные (Контрагент и ДоговорКонтрагента.Владелец), Построитель отчета успешно сохранит оба условия в свои настройки. Этот метод не требует программирования и доступен опытному пользователю.
Как показывает практика обсуждений на форумах, самым стабильным и современным решением является полный перевод отчета на СКД. В СКД механизм сохранения настроек работает принципиально иначе:
Settings.Если ваш «Универсальный отчет» сильно кастомизирован, переход на СКД может занять время, но это избавит вас от множества подобных архитектурных ограничений в будущем.
Мы выяснили, что невозможность сохранения нескольких отборов по одному полю — это не ошибка конкретного отчета, а особенность работы ПостроителяОтчета. Если вы столкнулись с этой задачей, выберите наиболее подходящий вам путь:
Использование этих методов позволит сделать работу пользователей более комфортной, а отчеты — более гибкими и функциональными.