Значение не превращается в Число: разбор ошибки типов в коде и запросах 1С
Содержание
Ошибка несовместимых типов при попытке выразить значение как число в 1С означает одно: платформа получила не число и не смогла его однозначно превратить в число. В коде это решается проверкой ТипЗнч() и конвертацией через Число() внутри Попытка, в запросе — конструкцией ВЫРАЗИТЬ(Поле КАК ЧИСЛО(15, 2)) с предварительным отбором по ТИПЗНАЧЕНИЯ(). Строку в число язык запросов не переводит вообще — это делают уже в коде после выполнения запроса.
Сбой встречается в двух местах с разными текстами. Во встроенном языке при неудачной конвертации строки платформа пишет Преобразование значения к типу Число не может быть выполнено. В тексте запроса конструктор или исполнение запроса останавливается с сообщением о несовместимых типах и координатами ошибки вида {(12, 5)} — номер строки и позиция символа в тексте запроса.
Откуда берётся конфликт типов
Встроенный язык 1С слабо типизирован: переменная может хранить что угодно, тип проверяется только в момент операции. Поэтому синтаксический контроль модуля такую ошибку не ловит, она всплывает у пользователя на конкретных данных.
Типичные источники «не того» значения:
- 📄 Строка из внешнего файла. Excel, CSV, XML, ответ веб-сервиса — всё приходит текстом, часто с пробелами, запятой вместо точки и знаком валюты.
- ❓ Неопределено. Пустой реквизит составного типа, не найденный ключ в
Соответствии, результат функции, которая ничего не вернула. - 🔗 Ссылка на справочник или документ. Разработчик ждёт сумму, а в поле лежит элемент справочника.
- ✅ Булево. Флажок формы, который пытаются сложить или подставить в числовой реквизит.
- 🧩 Составной тип реквизита. Поле «Значение» дополнительного реквизита или субконто может хранить и число, и строку, и ссылку одновременно в разных записях.
- 🕳 NULL в запросе. Появляется после левого соединения, когда в правой таблице нет записи.
Ошибка почти всегда воспроизводится не на всех строках, а на одной-двух. Ищите конкретную запись, на которой ломается цикл: это быстрее, чем перечитывать код.
Как работает функция Число() и где она ломается
Число(Значение) — основной способ превратить строку или Булево в число во встроенном языке. Функция строгая: она не угадывает формат, а требует корректную запись.
| Исходное значение | Результат Число() |
|---|---|
"125" | 125 |
"125.50" | 125.5 |
Истина / Ложь | 1 / 0 |
"" (пустая строка) | исключение |
"1 250,00" с пробелом-разделителем | исключение |
"125 руб." | исключение |
Неопределено | исключение |
| ссылка на справочник | исключение |
Разделитель дробной части в исходном тексте приводите к точке заранее, а все пробелы, включая неразрывные, удаляйте. Тогда поведение будет одинаковым на любом компьютере.
Ловушка «число → строка → число»
Классическая ситуация: число превратили в строку через Строка(Сумма) или неявной склейкой, сохранили, а потом пытаются вернуть обратно. Строка(1250) возвращает "1 250" с неразрывным пробелом между разрядами, и Число() на этом падает.
Правильно форматировать без группировки разрядов и с точкой:
ТекстСуммы = Формат(Сумма, "ЧГ=0; ЧРД=.");
ЧГ=0 отключает группировку разрядов, ЧРД=. задаёт точку как разделитель дробной части. Такую строку Число() прочитает без ошибок.
Защищённая конвертация через Попытка
Если источник данных внешний, без перехвата исключения не обойтись. Пример функции, которую удобно положить в общий модуль:
Функция ПривестиКЧислу(Знач Значение, ЗначениеПоУмолчанию = 0) Экспорт
Если ТипЗнч(Значение) = Тип("Число") Тогда
Возврат Значение;
КонецЕсли;
Если ТипЗнч(Значение) = Тип("Булево") Тогда
Возврат ?(Значение, 1, 0);
КонецЕсли;
Если ТипЗнч(Значение) <> Тип("Строка") Тогда
Возврат ЗначениеПоУмолчанию;
КонецЕсли;
Текст = СокрЛП(Значение);
Текст = СтрЗаменить(Текст, Символы.НПП, "");
Текст = СтрЗаменить(Текст, " ", "");
Текст = СтрЗаменить(Текст, ",", ".");
Если Текст = "" Тогда
Возврат ЗначениеПоУмолчанию;
КонецЕсли;
Попытка
Возврат Число(Текст);
Исключение
Возврат ЗначениеПоУмолчанию;
КонецПопытки;
КонецФункции
Функция сначала отсекает уже готовые числа и Булево, затем чистит строку и только потом вызывает Число(). Неопределено, ссылки и прочие типы получают значение по умолчанию.
Возврат нуля по умолчанию удобен, но опасен при загрузке денег и количеств. Строка «12,5 кг», молча превращённая в 0, — это недостача в учёте, а не исправленная ошибка. При загрузке документов лучше передавать в ЗначениеПоУмолчанию значение Неопределено и выводить пользователю список строк, которые не распознались.
ОписаниеТипов как альтернатива
Второй способ — объект ОписаниеТипов:
ОписаниеЧисла = Новый ОписаниеТипов("Число");
Сумма = ОписаниеЧисла.ПривестиЗначение(ТекстИзФайла);
ПривестиЗначение() не вызывает исключений: если значение не приводится, вы получите значение по умолчанию для типа, то есть 0. Минус тот же, что у функции выше: «ноль из файла» и «мусор из файла» становятся неотличимы. Для проверок качества данных используйте явный Попытка ... Исключение.
Для строк в формате XML (точка, без разделителей разрядов) есть ещё XMLЗначение(Тип("Число"), Текст) — удобно при разборе файлов обмена, где формат чисел строго задан.
Пустые строки, Неопределено и NULL до конвертации
Пустые значения — самый частый виновник. Проверяйте их до вызова Число(), а не после падения.
Если Значение = Неопределено Или Значение = Null Тогда
Количество = 0;
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") И ПустаяСтрока(Значение) Тогда
Количество = 0;
Иначе
Количество = ПривестиКЧислу(Значение);
КонецЕсли;
ПустаяСтрока() считает пустой и строку из одних пробелов, в отличие от сравнения с "". Для универсальной проверки любого типа подходит ЗначениеЗаполнено(): оно вернёт Ложь и для 0, и для пустой строки, и для пустой ссылки, и для Неопределено. Учтите, что 0 тоже окажется «незаполненным» — для числовых полей это не всегда то, что нужно.
Значение Null во встроенном языке появляется из результата запроса. Его надёжнее убирать в самом запросе функцией ЕСТЬNULL(Поле, 0), чем проверять в каждой строке кода.
Булево, строки и ссылки: как получить из них число
Булево
Число(Истина) даёт 1, Число(Ложь) — 0. Для читаемости часто пишут тернарный оператор: ?(Флаг, 1, 0). Пригодится, например, чтобы посчитать количество отмеченных строк табличной части сложением.
Строки со служебными символами
Перед конвертацией текст из внешнего источника нужно очистить:
- Убрать пробелы по краям —
СокрЛП(). - Удалить пробелы-разделители разрядов, обычные и неразрывные (
Символы.НПП). - Заменить запятую на точку.
- Отрезать единицы измерения и знаки валюты: «руб.», «шт», «%», «₽».
- Обработать отрицательные числа в скобках — бухгалтерские выгрузки иногда пишут
(1500)вместо-1500.
Для пятого пункта достаточно проверить первый и последний символ через Лев() и Прав(), убрать скобки и умножить результат на −1.
Ссылки на справочники и документы
Ссылку нельзя превратить в число — у неё нет числового смысла. Нужно взять конкретный реквизит:
- 🔢 Сумма или количество — обратитесь к реквизиту объекта:
Документ.СуммаДокумента. - 🏷 Числовой код справочника — если тип кода «Число», он уже числовой; если «Строка», нужно
Число(Ссылка.Код)с защитой, ведь коды бывают с префиксом. - 📊 Значение из регистра — получите его запросом по ссылке, а не через ссылку напрямую.
Если в поле внезапно оказалась ссылка, а ждали число, проверьте тип реквизита в конфигураторе. Часто тип составной, и кто-то из пользователей выбрал в поле элемент справочника вместо ввода числа.
Приведение к числу в запросах 1С через ВЫРАЗИТЬ
Язык запросов строже встроенного языка. Здесь ВЫРАЗИТЬ не конвертирует значения между типами, а лишь уточняет тип: выделяет число из составного поля или меняет разрядность числа.
Рабочий сценарий — поле составного типа, где часть записей содержит числа:
ВЫБРАТЬ
ДопСведения.Объект КАК Объект,
ВЫРАЗИТЬ(ДопСведения.Значение КАК ЧИСЛО(15, 2)) КАК Сумма
ИЗ
РегистрСведений.ДополнительныеСведения КАК ДопСведения
ГДЕ
ТИПЗНАЧЕНИЯ(ДопСведения.Значение) = ТИП(ЧИСЛО)
Отбор по ТИПЗНАЧЕНИЯ() оставляет только числовые записи. Без него ВЫРАЗИТЬ вернёт NULL для строк и ссылок, и арифметика по полю даст NULL вместо суммы.
Чего ВЫРАЗИТЬ сделать не может
| Задача | Можно в запросе? | Как решить |
|---|---|---|
| Выделить число из составного поля | Да | ВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) + отбор по типу |
| Изменить разрядность числа | Да | ВЫРАЗИТЬ(Поле КАК ЧИСЛО(10, 0)) |
| Заменить NULL нулём | Да | ЕСТЬNULL(Поле, 0) |
Превратить строку "125" в число | Нет | Выгрузить результат и конвертировать в коде |
| Сравнить строковое поле с числом | Нет | Передать параметр того же типа, что и поле |
Вторая частая причина ошибки в запросе — сравнение полей разных типов. Например, код справочника строковый, а в условие подставлено число: ГДЕ Номенклатура.Код = 15. Решение — передавать параметр того же типа, что и поле, а для строкового кода учитывать ведущие нули.
Что делать, если число лежит в строковом поле
Если цифры хранятся в строковом реквизите, в запросе их не посчитать. Варианты по возрастанию трудоёмкости:
- Выполнить запрос, выгрузить в таблицу значений, добавить числовую колонку и заполнить её через
ПривестиКЧислу()в цикле. - Для коротких кодов фиксированной длины разбирать строку посимвольно через
ПОДСТРОКАиВЫБОР— громоздко и годится только для 2-3 разрядов. - Исправить модель данных: добавить числовой реквизит и заполнить его обработкой один раз.
Третий вариант правильный, если поле участвует в отчётах постоянно.
Как найти, где и почему тип оказался не тем
Диагностика занимает меньше времени, чем кажется, если сразу поймать момент ошибки.
- В конфигураторе запустите отладку и включите
Отладка → Остановка по ошибке. Выполнение остановится ровно на строке, где возникло исключение. - Выделите переменную и откройте окно вычисления выражения (
Shift+F9). В нём видно и значение, и его тип. - Вычислите прямо там
ТипЗнч(Значение)— так вы точно узнаете, пришла ли строка, Неопределено или ссылка. - Для строки проверьте длину через
СтрДлина(). Если длина больше видимых символов, внутри есть непечатаемые знаки: неразрывный пробел, табуляция, перевод строки. - Для запросов скопируйте текст в консоль запросов и выполните по частям. Координаты из сообщения
{(строка, позиция)}укажут на проблемное выражение.
Без отладчика помогает временная запись в журнал регистрации:
ЗаписьЖурналаРегистрации("Загрузка.Отладка", УровеньЖурналаРегистрации.Информация,
, , "Строка " + Формат(НомерСтроки, "ЧГ=0") + ": тип " + Строка(ТипЗнч(Значение)));
Как не допускать ошибок типов при разработке
Большинство таких сбоев закладывается ещё при проектировании. Несколько правил, которые снимают проблему заранее:
- 🧱 Конкретные типы реквизитов. Составной тип делайте только там, где он действительно нужен. Поле «Сумма» должно быть
Число, а неСтрока«для гибкости». - 🚪 Конвертация на входе. Приводите данные к нужному типу один раз — в момент чтения файла или ответа сервиса, а дальше работайте только с чистыми значениями.
- 🧰 Одна функция на всю конфигурацию. Держите
ПривестиКЧислу()в общем модуле, а не пишитеЧисло()с разной очисткой в десяти местах. - 🏷 Типизированные параметры запроса. Передавайте в
УстановитьПараметр()значение того же типа, что и поле, а не строку «на всякий случай». - 📝 Комментарии к параметрам. Опишите в комментарии функции ожидаемые типы параметров — EDT и конфигуратор показывают их в подсказке при вызове.
- 🧪 Проверка на грязных данных. Тестируйте загрузку на файле с пустыми ячейками, запятыми, пробелами и текстом в числовой колонке, а не на идеальном примере.
Исправления в типовой конфигурации на поддержке вносите через расширение, а не прямой правкой модулей — иначе каждое обновление превратится в ручное сравнение и объединение. Перед любыми изменениями сделайте резервную копию базы.