Создание документа через Odata




Принцип обмена данными из 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='\

19 Comments

  1. TODD22

    По производительности этого подхода не подскажешь?

    За какое время устанавливается соединение?

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

    И например 100 документов в цикле с установленным соединением?

    Сейчас передаю документы через web сервис. Передача одного документа 3-6 секунд с установкой соединения. Если в цикле передавать 100 документов с установленным соединением то каждый документ вместе с проведением передаётся за 2.2 секунды в среднем.

    Reply
  2. belovo3000

    (1) TODD22, на большом количестве не пробовал, по времени 1 документ также, (правда база на Postgresql), тут просто с самой публикацией заморачиваться не надо.

    Reply
  3. TODD22

    (2) А формат O-data он для локально сети?

    Как аналог web сервиса не используют? Вроде выставить с сеть как сервис?

    Reply
  4. belovo3000
  5. TODD22

    (4) Сколько читал так и не понимаю когда надо SOAP использовать, когда REST 🙂

    Надо видимо ещё что то почитать 🙂

    Reply
  6. belovo3000

    (5) TODD22, Ну как-то то же не задумывался, поэксперементировал с Odata, кажеться это проще чем Web-Сервис, (но только для обменов между 1С). Возможно и для сайта тоже хорошо. Во всяком случае вот тут очень интересное решение Offline-first javascript framework OData Rest клиент 1С

    Reply
  7. unpete

    (1) TODD22,

    Передача одного документа 3-6 секунд с установкой соединения

    Откуда и куда передаёте? Штатно, установка соединения + авторизация + установка параметров сеанса, должна занимать не более 50-70 миллисекунд. Конечно, от конфигурации сильно зависит. Особенно, от использования RLS и сложности запросов при установке параметров сеанса.

    Если надо передать много объектов, их логично сначала завернуть в массив или blob, затем отправлять одним запросом на сервер.

    Reply
  8. TODD22

    (7) unpete, У меня пока что две базы. На одном ПК.

    Одна база серверная. 1С сервер установлен на этом же ПК. Вторая файловая на этом же ПК.

    Из одной базы передаю в другую 3-4 параметра и таблицу со строками. Их то же не много. Но даже простая функция принимающая на вход два параметра один строка, второй дата.

    Отрабатывает за 6-10 секунд. После того как я сделал в передающей конфигурации WS-ссылку. Стало отрабатывать за 3-6 секунд.

    Конфигурация типовая Розница 2. Вэб сервис то же типовой у бонусного сервиса. Немного переписан под себя.

    Судя по замерам и тд. Дольше всего отрабатывает именно установка соединения. Потому что далее код выполняется быстро.

    Даже пробовал весь код выполняемый в функции сервиса закомментировать. Быстрее работать не стало.

    Может это из за проверки прав или ещё чего то… Даже и не знаю. В принципе сокращение времени с 6-10 до 3-6 секунд меня устроило. Но конечно хотелось бы 2-3 секунды.

    Reply
  9. unpete

    (8) TODD22,

    Может это из за проверки прав или ещё чего то

    Розницу 2 не открывал, но в УТ11, ERP2.1 и КА2.0, права пользователя очень важны. Разница может превышать 10 раз для обычного и полноправного пользователя. Это особенность RLS, для работы которого требуются сложные подготовительные запросы. При работе клиента 1С, это проблем не создаёт, т.к. параметры готовятся один раз при старте приложения, но каждое обращение к web и http сервисам — это полноценный сеанс, в начале которого выполняется такой же серверный код, что и при старте клиентского приложения.

    Есть много способов организовать быстрый http интерфейс, реализация может сильно отличаться в зависимости от задачи.

    Reply
  10. belovo3000

    (9) unpete, Евгений добрый день. Я знаю что с Odata Вы работаете давно. нигде в описании не мог найти как сделать так чтобы при загрузке проверялась ссылочная целостность. Например передаем номенклатуру в ТЧ документа, а в базе получателе такой номенклатуры нет. На данный момент заходишь в документ и там Объект не найден….?

    Reply
  11. TODD22

    (10)

    На данный момент заходишь в документ и там Объект не найден….?

    Я у себя сделал так. У меня в базу через вэб сервис передаются документы. НСИ передаётся по идентификатору. Если по идентификатору не найдено то создаётся новый пустой элемент которому назначается этот идентификатор.

    И отдельно сделана загрузка НСИ. При загрузке осуществляется поиск по идентификатору и дозаполняется элемент.

    Reply
  12. belovo3000

    (11) TODD22, а загрузка всех НСИ или только тех которые не найдены при передаче документа? Если только тех которые не найдены, как источник знает какие именно не найдены?

    Reply
  13. TODD22

    (12) На планах обмена можно сделать. Но с ним заморачиватся было некогда. Сделал просто через проверку ключевых полей. Они по мимо того что могут быть не заполнены, могут и изменится….

    Ну а так как в базу в которую я загружаю у меня там то же РИБ. То гружу не все, а только те что в результате проверки надо записать или обновить. А иначе всё это регистрируется в РИБ и по узлам расходится. У меня номенклатуры не много…. 2500 позиций. Обмен НСИ занимаетя несколько минут.

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

    Reply
  14. aspirator23

    (8) TODD22, использую web-сервис. Полный цикл(подключение-запрос-ответ) из интернета на 8.1 — 0,15сек, на 8.2 — 0.5 сек. Запрос простой, таблица для выборки небольшая, rls нет. Для скорости важно поддерживать активным соединение web-сервер — сервер 1с. На клиенте самый долгий — первый запрос. Его нужно актуализировать заранее.

    Reply
  15. Rif_md

    Перенос в формате xml более громоздкий чем в формате JSON. Если использовать параметры запроса ЗаголовокHTTP = Новый Соответствие(); ЗаголовокHTTP.Вставить(«Accept», «application/json»);

    то перенос можно организовать в более компактном формате JSON. А если базы однотипные, то можно ещё и json сериализацию использовать, тогда и текст запроса короче будет.

    У меня вот после переноса документа не получается его провести. Пытаюсь сделать

    ЗаголовокHTTP.Вставить(«POST » + АдресРесурса + «(guid'» + GUID + «‘)/Post()»);

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

    HTTPЗапрос = Новый HTTPЗапрос(АдресРесурса, ЗаголовокHTTP);

    HTTPОтвет = HTTPСоединение.POST(HTTPЗапрос);

    КодСостояния = HTTPОтвет.КодСостояния;

    Ответ = HTTPОтвет.ПолучитьТелоКакСтроку(«UTF-8»);

    но выдает код ошибки 500. Может у кого то получилось провести документ после переноса?

    Reply
  16. belovo3000

    (15) а что кроме кода ошибки в ответе приходит? У меня ошибка 500 была из-за скуля. Не уникальный идекс. Может у вас что-то с транзакциями?

    Reply
  17. belovo3000

    (15) попробуйте ответ в файл записать чтобы увидеть

    Reply
  18. Rif_md
    Reply
  19. Rif_md

    И коды внутренних ошибок

    Функция ОписаниеВнутреннегоКодаОшибкиOData(КодОшибки) Экспорт

    Описание = Новый Структура();

    Описание.Вставить(«Код0», «Возможность не поддерживается»);

    Описание.Вставить(«Код1», «Не удалось разобрать строку»);

    Описание.Вставить(«Код2», «Неверный формат запроса»);

    Описание.Вставить(«Код3», «Запрошенный тип представления не поддерживается»);

    Описание.Вставить(«Код4», «Неверное значение свойства»);

    Описание.Вставить(«Код5», «Остсутствует обязательное значение свойства»);

    Описание.Вставить(«Код6», «Неверный URL»);

    Описание.Вставить(«Код7», «Не хватает элемента ключа сущности»);

    Описание.Вставить(«Код8», «Тип сущности не найден»);

    Описание.Вставить(«Код9», «Экземпляр сущности не найден»);

    Описание.Вставить(«Код10″,»Запрошенное свойство не найдено»);

    Описание.Вставить(«Код11″,»Метод не найден»);

    Описание.Вставить(«Код12″,»Отсутствует обязательный аргумент метода»);

    Описание.Вставить(«Код13″,»Создание строк табличных частей напрямую не поддерживается»);

    Описание.Вставить(«Код14″,»Ошибка разбора опций запроса»);

    Описание.Вставить(«Код15″,»Сущность с таким ключом уже существует»);

    Описание.Вставить(«Код16″,»Не удалось присвоить свойство»);

    Описание.Вставить(«Код17″,»Объект не поддерживает режим загрузки данных»);

    Описание.Вставить(«Код18″,»Ошибка инициализации интерфейса OData: в объекте есть свойства с однаковыми именами»);

    Описание.Вставить(«Код19″,»Использованный HTTP-запрос запрещен в данном контексте»);

    Описание.Вставить(«Код20″,»Ошибка прав доступа»);

    Описание.Вставить(«Код21″,»Вызов нереализованной функции»);

    Возврат ?(Описание.Свойство(КодОшибки),Описание[КодОшибки],»Неизвестная ошибка»);

    КонецФункции

    Reply

Leave a Comment

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