При работе с платформой 1С 7.7 программисты иногда сталкиваются с кажущимся аномальным поведением функции ПустоеЗначение() при проверке реквизитов ссылочного типа, например, Справочник.Контрагенты. Ситуация, когда реквизит формы визуально заполнен, но ПустоеЗначение(Реквизит) возвращает "1" (истина), в то время как метод Реквизит.Выбран() корректно возвращает "0" (ложь), может вызвать недоумение. Давайте разберем эту проблему подробно и выясним её причины, а также предложим методы диагностики и решения.
ПустоеЗначение() и Выбран() в 1С 7.7Прежде всего, крайне важно понимать принципиальную разницу между функцией ПустоеЗначение() и методом Выбран() применительно к ссылочным типам данных, таким как элементы справочников, документов или перечислений.
Функция ПустоеЗначение(<Значение>):
Эта функция предназначена для определения, является ли переданное значение пустым, и ведет себя по-разному в зависимости от типа данных:
ПустаяСтрока()).' . . ').ПустоеЗначение() определяет, указывает ли ссылка на какой-либо существующий элемент в базе данных. Если переменная содержит "пустую" ссылку (значение по умолчанию для данного ссылочного типа, не указывающее ни на один конкретный элемент), ПустоеЗначение() вернет "1". Если же переменная содержит ссылку на существующий элемент, либо на "битую" ссылку (ссылку на несуществующий элемент), она вернет "0".Метод <Ссылка>.Выбран():
Этот метод применим исключительно к переменным типа "Справочник", "Документ" или "Перечисление". Он проверяет, был ли произведен выбор элемента в форме или позиционирование переменной на существующий элемент. Он возвращает:
Ключевое отличие: Для "пустой" ссылки ПустоеЗначение() вернет "1", а Выбран() вернет "0". Для "битой" ссылки обе функции вернут "0". Это важный нюанс, который часто является источником путаницы.
ПустоеЗначение(Контрагент)Теперь, когда мы понимаем разницу, давайте рассмотрим типичные сценарии, которые могут приводить к ситуации, когда ПустоеЗначение(Контрагент) возвращает "1" для визуально заполненного реквизита, а Контрагент.Выбран() работает как ожидалось.
Одна из наиболее распространенных причин такого поведения в 1С 7.7 связана с некорректной работой с областью видимости переменных. Предположим, у вас на форме обработки есть реквизит с именем "Контрагент". Если в коде какой-либо процедуры вы случайно объявили локальную переменную с тем же именем:
Процедура ПриНажатииКнопки()
Перем Контрагент; // Здесь мы объявили локальную переменную
// ... далее по коду, возможно, эта локальная переменная не инициализируется
// или инициализируется каким-то пустым значением
Сообщить(ПустоеЗначение(Контрагент)); // Эта строка будет работать с ЛОКАЛЬНОЙ переменной
Сообщить(Контрагент.Выбран()); // А эта строка, возможно, все равно обращается к реквизиту формы
// или, если локальная переменная пуста, упадет с ошибкой "переменная не выбрана"
КонецПроцедуры
В этом случае, когда вы вызываете ПустоеЗначение(Контрагент), система может оперировать локальной переменной Контрагент, которая действительно пуста или не инициализирована, в то время как реквизит формы с тем же именем остается заполненным. Метод Выбран(), если он применяется к реквизиту формы, будет работать корректно. Этот эффект может быть особенно заметен, если вы копируете или изменяете код, не учитывая объявления Перем.
Чтобы избежать такой ситуации, всегда убедитесь, что вы работаете с нужной переменной. Если вы хотите обратиться к реквизиту формы, не объявляйте локальную переменную с таким же именем в той же процедуре или используйте явное указание контекста (например, Форма.Контрагент, хотя это и не всегда прямолинейно в 7.7).
Еще одна важная причина заключается в том, что переменная Контрагент может быть не ссылкой на существующий элемент справочника, а объектом справочника, который был создан, но еще не записан в базу данных. Рассмотрим пример:
Процедура СоздатьНовогоКонтрагента()
Контрагент = СоздатьОбъект("Справочник.Контрагенты");
Контрагент.Новый();
Контрагент.Наименование = "Новый Контрагент";
// ... заполняем другие реквизиты объекта
// Контрагент.Записать(); // Эту строку мы ЗАБЫЛИ или не вызвали намеренно
Сообщить("ЗначениеВСтрокуВнутр(Контрагент): " + ЗначениеВСтрокуВнутр(Контрагент));
Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент)); // Может вернуть 1
Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран()); // Вернет 0, потому что объект не записан
Сообщить("Контрагент.Вид(): " + Контрагент.Вид()); // Вернет "Справочник.Контрагенты"
КонецПроцедуры
В этом случае переменная Контрагент содержит объект справочника, но поскольку метод Записать() не был вызван, этот "контрагент" еще не существует как полноценный элемент в информационной базе. Объект Справочник, хотя и имеет заполненные поля, с точки зрения базы данных не "выбран" и не является "ссылкой". Для ПустоеЗначение() такой незаписанный объект может интерпретироваться как "пустой" или как не "выбранный" элемент, возвращая "1". Метод Выбран() для такого объекта всегда вернет "0", так как он еще не имеет постоянной ссылки в базе данных.
Если бы мы вызвали Контрагент.Записать(), то Контрагент стал бы ссылкой на существующий элемент, и тогда обе функции (ПустоеЗначение() и Выбран()) работали бы как ожидается.
Возможно, вы или другой участок кода присвоили реквизиту формы "пустую" ссылку (ПолучитьПустоеЗначение("Справочник.Контрагенты")) или не инициализировали его вовсе. Визуально реквизит может казаться пустым или содержать "0" в поле кода, но если вы по какой-то причине думаете, что он заполнен, а он на самом деле нет, то поведение ПустоеЗначение() будет абсолютно корректным.
Чтобы точно определить причину некорректного поведения, нам необходимо получить максимально подробную информацию о состоянии переменной Контрагент в момент выполнения кода. Мы используем для этого несколько полезных функций и методов 1С 7.7, как это было предложено на форуме.
Пошаговая диагностика:
Сообщить("--- Отладка Контрагента ---");
Сообщить("ЗначениеВСтрокуВнутр(Контрагент): " + ЗначениеВСтрокуВнутр(Контрагент));
Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент));
Сообщить("ТипЗначенияСтр(Контрагент): " + ТипЗначенияСтр(Контрагент));
// Эти методы применимы только если переменная имеет ссылочный тип (Справочник, Документ, Перечисление)
Если ТипЗначенияСтр(Контрагент) = "Справочник" Тогда
Сообщить("Контрагент.Вид(): " + Контрагент.Вид());
Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран());
Сообщить("Контрагент.ТекущийЭлемент(): " + Контрагент.ТекущийЭлемент());
// Если Контрагент - это объект, а не ссылка, могут быть дополнительные поля
// Если Контрагент.Выбран() = 1 Тогда
// Сообщить("Контрагент.Код: " + Контрагент.Код);
// Сообщить("Контрагент.Наименование: " + Контрагент.Наименование);
// КонецЕсли;
ИначеЕсли ТипЗначенияСтр(Контрагент) = "Неопределенный" Тогда
Сообщить("Переменная Контрагент не инициализирована или имеет неопределенный тип.");
КонецЕсли;
Сообщить("-------------------------");
Анализ вывода:
ЗначениеВСтрокуВнутр(Контрагент): Эта функция позволяет получить внутреннее представление значения переменной. Для пустой ссылки справочника вы увидите что-то вроде "Контрагенты : 0" или просто " : 0". Для существующей ссылки это будет выглядеть как "Контрагенты : 3a4e93f7-9200-4780-9286-d2495b5c7e10" (или другой GUID). Если вы видите "Контрагенты : 0", это однозначно указывает на пустую ссылку, которую ПустоеЗначение() корректно определяет как "1".
ТипЗначенияСтр(Контрагент): Этот вывод покажет нам фактический тип переменной. Если это "Справочник", "Строка", "Число" или "Неопределенный". Если вы увидите "Неопределенный", это означает, что переменная вообще не была инициализирована, и это явно укажет на проблему с областью видимости или неинициализацией.
Контрагент.Вид(): Если тип "Справочник", то это покажет имя справочника, например, "Справочник.Контрагенты". Если вы видите это, значит переменная как минимум является объектом справочника.
Контрагент.Выбран(): Как мы уже обсудили, это покажет, указывает ли переменная на существующий элемент в базе данных. Если Выбран() возвращает "0", а ЗначениеВСтрокуВнутр() показывает непустое значение (но не " : 0"), то скорее всего, переменная содержит "битую" ссылку или является незаписанным объектом справочника.
Контрагент.ТекущийЭлемент(): Этот метод возвращает ссылку на текущий элемент справочника, если он выбран. Если Выбран() равно 1, то ТекущийЭлемент() вернет эту ссылку. Если Выбран() равно 0, то этот метод, скорее всего, вернет пустую ссылку или вызовет ошибку, если переменная не является объектом справочника.
На основе анализа вывода этих функций мы сможем точно установить, что находится в переменной Контрагент, и почему ПустоеЗначение() ведет себя именно так. Если ЗначениеВСтрокуВнутр() содержит ": 0", а ТипЗначенияСтр() - "Справочник", это "пустая" ссылка. Если ТипЗначенияСтр() - "Неопределенный", проблема в области видимости. Если Выбран() - "0", но ЗначениеВСтрокуВнутр() не ": 0", то это, вероятно, незаписанный объект или "битая" ссылка.
После того как мы диагностировали истинную причину, мы можем применить соответствующее решение:
Если проблема в области видимости (`Перем`):
Мы должны убедиться, что всегда обращаемся к нужному реквизиту формы, а не к локальной переменной. Мы просматриваем код процедуры, где возникает проблема, и удаляем объявление Перем Контрагент;, если оно конфликтует с реквизитом формы. Если же локальная переменная необходима, мы даём ей другое, уникальное имя, например, Перем ЛокКонтрагент;. Мы всегда стараемся избегать ситуаций, когда имена локальных переменных совпадают с именами реквизитов формы, чтобы предотвратить такие неясности.
Если переменная является незаписанным объектом справочника:
Если наша диагностика показала, что переменная Контрагент — это объект, полученный через СоздатьОбъект() и Новый(), но не записанный в базу данных, нам нужно явно вызвать метод Записать(), чтобы сделать его полноценным элементом. Только после этого объект превратится в ссылку на существующий элемент, и функции ПустоеЗначение() и Выбран() начнут работать предсказуемо.
Процедура КорректноеСозданиеКонтрагента()
Контрагент = СоздатьОбъект("Справочник.Контрагенты");
Контрагент.Новый();
Контрагент.Наименование = "Новый Записанный Контрагент";
Контрагент.Записать(); // Теперь Контрагент - это ссылка на существующий элемент
Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент)); // Вернет 0
Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран()); // Вернет 1
КонецПроцедуры
В случае, если нам нужен только объект для временных операций, не предполагающих записи, и мы хотим проверить его "пустоту" относительно наличия данных, то использование ПустоеЗначение() для такого объекта может быть не самым адекватным способом. Вместо этого мы можем проверять заполненность ключевых полей объекта, например, Если ПустаяСтрока(Контрагент.Наименование) = 1 Тогда.
Если переменная содержит "пустую" ссылку:
В этом случае ПустоеЗначение() работает абсолютно корректно. Проблема не в функции, а в нашем предположении, что реквизит должен быть заполнен. Мы перепроверяем логику присвоения значения реквизиту и убеждаемся, что ему действительно присваивается корректная ссылка на элемент справочника.
ПустоеЗначение(), а когда Выбран()?Для проверки заполненности ссылочных типов (справочников, документов, перечислений) в 1С 7.7 мы обычно рекомендуем следующий подход:
Выбран(). Он более точно отражает состояние "выбранности" элемента.
Если Контрагент.Выбран() = 1 Тогда
// Контрагент заполнен и существует в базе
Иначе
// Контрагент пуст, не выбран или "бит"
КонецЕсли;
ПустоеЗначение(), но чаще всего Выбран() является более универсальным. Если мы хотим явно отличить пустую ссылку от битой, то это сложнее, но обычно достаточно Выбран().В большинстве практических задач, когда нам нужно убедиться, что пользователь выбрал какой-то элемент справочника или что ссылка на документ корректна, <Ссылка>.Выбран() является наиболее надежным и однозначным способом проверки.
В завершение, столкнувшись с неожиданным поведением, как в случае с ПустоеЗначение(), всегда применяйте методы отладки с выводом максимальной информации о переменной. Это поможет нам быстро локализовать проблему и принять верное решение, учитывая особенности работы с типами данных в 1С 7.7.