Практикум по созданию обменов данными через протокол oData «за полдня»

Программирование - Практика программирования

Про oData и 1С было довольно много написано, однако же описания работы и с чтением, и с записью данных через JSON я так и не встретил ни на этом ресурсе ни на других. Попробую раскрыть эту тему.

Реализация REST-интерфейса от 1С – песня долгая, унылая, про борьбу нещадную и Пирровы победы… Но всё же применение ему есть, и в некоторых случаях он не просто производительнее и выгоднее в реализации других технологий обмена, но и просто незаменим.

При этом, до сих пор порядка 50% специалистов или вообще об oData не слышали или «что-то слышали», но «нужен обмен – COM forever», при том что более тормозной технологии обмена, по-моему, вообще на текущий момент не существует.

Описание теории в этой статье давать не буду, как и описание публикации базы данных на web-сервере, на все необходимые (и весьма интересные) для прочтения статьи ссылки приведу в конце статьи, сейчас займемся жёсткой практикой, и поймем, что «полдня» - это вполне реально)

Решение задач на получение данных (например получение остатков по банковским счетам) вообще делается «на лету», поэтому разберем случай с выгрузкой данных в приемник.

Итак, имеем две БД «управленческая» сильно переписаная КА 1.1 и самописная БД для учета контрагентов и банковских счетов «юрбаза», в которой работают юристы и финансовый отдел. Первичный ввод информации о контрагенте производится в юрбазе, нам же нужно оперативно выгрузить из нее информацию в управленческую. В данном случае весь код отрабатывается в юрбазе посредством GET и POST запросов в управленческую.

Оговорюсь, код не претендует на безукоризненность и соответствие стандартам, т.к. писалось «на скорую руку», надеюсь что сильно пинать за это меня не будут)

Для начала добавляем регистр сведений, скажем «Измененные объекты» для регистрации объектов при их записи, пишем процедуры регистрации, в модулях выгрузки будем обращаться к данным регистра. Ессно добавляем рег.задание для запуска выгрузки, оставляем за собой возможность интерактивной обработки данных из обработки. Для хранения данных для соединения с управленческой базой добавляем регистр сведений «Серверы oData». Описание действий дано, дабы код запросов был более читабелен.

Итак, код:

Процедура ВыполнитьВыгрузкуИзмененныхОбъектов(ИнтерактивныйРежим = Ложь) Экспорт

	// если ничего нет не соединяемся
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ ПЕРВЫЕ 1
		|	ИзмененныеОбъекты.Объект КАК Объект
		|ИЗ
		|	РегистрСведений.ИзмененныеОбъекты КАК ИзмененныеОбъекты";
	
	Если Запрос.Выполнить().Пустой() Тогда
		Возврат;
	КонецЕсли;
	
	Если ИнтерактивныйРежим Тогда
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Старт обработки: %1'"), ТекущаяДатаСеанса());
		СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим);
	КонецЕсли;
	
	СтруктураПодключения = Новый Структура("Сервер,БазаДанных,Пользователь,Пароль");
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	СерверыOData.Сервер КАК Сервер,
		|	СерверыOData.БазаДанных КАК БазаДанных,
		|	СерверыOData.Пользователь КАК Пользователь,
		|	СерверыOData.Пароль КАК Пароль
		|ИЗ
		|	РегистрСведений.СерверыOData КАК СерверыOData
		|ГДЕ
		|	СерверыOData.Идентификатор = ЗНАЧЕНИЕ(Перечисление.ИдентификаторыБазДанных.Управленческая)";
	
	Выборка = Запрос.Выполнить().Выбрать();
	Если Выборка.Следующий() Тогда
		ЗаполнитьЗначенияСвойств(СтруктураПодключения, Выборка);
	Иначе
		ТекстСообщения = НСтр("ru = 'Запись регистра сведений Серверы oData для управленческой БД не обнаружена.'");
		СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим, УровеньЖурналаРегистрации.Ошибка);
		Возврат;
	КонецЕсли;
	
    ЗаголовокHTTP = Новый Соответствие();
	ЗаголовокHTTP.Вставить("Accept", "application/json");
	
    ЗаголовокHTTP2 = Новый Соответствие();
	ЗаголовокHTTP2.Вставить("Content-Type", "application/json; charset=utf-8");
	
	Попытка
		Соединение = Новый HTTPСоединение(СтруктураПодключения.Сервер, , СтруктураПодключения.Пользователь, СтруктураПодключения.Пароль);
	Исключение
		ТекстСообщения = НСтр("ru = 'Ошибка соединения с WEB-сервером управленческой БД.'");
		СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим, УровеньЖурналаРегистрации.Ошибка);
		Соединение = Неопределено;
		Возврат;
	КонецПопытки;
	
	ПараметрыHTTP = Новый Структура("ИнтерактивныйРежим,Соединение,АдресРесурса,ТекстЗапроса,ЗаголовокHTTP,КодСостояния,Ответ,ТекущийОбъект,КритическаяОшибка,Метод",
		ИнтерактивныйРежим, Соединение, "", "", ЗаголовокHTTP, 0, "", Неопределено, Ложь, Неопределено);
	
	// Контрагенты
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ИзмененныеОбъекты.Объект КАК Объект,
		|	ИзмененныеОбъекты.Объект.Наименование КАК Description,
		|	ИзмененныеОбъекты.Объект.Наименование КАК НаименованиеПолное,
		|	ИзмененныеОбъекты.Объект.ИНН КАК ИНН,
		|	ИзмененныеОбъекты.Объект.КПП КАК КПП,
		|	ИзмененныеОбъекты.Объект.ОГРН КАК ОГРН,
		|	ВЫБОР
		|		КОГДА ИзмененныеОбъекты.Объект.ФормаСобственности.Сокращение = ""ИП""
		|			ТОГДА ""ФизЛицо""
		|		ИНАЧЕ ""ЮрЛицо""
		|	КОНЕЦ КАК ЮрФизЛицо,
		|	ВЫБОР
		|		КОГДА ИзмененныеОбъекты.Объект.СистемаНалогообложения = ЗНАЧЕНИЕ(Справочник.СистемаНалогообложения.УСН)
		|			ТОГДА ""Упрощенная""
		|		ИНАЧЕ ""Общая""
		|	КОНЕЦ КАК СистемаНалогообложения
		|ИЗ
		|	РегистрСведений.ИзмененныеОбъекты КАК ИзмененныеОбъекты
		|ГДЕ
		|	ИзмененныеОбъекты.Объект ССЫЛКА Справочник.Контрагенты";
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		
		// Сначала поищем контрагента в базе-приемнике для определения типа запроса POST или PATCH
		// По правильному хорошо бы хранить GUID существующих в другой базе объектов и искать по нему, но...
		ПараметрыHTTP.ТекущийОбъект = Выборка.Объект;
		ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1/odata/standard.odata/Catalog_Контрагенты?$select=Ref_Key&$filter=ИНН eq %2%3%2 and КПП eq %2%4%2'"), 
			СтруктураПодключения.БазаДанных, "'", Выборка.ИНН, Выборка.КПП);
		
		СоздатьЗапрос(ПараметрыHTTP); // Оптимизируем код
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		КонецЕсли;
			
		ЧтениеJSON = Новый ЧтениеJSON; 
		ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
		СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
		МассивРеквизитов = СоответствиеJSON["value"]; // GET возвращает нам соответствие с массивом внутри
		
		СоответствиеРеквизитов = Новый Соответствие;
		СоответствиеРеквизитов.Вставить("odata.metadata", СоответствиеJSON["metadata"] + "/@Element");
		СтруктураНаименований = Новый Структура("Description,ИНН,КПП,ОГРН,НаименованиеПолное", );
		
		// Иногда в базе-источнике некорректно определена форма собственности контрагента, правят уже в управленческой, хаос)
		Если НЕ МассивРеквизитов.Количество() = 0 Тогда
			НовыйОбъект = Ложь;
			Ref_Key = МассивРеквизитов[0]["Ref_Key"];
		Иначе
			НовыйОбъект = Истина;
			СоответствиеРеквизитов.Вставить("ЮрФизЛицо", Выборка.ЮрФизЛицо); 
		КонецЕсли;
		
		Для каждого КлючИЗначение Из СтруктураНаименований Цикл
			СоответствиеРеквизитов.Вставить(КлючИЗначение.Ключ, Выборка[КлючИЗначение.Ключ]);
		КонецЦикла;
		
		ЗаписьJSON = Новый ЗаписьJSON;
		ЗаписьJSON.УстановитьСтроку();
		ЗаписатьJSON(ЗаписьJSON, СоответствиеРеквизитов,,,,);
		ПараметрыHTTP.ТекстЗапроса = ЗаписьJSON.Закрыть();
		
		Если НовыйОбъект Тогда
			ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1/odata/standard.odata/Catalog_Контрагенты'"), 
				СтруктураПодключения.БазаДанных);
			ПараметрыHTTP.Метод = "POST";
		Иначе
			ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1/odata/standard.odata/Catalog_Контрагенты(guid%2%3%2)'"), 
				СтруктураПодключения.БазаДанных, "'", Ref_Key);
			ПараметрыHTTP.Метод = "PATCH";
		КонецЕсли;
				
		СоздатьЗапрос(ПараметрыHTTP);
		ПараметрыHTTP.Метод = Неопределено;
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		ИначеЕсли ПараметрыHTTP.КодСостояния = 200 ИЛИ ПараметрыHTTP.КодСостояния = 201 Тогда
			
			// Если был POST нужно сбросить флаг отражения в регламентированном учете, есть у нас такой
			// Суть в том, что мы не можем управлять процедурами ПередЗаписью и ПриЗаписи в приемнике, 
			// указание флага 1C_OData_DataLoadMode = true (ОбменДанными.Загрузка) в заголовке запроса результата не дает
			Если НовыйОбъект Тогда
			
				ЧтениеJSON = Новый ЧтениеJSON; 
				ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
				СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
				Ref_Key = СоответствиеJSON["Ref_Key"];
				
				СоответствиеРеквизитов = Новый Соответствие;
				СоответствиеРеквизитов.Вставить("odata.metadata", СоответствиеJSON["metadata"]);
				СоответствиеРеквизитов.Вставить("ОтражатьВРегламентированномУчете", Ложь);
				
				ЗаписьJSON = Новый ЗаписьJSON;
				ЗаписьJSON.УстановитьСтроку();
				ЗаписатьJSON(ЗаписьJSON, СоответствиеРеквизитов,,,,);
				ПараметрыHTTP.ТекстЗапроса = ЗаписьJSON.Закрыть();
		
				ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
					НСтр("ru = '%1/odata/standard.odata/Catalog_Контрагенты(guid%2%3%2)'"), 
					СтруктураПодключения.БазаДанных, "'", Ref_Key);
				ПараметрыHTTP.Метод = "PATCH";
			
				СоздатьЗапрос(ПараметрыHTTP);
				ПараметрыHTTP.Метод = Неопределено;
				Если ПараметрыHTTP.КритическаяОшибка Тогда
					Возврат;
				ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
					Продолжить;
				КонецЕсли;
				
			КонецЕсли;
			
			НаборДанных = РегистрыСведений.ИзмененныеОбъекты.СоздатьНаборЗаписей();
			НаборДанных.Отбор.Объект.Установить(Выборка.Объект);
			НаборДанных.Записать(Истина);
			
		КонецЕсли;
		
	КонецЦикла;

	// Ищем валюту рубли в приемнике. Если не найдена - продолжать смысла нет, т.к. б/с не запишется.
	ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
		НСтр("ru = '%1/odata/standard.odata/Catalog_Валюты?$select=Ref_Key&$filter=Code eq %2643%2'"), 
		СтруктураПодключения.БазаДанных, "'");
		
	СоздатьЗапрос(ПараметрыHTTP);
	Если ПараметрыHTTP.КритическаяОшибка ИЛИ ПараметрыHTTP.КодСостояния > 299 Тогда
		Возврат;
	КонецЕсли;
	
	ЧтениеJSON = Новый ЧтениеJSON; 
	ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
	СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
	МассивРеквизитов = СоответствиеJSON["value"];
	
	Если НЕ МассивРеквизитов.Количество() = 0 Тогда
		ВалютаРубли = МассивРеквизитов[0]["Ref_Key"];
	Иначе
		ТекстСообщения = НСтр("ru = 'В базе-приемнике не найдена валюта с кодом 643. Выгрузка банковских счетов невозможна'");
		СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим, УровеньЖурналаРегистрации.Ошибка);
		Соединение = Неопределено;
		Возврат;
	КонецЕсли;
	
	// Банки и контрагенты для банковских счетов
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ИзмененныеОбъекты.Объект.Владелец КАК Объект,
		|	ИзмененныеОбъекты.Объект.Владелец.ИНН КАК ИНН,
		|	ИзмененныеОбъекты.Объект.Владелец.КПП КАК КПП,
		|	ВЫРАЗИТЬ("""" КАК СТРОКА(36)) КАК Ref_Key
		|ИЗ
		|	РегистрСведений.ИзмененныеОбъекты КАК ИзмененныеОбъекты
		|ГДЕ
		|	ИзмененныеОбъекты.Объект ССЫЛКА Справочник.БанковскиеСчета";
	
	ТаблицаКонтрагенты = Запрос.Выполнить().Выгрузить();
	
	Для каждого СтрокаТаблицы Из ТаблицаКонтрагенты Цикл
	
		// Контрагентов только ищем, если все корректно они уже должны быть в приемнике
		ПараметрыHTTP.ТекущийОбъект = СтрокаТаблицы.Объект;
		ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1/odata/standard.odata/Catalog_Контрагенты?$select=Ref_Key&$filter=ИНН eq %2%3%2 and КПП eq %2%4%2'"), 
			СтруктураПодключения.БазаДанных, "'", СтрокаТаблицы.ИНН, СтрокаТаблицы.КПП);
			
		СоздатьЗапрос(ПараметрыHTTP);
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		КонецЕсли;
		
		ЧтениеJSON = Новый ЧтениеJSON; 
		ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
		СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
		МассивРеквизитов = СоответствиеJSON["value"];
		
		Если НЕ МассивРеквизитов.Количество() = 0 Тогда
			СтрокаТаблицы.Ref_Key = МассивРеквизитов[0]["Ref_Key"];
		КонецЕсли;
		
	КонецЦикла;
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ИзмененныеОбъекты.Объект.Банк.Ссылка КАК Объект,
		|	ИзмененныеОбъекты.Объект.Банк.БИК КАК Code,
		|	ИзмененныеОбъекты.Объект.Банк.Наименование КАК Description,
		|	ИзмененныеОбъекты.Объект.Банк.КоррСчет КАК КоррСчет,
		|	ИзмененныеОбъекты.Объект.Банк.Адрес КАК Адрес,
		|	ИзмененныеОбъекты.Объект.Банк.Телефоны КАК Телефоны,
		|	ВЫРАЗИТЬ("""" КАК СТРОКА(36)) КАК Ref_Key
		|ИЗ
		|	РегистрСведений.ИзмененныеОбъекты КАК ИзмененныеОбъекты
		|ГДЕ
		|	ИзмененныеОбъекты.Объект ССЫЛКА Справочник.БанковскиеСчета";
	
	ТаблицаБанки = Запрос.Выполнить().Выгрузить();
	
	Для каждого СтрокаТаблицы Из ТаблицаБанки Цикл
	
		// Банки ищем и создаем отсутствующие
		ПараметрыHTTP.ТекущийОбъект = СтрокаТаблицы.Объект;
		ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1/odata/standard.odata/Catalog_Банки?$select=Ref_Key&$filter=Code eq %2%3%2'"), 
			СтруктураПодключения.БазаДанных, "'", СтрокаТаблицы.Code);
		
		СоздатьЗапрос(ПараметрыHTTP);
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		КонецЕсли;
		
		ЧтениеJSON = Новый ЧтениеJSON; 
		ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
		СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
		МассивРеквизитов = СоответствиеJSON["value"];
		
		// Существующий банк не обновляем, это нужно делать регламентной процедурой Загрузка классификатора
		Если НЕ МассивРеквизитов.Количество() = 0 Тогда
			СтрокаТаблицы.Ref_Key = МассивРеквизитов[0]["Ref_Key"];
			Продолжить;
		КонецЕсли;
		
		СоответствиеРеквизитов = Новый Соответствие;
		СоответствиеРеквизитов.Вставить("odata.metadata", СоответствиеJSON["metadata"] + "/@Element");
		СтруктураНаименований = Новый Структура("Code,Description,КоррСчет,Адрес,Телефоны", );
		
		Для каждого КлючИЗначение Из СтруктураНаименований Цикл
			СоответствиеРеквизитов.Вставить(КлючИЗначение.Ключ, СтрокаТаблицы[КлючИЗначение.Ключ]);
		КонецЦикла;
		
		ЗаписьJSON = Новый ЗаписьJSON;
		ЗаписьJSON.УстановитьСтроку();
		ЗаписатьJSON(ЗаписьJSON, СоответствиеРеквизитов,,,,);
		ПараметрыHTTP.ТекстЗапроса = ЗаписьJSON.Закрыть();
		
		ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1/odata/standard.odata/Catalog_Банки'"), 
			СтруктураПодключения.БазаДанных);
		ПараметрыHTTP.Метод = "POST";
			
		СоздатьЗапрос(ПараметрыHTTP);
		ПараметрыHTTP.Метод = Неопределено;
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		ИначеЕсли ПараметрыHTTP.КодСостояния = 200 ИЛИ ПараметрыHTTP.КодСостояния = 201 Тогда
			ЧтениеJSON = Новый ЧтениеJSON; 
			ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
			СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
			СтрокаТаблицы.Ref_Key = СоответствиеJSON["Ref_Key"];
		КонецЕсли;
		
	КонецЦикла;
	
	// БанковскиеСчета
	
	Запрос = Новый Запрос;
	Запрос.Текст = 
		"ВЫБРАТЬ
		|	ТаблицаБанки.Объект КАК Банк,
		|	ТаблицаБанки.Ref_Key КАК Ref_Key
		|ПОМЕСТИТЬ Банки
		|ИЗ
		|	&ТаблицаБанки КАК ТаблицаБанки
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ
		|	ТаблицаКонтрагенты.Объект КАК Контрагент,
		|	ТаблицаКонтрагенты.Ref_Key КАК Ref_Key
		|ПОМЕСТИТЬ Контрагенты
		|ИЗ
		|	&ТаблицаКонтрагенты КАК ТаблицаКонтрагенты
		|;
		|
		|////////////////////////////////////////////////////////////////////////////////
		|ВЫБРАТЬ РАЗЛИЧНЫЕ
		|	ИзмененныеОбъекты.Объект КАК Объект,
		|	""StandardODATA.Catalog_Контрагенты"" КАК Owner_Type,
		|	Контрагенты.Ref_Key КАК Owner,
		|	ИзмененныеОбъекты.Объект.НомерСчета + "", "" + ИзмененныеОбъекты.Объект.Банк.Наименование КАК Description,
		|	ИзмененныеОбъекты.Объект.НомерСчета КАК НомерСчета,
		|	Банки.Ref_Key КАК Банк_Key,
		|	""Расчетный"" КАК ВидСчета,
		|	ИзмененныеОбъекты.Объект.ДатаОткрытия КАК ДатаОткрытия,
		|	ИзмененныеОбъекты.Объект.ДатаЗакрытия КАК ДатаЗакрытия,
		|	&ВалютаРубли КАК ВалютаДенежныхСредств,
		|	ВЫБОР
		|		КОГДА ИзмененныеОбъекты.Объект.СтатусСчета = ЗНАЧЕНИЕ(Справочник.СтатусыРасчетныхСчетов.Закрыт)
		|				ИЛИ ИзмененныеОбъекты.Объект.СтатусСчета = ЗНАЧЕНИЕ(Справочник.СтатусыРасчетныхСчетов.Блок)
		|			ТОГДА ИСТИНА
		|		ИНАЧЕ ЛОЖЬ
		|	КОНЕЦ КАК ВременноНеиспользуется
		|ИЗ
		|	РегистрСведений.ИзмененныеОбъекты КАК ИзмененныеОбъекты
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Контрагенты КАК Контрагенты
		|		ПО ИзмененныеОбъекты.Объект.Владелец = Контрагенты.Контрагент
		|		ВНУТРЕННЕЕ СОЕДИНЕНИЕ Банки КАК Банки
		|		ПО ИзмененныеОбъекты.Объект.Банк = Банки.Банк
		|ГДЕ
		|	ИзмененныеОбъекты.Объект ССЫЛКА Справочник.БанковскиеСчета
		|	И НЕ Банки.Ref_Key = """"
		|	И НЕ Контрагенты.Ref_Key = """"";
	
	Запрос.УстановитьПараметр("ТаблицаБанки", ТаблицаБанки);
	Запрос.УстановитьПараметр("ТаблицаКонтрагенты", ТаблицаКонтрагенты);
	Запрос.УстановитьПараметр("ВалютаРубли", ВалютаРубли);
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	Пока Выборка.Следующий() Цикл
		
		ПараметрыHTTP.ТекущийОбъект = Выборка.Объект;
		ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
			НСтр("ru = '%1/odata/standard.odata/Catalog_БанковскиеСчета?$select=Ref_Key,Owner&$filter=НомерСчета eq %2%3%2'"), //  с isof и cast совместно в 8.3.10 не работает
			СтруктураПодключения.БазаДанных, "'", Выборка.НомерСчета);
		
		СоздатьЗапрос(ПараметрыHTTP);
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		КонецЕсли;
		
		ЧтениеJSON = Новый ЧтениеJSON; 
		ЧтениеJSON.УстановитьСтроку(ПараметрыHTTP.Ответ);
		СоответствиеJSON = ПрочитатьJSON(ЧтениеJSON, Истина);
		МассивРеквизитов = СоответствиеJSON["value"];
		
		СоответствиеРеквизитов = Новый Соответствие;
		СоответствиеРеквизитов.Вставить("odata.metadata", СоответствиеJSON["metadata"] + "/@Element");
		СтруктураНаименований = Новый Структура("Description,Банк_Key,ДатаОткрытия,ДатаЗакрытия,ВременноНеиспользуется", );
		
		НовыйОбъект = Истина;
		Если НЕ МассивРеквизитов.Количество() = 0 Тогда
			Для каждого Элемент Из МассивРеквизитов Цикл
				Если Элемент["Owner"] = Выборка.Owner Тогда
					НовыйОбъект = Ложь;
					Ref_Key = Элемент["Ref_Key"];
					Прервать;
				КонецЕсли;
			КонецЦикла;
		КонецЕсли;
		
		Если НовыйОбъект Тогда
			СоответствиеРеквизитов.Вставить("Owner_Type", Выборка.Owner_Type);
			СоответствиеРеквизитов.Вставить("Owner", Выборка.Owner);
			СоответствиеРеквизитов.Вставить("НомерСчета", Выборка.НомерСчета);
			СоответствиеРеквизитов.Вставить("ВидСчета", Выборка.ВидСчета);
			СоответствиеРеквизитов.Вставить("ВалютаДенежныхСредств_Key", Выборка.ВалютаДенежныхСредств);
		КонецЕсли;
		
		Для каждого КлючИЗначение Из СтруктураНаименований Цикл
			СоответствиеРеквизитов.Вставить(КлючИЗначение.Ключ, Выборка[КлючИЗначение.Ключ]);
		КонецЦикла;
		
		ЗаписьJSON = Новый ЗаписьJSON;
		ЗаписьJSON.УстановитьСтроку();
		ЗаписатьJSON(ЗаписьJSON, СоответствиеРеквизитов,,,,);
		ПараметрыHTTP.ТекстЗапроса = ЗаписьJSON.Закрыть();
		
		Если НовыйОбъект Тогда
			ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1/odata/standard.odata/Catalog_БанковскиеСчета'"), 
				СтруктураПодключения.БазаДанных);
			ПараметрыHTTP.Метод = "POST";
		Иначе
			ПараметрыHTTP.АдресРесурса = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(
				НСтр("ru = '%1/odata/standard.odata/Catalog_БанковскиеСчета(guid%2%3%2)'"), 
				СтруктураПодключения.БазаДанных, "'", Ref_Key);
			ПараметрыHTTP.Метод = "PATCH";
		КонецЕсли;
		
		СоздатьЗапрос(ПараметрыHTTP);
		ПараметрыHTTP.Метод = Неопределено;
		Если ПараметрыHTTP.КритическаяОшибка Тогда
			Возврат;
		ИначеЕсли ПараметрыHTTP.КодСостояния > 299 Тогда
			Продолжить;
		ИначеЕсли ПараметрыHTTP.КодСостояния = 200 ИЛИ ПараметрыHTTP.КодСостояния = 201 Тогда
			НаборДанных = РегистрыСведений.ИзмененныеОбъекты.СоздатьНаборЗаписей();
			НаборДанных.Отбор.Объект.Установить(Выборка.Объект);
			НаборДанных.Записать(Истина);
		КонецЕсли;
		
	КонецЦикла;
	
	Соединение = Неопределено;

	Если ИнтерактивныйРежим Тогда
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Завершение обработки: %1'"), ТекущаяДатаСеанса());
		СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим);
	КонецЕсли;
	
КонецПроцедуры

Код процедуры создания HTTP-запроса:

Процедура СоздатьЗапрос(ПараметрыHTTP)

	Попытка
	    Запрос = Новый HTTPЗапрос(ПараметрыHTTP.АдресРесурса, ПараметрыHTTP.ЗаголовокHTTP);
		Если ПараметрыHTTP.Метод = Неопределено Тогда
			ОтветОтСоединения = ПараметрыHTTP.Соединение.Получить(Запрос);
		Иначе
			Запрос.УстановитьТелоИзСтроки(ПараметрыHTTP.ТекстЗапроса);
			ОтветОтСоединения = ПараметрыHTTP.Соединение.ВызватьHTTPМетод(ПараметрыHTTP.Метод, Запрос);
		КонецЕсли;
		ПараметрыHTTP.КодСостояния = ОтветОтСоединения.КодСостояния;
	    ПараметрыHTTP.Ответ = СтрЗаменить(ОтветОтСоединения.ПолучитьТелоКакСтроку("UTF-8"), "odata.", "");
	Исключение
		ТекстСообщения = НСтр("ru = 'Ошибка соединения с WEB-сервером управленческой БД.'");
		СообщениеТрассировки(ТекстСообщения, ПараметрыHTTP.ИнтерактивныйРежим, УровеньЖурналаРегистрации.Ошибка);
		ПараметрыHTTP.Соединение = Неопределено;
		ПараметрыHTTP.КритическаяОшибка = Истина;
		Возврат;
	КонецПопытки;

	Если ПараметрыHTTP.КодСостояния > 299 Тогда
		ОбработатьОшибку(ПараметрыHTTP.Ответ, ПараметрыHTTP.КодСостояния, ПараметрыHTTP.ИнтерактивныйРежим, ПараметрыHTTP.ТекущийОбъект);
	КонецЕсли;
	
КонецПроцедуры

Процедуры трассировки и обработки ошибок:

Функция ОписаниеВнутреннегоКодаОшибки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","Вызов нереализованной функции"); 
	Описание.Вставить("Код101","Недопустимое значение аргумента функции"); 

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

Процедура ОбработатьОшибку(Ответ, КодСостояния, ИнтерактивныйРежим, ТекущийОбъект = Неопределено)

	Если Найти(Ответ, "<!DOCTYPE html") = 0 И Найти(Ответ, "<m:error xmlns") = 0 Тогда // json
		
		ЧтениеJSON = Новый ЧтениеJSON; 
		ЧтениеJSON.УстановитьСтроку(Ответ);
		СтруктураОтвета = ПрочитатьJSON(ЧтениеJSON);
		
		КодОшибки = СтрЗаменить(СтруктураОтвета.error.code, "-1", "101");	
		ОписаниеОшибки = ОписаниеВнутреннегоКодаОшибкиOData("Код"+КодОшибки);
		ЗначениеОшибки = СтруктураОтвета.error.message.value;
		
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Ошибка запроса. Описание ошибки: %1 - %2 Значение ошибки: %3'"), 
			СтрЗаменить(КодОшибки, "101", "-1"), ОписаниеОшибки, ЗначениеОшибки);
		
	Иначе
			
		ТекстСообщения = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Ошибка сервера. Код ошибки: %1'"), КодСостояния);
			
	КонецЕсли;
	
	Если НЕ ТекущийОбъект = Неопределено Тогда
		ТекстСообщения = ТекстСообщения + Символы.ПС + СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(НСтр("ru = 'Текущий объект: %1 Тип: %2'"), ТекущийОбъект, ТипЗнч(ТекущийОбъект));
	КонецЕсли;
	
	СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим, УровеньЖурналаРегистрации.Ошибка);

КонецПроцедуры

Процедура СообщениеТрассировки(ТекстСообщения, ИнтерактивныйРежим, УровеньЖР = Неопределено)

	Если ИнтерактивныйРежим Тогда
		ОбщегоНазначенияКлиентСервер.СообщитьПользователю(ТекстСообщения);
	Иначе
		УровеньЖР = ?(УровеньЖР=Неопределено, УровеньЖурналаРегистрации.Информация, УровеньЖР); 	
		ЗаписьЖурналаРегистрации(НСтр("ru='oData Выгрузка контрагентов и банковских счетов'"), УровеньЖР,
			ЭтотОбъект.Метаданные(),, ТекстСообщения);
	КонецЕсли;

КонецПроцедуры

Процедуру ОписаниеВнутреннегоКодаОшибкиOData сдернул откуда-то из просторов Интернет, к сожалению не помню автора, но спасибо выражаю.

В довершение добавлю, что на релизе платформы 8.3.10.2252 мне так и не удалось корректно записать через oData в приемнике запись независимого регистра сведений, перепробовал уйму вариантов, в т.ч. и через XML – стабильная ошибка 500, поэтому когда встала срочная задача сделать POST я реализовал через HTTP-сервис в приемнике. Если у кого-нибудь есть рабочие примеры - приведите плиз.

Для получения теоретического базиса и информации по публикации базы на web-сервере категорически рекомендуется прочесть:

https://its.1c.ru/db/v8311doc#bookmark:dev:TI000001358

https://its.1c.ru/db/v8311doc#bookmark:dev:TI000001426

//obrabotki1c.chavalah.ru/public/711302/

//obrabotki1c.chavalah.ru/public/565435/

Благодарю авторов данных статей, значительную часть моих познаний в данном вопросе я подчерпнул у них.

Код прилагаю в виде внешней обработки, с вопросами обращаемся)

Скачать файлы

Наименование Файл Версия Размер
Выгрузка контрагентов и банковских счетов
.epf 11,61Kb
20.03.18
4
.epf 11,61Kb 4 Скачать

См. также

Комментарии
1. Николай Беляев (freez1301) 213 21.03.18 08:33 Сейчас в теме
По мне так слишком сложно в реализации. Куда проще сделать все через SOAP и план обмена
2. Timur (timm00) 73 21.03.18 09:36 Сейчас в теме
(1) Да Николай, SOAP проще. Но. SOAP требует наличия SOAP-сервисов на стороне клиента, и производительность обмена ниже. У меня например любой обмен не занимает более 1,5 секунд при условии что происходит инициализация сеанса. А получение данных - вообще чудо производительности, обходит COM в десятки раз.
А вот планы обмена я просто недолюбливаю, считайте это эксцентрикой)
3. Константин Нагибович (gradi) 21.03.18 09:44 Сейчас в теме
(2)
обходит COM в десятки раз

Это все хорошо, когда есть возможность опубликовать базу на веб-сервере. Без этого только СОМ.
5. Timur (timm00) 73 21.03.18 10:14 Сейчас в теме
(3) Апач помогает если с лицензиями заморочка)
8. Константин Нагибович (gradi) 21.03.18 11:45 Сейчас в теме
9. Антон Антон (kare) 22.03.18 08:16 Сейчас в теме
(8)и с службой безопасности )
10. Евгений Заручейский (zarucheisky) 22.03.18 17:30 Сейчас в теме
(2) Вы хоть "ИМХО, в моём случае" добавляйте, а то так категорично судить что тормоз/шустро :(
4. Leo Po (webresurs) 149 21.03.18 09:47 Сейчас в теме
здорово!

Подскажите пож. в БП 3.0 через настройку OData включил доступ к документу платежное поручение и теперь надо в браузере получить документы за определенный период:

/odata/standard.odata/Document_ПлатежноеПоручение$format=json&$select=Number­,Date,Организация_Key,Контрагент,СуммаДокумента,НазначениеПлатежа,Комментарий,ДокументОснование_Type&$filter=Date(StartPeriod=datetime'2018-03-19T00:00:00',EndPeriod=datetime'2018-03-19T23:59:59')

выдает ошибку: не найдена , что не так подскажите?
6. Timur (timm00) 73 21.03.18 10:28 Сейчас в теме
(4) Знак вопроса забываете)
/odata/standard.odata/Document_ПлатежноеПоручение?$format=json&$select=Number ,Date,Организация_Key,Контрагент,СуммаДокумента,НазначениеПлатежа,Комментарий,ДокументОснование_Type&$filter=Date(StartPeriod=datetime'2018-03-19T00:00:00',EndPeriod=datetime'2018-03-19T23:59:59')
13. Leo Po (webresurs) 149 12.04.18 08:51 Сейчас в теме
(6) - ошибка
{
"odata.error": {
"code": "14",
"message": {
"lang": "ru",
"value": "Ошибка при разборе опции запроса $filter"
}
}
}
Показать
14. Leo Po (webresurs) 149 12.04.18 09:02 Сейчас в теме
(6) воспользовался ссылкой из (12)

/odata/standard.odata/Document_ПлатежноеПоручение?allowedOnly=true&$select=Number,Date,Контрагент,СуммаДокумента,НазначениеПлатежа,Комментарий,ДокументОснование_Type&$filter=Date ge datetime'2018-04-02T00:00:00' and Date le datetime'2018-04-02T23:59:59'
16. Timur (timm00) 73 12.04.18 12:28 Сейчас в теме
(14)
allowedOnly=true&
нужно убрать. Или добавить $
7. Илья Васильев (swimdog) 557 21.03.18 10:58 Сейчас в теме
12. Yaroslav Solntsev (Bright Sun) 23.03.18 13:32 Сейчас в теме
Мы сделали небольшой веб-сервис облегчающий генерацию OData-запросов по любой Конфигурации.
http://gen.bi-team.ru/GQ
Структуру метаданных можно передать как через файл ($metadata) так и через доп Обработку, которую можно запросить с ресурса http://bi-team.ru/
15. Leo Po (webresurs) 149 12.04.18 09:03 Сейчас в теме
(12) - спасибо
- поделитесь обработкой с "gen.bi-team.ru/GQ " на infostar ?
Bizerber; +1 Ответить
Оставьте свое сообщение