Каталог решений - О формах 1С замолвите слово… Необычное использование знакомого всем объекта

О формах 1С замолвите слово… Необычное использование знакомого всем объекта

О формах 1С замолвите слово… Необычное использование знакомого всем объекта

В наличии

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

Категория:

Описание

 

Что такое объект

 

 

Форма – это, в принципе, объект, некая изолированная логически сущность, у которой есть:

  • свойства, описывающие состояние;

  • методы, описывающие поведение этой сущности;

  • и события для реагирования на какие-то сообщения от других внешних объектов.

Все объекты относятся к какому-то классу.

В 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» – если другая база прислала ответ, что она получила команду, то записываем в переменную «ПришелОтвет = Истина» и успокаиваемся;

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

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

В данном примере используется компонента из публикации //sale.itcity.ru/public/69992/. Это довольно старая публикация, после нее на Инфостарте появлялись еще и другие решения по работе с UDP, так что, если вас это заинтересует, можете выбрать, что вам по вкусу.

 

Вопросы:

 

  • Меня интересует общение между базами 1С, установленными на разных компьютерах. Например, у нас есть две базы – «1С:Документооборот и 1С:Управление торговлей», у каждой из которых запущена внешняя компонента. Это работает не только на одном компьютере, но и в локальной сети?

  • Да, как я говорил, при отправке сообщения есть параметр «Адрес» – и это может быть адрес в локальной сети или какой-то другой адрес.

  • Правильно ли я понимаю, что, если мы на каждый компьютер в нашей компании установим эту компоненту и захотим создать свой чат в локальной сети, то один человек пишет сообщение в этом чате, нажимает кнопку и благодаря выполнению всего лишь одной строчки кода всем одновременно в тот же чат отправляется одно и то же сообщение. По сути – это может быть заменой системы взаимодействия от 1С?

  • Да, это очень похоже на систему взаимодействия. Это, можно сказать, ее старинный аналог.

  • Получается, что это – способ общения между разными формами разных конфигураций. Причем, в этом пакете мы можем посылать не только текстовую информацию. Мы можем пересылать ссылки. И если эта ссылка есть в конфигурации-приемнике, она ее нормально распознает как объект и сделает с ним определенные действия. Это действительно какая-то магия. Что это за компонента – она бесплатная, она работает на 64-бита или только на 32?

  • Конкретно это компонента довольно старая, она бесплатная и работает только под 32 бита, но есть и другие решения, построенные по технологии NativeAPI – они должны работать везде, в том числе кроссплатформенно.

 

*************

Данная статья написана по итогам доклада (видео), прочитанного на INFOSTART MEETUP Krasnodar. Больше статей можно прочитать здесь.

Приглашаем всех принять участие в тематических митапах Инфостарта: infostart.ru/events/

has been added to your cart:
Оформление заказа