Как передать ДокументОбъект и заставить работать форму «Цены и валюта» в управляемом интерфейсе 1С

Программист 1С v8.3 (Управляемые формы) Управленческий учет Промышленность, строительство и АПК
← На главную

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

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

Выясняем причину: почему падает код при простом переносе

Проанализируем типовой код вызова формы цен в обычной форме. Он выглядит следующим образом:


СтруктураЗначений = Ценообразование.ОткрытьФормуЦеныИВалюта(ЭтотОбъект, СтруктураРеквизитовДокумента, ...);

Если мы попытаемся в управляемой форме передать вместо ЭтотОбъект реквизит Объект или ЭтаФорма, система выдаст ошибку. Дело в том, что функция Ценообразование.ОткрытьФормуЦеныИВалюта внутри себя ожидает получить именно прикладной объект типа ДокументОбъект. Ей требуются метаданные документа, его табличные части и реквизиты для выполнения расчетов и заполнения реквизитов формы.

Попытка получить ДокументОбъект на сервере через функцию вида:


&НаСервере
Функция ДокументОбъектВЗначениеНаСервере()
     Возврат РеквизитФормыВЗначение("Объект");
КонецФункции

и передать результат обратно на клиент также обречена на провал в «чистом» управляемом приложении (в Тонком клиенте). Объект типа ДокументОбъект является мутабельным (изменяемым) значением. Такие типы физически не могут существовать на клиенте и не могут быть переданы в качестве возвращаемого значения из серверного вызова на клиент. Платформа мгновенно сгенерирует ошибку передачи данных между клиентом и сервером.

В Толстом клиенте (в режиме управляемого приложения) такой код может «случайно» заработать, так как там грань между клиентом и сервером размыта. Но для полноценной и надежной работы в веб-клиенте или тонком клиенте нам потребуется полностью переписать логику взаимодействия.

Вариант 1. Быстрое решение для Толстого клиента («Читерский» режим)

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

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


&НаСервере
Процедура ДокументОбъектВРеквизитНаСервере(ДокументОбъект)
    ЗначениеВРеквизитФормы(ДокументОбъект, "Объект");    
КонецПроцедуры

В этом случае на клиенте мы получаем объект во временную переменную, открываем форму, получаем результат изменений и сразу же отправляем объект обратно на сервер для обновления данных формы. Однако помните: этот способ не будет работать в Тонком клиенте!

Вариант 2. Правильная архитектура для Тонкого и Веб-клиента

Рассмотрим правильный, методически верный подход. Чтобы обойти ограничение на передачу мутабельных значений, нам необходимо разделить процесс на три логических этапа:

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

Посмотрим на пример реализации этого алгоритма в модуле управляемой формы документа.


&НаКлиенте 
Процедура ДействиеЦеныВалюта(Команда) 
    
    // Шаг 1. Получаем подготовленные параметры и структуры с сервера за один вызов
    Контекст = КонтекстИзмененияЦен();
     
    // Шаг 2. Вызов формы "Цены и валюта"
    // Передаем форму-владелец и параметры. В классической УПП это общая форма.
    СтруктураЗначений = Ценообразование.ОткрытьФормуЦеныИВалюта(
        ЭтотОбъект,
        Контекст.СтруктураРеквизитовДокумента,
        Контекст.ВалютаРегламентированногоУчета,
        Контекст.СтруктураЗапрещенных,
        Контекст.ИмяТабличнойЧасти
    );
                                                    
    // Шаг 3. Если пользователь применил изменения, обрабатываем их на сервере
    Если СтруктураЗначений <> Неопределено Тогда
        ИзменитьЦеныИВалютуНаСервере(Контекст, СтруктураЗначений);
    КонецЕсли; 
    
    УстановитьВидимость(); 
    
КонецПроцедуры 

Теперь детально разберем серверную функцию КонтекстИзмененияЦен. Она собирает все необходимые данные для корректной работы алгоритмов ценообразования:


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

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


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

Важные замечания по асинхронности в платформе 8.3

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

Для этого мы используем объект ОписаниеОповещения. Код вызова формы трансформируется следующим образом:


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

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

Использование связки методов РеквизитФормыВЗначение и ЗначениеВРеквизитФормы совместно с асинхронным открытием окон — это единственный архитектурно верный способ адаптации старых сложных алгоритмов пересчета под стандарты современной платформы 1С.

← На главную