Проверка «значение заполнено» в 1С делается одной функцией — ЗначениеЗаполнено(). Она работает на клиенте и на сервере, принимает значение любого типа и возвращает Ложь, если оно совпадает с пустым значением своего типа: 0, пустая строка, пустая дата, пустая ссылка, Неопределено, NULL, коллекция без элементов. Ниже — таблица результатов для каждого типа в 1С 8.3, ловушки с нулём и Булево, проверка реквизитов перед записью и сообщения с привязкой к полю.
| Значение | Тип | ЗначениеЗаполнено() |
|---|---|---|
Неопределено | Неопределено | Ложь |
NULL | Null | Ложь |
0 | Число | Ложь |
-5, 0.01 | Число | Истина |
"" | Строка | Ложь |
" " (только пробелы) | Строка | Ложь |
"0" | Строка | Истина |
Дата(1, 1, 1), '00010101' | Дата | Ложь |
Ложь | Булево | Истина |
Истина | Булево | Истина |
Справочники.Контрагенты.ПустаяСсылка() | СправочникСсылка | Ложь |
| Ссылка на удалённый объект | СправочникСсылка | Истина |
Новый Массив | Массив | Ложь |
Массив из одного Неопределено | Массив | Истина |
Новый Структура, Новый Соответствие | Структура, Соответствие | Ложь |
Три строки таблицы расходятся с интуицией. Ноль — незаполненное число, хотя для остатка, скидки или цены это нормальное значение. Ложь — заполненное Булево: функция не скажет, поставлен ли флажок. Ссылка на удалённый объект тоже заполнена: функция смотрит только на саму ссылку и к базе не обращается. Проверить таблицу на своей версии платформы можно обработкой с одной командой:
&НаКлиенте
Процедура ПроверитьТипы(Команда)
ПроверитьТипыНаСервере();
КонецПроцедуры
&НаСервереБезКонтекста
Процедура ПроверитьТипыНаСервере()
МассивСНеопределено = Новый Массив;
МассивСНеопределено.Добавить(Неопределено);
Примеры = Новый Массив;
Примеры.Добавить(НовыйПример("Неопределено", Неопределено));
Примеры.Добавить(НовыйПример("NULL", NULL));
Примеры.Добавить(НовыйПример("Число 0", 0));
Примеры.Добавить(НовыйПример("Число -5", -5));
Примеры.Добавить(НовыйПример("Пустая строка", ""));
Примеры.Добавить(НовыйПример("Три пробела", " "));
Примеры.Добавить(НовыйПример("Строка ""0""", "0"));
Примеры.Добавить(НовыйПример("Пустая дата", Дата(1, 1, 1)));
Примеры.Добавить(НовыйПример("Ложь", Ложь));
Примеры.Добавить(НовыйПример("Пустая ссылка", Справочники.Контрагенты.ПустаяСсылка()));
Примеры.Добавить(НовыйПример("Несуществующая ссылка",
Справочники.Контрагенты.ПолучитьСсылку(Новый УникальныйИдентификатор)));
Примеры.Добавить(НовыйПример("Пустой массив", Новый Массив));
Примеры.Добавить(НовыйПример("Массив из Неопределено", МассивСНеопределено));
Примеры.Добавить(НовыйПример("Пустая структура", Новый Структура));
Примеры.Добавить(НовыйПример("Пустое соответствие", Новый Соответствие));
Для Каждого Пример Из Примеры Цикл
Сообщить(Пример.Описание + ": "
+ Формат(ЗначениеЗаполнено(Пример.Значение), "БЛ=Ложь; БИ=Истина"));
КонецЦикла;
КонецПроцедуры
&НаСервереБезКонтекста
Функция НовыйПример(Описание, Значение)
Возврат Новый Структура("Описание, Значение", Описание, Значение);
КонецФункции
Директива &НаСервереБезКонтекста нужна из-за Справочники.Контрагенты: менеджеры справочников на клиенте недоступны. Несуществующая ссылка получается через ПолучитьСсылку() с новым УникальныйИдентификатор — объекта с таким GUID в базе нет. Формат "БЛ=Ложь; БИ=Истина" печатает результат словами Ложь и Истина вместо «Нет» и «Да». Справочник Контрагенты замените на любой свой.
Для обязательного реквизита код не нужен: в конфигураторе ставят свойство Проверка заполнения = Выдавать ошибку, и платформа сама выводит «Поле "Контрагент" не заполнено». Проверка срабатывает при интерактивной записи объекта из формы, а у проводимых документов — при проведении. Условные правила пишут в обработчике ОбработкаПроверкиЗаполнения модуля объекта. Параметр ПроверяемыеРеквизиты — массив имён, которые платформа проверит после обработчика: в него можно добавить реквизит или убрать его. Отказ = Истина отменяет запись.
// Модуль объекта документа ЗаказКлиента.
// В конфигураторе: АдресДоставки — «Не проверять»,
// колонка Товары.Количество — «Выдавать ошибку».
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Флажок проверяем по значению, а не через ЗначениеЗаполнено
Если ТребуетсяДоставка Тогда
ПроверяемыеРеквизиты.Добавить("АдресДоставки");
КонецЕсли;
// Стандартная проверка отсечёт 0, но пропустит -5: заменяем своей
УдалитьИзПроверяемых(ПроверяемыеРеквизиты, "Товары.Количество");
Для Каждого СтрокаТоваров Из Товары Цикл
Если СтрокаТоваров.Количество <= 0 Тогда
ПутьКПолю = "Товары[" + Формат(СтрокаТоваров.НомерСтроки - 1, "ЧН=0; ЧГ=0") + "].Количество";
ТекстОшибки = СтрШаблон("Строка %1: количество должно быть больше нуля",
СтрокаТоваров.НомерСтроки);
СообщитьОбОшибке(ТекстОшибки, ПутьКПолю, Отказ);
КонецЕсли;
КонецЦикла;
// Пустую дату пропускаем: обязательность задаёт флаг в конфигураторе
Если ЗначениеЗаполнено(ДатаОтгрузки) И ДатаОтгрузки < НачалоДня(Дата) Тогда
СообщитьОбОшибке("Дата отгрузки раньше даты заказа", "ДатаОтгрузки", Отказ);
КонецЕсли;
КонецПроцедуры
Процедура УдалитьИзПроверяемых(ПроверяемыеРеквизиты, ИмяРеквизита)
Индекс = ПроверяемыеРеквизиты.Найти(ИмяРеквизита);
Если Индекс <> Неопределено Тогда
ПроверяемыеРеквизиты.Удалить(Индекс);
КонецЕсли;
КонецПроцедуры
Процедура СообщитьОбОшибке(Текст, Поле, Отказ)
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = Текст;
Сообщение.Поле = Поле;
Сообщение.УстановитьДанные(ЭтотОбъект);
Сообщение.Сообщить();
Отказ = Истина;
КонецПроцедуры
ПроверяемыеРеквизиты.Добавить("АдресДоставки") делает адрес обязательным только при доставке, а текст ошибки платформа сформирует сама по синониму реквизита."Товары.Количество". Её убираем: стандартная проверка отсечёт 0, но пропустит -5, потому что отрицательное число заполнено.Товары[0].Количество привязывает сообщение к ячейке таблицы. Индекс строки считается с нуля, отсюда НомерСтроки - 1. Формат "ЧН=0; ЧГ=0" обязателен: без него ноль превратится в пустую строку, а 1000 — в «1 000».УстановитьДанные(ЭтотОбъект) связывает сообщение с объектом, и при проведении из формы оно встаёт у нужного поля. В конфигурациях на БСП то же делает ОбщегоНазначения.СообщитьПользователю() с параметрами Текст, КлючДанных, Поле, ПутьКДанным, Отказ.У формы свой метод ПроверитьЗаполнение(). Он вызывается на клиенте, проверяет реквизиты формы с флагом «Выдавать ошибку», запускает серверный обработчик ОбработкаПроверкиЗаполненияНаСервере (его назначают в событиях формы) и возвращает Ложь, если нашлась ошибка. Пример — диалог параметров выгрузки, который отдаёт выбранные значения вызвавшей форме:
// Модуль формы диалога параметров выгрузки. Реквизиты формы:
// Организация («Выдавать ошибку»), НачалоПериода, КонецПериода,
// ВыгрузитьВФайл (Булево), ИмяФайла (Строка).
&НаКлиенте
Процедура ОК(Команда)
// Проверит реквизиты с флагом «Выдавать ошибку», вызовет
// ОбработкаПроверкиЗаполненияНаСервере и покажет сообщения у полей
Если Не ПроверитьЗаполнение() Тогда
Возврат;
КонецЕсли;
Результат = Новый Структура;
Результат.Вставить("Организация", Организация);
Результат.Вставить("НачалоПериода", НачалоПериода);
Результат.Вставить("КонецПериода", КонецПериода);
Результат.Вставить("ИмяФайла", ?(ВыгрузитьВФайл, ИмяФайла, ""));
Закрыть(Результат);
КонецПроцедуры
&НаСервере
Процедура ОбработкаПроверкиЗаполненияНаСервере(Отказ, ПроверяемыеРеквизиты)
Если ВыгрузитьВФайл Тогда
ПроверяемыеРеквизиты.Добавить("ИмяФайла");
КонецЕсли;
// Пустая граница периода означает «без ограничения»
Если ЗначениеЗаполнено(НачалоПериода) И ЗначениеЗаполнено(КонецПериода)
И НачалоПериода > КонецПериода Тогда
Сообщение = Новый СообщениеПользователю;
Сообщение.Текст = "Начало периода позже его окончания";
Сообщение.Поле = "НачалоПериода";
Сообщение.Сообщить();
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Для реквизита формы в Сообщение.Поле достаточно его имени, для реквизита объекта на форме документа путь будет "Объект.Контрагент". Сравнение дат начинается с двух вызовов ЗначениеЗаполнено: пустая граница означает «без ограничения», и сравнивать её с другой датой бессмысленно.
Метод Записать() проверку заполнения не запускает. Документ, созданный загрузкой, обменом или обработкой, попадёт в базу с пустым контрагентом без единого сообщения. Перед записью вызывайте ПроверитьЗаполнение() у самого объекта: метод учтёт и флаги из конфигуратора, и ОбработкаПроверкиЗаполнения.
// Общий модуль ЗагрузкаЗаказовСервер.
// ДанныеЗаказа — Структура с реквизитами шапки заказа.
Функция СоздатьЗаказ(ДанныеЗаказа, ТекстОшибки) Экспорт
ЗаказОбъект = Документы.ЗаказКлиента.СоздатьДокумент();
ЗаполнитьЗначенияСвойств(ЗаказОбъект, ДанныеЗаказа);
ЗаказОбъект.Дата = ТекущаяДатаСеанса();
// Записать() проверку заполнения не запускает, вызываем её сами
Если Не ЗаказОбъект.ПроверитьЗаполнение() Тогда
Тексты = Новый Массив;
Для Каждого Сообщение Из ПолучитьСообщенияПользователю(Истина) Цикл
Тексты.Добавить(Сообщение.Текст);
КонецЦикла;
ТекстОшибки = СтрСоединить(Тексты, Символы.ПС);
Возврат Неопределено;
КонецЕсли;
Попытка
ЗаказОбъект.Записать(РежимЗаписиДокумента.Запись);
Исключение
ТекстОшибки = КраткоеПредставлениеОшибки(ИнформацияОбОшибке());
Возврат Неопределено;
КонецПопытки;
Возврат ЗаказОбъект.Ссылка;
КонецФункции
ПроверитьЗаполнение() возвращает только Булево, а тексты ошибок кладёт в очередь сообщений. ПолучитьСообщенияПользователю(Истина) забирает их и очищает очередь, иначе после серверного вызова пользователь увидит весь список в окне сообщений. В очередь попадают и сообщения, выведенные раньше в том же вызове, — учитывайте это при разборе.
Четыре проверки дают разный результат на одних и тех же значениях:
| Значение З | Не ЗначениеЗаполнено(З) | З = Неопределено | З = Справочники.Контрагенты.ПустаяСсылка() | З.Пустая() |
|---|---|---|---|---|
| Пустая ссылка на контрагента | Истина | Ложь | Истина | Истина |
| Пустая ссылка на физлицо | Истина | Ложь | Ложь | Истина |
Неопределено | Истина | Истина | Ложь | ошибка |
0 | Истина | Ложь | Ложь | ошибка |
Реквизит одного ссылочного типа никогда не содержит Неопределено — в нём лежит пустая ссылка, поэтому условие Объект.Контрагент <> Неопределено истинно всегда. У реквизита составного типа пустое значение — Неопределено, но пользователь может выбрать тип и оставить ссылку пустой. Сравнение с ПустаяСсылка() одного справочника пропустит второй тип, а вызов .Пустая() на Неопределено упадёт. ЗначениеЗаполнено закрывает все варианты одним вызовом.
Обратный случай — входящие данные из JSON или файла обмена, где 0 и Ложь допустимы. Там вопрос «передано ли значение» решает сравнение с Неопределено, а формат — проверка типа значения через ТипЗнч:
// Общий модуль ЗагрузкаТоваровСервер.
// ДанныеСтроки — Структура, полученная из JSON или файла обмена.
Функция ОшибкиСтрокиЗагрузки(ДанныеСтроки) Экспорт
Ошибки = Новый Массив;
// Ссылка: Неопределено и пустая ссылка одинаково «не указано»
Номенклатура = Неопределено;
ДанныеСтроки.Свойство("Номенклатура", Номенклатура);
Если Не ЗначениеЗаполнено(Номенклатура) Тогда
Ошибки.Добавить("Не указана номенклатура");
КонецЕсли;
// Число: цена 0 допустима, важен сам факт передачи
Цена = Неопределено;
ДанныеСтроки.Свойство("Цена", Цена);
Если Цена = Неопределено Тогда
Ошибки.Добавить("Не передана цена");
ИначеЕсли ТипЗнч(Цена) <> Тип("Число") Или Цена < 0 Тогда
Ошибки.Добавить("Недопустимая цена: " + Строка(Цена));
КонецЕсли;
// Булево: Ложь — осознанное «нет», проверяем только тип
Активен = Неопределено;
ДанныеСтроки.Свойство("Активен", Активен);
Если ТипЗнч(Активен) <> Тип("Булево") Тогда
Ошибки.Добавить("Не передан признак Активен");
КонецЕсли;
// Строка: "" и " " для ЗначениеЗаполнено одинаково пустые
Артикул = Неопределено;
ДанныеСтроки.Свойство("Артикул", Артикул);
Если Не ЗначениеЗаполнено(Артикул) Тогда
Ошибки.Добавить("Пустой артикул");
ИначеЕсли СтрДлина(СокрЛП(Артикул)) > 25 Тогда
Ошибки.Добавить("Артикул длиннее 25 символов");
КонецЕсли;
// Массив: отсутствие ключа и пустой массив ловит одна проверка
Штрихкоды = Неопределено;
ДанныеСтроки.Свойство("Штрихкоды", Штрихкоды);
Если Не ЗначениеЗаполнено(Штрихкоды) Тогда
Ошибки.Добавить("Нет ни одного штрихкода");
КонецЕсли;
Возврат Ошибки;
КонецФункции
Цена 0 означает «бесплатно», и Не ЗначениеЗаполнено(Цена) забраковала бы корректную строку. Активен = Ложь — тоже законный ответ, поэтому проверяется тип, а не значение. Для штрихкодов одна проверка ловит и отсутствие ключа, и пустой массив, а Штрихкоды.Количество() = 0 на Неопределено остановит код ошибкой. Второй параметр Свойство() получает значение ключа, если он есть; прямое обращение ДанныеСтроки.Цена к отсутствующему ключу дало бы «Поле объекта не обнаружено (Цена)».
В тексте запроса функции ЗначениеЗаполнено нет. Пустоту проверяют сравнением с пустым значением типа: ссылку — с ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка), дату — с ДАТАВРЕМЯ(1, 1, 1), число — с нулём, результат левого соединения — через ЕСТЬ NULL. Подробно про ссылки — в статье о пустой ссылке в запросе.
// Общий модуль ПроверкиЗаполненияСервер.
Функция ЗаказыСПропусками(НачалоПериода, КонецПериода) Экспорт
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Заказ.Ссылка КАК Заказ,
| ВЫБОР
| КОГДА Заказ.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
| ТОГДА ""Не указан контрагент""
| КОГДА Заказ.ДатаОтгрузки = ДАТАВРЕМЯ(1, 1, 1)
| ТОГДА ""Не указана дата отгрузки""
| КОГДА Товары.Ссылка ЕСТЬ NULL
| ТОГДА ""Нет строк товаров""
| ИНАЧЕ ""Нулевая сумма""
| КОНЕЦ КАК Проблема
|ИЗ
| Документ.ЗаказКлиента КАК Заказ
| ЛЕВОЕ СОЕДИНЕНИЕ Документ.ЗаказКлиента.Товары КАК Товары
| ПО Заказ.Ссылка = Товары.Ссылка
| И Товары.НомерСтроки = 1
|ГДЕ
| Заказ.Дата МЕЖДУ &НачалоПериода И &КонецПериода
| И НЕ Заказ.ПометкаУдаления
| И (Заказ.Контрагент = ЗНАЧЕНИЕ(Справочник.Контрагенты.ПустаяСсылка)
| ИЛИ Заказ.ДатаОтгрузки = ДАТАВРЕМЯ(1, 1, 1)
| ИЛИ Товары.Ссылка ЕСТЬ NULL
| ИЛИ Заказ.СуммаДокумента = 0)";
Запрос.УстановитьПараметр("НачалоПериода", НачалоДня(НачалоПериода));
Запрос.УстановитьПараметр("КонецПериода", КонецДня(КонецПериода));
Возврат Запрос.Выполнить().Выгрузить();
КонецФункции
Соединение с табличной частью по условию НомерСтроки = 1 находит документы без строк: если первой строки нет, поля Товары приходят как NULL. ВЫБОР возвращает первую найденную проблему, поэтому документ с двумя пропусками попадёт в выборку один раз.
Неопределено строка Если Объект.Получатель.Пустая() Тогда падает с ошибкой «Значение не является значением объектного типа (Пустая)». Замена — Не ЗначениеЗаполнено(Объект.Получатель).Выборка.Остаток - Резерв при NULL из левого соединения даёт «Преобразование значения к типу Число не может быть выполнено». Оберните поле в ЕСТЬNULL(Остатки.Остаток, 0) прямо в запросе.Справочники.Контрагенты.ПустаяСсылка() в процедуре &НаКлиенте не скомпилируется: «Переменная не определена (Справочники)». На клиенте хватает ЗначениеЗаполнено, а пустую ссылку даёт ПредопределенноеЗначение("Справочник.Контрагенты.ПустаяСсылка").ПроверяемыеРеквизиты.Удалить(ПроверяемыеРеквизиты.Найти("Склад")) падает с «Ошибка при вызове метода контекста (Удалить)», если реквизита в массиве нет.Значение заполнено для массива в 1С равно Ложь, если элементов нет, и Истина, если есть хотя бы один, даже Неопределено. ЗначениеЗаполнено(Массив) надёжнее, чем Массив.Количество() = 0, когда переменная может оказаться Неопределено. Пустые элементы внутри функция не ищет — их перебирают циклом, приёмы есть в статье про массив в 1С.
У Булево нет «пустого» значения: Ложь — такой же ответ, как Истина. Обязательный флажок вроде «Согласие на обработку данных» проверяют по значению в ОбработкаПроверкиЗаполнения: Если Не СогласиеПолучено Тогда, затем сообщение с привязкой к полю и Отказ = Истина.
В тексте запроса — нет, работают сравнения с пустыми значениями и ЕСТЬ NULL. В выражениях системы компоновки данных, например в вычисляемых полях, функция доступна.
Нет: для ссылки она смотрит только, не пустая ли ссылка, поэтому «битая» ссылка на удалённый объект считается заполненной. Существование проверяют запросом по полю Ссылка или через ПолучитьОбъект() = Неопределено — второй вариант читает объект целиком и работает медленнее.
Если проверки заполнения разбросаны по формам и модулям, а документы с пустыми полями всё равно попадают в базу через загрузку или обмен, правила стоит собрать в одном месте — это типовая задача из раздела услуг по доработке 1С. Опишите, какие реквизиты и в каких случаях должны быть обязательными, и разместите задание — исполнители оценят его по вашей конфигурации.
Станьте частью сообщества!
Войдите или зарегистрируйтесь, и вы сможете участвовать в обсуждениях.