При разработке отчетов на Системе Компоновки Данных (СКД) мы часто сталкиваемся с ситуацией, когда настройки, заданные в пользовательском режиме, работают корректно, а попытка повторить те же действия программным кодом приводит к неожиданным результатам. В данной статье мы подробно разберем ситуацию, когда программный отбор по полю с составным типом данных (например, Номенклатура) не применяется или вызывает ошибку платформы.
Мы проанализируем комплексную проблему, включающую в себя особенности работы платформы 1С версии 8.3.23, механизмы кэширования внешних отчетов и тонкости программной работы с настройками компоновщика (быстро набросать программную реализацию поможет Консоль кода + ИИ-помощник).
В ходе обсуждения мы выделили следующие симптомы:
Давайте последовательно разберем причины и способы решения каждой из этих проблем.
Первое, на что стоит обратить внимание — это версия платформы. Как было выяснено в ходе анализа, релизы ветки 8.3.23 (например, 8.3.23.2040 и близкие к нему) имеют специфические проблемы с оптимизатором запросов СКД.
Симптомы: Отчет просто закрывается («схлопывается») или выдает сообщение «Произошла непредвиденная ошибка» без возможности перейти в конфигуратор для отладки.
Решение:
Если вы столкнулись с таким поведением, попробуйте следующие шаги:
Если вы разрабатываете внешний отчет, одной из самых неочевидных проблем является кэширование метаданных. Платформа 1С идентифицирует отчеты не по имени файла на диске (например, ОтчетПоПродажам_v2.erf), а по внутреннему свойству Имя объекта метаданных внутри этого файла.
Суть проблемы:
Представим ситуацию: вы открыли внешний отчет, платформа считала его схему компоновки и закешировала её. Затем вы внесли изменения в схему (добавили поля, изменили типы) или модуль объекта во внешнем файле и сохранили его. Если вы снова откроете этот файл через «Файл — Открыть», но при этом внутреннее имя отчета не изменилось, платформа может продолжить использовать старую, закешированную версию схемы или скомпилированного макета.
Это объясняет ситуацию, когда вы меняете код программного отбора, а результат выполнения отчета не меняется.
Как исправить:
Простого переименования файла недостаточно. Необходимо выполнить следующие действия в Конфигураторе:
МойОтчет_Версия2).Это заставит платформу воспринимать отчет как совершенно новый объект и перечитать все его настройки и схемы заново.
Автор вопроса указал, что поле Номенклатура имеет составной тип: СправочникСсылка.Номенклатура и Строка. Это классический источник проблем при программной установке отбора.
Когда вы добавляете отбор в пользовательском режиме, интерфейс платформы неявно определяет тип значения, которое вы выбираете. Если вы выбрали ссылку из справочника, отбор работает по ссылке. Если ввели строку — по строке.
При программной работе, если в схеме компоновки данных для поля явно не указан тип, СКД может попытаться привести типы самостоятельно, и не всегда удачно. Например, если в базе есть "битые" ссылки или строки, похожие на GUID.
Рекомендация:
В Конструкторе схемы компоновки данных на закладке Наборы данных для поля Номенклатура явно укажите Тип значения. Отметьте галочками и Справочник, и Строку (если она действительно нужна). А лучше — используйте приведение типов в самом тексте запроса с помощью функции ВЫРАЗИТЬ, чтобы на уровень СКД приходил однозначный тип данных.
// Подготовить запрос поможет bm Консоль запросов
ВЫБРАТЬ
ВЫРАЗИТЬ(Таблица.Номенклатура КАК Справочник.Номенклатура) КАК Номенклатура
ИЗ
...
Это самая распространенная причина, почему программный код «не работает». В СКД существует два уровня настроек:
Важно: При компоновке макета приоритет имеют данные из ПользовательскиеНастройки. Если элемент отбора (например, по Номенклатуре) выведен в пользовательские настройки (имеет установленный ИдентификаторПользовательскойНастройки), то значение, которое вы программно запишете в КомпоновщикНастроек.Настройки.Отбор, будет проигнорировано.
Платформа возьмет значение из пользовательских настроек (которое может быть пустым или содержать старое значение).
Рассмотрим правильный алгоритм. Нам нужно найти элемент отбора не только в основных настройках, но и проверить, связан ли он с пользовательскими настройками. Если связан — менять значение нужно там.
Вот пример универсальной процедуры, которую мы можем использовать для установки отбора:
// Процедура устанавливает отбор в отчете на СКД
// Параметры:
// Компоновщик - КомпоновщикНастроекКомпоновкиДанных - объект отчета
// ИмяПоля - Строка - Имя поля компоновки (как в схеме)
// Значение - Произвольный - Значение для отбора
// ВидСравнения - ВидСравненияКомпоновкиДанных
// Использование - Булево - Включить или выключить отбор
//
Процедура УстановитьОтборСКД(Компоновщик, ИмяПоля, Значение, ВидСравнения = Неопределено, Использование = Истина)
ПолеКомпоновки = Новый ПолеКомпоновкиДанных(ИмяПоля);
// 1. Ищем элемент в основных настройках
ЭлементОтбора = Неопределено;
Для Каждого Элемент Из Компоновщик.Настройки.Отбор.Элементы Цикл
Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных")
И Элемент.ЛевоеЗначение = ПолеКомпоновки Тогда
ЭлементОтбора = Элемент;
Прервать;
КонецЕсли;
КонецЦикла;
// Если не нашли - создаем новый
Если ЭлементОтбора = Неопределено Тогда
ЭлементОтбора = Компоновщик.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
ЭлементОтбора.ЛевоеЗначение = ПолеКомпоновки;
КонецЕсли;
// Устанавливаем параметры в основном элементе
ЭлементОтбора.Использование = Использование;
If ВидСравнения <> Неопределено Тогда
ЭлементОтбора.ВидСравнения = ВидСравнения;
КонецЕсли;
ЭлементОтбора.ПравоеЗначение = Значение;
// 2. ВАЖНО: Проверяем пользовательские настройки
// Если у элемента есть идентификатор, значит он выведен на форму
Если ЭлементОтбора.ИдентификаторПользовательскойНастройки <> Неопределено Тогда
// Ищем соответствующий элемент в коллекции ПользовательскиеНастройки
ЭлементПользовательский = Компоновщик.ПользовательскиеНастройки.Элементы.Найти(
ЭлементОтбора.ИдентификаторПользовательскойНастройки);
Если ЭлементПользовательский <> Неопределено Then
ЭлементПользовательский.Использование = Использование;
If ВидСравнения <> Неопределено Тогда
ЭлементПользовательский.ВидСравнения = ВидСравнения;
КонецЕсли;
ЭлементПользовательский.ПравоеЗначение = Значение;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Используя этот подход, мы гарантируем, что значение применится вне зависимости от того, выведен отбор на форму или нет.
Еще один нюанс, который часто упускают. Если вы программно передаете в отбор Группу номенклатуры (папку), но оставляете вид сравнения по умолчанию (Равно), отчет будет пустым. Потому что в регистрах обычно хранятся элементы, а не папки, и ни один элемент не равен папке.
В пользовательском режиме интерфейс часто сам подставляет вид сравнения В группе или ВИерархии, когда вы выбираете папку. Программно вы должны сделать это явно.
Пример вызова процедуры для группы товаров:
// Устанавливаем отбор по группе с учетом иерархии
УстановитьОтборСКД(Отчет.КомпоновщикНастроек, "Номенклатура",
ПапкаТоваров,
ВидСравненияКомпоновкиДанных.ВИерархии);
Для решения проблемы с несрабатывающим отбором выполните следующие шаги:
ПользовательскиеНастройки, если отбор выведен на форму.ВИерархии, для списков — ВСписке.