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

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

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

Выясняем причину: особенности типизации в 1С

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

Рассмотрим ситуацию, с которой столкнулся автор темы. При проведении документа «Табель учета рабочего времени» в самописной конфигурации возникает указанная ошибка. Код расчета выглядит следующим образом:

СуммаОплаты = Оклад / (26 * НайтиРабочееВремя(ДолжнСотр));

Давайте проанализируем функцию НайтиРабочееВремя, код которой предоставил автор:


&НаСервере 
Функция НайтиРабочееВремя(ДолжностьСотр) 
    Запрос = Новый Запрос; 
    Запрос.Текст = 
        "ВЫБРАТЬ 
        | РабочиеЧасыСрезПоследних.Время 
        |ИЗ 
        | РегистрСведений.РабочиеЧасы.СрезПоследних КАК РабочиеЧасыСрезПоследних 
        |ГДЕ 
        | РабочиеЧасыСрезПоследних.ДолжностьСотрудника = &ДолжнСотр"; 
    
    Запрос.УстановитьПараметр("ДолжнСотр", ДолжностьСотр); 
    РезультатЗапроса = Запрос.Выполнить; 
    ВыборкаДетальныеЗаписи = РезультатЗапроса.Выбрать; 
    
    Пока ВыборкаДетальныеЗаписи.Следующий() Цикл 
        Возврат ВыборкаДетальныеЗаписи.Время; 
    КонецЦикла; 
КонецФункции

На первый взгляд код выглядит стандартно, однако в нем кроется грубейшая алгоритмическая ошибка. Конструкция Возврат находится внутри цикла Пока ... КонецЦикла. Разберем по шагам, что произойдет, если в регистре сведений «Рабочие часы» не окажется записи для переданной должности:

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

Далее управление возвращается в модуль проведения документа, где выполняется расчет:

СуммаОплаты = Оклад / (26 * Неопределено);

Попытка умножить число 26 на значение Неопределено и вызывает критическую ошибку преобразования типов.

Проблема отсутствия данных и значений NULL

Помимо ситуации с пустой выборкой, существует еще один неочевидный подводный камень — значение NULL. Оно может возникнуть в запросах при использовании левых соединений (ЛЕВОЕ СОЕДИНЕНИЕ), когда для записи из левой таблицы нет соответствия в правой.

Если в будущем логика получения рабочего времени усложнится и поле запроса примет значение NULL, то встроенный язык 1С также выдаст ошибку преобразования типов при попытке использовать его в формуле.

Для предотвращения подобных ситуаций на уровне СУБД в языке запросов 1С всегда следует использовать функцию ЕСТЬNULL. Она позволяет принудительно заменить отсутствующее значение на дефолтное (например, на число 0) непосредственно на этапе выполнения запроса.

Опасность «быстрых» решений и деления на ноль

На форумах часто предлагают простые решения в стиле «заплатки». Например, просто дописать в конце функции возврат нуля:


Пока ВыборкаДетальныеЗаписи.Следующий() Цикл 
    Возврат ВыборкаДетальныеЗаписи.Время; 
КонецЦикла; 
Возврат 0; // Быстрое исправление

Давайте проанализируем последствия такого исправления. Если функция вернет 0, то в строке расчета произойдет следующее:

СуммаОплаты = Оклад / (26 * 0);

В результате мы получим классическую ошибку «Деление на 0». Программа все равно завершит работу аварийно.

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

Принципы оборонительного программирования

Профессиональный подход к разработке подразумевает использование принципов «оборонительного программирования» (Defensive Programming). Программа должна быть готова к некорректным или отсутствующим данным, уметь корректно обрабатывать их, сообщать пользователю о конкретной проблеме и безопасно останавливать проведение документа.

Давайте разберем по шагам, как правильно переписать данный функционал.

Шаг 1. Создаем функцию безопасного деления

В крупных конфигурациях (например, построенных на базе БСП) для исключения ошибок деления на ноль применяется специальная вспомогательная функция. Рекомендуем добавить ее в общий модуль вашей конфигурации:


Функция БезопасноеДеление(Знач Делимое, Знач Делитель, Знач ЗначениеПриНуле = 0) Экспорт
    Если Делитель = 0 Тогда
        Возврат ЗначениеПриНуле;
    КонецЕсли;
    Возврат Делимое / Делитель;
КонецФункции

Шаг 2. Оптимизируем и защищаем функцию получения данных

Перепишем функцию НайтиРабочееВремя. Мы сделаем три важных улучшения:

  1. Добавим предварительную проверку заполнения входного параметра.
  2. Перенесем условие фильтрации из предложения ГДЕ в параметры виртуальной таблицы СрезПоследних. Это значительно ускорит выполнение запроса в базе данных.
  3. Используем функцию ЕСТЬNULL для защиты от неопределенных значений на уровне базы данных.

Вот как выглядит идеальный код функции:


&НаСервере
Функция НайтиРабочееВремя(ДолжностьСотр)
    
    // Проверяем входящие данные на заполненность
    Если Не ЗначениеЗаполнено(ДолжностьСотр) Тогда
        Возврат 0;
    КонецЕсли;
    
    Запрос = Новый Запрос;
    Запрос.Текст = 
        "ВЫБРАТЬ
        |   ЕСТЬNULL(РабочиеЧасыСрезПоследних.Время, 0) КАК Время
        |ИЗ
        |   РегистрСведений.РабочиеЧасы.СрезПоследних( , ДолжностьСотрудника = &ДолжнСотр) КАК РабочиеЧасыСрезПоследних";
        
    Запрос.УстановитьПараметр("ДолжнСотр", ДолжностьСотр);
    РезультатЗапроса = Запрос.Выполнить();
    
    Выборка = РезультатЗапроса.Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Время;
    КонецЕсли;
    
    // Возвращаем дефолтное значение, если запись в регистре полностью отсутствует
    Возврат 0; 
КонецФункции

Шаг 3. Изменяем логику проведения и расчета

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


РабВремя = НайтиРабочееВремя(СтрокаТЧ.ДолжностьСотрудника);

Если РабВремя = 0 Тогда
    // Формируем понятное сообщение об ошибке для бухгалтера
    ТекстОшибки = СтрШаблон("Для сотрудника %1 не заполнены рабочие часы для должности %2!", 
                            СтрокаТЧ.Сотрудник, 
                            СтрокаТЧ.ДолжностьСотрудника);
    
    // Сообщаем пользователю (в управляемом интерфейсе используем СообщитьПользователю)
    ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстОшибки, , "Объект.ТабличнаяЧастьСотрудники", СтрокаТЧ.НомерСтроки);
    
    // Устанавливаем признак отказа проведения документа
    Отказ = Истина;
    Возврат;
КонецЕсли;

// Производим безопасный расчет
СуммаОплаты = БезопасноеДеление(Оклад, (26 * РабВремя));

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

Другие частые причины возникновения ошибки преобразования типов

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

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

← На главную