Как исправить ошибку сравнения примитивных типов при создании печатной формы в 1С

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

При разработке или модификации механизмов печати в 1С:Предприятие 8 программисты часто сталкиваются с ошибкой: «Операции сравнения на больше-меньше допустимы только для значений совпадающих примитивных типов (Булево, Число, Строка, Дата)». Эта ситуация возникает в тот момент, когда система пытается сравнить числовое значение с чем-то, что числом не является (например, с Неопределено или NULL). Разберем на конкретном примере, почему это происходит и как правильно написать отказоустойчивый код.

Проанализируем причину возникновения ошибки

Рассмотрим фрагмент кода из модуля менеджера документа, который стал причиной сбоя. В процессе формирования печатной формы выполняется расчет текущего долга клиента и сравнение его с установленным лимитом:


Если Выборка.ДолгДолларТекущий + ИтогСуммаДоллар - Выборка.ОплаченоСейчасДоллар > ЛимитДоллар И ЛимитДоллар > 0 Тогда
    // Действия по оформлению области
КонецЕсли;

В этой строке система выполняет сразу несколько арифметических операций и два сравнения. Если хотя бы одна из переменных — Выборка.ДолгДолларТекущий, Выборка.ОплаченоСейчасДоллар или ЛимитДоллар — примет значение Неопределено, 1С не сможет вычислить результат выражения и выбросит исключение.

Выясним, откуда берутся «неправильные» типы. Проанализируем два основных источника данных в данном примере:

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

Решение №1: Правильная инициализация переменных и работа со свойствами

Посмотрим на пример из сообщения форума. Автор пытался получить данные из структуры следующим образом:


Лиммит = ОбщегоНазначения.ПолучитьДанныеДолжниковЛимит(Выборка.Клиент, Выборка.Дата);
ЛимитСум = 0;
ЛимитДоллар = 0;
Лиммит.Свойство("ЛимитСум", ЛимитСум);
Лиммит.Свойство("ЛимитДоллар", ЛимитДоллар);

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


// Инициализируем переменные значениями по умолчанию
ЛимитСум = 0;
ЛимитДоллар = 0;

// Проверяем, что функция вернула именно структуру, а не Неопределено
Если ТипЗнч(Лиммит) = Тип("Структура") Тогда
    
    // Метод Свойство вернет Истина, если ключ найден, 
    // и запишет значение во второй параметр
    Если НЕ Лиммит.Свойство("ЛимитСум", ЛимитСум) Тогда
        ЛимитСум = 0;
    КонецЕсли;

    Если НЕ Лиммит.Свойство("ЛимитДоллар", ЛимитДоллар) Тогда
        ЛимитДоллар = 0;
    КонецЕсли;
    
КонецЕсли;

Важный нюанс: даже если метод Свойство находит ключ, значение в нем все равно может быть Неопределено, если оно было так записано при создании структуры. Чтобы на 100% обезопасить себя, используем функцию преобразования типов Число().

Решение №2: Обработка NULL в тексте запроса

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


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

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

Решение №3: Защитное программирование при сравнении

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

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


// Принудительно приводим к числу все переменные
ТекДолг = Число(Выборка.ДолгДолларТекущий);
ИтогДолг = Число(ИтогСуммаДоллар);
Оплачено = Число(Выборка.ОплаченоСейчасДоллар);
Лимит = Число(ЛимитДоллар);

// Теперь сравнение абсолютно безопасно
Если (ТекДолг + ИтогДолг - Оплачено) > Лимит И Лимит > 0 Тогда
    // Логика оформления печатной формы
КонецЕсли;

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

Пошаговая инструкция по исправлению модуля менеджера

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

  1. Проверьте запрос: Все числовые поля, которые могут быть не заполнены в базе, оберните в функцию ЕСТЬNULL(Поле, 0).
  2. Инициализируйте переменные: Перед тем как получать данные из внешних функций или структур, всегда задавайте значения по умолчанию (например, Лимит = 0;).
  3. Проверяйте тип возвращаемого значения: Если функция должна возвращать объект (Структуру, Таблицу значений), обязательно делайте проверку Если ТипЗнч(Результат) = Тип("НужныйТип").
  4. Используйте отладчик: Если ошибка все же возникла, установите точку останова на проблемной строке. Используйте окно «Табло» (Shift+F9), чтобы проверить тип каждой переменной в выражении. Ищите ту, тип которой Неопределено или NULL.

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

← На главную