phase-3: BIT_FlightDeck CFE — HTTPService bfd_IntegrationAPI with 11 GET endpoints

- Configuration.xml: AddOn extension, prefix bfd_, compat Version8_3_14
- CommonModule bfd_IntegrationAPIHelpers (server, privileged): JSON response, ISO date parse, UUID, email extractor, limit parser
- HTTPService bfd_IntegrationAPI (rootURL=bfd-api): 11 templates
  - /v1/health, /v1/employees, /v1/works (+modified_since, +limit)
  - /v1/projects, /v1/stages, /v1/work_types, /v1/dictionaries
  - /v1/dept_history (+modified_since), /v1/project_register (+modified_since for MVP-3)
  - /v1/eva_mapping/projects, /v1/eva_mapping/clients (best-effort from BIT.RA registers)
- README with step-by-step instructions for 1C developer
- cfe-validate: 0 errors / 0 warnings (13/13 checks)
This commit is contained in:
Roman Chesnokov
2026-05-14 19:27:15 +05:00
parent c0e2856412
commit 7da9d9dae1
8 changed files with 1268 additions and 0 deletions
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<MetaDataObject xmlns="http://v8.1c.ru/8.3/MDClasses" xmlns:app="http://v8.1c.ru/8.2/managed-application/core" xmlns:cfg="http://v8.1c.ru/8.1/data/enterprise/current-config" xmlns:cmi="http://v8.1c.ru/8.2/managed-application/cmi" xmlns:ent="http://v8.1c.ru/8.1/data/enterprise" xmlns:lf="http://v8.1c.ru/8.2/managed-application/logform" xmlns:style="http://v8.1c.ru/8.1/data/ui/style" xmlns:sys="http://v8.1c.ru/8.1/data/ui/fonts/system" xmlns:v8="http://v8.1c.ru/8.1/data/core" xmlns:v8ui="http://v8.1c.ru/8.1/data/ui" xmlns:web="http://v8.1c.ru/8.1/data/ui/colors/web" xmlns:win="http://v8.1c.ru/8.1/data/ui/colors/windows" xmlns:xen="http://v8.1c.ru/8.3/xcf/enums" xmlns:xpr="http://v8.1c.ru/8.3/xcf/predef" xmlns:xr="http://v8.1c.ru/8.3/xcf/readable" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="2.17">
<CommonModule uuid="823879b6-d9f5-44d3-8dfd-2f17a757d9a6">
<Properties>
<Name>bfd_IntegrationAPIHelpers</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>BFD: API helpers</v8:content>
</v8:item>
</Synonym>
<Comment/>
<Global>false</Global>
<ClientManagedApplication>false</ClientManagedApplication>
<Server>true</Server>
<ExternalConnection>false</ExternalConnection>
<ClientOrdinaryApplication>false</ClientOrdinaryApplication>
<ServerCall>true</ServerCall>
<Privileged>false</Privileged>
<ReturnValuesReuse>DontUse</ReturnValuesReuse>
</Properties>
</CommonModule>
</MetaDataObject>
@@ -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);
КонецФункции
#КонецОбласти