Переписывать процедуры и функции на аналогичные но с клиент-серверной технологией достаточно не простая работа, тем более что так можно переписать большую часть конфигурации.И времени потребуется не одна сотня а может быть и тычячи друдочасов, при этом появятся различные ошибки и недочеты, отслеживание и исправление который займет не меньше времени.
Данными примерами я хочу показать как можно с наименьшим количеством трудозатрат и изменений кода реализовать управляемый интерфейс, для уже написаной конфигурации. Конечно эта технология не будет оптимальной, и в дальнейшем ее нужно переписать. Однако это позволит в короткие сроки создать управляемый интерфейс для уже существующих документовb справочников.
Я не буду писать о том как создавать сам интерфейс управляемой формы, единственное, для упрощения переноса функционала интерфейса я стараюсь расположить элементы формы так-же как и в обычном приложении, реквизиты формы назваю один в один. Это позволяет копировать уже имеющие процедуры и функции изменив только необходимые отличия обращений к объектам формы.
Например в толстом клиенте все элементы формы находяться в объекте с названием ЭлементыФормы, а в управляемом интерфейсе это будет Элементы.
Приведу пример процедуры из конфигурации УТ 10.3:
Процедура ПриИзмененииКонтрагента() Экспорт // Выполняем общие действия для всех документов при изменении Контрагент. ЗаполнениеДокументов.ПриИзмененииЗначенияКонтрагента(ЭтотОбъект, мСтруктураПараметровДляПолученияДоговора); Если НЕ ЗначениеЗаполнено(КонтактноеЛицоКонтрагента) Тогда КонтактноеЛицоКонтрагента = Контрагент.ОсновноеКонтактноеЛицо; Иначе Если КонтактноеЛицоКонтрагента.Владелец Контрагент Тогда КонтактноеЛицоКонтрагента = Контрагент.ОсновноеКонтактноеЛицо; КонецЕсли; КонецЕсли; КонецПроцедуры // ПриИзмененииКонтрагента()
Данная процедура находится в модуле формы документов. Скопировав код данной процедуры в модуль управляемой формы. в начале процедуры необходимо добавить строчку:
ЭтотОбъект = РеквизитФормыВЗначение("Объект");
Проще говоря этой функцией мы получаем привычный(или правильнее сказать обычный для работы в толстом клиенте) нам объект, а не доступный ДанныеФормыСтруктура, Да и еще вызов функции РеквизитФормыВЗначение доступен только на сервере поэтому перед объявлением нашей процедуры нужно поставить директиву компиляции &НаСервере, после этого у полученного объекта можно вызывать экспортные процедуры и функции расположенные в модуле объекта.
Например:
ЭтотОбъект = РеквизитФормыВЗначение("Объект"); мСуммаВсего = ЭтотОбъект.ПолучитьСуммуСНДС();
В конце выше приведенной процедуры, нужно будет вернуть данный объект на форму сделать это можно процедурой ЗначениеВРеквизитФормы:
ЗначениеВРеквизитФормы(ЭтотОбъект, "Объект")
И еще поскольку теперь нельзя обращаться на прямую к реквизитам формы или объекта, Нам перед каждым реквизитом нужно вставить слово ЭтотОбъект, в результате мы получили такую процедуру:
&НаСервере Процедура ПриИзмененииКонтрагента() Экспорт ЭтотОбъект = РеквизитФормыВЗначение("Объект"); // Выполняем общие действия для всех документов при изменении Контрагент. ЗаполнениеДокументов.ПриИзмененииЗначенияКонтрагента(ЭтотОбъект, мСтруктураПараметровДляПолученияДоговора); Если НЕ ЗначениеЗаполнено(ЭтотОбъект.КонтактноеЛицоКонтрагента) Тогда ЭтотОбъект.КонтактноеЛицоКонтрагента = ЭтотОбъект.Контрагент.ОсновноеКонтактноеЛицо; Иначе Если ЭтотОбъект.КонтактноеЛицоКонтрагента.Владелец ЭтотОбъект.Контрагент Тогда ЭтотОбъект.КонтактноеЛицоКонтрагента = ЭтотОбъект.Контрагент.ОсновноеКонтактноеЛицо; КонецЕсли; КонецЕсли; КонецПроцедуры // ПриИзмененииКонтрагента()
Согласитесь, что данная методика чем-то напоминает создание аналогичной внешней печатной формы, когда перед всеми рекизитами документа добавляется ДокументСсылка.
К сожалению данный метод не панацея т.к. работать во всех случаях не будет, например если вызываемый модуль содержит директивы компиляции #Если Клиент Тогда, то они ни когда не будут отработаны, т.к. в данном случае эта процедура будет всегда выполняться на сервере. И как правило в таких местах располагаются интерактивное взаимодействие с клиентом, т.е вывод сообщений или задаются уточняющие вопросы.
В таких случаях можно создать аналогичный модуль добавив в конце наименование слово “Клиент”, выполняемый только на клиенте и скопировав туда данную функцию, необходимо внести соответвующие изменения.
Например поскольку данная процедура или функция будет вызываться на клиенте то работать с объектом ДокументОбъект у нас нет возможности, у нас есть только объект типа ДанныеФормыСтруктура, а у данного объекта мы можем обращаться только к реквизитам “первого уровня”.
поэтому если в процедуре или функции встречается такой код:
Документ.ДоговорКонтрагента.ВидВзаиморасчетов,
его следует переписать на:
ОбщегоНазначения.ПолучитьРеквизитЭлемента(Документ.ДоговорКонтрагента,”ВидВзаиморасчетов”)
Предварительно добавив данную функция в общий модуль(пример кода этой функции приводить не буду т.к. ее можно найти в БСП) Так же в процедурах попадается сравнение с типом, это конструкции вида
ТипЗнч(ДокументОбъект) = Тип("ДокументОбъект.ОтчетОРозничныхПродажах")
данный код можно переписать на:
ТипЗнч(ДокументОбъект.Ссылка) = Тип("ДокументСсылка.ОтчетОРозничныхПродажах") И ТипЗнч(ДокументОбъект) = Тип("ДанныеФормыСтруктура")
Если в коде попадается обращение к перечислениям, то у нас есть два решения: если обращение встречается несколько раз, то что-бы уменьшить количество обращений к серверу можно написать функцию, которая вернет все значения перечисления и тогда
конструкцию:
Если ВидСклада = Перечисления.ВидыСкладов.Оптовый Тогда
можно переписать так
ВидыСкладов = ОбщегоНазначения.ПолучитьСтруктуруПеречислений("ВидыСкладов") ….... Если ВидСклада = ВидыСкладов.Оптовый Тогда
А в общий модуль вставим процедуру:
Функция ПолучитьСтруктуруПеречислений(ИмяПеречисления) Экспорт СписокЗначений = Новый Структура(); Для Каждого СтрокаЗначения Из Метаданные.Перечисления[ИмяПеречисления].ЗначенияПеречисления Цикл СписокЗначений.Вставить(СтрокаЗначения.Имя, Перечисления[ИмяПеречисления][СтрокаЗначения.Имя]); КонецЦикла; Возврат СписокЗначений; КонецФункции // ПолучитьСтруктуруПеречислений()
Если же обращение происходит всего один раз, тогда можно использовать и другой вариант, это обращение у предопределенной функции ПредопределенноеЗначение - пример использования приведен ниже.
Если в коде процедуры или функции происходит обращение к пустой ссылке или к предопределенному значению, а так-же к значениям перечислений, например
Если Номенклатура = Справочники.Номенклатура.ПустаяСсылка Тогда
то можно переписать код на
Если Номенклатура = ПредопределенноеЗначение("Справочник.Номенклатура.ПустаяСсылка") Тогда
подробнее об этой Функции можно почитать в описании синтакс-помошника.
Так же в типовых конфигурациях очень часто вызывается обращение к Метаданным что на клиенте впринципе невозможно, как правило эти обращения связаны чтобы проверить есть ли в переданном объекте тот или иной реквизит, т.е если встречается такой код:
МетаданныеДокумента = ДокументОбъект.Метаданные(); …. Если ОбщегоНазначения.ЕстьРеквизитДокумента("СкладОрдер", МетаданныеДокумента) Тогда
одним из решений будет получить структуру реквитов объекта. Для этого например в общем модуле вставляем функцию:
//Возвращает структуру переданного объекта // Функция ПолучитьРеквизитыДокумента(Объект) Экспорт МетаданныеОбъекта = Новый Структура; Для Каждого РеквизитОбъекта Из Объект.Метаданные().Реквизиты Цикл МетаданныеОбъекта.Вставить(РеквизитОбъекта.Имя,РеквизитОбъекта.Имя); КонецЦикла; ТабличныеЧасти = Новый Структура; Для Каждого РеквизитОбъекта Из Объект.Метаданные().ТабличныеЧасти Цикл МетаданныеТЧ = Новый Структура; Для Каждого РеквизитОбъектаТЧ Из Объект.Метаданные().ТабличныеЧасти[РеквизитОбъекта.Имя].Реквизиты Цикл МетаданныеТЧ.Вставить(РеквизитОбъектаТЧ.Имя,РеквизитОбъектаТЧ.Имя); КонецЦикла; ТабличныеЧасти.Вставить(РеквизитОбъекта.Имя,МетаданныеТЧ); КонецЦикла; МетаданныеОбъекта.Вставить("ТабличныеЧасти",ТабличныеЧасти); Возврат МетаданныеОбъекта; КонецФункции
В результате нам необходимо предварительно получить структуру данного объекта и переписать код на:
МетаданныеДокумента = ОбщегоНащначения.ПолучитьРеквизитыДокумента(Объект) ... Если МетаданныеДокумента.Свойство("СкладОрдер") Тогда
для конструкций вида:
Метаданные.НайтиПоТипу(ТипЗнч(СсылкаНаОбъект)).Имя
можно опять написать функцию в общем модуле:
Функция НайтиПоТипу(Значение) Экспорт Возврат Метаданные.НайтиПоТипу(ТипЗнч(Значение)).Имя; КонецФункции
и изменить код на
ОбщегоНазначения.НайтиПоТипу(СсылкаНаОбъект)
Так же очень часто в коде выполняются запросы, поскольку выполнение запросов возможно только на сервере, то данный кусок кода необходимо вынести например в новый модуль выполняемый на сервере а для простоты запоминания можно назвать его аналогично поставив в конце наименования слово “Сервер”
В заключение хочу повторить что данная методика не совсем оптимальна для постоянной работы , т.к в данном случае идет частое обращение с клиента на сервер, однако она позволяет в короткие сроки разработать управляемый интерфейс с аналогичным функционалом. Конечно в дальнейшем будет необходимо оптимизировать данные процедуры и функции согласно общим рекомендациям, т.е получение всех необходимых данных за одно обращение к серверу. например обратившись в начале процедуры на сервер и получив все необходимые реквизиты и значения:
&НаСервере Функция ПолучитьСтруктуруРеквизитовДокумента(); СтруктураПараметров = новый Структура(); СтруктураПараметров.Вставить("СпособЗаполненияЦен",Перечисления.СпособыЗаполненияЦен.ПоЦенамНоменклатуры); СтруктураПараметров.Вставить("МетаданныеДокумента",ОбщегоНазначения.ПолучитьРеквизитыДокумента(ЭтотОбъект)); ….. Возврат СтруктураПараметров; КонецФункции