Как исправить ошибку «Преобразование значения к типу Число не может быть выполнено» при расчете итогов в 1С

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

В процессе разработки внешних печатных форм или сложных отчетов программисты 1С часто сталкиваются с досадной ошибкой: «Преобразование значения к типу Число не может быть выполнено». Эта проблема кажется тривиальной, но она способна полностью остановить работу системы в самый неподходящий момент. Обычно ошибка возникает в строках кода, где происходит умножение цены на количество или суммирование итогов. В рамках этой статьи мы подробно разберем причины возникновения данной ситуации, проанализируем поведение системы при работе с запросами и научимся правильно обрабатывать данные, чтобы исключить подобные сбои.

Выясняем причину: откуда берется NULL

Давайте проанализируем ситуацию. Чаще всего ошибка возникает при использовании ЛЕВОЕ СОЕДИНЕНИЕ (LEFT JOIN) в запросах. Рассмотрим типичный сценарий: мы берем данные из табличной части документа (например, «Товары») и пытаемся присоединить к ним данные из регистра (например, «Партии товаров на складах»).

Если для какой-то строки товара в регистре не окажется соответствующей записи, система подставит в поля присоединяемой таблицы специальное значение — NULL. Важно понимать, что NULL в 1С — это не число ноль и не пустая строка. Это тип данных, обозначающий отсутствие значения. Когда мы пытаемся выполнить арифметическую операцию (сложение, умножение) с числом и NULL в коде на языке 1С, платформа выдает ошибку преобразования типа, так как она не знает, как интерпретировать «пустоту» в математическом контексте.

Рассмотрим пример кода, который вызывает ошибку:


// В выборке ВыборкаГруппа.Количество1 может оказаться NULL
ОбластьДанные2.Параметры.Итог = Итог + ВыборкаГруппа.Количество1 * ВыборкаГруппа.Цена;

В этой строке, если хотя бы один из операндов является NULL, программа прекратит выполнение с системной ошибкой.

Способ 1: Использование функции ЕСТЬNULL в запросе

Самый правильный и надежный способ решения проблемы — обработка потенциальных пустых значений непосредственно в тексте запроса. Для этого предназначена функция ЕСТЬNULL(Поле, ЗначениеЗамены). Разберем, как изменить текст запроса, чтобы обезопасить себя.

Вместо простого выбора поля:

| ПартииТоваровНаСкладах.Количество КАК Количество1

Используем защищенную конструкцию:

| ЕСТЬNULL(ПартииТоваровНаСкладах.Количество, 0) КАК Количество1

Теперь, даже если записи в регистре не будет, запрос вернет число 0, с которым 1С сможет корректно производить вычисления. Давайте посмотрим на обновленный фрагмент запроса:


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

Способ 2: Перенос расчетов в текст запроса

Проанализируем архитектуру решения. Опытные разработчики знают «золотое правило»: максимум вычислений нужно проводить в запросе. Это не только избавляет от ошибок типов в коде, но и значительно ускоряет работу программы, так как расчеты выполняет SQL-сервер.

Вместо того чтобы считать сумму в цикле обхода выборки, создадим вычисляемое поле прямо в секции ВЫБРАТЬ. При этом крайне важно учитывать скидки и наценки, если они есть в документе. Разберем пример сложного расчета суммы со скидкой:


| ВЫБРАТЬ
|   (ОтчетОРозничныхПродажахТовары.Цена - 
|    ОтчетОРозничныхПродажахТовары.Цена * ОтчетОРозничныхПродажахТовары.ПроцентСкидкиНаценки / 100) 
|    * ЕСТЬNULL(ПартииТоваровНаСкладах.Количество, 0) КАК СуммаСУчетомСкидки

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

Способ 3: Правильная настройка итогов по группировкам

Еще одна неочевидная причина ошибки — попытка обращения к ресурсам на уровне группировок (итогов), которые не были включены в секцию ИТОГИ. Если вы обходите результат запроса с использованием ОбходРезультатаЗапроса.ПоГруппировкам, то на уровне родительских записей поля, не являющиеся итогами, будут содержать NULL.

Посмотрим, как правильно настроить итоги, чтобы данные были доступны в выборке по группировкам:


|ИТОГИ
| СУММА(Количество1),
| СУММА(СуммаСУчетомСкидки),
| МАКСИМУМ(Цена)
|ПО
| СтатусПартии,
| НоменклатурнаяГруппа

Важное замечание: Обратите внимание на использование функции МАКСИМУМ для поля Цена. Если мы используем СУММА(Цена) на уровне группы, система просто сложит все цены товаров внутри этой группы, что даст некорректный («космический») результат. Если цена внутри группы одинакова, используйте МАКСИМУМ или МИНИМУМ, чтобы получить значение, а не сумму.

Разбор по шагам: исправление кода выборки

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

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

// Группа
ВыборкаГруппа = ВыборкаСтатус.Выбрать(ОбходРезультатаЗапроса.ПоГруппировкам, "НоменклатурнаяГруппа");
Пока ВыборкаГруппа.Следующий() Цикл
    ОбластьДанные2 = Макет.ПолучитьОбласть("Группа");
    ОбластьДанные2.Параметры.НоменклатурнаяГруппа = ВыборкаГруппа.НоменклатурнаяГруппа;
    
    // Теперь здесь не будет ошибки, так как СуммаСУчетомСкидки рассчитана в запросе
    // и включена в ИТОГИ СУММА
    ОбластьДанные2.Параметры.СуммаИтог = ВыборкаГруппа.СуммаСУчетомСкидки;
    ОбластьДанные2.Параметры.КолИтог = ВыборкаГруппа.Количество1;
    
    ТабДокумент.Вывести(ОбластьДанные2);
    
    // Детальные записи
    ВыборкаДетали = ВыборкаГруппа.Выбрать();
    Пока ВыборкаДетали.Следующий() Цикл
        // Обработка строк...
    КонецЦикла;
КонецЦикла;

Защитное программирование в коде 1С

Рассмотрим ситуацию, когда вы по каким-то причинам не можете изменить запрос (например, он формируется динамически в типовой конфигурации). В этом случае используйте методы «защитного программирования» прямо в коде. Это поможет избежать аварийного завершения работы печатной формы.

Для проверки типа и замены NULL на 0 используйте сокращенный оператор условия:


ЦенаДляРасчета = ?(ВыборкаГруппа.Цена = NULL, 0, ВыборкаГруппа.Цена);
КоличествоДляРасчета = ?(ВыборкаГруппа.Количество1 = NULL, 0, ВыборкаГруппа.Количество1);

Область.Параметры.Итог = ЦенаДляРасчета * КоличествоДляРасчета;

Также можно воспользоваться функцией Число(), но помните, что она не всегда корректно преобразует NULL в 0 в старых версиях платформы, поэтому явная проверка через условие или использование функции ЗначениеЗаполнено() является более предпочтительным.

Резюме и рекомендации

Подводя итог, можно составить краткий чек-лист для предотвращения ошибки преобразования к типу Число:

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

← На главную