Что такое объект
Форма – это, в принципе, объект, некая изолированная логически сущность, у которой есть:
-
свойства, описывающие состояние;
-
методы, описывающие поведение этой сущности;
-
и события для реагирования на какие-то сообщения от других внешних объектов.
Все объекты относятся к какому-то классу.
В 1С используется не чистое объектно-ориентированное программирование, но объекты здесь тоже есть:
-
в дереве метаданных конфигурации каждая ветка представляет собой набор объектов.
-
«Обработки» я выделил на слайде справа как универсальный объект, который используется для решения общих задач, потому что он не привязан к каким-то конкретным объектам базы данных – это отдельная логическая сущность.
Универсальные объекты в 1С
В 1С у нас есть два универсальных объекта – это обработка и форма.
Обработка есть только на сервере. Ее никак нельзя создать на клиенте, с ней можно работать только на сервере. У нее есть:
-
свойства – реквизиты, переменные;
-
методы, которые можно описать в модуле объекта;
-
событий у нее нет (она не может обрабатывать события);
-
и конструктора у нее тоже отдельного нет – если нужно как-то проинициализировать новый объект, нужно писать отдельную процедуру с параметрами.
Это все только на сервере. А если нужно такое на клиенте? Для этого есть форма.
Форма – это такой же объект, имеющий:
-
свойства – реквизиты, переменные;
-
методы;
-
также у нее есть события в виде обработчиков;
-
и конструктор «ПриСозданииНаСервере», где можно передать параметры.
Странно, что я говорю, что форма существует на клиенте, но при этом у нее есть конструктор «ПриСозданииНаСервере». Да, это такая двойственная сущность форм, что они одновременно есть и на клиенте, и на сервере. Чуть позже коснемся этого подробнее.
Методы формы
Отдельно о методах формы.
Здесь я создал форму и в модуле написал все варианты методов – НаКлиенте, НаСервере, НаСервереБезКонтекста и НаКлиентеНаСервереБезКонтекста:
-
каждый из этих методов представлен здесь в публичном и приватном варианте (объявленный с ключевым словом Экспорт или без него);
-
внутри каждого метода я для проверки просто пишу «Сообщить(ИмяМетода);»;
-
в самом низу в коде модуля (вне процедур, вне методов) тоже пишу «Сообщить(«КодМодуля»);»;
-
еще один момент, если можете заметить – внизу два раза объявлен метод «ПриватныйМетодНаКлиентеНаСервереБезКонтекста» с абсолютно одинаковым именем, и это не будет ошибкой. Это давно известная проблема на уровне платформы, которую почему-то все никак не решают – это просто нужно иметь в виду.
И в качестве теста мы создаем новую форму методом ПолучитьФорму и далее в попытке вызываем каждый из этих методов. Сразу как ложка дегтя – контекстная подсказка не работает, платформа 1С сама в шоке, что такое можно делать, она ничего не подсказывает.
Посмотрим, что выведется, на слайде оранжевым выделено то, что на выполнилось сервере, а голубым то, что произошло на клиенте:
-
первым делом сработал код модуля на сервере;
-
потом отработал обработчик «ПриСозданииНаСервере»;
-
обработчик «ПриОткрытии» не отработал, потому что форму я не открывал;
-
потом идет код модуля на клиенте;
-
потом – публичный метод;
-
и потом ряд методов на сервере - контекстные методы отрабатывают с кодом модуля на сервере, а бесконтекстные методы не вызывают код модуля.
Какие можно сделать из этого выводы:
-
во-первых, очевидно, что в коде модуля формы ничего писать не стоит, потому что это будет вызываться каждый раз при вызове каждого контекстного метода и отрабатывать как на сервере, так и на клиенте;
-
также видно, что никак не вызвать экспортный метод, объявленный с директивой «НаКлиентеНаСервереБезКонтекста»;
-
и еще видно, что приватные серверные методы формы почему-то получается вызывать извне – дело в том, что в версии 8.3.12 и старше в режиме совместимости можно вызывать приватные методы на сервере, такой баг платформы, в новых версиях она исправлена, но многие конфигурации еще находятся в режиме совместимости, поэтому эту ошибку можно еще много где встретить.
Здесь на слайде показана пояснительная диаграмма – как можно между собой выстроить взаимосвязь вызовов на разных директивах.
Кратко можно сказать, что все, что выше, может вызывать и себя и ниже по этой диаграмме – на клиенте можно вызвать с любым контекстом, а дальше по уменьшению.
Исключение – бесконтекстные методы. Они могут вызывать друг друга.
Реквизиты и переменные формы
Теперь о полях формы – какие они бывают, какое у них поведение, как их можно использовать. На форме, которая показана на слайде, у нас есть
-
один реквизит числового типа;
-
и в модуле я объявляю две переменные «Клиент» и «Сервер» (на клиенте и на сервере соответственно).
И дальше буду вызывать методы, которые будут работать с переменными.
Вначале эти переменные будут не инициализированы (будут иметь значение Неопределено), и потом я их буду либо инициализировать, либо просто прибавлять единицу. И посмотрим, что будет происходить.
-
В процедуре «ПриСозданииНаСервере» я два раза вызываю «МетодНаСервере»:
-
значение «Реквизита» у нас сразу же увеличивается и уже никуда не теряется;
-
а серверная переменная сначала была нулевая (она проинициализировась), потом ее вызвали еще раз – она увеличилась, все нормально.
-
-
Попали на клиент, вызвали «ПубличныйМетодНаКлиенте»:
-
«Реквизит» дальше везде будет сохраняться и просто увеличиваться;
-
переменную «Клиент» инициализировали, а потом увеличили;
-
вызываем «МетодНаСервере» – серверная переменная равна нулю (при переходе с сервера на клиент ее значение потерялось);
-
опять вызываем «МетодНаСервере» – серверная переменная опять нулевая, потому что теряем эту переменную при переходе;
-
а при вызове клиентского метода мы видим, что значение клиентской переменной никуда при переходе на сервер не пропало, оно сохранилось.
-
Какие можно сделать выводы:
-
реквизиты хранятся всегда и доступны как на клиенте, так и на сервере;
-
переменные на клиенте доступны только на клиенте;
-
переменные на сервере хранятся только во время серверного вызова, пока мы не ушли на клиент;
-
но в реквизитах нельзя хранить все типы данных – это только данные, которые можно определить на форме, которые могут свободно гулять между клиентом и сервером (в то время как в переменных на клиенте можно хранить любые типы объектов, которые доступны на клиенте).
Соответственно, если нужно работать с чем-то простым и на сервере, и на клиенте, то можно использовать реквизиты – они будут доступны везде. А для работы с объектами сложного типа на стороне клиента удобно использовать клиентские переменные.
Обработчики событий
Еще у форм 1С есть обработчики событий – их работу здесь демонстрирует немного странный, но показательный пример.
Считаем, что у нас есть отдельная специальная общая форма «СуммаКвадратов», которая умеет вычислять сумму квадратов. У нее есть:
-
табличная часть, где определяются слагаемые и для каждого слагаемого указывается признак, что оно было возведено в квадрат (реквизит «Возведено» с типом «Булево»);
-
и есть экспортный метод «ВычислитьСумму()» – считаем, что этот метод умеет только складывать, а в квадрат он возводить не умеет, поэтому для каждого слагаемого ему придется вызвать отдельную общую форму «Квадрат», которая возводит в квадрат. И вот мы создаем отдельную форму «Квадрат» и для каждого слагаемого посылаем оповещение «ВозвестиВКвадрат».
В форме «Квадрат»:
-
получаем оповещение с именем события «ВозвестиВКвадрат» и, если параметр «Возведено» не установлен, число перемножается само на себя, и возвращает параметр «Возведено» в значении «Истина»;
-
после этого оповещение «ВозведеноВКвадрат» передается обратно в источник.
Возвращаемся назад в форму «СуммаКвадратов»:
-
здесь в обработке оповещения для события «ВозведеноВКвадрат» мы проверяем, что каждой строчке таблицы слагаемых установлен признак «Возведено», и если все слагаемые возведены в квадрат, то сообщаем сумму итога по таблице.
Справа на слайде показан пример вызова – создаем форму «СуммаКвадратов», задаем слагаемые и вызываем метод «ВычислитьСумму».
В результате вызова видим ответ «Сумма = 14», значит, все считается верно.
Как я уже писал, пример может выглядеть странно, но он показывает, что здесь могут быть и более сложные интересные вычисления, которые распределены между разными формами, и формы общаются между собой такими сообщениями.
Примеры использования форм
Далее переходим от теории к практике. Где вообще это можно использовать?
Здесь я перечислил примеры, которые встречаются в типовых и отраслевых конфигурациях именно как такое использование форм через вызов ее методов. Обычно это работа с какими-то внешними системами, внешними объектами, оборудованием – такими как:
-
телефония;
-
почтовые клиенты;
-
электронный документооборот;
-
а также всем знакомый механизм «ВызовКлиентскогоМетода» из подсистемы «Дополнительные отчеты и обработки» БСП.
Во этих примерах вам для вызова клиентского метода нужно в своей внешней обработке создать форму, в которой написать экспортную процедуру «ВыполнитьКоманду». Либо из общего модуля получить форму обработки и вызывать ее экспортный метод, чтобы выполнить его на клиенте.
Форма как заменитель объекта
Дальше я покажу пример использования формы как заменителя объекта, который я использовал при мобильной разработке.
У меня было приложение с использованием функций фотографирования.
Для работы с камерой в 1С:
-
у объекта «СредстваМультимедиа» есть платформенный метод «СделатьФотоснимок», который после фотографирования возвращает типовой 1С-ный объект с типом «ДанныеМультимедиа»;
-
этот возвращаемый объект имеет свойства «РасширениеФайла» и «ТипСодержимого», а также метод «ПолучитьДвоичныеДанные», чтобы дальше работать с двоичными данными этого снимка.
Сначала я в приложении использовал этот встроенный метод, но потом после тестирования на большем количестве устройств увидел, что где-то платформенный метод срабатывает проблемно, где-то не хватает настроек, где-то плохое качество снимков.
Поэтому в качестве альтернативного варианта я рассмотрел метод Android ImageCapture, позволяющий вызывать для фотографирования внешние приложения (с помощью этого метода можно вызвать типовую камеру, либо какое-то другое приложение камеры, которое установлена в телефоне).
Однако этот метод Android, конечно же, возвращает не «ДанныеМультимедиа» от 1С, а имя файла, поэтому просто так заменить один метод на другой не получится (теперь в каждом куске кода уже нужно обрабатывать, откуда пришел этот снимок и забирать эти данные уже оттуда).
Чтобы этого не делать, я решил создать специальную отдельную форму, которая повторяет типовой объект «ДанныеМультимедиа», она также содержит:
-
экспортные клиентские переменные «РасширениеФайла» и «ТипСодержимого»,
-
а также приватную переменную «ДвоичныеДанные», в которой хранится сам снимок,
-
и экспортный метод «ПолучитьДвоичныеДанные()».
Теперь у меня в процессе фотографирования в качестве объекта «ДанныеМультимедиа» возвращалась эта форма, и полученные от нее данные шли в остальное приложение, которое работало, как и раньше, не замечая подмены – я спокойно использовал эту форму как другой объект 1С.
Модули прикладных объектов
Дальше рассмотрим, как можно использовать модуль формы как модуль прикладных объектов.
У каждого прикладного объекта, будь то справочник, документ и т.д. есть стандартные модули (модуль самого объекта для работы с конкретным объектом и модуль менеджера для работы с группой объектов или классом в целом), но они доступны только на сервере и вызвать их можно только с сервера.
Чтобы вызывать какие-то методы на клиенте для работы со ссылками и т.д. обычно создаются отдельные общие модули с различной комбинацией флагов «ВызовСервера», «Клиент», «Сервер» (в варианте КлиентСервер), что выглядит несколько странно, неудобно – как будто один объект разорван по дереву конфигурации.
Что можно сделать? Можно использовать специальную форму, которая будет внутри этого прикладного объекта. Назвать ее, допустим, «МодулиНаКлиенте», и все эти модули перенести туда, потому что там внутри можно делать и серверные, и клиентские варианты вызовов.
Это дает преимущества:
-
не нужны отдельные модули – все находится в одном месте;
-
можно даже переносить решение между конфигурациями, если используется какой-то независимый прикладной объект. Он просто копируется и все методы объекта пойдут вместе с ним;
-
и больше функциональности, потому что, как я уже говорил, формы могут хранить состояние в переменных, могут обрабатывать события (эти методы можно сделать только в форме).
И, как пример, что такое есть в типовых конфигурациях, справа показана обработка «ДокументооборотСКонтролирующимиОрганами». Вообще это, конечно, монстроидальный объект, потому что и в самом модуле объекта, и в форме «КонтейнерКлиентскихМетодов», по 60 000 строк, тысяча методов, сама эта обработка содержит 50 - 60 форм, но, тем не менее, одна из форм в ней используется для вызова методов на клиенте.
Работа с данными на клиенте
На слайде показан пример, который вызывает реакцию: «А что, это вообще законно?» Это – работа с данными базы на клиенте. Допустим, у нас есть заказ клиента, и мы хотим сделать диверсию: подменить партнера на своего знакомого и поделить все цены пополам. Мы в процедуре на клиенте:
-
создаем форму документа «ЗаказКлиента» методом «ПолучитьФорму»;
-
передаем в качестве ключа ссылку на заказ;
-
и дальше работаем со значением его реквизита «Объект, как с обычным объектом на сервере: заменяем партнера, поменяем цены в табличной части и в конце вызываем метод формы «Записать()».
Конечно, было бы обманом говорить, что все это делается только на клиенте, без сервера. По крайней мере, здесь есть два серверных вызова:
-
при создании формы – чтение ее данных;
-
при записи – запись на сервер.
Тем не менее, сам код написан на клиенте.
И в качестве совета – здесь я использую стандартную типовую форму объекта, но это будет, конечно, не очень оптимально, потому что там при создании на сервере отрабатывает очень много кода по обработке кучи элементов. Правильнее для работы с данными создавать отдельную пустую форму – главное, чтобы на ней в качестве основного реквизита использовался конкретный объект метаданных (в данном случае, «ЗаказКлиента»).
Взаимодействие открытых баз по протоколу UDP
И последний пример, о котором хочется рассказать подробнее (мы это в свое время мы делали в компании Юг-Авто) – это взаимодействие открытых баз.
Представьте, что у вас на предприятии используется несколько учетных систем. Например, вся работа с клиентами, первичная документация ведется в «Управлении торговлей», а бизнес-процессы, общение сотрудников, исполнительская деятельность по сотрудникам – в отдельной базе с «1С:Документооборот»:
-
пользователь работает за своим компьютером, у него открыто обе базы – он смотрит заказ клиента в «Управлении торговлей» и ему нужно поставить задачу какому-то сотруднику по этому заказу в «1С:Документообороте» – для этого в форме заказа есть кнопка «Создать бизнес-процесс»;
-
пользователь нажимает эту кнопку, и ему тут же в окне запущенного «Документооборота» открывается форма исполнения, куда уже был перенесен заказ клиента из другой базы – он заполняет в этой форме все необходимое (описание, что нужно сделать и исполнителя) и стартует бизнес-процесс;
-
исполнитель получает задачу и видит опять же заказ клиента в виде ссылки в этой форме задачи – нажимает на эту ссылку и ему уже, наоборот, открывается «Управление торговлей», где он видит этот заказ.
Получается, что, не делая обычный обмен между базами, можно на клиенте все равно предметно вести бизнес-процессы по каким-то объектам из другой базы.
Основа этого подхода – протокол UDP. Это довольно старый протокол, который был изобретен еще в начале 80-х годов, он расшифровывается как «Протокол пользовательских датаграмм». Довольно простой протокол, но не очень широко известен среди разработчиков 1С. Поэтому чтобы лучше его объяснить, я написал сравнение UDP и более знакомого 1С-никам HTTP на основе TCP.
В чем их разница:
-
UDP проще, у него нет подтверждения связи – мы просто отправляем сообщение, и точно не знаем, дошло оно или нет, а в TCP нужно создать отдельный канал (он передает данные только по каналу), ему нужно подтверждение и т.д.;
-
UDP более легковесный – меньше передает данных туда-обратно, не требует предварительной установки соединения и т.д.;
-
в UDP может быть широковещательная передача (всем одновременно), а в TCP – это всегда общение по конкретному созданному каналу
-
для передачи по протоколу UDP не нужна публикация на веб-сервере (это удобно технически со стороны 1С), а чтобы база принимала данные по HTTP, ее нужно обязательно опубликовать на веб-сервере.
Где можно применить UDP:
-
для связи 1С с другими запущенными программами – дальше я расскажу про пример с 1С, но можно применить UDP для связи не только с 1С – можно организовать общение с любыми программами, если на принимающей стороне также организовать работу по UDP;
-
причем можно организовать общение между программами не только на одном компьютере, но и можно передавать сообщения между разными компьютерами сети;
-
и можно передавать данные с сервера на клиент – если на сервере есть возможность обрабатывать протокол UDP, то можно наоборот, передавать данные именно с сервера на клиент (хотя обычно в 1С это делать нельзя). С помощью этого можно сделать прогресс-бар при долгих задачах на сервере, какие-то еще оповещения о завершении и т.д.
Какие особенности конкретно у этого алгоритма взаимодействия:
-
конечно, используется внешняя компонента, потому что в 1С нет встроенных механизмов для работы по UDP;
-
основная логика описана в модуле формы – в каждой базе есть форма, которая отвечает за этот механизм;
-
чтобы передать данные по UDP, нужно знать адрес и порт, на который передать эти данные (у каждой базы должен быть зафиксирован какой-то конкретный номер порта – мы открываем базу и начинаем слушать входящие сообщения на конкретном порте).
Допустим, нам нужно создать бизнес-процесс исполнения из «Управления торговлей» в «Документообороте». Для этого мы вызываем метод «ОтправитьКоманду»:
-
в нем методом SendTo компоненты UDPTor отправляем команду на заданный номер порта с адресом (по умолчанию в качестве адреса используется 127.0.0.1 – это localhost, потому что мы работаем на одном компьютере) – например, отправили команду «Создать бизнес-процесс исполнения по такому-то заказу клиента»;
-
по умолчанию задаем, что «ПришелОтвет = Ложь» (мы пока не уверены, что пришел ответ);
-
ждем одну секунду с помощью обработчика ожидания «ПроверитьПолучениеКоманды» – данные пошли;
-
обратно я нарисовал штриховую стрелку, потому что данные могут не вернуться (например, база «Документооборота» у пользователя в данном случае сейчас не открыта), но если база была открыта, то происходит обработка внешнего события «Server» от источника «UdptorChat» – если другая база прислала ответ, что она получила команду, то записываем в переменную «ПришелОтвет = Истина» и успокаиваемся;
-
а через секунду по обработчику ожидания смотрим – если ответ от открытой базы не пришел (а за секунду он точно должен был уже прийти, если база была включена), то дальше мы уже сами запускаем базу с параметрами.
По сути, параметры – это то же самое, что и команда. И тогда, если у пользователя база не открыта, то она сначала запускается, и потом уже в ней сразу открывается форма создания бизнес-процесса.
В данном примере используется компонента из публикации //infostart.ru/public/69992/. Это довольно старая публикация, после нее на Инфостарте появлялись еще и другие решения по работе с UDP, так что, если вас это заинтересует, можете выбрать, что вам по вкусу.
Вопросы:
-
Меня интересует общение между базами 1С, установленными на разных компьютерах. Например, у нас есть две базы – «1С:Документооборот и 1С:Управление торговлей», у каждой из которых запущена внешняя компонента. Это работает не только на одном компьютере, но и в локальной сети?
-
Да, как я говорил, при отправке сообщения есть параметр «Адрес» – и это может быть адрес в локальной сети или какой-то другой адрес.
-
Правильно ли я понимаю, что, если мы на каждый компьютер в нашей компании установим эту компоненту и захотим создать свой чат в локальной сети, то один человек пишет сообщение в этом чате, нажимает кнопку и благодаря выполнению всего лишь одной строчки кода всем одновременно в тот же чат отправляется одно и то же сообщение. По сути – это может быть заменой системы взаимодействия от 1С?
-
Да, это очень похоже на систему взаимодействия. Это, можно сказать, ее старинный аналог.
-
Получается, что это – способ общения между разными формами разных конфигураций. Причем, в этом пакете мы можем посылать не только текстовую информацию. Мы можем пересылать ссылки. И если эта ссылка есть в конфигурации-приемнике, она ее нормально распознает как объект и сделает с ним определенные действия. Это действительно какая-то магия. Что это за компонента – она бесплатная, она работает на 64-бита или только на 32?
-
Конкретно это компонента довольно старая, она бесплатная и работает только под 32 бита, но есть и другие решения, построенные по технологии NativeAPI – они должны работать везде, в том числе кроссплатформенно.
*************
Данная статья написана по итогам доклада (видео), прочитанного на INFOSTART MEETUP Krasnodar.