Мы часто сталкиваемся с ситуацией, когда необходимо убедиться, что из нескольких реквизитов объекта заполнен хотя бы один. Это может быть требование для сохранения документа, условие для отчета или критерий при обработке данных. Давайте вместе разберем, как эффективно решить эту задачу как в языке запросов 1С, так и программно.
Начнем с языка запросов 1С, поскольку именно там возник основной вопрос. Как правильно сформулировать условие, чтобы, например, из двух реквизитов (пусть это будут Реквизит1 и Реквизит2) был заполнен хотя бы один?
Рассмотрим логическое выражение `НЕ (Условие1 ИЛИ Условие2)`. Это стандартная логическая конструкция, известная как закон де Моргана, которая гласит, что отрицание дизъюнкции (логического "ИЛИ") эквивалентно конъюнкции (логическому "И") отрицаний. То есть, `НЕ (А ИЛИ В)` эквивалентно `(НЕ А) И (НЕ В)`.
Теперь применим это к нашей задаче. Мы хотим, чтобы **хотя бы один** из реквизитов был заполнен. Это означает, что **неверно**, что **оба** реквизита **не заполнены**.
Таким образом, условие "хотя бы один из реквизитов заполнен" можно выразить как:
`НЕ (Реквизит1 НЕ ЗАПОЛНЕН И Реквизит2 НЕ ЗАПОЛНЕН)`
Разберем, что в языке запросов 1С означает "не заполнен":
* `NULL`: Это значение обозначает отсутствие значения в выборке, полученной из базы данных. Оно часто встречается при использовании внешних соединений или при работе с "битыми" ссылками. Для проверки на NULL используем оператор `ЕСТЬ NULL`.
* `НЕОПРЕДЕЛЕНО`: Это значение по умолчанию для реквизитов составного типа, а также для объявленных переменных, которым еще не присвоено значение. В запросах для проверки ссылочных типов на "пустоту" часто достаточно `ЕСТЬ NULL`, но для составных типов или для точной проверки на пустую ссылку может потребоваться `Реквизит.Ссылка <> НЕОПРЕДЕЛЕНО`.
* `Пустые значения для простых типов`: Для числовых типов "пустым" значением является `0`, для строковых — пустая строка `""`, для дат — пустая дата.
Вернемся к нашей конструкции. Если мы хотим, чтобы Реквизит1 был заполнен ИЛИ Реквизит2 был заполнен, и под "не заполнен" подразумеваем `ЕСТЬ NULL`, то наше условие будет выглядеть так:
ГДЕ
НЕ (Документ.Реквизит1 ЕСТЬ NULL И Документ.Реквизит2 ЕСТЬ NULL)
Эта конструкция означает: "Неправда, что Реквизит1 равен NULL И Реквизит2 равен NULL". То есть, хотя бы один из них не равен NULL (т.е., заполнен). Эквивалентной формой, которая может быть более интуитивно понятна, является:
ГДЕ
НЕ Документ.Реквизит1 ЕСТЬ NULL ИЛИ НЕ Документ.Реквизит2 ЕСТЬ NULL
Эта форма прямо читается как: "Реквизит1 не равен NULL ИЛИ Реквизит2 не равен NULL".
Давайте посмотрим на пример запроса. Предположим, у нас есть документ "ЗаказКлиента", и нам нужно выбрать все заказы, у которых заполнен либо Менеджер, либо Ответственный. Оба поля являются ссылочными.
ВЫБРАТЬ
ЗаказКлиента.Ссылка,
ЗаказКлиента.Номер,
ЗаказКлиента.Дата
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ГДЕ
НЕ (ЗаказКлиента.Менеджер ЕСТЬ NULL И ЗаказКлиента.Ответственный ЕСТЬ NULL)
Или, используя вторую форму:
ВЫБРАТЬ
ЗаказКлиента.Ссылка,
ЗаказКлиента.Номер,
ЗаказКлиента.Дата
ИЗ
Документ.ЗаказКлиента КАК ЗаказКлиента
ГДЕ
НЕ ЗаказКлиента.Менеджер ЕСТЬ NULL ИЛИ НЕ ЗаказКлиента.Ответственный ЕСТЬ NULL
Обе эти формы дадут одинаковый результат, выбирая только те документы, у которых заполнен хотя бы один из указанных реквизитов.
Помимо запросов, часто требуется проверять заполненность реквизитов непосредственно в коде, например, перед записью объекта или при изменении данных в форме. Рассмотрим несколько подходов.
Декларативная проверка (свойство "Проверка заполнения")
Это самый простой способ для базовой проверки. В режиме конфигуратора для каждого реквизита объекта (документа, справочника) мы можем установить свойство "Проверка заполнения" в значение "Выдавать ошибку". В этом случае платформа автоматически проконтролирует заполнение реквизита при интерактивном вводе и перед записью объекта.
Однако этот механизм работает для каждого реквизита по отдельности. Если нам нужно условие "один из двух", то декларативная проверка не подойдет напрямую. Она вызовет ошибку, если любой из отмеченных реквизитов не заполнен, а нам нужно, чтобы хотя бы один был заполнен.
Программная проверка с использованием событий
Для более сложной логики, такой как "заполнен хотя бы один из двух", мы можем использовать обработчики событий объекта или формы.
Событие ОбработкаПроверкиЗаполнения (модуль объекта)
Это событие вызывается перед записью объекта. В нем мы можем программно проверить условия и, при необходимости, отменить запись. У нас есть доступ к параметру Отказ, который при установке в Истина предотвратит запись объекта. Мы также получаем массив ПроверяемыеРеквизиты, который содержит имена реквизитов, подлежащих проверке по декларативным настройкам. Мы можем удалить из него реквизиты, которые проверяем сами, или добавить свои.
Посмотрим на пример кода. Предположим, у нас есть документ, и нам нужно, чтобы было заполнено либо поле Ответственный, либо поле Исполнитель.
Процедура ОбработкаПроверкиЗаполнения(Отказ, ПроверяемыеРеквизиты)
// Удалим стандартную проверку для наших реквизитов,
// если она была установлена декларативно, чтобы избежать дублирования
Для Каждого ИмяРеквизита Из ПроверяемыеРеквизиты Цикл
Если ИмяРеквизита = "Ответственный" ИЛИ ИмяРеквизита = "Исполнитель" Тогда
ПроверяемыеРеквизиты.Удалить(ИмяРеквизита);
КонецЕсли;
КонецЦикла;
// Проверяем, что хотя бы один из реквизитов заполнен
Если ЗначениеЗаполнено(ЭтотОбъект.Ответственный) ИЛИ ЗначениеЗаполнено(ЭтотОбъект.Исполнитель) Тогда
// Все хорошо, продолжаем
Иначе
// Ни один из реквизитов не заполнен, выдаем ошибку
Сообщить("Необходимо заполнить либо 'Ответственный', либо 'Исполнитель'.", СтатусСообщения.Важное);
Отказ = Истина;
КонецЕсли;
КонецПроцедуры
Здесь мы используем функцию ЗначениеЗаполнено(), которая является универсальным способом проверки заполненности для большинства типов значений в 1С (не `NULL`, не `НЕОПРЕДЕЛЕНО`, не пустая строка, не `0`, не пустая дата, не пустая ссылка).
Событие ОбработкаПроверкиЗаполненияНаСервере (модуль формы)
Аналогичное событие существует в модуле формы. Оно вызывается на сервере при проверке заполнения формы. Логика работы с Отказ и ПроверяемыеРеквизиты здесь такая же.
Метод ПроверитьЗаполнение()
У большинства объектов 1С (справочников, документов) есть метод ПроверитьЗаполнение(). Он запускает декларативную проверку заполнения реквизитов. Если требуется запустить проверку заполнения для объекта, например, из обработчика кнопки, мы можем вызвать этот метод. Однако, как и декларативная проверка, он не подходит для условий "один из двух" без дополнительной программной логики в событиях.
В процессе работы с запросами и данными часто возникает необходимость манипулировать датами. В исходной теме упоминалась функция КОНЕЦПЕРИОДА(&КонецПериода, ДЕНЬ). Давайте проанализируем её применение.
Функция КОНЕЦПЕРИОДА() в языке запросов 1С используется для получения даты и времени окончания указанного периода для заданной даты.
* Параметры:
* Дата: Исходная дата, для которой нужно определить конец периода. В примере это `&КонецПериода`, что указывает на использование параметра запроса, который будет передан в запрос из кода 1С.
* Период: Тип периода. Может принимать значения `ГОД`, `КВАРТАЛ`, `МЕСЯЦ`, `НЕДЕЛЯ`, `ДЕНЬ`, `ЧАС`, `МИНУТА`, `СЕКУНДА`.
* ДЕНЬ: В качестве второго параметра `ДЕНЬ` означает, что функция вернет дату и время, соответствующее концу указанного дня (например, 23:59:59.999).
* Применение: Эта функция часто используется для построения временных интервалов в отчетах и запросах, например, чтобы включить в выборку все данные за определенный день до самого его конца.
Посмотрим на пример использования в запросе:
ВЫБРАТЬ
Документ.Ссылка,
Документ.Дата
ИЗ
Документ.НашДокумент КАК Документ
ГДЕ
Документ.Дата МЕЖДУ &НачалоПериода И КОНЕЦПЕРИОДА(&КонецПериода, ДЕНЬ)
В этом примере мы отбираем документы, даты которых попадают в заданный интервал, причем верхняя граница интервала будет включать весь последний день.
Мы рассмотрели различные подходы к проверке заполненности хотя бы одного из двух реквизитов в 1С. Для запросов мы выяснили, что логическая конструкция `НЕ (Реквизит1 ЕСТЬ NULL И Реквизит2 ЕСТЬ NULL)` или её эквивалент `НЕ Реквизит1 ЕСТЬ NULL ИЛИ НЕ Реквизит2 ЕСТЬ NULL` является наиболее подходящей. Для программной проверки мы можем использовать событие ОбработкаПроверкиЗаполнения в модуле объекта с функцией ЗначениеЗаполнено(). Также мы проанализировали полезную функцию КОНЕЦПЕРИОДА() для работы с датами в запросах. Надеемся, эти рекомендации помогут вам в вашей работе с 1С!