Значение не превращается в Число: разбор ошибки типов в коде и запросах 1С

· 10 мин

Содержание

Ошибка несовместимых типов при попытке выразить значение как число в 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). Пригодится, например, чтобы посчитать количество отмеченных строк табличной части сложением.

Строки со служебными символами

Перед конвертацией текст из внешнего источника нужно очистить:

  1. Убрать пробелы по краям — СокрЛП().
  2. Удалить пробелы-разделители разрядов, обычные и неразрывные (Символы.НПП).
  3. Заменить запятую на точку.
  4. Отрезать единицы измерения и знаки валюты: «руб.», «шт», «%», «₽».
  5. Обработать отрицательные числа в скобках — бухгалтерские выгрузки иногда пишут (1500) вместо -1500.

Для пятого пункта достаточно проверить первый и последний символ через Лев() и Прав(), убрать скобки и умножить результат на −1.

Ссылки на справочники и документы

Ссылку нельзя превратить в число — у неё нет числового смысла. Нужно взять конкретный реквизит:

  • 🔢 Сумма или количество — обратитесь к реквизиту объекта: Документ.СуммаДокумента.
  • 🏷 Числовой код справочника — если тип кода «Число», он уже числовой; если «Строка», нужно Число(Ссылка.Код) с защитой, ведь коды бывают с префиксом.
  • 📊 Значение из регистра — получите его запросом по ссылке, а не через ссылку напрямую.

Если в поле внезапно оказалась ссылка, а ждали число, проверьте тип реквизита в конфигураторе. Часто тип составной, и кто-то из пользователей выбрал в поле элемент справочника вместо ввода числа.

Приведение к числу в запросах 1С через ВЫРАЗИТЬ

Язык запросов строже встроенного языка. Здесь ВЫРАЗИТЬ не конвертирует значения между типами, а лишь уточняет тип: выделяет число из составного поля или меняет разрядность числа.

Рабочий сценарий — поле составного типа, где часть записей содержит числа:

ВЫБРАТЬ
	ДопСведения.Объект КАК Объект,
	ВЫРАЗИТЬ(ДопСведения.Значение КАК ЧИСЛО(15, 2)) КАК Сумма
ИЗ
	РегистрСведений.ДополнительныеСведения КАК ДопСведения
ГДЕ
	ТИПЗНАЧЕНИЯ(ДопСведения.Значение) = ТИП(ЧИСЛО)

Отбор по ТИПЗНАЧЕНИЯ() оставляет только числовые записи. Без него ВЫРАЗИТЬ вернёт NULL для строк и ссылок, и арифметика по полю даст NULL вместо суммы.

Чего ВЫРАЗИТЬ сделать не может

ЗадачаМожно в запросе?Как решить
Выделить число из составного поляДаВЫРАЗИТЬ(... КАК ЧИСЛО(15, 2)) + отбор по типу
Изменить разрядность числаДаВЫРАЗИТЬ(Поле КАК ЧИСЛО(10, 0))
Заменить NULL нулёмДаЕСТЬNULL(Поле, 0)
Превратить строку "125" в числоНетВыгрузить результат и конвертировать в коде
Сравнить строковое поле с числомНетПередать параметр того же типа, что и поле

Вторая частая причина ошибки в запросе — сравнение полей разных типов. Например, код справочника строковый, а в условие подставлено число: ГДЕ Номенклатура.Код = 15. Решение — передавать параметр того же типа, что и поле, а для строкового кода учитывать ведущие нули.

Что делать, если число лежит в строковом поле

Если цифры хранятся в строковом реквизите, в запросе их не посчитать. Варианты по возрастанию трудоёмкости:

  1. Выполнить запрос, выгрузить в таблицу значений, добавить числовую колонку и заполнить её через ПривестиКЧислу() в цикле.
  2. Для коротких кодов фиксированной длины разбирать строку посимвольно через ПОДСТРОКА и ВЫБОР — громоздко и годится только для 2-3 разрядов.
  3. Исправить модель данных: добавить числовой реквизит и заполнить его обработкой один раз.

Третий вариант правильный, если поле участвует в отчётах постоянно.

Как найти, где и почему тип оказался не тем

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

  1. В конфигураторе запустите отладку и включите Отладка → Остановка по ошибке. Выполнение остановится ровно на строке, где возникло исключение.
  2. Выделите переменную и откройте окно вычисления выражения (Shift+F9). В нём видно и значение, и его тип.
  3. Вычислите прямо там ТипЗнч(Значение) — так вы точно узнаете, пришла ли строка, Неопределено или ссылка.
  4. Для строки проверьте длину через СтрДлина(). Если длина больше видимых символов, внутри есть непечатаемые знаки: неразрывный пробел, табуляция, перевод строки.
  5. Для запросов скопируйте текст в консоль запросов и выполните по частям. Координаты из сообщения {(строка, позиция)} укажут на проблемное выражение.

Без отладчика помогает временная запись в журнал регистрации:

ЗаписьЖурналаРегистрации("Загрузка.Отладка", УровеньЖурналаРегистрации.Информация,
	, , "Строка " + Формат(НомерСтроки, "ЧГ=0") + ": тип " + Строка(ТипЗнч(Значение)));
Диагностика: выберите симптом

Как не допускать ошибок типов при разработке

Большинство таких сбоев закладывается ещё при проектировании. Несколько правил, которые снимают проблему заранее:

  • 🧱 Конкретные типы реквизитов. Составной тип делайте только там, где он действительно нужен. Поле «Сумма» должно быть Число, а не Строка «для гибкости».
  • 🚪 Конвертация на входе. Приводите данные к нужному типу один раз — в момент чтения файла или ответа сервиса, а дальше работайте только с чистыми значениями.
  • 🧰 Одна функция на всю конфигурацию. Держите ПривестиКЧислу() в общем модуле, а не пишите Число() с разной очисткой в десяти местах.
  • 🏷 Типизированные параметры запроса. Передавайте в УстановитьПараметр() значение того же типа, что и поле, а не строку «на всякий случай».
  • 📝 Комментарии к параметрам. Опишите в комментарии функции ожидаемые типы параметров — EDT и конфигуратор показывают их в подсказке при вызове.
  • 🧪 Проверка на грязных данных. Тестируйте загрузку на файле с пустыми ячейками, запятыми, пробелами и текстом в числовой колонке, а не на идеальном примере.

Исправления в типовой конфигурации на поддержке вносите через расширение, а не прямой правкой модулей — иначе каждое обновление превратится в ручное сравнение и объединение. Перед любыми изменениями сделайте резервную копию базы.

Частые вопросы

Почему 1 + "2" в 1С даёт 3, а "2" + 1 даёт "21"?
При сложении платформа приводит правый операнд к типу левого. Число слева — строка конвертируется в число, строка слева — число превращается в текст и склеивается.
Можно ли получить число из даты?
Напрямую через Число() нельзя. Разность двух дат даёт количество секунд, а год, месяц и день возвращают функции Год(), Месяц() и День().
Почему ВЫРАЗИТЬ в запросе возвращает NULL, а не ошибку?
Если в поле составного типа лежит значение другого типа, ВЫРАЗИТЬ отдаёт NULL. Отберите записи по ТИПЗНАЧЕНИЯ или оберните выражение в ЕСТЬNULL.
Как прочитать число из JSON без конвертации?
ЧитатьJSON сам возвращает числа типом Число, если в файле они записаны без кавычек. Если поставщик присылает числа в кавычках, это строки, и их нужно конвертировать.
Что лучше: Попытка с Число() или ОписаниеТипов?
Попытка позволяет отличить нераспознанное значение и сообщить о нём. ОписаниеТипов короче, но молча возвращает 0, поэтому годится там, где ноль — допустимый результат.