Давайте подружим 1С и Android. Часть 2 — авторизуемся в 1С




Принцип обмена данными из 1С с сайтом (на MySQL) и выдачи (публикации) этих данных по запросу.
PHP-Скрипт автоматической загрузки данных из файла данных в формате CSV в базу данных сайта работающего на WordPress.

В продолжение моей темы: 1С:Альфа-Авто Автосалон Автосервис: обмен с сайтом.
С помощью данного скрипта можно загружать в автоматическом режиме, по расписанию, данные сервисных книжек (ремонтов авто) из 1С:Альфа-Авто Автосалон Автосервис.
Также можно загружать данные в ручном режиме: для этого делается скрытая страница, где размещается специальная кнопка.
Комментарии размещенные внутри скрипта разъяснят логику и порядок действия.
Комментарии с "/////    echo" использовались для отладки.
Дополнительно создана таблица для журналирования результатов загрузки данных.
Скрипт включает в себя защиту от SQL инъекций (думаю безопасность соблюдена в полной мере).
В кратце:
1. Пишется скрипт, который запускает этот.
2. Создается регламентное задание в WordPress, по которому запускается скрипт из п.1. 
3. Этот скрипт осуществляет проверку на существование файла обмена в папке.
4. Если данные не новые, загрузка не производится.
5. Если данные новые, очищается таблица сервисных книжек.
6. Загружаются новые данные.

Собственно сам скрипт:

<?php // Полная загрузка сервисных книжек, создан 2026-01-05 12:44:55

global $wpdb2;
global $failure;
global $file_hist;

/////  echo '<H2><b>Старт загрузки</b></H2><br>';

$failure=FALSE;
//подключаемся к базе
$wpdb2 = include_once 'connection.php'; ; // подключаемся к MySQL
// если не удалось подключиться, и нужно оборвать PHP с сообщением об этой ошибке
if (!empty($wpdb2->error))
{
/////   echo '<H2><b>Ошибка подключения к БД, завершение.</b></H2><br>';
$failure=TRUE;
wp_die( $wpdb2->error );
}

$m_size_file=0;
$m_mtime_file=0;
$m_comment='';
/////проверка существования файлов выгрузки из 1С
////файл выгрузки сервисных книжек
$file_hist = ABSPATH.'/_1c_alfa_exchange/AA_hist.csv';
if (!file_exists($file_hist))
{
/////   echo '<H2><b>Файл обмена с сервисными книжками не существует.</b></H2><br>';
$m_comment='Файл обмена с сервисными книжками не существует';
$failure=TRUE;
}

/////инициируем таблицу лога
/////если не существует файла то возврат и ничего не делаем
if ($failure){
///включает защиту от SQL инъекций и данные можно передавать как есть, например: $_GET['foo']
/////   echo '<H2><b>Попытка вставить запись в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>$m_comment));
wp_die();
/////    echo '<H2><b>Возврат в начало.</b></H2><br>';
return $failure;
}
/////проверка лога загрузки, что бы не загружать тоже самое
$masiv_data_file=stat($file_hist);   ////передаем в массив свойство файла
$m_size_file=$masiv_data_file[7];    ////получаем размер файла
$m_mtime_file=$masiv_data_file[9];   ////получаем дату модификации файла
////создаем запрос на получение последней удачной загрузки
////выбираем по штампу времени создания (редактирования) файла загрузки AA_hist.csv, $m_mtime_file

/////   echo '<H2><b>Размер файла: '.$m_size_file.'</b></H2><br>';
/////   echo '<H2><b>Штамп времени файла: '.$m_mtime_file.'</b></H2><br>';
/////   echo '<H2><b>Формирование запроса на выборку из лога</b></H2><br>';
////препарируем запрос
$text_zaprosa=$wpdb2->prepare("SELECT * FROM `vin_logs` WHERE `last_mtime_upload` = %s", $m_mtime_file);
$results=$wpdb2->get_results($text_zaprosa);

if ($results)
{   foreach ( $results as $r)
{
////если штамп времени и размер файла совпадают, возврат
if (($r->last_mtime_upload==$m_mtime_file) && ($r->last_size_upload==$m_size_file))
{////echo '<H2><b>Возврат в начало, т.к. найдена запись в логе.</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>$m_mtime_file,'last_size_upload'=>$m_size_file,'comment'=>'Загрузка отменена, новых данных нет, т.к. найдена запись в логе.'));
wp_die();
return $failure;
}
}
}
////если данные новые, пишем в лог запись о начале загрузки
/////echo '<H2><b>Попытка вставить запись о начале загрузки в лог таблицу</b></H2><br>';
$insert_fail_zapros=$wpdb2->insert('vin_logs', array('time_stamp'=>time(),'last_mtime_upload'=>0, 'last_size_upload'=>$m_size_file, 'comment'=>'Начало загрузки'));

////очищаем таблицу
$clear_tbl_zap=$wpdb2->prepare("TRUNCATE TABLE %s", 'vin_history');
$clear_tbl_zap_repl=str_replace("'","`",$clear_tbl_zap);
$results=$wpdb2->query($clear_tbl_zap_repl);
/////   echo '<H2><b>Очистка таблицы сервисных книжек</b></H2><br>';
if (empty($results))
{
/////   echo '<H2><b>Ошибка очистки таблицы книжек, завершение.</b></H2><br>';
//// если очистка не удалась, возврат
$failure=TRUE;
wp_die();
return $failure;
}

////загружаем данные
$table='vin_history';         // Имя таблицы для импорта
//$file_hist Имя CSV файла, откуда берется информация     // (путь от корня web-сервера)
$delim=';';          // Разделитель полей в CSV файле
$enclosed='"';      // Кавычки для содержимого полей
$escaped='\

48 Comments

  1. infosoft-v

    Отличная статья. Спасибо.

    Reply ↓
  2. Ann.prog1C

    Полезно. Спасибо.

    Reply ↓
  3. flyer

    продолжайте слежу за темой

    Reply ↓
  4. gudun_ku

    Отличная статья. Код под Андроид хорош, оценил, что легко переложить на net cf, чем я и займусь как-нибудь. Жду продолжения, про сканирование.

    Reply ↓
  5. Ibrogim

    Вот теперь ставлю плюс.

    Вы кстати вдохновили меня тоже статью написать)

    Reply ↓
  6. WKBAPKA

    о, отличная статья. спасибо

    Reply ↓
  7. Yashazz

    Вообще не очень понимаю, нафига это всё… Мобильное приложение — кривоватое побочное дитя попытки 1С угнаться за модой, которое однажды канет в прошлое, как веб-расширения и тому подобная мутота… Охота вам силы тратить.

    Reply ↓
  8. solarisman

    (7) Yashazz, Не понял, при чем тут мобильное приложение 1С? Приложение нативное, написанное на Java. По поводу

    канет в прошлое

    — дык все в прошлое канет, что же теперь, ничего не делать? Google задумывается переходить с Java и заглядывается на другие языки. Никто же разработку не бросает.

    Reply ↓
  9. dj_serega

    Почему не на http?

    Reply ↓
  10. solarisman

    (9) dj_serega, Вообще не понял, о чем речь здесь? HTTP — протокол передачи данных, SOAP — протокол доступа к объектам, который использует HTTP.

    Reply ↓
  11. solarisman

    Коллеги, давайте по существу, хочется конструктивной критики.

    Reply ↓
  12. Nenaviju1C

    Нечто подобное тоже себе организовал:

    База (если так можно сказать) 1с — самописная конфиг-я для формирования ежемесячных отчетов по счетчикам гор/хол воды для ЖКХ с рассылкой определенную дату по регламенту от своего имени (*@mail.ru) на почту ЖКХ.

    Мобильное приложение написано на java (Android Studio). Транспорт использую тот же — WS (soap).

    Писал ЭТО для себя от нечего делать 🙂 … да и надоело счета собирать в папке на компе вручную ))

    По потоку:

    Из базы передается список XDTO состоящий из GUID «документа-отчета» и даты отчета.

    Каждая строка содержит список XDTO (номер счетчика + замеры до и после)

    На приложении:

    Всего 3 layout:настройки, список документов, детализация по документу (она же регистрация).

    Можно было и динамически клепать их, но лениво все описывать было да и отлавливать баги потом 🙂

    При открытии layouta документа (в т.ч. и нового) уже установлены номера счетчиков — только надо внести показания и все.

    Как то так …

    Мобильное приложение — кривоватое побочное дитя попытки 1С угнаться за модой, которое однажды канет в прошлое, как веб-расширения и тому подобная мутота…

    Что за консерватизм?? Мобильные устройства в бизнесе используются все больше и больше.

    «Мутота» — это для тех кто не знает КАК.

    Reply ↓
  13. solarisman

    (13) DitriX, А на что обижаться, все правильно написано.

    Конечно, мне до ваших статей далеко, но и на фурор я не рассчитывал.

    Reply ↓
  14. Yashazz

    (13) DitriX, а ещё от таких статей на ровном месте растёт «рейтинг»))) И потом начинаются распальцовки)

    Reply ↓
  15. Yashazz

    (12) Nenaviju1C,

    Что за консерватизм?? Мобильные устройства в бизнесе используются все больше и больше.

    «Мутота» — это для тех кто не знает КАК.

    Ясно. Внимательно читать не умеете. Я же не про мобильные устройства «вообще» написал. Я про мобильное приложение 1С. И кое-что о перспективах оного знаю.

    Reply ↓
  16. solarisman

    (15) Yashazz, Ладно, ладно, еще одну статью напишу в завершение и все, честно-честно. А то ишь, покусился на святой рейтинг топ-авторов.

    Вот честно, как дети. Напишите то же самое приложение с использованием

    (http реквесты, odata, json)

    покажите что оно лучше во всем и тогда я скажу что я опозорился и статьи удалю вообще

    Reply ↓
  17. solarisman

    (16) Yashazz,

    И кое-что о перспективах оного знаю.

    Поделитесь секретом-то. А то я понять не могу, почему у нас региональный пищевой холдинг автоматизируется на мобильной платформе.

    Reply ↓
  18. CheBurator

    (18) как заавтоматизируется и выйдет в плановую работу — отпишитесь. порадуемся или поплачем вместе…

    Reply ↓
  19. WKBAPKA

    (13) DitriX, я поддержу автора. Для меня, как человека, который изучает Андроид, эта статья очень полезна. Меня больше интересует не подача текста, а примеры, которые автор очень любезно выложил.

    Reply ↓
  20. mkostya

    Все отлично, заработало. Жду следующей статьи.

    Там будет и критика и предложения))

    Reply ↓
  21. solarisman

    (22) Значит буду делать работу над ошибками 🙂

    Постараюсь написать статью 2-3 числа, был в командировке и все не разберусь с текущими задачами

    Reply ↓
  22. agent00mouse

    2-3 Апреля? Долго, ну да ладно, ждём. Пилю приложение на базе 1С мобильного приложения, Да, функционал/идею протестировать годно, что серьёзнее и удобнее — нативный Java ведра нужен. т.ч. жду следующей статьи. Спасибо за разработку.

    И вопрос, soap конечно удобна, сам через неё работаю, но быстрее и главное проще же дёргать 1С за URL через HTTPСервисы, и как решается вопрос безопасного подключения(ssl)?

    Кому интересно, HTTPСервисы (I5-2500 (3,3GHz), 8Gb RAM, Пустая процедура ответа ~ 20000 подключений одновременно, с живой процедурой думаю цифра просядет)

    Reply ↓
  23. user706108_prog

    Если в 1С не создавать пакет XDTO, то из мобильного приложения нельзя будет подсоединиться к веб-сервису 1С? В чем суть пакета XDTO?

    Тимур, а Вы в своем мобильном приложении какие библиотеки используете?

    ksoap2 используете или нет?

    Reply ↓
  24. Serg O.

    главная идея вообще — БОМБА!

    1) мобильная платформа 1С — не нужна вообще!

    2) вместо этого — делаем красивое и простое приложение…

    в среде разработки — Android Studio

    3) с 1С — «общаемся» через web-сервисы 1С (или другие запросы кому как нравится)

    со стороны 1С — всё понятно, делаем web-сервисы и публикуем на «где-нибудь»

    мы тоже до этого «дошли» не так давно… один чел. у нас на Android Studio пишет

    другой — web-сервисы для него клепает…

    для программистов 1С — конечно трудновато «вспониминать» универ — создавать классы, методы, java язык с кучей скобок,

    вспоминать основы ООП…

    но визуальная среда — Android Studio — достаточно легка и интуитивно-понятна и многие вещи делаются по щелчку мыши

    в youtube — есть циклы обучения по Android Studio — я пару вечеров посидел… и вроде «вкурил» тему…

    Более того, Android Studio имеет автоматическую синхронизацию проекта в github

    Предлагаю проект выложить в Github — и далее там развивать эту тему…

    для «живого» проекта — наверное все-таки лучше приглашать отдельного java-программиста

    Reply ↓
  25. WKBAPKA

    (26) круто конечно пишешь, но не все там просто в этом Андроид 😉

    все же Андроид будет покруче и посложнее 1С в изучении 🙂

    и поверь, сам язык Java фигня, а вот API изучить, приемы работы….

    за пару дней вьехать, это ты конечно немножечко преувеличил

    Reply ↓
  26. WKBAPKA

    (26) а что там в студио по щелчку мыши можно сделать?

    layout-файл создать?

    😉

    Reply ↓
  27. WKBAPKA

    (26) мобильная платформа знаешь где нужна, когда тебе надо сделать приложение со всеми этими иерархическими справочниками и т.п.

    поверь, на андроид ты таких готовых view не найдешь … в этом сила мобильного приложения.

    а если тебе надо написать приложени чисто под андроид и специфичное, то согласен, мобильная платформа тут не нужна

    Reply ↓
  28. silberRus

    Поставил плюс.

    Пожелание:

    — сделать на http сервисе, через rest+json. SOAP + XML все таки громоздкий, как по отношению к данным так и по отношению к реализации.

    — добавить юнит тесты

    Reply ↓
  29. ECartman

    Настроил сервер 1с по инструкции. Скачал проект для Android. Теперь если я захожу браузером по ссылке, то получаю xml файл — т.е. вроде со стороны 1с все работает. Но вот если запускаю apk на андроиде то получаю сообщение «невозможно соединиться с сервером». Подскажите куда копать?

    Reply ↓
  30. ECartman

    (31) В классе MainActivity

    в параметрах подключения проставляю

    public void saveSettings(View v){
    
    soapParam_server    = etSoapServer.getText().toString();
    soapParam_database  = etSoapDatabase.getText().toString();
    soapParam_user      = etSoapUser.getText().toString();
    soapParam_pass      = etSoapPass.getText().toString();
    soapParam_timeout   = Integer.parseInt(etSoapTimeout.getText().toString());
    soapParam_URL       = «http://» + soapParam_server + «/» + soapParam_database + «/ws/AcceptingOrders.1cws?wsdl»;
    
    SharedPreferences.Editor editor = preferences.edit();
    editor.putString(«server», soapParam_server);
    editor.putString(«database», soapParam_database);
    editor.putString(«user», soapParam_user);
    editor.putString(«pass», soapParam_pass);
    editor.putString(«URL»,        soapParam_URL);
    editor.putInt(«timeout», soapParam_timeout);
    editor.apply();
    
    setContentView(R.layout.activity_main);
    startExchange(ACTION_GetLoginList);
    
    }
    
    protected void initiateConnectionSettings(){
    
    soapParam_server    = preferences.getString(«server», «192.168.0.25»);
    soapParam_database  = preferences.getString(«database», «AcceptingOrders»);
    soapParam_user      = preferences.getString(«user», «WSUser»);
    soapParam_pass      = preferences.getString(«pass», «1234»);
    soapParam_URL       = preferences.getString(«URL», «http://192.168.0.25/AcceptingOrders/ws/AcceptingOrders.1cws?wsdl»);
    soapParam_timeout   = preferences.getInt(«timeout», 10);
    
    }

    Показать

    Reply ↓
  31. ECartman
    Reply ↓
  32. WKBAPKA
    @Override
    protected String doInBackground(Void… voids) {
    try {
    
    DataXML.append(«Подключение к серверу …»);
    sendMessage(MainActivity.STATUS_MESSAGE);
    
    SoapObject request = new SoapObject(NAMESPACE, METHOD_NAME);
    request.addProperty(«IDLogin»,mlogin);
    SoapSerializationEnvelope envelope = new SoapSerializationEnvelope(SoapEnvelope.VER11);
    envelope.setOutputSoapObject(request);
    //envelope.dotNet = true;
    //envelope.implicitTypes = true;
    HttpTransportSE androidHttpTransport = new HttpTransportBasicAuthSE(URL, «pass», «pass»);
    androidHttpTransport.debug = true;
    try {
    DataXML.append(«Получение данных …»);
    sendMessage(MainActivity.STATUS_MESSAGE);
    
    androidHttpTransport.call(SOAP_ACTION, envelope);
    SoapObject resultRequestSOAP = (SoapObject) envelope.bodyIn;
    //Log.d(LOG_TAG, resultRequestSOAP.toString());
    return envelope.getResponse().toString();
    //return resultRequestSOAP.toString();
    }catch (Exception e){
    DataXML.append(«Ошибка при получении данных <» + e.toString() + «>»);
    sendMessage(MainActivity.STATUS_ERR);
    //                e.printStackTrace();
    }
    
    
    }catch (Exception e) {
    DataXML.append(«Ошибка при подключении к серверу <«+ e.toString() + «>»);
    sendMessage(MainActivity.STATUS_ERR);
    //            Log.d(LOG_TAG,e.toString());
    //            e.printStackTrace();
    }
    return «»;
    };
    
    

    Показать

    рабочий код из моего проекта

    Reply ↓
  33. ECartman

    (34)

    рабочий код из моего проекта

    Прошу прощения, это модификация этого проекта или это кусок кода из Вашего проекта? Просто не понимаю, как это может быть для меня полезно? В какой класс это нужно вставить и откуда нужно вызывать функцию «doInBackground»? Просто у меня проблема с подключением. Андроид просто не может обратиться к серверу. Библиотеку ksoap2 я качал jar и подключал через меню file->project structure->dependies.

    Reply ↓
  34. WKBAPKA

    Вы можете убрать лишнее и использовать в своем проекте. Я скопировал код реализации получения данных с помощью класса AsynkTask . За основу брал пример из этой статьи.

    Reply ↓
  35. WKBAPKA

    Проект мой, рабочий. Ещё обратите внимание на релиз платформы 1С. Проверять работу веб сервиса советую другими средствами.

    Reply ↓
  36. WKBAPKA

    я использовал SoapUI 5.3.0

    Reply ↓
  37. WKBAPKA

    Еще обратите внимание, что на 1С:Предприятие 8.3 (8.3.9.1850) работать не будет, нужно ставить свежее платформу

    Reply ↓
  38. zoytsa

    Коллеги, запутался с создагнием XDTO-пакета.

    Как LoginList создать, состоящий из Login?

    Вот так пробую (и еще много как):

    <xs:schema xmlns:tns=»AcceptingOrdersService» xmlns:xs=»http://www.w3.org/2001/XMLSchema» targetNamespace=»AcceptingOrdersService» attributeFormDefault=»unqualified» elementFormDefault=»qualified»>
    <xs:complexType name=»Login»>
    <xs:sequence>
    <xs:element name=»ID»/>
    <xs:element name=»Description»/>
    </xs:sequence>
    </xs:complexType>
    <xs:complexType name=»LoginList»>
    <xs:sequence>
    <xs:element name=»Login» type=»tns:Login»/>
    </xs:sequence>
    </xs:complexType>
    <xs:complexType name=»LoginResult»>
    <xs:sequence>
    <xs:element name=»Result»/>
    <xs:element name=»Name»/>
    </xs:sequence>
    </xs:complexType>
    </xs:schema>

    Показать

    Reply ↓
  39. user966277

    (0)Здравствуйте помогите вывести qr-код в табличный документ

    &НаКлиенте
    Процедура печатькода(Команда)
    ТабДок1=внешнийQR();
    Если ТабДок1<>Ложь Тогда
    ТабДок1.Показать();
    КонецЕсли;
    
    КонецПроцедуры
    
    
    
    
    &Наклиенте
    Функция внешнийQR()
    
    
    
    
    #Если МобильноеПриложениеКлиент  Тогда
    
    
    НовВз = Новый ЗапускПриложенияМобильногоУстройства();
    НовВз.Действие=»com.google.zxing.client.android.ENCODE»;
    НовВз.ДополнительныеДанные.Добавить(«ENCODE_DATA»,»123″,»String»);
    НовВз.ДополнительныеДанные.Добавить(«ENCODE_TYPE»,»Text»,»String»);
    НовВз.ДополнительныеДанные.Добавить(«ENCODE_FORMAT»,»UPC_A»,»String»);
    НовВз.Запустить(Истина);
    
    //Для Каждого Стр Из НовВз.ДополнительныеДанные Цикл
    //Сообщить(Стр.Ключ+» — «+Стр.Значение);
    // КонецЦикла;
    
    
    Для Каждого Стр Из НовВз.ДополнительныеДанные Цикл
    
    Сообщить(Стр.Ключ+» — «+Стр.Значение);
    
    КонецЦикла;
    
    
    #КонецЕсли
    
    
    
    
    
    КонецФункции // внешнийQR()

    Показать

    (0)

    Reply ↓
  40. WKBAPKA

    в последних проектах я использовал библиотеку Retrofit. В 1С HTTP сервис.

    Reply ↓
  41. Denzip

    (11)отличная статья. Какая тут критика?

    Я давно искал способ написать интернет магазин со связью 1с. По ходу нашёл точку опоры!

    Огромное Вам спасибо за труды. Это многого стоит

    Reply ↓
  42. fimbulwinter

    Спасибо, полезная статья.

    Reply ↓
  43. fimbulwinter

    может кто нибудь выложить проект на гитхаб?

    Reply ↓
  44. WKBAPKA

    (45) проще подключить Retrofit

    Reply ↓
  45. WKBAPKA

    (46) зачем? )

    Reply ↓
  46. WKBAPKA

    (30) Retrofit

    Reply ↓
  47. WKBAPKA

    статья устаревшая. Лучше попробуйте Retrofit.

    Проще отлаживать, проще подключать, можно работать с GSon

    вот пример интерфейса:

    public interface ApiService {
    @GET(«{secondURL}/{id}/products»)
    Call<List<RetrofitProduct>> getListProducts(@Path(value =  «secondURL», encoded = true) String secondURL, @Path(«id») String id_client);
    
    }

    вот вызов

       response = mApiService.getReport(
    App.getDefaultSecondURL(),
    App.CONSTANT_ID_PARTNER,
    «reports»,
    retrofitDataReport).execute();
    
    if (response.isSuccessful()) {
    updateInfoReportData(idReport, response.body());
    }else {
    String errorMessage = response.raw().toString() + »
    »
    + response.errorBody().string();
    updateInfoReportData(idReport, response.raw().toString());
    MyLog.d(errorMessage);
    res.putString(KEY_EVENT_TEXT_ERROR, errorMessage);
    
    }

    Показать

    Reply ↓
  48. WKBAPKA

    пример приведен синхронного вызова, т.к. работает с помощью WorkManager

    Reply ↓

Leave a Comment

Ваш адрес email не будет опубликован. Обязательные поля помечены *