Почему функция "ПустоеЗначение()" возвращает "1" для заполненного реквизита типа "Справочник" в 1С 7.7, тогда как "Выбран()" работает корректно?

Программист 1С v7.7 IT и автоматизация бизнеса
← На главную

При работе с платформой 1С 7.7 программисты иногда сталкиваются с кажущимся аномальным поведением функции ПустоеЗначение() при проверке реквизитов ссылочного типа, например, Справочник.Контрагенты. Ситуация, когда реквизит формы визуально заполнен, но ПустоеЗначение(Реквизит) возвращает "1" (истина), в то время как метод Реквизит.Выбран() корректно возвращает "0" (ложь), может вызвать недоумение. Давайте разберем эту проблему подробно и выясним её причины, а также предложим методы диагностики и решения.

Понимание различий между функциями ПустоеЗначение() и Выбран() в 1С 7.7

Прежде всего, крайне важно понимать принципиальную разницу между функцией ПустоеЗначение() и методом Выбран() применительно к ссылочным типам данных, таким как элементы справочников, документов или перечислений.

Функция ПустоеЗначение(<Значение>):

Эта функция предназначена для определения, является ли переданное значение пустым, и ведет себя по-разному в зависимости от типа данных:

  1. Для строковых значений она проверяет, является ли строка пустой (аналогично ПустаяСтрока()).
  2. Для числовых значений она проверяет, равно ли число нулю.
  3. Для значений типа "Дата" она проверяет на пустое значение даты (' . . ').
  4. Для значений типа "СписокЗначений" или "ТаблицаЗначений" она проверяет на отсутствие элементов или строк соответственно.
  5. Для ссылочных типов (Справочник, Документ, Перечисление): ПустоеЗначение() определяет, указывает ли ссылка на какой-либо существующий элемент в базе данных. Если переменная содержит "пустую" ссылку (значение по умолчанию для данного ссылочного типа, не указывающее ни на один конкретный элемент), ПустоеЗначение() вернет "1". Если же переменная содержит ссылку на существующий элемент, либо на "битую" ссылку (ссылку на несуществующий элемент), она вернет "0".

Метод <Ссылка>.Выбран():

Этот метод применим исключительно к переменным типа "Справочник", "Документ" или "Перечисление". Он проверяет, был ли произведен выбор элемента в форме или позиционирование переменной на существующий элемент. Он возвращает:

  1. "1" (истина), если элемент справочника (документа, перечисления) выбран, то есть переменная содержит корректную ссылку на существующий элемент базы данных.
  2. "0" (ложь), если элемент не выбран, что может означать:
    • Переменная содержит "пустую" ссылку (значение по умолчанию).
    • Переменная содержит "битую" ссылку (ссылку на элемент, который был удален или никогда не существовал).
    • Переменная является объектом справочника, который еще не записан в базу данных.

Ключевое отличие: Для "пустой" ссылки ПустоеЗначение() вернет "1", а Выбран() вернет "0". Для "битой" ссылки обе функции вернут "0". Это важный нюанс, который часто является источником путаницы.

Основные причины неожиданного поведения ПустоеЗначение(Контрагент)

Теперь, когда мы понимаем разницу, давайте рассмотрим типичные сценарии, которые могут приводить к ситуации, когда ПустоеЗначение(Контрагент) возвращает "1" для визуально заполненного реквизита, а Контрагент.Выбран() работает как ожидалось.

1. Проблема области видимости переменной (`Перем`)

Одна из наиболее распространенных причин такого поведения в 1С 7.7 связана с некорректной работой с областью видимости переменных. Предположим, у вас на форме обработки есть реквизит с именем "Контрагент". Если в коде какой-либо процедуры вы случайно объявили локальную переменную с тем же именем:


Процедура ПриНажатииКнопки()
    Перем Контрагент; // Здесь мы объявили локальную переменную
    // ... далее по коду, возможно, эта локальная переменная не инициализируется
    // или инициализируется каким-то пустым значением
    Сообщить(ПустоеЗначение(Контрагент)); // Эта строка будет работать с ЛОКАЛЬНОЙ переменной
    Сообщить(Контрагент.Выбран());       // А эта строка, возможно, все равно обращается к реквизиту формы
                                       // или, если локальная переменная пуста, упадет с ошибкой "переменная не выбрана"
КонецПроцедуры

В этом случае, когда вы вызываете ПустоеЗначение(Контрагент), система может оперировать локальной переменной Контрагент, которая действительно пуста или не инициализирована, в то время как реквизит формы с тем же именем остается заполненным. Метод Выбран(), если он применяется к реквизиту формы, будет работать корректно. Этот эффект может быть особенно заметен, если вы копируете или изменяете код, не учитывая объявления Перем.

Чтобы избежать такой ситуации, всегда убедитесь, что вы работаете с нужной переменной. Если вы хотите обратиться к реквизиту формы, не объявляйте локальную переменную с таким же именем в той же процедуре или используйте явное указание контекста (например, Форма.Контрагент, хотя это и не всегда прямолинейно в 7.7).

2. Переменная является объектом справочника, а не ссылкой

Еще одна важная причина заключается в том, что переменная Контрагент может быть не ссылкой на существующий элемент справочника, а объектом справочника, который был создан, но еще не записан в базу данных. Рассмотрим пример:


Процедура СоздатьНовогоКонтрагента()
    Контрагент = СоздатьОбъект("Справочник.Контрагенты");
    Контрагент.Новый();
    Контрагент.Наименование = "Новый Контрагент";
    // ... заполняем другие реквизиты объекта
    // Контрагент.Записать(); // Эту строку мы ЗАБЫЛИ или не вызвали намеренно
    
    Сообщить("ЗначениеВСтрокуВнутр(Контрагент): " + ЗначениеВСтрокуВнутр(Контрагент));
    Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент)); // Может вернуть 1
    Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран());       // Вернет 0, потому что объект не записан
    Сообщить("Контрагент.Вид(): " + Контрагент.Вид());             // Вернет "Справочник.Контрагенты"
КонецПроцедуры

В этом случае переменная Контрагент содержит объект справочника, но поскольку метод Записать() не был вызван, этот "контрагент" еще не существует как полноценный элемент в информационной базе. Объект Справочник, хотя и имеет заполненные поля, с точки зрения базы данных не "выбран" и не является "ссылкой". Для ПустоеЗначение() такой незаписанный объект может интерпретироваться как "пустой" или как не "выбранный" элемент, возвращая "1". Метод Выбран() для такого объекта всегда вернет "0", так как он еще не имеет постоянной ссылки в базе данных.

Если бы мы вызвали Контрагент.Записать(), то Контрагент стал бы ссылкой на существующий элемент, и тогда обе функции (ПустоеЗначение() и Выбран()) работали бы как ожидается.

3. "Пустая" ссылка, ошибочно принятая за заполненную

Возможно, вы или другой участок кода присвоили реквизиту формы "пустую" ссылку (ПолучитьПустоеЗначение("Справочник.Контрагенты")) или не инициализировали его вовсе. Визуально реквизит может казаться пустым или содержать "0" в поле кода, но если вы по какой-то причине думаете, что он заполнен, а он на самом деле нет, то поведение ПустоеЗначение() будет абсолютно корректным.

Методы диагностики проблемы

Чтобы точно определить причину некорректного поведения, нам необходимо получить максимально подробную информацию о состоянии переменной Контрагент в момент выполнения кода. Мы используем для этого несколько полезных функций и методов 1С 7.7, как это было предложено на форуме.

Пошаговая диагностика:

  1. Вставьте отладочный вывод: В то место кода, где возникает проблема, добавьте следующие строки:

Сообщить("--- Отладка Контрагента ---");
Сообщить("ЗначениеВСтрокуВнутр(Контрагент): " + ЗначениеВСтрокуВнутр(Контрагент));
Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент));
Сообщить("ТипЗначенияСтр(Контрагент): " + ТипЗначенияСтр(Контрагент));
// Эти методы применимы только если переменная имеет ссылочный тип (Справочник, Документ, Перечисление)
Если ТипЗначенияСтр(Контрагент) = "Справочник" Тогда
    Сообщить("Контрагент.Вид(): " + Контрагент.Вид());
    Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран());
    Сообщить("Контрагент.ТекущийЭлемент(): " + Контрагент.ТекущийЭлемент());
    // Если Контрагент - это объект, а не ссылка, могут быть дополнительные поля
    // Если Контрагент.Выбран() = 1 Тогда
    //     Сообщить("Контрагент.Код: " + Контрагент.Код);
    //     Сообщить("Контрагент.Наименование: " + Контрагент.Наименование);
    // КонецЕсли;
ИначеЕсли ТипЗначенияСтр(Контрагент) = "Неопределенный" Тогда
    Сообщить("Переменная Контрагент не инициализирована или имеет неопределенный тип.");
КонецЕсли;
Сообщить("-------------------------");

Анализ вывода:

На основе анализа вывода этих функций мы сможем точно установить, что находится в переменной Контрагент, и почему ПустоеЗначение() ведет себя именно так. Если ЗначениеВСтрокуВнутр() содержит ": 0", а ТипЗначенияСтр() - "Справочник", это "пустая" ссылка. Если ТипЗначенияСтр() - "Неопределенный", проблема в области видимости. Если Выбран() - "0", но ЗначениеВСтрокуВнутр() не ": 0", то это, вероятно, незаписанный объект или "битая" ссылка.

Решение проблемы

После того как мы диагностировали истинную причину, мы можем применить соответствующее решение:

  1. Если проблема в области видимости (`Перем`):

    Мы должны убедиться, что всегда обращаемся к нужному реквизиту формы, а не к локальной переменной. Мы просматриваем код процедуры, где возникает проблема, и удаляем объявление Перем Контрагент;, если оно конфликтует с реквизитом формы. Если же локальная переменная необходима, мы даём ей другое, уникальное имя, например, Перем ЛокКонтрагент;. Мы всегда стараемся избегать ситуаций, когда имена локальных переменных совпадают с именами реквизитов формы, чтобы предотвратить такие неясности.

  2. Если переменная является незаписанным объектом справочника:

    Если наша диагностика показала, что переменная Контрагент — это объект, полученный через СоздатьОбъект() и Новый(), но не записанный в базу данных, нам нужно явно вызвать метод Записать(), чтобы сделать его полноценным элементом. Только после этого объект превратится в ссылку на существующий элемент, и функции ПустоеЗначение() и Выбран() начнут работать предсказуемо.

    
    Процедура КорректноеСозданиеКонтрагента()
        Контрагент = СоздатьОбъект("Справочник.Контрагенты");
        Контрагент.Новый();
        Контрагент.Наименование = "Новый Записанный Контрагент";
        Контрагент.Записать(); // Теперь Контрагент - это ссылка на существующий элемент
            
        Сообщить("ПустоеЗначение(Контрагент): " + ПустоеЗначение(Контрагент)); // Вернет 0
        Сообщить("Контрагент.Выбран(): " + Контрагент.Выбран());       // Вернет 1
    КонецПроцедуры
    

    В случае, если нам нужен только объект для временных операций, не предполагающих записи, и мы хотим проверить его "пустоту" относительно наличия данных, то использование ПустоеЗначение() для такого объекта может быть не самым адекватным способом. Вместо этого мы можем проверять заполненность ключевых полей объекта, например, Если ПустаяСтрока(Контрагент.Наименование) = 1 Тогда.

  3. Если переменная содержит "пустую" ссылку:

    В этом случае ПустоеЗначение() работает абсолютно корректно. Проблема не в функции, а в нашем предположении, что реквизит должен быть заполнен. Мы перепроверяем логику присвоения значения реквизиту и убеждаемся, что ему действительно присваивается корректная ссылка на элемент справочника.

Когда использовать ПустоеЗначение(), а когда Выбран()?

Для проверки заполненности ссылочных типов (справочников, документов, перечислений) в 1С 7.7 мы обычно рекомендуем следующий подход:

  1. Для проверки наличия существующего элемента в базе данных, когда нас интересует, ссылается ли переменная на реальный, не удалённый элемент, предпочтительнее использовать метод Выбран(). Он более точно отражает состояние "выбранности" элемента.
  2. 
    Если Контрагент.Выбран() = 1 Тогда
        // Контрагент заполнен и существует в базе
    Иначе
        // Контрагент пуст, не выбран или "бит"
    КонецЕсли;
    
  3. Для проверки, является ли ссылка абсолютно "пустой" (т.е. не ссылается даже на "битый" или удаленный элемент, а именно находится в состоянии значения по умолчанию), мы можем использовать комбинацию или ПустоеЗначение(), но чаще всего Выбран() является более универсальным. Если мы хотим явно отличить пустую ссылку от битой, то это сложнее, но обычно достаточно Выбран().

В большинстве практических задач, когда нам нужно убедиться, что пользователь выбрал какой-то элемент справочника или что ссылка на документ корректна, <Ссылка>.Выбран() является наиболее надежным и однозначным способом проверки.

В завершение, столкнувшись с неожиданным поведением, как в случае с ПустоеЗначение(), всегда применяйте методы отладки с выводом максимальной информации о переменной. Это поможет нам быстро локализовать проблему и принять верное решение, учитывая особенности работы с типами данных в 1С 7.7.

← На главную