Работаем с сервисом подсказок на формах: dadata.ru




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

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

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

<?php // Полная загрузка сервисных книжек, создан 2024-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='\

30 Comments

  1. xzorkiix
    &НаСервере
    Процедура УстановитьПол(gender)
    
    Если gender = «MALE» Тогда
    Объект.Пол = Перечисления.ПолФизическогоЛица.Мужской;
    ИначеЕсли gender = «FEMALE» Тогда
    объект.Пол = Перечисления.ПолФизическогоЛица.Женский;
    КонецЕсли;
    
    КонецПроцедуры

    Показать

    Здесь достаточно на стороне клиента использовать ПредопределенноеЗначение(«Перечисление.ПолФизическогоЛица.Женский»);

    Reply
  2. xzorkiix
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(СтрокаXML);
    ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);

    wsСсылка, как таковая и не нужна для разбора ответа сервиса.

    Reply
  3. qwinter

    (2) xzorkiix, получается двойная работа, сначала создай wsСсылку и ее свойства, а затем еще надо присвоить им значения. Кода столько же как при HTTPЗапросе, плюс созданный объект в конфигурации. Вот в упор не вижу преимуществ при их использовании.

    Reply
  4. xzorkiix

    (3) я хотел указать xdto пакет не нужен (оговорился). По крайней мере в рамках описанной задачи. wsСсылка о другом об обмене через протокол SOAP. В вашем случае при разборе ответа нет необходимости расписывать модель сервиса. Достаточно перед обращением к свойствам сервиса проверять их наличие.

    Это если для разбора XML строки вы используете объекты XDTO

    Функция Свойство(ОбъектXDTO, ИмяСвойства, Значение = Неопределено)
    Результат = Не (ОбъектXDTO.Свойства().Получить(ИмяСвойства) = Неопределено);
    Если Результат Тогда
    Значение = ОбъектXDTO[ИмяСвойства];
    КонецЕсли;
    Возварт Результат;
    КонецФункции
    
    // . . .
    
    Предложения = Неопределено;
    Если Свойство(ОбъектXDTO, «suggestions», Предложения) Тогда
    // . . .
    КонецЕсли;
    

    Показать

    Reply
  5. qwinter

    (4) xzorkiix, коментарии как обычно полезнее статьи)))

    Reply
  6. bonv

    1.

    &НаКлиенте
    Функция ПолучитьОбъектXDTO(Текст, КоличествоПодсказок = 10)

    Не делайте это на клиенте, в веб-клиенте работать не будет, в нем нет объекта XDTO. Лучше делать внеконтекстный вызов сервера.

    2.

    Тело = ОтветHTTP.ПолучитьТелоКакСтроку();
    Если Тело = «» Тогда

    Это не совсем корректная проверка, если сервер вернет ошибку, то в теле будет ее описание.

    Надо использовать проверку на код состояния

    Если ОтветHTTP.КодСостояния = 200 Тогда

    3.

    структурныйТип = ФабрикаXDTO.Тип(«http://www.sample-package.org», «SuggestFioResponse»);
    
    МойXML = Новый ЧтениеXML;
    МойXML.УстановитьСтроку(Тело);
    ОбъектXDTO = ФабрикаXDTO.ПрочитатьXML(МойXML, структурныйТип);
    
    Возврат ОбъектXDTO;

    Собственно определять в метаданных и затем использовать объект XDTO это лишнее.

    Используете анонимный объект XDTO.

    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(Тело);
    ЧтениеXML.ПерейтиКСодержимому();
    ОбъектXDTO = Фабрика.ПрочитатьXML(ЧтениеXML);
    ЧтениеXML.Закрыть();
    
    Возврат ОбъектXDTO;

    4.

    &НаКлиенте
    Процедура НаименованиеАвтоПодбор(Элемент, Текст, ДанныеВыбора, Параметры, Ожидание, СтандартнаяОбработка)

    В этом методе лучше подключать обработчик ожидания, скажем на 0.5 сек и уже в нем заполнять список.

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

    Reply
  7. qwinter

    (6) doxflow, спасибо)

    Reply
  8. chmv

    Любопытно, но не более

    Reply
  9. Патриот

    Более чем любопытно, плюсую, хороший пример демонстрации возможностей работы с интернет сервисом.Комментаторам тоже спасибо за дополнения, сейчас попробую реализовать.

    Reply
  10. lyalius

    Отличная статья, только пример нерабочий.

    Проблема вот в этих двух строках:

    HTTPЗапрос.Заголовки.Вставить(«Authorization», «ВАШ КОД АВТОРИЗАЦИИ»);

    HTTPЗапрос.УстановитьТелоИзСтроки(«» + Текст + «» + КоличествоПодсказок + «»);

    Правильно так:

    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token ВАШ КОД АВТОРИЗАЦИИ»);

    HTTPЗапрос.УстановитьТелоИзСтроки(«<req><query>» + Текст + «</query><count>» + КоличествоПодсказок + «</count></req>»);

    Еще момент: если по введенной пользователем строке ничего не будет найдено, то код сломается на строчке

    > Если ТипЗнч(ОбъектXDTO.suggestions) = Тип(«СписокXDTO») Тогда

    потому что свойство suggestions в ответе отсутствует.

    Reply
  11. Патриот

    (10) lyalius, спасибо, так работает

    Reply
  12. mnemchinov

    У меня заработало только так:

    HTTPЗапрос.УстановитьТелоИзСтроки(«<req><query>» + Текст + «» + КоличествоПодсказок + «</query></req>»);

    И еще вместо:

    Если ТипЗнч(ОбъектXDTO) = Тип(«ОбъектXDTO») Тогда
    Если ТипЗнч(ОбъектXDTO.suggestions) = Тип(«СписокXDTO») Тогда

    сделал:

    Если ТипЗнч(ОбъектXDTO) = Тип(«ОбъектXDTO») Тогда
    Если ОбъектXDTO.Свойства().Получить(«suggestions») <> Неопределено Тогда
    Если ТипЗнч(ОбъектXDTO.suggestions) = Тип(«СписокXDTO») Тогда

    и тогда окончательно заработало

    Reply
  13. oslokot

    Всем привет! у этого сервиса, как я понимаю, сменилась версия api и теперь прежняя не работает.

    Пытаюсь сделать как в примере, но сервер возвращает ошибку 400.

    Текст ошибки: {«detail»: «JSON parse error — No JSON object could be decoded»}

    Странно, вроде все как надо сделал.

    HTTPЗапрос = Новый HTTPЗапрос();
    HTTPЗапрос.АдресРесурса = «/api/v2/clean»;
    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/json»);
    HTTPЗапрос.Заголовки.Вставить(«charset», «UTF-8»);
    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token API-ключ»);
    HTTPЗапрос.Заголовки.Вставить(«X-Secret», «Секретный ключ»);
    Стр = «{ «»structure»»: [ «»ADDRESS»» ], «»data»»: [ [ «»Москва, Милютинский 13″» ] ] }»;
    HTTPЗапрос.УстановитьТелоИзСтроки(Стр,КодировкаТекста.UTF8);
    Соединение = Новый HTTPСоединение(«dadata.ru»,,,,Новый ИнтернетПрокси,, Новый ЗащищенноеСоединениеOpenSSL);
    ОтветHTTP = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
    
    Сообщить(ОтветHTTP.КодСостояния);
    Тело = ОтветHTTP.ПолучитьТелоКакСтроку();
    Сообщить(Тело);
    

    Показать

    Может кто уже делал, подскажите пожалуйста где я наколбасил

    Reply
  14. oslokot

    (13) Сам себе и отвечу) Разобрался. Нужно перезаписать файл в кодировке UTF-8 без BOM и отправить серверу.

    В общем неплохой сервис dadata.ru, жаль платный.

    Reply
  15. anton.fly7

    (14) покажите как вы устанавливаете тело НТТРЗапроса?

    я делаю из строки, возвращает такую же ошибку

    ТекстЗапроса = «[ «»NAME»», «»PHONE»» ], «»data»»: [ [ «»» + Парам.Наименование + «»», «»» + Парам.Телефон + «»» ] ]»;
    
    ТекстЗапроса = «{ «»structure»»: » + ТекстЗапроса + » }»;
    
    HTTPЗапрос = Новый HTTPЗапрос();
    HTTPЗапрос.АдресРесурса = «/api/v2/clean»;
    
    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/json»);
    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token » + Token);
    HTTPЗапрос.Заголовки.Вставить(«X-Secret», Secret);
    HTTPЗапрос.Заголовки.Вставить(«charset», «UTF-8»);
    HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса);
    

    Показать

    Reply
  16. anton.fly7

    (15) сам себе отвечу )

    ИмяВременногоФайла = ПолучитьИмяВременногоФайла();
    ЗТ = Новый ЗаписьТекста(ИмяВременногоФайла, КодировкаТекста.ANSI);
    ЗТ.Закрыть();
    ЗТ = Новый ЗаписьТекста(ИмяВременногоФайла,,, Истина, Символы.ПС);
    ЗТ.Записать(ТекстЗапроса);
    ЗТ.Закрыть();
    
    HTTPЗапрос = Новый HTTPЗапрос();
    HTTPЗапрос.АдресРесурса = «/api/v2/clean»;
    
    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/json»);
    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token » + Token);
    HTTPЗапрос.Заголовки.Вставить(«X-Secret», Secret);
    HTTPЗапрос.Заголовки.Вставить(«charset», «UTF-8»);
    //HTTPЗапрос.УстановитьТелоИзСтроки(ТекстЗапроса, КодировкаТекста.UTF8);
    HTTPЗапрос.УстановитьТелоИзДвоичныхДанных(Новый ДвоичныеДанные(ИмяВременногоФайла));
    

    Показать

    Reply
  17. Zixxx

    А что там по поводу SOAP?

    По адресу: http://confluence.hflabs.ru/pages/viewpage.action?... существуют подсказки для протокола SOAP. Пробовал обратиться к wsdl по адресу: http://dadata.ru/suggestions/api/4_1/ws?wsdl, но в ответ получаю ошибку:

    Error loading [http://dadata.ru/suggestions/api/4_1/ws?wsdl]: org.apache.xmlbeans.XmlException: org.apache.xmlbeans.XmlException: error: does not close tag

    Я так понимаю что это неправильный адрес, но какой тогда правильный?

    Reply
  18. lyalius

    (17) Zixxx, правильный вот этот https://dadata.ru/api/suggest. Та страница, на которую вы сослались — это чтобы вызвать подсказки, которые у вас внутри на сервере установлены.

    Reply
  19. lyalius

    (14) oslokot, спасибо 🙂 Посмотрите, у нас теперь и подсказки по ЕГРЮЛ есть.

    Reply
  20. Zixxx

    (18) lyalius, И где там по ссылке wsdl?

    Reply
  21. oslokot

    (19) lyalius, Да-да, видел.

    Может как-нибудь воспользуюсь

    Пока что использую свою обработку по данным контур.фокус

    Ну а так, конечно молодцы, хорошая работа!

    Желаю вам успешного развития проекта!

    Reply
  22. lyalius

    (20) Zixxx, wsdl доступна тем, кто поставил сервис у себя в корпоративной сети. Вы не ставили и вам она не доступна. Вам нужно пользоваться вот этим сервисом: https://dadata.ru/api/suggest.

    Reply
  23. lyalius

    (21) oslokot, спасибо, очень приятно слышать такие пожелания 🙂

    Reply
  24. olegmedvedev
    HTTPЗапрос = Новый HTTPЗапрос();
    HTTPЗапрос.АдресРесурса = «/api/v2/suggest/address»;
    
    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/xml»);
    HTTPЗапрос.Заголовки.Вставить(«Accept», «application/xml»);
    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token «+Token);
    HTTPЗапрос.УстановитьТелоИзСтроки(«<req><query>» + Текст + «</query></req>»);
    
    Соединение = Новый HTTPСоединение(«dadata.ru»,,,,,, Новый ЗащищенноеСоединениеOpenSSL);
    
    ОтветHTTP = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
    

    Показать

    вот такой код работает сейчас

    Reply
  25. olegmedvedev
    HTTPЗапрос = Новый HTTPЗапрос();
    HTTPЗапрос.АдресРесурса = «/api/4_1/rs/suggest/address»;
    
    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/xml»);
    HTTPЗапрос.Заголовки.Вставить(«Accept», «application/xml»);
    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token «+Token);
    HTTPЗапрос.УстановитьТелоИзСтроки(«<req><query>» + Текст + «</query></req>»);
    
    Соединение = Новый HTTPСоединение(«suggestions.dadata.ru/suggestions»,,,,,, Новый ЗащищенноеСоединениеOpenSSL);
    
    ОтветHTTP = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
    

    Показать

    изменился API dadata

    Reply
  26. vaad80

    народ, подскажите плз, делаю запрос по последней инструкции, апи текущий, как в инструкции, а в ответ приходит код ответа 200 и соответствие

    «Content-Type»:»application/xml; charset=UTF-8″

    «Connection»:»keep-alive»

    «X-Powered-By»:»Undertow/1″

    «Vary»:»Accept-Encoding»

    «Server»:»nginx/1.13.0″

    «Date»:»Wed, 24 May 2017 17:46:12 GMT»

    «Transfer-Encoding»:»chunked»

    Поля suggestions нет(( в чем может быть ошибка?, вызываю &насервере

    код
    Reply
  27. olegmedvedev

    (26) Ответ кода 200 — «Запрос успешно обработан», все верно делаете). Сейчас гляну и может подскажу подробнее.

    Reply
  28. vaad80

    (27)

    Приветствую, со структурой разобрался, не выходило потому что результат не вогнал в тело. Сейчас приходит тегированная структура,<suggest><suggest>

    Всё ок, но это не устраивает, хотелось бы чтобы приходило в json,так как он легче и быстрее парсится 1ской.

    Затык происходит у меня при json на выставке параметра запроса, текста который ищу. Строкой не проходит, возможно надо туда объект json-a впихнуть…

    Может есть рабочий код с json?

    Reply
  29. tonimontana05

    (28)

    HTTPЗапрос = Новый HTTPЗапрос();

    HTTPЗапрос.АдресРесурса = «/api/4_1/rs/suggest/address»;

    HTTPЗапрос.Заголовки.Вставить(«Content-Type», «application/json»);

    HTTPЗапрос.Заголовки.Вставить(«Accept», «application/json»);

    HTTPЗапрос.Заголовки.Вставить(«Authorization», «Token » + «Свой токен»);

    HTTPЗапрос.УстановитьТелоИзСтроки(«{«+»»»query»»» + «:» + «»»» + Текст + «»»» + «,» +»»»count»»» + «:» + «» + КоличествоПодсказок + «» + «}»,

    КодировкаТекста.UTF8,

    ИспользованиеByteOrderMark.НеИспользовать);

    Соединение = Новый HTTPСоединение(«suggestions.dadata.ru/suggestions»,,,,Новый ИнтернетПрокси,, Новый

    ЗащищенноеСоединениеOpenSSL);

    Ответ = Соединение.ОтправитьДляОбработки(HTTPЗапрос);

    ОтветСервера = Ответ.ПолучитьТелоКакСтроку(КодировкаТекста.UTF8);

    Чтение = Новый ЧтениеJSON;

    Чтение.УстановитьСтроку(ОтветСервера);

    Данные = ПрочитатьJSON(Чтение);

    Чтение.Закрыть();

    Reply
  30. JetBrain

    Сервис выдает абсолютно не актуальную информацию, ИП три раза уже закрылось и открылось , а карточка старая из ФНС загрузилась, проверили пол базы из них 50-60 % только актуально у тех кто ни чего не менял в регистрации.

    Reply

Leave a Comment

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