v1.9.0: Phase 0 — инфраструктура CRM/лидов, миграции БД, расширение моделей

This commit is contained in:
2026-06-04 14:38:43 +03:00
parent e29e2984da
commit f06ecaabe3
15 changed files with 3315 additions and 10 deletions
+430
View File
@@ -0,0 +1,430 @@
# Интеграция с 1С:УНФ 3.0 (1С-Фреш)
> Дата исследования: 04.06.2026
> Статус: ИССЛЕДОВАНИЕ (код не написан)
---
## 1. Краткое описание
Документ описывает доступные API для интеграции сервисного портала AegisOne с 1С:УНФ 3.0, работающей в облаке 1С-Фреш.
**Цель:** Синхронизация данных между 1С:УНФ и порталом — клиенты, заказы, контактные лица.
---
## 2. Доступные интерфейсы 1С-Фреш
| Интерфейс | Назначение | Чтение | Запись | Сложность |
|-----------|-----------|--------|--------|-----------|
| **OData** | Стандартный интерфейс платформы 1С | ✅ | ✅ | Средняя |
| **REST API** | Портал 1С:ITS Fresh-Integration | ✅ | ✅ | Средняя |
| **HTTP-сервис** | УниверсальнаяИнтеграция (UniversalIntegration) | ✅ | ✅ | Высокая |
| **Система взаимодействия** | Вебхуки (внешние → 1С) | ❌ | ✅ | Низкая |
### Главный вывод
**OData** — основной и самый доступный способ интеграции. Через OData доступны практи­чески все объекты 1С:УНФ 3.0: справочники, документы, регистры.
---
## 3. OData интерфейс
### 3.1 Формат URL
```
https://<server>/a/unf/<tenant>/odata/standard.odata/<Entity>
```
| Параметр | Описание | Пример |
|----------|----------|--------|
| `server` | Адрес сервера 1С-Фреш | `https://xxx.1cfresh.com` |
| `app` | Код приложения | `unf` (для УНФ) |
| `tenant` | Номер области данных | `34` |
| `Entity` | Имя объекта метаданных | `Catalog_Контрагенты` |
### 3.2 Аутентификация
```
Authorization: Basic <base64(login:password)>
```
Используется сервисный пользователь 1С-Фреш с правами доступа к OData.
### 3.3 Формат ответа
```
?$format=json — JSON
?$format=atom — Atom/XML (по умолчанию)
?$format=json;odata=nometadata — JSON без метаданных
```
---
## 4. Доступные сущности 1С:УНФ 3.0
### 4.1 Справочники (Catalogs)
| OData URL | Описание | Приоритет для нас |
|-----------|----------|-------------------|
| `Catalog_Контрагенты` | Покупатели, поставщики, прочие контрагенты | 🔴 Высокий → **Клиенты** |
| `Catalog_КонтактныеЛица` | Контактные лица контрагентов | 🔴 Высокий → **Контакты** |
| `Catalog_Номенклатура` | Услуги, товары, работы | 🟡 Средний → Каталог услуг |
| `Catalog_Договоры` | Договоры с контрагентами | 🟡 Средний → Привязка к клиенту |
| `Catalog_Проекты` | Проекты | 🟡 Средний → Связь с заказами |
| `Catalog_Организации` | Наши организации | 🟢 Низкий → Контекст |
| `Catalog_БанковскиеСчета` | Расчётные счета | 🟢 Низкий |
### 4.2 Документы (Documents)
| OData URL | Описание | Приоритет для нас |
|-----------|----------|-------------------|
| `Document_ЗаказКлиента` | Заказ покупателя | 🔴 Высокий → **Заказ/Лид** |
| `Document_РеализацияТоваровУслуг` | Реализация товаров и услуг | 🟡 Средний → История работ |
| `Document_АктВыполненныхРабот` | Акт выполненных работ | 🟡 Средний → Подтверждение |
| `Document_СчетНаОплату` | Счёт на оплату | 🟡 Средний → Финансы |
| `Document_ПоступлениеТоваров` | Поступление товаров | 🟢 Низкий → Закупки |
| `Document_СчетНаОплатуПоставщика` | Счёт от поставщика | 🟢 Низкий |
### 4.3 Регистры сведений
| OData URL | Описание |
|-----------|----------|
| `InformationRegister_ЦеныНоменклатурыДокументов` | Цены номенклатуры |
| `InformationRegister_КурсыВалют` | Курсы валют |
| `InformationRegister_НастройкиСистемыНалогообложения` | Учётная политика |
### 4.4 Табличные части
Доступны через суффикс имени:
```
Document_ЗаказКлиента_Товары — табличная часть «Товары»
Document_ЗаказКлиента_Услуги — табличная часть «Услуги»
Catalog_Контрагенты_КонтактнаяИнформация — контактная информация
```
---
## 5. Примеры запросов
### 5.1 Все контрагенты (покупатели)
```http
GET /odata/standard.odata/Catalog_Контрагенты?$format=json
&$select=Ref_Key,Description,ИНН,КПП,РегистрационныйНомер
&$filter=not (IsFolder)
&$orderby=Description
&$top=100
Authorization: Basic <auth>
```
### 5.2 Поиск контрагента по ИНН
```http
GET /odata/standard.odata/Catalog_Контрагенты?$format=json
&$select=Ref_Key,Description,ИНН,КПП
&$filter=(ИНН eq '2310031540')
Authorization: Basic <auth>
```
### 5.3 Поиск контрагента по наименованию
```http
GET /odata/standard.odata/Catalog_Контрагенты?$format=json
&$select=Ref_Key,Description,ИНН
&$filter=like(Description, 'Аегис%')
Authorization: Basic <auth>
```
### 5.4 Заказы конкретного клиента
```http
GET /odata/standard.odata/Document_ЗаказКлиента?$format=json
&$expand=Контрагент
&$select=Ref_Key,Number,Date,СуммаДокумента,Статус,Контрагент/Description
&$filter=Контрагент_Key eq guid'...'
&$orderby=Date desc
Authorization: Basic <auth>
```
### 5.5 Контактные лица контрагента
```http
GET /odata/standard.odata/Catalog_КонтактныеЛица?$format=json
&$select=Ref_Key,Description,Должность,Владелец_Key
&$filter=Владелец_Key eq guid'...'
Authorization: Basic <auth>
```
### 5.6 Номенклатура (услуги)
```http
GET /odata/standard.odata/Catalog_Номенклатура?$format=json
&$expand=ЕдиницаИзмерения
&$select=Ref_Key,Description,Артикул,ВидНоменклатуры
&$filter=not (IsFolder)
&$orderby=Description
Authorization: Basic <auth>
```
### 5.7 Создание нового контрагента (POST)
```http
POST /odata/standard.odata/Catalog_Контрагенты
Content-Type: application/json
Authorization: Basic <auth>
{
"Description": "ООО Ромашка",
"ИНН": "2310031540",
"КПП": "231001001",
"ЮридическоеФизическоеЛицо": "ЮридическоеЛицо"
}
```
### 5.8 Обновление контрагента (PATCH)
```http
PATCH /odata/standard.odata/Catalog_Контрагенты(guid'...')
Content-Type: application/json
Authorization: Basic <auth>
{
"Description": "ООО Ромашка (обновлено)"
}
```
---
## 6. Маппинг сущностей 1С:УНФ → Наш портал
### 6.1 Контрагенты → Клиенты
| Поле 1С:УНФ | Поле портала | Тип маппинга |
|-------------|-------------|--------------|
| `Ref_Key` | `onec_id` (UUID) | Прямой |
| `Description` | `company_name` | Прямой |
| `ИНН` | `inn` | Прямой (ключ дедупликации) |
| `КПП` | `kpp` | Прямой |
| `Телефон` | `phone` | Нормализация |
| `Email` | `email` | Прямой |
| `Адрес` | `address` | Прямой |
### 6.2 Заказ клиента → Лид/Заказ
| Поле 1С:УНФ | Поле портала | Тип маппинга |
|-------------|-------------|--------------|
| `Ref_Key` | `onec_order_id` (UUID) | Прямой |
| `Number` | `number` | Прямой |
| `Date` | `created_at` | Конвертация |
| `СуммаДокумента` | `amount` | Прямой |
| `Статус` | `status` | Маппинг статусов |
| `Контрагент_Key` | `customer_id` | FK (по onec_id) |
### 6.3 Статусы заказов
| Статус 1С:УНФ | Статус портала |
|---------------|---------------|
| В работе | `in_progress` |
| Закрыт | `completed` |
| Отменён | `cancelled` |
---
## 7. Стратегия интеграции
### Фаза 0: Настройка доступа (отдельная задача)
1. **Получить от пользователя:**
- URL базы в 1С-Фреш (типа `https://xxx.1cfresh.com/a/unf/`)
- Логин/пароль сервисного пользователя
- Номер области данных (tenant)
2. **Проверить доступность:**
- Выполнить тестовый запрос к `$metadata`
- Убедиться в наличии нужных сущностей
- Проверить права доступа
3. **Создать таблицу настроек:**
```sql
CREATE TABLE onec_settings (
id SERIAL PRIMARY KEY,
base_url VARCHAR(500) NOT NULL,
tenant VARCHAR(50) NOT NULL,
login VARCHAR(100) NOT NULL,
password_encrypted TEXT NOT NULL, -- fernet
last_sync_at TIMESTAMP,
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT NOW()
);
```
### Фаза 1: Синхронизация контрагентов → клиенты
1. **Миграция:** Таблица `onec_sync_log` для логирования
```sql
CREATE TABLE onec_sync_log (
id SERIAL PRIMARY KEY,
entity_type VARCHAR(50) NOT NULL, -- 'contragent', 'order', etc.
onec_id UUID NOT NULL,
portal_id INTEGER,
action VARCHAR(20) NOT NULL, -- 'create', 'update', 'skip'
details JSONB,
synced_at TIMESTAMP DEFAULT NOW()
);
```
2. **Задача синхронизации:**
- Читаем `Catalog_Контрагенты` из 1С
- Дедупликация по ИНН (точное совпадение)
- Если ИНН нет — по наименованию + ИНН КПП
- Создаём/обновляем в таблице `customers`
- Логируем каждое действие
3. **Периодичность:** Раз в день (ночью) или по требованию
### Фаза 2: Синхронизация заказов
1. **Миграция:** Поле `onec_order_id` в `leads` или новая таблица
2. **Задача синхронизации:**
- Читаем `Document_ЗаказКлиента` из 1С
- Привязка к клиенту по `Контрагент_Key`
- Маппинг статусов
- Логирование
### Фаза 3 (опционально): Обратная запись
- Создание заказа в портале → создание в 1С
- Требует аккуратной обработки ошибок
- **Рекомендация:** Только после стабильности фаз 1-2
---
## 8. Технические детали
### 8.1 Python-клиент для OData
Рекомендуемая библиотека: `pyodata` или `requests` (raw OData).
```python
import base64
import httpx
class OneCFreshClient:
"""Клиент для работы с 1С-Фреш через OData."""
def __init__(self, base_url: str, tenant: str, login: str, password: str):
self.base_url = base_url.rstrip('/')
self.tenant = tenant
self.auth = base64.b64encode(f"{login}:{password}".encode()).decode()
async def get_contragents(self, top: int = 100, skip: int = 0) -> list:
"""Получить список контрагентов."""
url = f"{self.base_url}/a/unf/{self.tenant}/odata/standard.odata/Catalog_Контрагенты"
params = {
"$format": "json;odata=nometadata",
"$select": "Ref_Key,Description,ИНН,КПП",
"$filter": "not (IsFolder)",
"$top": str(top),
"$skip": str(skip),
}
headers = {"Authorization": f"Basic {self.auth}"}
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params, headers=headers)
resp.raise_for_status()
return resp.json().get("value", [])
async def get_contragent_by_inn(self, inn: str) -> dict | None:
"""Найти контрагента по ИНН."""
url = f"{self.base_url}/a/unf/{self.tenant}/odata/standard.odata/Catalog_Контрагенты"
params = {
"$format": "json;odata=nometadata",
"$select": "Ref_Key,Description,ИНН,КПП",
"$filter": f"(ИНН eq '{inn}')",
}
headers = {"Authorization": f"Basic {self.auth}"}
async with httpx.AsyncClient() as client:
resp = await client.get(url, params=params, headers=headers)
resp.raise_for_status()
items = resp.json().get("value", [])
return items[0] if items else None
```
### 8.2 Ограничения
| Ограничение | Описание | Решение |
|-------------|----------|---------|
| **Multi-tenancy** | Каждая база в своей области данных | Указать tenant в URL |
| **Публикация OData** | Должна быть включена в конфигурации | Проверить через `$metadata` |
| **Права доступа** | Сервисный пользователь должен иметь права на чтение | Настроить роли в 1С |
| **Пагинация** | Большие выборки — через `$top`/`$skip` | Построчная загрузка |
| **Rate limits** | Возможны ограничения на частоту запросов | Очередь задач, retry |
### 8.3 Проверка доступности
```bash
# Тестовый запрос метаданных
curl -u "login:password" \
"https://xxx.1cfresh.com/a/unf/34/odata/standard.odata/$metadata"
# Тестовый запрос контрагентов
curl -u "login:password" \
"https://xxx.1cfresh.com/a/unf/34/odata/standard.odata/Catalog_Контрагенты?\$format=json&\$top=5"
```
---
## 9. Сравнение с webhook (Система взаимодействия)
| Критерий | OData | Webhook |
|----------|-------|---------|
| **Чтение данных** | ✅ Да | ❌ Нет (только запись) |
| **Запись данных** | ✅ Да | ✅ Да (только в 1С) |
| **Направление** | Bidirectional | One-way (внешнее → 1С) |
| **Формат** | JSON (стандартный OData) | JSON (свой формат) |
| **Аутентификация** | Basic Auth | URL + логин/пароль |
| **Сложность** | Средняя | Низкая |
| **Использование** | Синхронизация данных | Уведомления, сообщения |
**Вывод:** OData — для синхронизации данных. Webhook — для отправки уведомлений в 1С.
---
## 10. Рекомендации
### Приоритет implementation
1. **OData синхронизация контрагентов** — самый важный функционал
2. **OData синхронизация заказов** — расширение функционала
3. **Webhook уведомления** — опционально, для двусторонней связи
### Вопросы к пользователю (для фазы 0)
1. URL базы в 1С-Фреш?
2. Логин/пароль сервисного пользователя?
3. Номер области данных (tenant)?
4. Какие данные в приоритете: контрагенты, заказы, или всё сразу?
5. Направление синхронизации: только из 1С → портал, или bidirectional?
### Рекомендация
**Начать с read-only синхронизации контрагентов** — самый безопасный и быстрый способ:
- Не требует записи обратно
- Минимальные риски
- Быстро даёт результат (клиенты из 1С появляются в портале)
- Потом можно расширять до заказов и bidirectional
---
## 11. Ссылки
- [Документация 1С-Фреш: OData](https://its.1c.ru/db/fresh/content/19956692/hdoc)
- [Платформа 1С: REST интерфейс](https://v8.1c.ru/platforma/rest-interfeys/)
- [OData: правила формирования имени ресурса](https://42clouds.com/ru-ru/manuals/interfeys-odata-pravila-formirovaniya-imeni-resursa/)
- [Работа с 1С через OData (Infostart)](https://infostart.ru/1c/articles/1570140/)
- [odata1c-client (GitHub)](https://github.com/Dakword/odata1c-client) — PHP-клиент с примерами
- [Контрагенты и контактные лица в 1С:УНФ](https://unf4you.ru/publ/kontragenty_i_kontaktnye_lica/1-1-0-407)
- [Заказ покупателя в 1С:УНФ](https://estart1c.ru/unf-30/189-kak-v-1sroznice-i-1sunf-sozdat-i-ispolzovat-zakaz-pokupatelja.html)