Как исправить ошибку «Значение не является значением объектного типа» в отчете СКД при обращении к реквизитам договора?

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

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

{ОбщийМодуль.ФункцииСКД.Модуль(59)}: Значение не является значением объектного типа (ДопустимоеЧислоДнейЗадолженности)

Разберем детально, почему возникает данная ситуация, как быстро локализовать проблему при отладке и какие архитектурные решения помогут избежать падения отчета.

Причины возникновения ошибки

Проанализируем строку кода, на которой происходит сбой:


Если ДоговорКонтрагента.ДопустимоеЧислоДнейЗадолженности = 0 тогда
    Возврат Дата;
КонецЕсли;

Ошибка означает, что переменная ДоговорКонтрагента не содержит ссылку на объект справочника ДоговорыКонтрагентов. В момент обращения через точку переменная имеет тип Неопределено или NULL. В контексте работы отчета СКД это происходит по следующим причинам:

  1. Расчет функции на уровне группировок (итоговых строк): Если функция вызывается в вычисляемых полях СКД, платформа по умолчанию вычисляет выражение для всех строк, включая вышестоящие группировки (по контрагенту, организации, общим итогам). На уровне группировки «Контрагент» поле ДоговорКонтрагента не определено и принимает значение Неопределено.
  2. Результат полного соединения в запросе: Если в схеме используется ПОЛНОЕ СОЕДИНЕНИЕ таблиц остатков и оборотов, для строк без соответствия поле договора возвращает значение NULL.
  3. Пустые ссылки или незаполненные измерения в регистрах: Наличие записей с незаполненным измерением ДоговорКонтрагента (например, после ручных корректировок записей регистров).

Методика отладки функций СКД

Выясним, как найти строку или группировку, на которой падает отчет, если стандартная пошаговая отладка сразу останавливается на строке с ошибкой:

  1. Включение остановки по ошибке: В конфигураторе откроем меню Отладка -> Остановка по ошибке и установим флаг остановки. При возникновении исключения платформа прервет выполнение, позволив изучить переменные в окне «Табло».
  2. Анализ стека вызовов: В момент падения откроем окно Стек вызовов. Это позволит увидеть, на каком этапе компоновки данных и при обработке какой структуры произошел сбой.
  3. Использование консоли СКД: Проверим промежуточные наборы данных через специализированную консоль компоновки данных. Это поможет увидеть сгенерированный итоговый запрос и реальные значения колонок.

Способ 1. Добавление проверки типа и заполненности данных в модуле

Самое быстрое и надежное исправление на уровне программного кода — защитить функцию от передачи некорректных типов данных. Добавим проверку перед обращением к реквизитам через точку:


Если Не ЗначениеЗаполнено(ДоговорКонтрагента) 
    Или ТипЗнч(ДоговорКонтрагента) <> Тип("СправочникСсылка.ДоговорыКонтрагентов") Тогда
    Возврат Дата;
КонецЕсли;

Если ДоговорКонтрагента.ДопустимоеЧислоДнейЗадолженности = 0 Тогда
    Возврат Дата;
КонецЕсли;

Такая проверка гарантирует, что при обработке итоговых строк группировок или пустых записей функция вернет значение по умолчанию (исходную дату) и не прервет формирование отчета.

Способ 2. Ограничение расчета ресурсов в схеме компоновки данных

Если функция должна рассчитываться строго для детальных записей или определенной группировки, настроим правила расчета на уровне СКД:

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

Способ 3. Перенос логики в запрос СКД (Оптимизация)

Посмотрим на исходный код функции, выполняющей запрос к производственному календарю:


Запрос = Новый Запрос();
Запрос.Текст = "ВЫБРАТЬ ПЕРВЫЕ " + ДоговорКонтрагента.ДопустимоеЧислоДнейЗадолженности + " 
              |    РегламентированныйПроизводственныйКалендарь.ДатаКалендаря
              |ПОМЕСТИТЬ Дни
              |ИЗ
              |    РегистрСведений.РегламентированныйПроизводственныйКалендарь КАК РегламентированныйПроизводственныйКалендарь
              |ГДЕ
              |    РегламентированныйПроизводственныйКалендарь.ВидДня В(&ВидДня)
              |    И РегламентированныйПроизводственныйКалендарь.ДатаКалендаря < НАЧАЛОПЕРИОДА(&Дата, ДЕНЬ)
              |УПОРЯДОЧИТЬ ПО
              |    РегламентированныйПроизводственныйКалендарь.ДатаКалендаря УБЫВ
              |;
              |ВЫБРАТЬ
              |    МИНИМУМ(Дни.ДатаКалендаря) КАК ДатаКалендаря
              |ИЗ
              |    Дни КАК Дни";

Выполнение отдельного запроса в цикле компоновщика для каждой строки отчета приводит к проблеме N+1 и существенно замедляет формирование отчета на больших базах. Рекомендуется перенести получение реквизита ДопустимоеЧислоДнейЗадолженности непосредственно в основной набор данных СКД:


ВЫБРАТЬ
    ЕСТЬNULL(ВзаиморасчетыОстатки.ДоговорКонтрагента, ВзаиморасчетыОбороты.ДоговорКонтрагента) КАК ДоговорКонтрагента,
    ЕСТЬNULL(ВзаиморасчетыОстатки.ДоговорКонтрагента.ДопустимоеЧислоДнейЗадолженности, 0) КАК ДопустимоеЧислоДней
ИЗ
    РегистрНакопления.ВзаиморасчетыСКонтрагентами.Остатки(&Дата, ) КАК ВзаиморасчетыОстатки
    ПОЛНОЕ СОЕДИНЕНИЕ РегистрНакопления.ВзаиморасчетыСКонтрагентами.Обороты(&Дата, , , ) КАК ВзаиморасчетыОбороты
    ПО ВзаиморасчетыОстатки.ДоговорКонтрагента = ВзаиморасчетыОбороты.ДоговорКонтрагента

Использование конструкции ЕСТЬNULL для поля соединения гарантирует корректное получение ссылки на договор независимо от того, в какой из соединяемых таблиц присутствуют данные.

← На главную