Существуют стандартные интеграции Битрикс и 1С, но они умеют плюс/минус ничего, зато с использованием rest api Битрикса можно получать и передавать практически все что угодно, двигать сделки по этапам, назначать ответственных и т.п.
В рассматриваемом примере подключение к Битриксу происходит через Вебхуки (приложение в Битриксе). В обработке данные берутся с формы, естественно, ничего не мешает заменить их на нужные источники – справочники, документы, бизнес-процессы и т.д.
Для изучения и тестирования методов настоятельно рекомендуется битриксовское же приложение «Документация по REST API». СРМу там посвящен свой раздел.
Обработка тестировалась на следующих версиях ПО:
1С-Битрикс: Корпоративный портал 18.1.8
1С:Предприятие 8.3 (8.3.14.1779)
Бухгалтерия предприятия КОРП, редакция 3.0 (3.0.70.39) (хотя конфигурация значения не имеет, в обработке используются только механизмы платформы)
Теория
Основная логика реализована в функции, которая действует по следующему алгоритму:
На входе получает структуру, где указан тип сущности, ее ORIGIN_ID (в боевых условиях для этих целей используются ГУИДы 1с) и структура всех полей - типа названия, стоимости и т.д.
Ищет сущность в СРМ по ORIGIN_ID
Если не находит – создает
Находит одну сущность – обновляет ее
Находит больше одной сущности – выдает ошибку
В случае успеха возвращает результат ок и Битрикс ИД созданной/обновленной сущности
В случае ошибки error и текст ошибки
&НаСервере
// Ищет и обновляет/создает сущность в СРМ Битрикс
// На входе - структура:
// СущностьСРМ - тип сущности, строка: contact, company, lead, deal, productsection, product
// ORIGIN_ID - ГУИД объекта 1С
// fields - структура полей для обновления
// Возвращает:
// Результат - ок/error
// Значение - если ок - битрикс ИД созданного/обновленного элемента, если error - текст ошибки
Функция СоздатьОбновитьСущностьБитрикс(СтруктураСущности)
//Готовим структуру для возврата
СтруктураСозданияОбновленияСущности = Новый Структура("Результат, Значение");
//Получаем настройки
ЛогинПортала = ЛогинПортала;
ПарольПортала = ПарольПортала;
АдресПортала = АдресПортала;
RestВебхук = RestВебхук;
//Создаем соединение
Соединение = Новый HTTPСоединение(АдресПортала, , ЛогинПортала, ПарольПортала,,, Новый ЗащищенноеСоединениеOpenSSL);
//Получаем название сущности СРМ
СущностьСРМ = СтруктураСущности.СущностьСРМ;
//Формируем запрос
HTTPЗапрос = Новый HTTPЗапрос(RestВебхук + "crm." + СущностьСРМ + ".list.json/");
//Запрос для поиска
СтрокаЗапроса = "";
Если СтруктураСущности.СущностьСРМ = "productsection" ИЛИ СтруктураСущности.СущностьСРМ = "product" Тогда
СтрокаЗапроса = СтрокаЗапроса + "filter[XML_ID]=" + КодироватьСтроку(СтруктураСущности.ORIGIN_ID, СпособКодированияСтроки.КодировкаURL);
Иначе
СтрокаЗапроса = СтрокаЗапроса + "filter[ORIGIN_ID]=" + КодироватьСтроку(СтруктураСущности.ORIGIN_ID, СпособКодированияСтроки.КодировкаURL);
КонецЕсли;
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8);
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
СтруктураОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
//Разбираем ответ
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтруктураОтвета);
Попытка
СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
Исключение
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = ОписаниеОшибки() + " | " + СтруктураОтвета;
Возврат СтруктураСозданияОбновленияСущности;
КонецПопытки;
//////////////
Если СтруктураОтвета.Свойство("total") И СтруктураОтвета.total = 1 Тогда
//Найдена одна сущность, обновляем
Соединение = Новый HTTPСоединение(АдресПортала, , ЛогинПортала, ПарольПортала,,, Новый ЗащищенноеСоединениеOpenSSL);
ИДНайденнойСущности = СтруктураОтвета.result[0].ID;
HTTPЗапрос = Новый HTTPЗапрос(RestВебхук + "crm." + СущностьСРМ + ".update.json?id=" + ИДНайденнойСущности);
СтрокаЗапроса = "";
Для Каждого Поле Из СтруктураСущности.fields Цикл
Если ТипЗнч(Поле.Значение) = Тип("Массив") И (Поле.Ключ = "CONTACT_IDS") Тогда
//Это для связанных объектов
Для Индекс = 0 По Поле.Значение.Количество()-1 Цикл
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "][" + Индекс + "]=" + КодироватьСтроку(Поле.Значение[Индекс], СпособКодированияСтроки.КодировкаURL);
КонецЦикла;
ИначеЕсли ТипЗнч(Поле.Значение) = Тип("Массив") Тогда
//Сие для телефонов и почты
Для Индекс = 0 По Поле.Значение.Количество()-1 Цикл
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "][" + Индекс + "][VALUE]=" + КодироватьСтроку(Поле.Значение[Индекс], СпособКодированияСтроки.КодировкаURL);
КонецЦикла;
Иначе
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "]=" + КодироватьСтроку(Поле.Значение, СпособКодированияСтроки.КодировкаURL);
КонецЕсли
КонецЦикла;
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8);
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
СтруктураОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
//Разбираем ответ
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтруктураОтвета);
СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
Если СтруктураОтвета.Свойство("result") И СтруктураОтвета.result Тогда
//Все ок
СтруктураСозданияОбновленияСущности.Результат = "ок";
СтруктураСозданияОбновленияСущности.Значение = ИДНайденнойСущности;
Возврат СтруктураСозданияОбновленияСущности;
ИначеЕсли СтруктураОтвета.Свойство("error") Тогда
//Сервис вернул ошибку
ТекстОшибки = "Ошибка при обновлении " + СущностьСРМ + ": " + СтруктураОтвета.error;
Если СтруктураОтвета.Свойство("error_description") И СтруктураОтвета.error_description <> "" Тогда
ТекстОшибки = ТекстОшибки + ", error_description: " + СтруктураОтвета.error_description;
КонецЕсли;
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = ТекстОшибки;
Возврат СтруктураСозданияОбновленияСущности;
Иначе
//Прочая неизвестная хрень
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = "Неизвестная ошибка при обновлении " + СущностьСРМ + " с id=" + ИДНайденнойСущности;
Возврат СтруктураСозданияОбновленияСущности;
КонецЕсли;
ИначеЕсли СтруктураОтвета.Свойство("total") И СтруктураОтвета.total = 0 Тогда
//Не найдено, создаем
Соединение = Новый HTTPСоединение(АдресПортала, , ЛогинПортала, ПарольПортала,,, Новый ЗащищенноеСоединениеOpenSSL);
HTTPЗапрос = Новый HTTPЗапрос(RestВебхук + "crm." + СущностьСРМ + ".add.json/");
СтрокаЗапроса = "";
Если СтруктураСущности.СущностьСРМ = "productsection" ИЛИ СтруктураСущности.СущностьСРМ = "product" Тогда
СтрокаЗапроса = СтрокаЗапроса + "fields[XML_ID]=" + КодироватьСтроку(СтруктураСущности.ORIGIN_ID, СпособКодированияСтроки.КодировкаURL);
Иначе
СтрокаЗапроса = СтрокаЗапроса + "fields[ORIGIN_ID]=" + КодироватьСтроку(СтруктураСущности.ORIGIN_ID, СпособКодированияСтроки.КодировкаURL);
КонецЕсли;
Для Каждого Поле Из СтруктураСущности.fields Цикл
Если ТипЗнч(Поле.Значение) = Тип("Массив") И (Поле.Ключ = "CONTACT_IDS") Тогда
//Это для связанных объектов
Для Индекс = 0 По Поле.Значение.Количество()-1 Цикл
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "][" + Индекс + "]=" + КодироватьСтроку(Поле.Значение[Индекс], СпособКодированияСтроки.КодировкаURL);
КонецЦикла;
ИначеЕсли ТипЗнч(Поле.Значение) = Тип("Массив") Тогда
//Сие для телефонов и почты
Для Индекс = 0 По Поле.Значение.Количество()-1 Цикл
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "][" + Индекс + "][VALUE]=" + КодироватьСтроку(Поле.Значение[Индекс], СпособКодированияСтроки.КодировкаURL);
КонецЦикла;
Иначе
СтрокаЗапроса = СтрокаЗапроса + "&fields[" + Поле.Ключ + "]=" + КодироватьСтроку(Поле.Значение, СпособКодированияСтроки.КодировкаURL);
КонецЕсли
КонецЦикла;
HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8);
HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);
СтруктураОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
//Разбираем ответ
ЧтениеJSON = Новый ЧтениеJSON;
ЧтениеJSON.УстановитьСтроку(СтруктураОтвета);
СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
//////////////
Если СтруктураОтвета.Свойство("result") И СтруктураОтвета.result <> "" Тогда
СтруктураСозданияОбновленияСущности.Результат = "ок";
СтруктураСозданияОбновленияСущности.Значение = Формат(СтруктураОтвета.result, "Л=ru_RU; ЧГ=0");
Возврат СтруктураСозданияОбновленияСущности;
ИначеЕсли СтруктураОтвета.Свойство("error") Тогда
//Сервис вернул ошибку
ТекстОшибки = "Ошибка при создании " + СущностьСРМ + ": " + СтруктураОтвета.error;
Если СтруктураОтвета.Свойство("error_description") И СтруктураОтвета.error_description <> "" Тогда
ТекстОшибки = ТекстОшибки + ", error_description: " + СтруктураОтвета.error_description;
КонецЕсли;
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = ТекстОшибки;
Возврат СтруктураСозданияОбновленияСущности;
Иначе
//Прочая неизвестная хрень
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = "Неизвестная ошибка при создании " + СущностьСРМ;
Возврат СтруктураСозданияОбновленияСущности;
КонецЕсли;
ИначеЕсли СтруктураОтвета.Свойство("total") И СтруктураОтвета.total > 1 Тогда
//Найдено больше одной компании, обтекаем
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = "Нарушена целостность данных. Найдено больше одной " + СущностьСРМ + " с одинаковым ORIGIN_ID";
Возврат СтруктураСозданияОбновленияСущности;
ИначеЕсли СтруктураОтвета.Свойство("error") Тогда
//Сервис вернул ошибку
ТекстОшибки = "Ошибка при поиске " + СущностьСРМ + ": " + СтруктураОтвета.error;
Если СтруктураОтвета.Свойство("error_description") И СтруктураОтвета.error_description <> "" Тогда
ТекстОшибки = ТекстОшибки + ", error_description: " + СтруктураОтвета.error_description;
КонецЕсли;
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = ТекстОшибки;
Возврат СтруктураСозданияОбновленияСущности;
Иначе
//Прочая неизвестная хрень
СтруктураСозданияОбновленияСущности.Результат = "error";
СтруктураСозданияОбновленияСущности.Значение = "Неизвестная ошибка при поиске " + СущностьСРМ;
Возврат СтруктураСозданияОбновленияСущности;
КонецЕсли;
КонецФункции
Использование обработки для тестирования
Указываем настройки
Адрес портала в виде «portal.mysite.com» ( без https:// )
Логин и пароль портала если используется http авторизация
Вебхук в виде «rest/1234/abc7skjbv1vyqxb2/» - т.е. точка доступа и собственно вебхук в одной строке
Отправка
Контакты и товары можно создавать независимо. Для создания сделки с контактом и товаром, а также для движения сделки по стадиям нужно заполнить все поля, так как каждый раз создаются/обновляются все сущности.
Результат на портале