Pull to refresh

Стандартный обмен 1С-Битрикс на BASH: Подробный разбор скрипта инкрементальной выгрузки

Reading time 8 min
Views 13K
Для обеспечения синхронизации каталога товаров между системой 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: Первая статья серии публикаций.
Tags:
Hubs:
+4
Comments 0
Comments Leave a comment

Articles