phase-3: replace CFE with main-config patch (per user request)
User decided to patch the main BIT.RA configuration instead of installing an extension. - bitra-cfe/ removed in previous step - bitra-patch/ added with BSL files for direct copy-paste into Configurator: - CommonModules/bfd_IntegrationAPIHelpers/Module.bsl (134 lines) — privileged server helpers - HTTPServices/bfd_IntegrationAPI/Module.bsl (478 lines) — 11 GET handlers - README.md (274 lines): step-by-step Configurator instructions: - object properties (CommonModule flags, HTTPService rootURL, URL templates, methods) - role permissions matrix (15 BIT.RA objects + HTTPService + CommonModule) - user bfd_api_user creation - Apache publication - curl tests for each endpoint
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// Общий модуль bfd_IntegrationAPIHelpers
|
||||
//
|
||||
// Назначение: вспомогательные процедуры/функции для HTTP-сервиса bfd_IntegrationAPI
|
||||
// (read-only REST API для проекта bit-flight-deck).
|
||||
//
|
||||
// Свойства модуля (выставить в Конфигураторе):
|
||||
// Сервер = Истина
|
||||
// Вызов сервера = Истина
|
||||
// Внешнее соединение = Истина (для всякого случая)
|
||||
// Привилегированный = Истина (важно — позволяет читать всё в обход RLS)
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#Область ПрограммныйИнтерфейс
|
||||
|
||||
// Формирует HTTPСервисОтвет с JSON-телом.
|
||||
// Параметры:
|
||||
// Данные — Произвольный (Структура / Массив / Число / Строка / Дата / Булево / Неопределено)
|
||||
// Код — Число (по умолчанию 200)
|
||||
// Возвращаемое значение: HTTPСервисОтвет
|
||||
//
|
||||
Функция СформироватьОтветJSON(Знач Данные, Знач Код = 200) Экспорт
|
||||
|
||||
ЗаписьJSON = Новый ЗаписьJSON;
|
||||
ПараметрыJSON = Новый ПараметрыЗаписиJSON(ПереносСтрокJSON.БезПереносов, "", Ложь, "yyyy-MM-ddTHH:mm:ss");
|
||||
ЗаписьJSON.УстановитьСтроку(ПараметрыJSON);
|
||||
ЗаписатьJSON(ЗаписьJSON, Данные);
|
||||
|
||||
Ответ = Новый HTTPСервисОтвет(Код);
|
||||
Ответ.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
|
||||
Ответ.УстановитьТелоИзСтроки(ЗаписьJSON.Закрыть(), КодировкаТекста.UTF8, ИспользованиеByteOrderMark.НеИспользовать);
|
||||
Возврат Ответ;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Сериализованная ошибка.
|
||||
//
|
||||
Функция СформироватьОтветОшибки(Знач СообщениеОшибки, Знач Код = 500) Экспорт
|
||||
|
||||
Структура = Новый Структура("error", СообщениеОшибки);
|
||||
Возврат СформироватьОтветJSON(Структура, Код);
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Парсит ISO 8601 дату из строки query-параметра.
|
||||
// Возвращает Дату или '00010101' при ошибке/пусто.
|
||||
//
|
||||
Функция ПарсДатуISO(Знач СтрокаДаты) Экспорт
|
||||
|
||||
Если ПустаяСтрока(СтрокаДаты) Тогда
|
||||
Возврат Дата(1, 1, 1);
|
||||
КонецЕсли;
|
||||
|
||||
ОчищеннаяСтрока = СтрЗаменить(СтрЗаменить(СтрокаДаты, "T", " "), "Z", "");
|
||||
Попытка
|
||||
Возврат Дата(ОчищеннаяСтрока);
|
||||
Исключение
|
||||
Возврат Дата(1, 1, 1);
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Строковое представление GUID ссылки или пустая строка.
|
||||
//
|
||||
Функция UUID(Знач Ссылка) Экспорт
|
||||
|
||||
Если НЕ ЗначениеЗаполнено(Ссылка) Тогда
|
||||
Возврат "";
|
||||
КонецЕсли;
|
||||
Возврат Строка(Ссылка.УникальныйИдентификатор());
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Извлекает email из ТЧ КонтактнаяИнформация пользователя.
|
||||
//
|
||||
Функция ИзвлечьEmail(Знач Пользователь) Экспорт
|
||||
|
||||
Если НЕ ЗначениеЗаполнено(Пользователь) Тогда
|
||||
Возврат "";
|
||||
КонецЕсли;
|
||||
|
||||
Попытка
|
||||
Объект = Пользователь.ПолучитьОбъект();
|
||||
Исключение
|
||||
Возврат "";
|
||||
КонецПопытки;
|
||||
|
||||
Если Объект = Неопределено Тогда
|
||||
Возврат "";
|
||||
КонецЕсли;
|
||||
|
||||
Попытка
|
||||
Для Каждого Стр Из Объект.КонтактнаяИнформация Цикл
|
||||
ВидНаименование = "";
|
||||
Если ЗначениеЗаполнено(Стр.Вид) Тогда
|
||||
ВидНаименование = Строка(Стр.Вид);
|
||||
КонецЕсли;
|
||||
Если СтрНайти(НРег(ВидНаименование), "email") > 0 ИЛИ СтрНайти(НРег(ВидНаименование), "почта") > 0 Тогда
|
||||
Если ЗначениеЗаполнено(Стр.Представление) Тогда
|
||||
Возврат Стр.Представление;
|
||||
КонецЕсли;
|
||||
КонецЕсли;
|
||||
КонецЦикла;
|
||||
Исключение
|
||||
Возврат "";
|
||||
КонецПопытки;
|
||||
|
||||
Возврат "";
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Лимит выборки из query-параметра.
|
||||
//
|
||||
Функция ПарсЛимит(Знач Запрос, Знач MaxDefault = 1000, Знач MaxAllowed = 10000) Экспорт
|
||||
|
||||
Параметр = Запрос.ПараметрыЗапроса.Получить("limit");
|
||||
Если ПустаяСтрока(Параметр) Тогда
|
||||
Возврат MaxDefault;
|
||||
КонецЕсли;
|
||||
|
||||
Попытка
|
||||
Значение = Число(Параметр);
|
||||
Исключение
|
||||
Возврат MaxDefault;
|
||||
КонецПопытки;
|
||||
|
||||
Если Значение <= 0 Тогда
|
||||
Возврат MaxDefault;
|
||||
КонецЕсли;
|
||||
Возврат Мин(Значение, MaxAllowed);
|
||||
|
||||
КонецФункции
|
||||
|
||||
#КонецОбласти
|
||||
@@ -0,0 +1,478 @@
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
// HTTPService bfd_IntegrationAPI — read-only REST API для bit-flight-deck.
|
||||
//
|
||||
// Корневой URL (RootURL) — bfd-api
|
||||
// После публикации: http://<host>/<dbname>/hs/bfd-api/v1/...
|
||||
//
|
||||
// Все методы GET. Аутентификация Basic (пользователь bfd_api_user).
|
||||
// Ошибки → JSON {"error":"..."} с HTTP-кодом 500.
|
||||
// Все ID объектов сериализуются как строковый UUID без префикса класса.
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
#Область ОбработчикиЗапросов
|
||||
|
||||
// GET /v1/health
|
||||
//
|
||||
Функция HealthGet(Запрос) Экспорт
|
||||
|
||||
Ответ = Новый Структура;
|
||||
Ответ.Вставить("status", "ok");
|
||||
Ответ.Вставить("service", "bfd_IntegrationAPI");
|
||||
Ответ.Вставить("server_time", Формат(ТекущаяДатаСеанса(), "ДФ=yyyy-MM-ddTHH:mm:ss"));
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Ответ);
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/employees
|
||||
//
|
||||
Функция EmployeesGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| Пользователи.Ссылка КАК Ссылка,
|
||||
| Пользователи.Наименование КАК ФИО,
|
||||
| Пользователи.EVA_ID КАК EvaID,
|
||||
| Пользователи.Офис КАК Офис,
|
||||
| Пользователи.Подразделение КАК Подразделение,
|
||||
| Пользователи.Ставка КАК Ставка,
|
||||
| Пользователи.Недействителен КАК Недействителен,
|
||||
| Пользователи.ДолженЗаполнятьОтчет КАК ДолженЗаполнятьОтчет
|
||||
|ИЗ
|
||||
| Справочник.Пользователи КАК Пользователи
|
||||
|ГДЕ
|
||||
| НЕ Пользователи.ПометкаУдаления
|
||||
|УПОРЯДОЧИТЬ ПО
|
||||
| Пользователи.Наименование";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("id", bfd_IntegrationAPIHelpers.UUID(Выборка.Ссылка));
|
||||
Запись.Вставить("full_name", Выборка.ФИО);
|
||||
Запись.Вставить("email", НРег(bfd_IntegrationAPIHelpers.ИзвлечьEmail(Выборка.Ссылка)));
|
||||
Запись.Вставить("eva_id", Выборка.EvaID);
|
||||
Запись.Вставить("office", Строка(Выборка.Офис));
|
||||
Запись.Вставить("department", Строка(Выборка.Подразделение));
|
||||
Запись.Вставить("rate", Выборка.Ставка);
|
||||
Запись.Вставить("is_active", НЕ Выборка.Недействителен);
|
||||
Запись.Вставить("should_fill_report", Выборка.ДолженЗаполнятьОтчет);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/works?modified_since=<ISO_DATE>&limit=N
|
||||
//
|
||||
Функция WorksGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
МодифицированоПосле = bfd_IntegrationAPIHelpers.ПарсДатуISO(Запрос.ПараметрыЗапроса.Получить("modified_since"));
|
||||
Лимит = bfd_IntegrationAPIHelpers.ПарсЛимит(Запрос, 1000, 10000);
|
||||
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.УстановитьПараметр("МодифицированоПосле", МодифицированоПосле);
|
||||
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ ПЕРВЫЕ " + Формат(Лимит, "ЧГ=") + "
|
||||
| Работы.Ссылка КАК Ссылка,
|
||||
| Работы.Номер КАК Номер,
|
||||
| Работы.Дата КАК Дата,
|
||||
| Работы.Исполнитель КАК Исполнитель,
|
||||
| Работы.Автор КАК Автор,
|
||||
| Работы.Подразделение КАК Подразделение,
|
||||
| Работы.Офис КАК Офис,
|
||||
| Работы.Утвержден КАК Утвержден,
|
||||
| Работы.Комментарий КАК Комментарий,
|
||||
| Работы.ИтогоЧасов КАК ИтогоЧасов
|
||||
|ИЗ
|
||||
| Документ.Работы КАК Работы
|
||||
|ГДЕ
|
||||
| Работы.Дата >= &МодифицированоПосле
|
||||
| И Работы.Проведен
|
||||
|УПОРЯДОЧИТЬ ПО
|
||||
| Работы.Дата УБЫВ";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("id", bfd_IntegrationAPIHelpers.UUID(Выборка.Ссылка));
|
||||
Запись.Вставить("number", Выборка.Номер);
|
||||
Запись.Вставить("date", Формат(Выборка.Дата, "ДФ=yyyy-MM-ddTHH:mm:ss"));
|
||||
Запись.Вставить("employee_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Исполнитель));
|
||||
Запись.Вставить("author_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Автор));
|
||||
Запись.Вставить("department", Строка(Выборка.Подразделение));
|
||||
Запись.Вставить("office", Строка(Выборка.Офис));
|
||||
Запись.Вставить("approved", Выборка.Утвержден);
|
||||
Запись.Вставить("comment", Выборка.Комментарий);
|
||||
Запись.Вставить("total_hours", Выборка.ИтогоЧасов);
|
||||
Запись.Вставить("rows", ПолучитьСтрокиРаботы(Выборка.Ссылка));
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/projects
|
||||
//
|
||||
Функция ProjectsGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| Проекты.Ссылка КАК Ссылка,
|
||||
| Проекты.Код КАК Код,
|
||||
| Проекты.Наименование КАК Наименование,
|
||||
| Проекты.EVA_ID КАК EvaID,
|
||||
| Проекты.Кодификатор КАК Кодификатор,
|
||||
| Проекты.Владелец КАК Клиент,
|
||||
| Проекты.Владелец.Наименование КАК КлиентИмя,
|
||||
| Проекты.Договор КАК Договор,
|
||||
| Проекты.Конфигурация КАК Конфигурация,
|
||||
| Проекты.Офис КАК Офис,
|
||||
| Проекты.РуководительПроекта КАК РуководительПроекта,
|
||||
| Проекты.МенеджерПроекта КАК МенеджерПроекта,
|
||||
| Проекты.СтатусПроекта КАК СтатусПроекта,
|
||||
| Проекты.СтатусДоговора КАК СтатусДоговора,
|
||||
| Проекты.ДатаСтарта КАК ДатаСтарта,
|
||||
| Проекты.ДатаФиниш КАК ДатаФиниш,
|
||||
| Проекты.Бюджет КАК Бюджет,
|
||||
| Проекты.ПометкаУдаления КАК ПометкаУдаления
|
||||
|ИЗ
|
||||
| Справочник.Проекты КАК Проекты";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("id", bfd_IntegrationAPIHelpers.UUID(Выборка.Ссылка));
|
||||
Запись.Вставить("code", Выборка.Код);
|
||||
Запись.Вставить("name", Выборка.Наименование);
|
||||
Запись.Вставить("eva_id", Выборка.EvaID);
|
||||
Запись.Вставить("project_code", Выборка.Кодификатор);
|
||||
Запись.Вставить("client_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Клиент));
|
||||
Запись.Вставить("client_name", Выборка.КлиентИмя);
|
||||
Запись.Вставить("contract_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Договор));
|
||||
Запись.Вставить("configuration", Строка(Выборка.Конфигурация));
|
||||
Запись.Вставить("office", Строка(Выборка.Офис));
|
||||
Запись.Вставить("manager_id", bfd_IntegrationAPIHelpers.UUID(Выборка.РуководительПроекта));
|
||||
Запись.Вставить("project_manager_id", bfd_IntegrationAPIHelpers.UUID(Выборка.МенеджерПроекта));
|
||||
Запись.Вставить("status", Строка(Выборка.СтатусПроекта));
|
||||
Запись.Вставить("contract_status", Строка(Выборка.СтатусДоговора));
|
||||
Запись.Вставить("start_date", ?(ЗначениеЗаполнено(Выборка.ДатаСтарта), Формат(Выборка.ДатаСтарта, "ДФ=yyyy-MM-dd"), ""));
|
||||
Запись.Вставить("finish_date", ?(ЗначениеЗаполнено(Выборка.ДатаФиниш), Формат(Выборка.ДатаФиниш, "ДФ=yyyy-MM-dd"), ""));
|
||||
Запись.Вставить("budget", Выборка.Бюджет);
|
||||
Запись.Вставить("deleted", Выборка.ПометкаУдаления);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/stages
|
||||
//
|
||||
Функция StagesGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| ЭтапыПроектов.Ссылка КАК Ссылка,
|
||||
| ЭтапыПроектов.Код КАК Код,
|
||||
| ЭтапыПроектов.Наименование КАК Наименование,
|
||||
| ЭтапыПроектов.Владелец КАК Владелец,
|
||||
| ЭтапыПроектов.Кодификатор КАК Кодификатор,
|
||||
| ЭтапыПроектов.ДатаНачала КАК ДатаНачала,
|
||||
| ЭтапыПроектов.ДатаОкончания КАК ДатаОкончания,
|
||||
| ЭтапыПроектов.Выполнен КАК Выполнен,
|
||||
| ЭтапыПроектов.АктПодписан КАК АктПодписан,
|
||||
| ЭтапыПроектов.ПометкаУдаления КАК ПометкаУдаления
|
||||
|ИЗ
|
||||
| Справочник.ЭтапыПроектов КАК ЭтапыПроектов";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("id", bfd_IntegrationAPIHelpers.UUID(Выборка.Ссылка));
|
||||
Запись.Вставить("code", Выборка.Код);
|
||||
Запись.Вставить("name", Выборка.Наименование);
|
||||
Запись.Вставить("project_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Владелец));
|
||||
Запись.Вставить("project_code",Выборка.Кодификатор);
|
||||
Запись.Вставить("start_date", ?(ЗначениеЗаполнено(Выборка.ДатаНачала), Формат(Выборка.ДатаНачала, "ДФ=yyyy-MM-dd"), ""));
|
||||
Запись.Вставить("end_date", ?(ЗначениеЗаполнено(Выборка.ДатаОкончания), Формат(Выборка.ДатаОкончания, "ДФ=yyyy-MM-dd"), ""));
|
||||
Запись.Вставить("completed", Выборка.Выполнен);
|
||||
Запись.Вставить("act_signed", Выборка.АктПодписан);
|
||||
Запись.Вставить("deleted", Выборка.ПометкаУдаления);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/work_types
|
||||
//
|
||||
Функция WorkTypesGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
Результат = Новый Массив;
|
||||
МетаОбъект = Метаданные.Перечисления.ВидыРабот;
|
||||
Для Каждого ЗначениеМД Из МетаОбъект.ЗначенияПеречисления Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("code", ЗначениеМД.Имя);
|
||||
Запись.Вставить("label", ЗначениеМД.Синоним);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/dictionaries
|
||||
//
|
||||
Функция DictionariesGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
Ответ = Новый Структура;
|
||||
Ответ.Вставить("offices", ПростойСправочник("Офис"));
|
||||
Ответ.Вставить("departments", ПростойСправочник("Подразделение"));
|
||||
Ответ.Вставить("managers", ПростойСправочник("Менеджеры"));
|
||||
Ответ.Вставить("configurations", ПростойСправочник("Конфигурации"));
|
||||
Ответ.Вставить("contracts", ПростойСправочник("Договоры"));
|
||||
Ответ.Вставить("planning_scenarios", ПростойСправочник("СценарииПланирования"));
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Ответ);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/dept_history?modified_since=<ISO_DATE>
|
||||
//
|
||||
Функция DeptHistoryGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
МодифицированоПосле = bfd_IntegrationAPIHelpers.ПарсДатуISO(Запрос.ПараметрыЗапроса.Получить("modified_since"));
|
||||
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.УстановитьПараметр("МодифицированоПосле", МодифицированоПосле);
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| ИсторияПодразделений.Период КАК Период,
|
||||
| ИсторияПодразделений.Сотрудник КАК Сотрудник,
|
||||
| ИсторияПодразделений.Подразделение КАК Подразделение
|
||||
|ИЗ
|
||||
| РегистрСведений.ПодразделениеСотрудников КАК ИсторияПодразделений
|
||||
|ГДЕ
|
||||
| ИсторияПодразделений.Период >= &МодифицированоПосле";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("period", Формат(Выборка.Период, "ДФ=yyyy-MM-dd"));
|
||||
Запись.Вставить("employee_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Сотрудник));
|
||||
Запись.Вставить("department_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Подразделение));
|
||||
Запись.Вставить("department_name", Строка(Выборка.Подразделение));
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/project_register?modified_since=<ISO_DATE>
|
||||
// Для MVP-3, на старте можно не использовать.
|
||||
//
|
||||
Функция ProjectRegisterGet(Запрос) Экспорт
|
||||
|
||||
Попытка
|
||||
МодифицированоПосле = bfd_IntegrationAPIHelpers.ПарсДатуISO(Запрос.ПараметрыЗапроса.Получить("modified_since"));
|
||||
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.УстановитьПараметр("МодифицированоПосле", МодифицированоПосле);
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| Регистр.Период КАК Период,
|
||||
| Регистр.Регистратор КАК Регистратор,
|
||||
| Регистр.Сценарий КАК Сценарий,
|
||||
| Регистр.Проект КАК Проект,
|
||||
| Регистр.ЭтапПроекта КАК ЭтапПроекта,
|
||||
| Регистр.ВидАналитики КАК ВидАналитики,
|
||||
| Регистр.Исполнитель КАК Исполнитель,
|
||||
| Регистр.Сумма КАК Сумма,
|
||||
| Регистр.СуммаАкт КАК СуммаАкт,
|
||||
| Регистр.СуммаРасход КАК СуммаРасход
|
||||
|ИЗ
|
||||
| РегистрНакопления.ОборотыПроектныхПоказателей_v2 КАК Регистр
|
||||
|ГДЕ
|
||||
| Регистр.Период >= &МодифицированоПосле";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("period", Формат(Выборка.Период, "ДФ=yyyy-MM-ddTHH:mm:ss"));
|
||||
Запись.Вставить("registrator_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Регистратор));
|
||||
Запись.Вставить("scenario", Строка(Выборка.Сценарий));
|
||||
Запись.Вставить("project_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Проект));
|
||||
Запись.Вставить("stage_id", bfd_IntegrationAPIHelpers.UUID(Выборка.ЭтапПроекта));
|
||||
Запись.Вставить("analytics_type", Строка(Выборка.ВидАналитики));
|
||||
Запись.Вставить("employee_id", bfd_IntegrationAPIHelpers.UUID(Выборка.Исполнитель));
|
||||
Запись.Вставить("sum_total", Выборка.Сумма);
|
||||
Запись.Вставить("sum_acted", Выборка.СуммаАкт);
|
||||
Запись.Вставить("sum_expense", Выборка.СуммаРасход);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/eva_mapping/projects
|
||||
//
|
||||
Функция EvaMappingProjectsGet(Запрос) Экспорт
|
||||
|
||||
Возврат ОтдатьРегистрМаппинга("СоответствиеПроектовEVA_РА");
|
||||
|
||||
КонецФункции
|
||||
|
||||
// GET /v1/eva_mapping/clients
|
||||
//
|
||||
Функция EvaMappingClientsGet(Запрос) Экспорт
|
||||
|
||||
Возврат ОтдатьРегистрМаппинга("СоответствиеКонтрагентовEVA_РА");
|
||||
|
||||
КонецФункции
|
||||
|
||||
#КонецОбласти
|
||||
|
||||
#Область СлужебныеПроцедурыИФункции
|
||||
|
||||
// Возвращает массив строк ТЧ "Работы" документа.
|
||||
//
|
||||
Функция ПолучитьСтрокиРаботы(Знач ДокументСсылка)
|
||||
|
||||
Объект = ДокументСсылка.ПолучитьОбъект();
|
||||
Если Объект = Неопределено Тогда
|
||||
Возврат Новый Массив;
|
||||
КонецЕсли;
|
||||
|
||||
Строки = Новый Массив;
|
||||
Для Каждого Стр Из Объект.Работы Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("row_index", Стр.НомерСтроки);
|
||||
Запись.Вставить("description", Стр.СодержаниеРабот);
|
||||
Запись.Вставить("hours", Стр.КоличествоЧасов);
|
||||
Запись.Вставить("work_type", Строка(Стр.ВидРаботы));
|
||||
Запись.Вставить("work_type_code", ?(ЗначениеЗаполнено(Стр.ВидРаботы), XMLСтрока(Стр.ВидРаботы), ""));
|
||||
Запись.Вставить("client_id", bfd_IntegrationAPIHelpers.UUID(Стр.Клиент));
|
||||
Запись.Вставить("client_name", Строка(Стр.Клиент));
|
||||
Запись.Вставить("manager_id", bfd_IntegrationAPIHelpers.UUID(Стр.Менеджер));
|
||||
Запись.Вставить("project_id", bfd_IntegrationAPIHelpers.UUID(Стр.Проект));
|
||||
Запись.Вставить("stage_id", bfd_IntegrationAPIHelpers.UUID(Стр.Этап));
|
||||
Запись.Вставить("request_number", Стр.НомерЗаявки);
|
||||
Запись.Вставить("lt_id", bfd_IntegrationAPIHelpers.UUID(Стр.ЛТ));
|
||||
Запись.Вставить("work_done", Стр.РаботаВыполнена);
|
||||
Строки.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
Возврат Строки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Возвращает простой плоский справочник как массив объектов {id, code, name}.
|
||||
//
|
||||
Функция ПростойСправочник(Знач ИмяСправочника)
|
||||
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.Текст =
|
||||
"ВЫБРАТЬ
|
||||
| Спр.Ссылка КАК Ссылка,
|
||||
| Спр.Код КАК Код,
|
||||
| Спр.Наименование КАК Наименование
|
||||
|ИЗ
|
||||
| Справочник." + ИмяСправочника + " КАК Спр
|
||||
|ГДЕ
|
||||
| НЕ Спр.ПометкаУдаления";
|
||||
|
||||
Выборка = Запрос1С.Выполнить().Выбрать();
|
||||
Результат = Новый Массив;
|
||||
Пока Выборка.Следующий() Цикл
|
||||
Запись = Новый Структура;
|
||||
Запись.Вставить("id", bfd_IntegrationAPIHelpers.UUID(Выборка.Ссылка));
|
||||
Запись.Вставить("code", Выборка.Код);
|
||||
Запись.Вставить("name", Выборка.Наименование);
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
Возврат Результат;
|
||||
|
||||
КонецФункции
|
||||
|
||||
// Универсальный helper для регистров маппинга EVA_РА.
|
||||
// Перебирает колонки регистра, ссылки сериализует UUID, даты ISO.
|
||||
//
|
||||
Функция ОтдатьРегистрМаппинга(Знач ИмяРегистра)
|
||||
|
||||
Попытка
|
||||
Запрос1С = Новый Запрос;
|
||||
Запрос1С.Текст = "ВЫБРАТЬ * ИЗ РегистрСведений." + ИмяРегистра;
|
||||
ТЗ = Запрос1С.Выполнить().Выгрузить();
|
||||
|
||||
Результат = Новый Массив;
|
||||
Для Каждого Стр Из ТЗ Цикл
|
||||
Запись = Новый Структура;
|
||||
Для Каждого Колонка Из ТЗ.Колонки Цикл
|
||||
Значение = Стр[Колонка.Имя];
|
||||
Если ТипЗнч(Значение) = Тип("Дата") Тогда
|
||||
Запись.Вставить(Колонка.Имя, Формат(Значение, "ДФ=yyyy-MM-ddTHH:mm:ss"));
|
||||
ИначеЕсли ТипЗнч(Значение) = Тип("Булево") ИЛИ ТипЗнч(Значение) = Тип("Число") ИЛИ ТипЗнч(Значение) = Тип("Строка") Тогда
|
||||
Запись.Вставить(Колонка.Имя, Значение);
|
||||
Иначе
|
||||
Запись.Вставить(Колонка.Имя, ?(ЗначениеЗаполнено(Значение), bfd_IntegrationAPIHelpers.UUID(Значение), ""));
|
||||
КонецЕсли;
|
||||
КонецЦикла;
|
||||
Результат.Добавить(Запись);
|
||||
КонецЦикла;
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветJSON(Результат);
|
||||
Исключение
|
||||
Возврат bfd_IntegrationAPIHelpers.СформироватьОтветОшибки(ОписаниеОшибки());
|
||||
КонецПопытки;
|
||||
|
||||
КонецФункции
|
||||
|
||||
#КонецОбласти
|
||||
@@ -0,0 +1,274 @@
|
||||
# Доработка BIT.RA — read-only REST API для bit-flight-deck
|
||||
|
||||
Доработка **самой конфигурации BIT.RA** (не расширение). Добавляются 3 объекта метаданных + 1 пользователь + публикация на Apache.
|
||||
|
||||
## Объекты для добавления в конфигурацию
|
||||
|
||||
### 1. Общий модуль `bfd_IntegrationAPIHelpers`
|
||||
|
||||
**Путь в дереве:** Общие → Общие модули → Добавить.
|
||||
|
||||
**Свойства модуля** (на вкладке свойств):
|
||||
|
||||
| Свойство | Значение |
|
||||
|---|---|
|
||||
| Имя | `bfd_IntegrationAPIHelpers` |
|
||||
| Синоним | `BFD: API helpers` |
|
||||
| Глобальный | Ложь |
|
||||
| **Сервер** | **Истина** |
|
||||
| **Вызов сервера** | **Истина** |
|
||||
| Клиент (управляемое приложение) | Ложь |
|
||||
| Клиент (обычное приложение) | Ложь |
|
||||
| **Внешнее соединение** | **Истина** |
|
||||
| **Привилегированный** | **Истина** ⚠️ важно |
|
||||
| Повторное использование возвращаемых значений | Не использовать |
|
||||
|
||||
**Код модуля** — вставить целиком из [`CommonModules/bfd_IntegrationAPIHelpers/Module.bsl`](CommonModules/bfd_IntegrationAPIHelpers/Module.bsl).
|
||||
|
||||
### 2. HTTP-сервис `bfd_IntegrationAPI`
|
||||
|
||||
**Путь в дереве:** Общие → HTTP-сервисы → Добавить.
|
||||
|
||||
**Свойства сервиса:**
|
||||
|
||||
| Свойство | Значение |
|
||||
|---|---|
|
||||
| Имя | `bfd_IntegrationAPI` |
|
||||
| Синоним | `BFD: Integration API` |
|
||||
| **Корневой URL** | **`bfd-api`** |
|
||||
| Повторное использование сессий | Не использовать |
|
||||
|
||||
**Шаблоны URL и методы.** Внутри HTTP-сервиса добавляем **11 шаблонов URL**. Для каждого:
|
||||
1. Правой кнопкой на сервис → Добавить → **Шаблон URL**.
|
||||
2. Задать имя шаблона и URL (см. таблицу).
|
||||
3. Внутри шаблона: правой кнопкой → Добавить → **Метод**. Имя — `Get`, HTTP-метод — `GET`, Обработчик — имя функции (автодополнится по `<ИмяШаблона><ИмяМетода>`).
|
||||
|
||||
| Имя шаблона | URL | Имя метода | HTTP | Обработчик (функция в модуле HTTP-сервиса) |
|
||||
|---|---|---|---|---|
|
||||
| `Health` | `/v1/health` | `Get` | GET | `HealthGet` |
|
||||
| `Employees` | `/v1/employees` | `Get` | GET | `EmployeesGet` |
|
||||
| `Works` | `/v1/works` | `Get` | GET | `WorksGet` |
|
||||
| `Projects` | `/v1/projects` | `Get` | GET | `ProjectsGet` |
|
||||
| `Stages` | `/v1/stages` | `Get` | GET | `StagesGet` |
|
||||
| `WorkTypes` | `/v1/work_types` | `Get` | GET | `WorkTypesGet` |
|
||||
| `Dictionaries` | `/v1/dictionaries` | `Get` | GET | `DictionariesGet` |
|
||||
| `DeptHistory` | `/v1/dept_history` | `Get` | GET | `DeptHistoryGet` |
|
||||
| `ProjectRegister` | `/v1/project_register` | `Get` | GET | `ProjectRegisterGet` |
|
||||
| `EvaMappingProjects` | `/v1/eva_mapping/projects` | `Get` | GET | `EvaMappingProjectsGet` |
|
||||
| `EvaMappingClients` | `/v1/eva_mapping/clients` | `Get` | GET | `EvaMappingClientsGet` |
|
||||
|
||||
**Код модуля HTTP-сервиса** — открыть «Модуль» сервиса и вставить целиком из [`HTTPServices/bfd_IntegrationAPI/Module.bsl`](HTTPServices/bfd_IntegrationAPI/Module.bsl).
|
||||
|
||||
### 3. Роль `bfd_API_Чтение`
|
||||
|
||||
**Путь в дереве:** Общие → Роли → Добавить.
|
||||
|
||||
**Свойства роли:**
|
||||
|
||||
| Свойство | Значение |
|
||||
|---|---|
|
||||
| Имя | `bfd_API_Чтение` |
|
||||
| Синоним | `BFD: API чтение` |
|
||||
|
||||
**Права на объекты** (в таблице прав отметить галочки):
|
||||
|
||||
| Объект | Чтение | Просмотр | Использование |
|
||||
|---|:-:|:-:|:-:|
|
||||
| `Справочник.Пользователи` | ✓ | ✓ | — |
|
||||
| `Справочник.Подразделение` | ✓ | ✓ | — |
|
||||
| `Справочник.Офис` | ✓ | ✓ | — |
|
||||
| `Справочник.Менеджеры` | ✓ | ✓ | — |
|
||||
| `Справочник.Клиенты` | ✓ | ✓ | — |
|
||||
| `Справочник.Проекты` | ✓ | ✓ | — |
|
||||
| `Справочник.ЭтапыПроектов` | ✓ | ✓ | — |
|
||||
| `Справочник.Конфигурации` | ✓ | ✓ | — |
|
||||
| `Справочник.Договоры` | ✓ | ✓ | — |
|
||||
| `Справочник.СценарииПланирования` | ✓ | ✓ | — |
|
||||
| `Документ.Работы` | ✓ | ✓ | — |
|
||||
| `РегистрСведений.ПодразделениеСотрудников` | ✓ | — | — |
|
||||
| `РегистрНакопления.ОборотыПроектныхПоказателей_v2` | ✓ | — | — |
|
||||
| `РегистрСведений.СоответствиеПроектовEVA_РА` | ✓ | — | — |
|
||||
| `РегистрСведений.СоответствиеКонтрагентовEVA_РА` | ✓ | — | — |
|
||||
| `Перечисление.ВидыРабот` | ✓ | — | — |
|
||||
| `HTTPСервис.bfd_IntegrationAPI` | — | — | ✓ |
|
||||
| `ОбщийМодуль.bfd_IntegrationAPIHelpers` | — | — | ✓ |
|
||||
| Запуск тонкого клиента / толстого / веб-клиента | — | — | (по необходимости) |
|
||||
|
||||
**Важно:** в свойствах роли отметить «**Устанавливать права для новых объектов**» = Ложь (роль строго ограниченная).
|
||||
|
||||
### 4. Пользователь `bfd_api_user`
|
||||
|
||||
В режиме **Предприятие** (или Конфигуратор → Администрирование → Пользователи) создать:
|
||||
|
||||
| Поле | Значение |
|
||||
|---|---|
|
||||
| Имя | `bfd_api_user` |
|
||||
| Полное имя | `BFD: API service user` |
|
||||
| Аутентификация 1С:Предприятия | ✓ (для Basic auth) |
|
||||
| Пароль | сгенерировать криптостойкий 16+ символов, передать в команду N8N |
|
||||
| Аутентификация ОС | — |
|
||||
| Запрещено изменять пароль | ✓ |
|
||||
| Запрещено восстанавливать пароль | ✓ |
|
||||
| Запрет интерактивного входа | ✓ (если есть такая настройка) |
|
||||
| **Роли** | `bfd_API_Чтение` + минимальная роль для входа (`БазовыеПрава` или эквивалент в BIT.RA) |
|
||||
|
||||
## Публикация на Apache
|
||||
|
||||
```
|
||||
Конфигуратор → Администрирование → Публикация на веб-сервере...
|
||||
```
|
||||
|
||||
Настройки:
|
||||
- **Веб-сервер:** Apache 2.4.
|
||||
- **Каталог:** имя публикации, например `bitra` (будет частью URL).
|
||||
- **HTTP-сервисы:** галочкой включить `bfd_IntegrationAPI`.
|
||||
- **Публиковать HTTP-сервисы по умолчанию** (если есть такая опция) — можно не включать, поскольку отметили вручную.
|
||||
|
||||
После Save — Apache перезапустить.
|
||||
|
||||
**Итоговый URL базовый:**
|
||||
```
|
||||
http://<host>/<publication-name>/hs/bfd-api/v1/
|
||||
```
|
||||
|
||||
Пример (если host=server.local, publication=bitra):
|
||||
```
|
||||
http://server.local/bitra/hs/bfd-api/v1/health
|
||||
```
|
||||
|
||||
## Тестирование через curl
|
||||
|
||||
```bash
|
||||
# Health (Basic auth обязательна для HTTP-сервисов 1С)
|
||||
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/health
|
||||
|
||||
# Сотрудники
|
||||
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/employees | head -c 500
|
||||
|
||||
# Работы за вчера
|
||||
curl -u bfd_api_user:<PASSWORD> "http://server.local/bitra/hs/bfd-api/v1/works?modified_since=$(date -d 'yesterday' '+%Y-%m-%d')" | head -c 500
|
||||
|
||||
# Проекты
|
||||
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/projects | head -c 1000
|
||||
|
||||
# Виды работ (быстро проверить что аутентификация работает + перечисление читается)
|
||||
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/work_types
|
||||
|
||||
# Справочники одним запросом
|
||||
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/dictionaries | head -c 2000
|
||||
|
||||
# История подразделений
|
||||
curl -u bfd_api_user:<PASSWORD> "http://server.local/bitra/hs/bfd-api/v1/dept_history?modified_since=2024-01-01" | head -c 500
|
||||
```
|
||||
|
||||
Если что-то возвращает HTTP 500 с `{"error":"..."}` — посмотреть журнал регистрации 1С (там полный стек ошибки запроса).
|
||||
|
||||
## Что передать команде N8N
|
||||
|
||||
После публикации:
|
||||
- `BITRA_BASE_URL` — `http://<host>/<publication>/hs/bfd-api/v1`
|
||||
- `BITRA_USER` — `bfd_api_user`
|
||||
- `BITRA_PASSWORD` — пароль
|
||||
|
||||
Эти значения попадают в `.env` проекта `bit-flight-deck` (на стороне сервера WSL).
|
||||
|
||||
## Список эндпоинтов (полная карта)
|
||||
|
||||
| Метод | URL | Назначение | Параметры |
|
||||
|---|---|---|---|
|
||||
| GET | `/v1/health` | Healthcheck | — |
|
||||
| GET | `/v1/employees` | `Справочник.Пользователи` | — |
|
||||
| GET | `/v1/works` | `Документ.Работы` + ТЧ | `modified_since`, `limit` (по умолчанию 1000, max 10000) |
|
||||
| GET | `/v1/projects` | `Справочник.Проекты` | — |
|
||||
| GET | `/v1/stages` | `Справочник.ЭтапыПроектов` | — |
|
||||
| GET | `/v1/work_types` | `Перечисление.ВидыРабот` (14 значений) | — |
|
||||
| GET | `/v1/dictionaries` | Офисы/Подразделения/Менеджеры/Конфигурации/Договоры/Сценарии — одним JSON | — |
|
||||
| GET | `/v1/dept_history` | `РегистрСведений.ПодразделениеСотрудников` | `modified_since` |
|
||||
| GET | `/v1/project_register` | `РегНак.ОборотыПроектныхПоказателей_v2` (для MVP-3) | `modified_since` |
|
||||
| GET | `/v1/eva_mapping/projects` | `РегистрСведений.СоответствиеПроектовEVA_РА` (best-effort из мёртвой интеграции) | — |
|
||||
| GET | `/v1/eva_mapping/clients` | `РегистрСведений.СоответствиеКонтрагентовEVA_РА` | — |
|
||||
|
||||
## Формат ответа
|
||||
|
||||
Всегда JSON, UTF-8 (без BOM), массив объектов или объект-обёртка. Все ID объектов — строковый UUID без префикса класса (8-4-4-4-12 hex).
|
||||
|
||||
### Пример ответа `/v1/employees`
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "f5631644-1948-11ee-94f0-c578ab9a5932",
|
||||
"full_name": "Иванов Иван",
|
||||
"email": "iivanov@1cbit.ru",
|
||||
"eva_id": "CmfPerson:abc...",
|
||||
"office": "ЕКБ",
|
||||
"department": "Группа РП №2",
|
||||
"rate": 1500.00,
|
||||
"is_active": true,
|
||||
"should_fill_report": true
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Пример ответа `/v1/works`
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"id": "...",
|
||||
"number": "WK-000123",
|
||||
"date": "2026-05-13T00:00:00",
|
||||
"employee_id": "...",
|
||||
"department": "Группа РП №2",
|
||||
"office": "ЕКБ",
|
||||
"approved": true,
|
||||
"total_hours": 8.0,
|
||||
"comment": "",
|
||||
"rows": [
|
||||
{
|
||||
"row_index": 1,
|
||||
"description": "Разработка отчёта по продажам",
|
||||
"hours": 4.0,
|
||||
"work_type": "ЛУРВ (платно)",
|
||||
"work_type_code": "ЛУРВ",
|
||||
"client_id": "...",
|
||||
"client_name": "ООО Ромашка",
|
||||
"manager_id": "...",
|
||||
"project_id": "...",
|
||||
"stage_id": "...",
|
||||
"request_number": "RQ-555",
|
||||
"lt_id": "",
|
||||
"work_done": true
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### Ошибки
|
||||
|
||||
При исключении внутри обработчика возвращается:
|
||||
```json
|
||||
{ "error": "Описание ошибки 1С" }
|
||||
```
|
||||
с HTTP-кодом 500.
|
||||
|
||||
## Безопасность и эксплуатация
|
||||
|
||||
- Все эндпоинты требуют Basic Auth (пользователь `bfd_api_user`).
|
||||
- Модуль `bfd_IntegrationAPIHelpers` имеет `Привилегированный=Истина` — внутри его методов игнорируются ограничения RLS. **Это намеренно** — аналитический слой должен видеть всё.
|
||||
- Пароль `bfd_api_user` хранить в `.env` проекта (не в git).
|
||||
- Доступ к публикации Apache желательно ограничить firewall'ом до IP-адреса сервера N8N.
|
||||
- Под нагрузкой полл происходит каждые 30 минут (`/works`, `/projects`), раз в сутки (справочники). Это минимальная нагрузка на BIT.RA.
|
||||
- Для MVP-1 не нужны: `/v1/project_register`, `/v1/eva_mapping/*` — они подключатся в MVP-3 / при оживлении EVA-РА интеграции.
|
||||
|
||||
## Файлы кода
|
||||
|
||||
- [CommonModules/bfd_IntegrationAPIHelpers/Module.bsl](CommonModules/bfd_IntegrationAPIHelpers/Module.bsl) — модуль помощников (вставить целиком в одноимённый общий модуль).
|
||||
- [HTTPServices/bfd_IntegrationAPI/Module.bsl](HTTPServices/bfd_IntegrationAPI/Module.bsl) — модуль HTTP-сервиса (вставить целиком в «Модуль» HTTP-сервиса).
|
||||
|
||||
## Связанные документы
|
||||
|
||||
- Спецификация: [`../docs/superpowers/specs/2026-05-13-mvp1-workload-design.md`](../docs/superpowers/specs/2026-05-13-mvp1-workload-design.md)
|
||||
- План: [`../docs/superpowers/plans/2026-05-13-mvp1-workload.md`](../docs/superpowers/plans/2026-05-13-mvp1-workload.md) — Phase 3
|
||||
- Северная звезда: [`../docs/superpowers/PROJECT_GOAL.md`](../docs/superpowers/PROJECT_GOAL.md)
|
||||
Reference in New Issue
Block a user