HTTP Transfer API: read-only выгрузка данных для переноса (v0.1).

Добавлены РК_TransferAPI, серверный/клиентский модули, константы ключа и адреса партнёрской ИБ, роль вызова API.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
11 changed files with 970 additions and 0 deletions
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<CommonModule uuid="a1b2c3d4-e5f6-4789-a011-000000000001">
<Properties>
<Name>РК_TransferAPIКлиент</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Transfer API клиент (партнёрская ИБ)</v8:content>
</v8:item>
</Synonym>
<Comment>HTTP-вызовы Transfer API партнёрской информационной базы</Comment>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>true</Server>
<ExternalConnection>true</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>true</ServerCall>
<Privileged>false</Privileged>
<ReturnValuesReuse>DontUse</ReturnValuesReuse>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,126 @@
#Область ПрограммныйИнтерфейс
// Выполняет именованный запрос на партнёрской ИБ (исходная crm3_placentapharm).
//
// Параметры:
// ИмяЗапроса - Строка - series_export, partners_by_inn, ...
// Параметры - Структура - параметры запроса
//
// Возвращаемое значение:
// Структура - meta, columns, rows (как в JSON API)
//
Функция ВыполнитьЗапросПартнёра(ИмяЗапроса, Параметры = Неопределено) Экспорт
Если Параметры = Неопределено Тогда
Параметры = Новый Структура;
КонецЕсли;
АдресБазы = "";
Ключ = "";
Попытка
АдресБазы = СокрЛП(Константы.РК_TransferAPIАдресПартнёра.Получить());
Ключ = СокрЛП(Константы.РК_TransferAPIКлюч.Получить());
Исключение
ВызватьИсключение "Константы Transfer API не настроены";
КонецПопытки;
Если ПустаяСтрока(АдресБазы) Тогда
ВызватьИсключение "Не задан РК_TransferAPIАдресПартнёра";
КонецЕсли;
Возврат ВыполнитьHTTPЗапрос(АдресБазы, Ключ, ИмяЗапроса, Параметры);
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ВыполнитьHTTPЗапрос(АдресБазы, Ключ, ИмяЗапроса, Параметры)
URL = СокрЛП(АдресБазы);
Тело = Новый Структура;
Тело.Вставить("query", ИмяЗапроса);
Тело.Вставить("params", Параметры);
Тело.Вставить("format", "json");
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет));
ЗаписатьJSON(Запись, Тело);
ТекстТела = Запись.Закрыть();
Разбор = РазобратьURL(АдресБазы);
ПутьЗапроса = Разбор.Путь;
Если Не СтрЗаканчиваетсяНа(ПутьЗапроса, "/") Тогда
ПутьЗапроса = ПутьЗапроса + "/";
КонецЕсли;
ПутьЗапроса = ПутьЗапроса + "v1/query";
Соединение = Новый HTTPСоединение(
Разбор.Хост,
Разбор.Порт,
Разбор.Пользователь,
Разбор.Пароль,
,
120,
?(Разбор.HTTPS, Новый ЗащищенноеСоединениеOpenSSL(), Неопределено));
ЗапросHTTP = Новый HTTPЗапрос(ПутьЗапроса);
ЗапросHTTP.Заголовки.Вставить("Authorization", "Bearer " + Ключ);
ЗапросHTTP.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
ЗапросHTTP.УстановитьТелоИзСтроки(ТекстТела, КодировкаТекста.UTF8);
ОтветHTTP = Соединение.ОтправитьДляОбработки(ЗапросHTTP);
ТекстОтвета = ОтветHTTP.ПолучитьТелоКакСтроку();
Если ОтветHTTP.КодСостояния < 200 Или ОтветHTTP.КодСостояния >= 300 Тогда
ЗаписьЖурналаРегистрации("РК_TransferAPI.Партнёр", УровеньЖурналаРегистрации.Ошибка, , ,
"HTTP " + ОтветHTTP.КодСостояния + ": " + ТекстОтвета);
ВызватьИсключение "Ошибка HTTP " + ОтветHTTP.КодСостояния + ": " + ТекстОтвета;
КонецЕсли;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстОтвета);
Результат = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
ЗаписьЖурналаРегистрации("РК_TransferAPI.Партнёр", УровеньЖурналаРегистрации.Информация, , ,
ИмяЗапроса + " → OK");
Возврат Результат;
КонецФункции
Функция РазобратьURL(Адрес)
Результат = Новый Структура("Хост, Порт, Путь, HTTPS, Пользователь, Пароль", "", 0, "", Ложь, "", "");
СтрокаURL = СокрЛП(Адрес);
HTTPS = СтрНачинаетсяС(НРег(СтрокаURL), "https://");
Если СтрНачинаетсяС(НРег(СтрокаURL), "http://") Тогда
СтрокаURL = Сред(СтрокаURL, 8);
ИначеЕсли HTTPS Тогда
СтрокаURL = Сред(СтрокаURL, 9);
КонецЕсли;
Результат.HTTPS = HTTPS;
ПозицияПути = СтрНайти(СтрокаURL, "/");
ХостПорт = ?(ПозицияПути > 0, Лев(СтрокаURL, ПозицияПути - 1), СтрокаURL);
Результат.Путь = ?(ПозицияПути > 0, Сред(СтрокаURL, ПозицияПути), "/");
ПозицияПорта = СтрНайти(ХостПорт, ":");
Если ПозицияПорта > 0 Тогда
Результат.Хост = Лев(ХостПорт, ПозицияПорта - 1);
Результат.Порт = Число(Сред(ХостПорт, ПозицияПорта + 1));
Иначе
Результат.Хост = ХостПорт;
Результат.Порт = ?(HTTPS, 443, 80);
КонецЕсли;
Возврат Результат;
КонецФункции
#КонецОбласти
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<CommonModule uuid="a1b2c3d4-e5f6-4789-a010-000000000001">
<Properties>
<Name>РК_TransferAPIСервер</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Transfer API сервер</v8:content>
</v8:item>
</Synonym>
<Comment>Логика read-only HTTP API переноса данных</Comment>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>true</Server>
<ExternalConnection>true</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>false</ServerCall>
<Privileged>false</Privileged>
<ReturnValuesReuse>DontUse</ReturnValuesReuse>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -0,0 +1,543 @@
#Область ПрограммныйИнтерфейс
Функция ОбработатьHealth(Запрос) Экспорт
РезультатПроверки = ПроверитьТокен(Запрос, Ложь);
Если РезультатПроверки <> Неопределено Тогда
Возврат РезультатПроверки;
КонецЕсли;
Данные = Новый Структура;
Данные.Вставить("status", "ok");
Данные.Вставить("ib", ИмяТекущейИБ());
Данные.Вставить("extension", "crm326RHANA");
Данные.Вставить("server_time", Формат(ТекущаяДатаСеанса(), "ДФ=yyyy-MM-ddTHH:mm:ss"));
Возврат ОтветJSON(200, Данные);
КонецФункции
Функция ОбработатьQuery(Запрос) Экспорт
РезультатПроверки = ПроверитьТокен(Запрос, Истина);
Если РезультатПроверки <> Неопределено Тогда
Возврат РезультатПроверки;
КонецЕсли;
Тело = ПрочитатьТелоJSON(Запрос);
Если ТипЗнч(Тело) <> Тип("Структура") Тогда
Возврат ОтветОшибка(400, "INVALID_BODY", "Ожидается JSON-объект");
КонецЕсли;
ИмяЗапроса = "";
Если Тело.Свойство("query") Тогда
ИмяЗапроса = СокрЛП(Строка(Тело.query));
КонецЕсли;
Если ПустаяСтрока(ИмяЗапроса) Тогда
Возврат ОтветОшибка(400, "MISSING_QUERY", "Не указан параметр query");
КонецЕсли;
Параметры = Новый Структура;
Если Тело.Свойство("params") И ТипЗнч(Тело.params) = Тип("Структура") Тогда
Параметры = Тело.params;
КонецЕсли;
Формат = "json";
Если Тело.Свойство("format") Тогда
Формат = НРег(СокрЛП(Строка(Тело.format)));
КонецЕсли;
Возврат СформироватьОтветЗапроса(ИмяЗапроса, Параметры, Формат);
КонецФункции
Функция ОбработатьExport(Запрос) Экспорт
РезультатПроверки = ПроверитьТокен(Запрос, Истина);
Если РезультатПроверки <> Неопределено Тогда
Возврат РезультатПроверки;
КонецЕсли;
Отчет = "";
Если Запрос.ПараметрыURL.Свойство("report") Тогда
Отчет = НРег(СокрЛП(Строка(Запрос.ПараметрыURL["report"])));
КонецЕсли;
Если ПустаяСтрока(Отчет) Тогда
Возврат ОтветОшибка(404, "MISSING_REPORT", "Не указан report в URL");
КонецЕсли;
Параметры = ПараметрыИзQueryString(Запрос);
Формат = "json";
Если Параметры.Свойство("format") Тогда
Формат = НРег(СокрЛП(Строка(Параметры.format)));
КонецЕсли;
ИмяЗапроса = ИмяЗапросаПоОтчету(Отчет);
Если ПустаяСтрока(ИмяЗапроса) Тогда
Возврат ОтветОшибка(404, "UNKNOWN_REPORT", "Неизвестный отчёт: " + Отчет);
КонецЕсли;
Возврат СформироватьОтветЗапроса(ИмяЗапроса, Параметры, Формат);
КонецФункции
Функция ВыполнитьИменованныйЗапрос(ИмяЗапроса, Параметры) Экспорт
Имя = НРег(СокрЛП(ИмяЗапроса));
Если Имя = "series_export" Тогда
Возврат ЗапросSeriesExport(Параметры);
ИначеЕсли Имя = "partners_by_inn" Тогда
Возврат ЗапросPartnersByInn(Параметры);
Иначе
ВызватьИсключение "Неизвестный запрос: " + ИмяЗапроса;
КонецЕсли;
КонецФункции
#КонецОбласти
#Область СлужебныеПроцедурыИФункции
Функция ПроверитьТокен(Запрос, ПривилегированныйДоступ)
ОжидаемыйКлюч = "";
Попытка
ОжидаемыйКлюч = СокрЛП(Константы.РК_TransferAPIКлюч.Получить());
Исключение
Возврат ОтветОшибка(503, "NOT_CONFIGURED", "Константа РК_TransferAPIКлюч недоступна");
КонецПопытки;
Если ПустаяСтрока(ОжидаемыйКлюч) Тогда
Возврат ОтветОшибка(503, "KEY_NOT_SET", "Не задан ключ API (РК_TransferAPIКлюч)");
КонецЕсли;
Переданный = ИзвлечьТокен(Запрос);
Если ПустаяСтрока(Переданный) Тогда
Возврат ОтветОшибка(401, "UNAUTHORIZED", "Требуется заголовок Authorization: Bearer");
КонецЕсли;
Если Переданный <> ОжидаемыйКлюч Тогда
ЗаписьЖурналаРегистрации("РК_TransferAPI.Авторизация", УровеньЖурналаРегистрации.Предупреждение, , ,
"Неверный токен API");
Возврат ОтветОшибка(401, "UNAUTHORIZED", "Неверный токен");
КонецЕсли;
Если ПривилегированныйДоступ Тогда
УстановитьПривилегированныйРежим(Истина);
КонецЕсли;
Возврат Неопределено;
КонецФункции
Функция ИзвлечьТокен(Запрос)
Заголовок = Запрос.Заголовки.Получить("Authorization");
Если Заголовок = Неопределено Тогда
Заголовок = Запрос.Заголовки.Получить("authorization");
КонецЕсли;
Если Заголовок <> Неопределено Тогда
Заголовок = СокрЛП(Строка(Заголовок));
Префикс = "Bearer ";
Если СтрНачинаетсяС(ВРег(Заголовок), ВРег(Префикс)) Тогда
Возврат СокрЛП(Сред(Заголовок, СтрДлина(Префикс) + 1));
КонецЕсли;
КонецЕсли;
Параметры = ПараметрыИзQueryString(Запрос);
Если Параметры.Свойство("token") Тогда
Возврат СокрЛП(Строка(Параметры.token));
КонецЕсли;
Возврат "";
КонецФункции
Функция ПараметрыИзQueryString(Запрос)
Результат = Новый Структура;
Для Каждого КлючИЗначение Из Запрос.ПараметрыЗапроса Цикл
Результат.Вставить(Строка(КлючИЗначение.Ключ), Строка(КлючИЗначение.Значение));
КонецЦикла;
Возврат Результат;
КонецФункции
Функция ПрочитатьТелоJSON(Запрос)
Текст = Запрос.ПолучитьТелоКакСтроку();
Если ПустаяСтрока(Текст) Тогда
Возврат Новый Структура;
КонецЕсли;
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(Текст);
Результат = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Возврат Результат;
КонецФункции
Функция СформироватьОтветЗапроса(ИмяЗапроса, Параметры, Формат)
Попытка
ДанныеЗапроса = ВыполнитьИменованныйЗапрос(ИмяЗапроса, Параметры);
Исключение
ТекстОшибки = ОписаниеОшибки();
ЗаписьЖурналаРегистрации("РК_TransferAPI.Запрос", УровеньЖурналаРегистрации.Ошибка, , ,
ИмяЗапроса + ": " + ТекстОшибки);
Если СтрНайти(ТекстОшибки, "Неизвестный запрос") > 0 Тогда
Возврат ОтветОшибка(400, "UNKNOWN_QUERY", ТекстОшибки);
КонецЕсли;
Возврат ОтветОшибка(500, "QUERY_ERROR", ТекстОшибки);
КонецПопытки;
Если Формат = "csv" Тогда
Возврат ОтветCSV(ДанныеЗапроса);
КонецЕсли;
Мета = Новый Структура;
Мета.Вставить("query", ИмяЗапроса);
Мета.Вставить("ib", ИмяТекущейИБ());
Мета.Вставить("count", ДанныеЗапроса.rows.Количество());
Мета.Вставить("generated_at", Формат(ТекущаяДатаСеанса(), "ДФ=yyyy-MM-ddTHH:mm:ss"));
ОтветДанные = Новый Структура;
ОтветДанные.Вставить("meta", Мета);
ОтветДанные.Вставить("columns", ДанныеЗапроса.columns);
ОтветДанные.Вставить("rows", ДанныеЗапроса.rows);
Возврат ОтветJSON(200, ОтветДанные);
КонецФункции
Функция ИмяЗапросаПоОтчету(Отчет)
Если Отчет = "series" Тогда
Возврат "series_export";
ИначеЕсли Отчет = "partners_by_inn" Тогда
Возврат "partners_by_inn";
КонецЕсли;
Возврат "";
КонецФункции
Функция ИмяТекущейИБ()
СтрокаСоединения = СтрокаСоединенияИнформационнойБазы();
Позиция = СтрНайти(СтрокаСоединения, "Ref=""");
Если Позиция > 0 Тогда
Хвост = Сред(СтрокаСоединения, Позиция + 5);
ПозицияКонец = СтрНайти(Хвост, """");
Если ПозицияКонец > 0 Тогда
Возврат Лев(Хвост, ПозицияКонец - 1);
КонецЕсли;
КонецЕсли;
Возврат "crm3";
КонецФункции
#Область ИменованныеЗапросы
Функция ЗапросSeriesExport(Параметры)
Лимит = ПараметрЧисло(Параметры, "limit", 0);
ВидGUID = ПараметрСтрока(Параметры, "vid_guid");
Колонки = Новый Массив;
Колонки.Добавить("GUID");
Колонки.Добавить("Наименование");
Колонки.Добавить("Номер");
Колонки.Добавить("ГоденДо");
Колонки.Добавить("ДатаПроизводства");
Колонки.Добавить("ВидНоменклатуры_GUID");
Колонки.Добавить("ВидНоменклатуры");
Колонки.Добавить("ИспНомерСерии");
Колонки.Добавить("ИспСрокГодности");
Колонки.Добавить("СерияЦО_GUID");
Колонки.Добавить("СерияЦО");
Колонки.Добавить("ПометкаУдаления");
Колонки.Добавить("СсылокДвижений");
ЕстьСерииЦО = Метаданные.Справочники.Найти("СерииНоменклатурыДляЦенообразования") <> Неопределено;
ТекстЗапроса =
"ВЫБРАТЬ
| Сер.Ссылка КАК Ссылка,
| Сер.Наименование КАК Наименование,
| Сер.Номер КАК Номер,
| Сер.ГоденДо КАК ГоденДо,
| Сер.ДатаПроизводства КАК ДатаПроизводства,
| Сер.ВидНоменклатуры КАК ВидНоменклатуры,
| Сер.ВидНоменклатуры.Наименование КАК ВидНаименование,
| Сер.ПометкаУдаления КАК ПометкаУдаления";
Если ЕстьСерииЦО Тогда
ТекстЗапроса = ТекстЗапроса + ",
| Сер.СерияНоменклатурыДляЦенообразования КАК СерияЦО";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|ИЗ
| Справочник.СерииНоменклатуры КАК Сер
|ГДЕ
| ИСТИНА";
Если ЗначениеЗаполнено(ВидGUID) Тогда
ТекстЗапроса = ТекстЗапроса + "
| И Сер.ВидНоменклатуры.УникальныйИдентификатор() = &ВидGUID";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|УПОРЯДОЧИТЬ ПО
| ВидНаименование,
| Номер,
| Наименование";
Запрос = Новый Запрос(ТекстЗапроса);
Если ЗначениеЗаполнено(ВидGUID) Тогда
Запрос.УстановитьПараметр("ВидGUID", Новый УникальныйИдентификатор(ВидGUID));
КонецЕсли;
Выб = Запрос.Выполнить().Выбрать();
Строки = Новый Массив;
Сч = 0;
Пока Выб.Следующий() Цикл
Если Лимит > 0 И Сч >= Лимит Тогда
Прервать;
КонецЕсли;
Вид = Выб.ВидНоменклатуры;
ИспНомер = Ложь;
ИспСрок = Ложь;
Если ЗначениеЗаполнено(Вид) Тогда
Попытка
Настр = Справочники.ВидыНоменклатуры.НастройкиИспользованияСерий(Вид);
ИспНомер = Настр.ИспользоватьНомерСерии;
ИспСрок = Настр.ИспользоватьСрокГодностиСерии;
Исключение
КонецПопытки;
КонецЕсли;
Ссылок = 0;
ЗапросДв = Новый Запрос(
"ВЫБРАТЬ
| КОЛИЧЕСТВО(РАЗЛИЧНЫЕ Дв.Регистратор) КАК Кол
|ИЗ
| РегистрНакопления.ДвиженияСерийТоваров КАК Дв
|ГДЕ
| Дв.Серия = &Серия");
ЗапросДв.УстановитьПараметр("Серия", Выб.Ссылка);
Попытка
ВыбДв = ЗапросДв.Выполнить().Выбрать();
Если ВыбДв.Следующий() Тогда
Ссылок = ВыбДв.Кол;
КонецЕсли;
Исключение
КонецПопытки;
СтрокаДанных = Новый Массив;
СтрокаДанных.Добавить(GUIDСтрока(Выб.Ссылка));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.Наименование));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.Номер));
СтрокаДанных.Добавить(ДатаСтрока(Выб.ГоденДо));
СтрокаДанных.Добавить(ДатаСтрока(Выб.ДатаПроизводства));
СтрокаДанных.Добавить(GUIDСтрока(Вид));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.ВидНаименование));
СтрокаДанных.Добавить(?(ИспНомер, "Да", "Нет"));
СтрокаДанных.Добавить(?(ИспСрок, "Да", "Нет"));
Если ЕстьСерииЦО Тогда
СтрокаДанных.Добавить(GUIDСтрока(Выб.СерияЦО));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.СерияЦО));
Иначе
СтрокаДанных.Добавить("");
СтрокаДанных.Добавить("");
КонецЕсли;
СтрокаДанных.Добавить(?(Выб.ПометкаУдаления, "Да", "Нет"));
СтрокаДанных.Добавить(Формат(Ссылок, "ЧГ=0"));
Строки.Добавить(СтрокаДанных);
Сч = Сч + 1;
КонецЦикла;
Возврат Новый Структура("columns, rows", Колонки, Строки);
КонецФункции
Функция ЗапросPartnersByInn(Параметры)
ИНН = ПараметрСтрока(Параметры, "inn");
КПП = ПараметрСтрока(Параметры, "kpp");
Если ПустаяСтрока(ИНН) Тогда
ВызватьИсключение "Не указан параметр inn";
КонецЕсли;
Колонки = Новый Массив;
Колонки.Добавить("GUID");
Колонки.Добавить("Наименование");
Колонки.Добавить("ИНН");
Колонки.Добавить("КПП");
Колонки.Добавить("ПометкаУдаления");
Колонки.Добавить("Партнер_GUID");
Колонки.Добавить("Партнер");
Колонки.Добавить("ЮрФизЛицо");
ТекстЗапроса =
"ВЫБРАТЬ
| Контр.Ссылка КАК Ссылка,
| Контр.Наименование КАК Наименование,
| Контр.ИНН КАК ИНН,
| Контр.КПП КАК КПП,
| Контр.ПометкаУдаления КАК ПометкаУдаления,
| Контр.Партнер КАК Партнер,
| Контр.ЮрФизЛицо КАК ЮрФизЛицо
|ИЗ
| Справочник.Контрагенты КАК Контр
|ГДЕ
| Контр.ИНН = &ИНН";
Если ЗначениеЗаполнено(КПП) Тогда
ТекстЗапроса = ТекстЗапроса + "
| И Контр.КПП = &КПП";
КонецЕсли;
ТекстЗапроса = ТекстЗапроса + "
|УПОРЯДОЧИТЬ ПО
| Контр.ПометкаУдаления,
| Контр.Наименование";
Запрос = Новый Запрос(ТекстЗапроса);
Запрос.УстановитьПараметр("ИНН", ИНН);
Если ЗначениеЗаполнено(КПП) Тогда
Запрос.УстановитьПараметр("КПП", КПП);
КонецЕсли;
Выб = Запрос.Выполнить().Выбрать();
Строки = Новый Массив;
Пока Выб.Следующий() Цикл
СтрокаДанных = Новый Массив;
СтрокаДанных.Добавить(GUIDСтрока(Выб.Ссылка));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.Наименование));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.ИНН));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.КПП));
СтрокаДанных.Добавить(?(Выб.ПометкаУдаления, "Да", "Нет"));
СтрокаДанных.Добавить(GUIDСтрока(Выб.Партнер));
СтрокаДанных.Добавить(ЭкранироватьCSV(Выб.Партнер));
СтрокаДанных.Добавить(Строка(Выб.ЮрФизЛицо));
Строки.Добавить(СтрокаДанных);
КонецЦикла;
Возврат Новый Структура("columns, rows", Колонки, Строки);
КонецФункции
#КонецОбласти
#Область ОтветыHTTP
Функция ОтветJSON(КодСостояния, Данные)
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку(Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.Нет));
ЗаписатьJSON(Запись, Данные);
ТекстJSON = Запись.Закрыть();
Ответ = Новый HTTPСервисОтвет(КодСостояния);
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(ТекстJSON, КодировкаТекста.UTF8);
Возврат Ответ;
КонецФункции
Функция ОтветОшибка(КодСостояния, КодОшибки, Сообщение)
Тело = Новый Структура("error", Новый Структура("code, message", КодОшибки, Сообщение));
Возврат ОтветJSON(КодСостояния, Тело);
КонецФункции
Функция ОтветCSV(ДанныеЗапроса)
СтрокиТекста = Новый Массив;
СтрокиТекста.Добавить(СтрСоединить(ДанныеЗапроса.columns, ";"));
Для Каждого СтрокаДанных Из ДанныеЗапроса.rows Цикл
СтрокиТекста.Добавить(СтрСоединить(СтрокаДанных, ";"));
КонецЦикла;
ТекстCSV = СтрСоединить(СтрокиТекста, Символы.ПС);
Ответ = Новый HTTPСервисОтвет(200);
Ответ.Заголовки.Вставить("Content-Type", "text/csv; charset=utf-8");
Ответ.УстановитьТелоИзСтроки(ТекстCSV, КодировкаТекста.UTF8);
Возврат Ответ;
КонецФункции
#КонецОбласти
#Область Вспомогательные
Функция ПараметрСтрока(Параметры, Имя)
Если ТипЗнч(Параметры) = Тип("Структура") И Параметры.Свойство(Имя) Тогда
Возврат СокрЛП(Строка(Параметры[Имя]));
КонецЕсли;
Возврат "";
КонецФункции
Функция ПараметрЧисло(Параметры, Имя, ЗначениеПоУмолчанию)
Стр = ПараметрСтрока(Параметры, Имя);
Если ПустаяСтрока(Стр) Тогда
Возврат ЗначениеПоУмолчанию;
КонецЕсли;
Попытка
Возврат Число(Стр);
Исключение
Возврат ЗначениеПоУмолчанию;
КонецПопытки;
КонецФункции
Функция GUIDСтрока(Ссылка)
Если Не ЗначениеЗаполнено(Ссылка) Тогда
Возврат "";
КонецЕсли;
Возврат НРег(Строка(Ссылка.УникальныйИдентификатор()));
КонецФункции
Функция ДатаСтрока(ДатаЗнач)
Если Не ЗначениеЗаполнено(ДатаЗнач) Или ДатаЗнач <= Дата(2000, 1, 1) Тогда
Возврат "";
КонецЕсли;
Возврат Формат(ДатаЗнач, "ДФ=yyyy-MM-dd");
КонецФункции
Функция ЭкранироватьCSV(Значение)
Текст = СтрЗаменить(Строка(Значение), ";", ",");
Возврат СокрЛП(Текст);
КонецФункции
#КонецОбласти
#КонецОбласти
+6
View File
@@ -92,7 +92,10 @@
<Role>РК_ОсновнаяРоль</Role>
<Role>РК_ОтладкаОтображение</Role>
<Role>РК_ОтладкаУправление</Role>
<Role>РК_ВызовTransferAPI</Role>
<CommonModule>РК_ОбщегоНазначения</CommonModule>
<CommonModule>РК_TransferAPIСервер</CommonModule>
<CommonModule>РК_TransferAPIКлиент</CommonModule>
<CommonModule>РК_ОбщегоНазначенияКлиентСервер</CommonModule>
<CommonModule>РК_ОбщегоНазначенияВызовСервера</CommonModule>
<CommonModule>ОбщегоНазначенияКлиентСервер</CommonModule>
@@ -103,6 +106,9 @@
<FunctionalOption>РК_ФункциональнаяОпцияРежимаОтладки</FunctionalOption>
<DefinedType>РаспоряжениеНаОтгрузку</DefinedType>
<Constant>РК_РежимОтладкиРана</Constant>
<Constant>РК_TransferAPIКлюч</Constant>
<Constant>РК_TransferAPIАдресПартнёра</Constant>
<HTTPService>РК_TransferAPI</HTTPService>
<CommonForm>РК_ФормаНастроекРана</CommonForm>
<Catalog>ДоговорыКонтрагентов</Catalog>
<Document>РеализацияТоваровУслуг</Document>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<Constant uuid="a1b2c3d4-e5f6-4789-a021-000000000001">
<InternalInfo>
<xr:GeneratedType name="ConstantManager.РК_TransferAPIАдресПартнёра" category="Manager">
<xr:TypeId>a1b2c3d4-e5f6-4789-a021-000000000011</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a021-000000000012</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ConstantValueManager.РК_TransferAPIАдресПартнёра" category="ValueManager">
<xr:TypeId>a1b2c3d4-e5f6-4789-a021-000000000013</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a021-000000000014</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ConstantValueKey.РК_TransferAPIАдресПартнёра" category="ValueKey">
<xr:TypeId>a1b2c3d4-e5f6-4789-a021-000000000015</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a021-000000000016</xr:ValueId>
</xr:GeneratedType>
</InternalInfo>
<Properties>
<Name>РК_TransferAPIАдресПартнёра</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Transfer API — адрес партнёрской ИБ</v8:content>
</v8:item>
</Synonym>
<Comment>Базовый URL API исходной ИБ, напр. https://host/crm3_placentapharm/hs/transfer</Comment>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<UseStandardCommands>true</UseStandardCommands>
<PasswordMode>false</PasswordMode>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<FillChecking>DontCheck</FillChecking>
<DataLockControlMode>Managed</DataLockControlMode>
</Properties>
</Constant>
</MetaDataObject>
@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<Constant uuid="a1b2c3d4-e5f6-4789-a020-000000000001">
<InternalInfo>
<xr:GeneratedType name="ConstantManager.РК_TransferAPIКлюч" category="Manager">
<xr:TypeId>a1b2c3d4-e5f6-4789-a020-000000000011</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a020-000000000012</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ConstantValueManager.РК_TransferAPIКлюч" category="ValueManager">
<xr:TypeId>a1b2c3d4-e5f6-4789-a020-000000000013</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a020-000000000014</xr:ValueId>
</xr:GeneratedType>
<xr:GeneratedType name="ConstantValueKey.РК_TransferAPIКлюч" category="ValueKey">
<xr:TypeId>a1b2c3d4-e5f6-4789-a020-000000000015</xr:TypeId>
<xr:ValueId>a1b2c3d4-e5f6-4789-a020-000000000016</xr:ValueId>
</xr:GeneratedType>
</InternalInfo>
<Properties>
<Name>РК_TransferAPIКлюч</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Transfer API — ключ доступа</v8:content>
</v8:item>
</Synonym>
<Comment>Bearer-токен для HTTP API переноса данных</Comment>
<Type>
<v8:Type>xs:string</v8:Type>
<v8:StringQualifiers>
<v8:Length>0</v8:Length>
<v8:AllowedLength>Variable</v8:AllowedLength>
</v8:StringQualifiers>
</Type>
<UseStandardCommands>true</UseStandardCommands>
<PasswordMode>true</PasswordMode>
<MultiLine>false</MultiLine>
<ExtendedEdit>false</ExtendedEdit>
<FillChecking>DontCheck</FillChecking>
<DataLockControlMode>Managed</DataLockControlMode>
</Properties>
</Constant>
</MetaDataObject>
+107
View File
@@ -0,0 +1,107 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<HTTPService uuid="a1b2c3d4-e5f6-4789-a001-000000000001">
<Properties>
<Name>РК_TransferAPI</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Transfer API (перенос данных)</v8:content>
</v8:item>
</Synonym>
<Comment>Read-only HTTP API для анализа и переноса данных</Comment>
<RootURL>transfer</RootURL>
<ReuseSessions>Use</ReuseSessions>
<SessionMaxAge>20</SessionMaxAge>
</Properties>
<ChildObjects>
<URLTemplate uuid="a1b2c3d4-e5f6-4789-a001-000000000002">
<Properties>
<Name>Health</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Health</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Template>/v1/health</Template>
</Properties>
<ChildObjects>
<Method uuid="a1b2c3d4-e5f6-4789-a001-000000000003">
<Properties>
<Name>GET</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>GET</v8:content>
</v8:item>
</Synonym>
<Comment/>
<HTTPMethod>GET</HTTPMethod>
<Handler>HealthGETЗапрос</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="a1b2c3d4-e5f6-4789-a001-000000000004">
<Properties>
<Name>Query</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Query</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Template>/v1/query</Template>
</Properties>
<ChildObjects>
<Method uuid="a1b2c3d4-e5f6-4789-a001-000000000005">
<Properties>
<Name>POST</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>POST</v8:content>
</v8:item>
</Synonym>
<Comment/>
<HTTPMethod>POST</HTTPMethod>
<Handler>QueryPOSTЗапрос</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="a1b2c3d4-e5f6-4789-a001-000000000006">
<Properties>
<Name>Export</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Export</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Template>/v1/export/{report}</Template>
</Properties>
<ChildObjects>
<Method uuid="a1b2c3d4-e5f6-4789-a001-000000000007">
<Properties>
<Name>GET</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>GET</v8:content>
</v8:item>
</Synonym>
<Comment/>
<HTTPMethod>GET</HTTPMethod>
<Handler>ExportGETЗапрос</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
</ChildObjects>
</HTTPService>
</MetaDataObject>
@@ -0,0 +1,16 @@
#Область ОбработчикиHTTP
Функция HealthGETЗапрос(Запрос)
Возврат РК_TransferAPIСервер.ОбработатьHealth(Запрос);
КонецФункции
Функция QueryPOSTЗапрос(Запрос)
Возврат РК_TransferAPIСервер.ОбработатьQuery(Запрос);
КонецФункции
Функция ExportGETЗапрос(Запрос)
Возврат РК_TransferAPIСервер.ОбработатьExport(Запрос);
КонецФункции
#КонецОбласти
+15
View File
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.20">
<Role uuid="a1b2c3d4-e5f6-4789-a030-000000000001">
<Properties>
<Name>РК_ВызовTransferAPI</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Вызов Transfer API (перенос данных)</v8:content>
</v8:item>
</Synonym>
<Comment>HTTP read-only API для анализа и переноса</Comment>
</Properties>
</Role>
</MetaDataObject>
@@ -0,0 +1,27 @@
<?xml version="1.0" encoding="UTF-8"?>
<Rights xmlns="http://v8.1c.ru/8.2/roles" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="Rights" version="2.20">
<setForNewObjects>false</setForNewObjects>
<setForAttributesByDefault>true</setForAttributesByDefault>
<independentRightsOfChildObjects>false</independentRightsOfChildObjects>
<object>
<name>HTTPService.РК_TransferAPI</name>
<right>
<name>Use</name>
<value>true</value>
</right>
</object>
<object>
<name>Constant.РК_TransferAPIКлюч</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
<object>
<name>Constant.РК_TransferAPIАдресПартнёра</name>
<right>
<name>Read</name>
<value>true</value>
</right>
</object>
</Rights>