Как получить статус из регистра сведений для документов разных типов и избежать типичных ошибок программного кода

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

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

Разбор основной ошибки: Объект против Ссылки

Проанализируем ситуацию, возникшую у автора темы. При попытке проверить соответствие документа в выборке запроса использовался следующий код:


Если РасходнаяНакладная.Объект.Ссылка = Выборка.Документ Тогда
    Возврат Выборка.Статус;
КонецЕсли;

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

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

Правильная реализация функции получения статуса

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


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

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

Обработка нескольких документов в одном запросе

Если нам необходимо получить статусы сразу для двух и более документов (например, в рамках одной обработки или отчета), не следует вызывать функцию в цикле. Это приведет к избыточному количеству обращений к базе данных. Проанализируем вариант с использованием массива:


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

Этот подход позволяет одним запросом получить данные для неограниченного количества документов разных типов (и «Приходных», и «Расходных»), так как измерение регистра сведений имеет составной тип.

Универсальный подход через метаданные

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


Функция ПолучитьЗначениеИзРегистраУниверсально(Документ) Экспорт
    
    // Проверяем, что передана ссылка, а не объект
    Если НЕ ЗначениеЗаполнено(Документ) Тогда
        Возврат Неопределено;
    КонецЕсли;
    
    ИмяРегистра = "СтатусыДокументов";
    Запрос = Новый Запрос;
    
    // Используем СтрШаблон для динамического формирования текста (если нужно менять имя регистра)
    Запрос.Текст = СтрШаблон(
        "ВЫБРАТЬ Статус ИЗ РегистрСведений.%1.СрезПоследних(&Дата, Документ = &Документ)", 
        ИмяРегистра
    );
    
    Запрос.УстановитьПараметр("Дата", ТекущаяДата());
    Запрос.УстановитьПараметр("Документ", Документ);
    
    Выборка = Запрос.Выполнить().Выбрать();
    Если Выборка.Следующий() Тогда
        Возврат Выборка.Статус;
    КонецЕсли;
    
    Возврат Неопределено;
    
КонецФункции

Рекомендации по оптимизации и безопасности кода

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

  1. Индексация измерений: Убедитесь, что в регистре сведений «Статусы документов» для измерения «Документ» установлено свойство Индексировать. Поскольку тип измерения составной, отсутствие индекса может привести к полному сканированию таблицы (Full Scan) при каждом запросе статуса.
  2. Проверка на пустые значения: Всегда проверяйте входящий параметр на ЗначениеЗаполнено(). Передача пустой ссылки в параметры виртуальной таблицы может вернуть неожиданный результат или вызвать ошибку выполнения запроса.
  3. Использование оператора ССЫЛКА: Если в одном запросе вам все же нужно разделить логику обработки «Приходных» и «Расходных» накладных, используйте в ГДЕ или ВЫБОР конструкцию Документ ССЫЛКА Документ.ПриходнаяНакладная. Это работает быстрее, чем анализ строковых имен метаданных.
  4. Вызов с клиента: Помните, что запросы выполняются только на сервере. Поэтому из формы (клиента) мы должны вызывать функцию общего модуля, который имеет признак Сервер. При вызове передаем Объект.Ссылка, а не ЭтотОбъект.

Подведем итог: для корректного получения статусов для разных документов необходимо использовать запросы к виртуальным таблицам регистра сведений, передавать в параметры только Ссылки (DocumentRef) и избегать обращений к свойствам Объект внутри серверных функций. Следуя этим правилам, вы обеспечите стабильную и быструю работу вашей конфигурации.

← На главную