diff --git a/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers.xml b/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers.xml
new file mode 100644
index 0000000..448dd97
--- /dev/null
+++ b/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ bfd_IntegrationAPIHelpers
+
+
+ ru
+ BFD: API helpers
+
+
+
+ false
+ false
+ true
+ false
+ false
+ true
+ false
+ DontUse
+
+
+
diff --git a/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers/Ext/Module.bsl b/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers/Ext/Module.bsl
new file mode 100644
index 0000000..c13f4fd
--- /dev/null
+++ b/bitra-cfe/CommonModules/bfd_IntegrationAPIHelpers/Ext/Module.bsl
@@ -0,0 +1,130 @@
+////////////////////////////////////////////////////////////////////////////////
+// bfd_IntegrationAPIHelpers
+//
+// Назначение: вспомогательные процедуры/функции для bfd_IntegrationAPI
+// (read-only REST API для проекта bit-flight-deck).
+//
+// Context: Server, Privileged.
+////////////////////////////////////////////////////////////////////////////////
+
+#Область ПрограммныйИнтерфейс
+
+// Формирует 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);
+
+КонецФункции
+
+#КонецОбласти
diff --git a/bitra-cfe/Configuration.xml b/bitra-cfe/Configuration.xml
new file mode 100644
index 0000000..3410e74
--- /dev/null
+++ b/bitra-cfe/Configuration.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+ 9cd510cd-abfc-11d4-9434-004095e12fc7
+ 617fa0c0-9a19-4f15-bebd-1858bb176245
+
+
+ 9fcd25a0-4822-11d4-9414-008048da11f9
+ 12af5411-d422-4db1-aebd-095700acce53
+
+
+ e3687481-0a87-462c-a166-9f34594f9bba
+ 2911c4d3-5fce-4d58-bcbe-b656f73272a9
+
+
+ 9de14907-ec23-4a07-96f0-85521cb6b53b
+ f3a0b9ca-ad92-40f7-a543-1728863ba9a5
+
+
+ 51f2d5d8-ea4d-4064-8892-82951750031e
+ e371da3e-ae95-4395-b867-456aaadbe66c
+
+
+ e68182ea-4237-4383-967f-90c1e3370bc7
+ b7783cb4-7160-4c0b-a329-f7679c9f9bed
+
+
+ fb282519-d103-4dd3-bc12-cb271d631dfc
+ bf401d2b-0f97-4ebe-b962-ef712d65db37
+
+
+
+ Adopted
+ BIT_FlightDeck
+
+
+ ru
+ BIT Flight Deck Read-Only API
+
+
+
+ AddOn
+ true
+ bfd_
+ Version8_3_14
+ ManagedApplication
+
+ PlatformApplication
+
+ Russian
+
+ Role.bfd_ОсновнаяРоль
+
+ bit-flight-deck
+ 1.0.0.1
+ Language.Русский
+
+
+
+
+
+ TaxiEnableVersion8_2
+
+
+ Русский
+ bfd_ОсновнаяРоль
+ bfd_IntegrationAPIHelpers
+ bfd_IntegrationAPI
+
+
+
\ No newline at end of file
diff --git a/bitra-cfe/HTTPServices/bfd_IntegrationAPI.xml b/bitra-cfe/HTTPServices/bfd_IntegrationAPI.xml
new file mode 100644
index 0000000..4027626
--- /dev/null
+++ b/bitra-cfe/HTTPServices/bfd_IntegrationAPI.xml
@@ -0,0 +1,317 @@
+
+
+
+
+ bfd_IntegrationAPI
+
+
+ ru
+ BFD: Integration API
+
+
+
+ bfd-api
+ DontUse
+ 20
+
+
+
+
+ Dictionaries
+
+
+ ru
+ Dictionaries
+
+
+ /v1/dictionaries
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ DictionariesGet
+
+
+
+
+
+
+ EvaMappingClients
+
+
+ ru
+ Eva mapping clients
+
+
+ /v1/eva_mapping/clients
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ EvaMappingClientsGet
+
+
+
+
+
+
+ Stages
+
+
+ ru
+ Stages
+
+
+ /v1/stages
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ StagesGet
+
+
+
+
+
+
+ Projects
+
+
+ ru
+ Projects
+
+
+ /v1/projects
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ ProjectsGet
+
+
+
+
+
+
+ DeptHistory
+
+
+ ru
+ Dept history
+
+
+ /v1/dept_history
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ DeptHistoryGet
+
+
+
+
+
+
+ WorkTypes
+
+
+ ru
+ Work types
+
+
+ /v1/work_types
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ WorkTypesGet
+
+
+
+
+
+
+ EvaMappingProjects
+
+
+ ru
+ Eva mapping projects
+
+
+ /v1/eva_mapping/projects
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ EvaMappingProjectsGet
+
+
+
+
+
+
+ Employees
+
+
+ ru
+ Employees
+
+
+ /v1/employees
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ EmployeesGet
+
+
+
+
+
+
+ ProjectRegister
+
+
+ ru
+ Project register
+
+
+ /v1/project_register
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ ProjectRegisterGet
+
+
+
+
+
+
+ Health
+
+
+ ru
+ Health
+
+
+ /v1/health
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ HealthGet
+
+
+
+
+
+
+ Works
+
+
+ ru
+ Works
+
+
+ /v1/works
+
+
+
+
+ Get
+
+
+ ru
+ Get
+
+
+ GET
+ WorksGet
+
+
+
+
+
+
+
diff --git a/bitra-cfe/HTTPServices/bfd_IntegrationAPI/Ext/Module.bsl b/bitra-cfe/HTTPServices/bfd_IntegrationAPI/Ext/Module.bsl
new file mode 100644
index 0000000..aeeb005
--- /dev/null
+++ b/bitra-cfe/HTTPServices/bfd_IntegrationAPI/Ext/Module.bsl
@@ -0,0 +1,477 @@
+////////////////////////////////////////////////////////////////////////////////
+// HTTPService bfd_IntegrationAPI — read-only REST API для bit-flight-deck.
+//
+// Корневой URL после публикации: http:////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=&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?modified_since=
+//
+Функция 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 — значения Enum.ВидыРабот.
+//
+Функция 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=
+//
+Функция 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=
+// Для 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.СформироватьОтветОшибки(ОписаниеОшибки());
+ КонецПопытки;
+
+КонецФункции
+
+#КонецОбласти
diff --git a/bitra-cfe/Languages/Русский.xml b/bitra-cfe/Languages/Русский.xml
new file mode 100644
index 0000000..626f509
--- /dev/null
+++ b/bitra-cfe/Languages/Русский.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ Adopted
+ Русский
+
+ eb0c1f80-0194-40b4-8447-89fd07487e25
+ ru
+
+
+
\ No newline at end of file
diff --git a/bitra-cfe/README.md b/bitra-cfe/README.md
new file mode 100644
index 0000000..14fa174
--- /dev/null
+++ b/bitra-cfe/README.md
@@ -0,0 +1,225 @@
+# BIT_FlightDeck — расширение BIT.RA с REST API для bit-flight-deck
+
+Расширение конфигурации (CFE) для **BIT.RA** (1С:Предприятие 8.3, режим совместимости `Version8_3_14`). Добавляет HTTP-сервис read-only для аналитического слоя проекта `bit-flight-deck`. Не изменяет существующие объекты конфигурации (никаких заимствований).
+
+## Состав расширения
+
+```
+bitra-cfe/
+├── Configuration.xml Заголовок расширения, Purpose=AddOn, Prefix=bfd_
+├── Languages/
+│ └── Русский.xml Язык расширения
+├── Roles/
+│ └── bfd_ОсновнаяРоль/ ⚠ Дефолтная роль из cfe-init — НУЖНО НАСТРОИТЬ ПРАВА (см. ниже)
+├── CommonModules/
+│ └── bfd_IntegrationAPIHelpers/
+│ └── Ext/Module.bsl Хелперы (JSON-ответ, парс даты, UUID, ИзвлечьEmail, ПарсЛимит)
+└── HTTPServices/
+ └── bfd_IntegrationAPI/
+ └── Ext/Module.bsl 11 обработчиков GET-эндпоинтов
+```
+
+## Что нужно сделать 1С-разработчику (по шагам)
+
+### 1. Импорт расширения в Конфигуратор
+
+1. Открыть Конфигуратор BIT.RA.
+2. **Конфигурация → Расширения конфигурации → Добавить** (или клавиша Insert на дереве расширений).
+3. Указать имя расширения `BIT_FlightDeck`, назначение `Дополнение`, префикс `bfd_`.
+4. В созданном расширении: **Конфигурация → Загрузить конфигурацию из файлов...** — указать каталог `bitra-cfe/`.
+5. Проверить дерево объектов: должны быть `bfd_IntegrationAPI` (HTTP-сервис), `bfd_IntegrationAPIHelpers` (общий модуль), `bfd_ОсновнаяРоль` (роль).
+6. **Обновить конфигурацию базы данных** (F7).
+
+### 2. Настройка роли `bfd_ОсновнаяРоль`
+
+Дефолтная роль из шаблона **пустая**. Нужно добавить права на чтение объектов, к которым обращается API:
+
+| Объект | Право |
+|---|---|
+| `Справочник.Пользователи` | Чтение, Просмотр |
+| `Справочник.Подразделение` | Чтение, Просмотр |
+| `Справочник.Офис` | Чтение, Просмотр |
+| `Справочник.Менеджеры` | Чтение, Просмотр |
+| `Справочник.Клиенты` | Чтение, Просмотр |
+| `Справочник.Проекты` | Чтение, Просмотр |
+| `Справочник.ЭтапыПроектов` | Чтение, Просмотр |
+| `Справочник.Конфигурации` | Чтение, Просмотр |
+| `Справочник.Договоры` | Чтение, Просмотр |
+| `Справочник.СценарииПланирования` | Чтение, Просмотр |
+| `Документ.Работы` | Чтение, Просмотр |
+| `РегистрСведений.ПодразделениеСотрудников` | Чтение |
+| `РегистрНакопления.ОборотыПроектныхПоказателей_v2` | Чтение |
+| `РегистрСведений.СоответствиеПроектовEVA_РА` | Чтение |
+| `РегистрСведений.СоответствиеКонтрагентовEVA_РА` | Чтение |
+| `Перечисление.ВидыРабот` | (доступно по умолчанию) |
+| `HTTPСервис.bfd_IntegrationAPI` | Использование |
+| `ОбщийМодуль.bfd_IntegrationAPIHelpers` | Использование |
+
+Также **в свойстве расширения**: **«Активные роли» / «Назначаемые роли»** → отметить `bfd_ОсновнаяРоль`.
+
+### 3. Создание сервисного пользователя
+
+В режиме **Предприятие** (или в Конфигураторе → Администрирование → Пользователи):
+1. Создать пользователя `bfd_api_user`.
+2. Пароль — сгенерировать криптостойкий (~16 символов), сохранить отдельно для передачи в `.env` файла N8N.
+3. Аутентификация: **Аутентификация 1С:Предприятия** (для Basic auth по HTTP).
+4. Запретить интерактивный вход (галка «Запрещено изменять пароль», без членства в группах с интерактивными правами).
+5. Назначить роли:
+ - `bfd_ОсновнаяРоль` (из расширения).
+ - Также понадобятся базовые БСП-роли: `БазовыеПрава` (или эквивалент в BIT.RA), чтобы пользователь мог войти в сеанс.
+
+### 4. Публикация HTTP-сервиса через Apache
+
+```
+В Конфигураторе: Администрирование → Публикация на веб-сервере...
+```
+
+В диалоге:
+- **Web-сервер:** Apache 2.4 (выбрать установленный).
+- **Каталог:** имя публикации (например `bitra`) — будет частью URL.
+- **Поставить галку:** «Публиковать HTTP-сервисы расширений конфигурации по умолчанию».
+- В списке `HTTP-сервисы` отметить `bfd_IntegrationAPI`.
+- ОК → перезапуск Apache.
+
+URL сервиса после публикации: `http:////hs/bfd-api/v1/...`
+
+Пример (полный URL):
+```
+http://server.local/bitra/hs/bfd-api/v1/health
+http://server.local/bitra/hs/bfd-api/v1/employees
+http://server.local/bitra/hs/bfd-api/v1/works?modified_since=2026-05-01
+```
+
+### 5. Тестирование через curl
+
+```bash
+# health (без auth тоже работает, но Basic auth нужна для остальных)
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/health
+
+# Сотрудники
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/employees | head -c 500
+
+# Работы за вчера
+curl -u bfd_api_user: "http://server.local/bitra/hs/bfd-api/v1/works?modified_since=$(date -d 'yesterday' '+%Y-%m-%d')"
+
+# Проекты
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/projects | head -c 1000
+
+# Этапы
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/stages | head -c 500
+
+# Виды работ
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/work_types
+
+# Справочники (одним запросом)
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/dictionaries | head -c 2000
+
+# История подразделений
+curl -u bfd_api_user: "http://server.local/bitra/hs/bfd-api/v1/dept_history?modified_since=2024-01-01" | head -c 500
+
+# Маппинг EVA проектов (может быть пустым — пользователь подтвердил что интеграция мёртвая)
+curl -u bfd_api_user: http://server.local/bitra/hs/bfd-api/v1/eva_mapping/projects
+```
+
+### 6. Передать команде N8N (роль владельца проекта)
+
+После успешной публикации передать:
+- `BITRA_BASE_URL` — `http:////hs/bfd-api/v1`
+- `BITRA_USER` — `bfd_api_user`
+- `BITRA_PASSWORD` — пароль
+
+Эти значения попадают в файл `.env` проекта `bit-flight-deck`. На стороне N8N будут настроены HTTP Request ноды с Basic-auth.
+
+## Список эндпоинтов (полная карта)
+
+| Метод | URL | Назначение | Параметры |
+|---|---|---|---|
+| GET | `/v1/health` | Healthcheck | — |
+| GET | `/v1/employees` | `Справочник.Пользователи` | — |
+| GET | `/v1/works` | `Документ.Работы` + ТЧ | `modified_since`, `limit` |
+| GET | `/v1/projects` | `Справочник.Проекты` | — |
+| GET | `/v1/stages` | `Справочник.ЭтапыПроектов` | — |
+| GET | `/v1/work_types` | `Перечисление.ВидыРабот` | — |
+| GET | `/v1/dictionaries` | Офисы/Подразделения/Менеджеры/Конфигурации/Договоры/Сценарии | — |
+| GET | `/v1/dept_history` | `РегистрСведений.ПодразделениеСотрудников` | `modified_since` |
+| GET | `/v1/project_register` | `РегНак.ОборотыПроектныхПоказателей_v2` (MVP-3) | `modified_since` |
+| GET | `/v1/eva_mapping/projects` | `РегистрСведений.СоответствиеПроектовEVA_РА` | — |
+| GET | `/v1/eva_mapping/clients` | `РегистрСведений.СоответствиеКонтрагентовEVA_РА` | — |
+
+## Формат ответа
+
+Всегда JSON, UTF-8 (без BOM), массив объектов или объект-обёртка.
+
+### Пример ответа `/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`.
+- Modul `bfd_IntegrationAPIHelpers` помечен как `Privileged=true` — внутри него игнорируются ограничения RLS. Пользователь BFD_api имеет доступ ко всем строкам справочников/документов через API. **Это намеренно** — аналитический слой должен видеть всё.
+- Пароль `bfd_api_user` хранится в `.env` проекта (не в git).
+- Доступ к публикации Apache желательно ограничить firewall'ом IP-адресом N8N-сервера (или ходить через CF Tunnel/VPN внутри сети).
+
+## Связанные документы
+
+- Спецификация: [`../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
+- Решения по транспорту: см. memory `architecture_transport_decisions`
diff --git a/bitra-cfe/Roles/bfd_ОсновнаяРоль.xml b/bitra-cfe/Roles/bfd_ОсновнаяРоль.xml
new file mode 100644
index 0000000..6bab539
--- /dev/null
+++ b/bitra-cfe/Roles/bfd_ОсновнаяРоль.xml
@@ -0,0 +1,10 @@
+
+
+
+
+ bfd_ОсновнаяРоль
+
+
+
+
+
\ No newline at end of file