Как исправить ошибку «Несоответствие типов» и правильно работать с внешними наборами данных в СКД

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

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

Анализ ошибки «Несоответствие типов» в параметрах виртуальных таблиц

Одной из самых частых причин падения кода на строке запрос.Выполнить() является передача некорректного значения в параметры виртуальных таблиц, таких как Остатки или ОстаткиИОбороты. В приведенном примере автор использует параметр &ПарамПамПам для определения периода получения остатков. Ошибка возникает в следующих случаях:

Проанализируем ситуацию с передачей момента времени. Для получения максимально точных остатков на конкретную секунду, включая документы этой секунды, рекомендуется использовать объект Граница. Разберем на примере, как правильно подготовить параметр:


Если ТипЗнч(ДатаОтчета) = Тип("Дата") Тогда
    // Если нужно включить документы, проведенные в ту же секунду
    ПараметрПериод = Новый Граница(ДатаОтчета, ВидГраницы.Включая);
Иначе
    ПараметрПериод = ДатаОтчета; // Ожидаем, что там уже может быть Граница или Дата
КонецЕсли;

Запрос.УстановитьПараметр("ПарамПамПам", ПараметрПериод);

Безопасное обращение к пользовательским настройкам

Автор кода использует обращение к элементам настроек по индексу: ПользовательскиеНастройки.Элементы[0]. Это критическая ошибка, которую нельзя допускать в промышленной разработке. Пользовательские настройки — это динамическая коллекция. Если пользователь в режиме «1С:Предприятие» добавит новый отбор, изменит порядок полей или группировок, индексы сместятся. Параметр, который был под номером 0, может стать номером 5, и ваш код начнет получать Склад вместо Даты, что и приведет к несоответствию типов.

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


Функция ПолучитьЗначениеНастройки(КомпоновщикНастроек, ИмяПараметра)
    Схема = ЭтотОбъект.СхемаКомпоновкиДанных;
    ПараметрСхемы = Схема.Параметры.Найти(ИмяПараметра);
    
    Если ПараметрСхемы = Неопределено Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    ИдНастройки = ПараметрСхемы.ИдентификаторПользовательскойНастройки;
    
    // Ищем в пользовательских настройках по идентификатору
    ЭлементНастройки = КомпоновщикНастроек.ПользовательскиеНастройки.Элементы.Найти(ИдНастройки);
    
    Если ЭлементНастройки <> Неопределено Тогда
        Возврат ЭлементНастройки.Значение;
    КонецЕсли;
    
    Возврат Неопределено;
КонецФункции

Используя такой подход, мы гарантируем, что получим именно Склад или Дату, независимо от того, как пользователь перемешал настройки в интерфейсе.

Корректное удаление строк из таблицы значений

Разберем алгоритмическую ошибку в цикле обработки таблицы значений. В исходном коде используется конструкция Для каждого Строка Из ТЗ Цикл ... ТЗ.Удалить(Строка). Это классическая «ловушка» для начинающих. При удалении элемента из коллекции, по которой идет итерация, индексы оставшихся элементов сдвигаются. В результате итератор пропускает следующую за удаленной строку, и часть данных остается необработанной.

Выясним причину, почему это опасно: если условию удаления соответствуют две идущие подряд строки, вторая строка никогда не будет проверена. Рассмотрим два правильных способа удаления:

Способ 1: Обход с конца (рекомендуется)


КолличествоСтрок = ТЗ.Количество();
Для Индекс = 1 По КолличествоСтрок Цикл
    ТекущийИндекс = КолличествоСтрок - Индекс;
    Строка = ТЗ[ТекущийИндекс];
    
    Если Процент > ПроцентСрокаГодности Тогда
        ТЗ.Удалить(ТекущийИндекс);
    КонецЕсли;
КонецЦикла;

Способ 2: Использование массива для удаления


СтрокиНаУдаление = Новый Массив;
Для Каждого Строка Из ТЗ Цикл
    Если Процент > ПроцентСрокаГодности Тогда
        СтрокиНаУдаление.Добавить(Строка);
    КонецЕсли;
КонецЦикла;

Для Каждого Строка Из СтрокиНаУдаление Цикл
    ТЗ.Удалить(Строка);
КонецЦикла;

Работа с внешними наборами данных: регистр и имена

При передаче таблицы значений в ПроцессорКомпоновкиДанных через структуру ВнешниеНаборыДанных, крайне важно следить за именованием полей. Рассмотрим основные правила:

  1. Соответствие имен: Имена колонок в ТаблицеЗначений должны в точности (включая регистр букв в некоторых версиях платформы) совпадать с именами полей, описанными в Наборе данных - объект внутри схемы СКД.
  2. Типизация колонок: Желательно, чтобы колонки ТЗ были типизированы (созданы с указанием ОписаниеТипов). Если в колонке будет перемешка типов, СКД может выдать ошибку при попытке применения функций агрегации (СУММА) или группировки.
  3. Имя набора: Ключ в структуре ВнешниеНаборыДанных.Вставить("ИмяНабора", ТЗ) должен совпадать с именем объекта, указанным в поле «Имя объекта» в настройках набора данных СКД.

Использование вычисляемых полей вместо ручного кода

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

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

Например, выражение для вычисляемого поля может выглядеть так (если мы все же парсим строку): ДОБАВИТЬКДАТЕ(ДАТАВРЕМЯ(2000, 1, 1), "ГОД", ЧИСЛО(ПОДСТРОКА(СерияНомер, 3, 2))). Хотя лучше использовать готовые реквизиты объекта.

Нюансы расшифровки и вывода результата

Если вы программно перехватываете компоновку в ПриКомпоновкеРезультата, не забывайте про ДанныеРасшифровки. Если в процедуре инициализируется ПроцессорКомпоновкиДанных, объект ДанныеРасшифровки должен быть передан во все звенья цепи. Если вы просто выводите результат в табличный документ, убедитесь, что вы не потеряли связь с формой отчета, иначе пользователь не сможет «провалиться» в документ или справочник из отчета.

Посмотрим на правильную финальную последовательность действий:


// 1. Создаем макет компоновки
КомпоновщикМакета = Новый КомпоновщикМакетаКомпоновкиДанных;
МакетКомпоновки = КомпоновщикМакета.Выполнить(СхемаКомпоновкиДанных, КомпоновщикНастроек.ПолучитьНастройки(), ДанныеРасшифровки);

// 2. Инициализируем процессор компоновки
ПроцессорКомпоновки = Новый ПроцессорКомпоновкиДанных;
ПроцессорКомпоновки.Инициализировать(МакетКомпоновки, ВнешниеНаборыДанных, ДанныеРасшифровки, Истина);

// 3. Выводим результат
ПроцессорВывода = Новый ПроцессорВыводаРезультатаКомпоновкиДанныхВТабличныйДокумент;
ПроцессорВывода.УстановитьДокумент(ДокументРезультат);
ПроцессорВывода.Вывести(ПроцессорКомпоновки);

Обратите внимание на четвертый параметр в методе Инициализировать — Истина (ВозможностьИспользованияВнешнихФункций). Он необходим, если в вашей схеме используются функции общих модулей или сложные выражения, требующие доступа к контексту сервера.

Соблюдение этих правил превращает «трэш от 1С» в стабильно работающий и легко поддерживаемый инструмент управленческой отчетности.

← На главную