NULL в 1С — это не «пусто», а «значение неизвестно». Поэтому в запросе условие Поле = NULL никогда не выполняется, арифметика с NULL возвращает NULL, а во встроенном языке приводит к ошибке. Проверять NULL нужно конструкцией ЕСТЬ NULL или функцией ЕСТЬNULL, а для универсальной проверки заполненности в коде — ЗначениеЗаполнено, помня о её особенностях.

Ниже — набор таких ловушек в самых простых конструкциях: NULL и пустые значения, битые ссылки, текущая дата в разных часовых поясах, форматирование дат и превращение строки в число. Каждая из них хотя бы раз стоила мне часа отладки.

Виды «пустоты» в 1С

NULL и пустые значения в 1С: разные виды отсутствия данных
NULL и пустые значения в 1С: разные виды отсутствия данных
ЗначениеОткуда берётсяЧем опасно
NULLЗапросы: левое соединение без пары, реквизит не того типа, пустой итогСравнение через = не работает, арифметика ломается
НеопределеноНеинициализированная переменная, составной тип по умолчаниюУ него нет методов ссылки
Пустая строка ""Строковые реквизитыНе равна Неопределено
0, ЛожьЧисла и булево по умолчаниюЗначениеЗаполнено считает их незаполненными
Пустая ссылкаСсылочные реквизитыОтличается от битой ссылки

NULL в запросе: сравнение и арифметика

Сравнение

Запрос 1С
ВЫБРАТЬ
	ВЫБОР
		КОГДА Цены.Цена = NULL
			ТОГДА "Нет цены"
		ИНАЧЕ "Есть цена"
	КОНЕЦ КАК Состояние
ИЗ
	Справочник.Номенклатура КАК Номенклатура
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
		ПО Цены.Номенклатура = Номенклатура.Ссылка

Для товара без цены этот запрос вернёт «Есть цена». Сравнение с NULL через = даёт неопределённый результат, который трактуется как Ложь. Правильно:

Запрос 1С
ВЫБОР
	КОГДА Цены.Цена ЕСТЬ NULL
		ТОГДА "Нет цены"
	ИНАЧЕ "Есть цена"
КОНЕЦ КАК Состояние

То же касается <>: условие ГДЕ Цены.Цена <> 0 отбросит строки с NULL, хотя по смыслу «цена не равна нулю» они вроде бы проходят.

Арифметика

Любая операция с NULL возвращает NULL. Если в запросе считать Количество * Цены.Цена, у товаров без цены сумма будет NULL, а не 0 — и при попытке сложить её во встроенном языке вы получите ошибку «Преобразование значения к типу Число не может быть выполнено».

Запрос 1С
ВЫБРАТЬ
	Остатки.Номенклатура КАК Номенклатура,
	Остатки.КоличествоОстаток * ЕСТЬNULL(Цены.Цена, 0) КАК Стоимость
ИЗ
	РегистрНакопления.ТоварыНаСкладах.Остатки КАК Остатки
		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
		ПО Цены.Номенклатура = Остатки.Номенклатура

ЕСТЬNULL ставьте на поля из присоединённых таблиц и на итоги — там NULL появляется чаще всего. Во встроенном языке аналог — явная проверка Значение = NULL (здесь равенство работает) или функция, которая заменяет NULL нулём.

Пустая() и ЗначениеЗаполнено()

Метод Пустая() есть только у ссылок конкретного типа. Если реквизит составного типа и в нём Неопределено или строка, вызов упадёт с ошибкой «Значение не является значением объектного типа».

1С
Таблица = Новый ТаблицаЗначений;
Типы = Новый ОписаниеТипов("Строка, СправочникСсылка.Контрагенты");
Таблица.Колонки.Добавить("Значение", Типы);

Строка = Таблица.Добавить();
// Строка.Значение = Неопределено — значение по умолчанию для составного типа
// Строка.Значение.Пустая() — ошибка выполнения

Если Не ЗначениеЗаполнено(Строка.Значение) Тогда
	// Сработает для Неопределено, "", пустой ссылки и NULL
КонецЕсли;

ЗначениеЗаполнено безопасна для любого типа, но у неё своя ловушка: для 0 и Ложь она возвращает Ложь. Если реквизит «Скидка» равен нулю или флажок «Отменён» снят, проверка Не ЗначениеЗаполнено скажет «не заполнено», хотя значение осмысленное. Для чисел и булевых проверяйте тип или сравнивайте явно.

Битая ссылка: не пустая, но и не существующая

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

ПроверкаПустая ссылкаБитая ссылкаНормальная ссылка
Ссылка.Пустая()ИстинаЛожьЛожь
ЗначениеЗаполнено(Ссылка)ЛожьИстинаИстина
Ссылка.ПолучитьОбъект()—НеопределеноОбъект

Получать объект ради проверки дорого: он читается целиком со всеми табличными частями. Быстрее запрос:

1С
// Возвращает Истина, если объект по ссылке есть в базе.
Функция ОбъектСуществует(Ссылка) Экспорт

	Если Не ЗначениеЗаполнено(Ссылка) Тогда
		Возврат Ложь;
	КонецЕсли;

	Запрос = Новый Запрос;
	Запрос.Текст = СтрШаблон(
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИСТИНА КАК Есть
		|ИЗ
		|	%1 КАК Т
		|ГДЕ
		|	Т.Ссылка = &Ссылка", Ссылка.Метаданные().ПолноеИмя());
	Запрос.УстановитьПараметр("Ссылка", Ссылка);

	Возврат Не Запрос.Выполнить().Пустой();

КонецФункции

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

Текущая дата и часовые пояса

Часовые пояса в 1С: сравнение моментов времени через универсальное время
Часовые пояса в 1С: сравнение моментов времени через универсальное время
  • ТекущаяДата() — системное время компьютера, на котором выполняется код. На клиенте и сервере оно может различаться. В современных конфигурациях функцию не используют.
  • ТекущаяДатаСеанса() — время с учётом часового пояса сеанса. Правильный выбор для дат документов.
  • ТекущаяУниверсальнаяДата() — время UTC.

Пример ловушки. Срок приёма заявок — 10:20, пользователи в Ижевске (UTC+4) и Краснодаре (UTC+3) в один момент выполняют:

1С
СрокПриема = Дата(2026, 2, 6, 10, 20, 0);
Если ТекущаяДатаСеанса() > СрокПриема Тогда
	ВызватьИсключение "Приём заявок закрыт";
КонецЕсли;

Когда в Краснодаре 10:00, в Ижевске уже 11:00: один пользователь заявку отправит, другой — нет. Если срок — это конкретный момент времени, а не «10:20 по местному времени каждого», храните его в UTC и сравнивайте с универсальным временем:

1С
// Срок задан по времени информационной базы — переводим в UTC один раз
СрокПриемаUTC = УниверсальноеВремя(Дата(2026, 2, 6, 10, 20, 0), ЧасовойПояс());

Если ТекущаяУниверсальнаяДата() > СрокПриемаUTC Тогда
	ВызватьИсключение "Приём заявок закрыт";
КонецЕсли;

ЧасовойПояс() возвращает часовой пояс информационной базы, ЧасовойПоясСеанса() — сеанса. Перепутать их — ещё один частый источник сдвига на несколько часов.

Форматирование дат

Формат(Дата, "ДЛФ=Д") выводит дату в локальном формате, который зависит от региональных установок информационной базы и сеанса. В одной базе это будет «06.02.2026», в другой — «2026-02-06». Для интерфейса это нормально, а вот для имён файлов, обмена и интеграций — нет.

Когда формат важен, задавайте его явно:

1С
ИмяФайла = "Выгрузка_" + Формат(ТекущаяДатаСеанса(), "ДФ=yyyyMMdd_HHmmss") + ".xml";
ДатаДляAPI = Формат(Дата, "ДФ=yyyy-MM-dd");

Для XML и JSON используйте сериализацию платформы (XMLСтрока, ЗаписатьJSON с настройками формата даты), а не Формат.

Строка в число

Число("10") работает, а Число("10 1"), Число("") и строка с запятой вместо точки вызывают исключение «Преобразование значения к типу Число не может быть выполнено». Строки из Excel, сайтов и сканеров часто содержат неразрывные пробелы, разделители разрядов и запятые.

1С
// Преобразует строку в число; при неудаче возвращает ЗначениеПоУмолчанию.
Функция ЧислоИзСтроки(Знач Строка, ЗначениеПоУмолчанию = 0) Экспорт

	Если ТипЗнч(Строка) <> Тип("Строка") Тогда
		Возврат ЗначениеПоУмолчанию;
	КонецЕсли;

	Строка = СтрЗаменить(Строка, Символы.НПП, "");
	Строка = СтрЗаменить(Строка, " ", "");
	Строка = СтрЗаменить(Строка, ",", ".");

	Если ПустаяСтрока(Строка) Тогда
		Возврат ЗначениеПоУмолчанию;
	КонецЕсли;

	Попытка
		Возврат Число(Строка);
	Исключение
		Возврат ЗначениеПоУмолчанию;
	КонецПопытки;

КонецФункции

Замена запятой на точку подходит, когда запятая — дробный разделитель. Если во входных данных запятая отделяет разряды («1,250.50»), правила надо определить по источнику, иначе 1 250,5 превратится в 1,25. В БСП есть СтроковыеФункцииКлиентСервер.СтрокаВЧисло — перед использованием посмотрите в своей версии, что она возвращает для некорректной строки.

Типичные ошибки — короткий список

  • ГДЕ Поле = NULL вместо ГДЕ Поле ЕСТЬ NULL.
  • Умножение на поле из левого соединения без ЕСТЬNULL.
  • Пустая() у реквизита составного типа.
  • Не ЗначениеЗаполнено для числового или булева реквизита, где 0 и Ложь — нормальные значения.
  • Проверка существования объекта через ПолучитьОбъект() в цикле.
  • ТекущаяДата() вместо ТекущаяДатаСеанса() и сравнение локальных времён пользователей из разных поясов.
  • Формат без явной строки формата в именах файлов и интеграциях.
  • Число() без нормализации строки и без обработки исключения.
Большинство таких ошибок не видны на тестовой базе: там нет пустых цен, битых ссылок и пользователей из другого часового пояса. Пишите проверки сразу, а не после первого обращения в поддержку.

Такие ловушки я регулярно нахожу при аудите 1С чужих доработок; исправление обычно занимает минуты, а поиск — часы. Если нужна помощь с конкретной ошибкой, подойдёт консультация или доработка 1С.

Итог

  • NULL в 1С проверяется через ЕСТЬ NULL и заменяется через ЕСТЬNULL; равенство с NULL в запросе не работает.
  • ЗначениеЗаполнено безопаснее Пустая(), но считает 0 и Ложь незаполненными.
  • Битая ссылка заполнена, но объекта нет — проверяйте запросом или ОбщегоНазначения.СсылкаСуществует.
  • Для сравнения моментов времени между часовыми поясами используйте универсальное время.
  • Форматы дат и преобразование строк в числа задавайте явно и с обработкой ошибок.