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