Initial commit MKD fixes

This commit is contained in:
Arsen
2026-02-04 00:17:04 +05:00
commit de94ad707b
312 changed files with 138754 additions and 0 deletions

View File

@@ -0,0 +1,224 @@
# Архитектура автоматизации воронки развития
## Обзор
Воронка развития должна автоматически управлять жизненным циклом потенциальных объектов от момента обнаружения до передачи в управление.
## Источники данных для воронки
### 1. Ручное добавление (реализовано)
- Менеджер вручную добавляет объект через форму
- Статус: `analysis` (по умолчанию)
### 2. Импорт из файлов (CSV/Excel)
- Массовый импорт объектов из внешних источников
- Парсинг адресов, характеристик, контактов
- Автоматическое создание записей в воронке
### 3. Интеграция с внешними API
- Парсинг публичных реестров (Росреестр, ГИС ЖКХ)
- Мониторинг сайтов конкурентов
- Интеграция с CRM системами
### 4. Автоматическое обнаружение
- Анализ отзывов жильцов (модуль PR)
- Выявление недовольства текущей УК
- Мониторинг социальных сетей и форумов
## Автоматическое перемещение по стадиям
### Стадии воронки:
1. **analysis** (Анализ) - начальная стадия
2. **negotiation** (Переговоры) - активная работа с собственниками
3. **preparation** (Подготовка) - подготовка к ОСС
4. **voting** (Голосование) - идет ОСС
5. **transfer** (Передача) - объект передан в управление
### Правила автоматического перехода:
#### 1. Analysis → Negotiation
**Триггеры:**
- Probability >= 30% (автоматически)
- Создана маркетинговая активность для объекта
- Проведена первая встреча с активистами
**Условия:**
- Минимум 3 контакта с собственниками
- Наличие хотя бы 1 активиста
#### 2. Negotiation → Preparation
**Триггеры:**
- Probability >= 60%
- Собрано >= 5 активистов
- Проведено >= 3 встреч
**Условия:**
- Технический аудит завершен
- Подготовлен проект договора управления
#### 3. Preparation → Voting
**Триггеры:**
- Создано ОСС для объекта (автоматически)
- Probability >= 70%
**Условия:**
- ОСС создано и запланировано
- Подготовлены все документы
#### 4. Voting → Transfer
**Триггеры:**
- ОСС завершено успешно (quorum > 50%, решение "за")
- Probability >= 90%
**Условия:**
- ОСС статус = 'completed'
- Результат голосования положительный
#### 5. Откат стадий
**Negotiation → Analysis:**
- Нет активности > 30 дней
- Probability < 20%
**Preparation → Negotiation:**
- ОСС отменено
- Probability < 50%
## Автоматический расчет probability
### Формула расчета:
```
probability = baseProbability +
(activistsCount * 5) +
(meetingsHeld * 3) +
(adsDistributed / 10) +
(auditScore * 0.2) -
(daysInactive * 0.5)
```
Где:
- `baseProbability` = 20% (для новых объектов)
- `activistsCount` = количество активистов (макс 10)
- `meetingsHeld` = количество встреч (макс 10)
- `adsDistributed` = количество розданных листовок (макс 500)
- `auditScore` = оценка технического аудита (0-100)
- `daysInactive` = дней без активности (макс 60)
### Обновление probability:
- При каждом изменении маркетинговых метрик
- При завершении технического аудита
- Ежедневно (cron job) для пересчета по времени
## События и триггеры
### 1. Создание ОСС
```sql
-- При создании ОСС автоматически:
-- 1. Обновить статус pipeline на 'voting'
-- 2. Связать ОСС с pipeline объектом
-- 3. Отправить уведомление менеджеру
```
### 2. Завершение ОСС
```sql
-- При завершении ОСС:
-- 1. Если quorum > 50% и решение "за":
-- - Обновить статус pipeline на 'transfer'
-- - Создать запись в buildings (если еще нет)
-- - Связать building_id с pipeline
-- 2. Если ОСС провалено:
-- - Обновить probability (-20%)
-- - Вернуть статус на 'preparation' или 'negotiation'
```
### 3. Завершение технического аудита
```sql
-- При завершении аудита:
-- 1. Обновить probability на основе wear_percent и projected_margin
-- 2. Если wear_percent > 70%:
-- - Уменьшить probability на 15%
-- 3. Если projected_margin < 10%:
-- - Уменьшить probability на 10%
```
### 4. Обновление маркетинговых метрик
```sql
-- При обновлении метрик:
-- 1. Пересчитать probability
-- 2. Проверить условия для перехода на следующую стадию
-- 3. Автоматически перевести, если условия выполнены
```
## Deadlines и напоминания
### Автоматические напоминания:
- **7 дней в стадии** - напомнить менеджеру о необходимости действий
- **30 дней без активности** - предупреждение о риске потери объекта
- **Deadline ОСС** - напоминание за 3 дня до окончания
- **Просроченные задачи** - ежедневные уведомления
## Интеграция с другими модулями
### Связь с модулями:
1. **ОСС модуль** - автоматическое создание ОСС при переходе в 'preparation'
2. **Аудит модуль** - автоматическое создание задачи на аудит при 'negotiation'
3. **Маркетинг модуль** - автоматическое создание активности при добавлении объекта
4. **Buildings модуль** - автоматическое создание здания при 'transfer'
## Реализация
### 1. Таблица для истории переходов
```sql
CREATE TABLE development_pipeline_history (
id BIGSERIAL PRIMARY KEY,
pipeline_id VARCHAR(50) REFERENCES development_pipeline(id) ON DELETE CASCADE,
from_status VARCHAR(20),
to_status VARCHAR(20),
reason TEXT,
triggered_by VARCHAR(50), -- 'auto' | 'manual' | user_id
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 2. Таблица для правил автоматизации
```sql
CREATE TABLE development_automation_rules (
id BIGSERIAL PRIMARY KEY,
rule_name VARCHAR(100) NOT NULL,
from_status VARCHAR(20),
to_status VARCHAR(20),
conditions JSONB, -- условия для срабатывания
enabled BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
```
### 3. Cron задачи (Node.js)
- Ежедневная проверка условий для автоматических переходов
- Пересчет probability для всех объектов
- Проверка deadlines и отправка уведомлений
### 4. Триггеры в БД
- Автоматическое обновление статуса при создании ОСС
- Автоматическое обновление при завершении ОСС
- Логирование всех изменений статуса
## Примеры workflow
### Сценарий 1: Успешный объект
1. Объект добавлен вручную → `analysis` (probability: 20%)
2. Проведена встреча, найдены активисты → probability: 35% → `negotiation`
3. Проведено 3 встречи, 5 активистов → probability: 65% → `preparation`
4. Создан технический аудит → probability: 70%
5. Создано ОСС → автоматически `voting`
6. ОСС завершено успешно → автоматически `transfer` + создан building
### Сценарий 2: Потеря объекта
1. Объект в `negotiation` 45 дней без активности
2. Автоматическое снижение probability до 15%
3. Автоматический откат в `analysis`
4. Уведомление менеджеру о риске потери
### Сценарий 3: Импорт из файла
1. Загружен CSV с 50 объектами
2. Автоматическое создание записей в `analysis`
3. Автоматическое создание маркетинговых активностей
4. Распределение по менеджерам (round-robin или по районам)

209
docs/DOMA_AI_INTEGRATION.md Executable file
View File

@@ -0,0 +1,209 @@
# Интеграция с Doma AI API
Этот документ описывает интеграцию приложения с Doma AI для автоматической синхронизации заявок.
## Обзор
Модуль заявок (`ApplicationsModule`) интегрирован с Doma AI через GraphQL API. Все заявки автоматически загружаются из Doma AI и отображаются в разделе "Заявки".
## Настройка
### 1. Получение доступа к Doma AI
**Для продакшена:**
1. Получите доступ к вашему продакшн инстансу Doma AI
2. Убедитесь, что у вас есть учетные данные для API
3. Получите URL API вашего инстанса (обычно: `https://your-domain.doma.ai/admin/api`)
**Для тестирования (опционально):**
1. Перейдите на [тестовый стенд Doma AI](https://condo.d.doma.ai/)
2. Зарегистрируйтесь, указав email и пароль
3. Создайте новую организацию в системе
### 2. Настройка переменных окружения
Создайте файл `.env.local` в корне проекта и добавьте:
```env
# URL API Doma AI (PRODUCTION)
# ВАЖНО: Укажите URL вашего продакшн инстанса Doma AI
# Пример: https://your-domain.doma.ai/admin/api
VITE_DOMA_AI_API_URL=https://your-domain.doma.ai/admin/api
# Учетные данные (используйте email ИЛИ телефон)
# Для продакшена используйте учетные данные вашей организации
VITE_DOMA_AI_EMAIL=your-email@example.com
VITE_DOMA_AI_PASSWORD=your-password
# ИЛИ используйте телефон:
# VITE_DOMA_AI_PHONE=+79991234567
# VITE_DOMA_AI_PASSWORD=your-password
```
**Для продакшена:**
- Укажите URL вашего инстанса Doma AI (не тестовый стенд)
- Используйте учетные данные вашей организации в Doma AI
- Убедитесь, что URL доступен из вашего окружения
### 3. Авторизация
При первом открытии раздела "Заявки" приложение автоматически:
- Проверяет наличие сохраненного токена
- Если токена нет, выполняет авторизацию с использованием учетных данных из переменных окружения
- Сохраняет токен в localStorage для последующих запросов
## Архитектура
### Компоненты
1. **`domaGraphQLClient.ts`** - GraphQL клиент для работы с Doma AI API
- Управление авторизацией (токены)
- Выполнение GraphQL запросов и мутаций
- Обработка ошибок
2. **`domaService.ts`** - Сервис для работы с заявками
- Инициализация подключения
- Получение заявок из Doma AI
- Преобразование данных из формата Doma AI в формат приложения
- Fallback на бэкенд или мок-данные при ошибках
3. **`ApplicationsModule.tsx`** - UI компонент модуля заявок
- Отображение заявок
- Инициализация подключения при загрузке
## Работа с API
### Получение заявок
```typescript
import { domaService } from './services/domaService';
// Инициализация (выполняется автоматически при первом использовании)
await domaService.initialize();
// Получение всех заявок
const applications = await domaService.getApplications();
// Получение заявок с фильтрами
const applications = await domaService.getApplications({
status: 'new',
limit: 50
});
```
### Авторизация вручную
```typescript
import { domaGraphQLClient } from './services/domaGraphQLClient';
// Через email
await domaGraphQLClient.authenticate('email@example.com', 'password');
// Через телефон
await domaGraphQLClient.authenticateWithPhone('+79991234567', 'password');
```
## Маппинг данных
Данные из Doma AI автоматически преобразуются в формат приложения:
| Doma AI | Приложение |
|---------|------------|
| `ticket.id` | `application.id` |
| `ticket.number` | `application.number` |
| `ticket.status.type` | `application.status` (new/in_progress/done/canceled) |
| `ticket.description` | `application.description` |
| `ticket.property.address` | `application.address` |
| `ticket.property.unitName` | `application.apartment` |
| `ticket.client.name` | `application.clientName` |
| `ticket.assignee.name` | `application.performer.name` |
| `ticket.createdAt` | `application.createdAt` |
| `ticket.deadline` | `application.deadlineAt` |
## Обработка ошибок
Сервис имеет многоуровневую систему fallback:
1. **Doma AI API** - основной источник данных
2. **Backend API** - если Doma AI недоступен
3. **Мок-данные** - если оба источника недоступны
Это обеспечивает работу приложения даже при проблемах с подключением.
## Отладка
Для отладки интеграции откройте консоль браузера. Сервис выводит подробные логи:
- `[domaService]` - логи сервиса заявок
- `[DomaGraphQLClient]` - логи GraphQL клиента
### Типичные проблемы
1. **Ошибка авторизации**
- Проверьте правильность email/телефона и пароля
- Убедитесь, что учетная запись создана в Doma AI
2. **Заявки не загружаются**
- Проверьте URL API в переменных окружения
- Убедитесь, что токен сохранен (проверьте localStorage)
- Проверьте консоль на наличие ошибок GraphQL
3. **Неправильные данные**
- Возможно, схема GraphQL в Doma AI отличается от ожидаемой
- Проверьте структуру ответа в Network вкладке DevTools
- Может потребоваться обновление запросов в `domaGraphQLClient.ts`
## Документация Doma AI
- [Общая документация](https://developers.doma.ai/ru/docs/index)
- [API Playground](https://condo.d.doma.ai/admin/api)
- [Примеры использования](https://developers.doma.ai/ru/docs/api/examples)
## Расширение функциональности
### Добавление новых запросов
Для добавления новых запросов к Doma AI:
1. Добавьте метод в `domaGraphQLClient.ts`:
```typescript
async getCustomData(): Promise<CustomData> {
const query = `
query GetCustomData {
customData {
id
name
}
}
`;
const response = await this.request<{ customData: CustomData }>(query);
return response.data?.customData;
}
```
2. Используйте его в `domaService.ts`:
```typescript
async getCustomData(): Promise<CustomData> {
await this.initialize();
return await domaGraphQLClient.getCustomData();
}
```
### Обновление статуса заявки
Для реализации обновления статуса заявки в Doma AI:
1. Изучите схему GraphQL в API Playground
2. Создайте мутацию в `domaGraphQLClient.ts`
3. Реализуйте метод в `domaService.ts`
## Безопасность
⚠️ **Важно**: Не коммитьте файлы `.env.local` с реальными учетными данными в репозиторий!
- Используйте `.env.example` для примера конфигурации
- Добавьте `.env.local` в `.gitignore`
- Для продакшена используйте переменные окружения сервера

244
docs/PIPELINE_AUTOMATION_FLOW.md Executable file
View File

@@ -0,0 +1,244 @@
# Схема автоматизации воронки развития
## Обзор потока данных
```
┌─────────────────────────────────────────────────────────────────┐
│ ИСТОЧНИКИ ДАННЫХ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ Ручное │ │ Импорт │ │ Интеграция │ │
│ │ добавление │ │ из файлов │ │ с API │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ └──────────────────┼──────────────────┘ │
│ │ │
│ ┌───────▼────────┐ │
│ │ ВОРОНКА │ │
│ │ (analysis) │ │
│ └───────┬────────┘ │
└────────────────────────────┼────────────────────────────────────┘
│ Автоматические переходы
┌────────────────────┼────────────────────┐
│ │ │
┌───────▼────────┐ ┌────────▼────────┐ ┌────────▼────────┐
│ NEGOTIATION │ │ PREPARATION │ │ VOTING │
│ │ │ │ │ │
│ Триггеры: │ │ Триггеры: │ │ Триггеры: │
│ • probability │ │ • probability │ │ • ОСС создано │
│ >= 30% │ │ >= 60% │ │ (автоматически│
│ • активисты │ │ • аудит готов │ │ через триггер│
│ • встречи │ │ • документы │ │ БД) │
└───────┬────────┘ └────────┬───────┘ └────────┬────────┘
│ │ │
│ │ │
└────────────────────┼────────────────────┘
┌────────▼────────┐
│ TRANSFER │
│ │
│ Триггеры: │
│ • ОСС успешно │
│ (кворум > 50%)│
│ • probability │
│ >= 90% │
└────────┬────────┘
┌────────▼────────┐
│ Создание │
│ Building │
│ (автоматически)│
└─────────────────┘
```
## Детальная схема автоматических переходов
### 1. Analysis → Negotiation
**Условия:**
- `probability >= 30%` ИЛИ
- Создана маркетинговая активность с `activists_count >= 1` ИЛИ
- Проведена первая встреча (`meetings_held >= 1`)
**Действия:**
- Автоматический переход статуса
- Создание маркетинговой активности (если еще нет)
- Уведомление менеджеру
**Код:**
```javascript
// В pipelineAutomation.js
if (pipeline.status === 'analysis' &&
(probability >= 30 || marketing.activists_count >= 1 || marketing.meetings_held >= 1)) {
await transitionStatus(pipeline.id, 'analysis', 'negotiation', 'Автоматический переход: условия выполнены', 'auto');
}
```
### 2. Negotiation → Preparation
**Условия:**
- `probability >= 60%` И
- `activists_count >= 5` И
- `meetings_held >= 3` И
- Технический аудит завершен (`development_audits` существует)
**Действия:**
- Автоматический переход статуса
- Создание задачи на подготовку ОСС
- Уведомление менеджеру
### 3. Preparation → Voting
**Условия:**
- Создано ОСС со статусом `active` (триггер БД)
- `probability >= 70%`
**Действия:**
- **Триггер БД** автоматически переводит в `voting`
- Логирование в `development_pipeline_history`
- Уведомление менеджеру
**SQL Триггер:**
```sql
-- Автоматически срабатывает при создании ОСС
CREATE TRIGGER trigger_auto_transition_to_voting
AFTER INSERT OR UPDATE ON development_oss_sessions
WHEN (NEW.status = 'active')
EXECUTE FUNCTION auto_transition_to_voting();
```
### 4. Voting → Transfer
**Условия:**
- ОСС завершено (`status = 'completed'`)
- Кворум > 50%
- Результат голосования положительный
**Действия:**
- **Триггер БД** автоматически переводит в `transfer`
- Создание записи в `buildings` (если еще нет)
- Связывание `building_id` с pipeline объектом
- Уведомление менеджеру и директору
**SQL Триггер:**
```sql
-- Автоматически срабатывает при завершении ОСС
CREATE TRIGGER trigger_auto_handle_oss_completion
AFTER UPDATE ON development_oss_sessions
WHEN (NEW.status = 'completed' AND OLD.status != 'completed')
EXECUTE FUNCTION auto_handle_oss_completion();
```
## Автоматический расчет probability
### Формула:
```
probability = 20 (базовая) +
(activists_count * 5, макс 50) +
(meetings_held * 3, макс 30) +
(ads_distributed / 10, макс 20) +
(audit_score * 0.2, макс 20) -
(days_inactive * 0.5, макс 30)
```
### Когда пересчитывается:
1. **При обновлении маркетинговых метрик** (PUT /api/development/marketing/:id/metrics)
2. **При создании/обновлении аудита** (POST/PUT /api/development/audits)
3. **Ежедневно** (cron задача в 9:00)
4. **Вручную** (POST /api/development/pipeline/:id/recalculate)
## Источники попадания в воронку
### 1. Ручное добавление ✅ (реализовано)
- Форма в UI
- Статус: `analysis`
- Probability: 20%
### 2. Импорт из файлов (TODO)
```javascript
// POST /api/development/pipeline/import
// Принимает CSV/Excel с колонками:
// - address, type, floors, area, apartments, manager
// Автоматически создает записи в analysis
```
### 3. Интеграция с PR модулем (TODO)
```javascript
// При обнаружении негативных отзывов о текущей УК:
// - Автоматически создается pipeline объект
// - Статус: analysis
// - Создается маркетинговая активность
```
### 4. Мониторинг внешних источников (TODO)
- Парсинг сайтов конкурентов
- Мониторинг ГИС ЖКХ
- Интеграция с CRM
## Откаты стадий
### Negotiation → Analysis
**Условия:**
- Нет активности > 30 дней
- `probability < 20%`
### Preparation → Negotiation
**Условия:**
- ОСС отменено
- `probability < 50%`
### Voting → Preparation
**Условия:**
- ОСС провалено (кворум < 50%)
- Автоматически через триггер БД
## Уведомления
### Типы уведомлений:
1. **Переход на следующую стадию** - менеджеру объекта
2. **Откат стадии** - менеджеру + руководителю отдела
3. **Риск потери объекта** (30 дней без активности) - менеджеру + директору
4. **ОСС завершено успешно** - менеджеру + директору + финансисту
5. **Объект передан в управление** - всем заинтересованным
## API Endpoints для автоматизации
### Ручной запуск проверки
```
POST /api/development/pipeline/auto-check
```
Запускает ежедневную проверку всех объектов вручную.
### Пересчет probability
```
POST /api/development/pipeline/:id/recalculate
```
Пересчитывает probability для конкретного объекта и проверяет условия для перехода.
## Мониторинг и логирование
Все автоматические переходы логируются в `development_pipeline_history`:
- `from_status` - предыдущий статус
- `to_status` - новый статус
- `reason` - причина перехода
- `triggered_by` - 'auto' | 'manual' | user_id
- `created_at` - время перехода
## Примеры workflow
### Успешный сценарий:
1. Объект добавлен → `analysis` (20%)
2. Встреча + 2 активиста → probability: 35% → `negotiation`
3. 3 встречи + 5 активистов + аудит → probability: 68% → `preparation`
4. Создано ОСС → автоматически `voting` (триггер БД)
5. ОСС успешно (кворум 55%) → автоматически `transfer` (триггер БД)
6. Создан Building → объект в управлении
### Неудачный сценарий:
1. Объект в `voting` (ОСС идет)
2. ОСС провалено (кворум 45%) → автоматически `preparation` (триггер БД)
3. Probability снижен на 20%
4. Уведомление менеджеру о необходимости дополнительной работы

View File

@@ -0,0 +1,104 @@
# Отчёт №1 — Роли и ограничения (для согласования с заказчиком)
Документ описывает роли портала МКД, их ограничения по разделам и данным, связь с базой данных и соотношение с ролями workflow согласования счетов. После согласования используется как основа для отчёта №2 и переработки логики прав.
---
## 1. Список ролей портала (после переработки)
Рекомендуется сохранить текущие коды ролей в БД. Ниже — код, название для UI и краткое описание назначения.
| Код (БД) | Название для UI | Описание назначения |
|------------|-----------------|----------------------|
| DIRECTOR | Директор | Полный доступ ко всем разделам портала. Руководство, контроль показателей, настройки. |
| ENGINEER | Гл. Инженер | Производство, участки, заявки, офис, развитие. Оперативное управление технической и производственной частью. |
| MASTER | Мастер | Участки и заявки. Работа в рамках назначенных участков (дома, заявки, штат участка). |
| LAWYER | Юрист | Сводка, юр. отдел, участки, заявки. Работа с договорами, судами, досудебной работой, долгами. |
| FINANCIER | Финансист | Сводка, финансы, офис, участки. Реестр счетов, календарь оплат, отчёты, закупки. |
| HR_MANAGER | HR-менеджер | Сводка, кадры, офис. Сотрудники, календарь, вакансии, подбор, охрана труда. |
| PR_MANAGER | PR-менеджер | Сводка, PR и NPS, заявки. SMM, мероприятия, обратная связь, отчёты, NPS, негатив. |
---
## 2. Ограничения по каждой роли
### 2.1 Разделы по умолчанию
При пустом поле `permissions` у пользователя применяются разделы по роли (из таблицы ниже). «all» означает доступ ко всем разделам портала.
| Роль | Разделы по умолчанию |
|------------|------------------------|
| DIRECTOR | all (все разделы) |
| ENGINEER | dashboard, objects, requests, office, development |
| MASTER | objects, requests |
| LAWYER | dashboard, legal, objects, requests |
| FINANCIER | dashboard, finance, office, objects |
| HR_MANAGER | dashboard, hr, office |
| PR_MANAGER | dashboard, pr, requests |
Соответствие кодов разделов и названий: dashboard — Сводка; objects — Участки; requests — Заявки; pr — PR и NPS; finance — Финансы; legal — Юр. отдел; development — Развитие; hr — Кадры; office — Офис; admin — Панель управления.
### 2.2 Уровень доступа по умолчанию
По умолчанию для роли по всем доступным разделам и подразделам задаётся уровень **«редактирование»** (edit): пользователь может просматривать и изменять данные в рамках своих разделов. Различие «только чтение» / «только своё» задаётся только через кастомные `permissions` у пользователя или через шаблоны прав.
### 2.3 Ограничение по данным (участки)
| Роль | Scope по умолчанию | Пояснение |
|------------|--------------------|------------|
| DIRECTOR | all | Все участки |
| ENGINEER | all | Все участки |
| MASTER | own_district | Только свои назначенные участки (из employee_districts / assigned_district_id) |
| LAWYER | all | Все участки |
| FINANCIER | all | Все участки |
| HR_MANAGER | all | Все участки |
| PR_MANAGER | all | Все участки |
При `scope = own_district` список разрешённых участков для фильтрации данных (дома, заявки и т.д.) формируется из таблицы **employee_districts** (многие ко многим); при отсутствии записей — из поля **employees.assigned_district_id**.
### 2.4 Особые ограничения
- **Счета на оплату:** в API поддерживается параметр `scope=own` — показ только счетов, созданных текущим пользователем (`created_by = employee_id`). Это отдельно от роли портала и может использоваться для любой роли при необходимости.
- **Workflow согласования счетов** задаётся таблицей **user_roles** (manager, finance_manager, director и т.д.) и не дублирует роли портала (см. раздел 4).
При необходимости в будущем можно ввести особые ограничения вида «только свои заявки» через уровень доступа «только своё» (own) по подразделу и проверки на бэкенде.
---
## 3. Связь с базой данных
### 3.1 portal_users
- **role** (VARCHAR) — код роли портала (DIRECTOR, ENGINEER, MASTER, LAWYER, FINANCIER, HR_MANAGER, PR_MANAGER).
- **permissions** (JSONB) — массив строк прав. Если массив не пустой — используются только эти права (разделы/подразделы и уровни); иначе применяются права по роли из единого справочника (ROLE_ACCESS и уровень edit по умолчанию).
- **scope** (VARCHAR: 'all' | 'own_district') — ограничение по участкам: все участки или только назначенные сотруднику.
При пустом `permissions` бэкенд и фронт вычисляют доступ по полю `role` и единому справочнику разделов/уровней по умолчанию.
### 3.2 employees и участки
- **employees.assigned_district_id** (VARCHAR, один участок) — для обратной совместимости; используется как fallback, если у сотрудника нет записей в employee_districts.
- **employee_districts** (таблица связей employee_id, district_id) — многие ко многим: один сотрудник может быть назначен на несколько участков.
При **scope = own_district** список доступных district_id для фильтрации данных должен формироваться так: сначала выборка из **employee_districts** по employee_id пользователя; если записей нет — использовать **assigned_district_id** (один участок). Все API, которые фильтруют данные по участку (дома, при необходимости заявки, отчёты и т.д.), должны использовать этот единый список.
### 3.3 permission_templates
- Шаблоны хранят: name, description, **permissions** (JSONB), **scope**, for_position, suggested_role. Применение шаблона к пользователю подставляет permissions и scope (и при необходимости suggested_role) в portal_users. Структура БД менять не требуется; при переработке важно сохранять тот же формат permissions и scope.
### 3.4 Новые поля или таблицы
- Текущей модели достаточно. Матрица «роль → разделы/уровень/scope» по умолчанию может храниться в едином модуле констант в коде (общий для фронта и бэкенда). При желании в будущем можно вынести матрицу в таблицу (например, role_default_access) для настройки без деплоя.
---
## 4. user_roles (workflow) и роли портала
- **user_roles** — таблица для цепочки согласования счетов на оплату. Роли: manager, finance_manager, financier, finance_director, director, top_management. Используются только в модуле согласования счетов (paymentInvoiceWorkflow и связанные API).
- **Роли портала** (portal_users.role) определяют доступ к разделам и данным портала (меню, вкладки, фильтр по участкам). Они не задают права согласования счетов.
- Связывать user_roles с portal_users.role не обязательно: один и тот же человек может быть DIRECTOR в портале и не иметь роли в user_roles, или иметь роль manager/finance_manager в user_roles при другой роли в портале.
- В админке целесообразно показывать «роль в портале» и «роли в workflow» отдельно (например, в карточке пользователя — раздел «Согласование счетов» с назначением ролей из user_roles).
---
После согласования данного отчёта переходим к отчёту №2: большая таблица доступов (раздел × подраздел × роль), рекомендуемые шаблоны прав и пошаговый план переписывания логики прав с чек-листом файлов.

View File

@@ -0,0 +1,204 @@
# Отчёт №2 — Таблица доступов и план переписывания прав
Документ подготовлен после согласования отчёта №1. Содержит: большую таблицу доступов (раздел × подраздел × роль), рекомендуемые шаблоны прав, пошаговый план переписывания логики прав и чек-лист файлов для правок.
---
## 1. Большая таблица доступов
Уровни: **—** нет доступа, **Ч** чтение, **Р** редактирование, **С** только своё. По умолчанию для роли везде, где раздел доступен, используется **Р** (редактирование).
### 1.1 Сводка (dashboard)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|------------------------|----------|----------|--------|--------|-----------|------------|------------|
| Стратегические цели | Р | Р | — | Р | Р | Р | Р |
| Панель «Производство» | Р | Р | Р¹ | Р | Р | Р | Р |
| Панель «PR и клиентский опыт» | Р | Р | — | Р | Р | Р | Р |
| Панель «Финансы» | Р | Р | — | Р | Р | Р | Р |
| Панель «Развитие» | Р | Р | — | Р | Р | Р | Р |
| Панель «Юр. отдел» | Р | Р | — | Р | Р | Р | Р |
| Панель «Кадры» | Р | Р | — | Р | Р | Р | Р |
| Мои задачи | Р | Р | — | Р | Р | Р | Р |
| Новости компании | Р | Р | — | Р | Р | Р | Р |
### 1.2 Участки (objects)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|---------------|----------|----------|--------|--------|-----------|------------|------------|
| Обзор участков| Р | Р | Р* | Р | Р | — | — |
| Дома | Р | Р | Р* | Р | Р | — | — |
| Штат участка | Р | Р | Р* | Р | Р | — | — |
\* MASTER: по умолчанию scope = только свой участок (см. п. 1.10).
### 1.3 Заявки (requests)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|-------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | Р | Р* | Р | — | — | Р |
| Реестр заявок | Р | Р | Р* | Р | — | — | Р |
| Контроль | Р | Р | Р* | Р | — | — | Р |
| Качество | Р | Р | Р* | Р | — | — | Р |
| Привязки | Р | Р | Р* | Р | — | — | Р |
| Отключения | Р | Р | Р* | Р | — | — | Р |
### 1.4 PR и NPS (pr)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|----------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | — | — | — | — | — | Р |
| SMM | Р | — | — | — | — | — | Р |
| Мероприятия | Р | — | Р¹ | — | — | — | Р |
| Обратная связь | Р | — | — | — | — | — | Р |
| Отчёты | Р | — | — | — | — | — | Р |
| Фото работ | Р | — | Р¹ | — | — | — | Р |
| NPS | Р | — | — | — | — | — | Р |
| Негатив | Р | — | — | — | — | — | Р |
### 1.5 Финансы (finance)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|-----------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | — | — | — | Р | — | — |
| Реестр счетов | Р | — | С¹ | — | Р | — | — |
| Календарь оплат | Р | — | Р¹ | — | Р | — | — |
| Отчёты | Р | — | — | — | Р | — | — |
### 1.6 Юр. отдел (legal)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|---------------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | — | — | Р | — | — | — |
| Взыскание долгов | Р | — | — | Р | — | — | — |
| Договоры | Р | — | — | Р | — | — | — |
| Судебные дела | Р | — | — | Р | — | — | — |
| Досудебная работа | Р | — | — | Р | — | Р | — |
| Соответствие | Р | — | — | Р | — | — | — |
### 1.7 Развитие (development)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|---------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | Р | — | — | — | — | — |
| Реестр объектов | Р | Р | — | — | — | — | — |
| ОСС | Р | Р | — | — | — | — | — |
| Техаудит | Р | Р | — | — | — | — | — |
| Маркетинг | Р | Р | — | — | — | — | — |
### 1.8 Кадры (hr)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|--------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | — | — | — | — | Р | — |
| Сотрудники | Р | — | — | — | — | Р | — |
| Календарь | Р | — | — | — | — | Р | — |
| Вакансии | Р | — | — | — | — | Р | — |
| Подбор | Р | — | — | — | — | Р | — |
| Охрана труда | Р | — | — | — | — | Р | — |
### 1.9 Офис (office)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|------------|----------|----------|--------|--------|-----------|------------|------------|
| Сводка | Р | Р | — | — | Р | Р | — |
| Закупки | Р | Р | Р¹ | — | Р | Р | — |
| Документы | Р | Р | — | — | Р | Р | — |
| Объекты | Р | Р | — | — | Р | Р | — |
| Ремонты | Р | Р | Р¹ | — | Р | Р | — |
| База знаний| Р | Р | — | — | Р | Р | — |
| Встречи | Р | Р | — | — | Р | Р | — |
| Новости | Р | Р | — | — | Р | Р | — |
### 1.10 Панель управления (admin)
| Подраздел | DIRECTOR | ENGINEER | MASTER | LAWYER | FINANCIER | HR_MANAGER | PR_MANAGER |
|----------------------|----------|----------|--------|--------|-----------|------------|------------|
| Все подразделы | Р | — | — | — | — | — | — |
Только роль DIRECTOR по умолчанию имеет доступ к разделу admin. Остальные роли — нет доступа (при необходимости доступ задаётся кастомными permissions).
### 1.11 Ограничение по участкам
| Роль | Ограничение по участкам |
|------------|---------------------------|
| DIRECTOR | Все участки |
| ENGINEER | Все участки |
| MASTER | Только свой участок (свои назначенные участки) |
| LAWYER | Все участки |
| FINANCIER | Все участки |
| HR_MANAGER | Все участки |
| PR_MANAGER | Все участки |
Список «своих» участков формируется из employee_districts с fallback на employees.assigned_district_id.
¹ MASTER: доступ по шаблону «Мастер участка (расширенный)» — при назначении шаблона подставляются permissions (в т.ч. dashboard_production, finance_invoices:own, finance_calendar, office_supply, office_repair, pr_events, pr_photos), scope = own_district. Реестр счетов для мастера — уровень «только своё» (С): видны только счета, созданные им (scope=own в API).
---
## 2. Шаблоны прав
Рекомендуемые шаблоны для быстрого назначения прав пользователям. Каждый шаблон задаёт permissions (массив ключей раздел/подраздел:уровень), scope и suggested_role.
| Название шаблона | Разделы и подразделы (уровень) | Рекомендуемая роль | Scope | Назначение |
|--------------------|--------------------------------|--------------------|--------------|------------|
| Мастер участка | objects (все подразделы), requests (все подразделы) | MASTER | own_district | Мастер: участки и заявки только по своим участкам. |
| Мастер участка (расширенный) | objects, requests, dashboard_production, finance_invoices:own, finance_calendar, office_supply, office_repair, pr_events, pr_photos | MASTER | own_district | Мастер: участки, заявки, сводка (производство), реестр счетов (только свои), календарь оплат, закупки ТМЦ, ремонт техники, мероприятия и фото отчёты по своим домам. |
| Гл. инженер | dashboard, objects, requests, office, development (все подразделы — edit) | ENGINEER | all | Производство, участки, заявки, офис, развитие. |
| Начальник PR | dashboard, pr, requests (все подразделы — edit) | PR_MANAGER | all | PR и NPS, заявки, сводка. |
| Финансист | dashboard, finance, office, objects (все подразделы — edit) | FINANCIER | all | Финансы, офис, участки (для отчётности). |
| HR-менеджер | dashboard, hr, office (все подразделы — edit) | HR_MANAGER | all | Кадры, офис. |
| Юрист | dashboard, legal, objects, requests (все подразделы — edit) | LAWYER | all | Юр. отдел, участки, заявки. |
| Только чтение (сводка и участки) | dashboard:read, objects:read (подразделы — read) | ENGINEER или MASTER | all / own_district | Просмотр без редактирования. |
В интерфейсе админки шаблоны отображаются как справочник: при создании/редактировании пользователя можно применить шаблон — подставляются permissions, scope и при необходимости suggested_role. Таблица доступов (раздел × подраздел) остаётся единой; шаблон — это готовый набор строк этой таблицы для выбранной роли и scope.
---
## 3. План переписывания прав
### 3.1 Единый источник прав
- Создать один модуль констант: коды ролей, названия ролей (ROLE_NAMES), список разделов (SECTION_IDS / ALL_SECTION_KEYS), матрица «роль → разделы по умолчанию» (ROLE_ACCESS), матрица «роль → scope по умолчанию» (например ROLE_DEFAULT_SCOPE).
- Варианты реализации: (1) общий файл в репозитории, импортируемый и на фронте (TypeScript), и на бэкенде (через require/импорт JS-версии или общий JSON); (2) генерация из одного JSON/YAML в constants для фронта и в константы для Node. Цель: убрать дублирование ROLE_ACCESS, ROLE_NAMES, SECTION_IDS в App.tsx, Navigation.tsx, server.js, UsersSection.tsx, PermissionsSection.tsx.
- В constants/permissions.ts оставить SECTION_LABELS, SECTION_SUBS, функции canAccessSub, getPermissionLevel, allowedSubsForSection и т.д.; роли и ROLE_ACCESS импортировать из единого источника.
### 3.2 Изменения в БД
- Миграции при необходимости: структура portal_users (role, permissions, scope) и permission_templates не меняется. При scope=own_district во всех API, где идёт фильтр по участку, единообразно использовать список district_id из employee_districts с fallback на assigned_district_id (см. п. 3.4).
### 3.3 Фронт
- Заменить все проверки «по роли» на единые функции из общего модуля прав с учётом permissions и scope. Логика уже опирается на allowedSections и userPermissions из /auth/me; нужно убрать локальные константы и импортировать ROLE_ACCESS, ROLE_NAMES (и при необходимости SECTION_IDS) из единого источника.
- Компоненты, использующие allowedSubsForSection, canAccessSub, useSubPermission, оставить без изменения логики; источник констант — общий.
### 3.4 Бэкенд
- Все API, отдающие данные в зависимости от прав: использовать единую функцию вычисления allowedSections (и при необходимости уровней по подразделам) из констант. Для фильтра по участку при scope=own_district — единая функция: получить разрешённые district_id из employee_districts по employee_id пользователя; если пусто — взять assigned_district_id. Применять этот список в запросах (buildings — фильтр домов по district_id IN (...), при необходимости заявки, отчёты и т.д.).
- В backend/routes/buildings.js заменить текущую логику (только assigned_district_id) на получение списка участков через employee_districts + fallback и фильтр WHERE (data->>'districtId') = ANY($1).
### 3.5 Админка
- Создание/редактирование пользователей и шаблонов — сохранение в текущем формате (role, permissions, scope); при необходимости расширить под полную матрицу или подсказки по шаблонам. ROLE_NAMES брать из единого источника.
---
## 4. Чек-лист файлов для правок
| Файл | Что менять |
|------|------------|
| **Единый источник (новый файл)** | Создать модуль с ролями, ROLE_NAMES, ROLE_ACCESS, SECTION_IDS, ROLE_DEFAULT_SCOPE. Вариант: `constants/roleAccess.ts` (фронт) + использование на бэкенде через копию или общий пакет. |
| constants/permissions.ts | Импорт SECTION_IDS/ALL_SECTION_KEYS и ролей из единого источника при необходимости; оставить SECTION_LABELS, SECTION_SUBS, все функции. |
| types.ts | UserRole синхронизировать с единым источником (экспорт типа из источника или дублировать список). |
| App.tsx | Удалить локальные ROLE_ACCESS, ROLE_NAMES; импорт из единого модуля. |
| components/Navigation.tsx | Удалить ROLE_ACCESS; импорт из единого модуля. |
| components/admin/UsersSection.tsx | Удалить ROLE_NAMES; импорт из единого модуля. |
| components/admin/PermissionsSection.tsx | Удалить ROLE_NAMES; импорт из единого модуля. |
| backend/server.js | Удалить локальные ROLE_ACCESS, SECTION_IDS; подключать константы из общего модуля (например shared/roleAccess.js или сгенерированный файл). Функцию allowedSectionsFromPermissions оставить, секции брать из констант. |
| backend/routes/buildings.js | При scope=own_district: получать список district_id из employee_districts по employee_id пользователя; при пустом — [assigned_district_id]. Фильтр домов: WHERE (data->>'districtId') = ANY($1::text[]). |
| Другие API в server.js | Найти все места фильтра по участку или проверки раздела; подключить единую функцию получения списка участков для scope=own_district. |
| Миграции | При появлении новых полей или таблиц ролей по умолчанию — добавить миграции. Текущий план миграций не требует. |
---
После выполнения пунктов чек-листа логика прав будет приведена к единому источнику, дубли убраны, фильтр по участкам для scope=own_district будет учитывать все назначенные участки (employee_districts).