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