Стандартний обмін 1С-Бітрікс на BASH: Докладний розбір скрипта инкрементальной вивантаження

Для забезпечення синхронізації каталогу товарів між системою 1С і сайтом на системі управління Бітрікс, використовується стандартний для Битрикса протокол обміну XML файлами у форматі CommerceML, заснований на передачі від 1С до Битриксу HTTP GET і POST запитів з певними параметрами, і отриманні стандартних відповідей, що містять статус операції, що позначає результат її виконання.

першій статті цієї серії дано обґрунтування можливості застосування окремого скрипта, що бере сформовані 1С або іншою системою або програмою, XML файли, і передавального їх Битриксу, використовуючи стандартний протокол.

У цій статті я дам докладні коментарі до кожної рядку скрипта. Це дозволить спростити його модифікацію під ваші потреби.

Скрипт написаний на BASH і є одним з кількох сценаріїв, що забезпечують різні обміни через один і той же стандарт, який пропонує 1С-Бітрікс для передачі каталогу товарів з 1С і завантаження замовлень з сайту, побудованого на основі системи управління Бітрікс.

Далі йде текст скрипта з коментарями. Короткий вид скрипта, що містить виключно код, наведений у попередній статті, і доступний github проекту bitrexchange.

Налаштування скрипта і локальних файлів
Для того, щоб не передалися порожні або биті файли, для запобігання псування даних, при будь-якій помилці завершуємо роботу скрипта.

set -e

Це такий цікавий прийом переходу в поточну директорію, і одночасно збереження повного поточного шляху в змінну

cd $(dirname $0)
cdir=$(pwd)"/"

Зазвичай, 1С викладає файли в якусь расшареную папку на сервері в мережі, яку ми заздалегідь примонтируем. Але це може бути і локальна директорія для вас, яка расшарена вами в мережі

remote_dir="/mnt/localwinserver_fs/import/webdata/"

Ім'я файлу zip, в який будуть запаковані xml файли. Замість цього, можна використовувати просто тимчасове ім'я. Але таке жорстко заданий ім'я файлу використовується для збереження копій раніше переданих файлів. Дуже зручно: можна в будь-який момент подивитися, в який день з'явився якийсь товар і за якою ціною і т. п.

zip_fname="catalogue.zip"

Імена файлів, які ми будемо передавати. Ці файли формує 1С, зазвичай у них такі імена, але можуть бути і інші! Потрібно перевірити це! Файли перераховуються через пробіл.

xml_files="import0_1.xml offers0_1.xml"

Пошта адміністратора, на яку надсилаються повідомлення про помилки.

email1="admin@yourinternetshop.com";
email2="alert@yourinternetshop.com";

Визначаємо поточний час:

ctime=$(date +%Y-%m-%d-%H%M)

Перевірка і підготовка файлів

Цей скрипт — приватний випадок всіх численних видів завантажень. Крім вивантаження каталогу, яку ми зараз робимо, існують інші обміни, для яких існують свої окремі скрипти. Але і в рамках цієї вивантаження — вивантаження каталогу, визначені два типу: так звана инкрементальная вивантаження, тобто, вивантаження змін, і повне вивантаження. За форматом відрізняються вони один від одного тільки одним параметром: значенням атрибута

СодержитТолькоИзменения

— він дорівнює або true, або false. Говорячи про вміст XML файлів, можна сказати, що инкрементальная вивантаження може містити і повний набір товарів, різниця в іншому: якщо бітрікс отримує повне вивантаження, то він повністю стирає весь наявний каталог товарів і заповнює його заново. Також втрачаються і всі картинки товарів, тому повне вивантаження обов'язково включає в себе всі зображення, тоді як инкрементальная може не включати. Ну і звичайно, якщо важливо зберегти якісь створені вами прив'язки в базі сайту, то краще не видаляти існуючий каталог. Загалом, так чи інакше, є величезна, визначальна різниця, який саме тип вивантаження використовувати. Тому обов'язково перевіримо, ні в якому разі не довіряючи 1C, що ця вивантаження дійсно инкрементальная, причому перевіримо в обох файлах. Заодно перевіримо, що ці файли взагалі існують, і що їх два. Іноді, наприклад, 1С чомусь вивантажує тільки номерклатуру, а може так статися, що кулі відвалилася і файли взагалі не вивантажилися. Тому, у разі проблеми, повідомляємо собі про неї і виходимо.

chan=$(grep -e "СодержитТолькоИзменения=\"true" ${remote_dir}*.xml | wc -l)
if test "$chan" != "2"; then
echo "Error: XMLs are not in 'changes only' mode or file(s) are missing"
mail -s "Завантаження цін" -a "From: bitrexchange <${email1}>" $email1,$email2 <<< "Не пройшла завантаження цін на сайт."
exit 1
else
echo "OK: Format of XMLs are 'changes only'"
fi

Починаємо власне роботу: запаковуємо файли і робимо архів попереднього файлу поточною датою. Зверніть увагу, що ця команда запаковує всі xml файли в каталозі.

if [ -f $zip_fname ]; then mv $zip_fname "${zip_fname}.${ctime}"; fi
/usr/bin/zip -9j "$zip_fname" ${remote_dir}*.xml

Налаштування HTTP запитів
Задаємо заголовки HTTP запиту. Насправді це, по-перше, абсолютно необов'язково, а по-друге, можна поставити якісь свої, наприклад, user-agent, по якому потім можна відловити ці запити в логах апача на приймаючій стороні.

headers="--header=\"User-Agent: 1C+Enterprise/8.2\" --header=\"Accept-Encoding: deflate\""

Логін і пароль користувача, під яким буде логінитися завантаження. Зазвичай це 1c_import або import. Ці логін і пароль задаються в панелі управління битрикса! Для тесту ви можете використовувати свій адміністраторський аккаунт, admin.

login="import"
password="yourpasswordonbitrix"

Щоб не захаращувати скрипт, винесемо в окрему змінну базовий url всіх запитів. зазвичай це вашдомен/bitrix/admin/1c_exchange.php

baseurl="http://yourinternetshop.com/bitrix/admin/1c_exchange.php"

Власне обмін
Оскільки документація дуже(!) поверхнева, і конкретні способи застосування протоколу різняться від конфігурації 1С, використовуваний спосіб обміну перевірений і підтверджений експериментально і працює без змін на декількох робочих проектах, що підтверджує можливість його масового використання.

Згідно з протоколом обміну, запити декількох типів передаються послідовно. Тип запиту визначається параметром, який додається до базового url, який ми вже поставили вище як baseurl. Дві змінні в url задають тип запиту. Перша, type, у нас приймає значення sale, або catalog. Ви можете спробувати змінити це, принаймні для першого запиту, і використовувати завжди catalog. Друга, більш важлива для нас, так як вона і визначає етап обміну, у змінній mode.

Перший запит, mode=checkauth
Другий запит, mode=init
Третій запит, mode=file + POST zip-файлу.
Наступні запити, mode=import

Перший запит

Ми логинимся з допомогою аутентифікації по HTTP протоколу! Звичайно, такий спосіб включений в битриксе, але якщо немає, перевірте налаштування сервера. Наскільки я знаю, якийсь налаштування для включення або виключення цього способу в битриксе немає, але аутентифікація може бути вимкнена програмно багатьма способами. Також може мішатися так звана «проактивний захист», якщо ви ніяк не можете залогінитись, спробуйте відключити її на час тестування.

ret_line=$( wget $headers --user=${login} --password=${password} --auth-no-challenge -O - -q "${baseurl}?type=sale&mode=checkauth" )
read -a ret_ar <<< $ret_line

Згідно з протоколом, у разі успішного входу, бітрікс віддає відповідь у вигляді трьох рядків у тілі HTTP відповіді:

Перша — рядок success
Друга — змінна сесії, зазвичай PHPSESSID
Третє — значення сесії, рядок.

Якщо sussecc отримано, зберігаємо переменнную сесії та її значення для подальшого використання, якщо немає, виходимо з скрипта. На цьому моменті у разі помилки ми можемо отримати HTML сторінку, яку ви можете зберегти і дещо переглянути, і, швидше за все, ви знайдете в ній повідомлення про те, що вам треба залогінитись. На цьому етапі найбільш імовірно — перевіряйте логін і пароль користувача, спробуйте, перш за все, зайти під цими логіном і паролем панель управління битрикса.

if [ ${ret_ar[0]} != "success" ]; then echo "Login error\r\n"; exit -1; fi
sessvar=${ret_ar[1]}
sessid=${ret_ar[2]}
echo sessid=$sessid

Другий запит

Другий запит — Init. При отриманні запиту повністю очищається(!) директорія /upload/1c_catalog/ Будьте уважні, якщо вам потрібні якісь дані попереднього завантаження. Наприклад, це буде важливий, якщо ви передаєте картинки товарів. 1С версії 8.3, як було виявлено, чомусь цей запит не передає, а відразу переходить до запиту file. На цьому етапі можлива поява помилки, яка буде описана далі в додатку, див. можливі помилки(1).

ret=$(wget $headers --header="Cookie: ${sessvar}=${sessid}" -O - -q "${baseurl}?type=catalog&mode=init"); echo $ret

Третій запит

У третьому запиті потрібно передати POST-запитом zip-файл. mode має бути = file, а от ім'я файлу — параметр filename — як я розумію, може бути будь-яким, бо воно ніде більше не фігурує у запиті, та задає тільки ім'я, під яким передаються дані зберігаються у вигляді файла (файл передається через POST запит і тому насправді в контексті HTTP це не файл, а просто якийсь шматок даних, який може бути чим завгодно).

На цьому кроці в директорії upload/1c_catalog знаходиться файл import.zip, нераспакованный. Цікаво, що більше ніяких додаткових запитів на нього посилати не потрібно. Як тільки буде перший запит на імпорт, він сам распакуется.

ret=$(wget $headers --post-file ${zip_fname} --header="Cookie: ${sessvar}=${sessid}" -O - -q "${baseurl}?type=catalog&mode=file&filename=import.zip"); echo $ret

Запити імпорту

Далі, згідно з протоколом обміну 1C-Бітрікс, треба посилати запити з mode=file, до тих пір, поки не буде отримано рядок success. Насправді, потрібно перевіряти не success, а те, що на кожен запит є відповідь progress, і якщо він є, то можна продовжувати імпорт, а якщо прийшло щось інше, то імпорт закінчився з якихось причин, однією з яких може бути і success. Один з численних недокументованих моментів полягає в тому, що такий цикл запитів треба повторювати для кожного розпакованого файлу, а ім'я zip-файлу більше ніде вказувати не потрібно, оскільки він розгортається відразу після отримання. Також див. додаток, можливі помилки(2).

for fname in $xml_files; do
st="progress";
while [ "$st" = "progress" ]; do
ret=$(wget $headers --header="Cookie: ${sessvar}=${sessid}" -O - -q "${baseurl}?type=catalog&mode=import&filename=${fname}");
st=$( <<< "$ret" head -n1 | cut -c1-8); echo "$ret" | iconv -f cp1251 -t utf-8
done
done

Після цього циклу потрібно перевіряти, що останнє повідомлення = success, і в іншому випадку, знову повідомляти себе. Це можна робити в рамках цього скрипта, або якимось іншим способом, наприклад, перевіряти лог.

Останній запит

Згідно документації, останнім запитом повинен йти type=success, 1С цей запит відсилає, але цей скрипт обходиться без нього.

Додаток:

Можливі помилки(1)

На другому запиті видається помилка «failure». Компонент повинен видавати при помилці слово «failure» і далі опис помилки. Але, мабуть, не може визначити мову при запиті, і тому віддається просто failure без деталізації помилки. Хоча повинен існувати якийсь мову за замовчуванням. Можливо, причина в чомусь іншому, але факт той, що віддається failure і все. Джерело помилки визначити неможливо. Вирішується, як завжди, аналогічне виразу " лізти в код.

COption::GetOptionString("sale", "secure_1c_exchange", "N") == "Y"

можна відключити перевірку цього параметра прямо в коді, можна встановити його в N:
Налаштування — Інструменти — Командна PHP-рядок.

COption::SetOptionString("catalog", "DEFAULT_SKIP_SOURCE_CHECK", "Y" );
COption::SetOptionString("sale", "secure_1c_exchange", "N" );

Можливі помилки(2)

Неочевидна помилка: на кожному етапі віддаються два рядки: перша — progress, і друга — опис, що робиться. Так от, обов'язково повинні бути блоки progress. Опрацьовано XXXX з YYYYY елементів.

— Ось це «опрацьовано стільки-то стільки-то елементів» — це і є імпорт. якщо цього немає, то імпорт не робиться. такі випадки були в певних конфігураціях.

Стандартний обмін 1С-Бітрікс на BASH: Перша стаття з серії публікацій.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.