From 2dfa49b2cfb905842c1413cee7997eaa890aac89 Mon Sep 17 00:00:00 2001 From: Michael BAG Date: Thu, 9 Jul 2026 15:22:07 +0300 Subject: [PATCH] =?UTF-8?q?HTTP=20Transfer=20API:=20read-only=20=D0=B2?= =?UTF-8?q?=D1=8B=D0=B3=D1=80=D1=83=D0=B7=D0=BA=D0=B0=20=D0=B4=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D1=8B=D1=85=20=D0=B4=D0=BB=D1=8F=20=D0=BF=D0=B5=D1=80?= =?UTF-8?q?=D0=B5=D0=BD=D0=BE=D1=81=D0=B0=20(v0.1).?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Добавлены РК_TransferAPI, серверный/клиентский модули, константы ключа и адреса партнёрской ИБ, роль вызова API. Co-authored-by: Cursor --- src/CommonModules/РК_TransferAPIКлиент.xml | 23 + .../РК_TransferAPIКлиент/Ext/Module.bsl | 126 ++++ src/CommonModules/РК_TransferAPIСервер.xml | 23 + .../РК_TransferAPIСервер/Ext/Module.bsl | 543 ++++++++++++++++++ src/Configuration.xml | 6 + src/Constants/РК_TransferAPIАдресПартнёра.xml | 42 ++ src/Constants/РК_TransferAPIКлюч.xml | 42 ++ src/HTTPServices/РК_TransferAPI.xml | 107 ++++ .../РК_TransferAPI/Ext/Module.bsl | 16 + src/Roles/РК_ВызовTransferAPI.xml | 15 + src/Roles/РК_ВызовTransferAPI/Ext/Rights.xml | 27 + 11 files changed, 970 insertions(+) create mode 100644 src/CommonModules/РК_TransferAPIКлиент.xml create mode 100644 src/CommonModules/РК_TransferAPIКлиент/Ext/Module.bsl create mode 100644 src/CommonModules/РК_TransferAPIСервер.xml create mode 100644 src/CommonModules/РК_TransferAPIСервер/Ext/Module.bsl create mode 100644 src/Constants/РК_TransferAPIАдресПартнёра.xml create mode 100644 src/Constants/РК_TransferAPIКлюч.xml create mode 100644 src/HTTPServices/РК_TransferAPI.xml create mode 100644 src/HTTPServices/РК_TransferAPI/Ext/Module.bsl create mode 100644 src/Roles/РК_ВызовTransferAPI.xml create mode 100644 src/Roles/РК_ВызовTransferAPI/Ext/Rights.xml diff --git a/src/CommonModules/РК_TransferAPIКлиент.xml b/src/CommonModules/РК_TransferAPIКлиент.xml new file mode 100644 index 0000000..ac618ac --- /dev/null +++ b/src/CommonModules/РК_TransferAPIКлиент.xml @@ -0,0 +1,23 @@ + + + + + РК_TransferAPIКлиент + + + ru + Transfer API клиент (партнёрская ИБ) + + + HTTP-вызовы Transfer API партнёрской информационной базы + false + false + true + true + false + true + false + DontUse + + + diff --git a/src/CommonModules/РК_TransferAPIКлиент/Ext/Module.bsl b/src/CommonModules/РК_TransferAPIКлиент/Ext/Module.bsl new file mode 100644 index 0000000..0a48328 --- /dev/null +++ b/src/CommonModules/РК_TransferAPIКлиент/Ext/Module.bsl @@ -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); + КонецЕсли; + + Возврат Результат; + +КонецФункции + +#КонецОбласти diff --git a/src/CommonModules/РК_TransferAPIСервер.xml b/src/CommonModules/РК_TransferAPIСервер.xml new file mode 100644 index 0000000..8f5f7d6 --- /dev/null +++ b/src/CommonModules/РК_TransferAPIСервер.xml @@ -0,0 +1,23 @@ + + + + + РК_TransferAPIСервер + + + ru + Transfer API сервер + + + Логика read-only HTTP API переноса данных + false + false + true + true + false + false + false + DontUse + + + diff --git a/src/CommonModules/РК_TransferAPIСервер/Ext/Module.bsl b/src/CommonModules/РК_TransferAPIСервер/Ext/Module.bsl new file mode 100644 index 0000000..56ba6f5 --- /dev/null +++ b/src/CommonModules/РК_TransferAPIСервер/Ext/Module.bsl @@ -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(Значение) + + Текст = СтрЗаменить(Строка(Значение), ";", ","); + Возврат СокрЛП(Текст); + +КонецФункции + +#КонецОбласти + +#КонецОбласти diff --git a/src/Configuration.xml b/src/Configuration.xml index 1698bcb..d07a432 100644 --- a/src/Configuration.xml +++ b/src/Configuration.xml @@ -92,7 +92,10 @@ РК_ОсновнаяРоль РК_ОтладкаОтображение РК_ОтладкаУправление + РК_ВызовTransferAPI РК_ОбщегоНазначения + РК_TransferAPIСервер + РК_TransferAPIКлиент РК_ОбщегоНазначенияКлиентСервер РК_ОбщегоНазначенияВызовСервера ОбщегоНазначенияКлиентСервер @@ -103,6 +106,9 @@ РК_ФункциональнаяОпцияРежимаОтладки РаспоряжениеНаОтгрузку РК_РежимОтладкиРана + РК_TransferAPIКлюч + РК_TransferAPIАдресПартнёра + РК_TransferAPI РК_ФормаНастроекРана ДоговорыКонтрагентов РеализацияТоваровУслуг diff --git a/src/Constants/РК_TransferAPIАдресПартнёра.xml b/src/Constants/РК_TransferAPIАдресПартнёра.xml new file mode 100644 index 0000000..7343707 --- /dev/null +++ b/src/Constants/РК_TransferAPIАдресПартнёра.xml @@ -0,0 +1,42 @@ + + + + + + a1b2c3d4-e5f6-4789-a021-000000000011 + a1b2c3d4-e5f6-4789-a021-000000000012 + + + a1b2c3d4-e5f6-4789-a021-000000000013 + a1b2c3d4-e5f6-4789-a021-000000000014 + + + a1b2c3d4-e5f6-4789-a021-000000000015 + a1b2c3d4-e5f6-4789-a021-000000000016 + + + + РК_TransferAPIАдресПартнёра + + + ru + Transfer API — адрес партнёрской ИБ + + + Базовый URL API исходной ИБ, напр. https://host/crm3_placentapharm/hs/transfer + + xs:string + + 0 + Variable + + + true + false + false + false + DontCheck + Managed + + + diff --git a/src/Constants/РК_TransferAPIКлюч.xml b/src/Constants/РК_TransferAPIКлюч.xml new file mode 100644 index 0000000..7d3f02f --- /dev/null +++ b/src/Constants/РК_TransferAPIКлюч.xml @@ -0,0 +1,42 @@ + + + + + + a1b2c3d4-e5f6-4789-a020-000000000011 + a1b2c3d4-e5f6-4789-a020-000000000012 + + + a1b2c3d4-e5f6-4789-a020-000000000013 + a1b2c3d4-e5f6-4789-a020-000000000014 + + + a1b2c3d4-e5f6-4789-a020-000000000015 + a1b2c3d4-e5f6-4789-a020-000000000016 + + + + РК_TransferAPIКлюч + + + ru + Transfer API — ключ доступа + + + Bearer-токен для HTTP API переноса данных + + xs:string + + 0 + Variable + + + true + true + false + false + DontCheck + Managed + + + diff --git a/src/HTTPServices/РК_TransferAPI.xml b/src/HTTPServices/РК_TransferAPI.xml new file mode 100644 index 0000000..8ee3d17 --- /dev/null +++ b/src/HTTPServices/РК_TransferAPI.xml @@ -0,0 +1,107 @@ + + + + + РК_TransferAPI + + + ru + Transfer API (перенос данных) + + + Read-only HTTP API для анализа и переноса данных + transfer + Use + 20 + + + + + Health + + + ru + Health + + + + + + + + + GET + + + ru + GET + + + + GET + HealthGETЗапрос + + + + + + + Query + + + ru + Query + + + + + + + + + POST + + + ru + POST + + + + POST + QueryPOSTЗапрос + + + + + + + Export + + + ru + Export + + + + + + + + + GET + + + ru + GET + + + + GET + ExportGETЗапрос + + + + + + + diff --git a/src/HTTPServices/РК_TransferAPI/Ext/Module.bsl b/src/HTTPServices/РК_TransferAPI/Ext/Module.bsl new file mode 100644 index 0000000..7f4c237 --- /dev/null +++ b/src/HTTPServices/РК_TransferAPI/Ext/Module.bsl @@ -0,0 +1,16 @@ + +#Область ОбработчикиHTTP + +Функция HealthGETЗапрос(Запрос) + Возврат РК_TransferAPIСервер.ОбработатьHealth(Запрос); +КонецФункции + +Функция QueryPOSTЗапрос(Запрос) + Возврат РК_TransferAPIСервер.ОбработатьQuery(Запрос); +КонецФункции + +Функция ExportGETЗапрос(Запрос) + Возврат РК_TransferAPIСервер.ОбработатьExport(Запрос); +КонецФункции + +#КонецОбласти diff --git a/src/Roles/РК_ВызовTransferAPI.xml b/src/Roles/РК_ВызовTransferAPI.xml new file mode 100644 index 0000000..958ed19 --- /dev/null +++ b/src/Roles/РК_ВызовTransferAPI.xml @@ -0,0 +1,15 @@ + + + + + РК_ВызовTransferAPI + + + ru + Вызов Transfer API (перенос данных) + + + HTTP read-only API для анализа и переноса + + + diff --git a/src/Roles/РК_ВызовTransferAPI/Ext/Rights.xml b/src/Roles/РК_ВызовTransferAPI/Ext/Rights.xml new file mode 100644 index 0000000..1c99335 --- /dev/null +++ b/src/Roles/РК_ВызовTransferAPI/Ext/Rights.xml @@ -0,0 +1,27 @@ + + + false + true + false + + HTTPService.РК_TransferAPI + + Use + true + + + + Constant.РК_TransferAPIКлюч + + Read + true + + + + Constant.РК_TransferAPIАдресПартнёра + + Read + true + + +