воскресенье, 17 ноября 2013 г.

1С:Предприятие 8. Веб-сервисы. Синхронизация с сайтом

Содержание статей: 1С:Предприятие 8. Веб-сервисы
Предыдущая статья: 1С:Предприятие 8. Веб-сервисы. Фасеты как способ ограничить значения данных

Веб-сервисы идеально подходят для обмена данными между различными информационными системами. В этой статье я продемонстрирую, как можно сделать синхронизацию товаров с сайтом на простых примерах. В примерах будет использован язык PHP.



Самым простым случаем является вызов операций веб-сервисов 1С с сайта при необходимости. Например, при открытии сайта можно получить актуальный список товаров и их остатки.
Давайте для примера создадим простенькую базу 1С с одним справочником номенклатура, в котором будут храниться все необходимые данные, включая остатки.


И в этой базе создадим веб-сервис, который будет выдавать список товаров.


На рисунке изображен модуль ws-операции ПолучитьНоменклатуру() и ПакетXDTO, который содержит описание полей выгружаемой номенклатуры. Мы не используем отборы и выгружаем сразу весь список товаров. Более подробное о создании веб-сервисов в среде 1С вы сможете узнать на странице 1С:Предприятие 8. Веб-сервисы. Реализация веб-сервиса.

Для получения списка товаров на стороне сайта будет необходимо вызвать ws-операцию ПолучитьНоменклатуру() веб-сервиса "WebСервис" без параметров. В ответ будет получен массив объектов "Номенклатура" с полями:
  • УникальныйИдентификатор
  • Наименование
  • Цена
  • Остаток
Поле УникальныйИдентификатор необходимо для однозначного определения товара независимо от наименования.
Для работы с веб-сервисами в PHP существует класс SoapClient. Довольно легко с ним работать.  Достаточно в вызове конструктора класса указать путь к WSDL веб-сервиса, логин пароль и уже можно работать. Давайте создадим страничку, например index1.php, и запишем туда следующий код:

<?php

header("Content-Type: text/html; charset=UTF-8");
$SoapClient1C = new SoapClient("http://ghostaz.no-ip.org/sync/ws/WebСервис?wsdl");
$List = $SoapClient1C->ПолучитьНоменклатуру();

if(is_array($List->return->Список )) 
{
foreach ( $List->return->Список as $key) 
echo $key->УникальныйИдентификатор." ".$key->Наименование." ".$key->Цена." ".$key->Остаток." "."<br>";
 } else
echo $List->return->Список->УникальныйИдентификатор." ".$List->return->Список->Наименование." ".$List->return->Список->Цена." ".$List->return->Список->Остаток;

?>


1С работает с UTF-8 потому в первой строке мы указываем кодировку. Далее мы создаем объект $SoapClient1C. ПРи его создании указываем ссылку на WSDL для нашего веб-сервиса. Узнать как формируется строка ссылки можно в статье 1С:Предприятие 8. Веб-сервисы. Публикация и тестирование.

В строке 

$List = $SoapClient1C->ПолучитьНоменклатуру();

мы вызываем ws-операцию ПолучитьНоменклатуру(). Результат операции записываем в переменную &List.

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

В результате запуска index1.php на экран будет выведено примерно следующее:

c5c291c5-3fe9-11e3-824f-083e8e40aa90 Гайки 120 5
2d490b6d-4f6a-11e3-beaf-28924a4e4c34 Гвозди 145 5 

Это список товаров, которые заведены в базе 1С, с наименованием, ценой и количеством.

Этот способ синхронизации малопригоден для рабочих сайтов, так как при каждом открытии сайта или обновлении будет инициировано обращение к базе 1С, что замедлит работу сайта и увеличит трафик. Давайте улучшим текущую схему. 

Для вывода на экран актуальных данных совсем не обязательно обращаться к базе 1С так часто, достаточно обратиться за получением остатков в момент их изменения и выводить эти данные до следующего изменения. Таким образом необходимо инициировать синхронизацию лишь в момент изменения данных в базе 1С, а полученные данные хранить на стороне хостинга. Момент, когда происходит изменение данных знает 1С, соответственно только она может инициировать синхронизацию. Для синхронизации она может вызвать какой либо скрипт, который из веб-сервиса получит остатки и запишет их в базу данных. План такой:
  1. Пользователь меняет справочник Номенклатура;
  2. Информационная база вызывает скрипт sync.php;
  3. Скрипт sync.php вызывает ws-операцию ПолучитьНоменклатуру() и помещает результат в таблицу tovar;
  4. Пользователь запускает index2.php, который выводит содержимое таблицы tovary.
На стороне базы 1С необходимо осуществить вызов скрипта sync.php. Данный вызов будем делать в процедуре ПослеЗаписиНаСервере формы справочника Номенклатура. Это крайне не оптимально, стоит использовать фоновые задания для вызова скриптов. 



В данном коде мы используем GET запрос и вызываем скрипт "http://sync.ghostaz.ru/sync.php". Все что скрипт выводит на экран, мы увидим во временном файле. Название файла хранится в переменной "УИ".

Для такой схемы нам нужно иметь базу данных и таблицу. Мы создадим таблицу с четырьмя полями:
  • UID CHAR(36)
  • Name VARCHAR(25)
  • Cena REAL
  • Ost REAL
Давайте посмотрим на результируюий код скрипта синхронизации. В комментариях я опишу назначение команд.

<?php

// устанавливаем кодировку
header("Content-Type: text/html; charset=UTF-8");

// создаем подключение к СУБД
$Link = mysql_connect ("localhost", "Логин", "Пароль") ;
// выбираем нужную базу данных
$rv = mysql_select_db("ghostaz", $Link);
// создаем таблицу товары если ее еще нет
mysql_query("CREATE TABLE IF NOT EXISTS tovary (UID CHAR(36) PRIMARY KEY, Name VARCHAR(25), Cena REAL, Ost REAL)");

// очищаем таблицу перед заполнением
mysql_query("DELETE FROM tovary");

// создаем клиент для веб-сервисов и вызываем операцию, которая в $List помещает массив номенклатуры
$SoapClient1C = new SoapClient("http://ghostaz.no-ip.org/sync/ws/WebСервис?wsdl");
$List = $SoapClient1C->ПолучитьНоменклатуру();

// если ws-операция вернула массив
if(is_array($List->return->Список )) 
{
// то перебираем весь список
foreach ( $List->return->Список as $key) 

// и доавляем номенклатуру в базу   
mysql_query("INSERT INTO tovary VALUES ('".$key->УникальныйИдентификатор."', '".$key->Наименование."', ".$key->Цена.", ".$key->Остаток.")");

 } else

// если же это не массив, то одно единственное значение вносим в базу
mysql_query("INSERT INTO tovary VALUES ('".$key->return->Список->УникальныйИдентификатор."', '".$key->return->Список->Наименование."', ".$key->return->Список->Цена.", ".$key->return->Список->Остаток.")");

// проверяме что синхронизация прошла - выводим содержимое таблицы
$sql=mysql_query("SELECT * FROM tovary");
while($row = mysql_fetch_array($sql)) echo $row['UID']." ".$row['Name']." ".$row['Cena']." ".$row['Ost'].'<br>';

// закрываем соединение
mysql_close ($Link);

?>

Код файла index2.php может теперь содержать только вывод таблицы:

<?php

// устанавливаем кодировку
header("Content-Type: text/html; charset=UTF-8");

// создаем подключение к СУБД
$Link = mysql_connect ("localhost", "Логин", "Пароль") ;
// выбираем нужную базу данных
$rv = mysql_select_db("ghostaz", $Link);

//выводим содержимое таблицы
$sql=mysql_query("SELECT * FROM tovary");
while($row = mysql_fetch_array($sql)) echo $row['UID']." ".$row['Name']." ".$row['Cena']." ".$row['Ost'].'<br>';

// закрываем соединение
mysql_close ($Link);

?>

От себя добавлю, что данный способ синхронизации отлично работает в коммерческих проектах. Скорость отработки довольно высока. К примеру сложные и большие запросы отрабатываются за 0,2 секунды. Быстрые же запросы к веб-сервису могут заполнить 30 тысяч записей таблицы за 16 секунд.

12 комментариев:

  1. Анонимный05 мая, 2014 13:21

    Владимир! Подскажите, как передать на сайт фотографию из 1С? Какого типа будет этот параметр?

    ОтветитьУдалить
    Ответы
    1. Я вам предлагаю посмотреть код Библиотеки стандартных подсистем. Там есть веб сервис Files. Используется тип File, в котором сами бинарные данные передаются через base64Binary (http://www.w3.org/2001/XMLSchema).

      Удалить
    2. Анонимный05 мая, 2014 13:41

      Спасибо, Владимир! Посмотрю!

      Удалить
    3. Анонимный05 мая, 2014 14:29

      Установила БСП 2.1 - к сожалению не нашла там такого веб-сервиса Files...

      Удалить
    4. Анонимный08 мая, 2014 14:17

      Владимир! Передать картинку через веб-сервис - получилось! Но это работает только когда маленькие картинки. На больших картинках (например 3Мб) выдает ошибку. Веб-сервис сторонней программы. Я из 1С его вызываю и передаю ему картинку.
      Ошибка: {Форма.Синхронизация_ФизЛиц_С_ИС.Форма(203)}: Ошибка при вызове метода контекста (AddPhoto): Ошибка вызова операции сервиса: {www.ххххххххх.ru/}:HRM:AddPhoto(): Ошибка SOAP сервера: Исключение при запуске расширений, указанных в файле конфигурации. ---> Превышена максимальная длина запроса.

      Мне говорят, что где-то в 1С нужно увеличить какой-то параметр????
      Вопрос: эта ошибка у меня в 1С? Нужно где-то что-то донастроить - чтобы работать с большими файлами? Или эта ошибка в сторонней программе?

      Удалить
    5. Форма.Синхронизация_ФизЛиц_С_ИС.Форма это ваша форма?

      Удалить
    6. Файлы объемом более 300кб я не передавал. Ошибки такой не видел. Ошибка возникла на стороне сервера, значит у них. В платформе 1С параметров не встречал.

      Удалить
    7. Анонимный08 мая, 2014 16:07

      Форма.Синхронизация_ФизЛиц_С_ИС. - да, это моя форма, в ней вызывается сторонний веб-сервис, который возвращает вышеуказанную ошибку.

      Еще для размышления:
      разработчики сторонней программы и этого стороннего веб-сервиса говорят, что у них все работает. И Говорят, что нужно где-то в 1С настроить такой параметр и приводят для примера настройку из другой программы:








      в общем смысл такой, что нужно увеличить настройку размера передаваемого значения.
      Но они могут ошибаться, т.к. не могут четко объяснить что к чему....

      Еще есть такая информация: что нужно поправить настройки web.config:
      Инет говорит следующее:
      Для исправления этой ситуации следует править следующую строку в Web.config:

      110
      Красным выделены интересующие нас «максимальная длина запроса» (в килобайтах) и «максимальное время обработки запроса» (в секундах).
      Для наиболее полной информации по редактированию данного файла следует смотреть раздел MSDN «Изменение файлов конфигурации ASP.NET» (http://msdn.microsoft.com/ru-ru/library/ackhksh7.aspx).

      Я написала свой веб-сервис в 1С, чтобы передать картинку. Вызываю его из SOAPUI - все отрабатывает - возвращает двоичный код картинки.

      Что из этого следует? Где все-таки надо настраивать?

      Удалить
    8. Анонимный08 мая, 2014 16:10

      Разработчики сторонней программы мне говорят, я где-то в 1С должна установить какой-то параметр больше.
      для этого мне приводят пример, как это делается в другой программе:
      bindings
      basicHttpBinding
      binding name="DirectorySoap" maxReceivedMessageSize = "2147483647"
      readerQuotas maxArrayLength="2147483647"/
      /binding
      /basicHttpBinding
      /bindings

      Удалить
    9. Это похоже на настройку IIS. Вы его используете? Не пробовали использовать апач?

      Удалить
  2. Fatal error: Uncaught SoapFault exception: [soap:Client] Неизвестная ошибка. {WebСервис.ПолучитьНомеклатуру.Модуль(27)}: Значение не является значением объектного типа (Добавить) по причине: {WebСервис.ПолучитьНомеклатуру.Модуль(27)}: Значение не является значением объектного типа (Добавить) in C:\inetpub\wwwroot\wsClient\nomen.php:5 Stack trace: #0 C:\inetpub\wwwroot\wsClient\nomen.php(5): SoapClient->__call('\xD0\x9F\xD0\xBE\xD0\xBB\xD1\x83\xD1\x87\xD0\xB8\xD1\x82\xD1...', Array) #1 C:\inetpub\wwwroot\wsClient\nomen.php(5): SoapClient->ПолучитьНоменклатуру() #2 {main} thrown in C:\inetpub\wwwroot\wsClient\nomen.php on line 5


    У меня выдается такая ошибка. было бы хорошо если бы расписали какие типы данных принимают свойства и типы пакетов XDTO

    ОтветитьУдалить