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);
КонецФункции
#КонецОбласти
+73
View File
@@ -0,0 +1,73 @@
<?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">
<Configuration uuid="773c65cd-8145-4651-84bc-489bd245fb58">
<InternalInfo>
<xr:ContainedObject>
<xr:ClassId>9cd510cd-abfc-11d4-9434-004095e12fc7</xr:ClassId>
<xr:ObjectId>617fa0c0-9a19-4f15-bebd-1858bb176245</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>9fcd25a0-4822-11d4-9414-008048da11f9</xr:ClassId>
<xr:ObjectId>12af5411-d422-4db1-aebd-095700acce53</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>e3687481-0a87-462c-a166-9f34594f9bba</xr:ClassId>
<xr:ObjectId>2911c4d3-5fce-4d58-bcbe-b656f73272a9</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>9de14907-ec23-4a07-96f0-85521cb6b53b</xr:ClassId>
<xr:ObjectId>f3a0b9ca-ad92-40f7-a543-1728863ba9a5</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>51f2d5d8-ea4d-4064-8892-82951750031e</xr:ClassId>
<xr:ObjectId>e371da3e-ae95-4395-b867-456aaadbe66c</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>e68182ea-4237-4383-967f-90c1e3370bc7</xr:ClassId>
<xr:ObjectId>b7783cb4-7160-4c0b-a329-f7679c9f9bed</xr:ObjectId>
</xr:ContainedObject>
<xr:ContainedObject>
<xr:ClassId>fb282519-d103-4dd3-bc12-cb271d631dfc</xr:ClassId>
<xr:ObjectId>bf401d2b-0f97-4ebe-b962-ef712d65db37</xr:ObjectId>
</xr:ContainedObject>
</InternalInfo>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>BIT_FlightDeck</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>BIT Flight Deck Read-Only API</v8:content>
</v8:item>
</Synonym>
<Comment />
<ConfigurationExtensionPurpose>AddOn</ConfigurationExtensionPurpose>
<KeepMappingToExtendedConfigurationObjectsByIDs>true</KeepMappingToExtendedConfigurationObjectsByIDs>
<NamePrefix>bfd_</NamePrefix>
<ConfigurationExtensionCompatibilityMode>Version8_3_14</ConfigurationExtensionCompatibilityMode>
<DefaultRunMode>ManagedApplication</DefaultRunMode>
<UsePurposes>
<v8:Value xsi:type="app:ApplicationUsePurpose">PlatformApplication</v8:Value>
</UsePurposes>
<ScriptVariant>Russian</ScriptVariant>
<DefaultRoles>
<xr:Item xsi:type="xr:MDObjectRef">Role.bfd_ОсновнаяРоль</xr:Item>
</DefaultRoles>
<Vendor>bit-flight-deck</Vendor>
<Version>1.0.0.1</Version>
<DefaultLanguage>Language.Русский</DefaultLanguage>
<BriefInformation />
<DetailedInformation />
<Copyright />
<VendorInformationAddress />
<ConfigurationInformationAddress />
<InterfaceCompatibilityMode>TaxiEnableVersion8_2</InterfaceCompatibilityMode>
</Properties>
<ChildObjects>
<Language>Русский</Language>
<Role>bfd_ОсновнаяРоль</Role>
<CommonModule>bfd_IntegrationAPIHelpers</CommonModule>
<HTTPService>bfd_IntegrationAPI</HTTPService>
</ChildObjects>
</Configuration>
</MetaDataObject>
@@ -0,0 +1,317 @@
<?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">
<HTTPService uuid="8e09cf5b-a6fa-4c4b-98cb-82db2e162aa6">
<Properties>
<Name>bfd_IntegrationAPI</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>BFD: Integration API</v8:content>
</v8:item>
</Synonym>
<Comment/>
<RootURL>bfd-api</RootURL>
<ReuseSessions>DontUse</ReuseSessions>
<SessionMaxAge>20</SessionMaxAge>
</Properties>
<ChildObjects>
<URLTemplate uuid="12ce1726-bb0a-40ae-8aaf-0b9d591a4dd5">
<Properties>
<Name>Dictionaries</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Dictionaries</v8:content>
</v8:item>
</Synonym>
<Template>/v1/dictionaries</Template>
</Properties>
<ChildObjects>
<Method uuid="57368469-9ffa-41b1-adfc-6dbbcc85d12e">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>DictionariesGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="995b8120-dfe7-471a-9f2d-8ba97ed34449">
<Properties>
<Name>EvaMappingClients</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Eva mapping clients</v8:content>
</v8:item>
</Synonym>
<Template>/v1/eva_mapping/clients</Template>
</Properties>
<ChildObjects>
<Method uuid="7196efa0-b635-48c0-a8ca-a6460731b42a">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>EvaMappingClientsGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="6b64cfdc-924b-4222-a5d0-675fd86b6346">
<Properties>
<Name>Stages</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Stages</v8:content>
</v8:item>
</Synonym>
<Template>/v1/stages</Template>
</Properties>
<ChildObjects>
<Method uuid="840c3931-447f-4a45-b32b-5704e75d265e">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>StagesGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="8e183fe8-5bf0-43cc-9a2e-9c85adc93831">
<Properties>
<Name>Projects</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Projects</v8:content>
</v8:item>
</Synonym>
<Template>/v1/projects</Template>
</Properties>
<ChildObjects>
<Method uuid="0a57d13a-26a1-4790-a1ed-d495fdf3afa7">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>ProjectsGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="37c27edf-7dec-4ad2-b29d-652cf3aaffb8">
<Properties>
<Name>DeptHistory</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Dept history</v8:content>
</v8:item>
</Synonym>
<Template>/v1/dept_history</Template>
</Properties>
<ChildObjects>
<Method uuid="4a80df3f-e412-4a5d-8045-ed146c3fef00">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>DeptHistoryGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="b5ca9926-d37b-43c5-b378-c8f3f32dd55d">
<Properties>
<Name>WorkTypes</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Work types</v8:content>
</v8:item>
</Synonym>
<Template>/v1/work_types</Template>
</Properties>
<ChildObjects>
<Method uuid="fc85a2e2-0614-4048-9316-ff960f896454">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>WorkTypesGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="9e9b6804-e351-4d83-96fc-af420bea5039">
<Properties>
<Name>EvaMappingProjects</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Eva mapping projects</v8:content>
</v8:item>
</Synonym>
<Template>/v1/eva_mapping/projects</Template>
</Properties>
<ChildObjects>
<Method uuid="b45e6a6d-23ac-4e83-a459-f3b53a9c27a2">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>EvaMappingProjectsGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="d374a68f-b205-47d4-8f08-cf99b9286f5a">
<Properties>
<Name>Employees</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Employees</v8:content>
</v8:item>
</Synonym>
<Template>/v1/employees</Template>
</Properties>
<ChildObjects>
<Method uuid="0dc75ca9-fb55-4cdf-8df8-a51fc14fcbab">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>EmployeesGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="a9a6db31-d475-4758-80ad-72f0d019dc59">
<Properties>
<Name>ProjectRegister</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Project register</v8:content>
</v8:item>
</Synonym>
<Template>/v1/project_register</Template>
</Properties>
<ChildObjects>
<Method uuid="21d3c7a1-3fd2-4514-b620-14a4df1bcec2">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>ProjectRegisterGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="ba7af844-c2f5-4a3e-95a3-3400f183e2dd">
<Properties>
<Name>Health</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Health</v8:content>
</v8:item>
</Synonym>
<Template>/v1/health</Template>
</Properties>
<ChildObjects>
<Method uuid="6e7a8ee9-10ec-4c36-87f5-60b1fa326d0d">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>HealthGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
<URLTemplate uuid="8437da03-c34b-45a2-9305-3bc1d9532e41">
<Properties>
<Name>Works</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Works</v8:content>
</v8:item>
</Synonym>
<Template>/v1/works</Template>
</Properties>
<ChildObjects>
<Method uuid="431211f0-db1a-40ff-bc93-5382f852b9ee">
<Properties>
<Name>Get</Name>
<Synonym>
<v8:item>
<v8:lang>ru</v8:lang>
<v8:content>Get</v8:content>
</v8:item>
</Synonym>
<HTTPMethod>GET</HTTPMethod>
<Handler>WorksGet</Handler>
</Properties>
</Method>
</ChildObjects>
</URLTemplate>
</ChildObjects>
</HTTPService>
</MetaDataObject>
@@ -0,0 +1,477 @@
////////////////////////////////////////////////////////////////////////////////
// HTTPService bfd_IntegrationAPI — read-only REST API для bit-flight-deck.
//
// Корневой URL после публикации: 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?modified_since=<ISO_DATE>
//
Функция 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=<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.СформироватьОтветОшибки(ОписаниеОшибки());
КонецПопытки;
КонецФункции
#КонецОбласти
+13
View File
@@ -0,0 +1,13 @@
<?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">
<Language uuid="5bf6d57d-cea1-4792-a098-1c63b1dcac37">
<InternalInfo/>
<Properties>
<ObjectBelonging>Adopted</ObjectBelonging>
<Name>Русский</Name>
<Comment/>
<ExtendedConfigurationObject>eb0c1f80-0194-40b4-8447-89fd07487e25</ExtendedConfigurationObject>
<LanguageCode>ru</LanguageCode>
</Properties>
</Language>
</MetaDataObject>
+225
View File
@@ -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://<host>/<publication-name>/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:<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')"
# Проекты
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/stages | head -c 500
# Виды работ
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
# Маппинг EVA проектов (может быть пустым — пользователь подтвердил что интеграция мёртвая)
curl -u bfd_api_user:<PASSWORD> http://server.local/bitra/hs/bfd-api/v1/eva_mapping/projects
```
### 6. Передать команде N8N (роль владельца проекта)
После успешной публикации передать:
- `BITRA_BASE_URL``http://<host>/<publication>/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`
@@ -0,0 +1,10 @@
<?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">
<Role uuid="1d6ac63c-b3b8-4509-999f-2a6cf20b503e">
<Properties>
<Name>bfd_ОсновнаяРоль</Name>
<Synonym/>
<Comment/>
</Properties>
</Role>
</MetaDataObject>