Как автоматически обновлять состояние в документе из регистра сведений и исправить ошибку «Значение не является значением объектного типа»?

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

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

Анализ исходной проблемы и причины возникновения ошибки

Рассмотрим ситуацию: разработчик пытается получить последнее значение из регистра сведений СостояниеЗаказа с помощью встроенного метода ПолучитьПоследнее(). Код в модуле документа выглядит следующим образом:


Процедура ПередЗаписью(Отказ, РежимЗаписи, РежимПроведения)
    // Попытка получить состояние и записать его в реквизит документа
    ТекущееСостояниеЗаказа = ТекСостЗаказа(Дата, Транспорт);
КонецПроцедуры

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

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

  1. Регистр является непериодическим. Метод ПолучитьПоследнее() предназначен исключительно для периодических регистров сведений. Если в свойствах регистра в конфигураторе поле «Периодичность» установлено в значение «Непериодический», этот метод работать не будет или вернет неопределенный результат, так как понятия «последней записи» для такого регистра с точки зрения платформы не существует (все записи равнозначны и определяются только набором измерений).
  2. Запись не найдена. Если регистр периодический, но на указанную дату (или ранее) в базе нет ни одной записи, соответствующей условиям отбора, метод может вернуть пустые значения, и если логика не предусматривает проверку результата, это приведет к ошибке при попытке дальнейшей обработки данных.

Решение №1: Настройка периодичности регистра

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

Разберем по шагам, что нужно сделать в конфигураторе:

  1. Найдем в дереве метаданных наш регистр сведений СостояниеЗаказа.
  2. Перейдем на вкладку «Основные».
  3. В поле «Периодичность» установим значение, отличное от «Непериодический». Для отслеживания перемещений транспорта и быстрой смены статусов лучше всего подходит вариант «В пределах секунды».
  4. Если записи в регистр вносятся документами, убедимся, что выбран правильный «Режим записи» (независимый или подчинение регистратору).

После установки периодичности платформа начнет автоматически поддерживать виртуальную таблицу «СрезПоследних», к которой и обращается метод ПолучитьПоследнее(). Это позволит системе мгновенно находить запись, чья дата является максимальной, но не превышает переданный параметр АктуальнаяДата.

Решение №2: Использование запросов для получения данных

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

Рассмотрим пример кода с использованием запроса к виртуальной таблице СрезПоследних. Этот метод более гибкий и надежный:


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

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

Оптимизация работы в процедуре ПередЗаписью

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

Рассмотрим, как оптимизировать вызов функции:

  1. Проверка модифицированности: Если в документе не изменилась «Машина» и «Дата», возможно, нет смысла перезапрашивать состояние из регистра, если оно уже заполнено.
  2. Индексирование измерений: Убедитесь, что у измерения Машина в регистре сведений стоит галочка «Индексировать». Без индекса поиск по истории в тысячи записей будет приводить к значительным задержкам.
  3. Проблема «Будущего»: При использовании ПередЗаписью важно помнить, что если вы записываете документ текущим временем, а в регистре уже есть запись «будущим числом» (например, планируемое состояние), то ПолучитьПоследнее вернет именно то состояние, которое актуально на секунду документа. Если вам нужно самое последнее состояние «вообще», в качестве периода следует передавать Неопределено или пустую дату.

Важные нюансы и рекомендации

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

Типизация данных: Если ресурс регистра имеет тип ПеречислениеСсылка, то и реквизит документа должен иметь точно такой же тип. Если типы будут различаться (например, в регистре — строка, а в документе — перечисление), при попытке присвоения значения 1С не выдаст ошибку сразу, но данные не будут сохранены корректно.

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


ЗначениеРесурсов = РегистрыСведений.СостояниеЗаказа.ПолучитьПоследнее(Дата, Отбор);
Если ЗначениеРесурсов.Состояние = Неопределено Тогда
    ТекущееСостояниеЗаказа = Перечисления.СостоянияМашин.НеВыехала;
Иначе
    ТекущееСостояниеЗаказа = ЗначениеРесурсов.Состояние;
КонецЕсли;

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

← На главную