Почему не срабатывает программный отбор в отчете на СКД или происходит аварийное завершение работы?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке отчетов на Системе Компоновки Данных (СКД) мы часто сталкиваемся с ситуацией, когда настройки, заданные в пользовательском режиме, работают корректно, а попытка повторить те же действия программным кодом приводит к неожиданным результатам. В данной статье мы подробно разберем ситуацию, когда программный отбор по полю с составным типом данных (например, Номенклатура) не применяется или вызывает ошибку платформы.

Мы проанализируем комплексную проблему, включающую в себя особенности работы платформы 1С версии 8.3.23, механизмы кэширования внешних отчетов и тонкости программной работы с настройками компоновщика (быстро набросать программную реализацию поможет Консоль кода + ИИ-помощник).

Основные симптомы проблемы

В ходе обсуждения мы выделили следующие симптомы:

  1. При добавлении отбора в режиме «1С:Предприятие» отчет формируется корректно.
  2. При попытке добавить тот же отбор программно (через модуль объекта или формы) фильтрация не происходит — в отчет попадают все данные (иногда требуется и обратное — скрыть пустые столбцы, когда данных нет).
  3. В некоторых случаях (особенно на платформе версии 8.3.23.xxxx) формирование отчета с программным отбором приводит к аварийному завершению работы программы («вылет» с дампом или сообщение о непредвиденной ошибке).

Давайте последовательно разберем причины и способы решения каждой из этих проблем.

1. Аварийное завершение работы на платформе 8.3.23

Первое, на что стоит обратить внимание — это версия платформы. Как было выяснено в ходе анализа, релизы ветки 8.3.23 (например, 8.3.23.2040 и близкие к нему) имеют специфические проблемы с оптимизатором запросов СКД.

Симптомы: Отчет просто закрывается («схлопывается») или выдает сообщение «Произошла непредвиденная ошибка» без возможности перейти в конфигуратор для отладки.

Решение:

Если вы столкнулись с таким поведением, попробуйте следующие шаги:

  1. Обновление платформы. Проверьте актуальность вашего релиза. Часто ошибки, связанные с утечкой памяти или некорректной работой оптимизатора, исправляются в следующих сборках.
  2. Отключение оптимизации в схеме. В некоторых случаях помогает отключение автоматической оптимизации полей в макете СКД. Хотя это временная мера (проверить результат можно через Управляемую консоль отчетов), она может стабилизировать работу до обновления платформы.

2. Проблема кэширования внешних отчетов

Если вы разрабатываете внешний отчет, одной из самых неочевидных проблем является кэширование метаданных. Платформа 1С идентифицирует отчеты не по имени файла на диске (например, ОтчетПоПродажам_v2.erf), а по внутреннему свойству Имя объекта метаданных внутри этого файла.

Суть проблемы:

Представим ситуацию: вы открыли внешний отчет, платформа считала его схему компоновки и закешировала её. Затем вы внесли изменения в схему (добавили поля, изменили типы) или модуль объекта во внешнем файле и сохранили его. Если вы снова откроете этот файл через «Файл — Открыть», но при этом внутреннее имя отчета не изменилось, платформа может продолжить использовать старую, закешированную версию схемы или скомпилированного макета.

Это объясняет ситуацию, когда вы меняете код программного отбора, а результат выполнения отчета не меняется.

Как исправить:

Простого переименования файла недостаточно. Необходимо выполнить следующие действия в Конфигураторе:

  1. Откройте внешний отчет.
  2. Встаньте на корень дерева метаданных отчета (самая верхняя строка).
  3. В палитре свойств найдите свойство Имя.
  4. Измените его на уникальное (например, добавьте цифру версии или суффикс: МойОтчет_Версия2).
  5. Сохраните отчет и откройте его заново в режиме Предприятия.

Это заставит платформу воспринимать отчет как совершенно новый объект и перечитать все его настройки и схемы заново.

3. Составные типы данных в отборах СКД

Автор вопроса указал, что поле Номенклатура имеет составной тип: СправочникСсылка.Номенклатура и Строка. Это классический источник проблем при программной установке отбора.

Когда вы добавляете отбор в пользовательском режиме, интерфейс платформы неявно определяет тип значения, которое вы выбираете. Если вы выбрали ссылку из справочника, отбор работает по ссылке. Если ввели строку — по строке.

При программной работе, если в схеме компоновки данных для поля явно не указан тип, СКД может попытаться привести типы самостоятельно, и не всегда удачно. Например, если в базе есть "битые" ссылки или строки, похожие на GUID.

Рекомендация:

В Конструкторе схемы компоновки данных на закладке Наборы данных для поля Номенклатура явно укажите Тип значения. Отметьте галочками и Справочник, и Строку (если она действительно нужна). А лучше — используйте приведение типов в самом тексте запроса с помощью функции ВЫРАЗИТЬ, чтобы на уровень СКД приходил однозначный тип данных.


// Подготовить запрос поможет bm Консоль запросов
ВЫБРАТЬ
    ВЫРАЗИТЬ(Таблица.Номенклатура КАК Справочник.Номенклатура) КАК Номенклатура
ИЗ
    ...

4. Программная установка: Настройки vs ПользовательскиеНастройки

Это самая распространенная причина, почему программный код «не работает». В СКД существует два уровня настроек:

  1. КомпоновщикНастроек.Настройки — это "дефолтные" или основные настройки схемы.
  2. КомпоновщикНастроек.ПользовательскиеНастройки — это те настройки, которые пользователь видит на форме и может менять.

Важно: При компоновке макета приоритет имеют данные из ПользовательскиеНастройки. Если элемент отбора (например, по Номенклатуре) выведен в пользовательские настройки (имеет установленный ИдентификаторПользовательскойНастройки), то значение, которое вы программно запишете в КомпоновщикНастроек.Настройки.Отбор, будет проигнорировано.

Платформа возьмет значение из пользовательских настроек (которое может быть пустым или содержать старое значение).

Как правильно устанавливать отбор программно?

Рассмотрим правильный алгоритм. Нам нужно найти элемент отбора не только в основных настройках, но и проверить, связан ли он с пользовательскими настройками. Если связан — менять значение нужно там.

Вот пример универсальной процедуры, которую мы можем использовать для установки отбора:


// Процедура устанавливает отбор в отчете на СКД
// Параметры:
//  Компоновщик - КомпоновщикНастроекКомпоновкиДанных - объект отчета
//  ИмяПоля     - Строка - Имя поля компоновки (как в схеме)
//  Значение    - Произвольный - Значение для отбора
//  ВидСравнения - ВидСравненияКомпоновкиДанных
//  Использование - Булево - Включить или выключить отбор
//
Процедура УстановитьОтборСКД(Компоновщик, ИмяПоля, Значение, ВидСравнения = Неопределено, Использование = Истина)
    
    ПолеКомпоновки = Новый ПолеКомпоновкиДанных(ИмяПоля);
    
    // 1. Ищем элемент в основных настройках
    ЭлементОтбора = Неопределено;
    Для Каждого Элемент Из Компоновщик.Настройки.Отбор.Элементы Цикл
        Если ТипЗнч(Элемент) = Тип("ЭлементОтбораКомпоновкиДанных") 
           И Элемент.ЛевоеЗначение = ПолеКомпоновки Тогда
            ЭлементОтбора = Элемент;
            Прервать;
        КонецЕсли;
    КонецЦикла;
    
    // Если не нашли - создаем новый
    Если ЭлементОтбора = Неопределено Тогда
        ЭлементОтбора = Компоновщик.Настройки.Отбор.Элементы.Добавить(Тип("ЭлементОтбораКомпоновкиДанных"));
        ЭлементОтбора.ЛевоеЗначение = ПолеКомпоновки;
    КонецЕсли;
    
    // Устанавливаем параметры в основном элементе
    ЭлементОтбора.Использование = Использование;
    If ВидСравнения <> Неопределено Тогда
        ЭлементОтбора.ВидСравнения = ВидСравнения;
    КонецЕсли;
    ЭлементОтбора.ПравоеЗначение = Значение;
    
    // 2. ВАЖНО: Проверяем пользовательские настройки
    // Если у элемента есть идентификатор, значит он выведен на форму
    Если ЭлементОтбора.ИдентификаторПользовательскойНастройки <> Неопределено Тогда
        
        // Ищем соответствующий элемент в коллекции ПользовательскиеНастройки
        ЭлементПользовательский = Компоновщик.ПользовательскиеНастройки.Элементы.Найти(
            ЭлементОтбора.ИдентификаторПользовательскойНастройки);
            
        Если ЭлементПользовательский <> Неопределено Then
            ЭлементПользовательский.Использование = Использование;
            If ВидСравнения <> Неопределено Тогда
                ЭлементПользовательский.ВидСравнения = ВидСравнения;
            КонецЕсли;
            ЭлементПользовательский.ПравоеЗначение = Значение;
        КонецЕсли;
        
    КонецЕсли;
    
КонецПроцедуры

Используя этот подход, мы гарантируем, что значение применится вне зависимости от того, выведен отбор на форму или нет.

5. Вид сравнения для иерархических справочников

Еще один нюанс, который часто упускают. Если вы программно передаете в отбор Группу номенклатуры (папку), но оставляете вид сравнения по умолчанию (Равно), отчет будет пустым. Потому что в регистрах обычно хранятся элементы, а не папки, и ни один элемент не равен папке.

В пользовательском режиме интерфейс часто сам подставляет вид сравнения В группе или ВИерархии, когда вы выбираете папку. Программно вы должны сделать это явно.

Пример вызова процедуры для группы товаров:


// Устанавливаем отбор по группе с учетом иерархии
УстановитьОтборСКД(Отчет.КомпоновщикНастроек, "Номенклатура", 
    ПапкаТоваров, 
    ВидСравненияКомпоновкиДанных.ВИерархии);

Подведем итог

Для решения проблемы с несрабатывающим отбором выполните следующие шаги:

  1. Измените внутреннее Имя отчета в метаданных, чтобы сбросить кэш (для внешних отчетов).
  2. Убедитесь, что ваш код изменяет значение в ПользовательскиеНастройки, если отбор выведен на форму.
  3. Проверьте ВидСравнения. Для групп используйте ВИерархии, для списков — ВСписке.
  4. Если платформа "вылетает", попробуйте обновить релиз или временно отключить оптимизацию в схеме СКД.
  5. Явно типизируйте поля в схеме компоновки данных.
← На главную