Как исправить ошибку «Необъектный тип Регистратора» в 1С при работе с запросами и структурами?

Программист 1С v8.3 (Управляемые формы) 1С:ERP Управление предприятием Бухгалтерский учет
← На главную

При работе с платформой 1С:Предприятие разработчики часто сталкиваются с ошибкой «Необъектный тип». Эта ошибка может проявляться в различных сценариях, но особенно часто возникает при попытке обращения к свойствам или методам переменной, которая не содержит ссылку на объект или имеет специальное значение, указывающее на отсутствие данных, такое как Неопределено, Null или ПустаяСсылка. В данной теме мы подробно разберем, как диагностировать и исправить эту ошибку применительно к полю Регистратор, полученному из запроса или структуры.

Понимание ошибки «Необъектный тип»

Прежде чем перейти к конкретному сценарию с Регистратором, давайте проясним, что означают эти специальные значения и чем они отличаются.

  1. Неопределено: Это специальный тип значения в 1С, который указывает на отсутствие какого-либо значения. Переменная может стать Неопределено, если она была объявлена, но не инициализирована, если происходит обращение к несуществующему свойству объекта или если функция, по задумке, должна вернуть пустое значение. Проверить значение на Неопределено можно с помощью выражения ТипЗнч(Переменная) = Тип("Неопределено") или просто Переменная = Неопределено.
  2. Null: Это значение отличается от Неопределено и чаще всего встречается в результатах запросов к базе данных, особенно при использовании левых или полных соединений, когда в присоединяемой таблице нет соответствующей записи. Null также может быть у реквизитов иерархических справочников для элементов-групп, если реквизит используется только для элементов. Важно помнить, что сравнение любого значения с Null обычным равенством (=) всегда даст Ложь. Для корректной обработки Null в запросах используем конструкции ЕСТЬ NULL или функцию ЕСТЬNULL(Значение, РезультатЕслиNULL). Платформа 1С при получении Null из запроса во встроенном языке обычно преобразует его в Неопределено, но это не всегда гарантировано для всех типов данных и версий платформы.
  3. ПустаяСсылка: Это ссылка на объект метаданных, который существует в конфигурации (например, документ или справочник), но не создан в базе данных. Это не отсутствие значения, а ссылка на несуществующий экземпляр объекта. Пустые ссылки могут использоваться в запросах для сравнения или для вывода в результат. Проверить на пустую ссылку можно с помощью метода Ссылка.Пустая() = Истина или сравнив со значением Значение(Справочник.ИмяСправочника.ПустаяСсылка).

Ошибка «Необъектный тип» возникает, когда мы пытаемся получить свойство или вызвать метод у переменной, которая содержит одно из этих «пустых» значений, но ожидается ссылка на объект или сам объект.

Анализируем исходную проблему: «Необъектный тип Регистратора»

Давайте рассмотрим код, представленный в теме, который вызывает ошибку:

Автор пытается получить Регистратор и Цену из регистра бухгалтерии, используя запрос, а затем упаковать эти данные в структуру:


// Фрагмент функции НайтиПартиюИЦенуНоменклатуры
Запрос1 = Новый Запрос("ВЫБРАТЬ 
| ХозрасчетныйОбороты.Регистратор КАК Регистратор, 
| ХозрасчетныйОбороты.СуммаОборот КАК СуммаОборот, 
| ХозрасчетныйОбороты.КоличествоОборот КАК Количество, 
| ХозрасчетныйОбороты.СуммаОборот / ХозрасчетныйОбороты.КоличествоОборот КАК Цена 
|ИЗ 
| РегистрБухгалтерии.Хозрасчетный.Обороты(, , Авто, Счет = &Счет, &ВидыСубконто, Субконто1 = &Номенклатура, , ) КАК ХозрасчетныйОбороты 
|ГДЕ 
| ХозрасчетныйОбороты.Регистратор ССЫЛКА Документ.ПередачаМатериаловВЭксплуатацию");
// ... установка параметров запроса
РезультатЗапроса = Запрос1.Выполнить();
Выборка = РезультатЗапроса.Выбрать();

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

Затем, в другом месте кода, автор вызывает эту функцию и пытается получить поля из возвращенной структуры:


// ...
Структура = НайтиПартиюИЦенуНоменклатуры(Номенклатура);
Регистратор = Структура.Регистратор; // Здесь возникает ошибка "Необъектный тип"
Цена = Структура.Цена;
// ...

Выявляем причины возникновения ошибки

Проанализируем представленные фрагменты кода и обсуждение на форуме, чтобы выяснить, почему возникает ошибка «Необъектный тип» при попытке обращения к Структура.Регистратор:

  1. Пустой результат запроса или некорректные параметры.

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

    В этом случае, переменная Структура, которая объявлена, но не инициализирована до цикла, так и останется неинициализированной. В контексте 1С это означает, что Структура будет иметь значение Неопределено.

    Когда функция НайтиПартиюИЦенуНоменклатуры возвращает это Неопределено, а вызывающий код пытается обратиться к Неопределено.Регистратор, система выдает ошибку «Необъектный тип», так как у значения Неопределено нет свойства Регистратор.

    Обсуждение на форуме также указывает на противоречие: запрос содержит условие Регистратор ССЫЛКА Документ.ПередачаМатериаловВЭксплуатацию, что подразумевает, что Регистратор всегда должен быть ссылкой на документ. Однако автор всё равно проверяет ТипЗнч(Выборка.Регистратор) = Неопределено. Это может говорить о том, что запрос, возможно, не возвращает ожидаемых данных, или что данные, даже если они есть, по каким-то причинам приводят к Неопределено на стороне 1С (например, если NULL из базы данных трансформируется в Неопределено, а такое может быть в более сложных случаях, хотя для ссылочных типов это менее типично).

  2. Неверное расположение кода инициализации структуры внутри функции НайтиПартиюИЦенуНоменклатуры.

    Даже если запрос вернул бы данные, в исходном коде функции НайтиПартиюИЦенуНоменклатуры содержится логическая ошибка, которая не позволит переменной Структура быть инициализированной:

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

    Давайте пошагово разберем эту логику:

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

    Таким образом, переменная Структура внутри этой функции никогда не будет инициализирована, что делает ее всегда Неопределено при выходе из функции. Это и есть основная причина ошибки «Необъектный тип» в данном конкретном случае.

  3. Необработка возвращаемого значения из функции.

    Вызывающий код Регистратор = Структура.Регистратор; предполагает, что Структура всегда будет корректным объектом типа Структура. Однако, как мы выяснили, функция НайтиПартиюИЦенуНоменклатуры всегда возвращает Неопределено. Отсутствие проверки на Неопределено перед использованием результата функции также приводит к ошибке.

Предлагаемые решения и способы исправления

Для исправления ошибки «Необъектный тип Регистратора» мы рекомендуем комплексный подход, который включает в себя проверку запросов, корректировку логики инициализации переменных и надежную обработку возвращаемых значений.

  1. Проверяем параметры запроса и результаты

    Начнем с проверки самого запроса. Убедимся, что он возвращает ожидаемые данные. Для этого используем консоль запросов 1С или отладчик.

    Шаги по проверке:

    1. Убедитесь, что все параметры запроса (&Счет, &ВидыСубконто, &Номенклатура) корректно установлены перед выполнением запроса.
    2. Выполните запрос в консоли запросов с теми же параметрами, чтобы убедиться, что он возвращает хоть какие-то строки.
    3. В коде, после выполнения запроса, явно проверьте, содержит ли РезультатЗапроса какие-либо строки.

    Пример кода для проверки результата запроса:

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

    Эта предварительная проверка позволяет отловить ситуацию, когда запрос просто не находит данных, и заранее предотвратить попытку работы с пустой выборкой.

  2. Корректно размещаем код инициализации структуры

    Основная логическая ошибка в исходном коде заключалась в неправильном расположении оператора создания Структура. Мы должны перенести его так, чтобы он выполнялся, когда мы действительно хотим создать структуру.

    Пример исправления логики внутри функции НайтиПартиюИЦенуНоменклатуры:

    
    // Инициализируем структуру значением Неопределено до цикла.
    // Если цикл не выполнится или данные не будут найдены,
    // функция вернет именно Неопределено.
    Перем Структура; 
    Структура = Неопределено; 
    
    Выборка = РезультатЗапроса.Выбрать();
    Пока Выборка.Следующий() Цикл
        // Если мы дошли до сюда, значит, в выборке есть строка.
        // Теперь нам нужно убедиться, что Регистратор не Неопределено,
        // если мы хотим его использовать.
        // Исходную проверку: Если ТипЗнч(Выборка.Регистратор) = Неопределено Тогда Продолжить;
        // можно оставить, если в теории Регистратор может быть Неопределено
        // даже при наличии строки выборки, но с учетом ГДЕ ССЫЛКА это маловероятно.
        // В случае наличия ГДЕ ССЫЛКА эту проверку можно убрать,
        // так как Регистратор будет ссылочным типом.
                
        // Если ТипЗнч(Выборка.Регистратор) = Неопределено Тогда
        //     Продолжить;
        // КонецЕсли;
    
        // Теперь создаем структуру корректно, вне "Если" блока,
        // который раньше мешал ее инициализации.
        Структура = Новый Структура("Регистратор, Цена", Выборка.Регистратор, Выборка.Цена);
                
        // Если нам нужна только первая найденная запись, то можно прервать цикл.
        Прервать; 
    КонецЦикла;
    
    Возврат Структура;
    

    В этом исправленном коде:

    • Переменная Структура объявляется и инициализируется как Неопределено до начала цикла.
    • Если цикл Пока Выборка.Следующий() не будет выполнен (из-за пустого результата запроса), функция НайтиПартиюИЦенуНоменклатуры корректно вернет Неопределено.
    • Если цикл выполняется, и мы находим строку, то Структура будет создана и заполнена данными из Выборка.Регистратор и Выборка.Цена.
    • Оператор Прервать; используется, если функция должна вернуть только первую найденную партию и цену. Если требуется получить все партии, этот оператор следует убрать, а функция должна возвращать, например, массив структур или коллекцию.

    Таким образом, мы гарантируем, что если данные найдены, Структура будет объектом, а если нет — Неопределено, что является предсказуемым поведением.

  3. Обрабатываем возвращаемое значение из функции НайтиПартиюИЦенуНоменклатуры

    Даже после исправления логики внутри функции, вызывающий код должен быть готов к тому, что функция может вернуть Неопределено, если данные не были найдены. Всегда выполняйте проверку на Неопределено перед попыткой доступа к свойствам возвращенного значения.

    Пример кода для обработки возвращаемого значения:

    
    // ...
    Структура = НайтиПартиюИЦенуНоменклатуры(Номенклатура);
            
    Если Структура = Неопределено Тогда
        // Обрабатываем ситуацию, когда партия и цена для данной номенклатуры не найдены.
        Сообщить("Не удалось найти партию и цену для номенклатуры: " + Номенклатура);
        Продолжить; // Пропускаем текущую строку в объемлющем цикле,
                    // если для данной номенклатуры не найдено партии.
    КонецЕсли;
    
    // Если мы дошли до сюда, значит, Структура содержит корректные данные.
    // Теперь безопасно обращаемся к полям структуры.
    Регистратор = Структура.Регистратор;
    Цена = Структура.Цена;
    // ... далее используем Регистратор и Цена
    

    Эта проверка гарантирует, что мы не будем пытаться обратиться к свойству Регистратор у значения Неопределено, тем самым предотвращая ошибку «Необъектный тип».

Используем отладчик для диагностики

Для выявления точной причины ошибки «необъектный тип» критически важно использовать отладчик 1С. Отладка позволяет пошагово выполнять код, просматривать значения переменных, стек вызовов и вычислять выражения в «Табло» в момент возникновения ошибки. Это помогает определить, на каком именно шаге и с каким значением переменной возникает Неопределено, Null или ПустаяСсылка, приводящие к ошибке «необъектного типа».

Рекомендации по использованию отладчика:

  1. Установите точки останова. Разместите точку останова непосредственно перед строкой, где возникает ошибка (например, Регистратор = Структура.Регистратор;). Также установите точки останова в начале функции НайтиПартиюИЦенуНоменклатуры и внутри цикла Пока Выборка.Следующий().
  2. Пошаговое выполнение. Запустите выполнение кода в режиме отладки и пошагово продвигайтесь по коду.
  3. Наблюдение за переменными. В окне «Переменные» отслеживайте значения ключевых переменных:
    • РезультатЗапроса: Проверьте его на метод .Пустой().
    • Выборка: После Выборка.Следующий() убедитесь, что она позиционировалась, и что Выборка.Регистратор содержит ожидаемое значение (например, ссылку на документ, а не Неопределено).
    • Структура: До и после выполнения строки Структура = Новый Структура(...) убедитесь, что она корректно инициализировалась.
    • Аргументы функции: Проверьте, какие значения передаются в функцию НайтиПартиюИЦенуНоменклатуры (например, параметр Номенклатура).
  4. Использование «Табло». В окне «Табло» вычисляйте выражения, такие как ТипЗnч(Выборка.Регистратор), Выборка.Регистратор.Пустая(), Структура = Неопределено, чтобы получить немедленную информацию о типе и состоянии переменных.

Тщательная отладка позволит вам точно определить, на каком этапе и по какой причине переменная, которая должна быть объектом, приобретает значение Неопределено, Null или ПустаяСсылка, что и приводит к ошибке «Необъектный тип».

Таким образом, комбинируя правильную постановку запросов, корректную логику инициализации переменных и надежную обработку возвращаемых значений, а также активно используя отладчик, мы сможем эффективно устранить ошибку «Необъектный тип Регистратора» и сделать наш код более надежным.

← На главную