Синхронизация через веб-сервисы
Довелось как-то поучаствовать в одном проекте. Суть участия была настройка синхронизации между мобильным устройством на Android и основной базой. Одним из главных условий было динамическое количество устройств, поэтому планы обмена не подходили. Решено было использовать веб-сервисы.
За время выполнения работы многое было переосмыслено и сейчас понимаю, что некоторые моменты надо было сделать немного иначе, но в целом задача была выполнена и в большинстве своем успешно. Я не буду привязываться к той задаче, поэтому сделаем все на абсолютно чистых базах и конфигурациях. Разберем в качестве примера синхронизацию справочников, как основной вид хранения информации.
Итак, создадим в чистой конфигурации справочники Номенклатура и, например, Категории. В номенклатуру добавим реквизит Категория с типом СправочникСсылка.Категории.
Далее, сделаем аналогичную конфигурацию для мобильного приложения и сразу перейдем к настройке веб-сервиса для синхронизации.
В ЦБ создаем объект конфигурации XDTO-пакет следующего вида
В данном пакете мы описываем структуру передаваемых нами данных. Свойства типов Code и Name у нас имеют тип string и означают Код и Наименование соответственно. Сами типы CategoryString и ProducString соответствуют строке элемента справочника. Типы ProductTable и CategoryTable представляют из себя массивы строк соответствующих справочников. Для того, чтобы тип стал массивом, в свойствах его свойства(ProductStr например) необходимо максимальное значение указать как «-1». Свойства CategoryStr и ProductStr имеют тип CategoryString и ProducString соответственно. И последнее оставшееся свойство это Category, которое имеет тип CategoryStr. Далее нам необходимо создать веб-сервис и операцию с типом возвращаемого объекта ProductTable. почему именно с этим типом, потому что он содержит необходимые нам категории.
Теперь добавим немножко кода, а именно опишем, как мы будем получать данные для выгрузки. Для данной операции нам не нужны никакие входящие параметры, по крайней мере в этом примере мы этого использовать не будем. В коде, мы не будем использовать тип CategoryTable, потому что он может понадобиться, если мы захотим выгрузить весь справочник категорий. В модуль веб-сервиса добавим следующую функцию:
Теперь наш веб-сервис готов к публикации и после этого, можно приступать к настройке МП. Напомню, там мы на данный момент имеем только два справочника аналогичных ЦБ. Для простоты, в МП создадим объект обработка из которой и будем вызывать нашу синхронизацию. Добавим на форме обработки всего одну кнопку, которая и будет вызывать процедуру синхронизации, на это подробно останавливаться не буду. Код процедуры ниже:
Собственно на этом все. Теперь при открытии обработки, нажимая кнопку Синхронизировать, получаем справочники или обновляем из ЦБ. Прошу строго не судить, т.к. это моя первая публикация. На грамотность кода особо внимания не обращайте, т.к. не нагромождал с целью упрощения. К публикации прикладываю обе конфигурации из статьи.
Работаем с веб-сервисом 1С из приложения на Android
При работе над фронтом для кафе появилась задача обращаться к веб-сервису 1С из приложения, разрабатываемого на Android. Google мне дал несколько ответов на тему как вообще работать с SOAP, используя библиотеку ksoap2-android. Они помогли в передаче простых типов, но когда дело дошло до передачи массива, пришлось немного подумать.
Веб-сервис на стороне 1С
В конфигурации 1С создан веб-сервис с методом WriteSale. Метод принимает несколько параметров, один из которых, items, имеет тип ItemsSold (задан в пакете XDTO конфигурации). Остальные параметры имеют простые типы (string, datetime). Скрин конфигурации:
У типа ItemSold все свойства простого типа. Метод WriteSale веб-сервиса имеет следующий код:
Клиент на стороне Android
Для обращения к веб-сервису из приложения Android написал следующий код (в соответствии с хорошим примером простого клиента):
Вроде бы код выглядит правильно, формирует красивый xml-запрос:
Но веб-сервис отвечает на него 500-й ошибкой. При этом, обращаясь к другому методу с параметрами простого типа на том же веб-сервисе, мы получаем корректный ответ. Более того, обращаясь из другой базы 1С через WS-ссылка к приведенному выше методу веб-сервиса, мы получаем корректный ответ и выполнение необходимых действий на стороне веб-сервиса. Поэтому пришлось перехватить запрос, формируемый другой базой 1С. Сделать это фидлером не получилось, так как он каким-то образом обрезал само тело запроса с xml и не передавал его веб-сервису. Нормально перехватить запрос удалось только с помощью WireShark. Итак, текст запроса от 1С:
Несложно заметить, что для вложенных элементов массивов (Code, Price. ) библиотека ksoap2-android не проставляет префиксы с пространством имен. Для корневых элементов (id, date. ) они также не проставлены, но этот факт 1С в ступор не вводит. А их отсутствие у под-элементов заставляет программу усомниться в корректности входных данных, прочитать она их не может.
Изучив код библиотеки, решил, что наиболее рациональным будет модифицировать метод SoapObject#addProperty(String, Object) следующим образом:
В исходном коде я заменил объекты SoapObject на SoapObjectCustom в следующих местах:
Заключение
Скорее всего, есть смысл в том, что авторы не включали префиксы пространства имен в свойства элементов. И вполне возможно, что в работе с другими веб-сервисами такие коррективы приведут к некорректному поведению программы. Тем не менее, данный метод работает с веб-сервисами 1С, надеюсь это описание кому-нибудь поможет в работе.
Описанное выше было протестировано с 1С v.8.2.15.294 и Android 12 (3.0).
Планы обмена для мобильного приложения 1С
Эту и другие технические статьи написали наши программисты 1С и получили за них премии. Если вы тоже работаете с 1С и любите делиться опытом, приходите разработчиком в Neti → |
Мобильная платформа нацелена на ускорение операций, и переносить каждый раз десятки мегабайт информации при обмене с мобильным устройством задача нелегкая. Целесообразно выгружать только новую или изменившуюся информацию используя планы обмена.
Давайте рассмотрим на примере создание двустороннего обмена мобильного устройства и базы. Для начала создадим новую базу, назовем ее «Центральная база», назначение использования как на скриншоте:
Теперь создадим объекты метаданных для ввода данных и тестирования обмена данными. Создадим справочник, документ и регистр сведений:
1) Справочник «Номенклатура»:
2) Создадим документ «Установка цен номенклатуры»: реквизиты
Номенклатура — тип «СправочникСсылка. Номенклатура»
3) Создадим регистр сведений «Цены номенклатуры»:
Периодичность: в пределах дня
Режим записи: в пределах дня
Добавляем измерение: Номенклатура — тип «СправочникСсылка. Номенклатура»;
Ресурс: Количество — тип Число.
Для документа «Установка цен номенклатуры» напишем процедуру проведения.
Создадим план обмена, установим ему имя «МобильныйОбмен». В состав плана обмена включим созданные метаданные (справочник, документ и регистр сведений). Для всех выбранных объектов разрешим авторегистрацию.
Теперь создадим общую команду «ВыполнитьСинхронизациюСБД», которая будет синхронизировать данные мобильного приложения и базы, причем в обмене будут участвовать только измененные и вновь созданные объекты.
Добавим команду в панель навигации: Важное.
Пришло время добавить обработку команды:
Для обмена данными нам будет необходим веб-сервис. Давайте создадим его и присвоим ему имя «WebОбмен». Предлагаю использовать для обмена данными хранилище значения, т. к. хранилище значения сжимает данные и это очень важно для мобильного трафика, и хранилище значения может быть сериализовано в/из XDTO.
Заполним URI пространства имен: Data,
Пакеты XDTO: http://v8.1c.ru/8.1/data/core,
Имя файла публикации: WebОбмен.1cws.
Добавим операцию: Синхронизация,
Тип возвращаемого значения: ValueStorage ).
Добавляем параметр «Данные» и установим ему тип значения: ValueStorage ).
Для операции Синхронизация добавим процедуру Синхронизация:
Необходимо опубликовать мобильное приложение:
Теперь добавляем базу на мобильном устройстве включаем перезапуск из конфигуратора и разрешаем отладку. Также необходимо прописать адрес сервера отладки.
Настраиваем планы обмена в базе и на мобильном приложении следующим образом:
Теперь создаем в мобильном приложении номенклатуры и документ и записываем:
В базе создаем номенклатуру:
Теперь в мобильном приложении нажимаем кнопку «Выполнить синхронизацию с БД»:
И видим результат, номенклатура созданная в базе появилась на мобильном устройстве, а в базе появилась номенклатура созданная в мобильном приложении:
Планы обмена значительно уменьшают трафик, а в сочетании с мобильным приложением позволяют работать в удаленных местах, где нет высокоскоростного интернета, например, в удаленных складах.
Бесплатный курс по Мобильной платформе 1C. Модуль 2
Модуль 2. Обмен данными с мобильным приложением через веб-сервисы
Изучив второй модуль, Вы научитесь:
Порядок обучения
Скачивайте теоретические материалы в PDF и видео-формате. Рекомендуем начинать именно с изучения теории.
Выполняйте практическое задание для закрепления полученных знаний.
Выполните самоконтроль, просмотрев видео-решение преподавателя.
Теоретические материалы
Итак, приступайте к изучению теоретического материала второго модуля курса.
Вопросы
Если у Вас возникли вопросы, Вы можете просто просмотреть комментарии ниже, наверняка Вы найдете ответ.
Обратите внимание, для навигации по комментариям внизу страницы есть ссылки
Комментарии / обсуждение (873):
Еще была ошибка в процедуре веб сервиса на строке:
Если ЗначениеЗаполнено(СтруктураДанных) Тогда…
Видимо с мутабельными значениями проблема.
Заменил на: Если СтруктураДанныхНеопределено Тогда…
Если СтруктураДанныхНеопределено Тогда…
меньше больше (оператор неравенства) здесь не отображаются (((
Если НЕ СтруктураДанных=Неопределено Тогда…
У меня такие ошибки.
1. Набираю в хроме ссылку на веб сервис, но ничего вразумительного не получаю. Абракадабра. А виндовый эксплорер показывает нормально XML.
2. На Samsung Galaxy Note 2 при обновлении конфы пишет про ошибку разбора XML.
Решил проблему убрав из веб сервиса и пакета XDTO кириллицу. Т.е. все наименования объектов конфы сделал латиницей и заработало!
<ОбщаяКоманда.ВыполнитьОбмен.МодульКоманды(15)>: Ошибка при вызове метода контекста (ВыполнитьОбмен)
Ответ = Десериализовать(Соединение.ВыполнитьОбмен(СтрокаДанных));
по причине:
Ошибка вызова операции сервиса:
по причине:
Ошибка SOAP сервера: Неизвестная ошибка. Ошибка отображения типов:
Отображение типа ‘Соответствие’ в тип ‘
по причине:
Ошибка отображения типов:
Отображение типа ‘Соответствие’ в тип ‘
Была ошибка в функции СоздатьОтветКлиенту
Возврат Соответствие вместо Возврат Сериализовать(Соответствие)
Вопрос про серилазиацию/десириализацию XTDO.
Как сериализуется ссылочный тип?
сериализуется в ссылку. Если в базе ссылки таклгл типа и с таким УИДом нет то будет написано “объект не найден…”
Не понял сразу вопрос. Можно переносить двумя способами. Сериализовать объект и после десериализации выполнить метод записать(). Если имя типа совпадает то либо подхватится элемент с таким УИДом или создастся новый. Дибо переносить структуру с реквизитами и УИДом. Тогда надо у менеджера нужного типа выполнить метод ПолучитьСсылку(УИД) и получать объект потом.
Ошибка при обращении по ссылке http://127.0.0.1:8080/DemoModule3/ws/ОбменДанными.1cws.
в httpd.conf нужный модуль подгружается
LoadModule _1cws_module “C:/Program Files (x86)/1cv8/8.3.4.465/bin/wsap22.dll”
Проблема была в том, что файловая база размещалась на смапированном диске D:. После переНастройки на C: – картинка ожила.
Помогите пожалуйста возникает ошибка при отладке
<ОбщаяФорма.Форма.Форма(34)>: Ошибка при вызове метода контекста (ПолучитьСреднее)
Ответ = Соединение.ПолучитьСреднее(Неопределено,Сериализовать(СтруктураДанных));
по причине:
Ошибка вызова операции сервиса: <ФункцииДляРассчета>:ФункцииДляРассчета:ПолучитьСреднее()
по причине:
Ошибка SOAP сервера: Неизвестная ошибка.
по причине:
Попробовал воспользоваться хранилищем значения, выяснилось, что конструкция вида:
…
ХрЗнч = Новый ХранилищеЗначения(Соответствие,Новый СжатиеДанных(9));
Возврат Сериализовать(ХрЗнч);
приводит к тому, что все ссылки на принимающей стороне “слетают”.
Работающим оказался вариант с двойной сериализацией, когда в хранилище значения добавляется уже сериализованный объект, и затем оно ещё раз сериализуется, например, так:
…
ХрЗнч = Новый ХранилищеЗначения(Сериализовать(Соответствие),Новый СжатиеДанных(9));
Возврат Сериализовать(ХрЗнч);
…
На принимающей стороне при этом код выглядит так:
ХрЗнч = Десериализовать(стрДанные);
Соответствие = Десериализовать(ХрЗнч.Получить());
…
Экономия на размере пакета получается примерно десятикратная.
Вопрос: это единственно правильный способ работы с хранилищем значений с целью упаковки пакета передачи данных?
Когда в мобильной платформе будет механизм FastInfoset (он пока только в стационарной доступен), то можно будет не использоват хранилище, а обмениваться бинарными данными, но в принципе – разница в коде будет не большая, а вот в сжатии – не знаю.
У меня при выполнении команды обмена в клиенте вылезла такая ошибка. С чем может быть связана?
<ОбщаяКоманда.ВыполнитьОбмен.МодульКоманды(14)>: Ошибка при вызове метода контекста (ВыполнитьОбмен)
Ответ = Десериализовать(Соединение.ВыполнитьОбмен(СтрокаДанных));
по причине:
Ошибка вызова операции сервиса:
по причине:
Ошибка SOAP сервера: Обязательное возвращаемое значение не задано: DataTransfer>:DataTransferSoapBinding:ВыполнитьОбмен()
поставьте галочку упараметра – возможно пустое значение
Чтобы увидеть материал текущего курса, нужно регистрировать токен с 20.04.2014? Если с сегодняшнего дня, то ничего не увижу уже? А если с 20.04.2014, то доступ прекратиться 30.04.2014 или у меня всегда будет доступ к материалам, выложенным за этот период?
Добрый вечер.
Перехожу по опубликованной ссылке, а у меня вот так выглядит
This XML file does not appear to have any style information associated with it. The document tree is shown below.
Операция не найдена: <ОбменДанными>:ОбменДанными:ВыполнитьОбмен()
Вопрос по видеоуроку.
После добавления второго параметра (“СериализованнаяСтруктураДанных”) в функцию Web-сервиса “ПолучитьСреднее”
тренер резонно добавляет первый параметр (“Неопределено”) в строчке
Ответ = Соединение.ПолучитьСреднее(Неопределено, Сериализовать(СтруктураДанных));
(Процедура ПередатьСериализациюНаСервере() модуля ОбщейФормы)
НО не добавляет второй параметр в строчке:
Ответ = Соединение.ПолучитьСреднее(СтруктураДанных);
(Процедура ПодключитьсяЧерезСтатическуюСсылкуНаСервере()).
что приводит к ошибке: “Недостаточно фактических параметров”.
Если же в этой строчке в качестве второго параметра указать “Неопределено”, то получим не менее загадочное сообщение об ошибке “Ошибка SOAP сервера …по причине: Проверка мутабельных значений на заполненность не поддерживается”
Создание мобильного клиента 1С на Android с использованием HTTP-сервисов
На Инфостарте есть несколько публикаций на тему создания приложений для Android и его связке с 1С через Web-сервис. Но на дворе сейчас конец 2017 года, и пришла пора освежить свои навыки.
Во-первых, с выходом Android Studio 3.0.0 в корпорации Google уже окончательно определились с будущим основным языком программирования, и это будет Kotlin. В данной публикации будем использовать именно этот язык. Он совместим с Java и с переходом у вас никаких проблем не будет, получите только позитивные эмоции.
Во-вторых, не стоит вкладывать свои силы в разработку Web-сервисов. Этому есть несколько причин:
Точно так же как и для Web-сервисов, я не рекомендую вам писать бизнес-логику в модуле HTTP-сервиса, так как в нём отсутствует проверка кода на ошибки. Я вам привел плохой пример исключительно для простоты изложения. Вместо этого старайтесь максимально переносить свой код в общие модули.
А теперь переходим к клиенту: в Android Studio 3 создайте новый проект, включите поддержку языка Kotlin. Укажите минимальный SDK 25 уровня. В файле build.gradle вашего проекта добавьте следующие зависимости:
Google рекомендует бизнес-логику приложения выносить в класс, наследуемый от Service, поэтому создайте простейший сервис примерно такого содержания:
Не могу удержаться и покажу, как теперь стало просто запускать сервис из главной Activity при помощи расширения языка Anko:
По умолчанию Retrofit не использует авторизацию на HTTP-сервере, но к счастью его легко добавить. Для этого нам необходимо написать класс для авторизации на сервере 1С методом BASIC, так называемый интерцептор, вот его полный код:
Не забывайте, что методом BASIC пароли пользователей 1С передаются через сеть открытым текстом, поэтому при боевом развертывании приложения всегда настраивайте веб-сервер на использование только шифрованного протокола HTTPS.
Теперь необходимо написать интерфейс API нашего HTTP-сервиса. Для начала возьмем блокнот и посмотрим на структуру данных, полученную ранее. Этот файл поможет нам создать классы Java, в которые будут завёрнуты наши данные. В случае использования ksoap2 вы бы на этом пункте хорошенько вспотели. Но ничего не бойтесь, с нами Retrofit, поэтому идем на сайт www.jsonschema2pojo.org/ и в левой его части вставляем содержание вашего JSON-пакета. В правой части заполняем как на рисунке:
На основе введеных данных этот сайт бесплатно сгенерирует нам два класса на языке Java в 2 файлах:
Принцип построения этих классов-обёрток думаю теперь вам стал понятен и немного попрактиковавшись вы сможете писать их на языке Kotlin без всяких помощников типа сайта, приведенного выше.
Добавим в наш сервисный класс BLService переменную для хранения массива полученных из 1С данных:
А теперь переходим непосредственно к написанию интерфейса API. Звучит угрожающе, но на самом деле для нашего HTTP-сервиса это будет такой простой код;
Полный URL в браузере, соответствующий функции getStore() выглядел бы с учётом вышенаписанного кода так: http://server/Database1c/hs/wms/table/stores. Функции в интерфейсе всегда должны возвращать тип Call с указанием получаемого от HTTP-сервиса класса-обертки в угловых скобках. Параметры в строке @GET можно точно так же как в 1С заключать в фигурные скобки и указывать их в параметре функции, например вот так:
Интерфейс API инициализируется в коде нашего класса BLService такой строкой:
Вы наверное удивитесь, но на этом всё. Теперь вы можете дергать 1С при помощи HTTP-сервиса. Для простоты уберу обработчики исключений и асинхронные штучки, оставив только самую суть: