Как получить Объект на клиенте по ссылке для использования в типовых процедурах

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

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

Почему нельзя просто получить Объект на клиенте?

Для начала разберем архитектурную причину проблемы. В управляемом приложении существует четкое разделение между данными в базе и их представлением на экране:

  1. ДокументСсылка — это лишь идентификатор записи в базе данных.
  2. ДокументОбъект — это тяжелый объект для чтения и записи, который существует только на стороне сервера.
  3. ДанныеФормыСтруктура (тот самый Объект на клиенте) — это проекция данных, оптимизированная для передачи по сети и отображения в форме.

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

Решение 1: Эмуляция объекта через Структуру (Duck Typing)

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

Рассмотрим пример формирования такой структуры на сервере для последующей передачи в клиентскую процедуру:


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

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

Решение 2: Перенос логики на сторону сервера

Это самый правильный и профессиональный подход. Если вам нужно обработать массив ссылок, делать это на клиенте крайне непроизводительно из-за постоянных вызовов сервера в цикле. Вместо того чтобы пытаться вызвать процедуру ФинансыКлиент.ОчиститьСтроки, выясним, что она делает, и найдем (или создадим) ее серверный аналог.

Разберем алгоритм действий:

  1. Откройте код клиентской процедуры, которую вы хотите вызвать.
  2. Проанализируйте, какие изменения она вносит в данные.
  3. Создайте свою процедуру в серверном модуле вашей обработки или расширения.
  4. Реализуйте ту же логику, но уже работая с типом ДокументОбъект.

Пример адаптации логики:


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

Решение 3: Использование ЗначениеВДанныеФормы (в контексте формы)

Если ваша задача выполняется внутри какой-то обработки, у которой есть форма, и вам нужно временно загрузить данные документа в реквизит формы, чтобы отработали стандартные механизмы, используйте метод ЗначениеВДанныеФормы. Рассмотрим этот метод подробнее.

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


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

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

Резюме и рекомендации

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

← На главную