Как корректно обратиться к вложенному реквизиту формы, такому как Упаковка.Вес, в 1С:Предприятие 8.3 Управляемые формы?

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

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

Предположим, у нас есть форма документа или обработки с табличной частью Товары. Каждая строка этой табличной части имеет реквизит Упаковка, который является ссылкой на элемент справочника "Упаковки", а уже у этого справочника есть реквизит Вес. Наша задача – получить значение этого Веса для каждой строки табличной части, например, для пересчета общей массы или вывода в сообщение.

Понимание контекста: Клиент и Сервер в 1С Управляемых Формах

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

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

Поэтому, если нам нужно получить данные из вложенного объекта, который является ссылочным типом (например, СправочникСсылка.Упаковки), нам необходимо выполнять этот код на сервере. Попытка обратиться к Товара.Упаковка.Вес напрямую на клиенте приведет к ошибке "Объект не найден" или "Поле объекта не обнаружено", потому что клиент не "знает" о реквизите Вес у ссылки.

Корректное обращение к табличной части формы

Для начала работы с табличной частью нам нужно правильно к ней обратиться. В управляемой форме табличная часть обычно является реквизитом формы, который может быть связан с основным объектом формы (например, документом). Если табличная часть привязана к основному реквизиту формы (например, к документу), то к ней можно обратиться через свойство Объект формы.

Например, если основной реквизит формы — это документ, и у него есть табличная часть Товары, то мы обращаемся к ней так:

ТабЧасть = Объект.Товары;

Если же табличная часть является отдельным реквизитом формы, не связанным напрямую с основным объектом, то для передачи ее данных на сервер нам понадобится функция РеквизитФормыВЗначение():


// На клиенте
ПереданныеДанные = РеквизитФормыВЗначение("Товары"); 

// Затем на сервере мы можем работать с ПереданныеДанные
// ...

Однако в большинстве случаев, когда мы работаем с табличными частями документов или справочников, привязанных к основному реквизиту формы, подход с Объект.Товары будет наиболее прямым и корректным.

Пошаговое решение: Передача данных на сервер и обработка

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

  1. Создаем клиентскую процедуру

    На клиенте мы только инициируем процесс, вызывая серверную функцию. Результат работы серверной функции (в нашем случае – массив весов) мы затем можем обработать или вывести.

    
    &НаКлиенте
    Процедура ПолучитьИВывестиВесУпаковок(Команда)
        // Вызываем серверную функцию для получения массива весов
        МассивВесовУпаковок = ПолучитьВесУпаковокНаСервере();
    
        // Обрабатываем полученные данные на клиенте
        Если МассивВесовУпаковок.Количество() > 0 Тогда
            Сообщить("Полученные веса упаковок:");
            Для Каждого Вес Из МассивВесовУпаковок Цикл
                Сообщить("   - " + Вес + " кг");
            КонецЦикла;
        Иначе
            Сообщить("В табличной части 'Товары' не найдено заполненных упаковок или произошла ошибка.");
        КонецЕсли;
    КонецПроцедуры
    

    Мы используем директиву &НаКлиенте для указания контекста выполнения. Процедура может быть привязана к кнопке на форме или вызываться по другому событию.

  2. Реализуем серверную функцию

    Теперь рассмотрим саму серверную функцию, где и будет происходить основная логика доступа к вложенному реквизиту. Здесь мы будем работать с основным реквизитом формы Объект.

    
    &НаСервере
    Функция ПолучитьВесУпаковокНаСервере()
        МассивВесов = Новый Массив; // Создаем массив для сбора весов
    
        // Получаем табличную часть "Товары" из основного объекта формы
        // Здесь "Объект" ссылается на сам объект конфигурации (например, ДокументОбъект),
        // который привязан к форме и содержит табличную часть "Товары".
        ТабЧастьТовары = Объект.Товары; 
    
        // Перебираем каждую строку табличной части
        Для Каждого СтрокаТовара Из ТабЧастьТовары Цикл
            // Важно: Прежде чем обращаться к реквизитам вложенного объекта,
            // всегда проверяйте, что сам вложенный объект не Неопределено 
            // и не является пустой ссылкой.
            Если Не СтрокаТовара.Упаковка = Неопределено И Не СтрокаТовара.Упаковка.Пустая() Тогда
                Попытка
                    // Если "Упаковка" является ссылочным типом (например, СправочникСсылка.Упаковки),
                    // нам необходимо получить сам объект этого справочника, чтобы обратиться к его реквизитам.
                    ОбъектУпаковки = СтрокаТовара.Упаковка.ПолучитьОбъект();
                            
                    // Теперь, когда у нас есть сам объект упаковки, мы можем безопасно обратиться к его реквизиту "Вес"
                    МассивВесов.Добавить(ОбъектУпаковки.Вес);
                Исключение
                    // Обработка возможной ошибки при получении объекта, например, если ссылка битая
                    Сообщить("Ошибка при получении объекта упаковки для строки " 
                             + СтрокаТовара.НомерСтроки + " (" + СтрокаТовара.Номенклатура + "): " 
                             + ОписаниеОшибки());
                    МассивВесов.Добавить(0); // Добавляем 0 или другое значение по умолчанию при ошибке
                КонецПопытки;
            Иначе
                // Если реквизит "Упаковка" не заполнен или пуст, 
                // добавляем 0 или другое значение по умолчанию
                МассивВесов.Добавить(0); 
            КонецЕсли;
        КонецЦикла;
    
        Возврат МассивВесов; // Возвращаем собранный массив весов на клиент
    КонецФункции
    

    Здесь &НаСервере указывает, что эта функция будет выполняться на сервере 1С:Предприятия. Это позволяет нам работать с Объект (основным реквизитом формы) и вызывать метод ПолучитьОбъект() для ссылочных типов.

Важные моменты и дополнительные проверки

Для обеспечения надежности и корректной работы кода, мы должны учитывать несколько критически важных аспектов:

  1. Проверка на Неопределено и Пустая()

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

    
    Если Не СтрокаТовара.Упаковка = Неопределено И Не СтрокаТовара.Упаковка.Пустая() Тогда
        // ... работаем с Упаковка
    Иначе
        // ... обрабатываем ситуацию, когда Упаковка не заполнена
    КонецЕсли;
    

    Без этой проверки, если в какой-либо строке табличной части реквизит Упаковка будет Неопределено, попытка выполнить СтрокаТовара.Упаковка.Пустая() вызовет ошибку "Поле объекта не обнаружено", так как у Неопределено нет метода Пустая(). Аналогично, после этой проверки, если Упаковка не является пустой ссылкой, мы можем перейти к следующему шагу.

  2. Разыменование ссылочных типов: Метод ПолучитьОбъект()

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

    
    ОбъектУпаковки = СтрокаТовара.Упаковка.ПолучитьОбъект();
    Сообщить(ОбъектУпаковки.Вес);
    

    Этот метод возвращает объект базы данных (например, СправочникОбъект.Упаковки), у которого уже есть доступные реквизиты, такие как Вес. Важно помнить, что вызов ПолучитьОбъект() может быть относительно ресурсоемким, так как он обращается к базе данных. Если вы работаете с большим количеством строк, стоит рассмотреть возможность использования запросов для оптимизации производительности.

  3. Тип реквизита Упаковка

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

    • Если Упаковка - это СправочникСсылка или ДокументСсылка, используем ПолучитьОбъект().
    • Если Упаковка - это Структура или Соответствие, доступ к Вес может быть через СтрокаТовара.Упаковка.Вес (если это свойство структуры) или СтрокаТовара.Упаковка["Вес"] (если это ключ соответствия).
    • Если Упаковка - это составной тип данных, который может включать как ссылку, так и, например, число, то логика обращения должна учитывать все возможные типы.

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

  4. Использование отладчика

    Когда вы сталкиваетесь с подобными ошибками, самым эффективным инструментом является отладчик 1С. Мы рекомендуем:

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

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

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

Если наша задача – не просто вывести вес, а перемножить его на количество товара и записать результат в другой реквизит табличной части (например, ТоварыУпаковкаВес), то логика внутри серверной функции будет немного отличаться:


&НаСервере
Процедура ПересчитатьИЗаполнитьВесУпаковокНаСервере()
    // Получаем табличную часть "Товары" из основного объекта формы
    ТабЧастьТовары = Объект.Товары; 

    // Перебираем каждую строку табличной части
    Для Каждого СтрокаТовара Из ТабЧастьТовары Цикл
        ПеременныйВесУпаковки = 0; // Значение по умолчанию

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

        // Перемножаем Вес на Количество и заполняем реквизит "ТоварыУпаковкаВес"
        // Убедитесь, что реквизит "Количество" существует в табличной части "Товары"
        // и реквизит "ТоварыУпаковкаВес" также существует и имеет подходящий тип (например, Число)
        СтрокаТовара.ТоварыУпаковкаВес = ПеременныйВесУпаковки * СтрокаТовара.Количество; 
    КонецЦикла;
КонецПроцедуры

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

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

Таким образом, мы видим, что успешный доступ к вложенным реквизитам в управляемых формах 1С требует понимания клиент-серверной архитектуры, аккуратной работы со ссылочными типами данных и тщательных проверок на заполненность объектов. Используя предложенные подходы и активно применяя отладчик, вы сможете эффективно решать подобные задачи.

← На главную