ЗначениеЗаполнено, Пустая() и сравнение с Неопределено в 1С 8.3
Содержание
Универсальный способ проверить, заполнено ли значение в 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 нужны, только когда важно различить, почему значение пустое.Почему ЗначениеЗаполнено для строки из пробелов возвращает Ложь?
" " после такой обрезки пустая, поэтому считается незаполненной. Если нужно учитывать пробелы как значимые символы, сравнивайте длину через СтрДлина без обрезки.Как подсветить незаполненное поле прямо на форме?
Новый СообщениеПользователю, задайте Текст и свойство Поле с именем реквизита, вызовите Сообщить() и установите Отказ = Истина в обработчике проверки. Платформа подсветит указанное поле.Как проверить сразу несколько обязательных реквизитов?
Объект.ПроверитьЗаполнение() — он проверит все реквизиты с включённой в конфигураторе проверкой заполнения и вернёт Ложь, если хоть один пуст. Дополнительные условия добавляйте в обработчике ОбработкаПроверкиЗаполнения.