Выгрузка данных на сайт из 1С через Django REST framework

10.06.24

Интеграция - Сайты и интернет-магазины

Пример одностороннего обмена с сайтом Django через Django REST framework.

Привет всем. Ко мне обратился заказчик из стоматологии с просьбой сделать интеграцию с системой ЛоялМед. Это система лояльности для медицинских клиник у них есть чат-бот и система начисления бонусов за оказанные услуги.

После детального изучения выяснилось: необходим односторонний обмен - выгрузка данных пациентов, их записей на прием, приемов, платежей на сайт ЛоялМеда. Со стороны ЛоялМед настроено Django REST framework(далее DRF) для приема данных в JSON.

Конфигурация заказчика БИТ.Стоматология 2.0

Решение было следующее:

В отдельном расширении создан регистр сведений для регистрации очереди на выгрузку в DRF.

Подписки на события при записи объектов: справочник Клиенты, документ Заявка, документ ОказаниеУслуг

Регламентное задание на выгрузку DRF, если выгрузка объекта удачна - запись удаляется из регистра, если нет в логах DRF - пишется описание ошибки.

Авторизация идет по токену, хост и токен положил в константы.

Выгрузка сперва идет методом POST, если объект уже создан, то выгрузка идет методом PUT.

Ниже пример выгрузки клиента на сайт:

Процедура ВыгрузитьКлиента(Клиент, ТокенЛМ, ХостЛМ) 
	Попытка
		Соединение  =  Новый HTTPСоединение(ХостЛМ,,"","",,,Новый ЗащищенноеСоединениеOpenSSL()
		);
		Запрос = Новый Запрос;
		Запрос.Текст = 
		"ВЫБРАТЬ
		|	Клиенты.Код КАК Код,
		|	Клиенты.Фамилия КАК Фамилия,
		|	Клиенты.Имя КАК Имя,
		|	Клиенты.Отчество КАК Отчество,
		|	Клиенты.ДатаРождения КАК ДатаРождения,
		|	Клиенты.ДатаСоздания КАК ДатаСоздания,
		|	Клиенты.Пол КАК Пол,
		|	Клиенты.КлиентЛМ КАК КлиентЛМ,
		|	Клиенты.ДатаЛМ КАК ДатаЛМ,
		|	Клиенты.ЗаконныйПредставитель КАК ЗаконныйПредставитель,
		|	ЕСТЬNULL(КонтактнаяИнформация.Представление, """") КАК Телефон,
		|	Клиенты.ЗаконныйПредставитель.Код КАК ЗаконныйПредставительКод
		|ИЗ
		|	Справочник.Клиенты КАК Клиенты
		|		ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.КонтактнаяИнформация КАК КонтактнаяИнформация
		|		ПО Клиенты.Ссылка = КонтактнаяИнформация.Объект
		|			И (КонтактнаяИнформация.Вид = &Вид)
		|ГДЕ
		|	Клиенты.Ссылка = &Ссылка";
		Запрос.УстановитьПараметр("Ссылка", Клиент);
		Запрос.УстановитьПараметр("Вид",  Справочники.ВидыКонтактнойИнформации.ТелефонСотовый); 
		Результат = Запрос.Выполнить();
		Контрагент = Результат.Выбрать();
		Пока Контрагент.Следующий() Цикл
			//Формируем структуру с данными
			СтруктураЗапроса = Новый Структура; 
			КК = ПолучитьЧислоИзСтроки(Контрагент.Код);
			СтруктураЗапроса.Вставить("id", КК);
			СтруктураЗапроса.Вставить("lastname", Контрагент.Фамилия);
			СтруктураЗапроса.Вставить("firstname", Контрагент.Имя);
			СтруктураЗапроса.Вставить("midname", Контрагент.Отчество);
			Если ЗначениеЗаполнено(Контрагент.ДатаРождения) Тогда
				СтруктураЗапроса.Вставить("birthdate", Формат(Контрагент.ДатаРождения,"ДФ=гггг-ММ-дд"));
			КонецЕсли;
			СтруктураЗапроса.Вставить("created_in_mis", ?(ЗначениеЗаполнено(Контрагент.ДатаСоздания),Формат(Контрагент.ДатаСоздания,"ДФ=гггг-ММ-ддTЧЧ:мм:сс"), Формат(ТекущаяДата(),"ДФ=гггг-ММ-ддTЧЧ:мм:сс")));
			СтруктураЗапроса.Вставить("gender", ПолучитьПол(Контрагент.Пол));
			СтруктураЗапроса.Вставить("loyalmed_type", ?(Контрагент.КлиентЛМ = Истина,1,0));
			
			Тел = Контрагент.Телефон;
			Тел = ПолучитьЧислоИзСтроки(Тел);  
			Если Лев(Тел,1) = "8" Тогда
				Тел = Сред(Тел,2);
				Тел = "7"+Тел;
			КонецЕсли;
			СтруктураЗапроса.Вставить("phone1", Тел);
			
			Если ЗначениеЗаполнено(Контрагент.ДатаЛМ) Тогда
				СтруктураЗапроса.Вставить("loyalmed_enabled_timestamp", Формат(Контрагент.ДатаЛМ,"ДФ=гггг-ММ-ддTЧЧ:мм:сс"));
			КонецЕсли;
			
			СтруктураЗапроса.Вставить("updated_in_mis", Формат(ТекущаяДата(),"ДФ=гггг-ММ-ддTЧЧ:мм:сс"));
			
			Если ЗначениеЗаполнено(Контрагент.ЗаконныйПредставитель) И ТипЗнч(Контрагент.ЗаконныйПредставитель) = Тип("СправочникСсылка.Клиенты") Тогда
				СтруктураЗапроса.Вставить("representative_id", ПолучитьЧислоИзСтроки(Контрагент.ЗаконныйПредставительКод));
			КонецЕсли;
			
			//Преобразуем полученную структура в JSON
			ЗаписьJSON = Новый ЗаписьJSON;
			ЗаписьJSON.УстановитьСтроку();
			ЗаписатьJSON(ЗаписьJSON, СтруктураЗапроса);
			СтрокаЗапроса = ЗаписьJSON.Закрыть();
			
			HTTPОтвет = ОтправитьPOSTЗапрос(Соединение, СтрокаЗапроса, ТокенЛМ, "clients");
			//если объект успешно создан DRF возвращает код 201, иначе отправляем PUT
			Если HTTPОтвет.КодСостояния = 201 Тогда
				//удаляем объект из регистра очереди
				УдалитьОбъектИзОчереди(Клиент);
			Иначе
				HTTPОтвет = ОтправитьPUTЗапрос(Соединение, СтрокаЗапроса, ТокенЛМ, "clients", КК);
				//если обновление объекта прошло - возращается код 200, иначе сохраняем ответ в спец регистр
				Если HTTPОтвет.КодСостояния = 200 Тогда
					УдалитьОбъектИзОчереди(Клиент);
				Иначе
					ТелоОтвета = HTTPОтвет.ПолучитьТелоКакСтроку();
					ЗаписатьОтвет(Контрагент, ТелоОтвета);
				КонецЕсли;
				
			КонецЕсли;
		КонецЦикла;	
	Исключение
		ЗаписатьОшибку(Контрагент, ОписаниеОшибки());
		//ЖурналРегистрации.
	КонецПопытки;
	
	
КонецПроцедуры

Процедура УдалитьОбъектИзОчереди(Объект)
	ЗаписьCOM = РегистрыСведений.ОбъектыДляВыгрузкиВЛМ.СоздатьМенеджерЗаписи();
	ЗаписьCOM.ОбъектДляВыгрузки		  = Объект;
	ЗаписьCOM.Удалить();
КонецПроцедуры

Функция ОтправитьPOSTЗапрос(Соединение, СтрокаЗапроса, ТокенЛМ, ТипОбъекта)
	HTTPЗапрос = Новый HTTPЗапрос("/"+ТипОбъекта+"/");
	HTTPЗапрос.Заголовки.Вставить("Content-type", "application/json");
	//Авторизация идет по токену, токен выдали на стороне DRF
	HTTPЗапрос.Заголовки.Вставить("Authorization", "token "+ТокенЛМ+"");
	HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
			
	HTTPОтвет = Соединение.ОтправитьДляОбработки(HTTPЗапрос);   
	Возврат HTTPОтвет; 
КонецФункции

Функция ОтправитьPUTЗапрос(Соединение, СтрокаЗапроса, ТокенЛМ, ТипОбъекта, ИдОбъекта)
	HTTPЗапрос = Новый HTTPЗапрос("/"+ТипОбъекта+"/"+ИдОбъекта+"/");
	HTTPЗапрос.Заголовки.Вставить("Content-type", "application/json");
	HTTPЗапрос.Заголовки.Вставить("Authorization", "token "+ТокенЛМ+"");
	HTTPЗапрос.УстановитьТелоИзСтроки(СтрокаЗапроса, КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
	HTTPОтвет = Соединение.ВызватьHTTPМетод("PUT",HTTPЗапрос);   
	Возврат HTTPОтвет;
	
КонецФункции

 

Вступайте в нашу телеграмм-группу Инфостарт

обмен DRF HTTP-Запрос

См. также

Сайты и интернет-магазины 1С v8.3 1С:Розница 2 Розничная и сетевая торговля (FMCG) Россия Платные (руб)

Интеграция сервиса dolyame.ru с 1С:Розница 2.3 для приема платежей в рассрочку. Готовое интеграционное решение для оплаты покупок Долями в 1C:Розница 2.3. Реализовано в виде расширения. Интеграция сервиса dolyame.ru для приема платежей в рассрочку. Поддерживает работу от разных юридических лиц. Работа: в составе РИБ, отдельно от РИБ, тонкий, толстый клиент, web-клиент (через интернет-браузер), поддерживается старый РМК, работа через чек ККМ.

24000 руб.

19.12.2023    10327    59    14    

54

Сайты и интернет-магазины WEB-интеграция Системный администратор Программист Пользователь 1С v8.3 1C:Бухгалтерия 1С:Управление торговлей 11 Автомобили, автосервисы Россия Управленческий учет Платные (руб)

Интеграционный модуль обмена между конфигурацией Альфа Авто 5 и Альфа Авто 6 и порталом AUTOCRM / LOGICSTARS. Данный модуль универсален. Позволяет работать с несколькими обменами AUTOCRM / LOGICSTAR разных брендов в одной информационной базе в ручном и автоматическом режиме.

36000 руб.

03.08.2020    21673    30    24    

24

Сайты и интернет-магазины Программист Пользователь 1С v8.3 Бухгалтерский учет 1С:Бухгалтерия 3.0 Россия Платные (руб)

Данный модуль предоставляет возможность выгружать заказы из 1С: Управление торговлей 11 версии в "MEASOFT" (ранее "Курьерская служба 2008").

9600 руб.

27.04.2022    12461    22    3    

13

Сайты и интернет-магазины 1С v8.3 1С:Розница 3.0 Розничная и сетевая торговля (FMCG) Россия Платные (руб)

Готовое интеграционное решение для оплаты покупок Долями в 1C:Розница 3.0. Реализовано в виде расширения. Интеграция сервиса dolyame.ru для приема платежей в рассрочку. Поддерживает работу от разных юридических лиц. Работа: в составе РИБ, отдельно от РИБ, тонкий, толстый клиент, web-клиент (через интернет-браузер). Интегрировано в Чек ККМ, Рабочее место кассира (РМК)

26400 руб.

02.11.2024    2751    13    2    

11

Файловый обмен (TXT, XML, DBF), FTP Сайты и интернет-магазины 1С v8.3 1С:Управление торговлей 11 Россия Платные (руб)

Загрузка файла YML (формат Yandex Market Language(YML) — собственный стандарт Яндекса, основанный на XML) в УТ11.5 Создание дерева номенклатуры, создание карточек номенклатуры и доп реквизитов, загрузка картинок в карточку. Оптимизировано для выгрузки в Битрикс. Возможна загрузка нескольких Фид - предусмотрено разделение по площадкам

7000 руб.

01.11.2024    1201    1    1    

2

Прайсы Оптовая торговля Ценообразование, анализ цен Сайты и интернет-магазины Пользователь 1С v8.3 1C:Бухгалтерия 1С:Управление торговлей 11 Управленческий учет Платные (руб)

Легкая обработка для мониторинга цен практически любого сайта (парсинга), не требует специальных знаний при настройке.

6960 руб.

17.04.2017    33429    30    28    

43
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. Nikola23 709 10.06.24 17:25 Сейчас в теме
Прошу уточнить, при чем тут Jango, если пример демонстрирует формирование HTTP запроса?
Не сомневаюсь, что на той стороне джанго существует, но разве это имеет какое-то значение?
В примере это не раскрыто

А из своего опыта, я понимаю, что никакой особой разницы, какой там фреймворк - нет
2. gea29021988 6 10.06.24 17:37 Сейчас в теме
(1) Насколько я знаю именно строка адреса запроса формируется у всех по-разному, здесь именно Django REST Framework, это не джанго чистый это API к Django и здесь я показываю запрос именно к этому API вида
"/"+ТипОбъекта+"/"+ИдОбъекта+"/"
Знаю точно что в других API есть где ИдОбъекта пихается в тело запроса, а не в адрес.
3. Nikola23 709 10.06.24 22:52 Сейчас в теме
(2) Я абсолютно не против статей, где раз за разом показывают одно и тоже
Но вот без указания, что фишка статьи вот в этой строке
"/"+ТипОбъекта+"/"+ИдОбъекта+"/"
- как-то я совсем не догадался.
Может как-то выделить это?)
8. RocKeR_13 1457 11.06.24 16:30 Сейчас в теме
(3)
что фишка статьи вот в этой строке

Это не фишка, это обычная строка адреса запроса, правило формирования которой должно быть изложено в документации. Фишкой в свое время было описание формирования тела запроса multipart/form-data. А это именно то, что вы и написали сначала:

пример демонстрирует формирование HTTP запроса
4. gybson 11.06.24 00:02 Сейчас в теме
Такие огромные попытки зло и здесь очень хорошо видно почему. Если исключение произойдет при установке соединения, то при попытке сформировать сообщение об ошибке произойдет исключение. И даже если исключение обработается, то из него не очень будет понятно где и что произошло.

Если получен код 4** или 500, то смысл продолжать?

Конкатенация строк вместо шаблона.

Транзакции нет.

ОбъектыДляВыгрузкиВЛМ не используется в запросе

Как-то вообще все не так.
5. gea29021988 6 11.06.24 07:21 Сейчас в теме
(4) Если получен код 4** или 500, то смысл продолжать?
Если метод POST возвращает 400, то запускается PUT редактирование объекта
6. RocKeR_13 1457 11.06.24 16:24 Сейчас в теме
(4)
Транзакции нет.

Про транзакцию не понял: зачем она тут? В остальном согласен: много воды, вопросов и ничего нового.
7. gybson 11.06.24 16:28 Сейчас в теме
(6) чтобы не снимать с регистрации без положительного ответа сервиса
10. RocKeR_13 1457 11.06.24 16:35 Сейчас в теме
(7)
чтобы не снимать с регистрации без положительного ответа сервиса

А транзакция-то тут при чем?) Отправили запрос - пришел ответ: если ок, то сняли, если ошибка - продолжили. Единственный смысл - это открыть транзакцию перед началом цикла, но она, наоборот, в этом случае будет только вредить: транзакция в случае ошибки будет откатываться исключительно в 1С, а на стороне http-сервиса успешные запросы не откатятся; в итоге если на 5 запросе придет ошибка, то в 1С регистрация не снимется, но первые 4 запроса так и останутся успешно обработанными на стороне сервиса - в итоге 4 запроса при следующей выгрузке лишний раз будут продублированы
13. gybson 11.06.24 18:44 Сейчас в теме
(10) несомненно именно так и будет, если так сделать


Имеется в виду объединение в транзакцию удаление записи из регистра и запрос на сервис. Если запрос вызвал исключение, то записи не будет. А исключение он может дать запросто. Например сервис закроет соединение за спам или по таймауту.
14. RocKeR_13 1457 11.06.24 21:10 Сейчас в теме
(13) Ну отвалится по таймауту и ладно, транзакция зачем?) Транзакция нужна, чтобы данные в базе изменялись согласованно и при возникновении ошибки отменялись все изменения, произведённые в рамках транзакции. А тут по сути 2 действия: запрос к стороннему сервису и удаление записи регистра; первое действие не производит никаких действий в базе и отмена транзакции никак на него не может повлиять. Тут простая проверка условия: запрос выполнен успешно - удаляем запись; иначе делаем другие манипуляции или пишем ошибку в журнал. Транзакция не нужна для этого
9. Nikola23 709 11.06.24 16:33 Сейчас в теме
(8)
(6)
много воды, вопросов и ничего нового


Вот и я в первом сообщении завуалировал эту мысль.
Но не хотелось быть токсиком, поэтому не стал хэйтить, пошел вокруг)
11. RocKeR_13 1457 11.06.24 16:40 Сейчас в теме
(9) Ну можно же не минусовать) Ну познакомился человек с http-запросами - отлично и полезно. А мы тут подскажем, что не так и, может быть, подскажем, что сделать, чтобы было другим полезно и интересно))
12. Nikola23 709 11.06.24 16:59 Сейчас в теме
(11) а при чем тут минуса? Это не ко мне
15. RocKeR_13 1457 11.06.24 21:11 Сейчас в теме
Для отправки сообщения требуется регистрация/авторизация