# Слои показателей - Базовый модуль
**name**: mklab_base_indicators
**version**: 19.0.2025.11.17
**author**: MK.Lab
**depends:** `base`

## Архитектура

Модуль реализует структуру данных **направленного гиперграфа** для аналитики KPI. 

```
mklab_base_indicators
├── hg.node          — вершина графа (generic: res_model + res_id)
├── hg.index         — показатель (метрика), привязан к вершине
├── hg.index.code    — классификатор показателей
├── hg.value         — значение показателя (план/факт/дата/формула)
├── hg.link          — направленная связь: source_node к [target_nodes]
└── hg.hg_mixin      — AbstractModel: подключает любую модель к графу
```

---

## Модели

### `hg.node` — Вершина графа

**Файл:** `models/hg_node.py`

Вершина представляет конкретный объект Odoo в структуре гиперграфа. Хранит generic-ссылку на запись через пару `(res_model, res_id)`. При подключении модели через `hg.hg_mixin` вершина создаётся автоматически при создании записи.

| Поле | Тип | Описание |
|---|---|---|
| `name` | Char | Название вершины |
| `res_id` | Integer | ID записи в Odoo |
| `res_model` | Char | Техническое имя модели (`project.task`, `sale.order` и др.) |

#### Методы

**`goto_related()`** — возвращает действие `ir.actions.act_window` для открытия связанной записи в диалоге. Используется для навигации из графа к исходному объекту.

---

### `hg.index` — Показатель

**Файл:** `models/hg_index.py`

Показатель — именованная KPI-метрика, привязанная к конкретной вершине графа. Хранит историю значений через связанные записи `hg.value` и вычисляет актуальное значение на текущую дату.

| Поле | Тип | Описание |
|---|---|---|
| `name` | Char | Название показателя |
| `node_id` | Many2one → `hg.node` | Вершина-владелец показателя |
| `internal_code_id` | Many2one → `hg.index.code` | Внутренний классификатор |
| `external_code` | Char | Внешний код для интеграций |
| `public` | Boolean | Признак публичного показателя |
| `value_ids` | One2many → `hg.value` | История значений показателя |
| `current_value` | Float | Текущее значение на сегодня |

#### Методы

**`_compute_current_value()`** — вычисляет `current_value` на основе `value_ids`. Из всех значений отбираются те, у которых `date_due <= сегодня`. Среди отобранных берётся запись с наибольшей датой — её `value_float_actual` становится текущим значением. Если подходящих значений нет — возвращается `0`.

**`calc()`** — метод вычисления значения показателя по формуле или связанным вершинам. Вызывается явно при необходимости пересчёта.

---

### `hg.index.code` — Классификатор показателей

**Файл:** `models/hg_index_code.py`

Справочник внутренних кодов для группировки показателей. Позволяет объединять показатели разных вершин в один срез при аналитике (например, «Выручка» по всем задачам проекта).

| Поле | Тип | Описание |
|---|---|---|
| `name` | Char | Название кода |
| `index_ids` | One2many → `hg.index` | Показатели с данным кодом |

---

### `hg.value` — Значение показателя

**Файл:** `models/hg_value.py`

Хранит конкретное числовое значение (плановое и фактическое) для показателя на определённую дату. Поддерживает два режима: простое значение и вычисление по формуле.

| Поле | Тип | Описание |
|---|---|---|
| `name` | Char | Название значения |
| `index_id` | Many2one → `hg.index` | Родительский показатель |
| `date_due` | Date (required) | Дата значения |
| `value_float_plan` | Float | Плановое значение |
| `value_float_actual` | Float | Фактическое значение |
| `type` | Selection | `alone` — простое значение; `formula` — вычисляется по формуле |
| `formula` | Char | Python-выражение для вычисления (используется при `type='formula'`) |

#### Методы

**`calc()`** — вычисляет `value_float_actual` для значений с `type='formula'`. Вычисление выполняется через `safe_eval` с контекстом:

| Переменная | Значение |
|---|---|
| `node_value` | текущая запись `hg.value` |
| `node_index` | родительский показатель `hg.index` |
| `datatime` | текущая дата `fields.Date.today()` |

---

### `hg.link` — Матрица связности

**Файл:** `models/hg_link.py`

Представляет направленное гиперребро графа: один источник и множество вершин-приёмников. Используется для построения отчётов по связанным объектам и визуализации зависимостей.

| Поле | Тип | Описание |
|---|---|---|
| `name` | Char | Название строки связи |
| `source_id` | Many2one → `hg.node` | Вершина-источник |
| `target_ids` | Many2many → `hg.node` | Множество вершин-приёмников |

---

### `hg.hg_mixin` — Миксин для моделей

**Файл:** `models/hg_mixin.py`  
**Тип:** `models.AbstractModel`

Абстрактная модель, подключаемая через `_inherit` к любой модели Odoo. После подключения модель автоматически становится участником гиперграфа: при создании записи создаётся соответствующая вершина `hg.node`.

#### Подключение к модели

```python
class ProjectTask(models.Model):
    _name = 'project.task'
    _inherit = ['project.task', 'hg.hg_mixin']
```

#### Поля, добавляемые в модель

| Поле | Тип | Описание |
|---|---|---|
| `node_id` | Many2one → `hg.node` | Вершина графа для данной записи |
| `index_ids` | Many2many → `hg.index` | Показатели, привязанные к вершине записи |
| `related_ids` | Many2many → `hg.node` | Вершины-приёмники из матрицы связности |

#### Методы

**`create()`** — переопределён: после создания записи автоматически создаёт `hg.node` с `res_model` и `res_id` текущей записи, устанавливает `node_id`.

**`_compute_indexes()`** — ищет все `hg.index` с `node_id` равным вершине записи, заполняет `index_ids`.

**`_compute_related()`** — ищет все `hg.link` где `source_id` равен вершине записи, собирает все `target_ids` из найденных связей, заполняет `related_ids`.

---

## Представления и меню

**Файл:** `views/views.xml`

Создаёт корневое меню **«Слои показателей»** со следующей структурой:

```
Слои показателей
├── Граф
│   ├── Вершины графа        → hg.node (list / form)
│   └── Матрица связности    → hg.link (list / form)
└── Показатели
    ├── Показатели           → hg.index (list / form)
    ├── Значения показателей → hg.value (list / form)
    └── Внутренний код       → hg.index.code (list / form)
```

---

## Безопасность

**Файл:** `security/res_groups.xml`

Группа `group_indicators_admin` — **«Администратор Слоёв показателей»**. Полный CRUD-доступ ко всем моделям модуля.

**Файл:** `security/ir.model.access.csv`

| Модель | Группа | Права |
|---|---|---|
| `hg.node` | `group_indicators_admin` | CRUD |
| `hg.index` | `group_indicators_admin` | CRUD |
| `hg.index.code` | `group_indicators_admin` | CRUD |
| `hg.value` | `group_indicators_admin` | CRUD |
| `hg.link` | `group_indicators_admin` | CRUD |
| `hg.hg_mixin` | `group_indicators_admin` | CRUD |

---

## Тесты

**Файл:** `tests/`

| Файл | Что покрывает |
|---|---|
| `test_hg_node.py` | Создание вершины, метод `goto_related()` |
| `test_hg_index.py` | Создание показателя, вычисление `current_value` в различных сценариях |
| `test_hg_value.py` | Создание значения, вызов `calc()` для типов `alone` и `formula` |
| `test_hg_link.py` | Создание связи с вершинами-приёмниками и без них |
| `test_indicators.py` | Интеграционный тест цепочки модулей |

---