ЗначениеЗаполнено, Пустая() и сравнение с Неопределено в 1С 8.3

· 9 мин

Содержание

Универсальный способ проверить, заполнено ли значение в 1С 8.3, — функция ЗначениеЗаполнено(Значение). Она возвращает Ложь, если значение совпадает со значением по умолчанию для своего типа: Неопределено, NULL, пустая строка, число 0, пустая дата, пустая ссылка. Одной проверкой закрываются почти все случаи.

Для ссылочных реквизитов есть и короткий метод Ссылка.Пустая(). Строку проверяют функцией ПустаяСтрока(Стр) или сравнением СокрЛП(Стр) = "". Отдельно сравнивают с Неопределено (значение не установлено) и NULL (поле пришло из базы пустым).

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

Универсальная проверка: функция ЗначениеЗаполнено

ЗначениеЗаполнено работает и на клиенте, и на сервере, с любым типом. Логика простая: значение «заполнено», если оно отличается от пустого значения своего типа.

Если Не ЗначениеЗаполнено(Объект.Контрагент) Тогда
    Сообщить("Не заполнен контрагент");
    Возврат;
КонецЕсли;

Что функция считает незаполненным:

  • 🔹 Неопределено и NULL → Ложь;
  • 🔹 пустая строка "" или строка из одних пробелов → Ложь;
  • 🔹 число 0 → Ложь;
  • 🔹 пустая дата '00010101' → Ложь;
  • 🔹 пустая ссылка справочника или документа → Ложь;
  • 🔹 пустой массив, структура, соответствие → Ложь;
  • 🔹 Ложь для типа Булево → тоже Ложь (это значение по умолчанию).

Главный плюс ЗначениеЗаполнено — она не падает на составных реквизитах. Если поле может содержать то ссылку, то Неопределено, вызов метода .Пустая() оборвётся с ошибкой, а ЗначениеЗаполнено отработает корректно.

Единственная ловушка — последний пункт про Булево и число 0. Если 0 или Ложь для вашего поля допустимое значение (количество, процент скидки, флаг), проверять его на заполненность через ЗначениеЗаполнено нельзя — она посчитает такое поле пустым.

Неопределено и NULL: в чём разница и как ловить оба

Эти два значения часто путают, а это разные вещи.

Неопределено — это значение по умолчанию для ещё не установленного значения. Его возвращают:

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

NULL — это отсутствующее значение из базы данных. Чаще всего приходит из запроса: левое соединение не нашло пару, ресурс в итогах пуст, поле в составной таблице не относится к этой строке.

Сравнивают их через обычное равенство:

Если Значение = Неопределено Тогда
    // значение не установлено
КонецЕсли;

Если Значение = NULL Тогда
    // пустое значение из базы
КонецЕсли;

Типичный пример с соответствием — проверить, есть ли ключ:

Значение = КэшДанных.Получить(Ключ);
Если Значение = Неопределено Тогда
    // ключа в соответствии нет
    Значение = ПрочитатьИзБазы(Ключ);
КонецЕсли;

Если нужно за один раз отсеять и Неопределено, и NULL, и пустую ссылку — снова берите ЗначениеЗаполнено: она ловит всё перечисленное.

В языке запросов NULL нельзя сравнивать через =. Там работает только предикат ЕСТЬ NULL (Поле ЕСТЬ NULL / Поле ЕСТЬ НЕ NULL). В встроенном языке — наоборот, Значение = NULL корректно.

Пустые строки и нулевые числа

Для строк частая ошибка — проверка Если Стр <> "" Тогда. Она пропускает строку из пробелов: визуально пусто, а длина не ноль.

Надёжные варианты:

// 1. глобальная функция, учитывает пробелы
Если ПустаяСтрока(Объект.Комментарий) Тогда ...

// 2. обрезка краёв и сравнение с пустой строкой
Если СокрЛП(Объект.Комментарий) = "" Тогда ...

// 3. универсально — тоже считает пробелы пустотой
Если Не ЗначениеЗаполнено(Объект.Комментарий) Тогда ...

Все три для строки из пробелов вернут «пусто». ПустаяСтрока и ЗначениеЗаполнено отбрасывают пробелы и непечатаемые символы внутри себя, СокрЛП обрезает их по краям вручную.

С числами аккуратнее. ЗначениеЗаполнено(0) возвращает Ложь. Для суммы или цены это обычно правильно: «ноль» = «не указано». Но для количества, остатка, процента 0 — рабочее значение, и тогда проверка на заполненность не подходит — сравнивайте явно с тем, что для вас «пусто», либо используйте отдельный флаг «значение задано».

С датами всё проще. Пустая дата в 1С — это '00010101' (первое января первого года), и ЗначениеЗаполнено(Дата) корректно её отлавливает:

Если Не ЗначениеЗаполнено(Объект.ДатаОплаты) Тогда
    Сообщить("Дата оплаты не указана");
КонецЕсли;

Не сравнивайте дату с 0 или с текущей датой — проверка на пустоту этого не заменяет. Если поле хранит дату со временем, время на результат ЗначениеЗаполнено не влияет: значимы и дата, и время, но пустое значение у них общее.

ТипКак проверить на пустотуНюанс
СтрокаПустаяСтрока(Стр)пробелы считаются пустотой
Число (сумма)ЗначениеЗаполнено(Чис)0 трактуется как пусто
Число (количество)Чис = 0 явно или флаг0 может быть валидным
ДатаЗначениеЗаполнено(Дат)пустая дата — '00010101'
СсылкаСсылка.Пустая()только для ссылочных типов

Пустые ссылки и реквизиты объектов

У ссылочных типов (СправочникСсылка, ДокументСсылка, ПеречислениеСсылка и прочих) есть метод Пустая():

Если Объект.Договор.Пустая() Тогда
    Сообщить("Договор не выбран");
КонецЕсли;

Это эквивалент Не ЗначениеЗаполнено(Объект.Договор), но короче и чуть нагляднее в коде, где точно известно, что реквизит — ссылка.

Разница проявляется на составных реквизитах. Если реквизит может содержать ссылки разных типов или Неопределено, прямой вызов .Пустая() опасен: на значении Неопределено у него нет такого метода, и код упадёт. Здесь либо ЗначениеЗаполнено, либо сначала проверка типа:

Если ТипЗнч(Объект.Получатель) = Тип("СправочникСсылка.Контрагенты") Тогда
    Если Объект.Получатель.Пустая() Тогда
        // пустой контрагент
    КонецЕсли;
КонецЕсли;

Явную пустую ссылку конкретного типа берут у менеджера:

ПустойДоговор = Справочники.ДоговорыКонтрагентов.ПустаяСсылка();
Если Объект.Договор = ПустойДоговор Тогда ...

Для структур и их свойств удобна связка «проверить наличие + извлечь» методом Свойство:

Значение = Неопределено;
Если Параметры.Свойство("Организация", Значение) И ЗначениеЗаполнено(Значение) Тогда
    // ключ есть И значение заполнено
КонецЕсли;

Структура.Свойство("Имя") возвращает Истина/Ложь (есть ли свойство), а второй параметр принимает само значение — так не нужно отдельно обращаться к несуществующему ключу и ловить ошибку.

Проверка в запросах и выборках без лишних обращений к базе

Самая частая потеря производительности — когда заполненность проверяют в цикле по выборке, дёргая каждый раз реквизиты через точку. Каждое такое обращение — это запрос к базе.

Правильнее отфильтровать пустые значения ещё в запросе:

ВЫБРАТЬ
    Товары.Номенклатура КАК Номенклатура
ИЗ
    Документ.РеализацияТоваровУслуг.Товары КАК Товары
ГДЕ
    Товары.Номенклатура = ЗНАЧЕНИЕ(Справочник.Номенклатура.ПустаяСсылка)

Пустое поле из левого соединения ловят предикатом ЕСТЬ NULL, а для арифметики заменяют NULL на 0 функцией ЕСТЬNULL:

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

Без ЕСТЬNULL попытка сложить NULL с числом при обходе выборки даст ошибку.

Если проверку всё же делаете в коде, не пересчитывайте границы изменяемой коллекции внутри цикла. Для удаления пустых элементов из массива идите с конца через Пока:

Индекс = Массив.Количество() - 1;
Пока Индекс >= 0 Цикл
    Если Не ЗначениеЗаполнено(Массив[Индекс]) Тогда
        Массив.Удалить(Индекс);
    КонецЕсли;
    Индекс = Индекс - 1;
КонецЦикла;

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

Встроенный механизм проверки заполнения реквизитов

Для форм и объектов не обязательно писать проверки руками. У реквизита в конфигураторе есть свойство «Проверка заполнения» со значением «Выдавать ошибку» — платформа сама проверит поле при записи и подсветит его.

Программно это запускает метод объекта ПроверитьЗаполнение():

Если Не Объект.ПроверитьЗаполнение() Тогда
    // есть незаполненные обязательные реквизиты
    Возврат;
КонецЕсли;

Свою логику, которую нельзя задать галочкой (поле обязательно только при определённом виде операции), пишут в обработчике события ОбработкаПроверкиЗаполнения:

Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
    Если ВидОперации = Перечисления.ВидыОпераций.Оплата
        И Не ЗначениеЗаполнено(Договор) Тогда

        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Для оплаты нужно указать договор";
        Сообщение.Поле = "Договор";
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЕсли;
КонецПроцедуры

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

Для строк табличной части обходят каждую строку и указывают её номер в пути к данным, чтобы подсветка попала в нужную ячейку:

Для Каждого СтрокаТЧ Из Товары Цикл
    Если Не ЗначениеЗаполнено(СтрокаТЧ.Цена) Тогда
        Сообщение = Новый СообщениеПользователю;
        Сообщение.Текст = "Не указана цена";
        Сообщение.Поле = СтрШаблон("Товары[%1].Цена", СтрокаТЧ.НомерСтроки - 1);
        Сообщение.Сообщить();
        Отказ = Истина;
    КонецЕсли;
КонецЦикла;

Индекс строки в пути нумеруется с нуля, поэтому из НомерСтроки (нумерация с единицы) вычитают один. Число в путь подставляют через СтрШаблон, а не склеивают со строкой оператором + — иначе получите ошибку преобразования типа.

Что делать с ошибками при доступе к значению

Когда значение приходит извне (из файла, строки, внешнего источника) и тип неизвестен, преобразование оборачивают в Попытка:

Попытка
    ЧисловоеЗначение = Число(ВходнаяСтрока);
Исключение
    ЧисловоеЗначение = 0;
    ЗаписьЖурналаРегистрации("Загрузка",
        УровеньЖурналаРегистрации.Предупреждение, , ,
        ОписаниеОшибки());
КонецПопытки;

ОписаниеОшибки() даёт текст, ИнформацияОбОшибке() — структурированное описание для разбора.

Но исключения — не способ управлять логикой. Если можно заранее проверить значение методами ЗначениеЗаполнено, ТипЗнч, Структура.Свойство, делайте это, а не ловите ошибку доступа. Попытка дешёвая, пока исключение не выброшено; само возбуждение исключения уже заметно дороже обычной проверки.

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

Какой тип у проверяемого значения?

Частые ошибки

  • ❌ Если Строка <> "" Тогда — пропускает строку из пробелов. Берите ПустаяСтрока или ЗначениеЗаполнено.
  • ❌ Реквизит.Пустая() на составном типе — падает, когда в реквизите Неопределено. Проверяйте ТипЗнч или используйте ЗначениеЗаполнено.
  • ❌ ЗначениеЗаполнено(Количество) там, где 0 допустим, — поле с нулём ошибочно считается незаполненным.
  • ❌ Поле = NULL в запросе — всегда даёт «неизвестно». В языке запросов нужен ЕСТЬ NULL.
  • ❌ Арифметика с NULL из левого соединения — NULL + Число рушит выборку. Оборачивайте в ЕСТЬNULL(Поле, 0).
  • ❌ Проверка заполненности через точку в цикле — каждое обращение к реквизиту ссылки лезет в базу. Выбирайте нужные поля в запросе заранее.

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

Чем ЗначениеЗаполнено отличается от метода Пустая()?
Пустая() есть только у ссылочных типов и проверяет именно пустую ссылку. ЗначениеЗаполнено работает с любым типом (строка, число, дата, коллекция, Неопределено, NULL) и не падает на составных реквизитах. Для чистой ссылки обе дают одинаковый результат.
Можно ли одной проверкой закрыть Неопределено, NULL и пустую ссылку?
Да. ЗначениеЗаполнено(Значение) вернёт Ложь для всех трёх случаев сразу. Отдельные сравнения = Неопределено и = NULL нужны, только когда важно различить, почему значение пустое.
Почему ЗначениеЗаполнено для строки из пробелов возвращает Ложь?
Функция отбрасывает начальные и конечные пробелы и непечатаемые символы. Строка " " после такой обрезки пустая, поэтому считается незаполненной. Если нужно учитывать пробелы как значимые символы, сравнивайте длину через СтрДлина без обрезки.
Как подсветить незаполненное поле прямо на форме?
Создайте объект Новый СообщениеПользователю, задайте Текст и свойство Поле с именем реквизита, вызовите Сообщить() и установите Отказ = Истина в обработчике проверки. Платформа подсветит указанное поле.
Как проверить сразу несколько обязательных реквизитов?
Вызовите Объект.ПроверитьЗаполнение() — он проверит все реквизиты с включённой в конфигураторе проверкой заполнения и вернёт Ложь, если хоть один пуст. Дополнительные условия добавляйте в обработчике ОбработкаПроверкиЗаполнения.