21 KiB
Directive Layer (Слой директив)
name: directive_layer
version: 19.0.1.0.0
author: Mk.Lab
depends: base, web, base_user_role, project
Архитектура
directive_layer
├── directive.directive — основная сущность: директива
├── directive.stage — справочник стадий (7 стадий)
├── directive.work.type — справочник видов работ
├── directive.template — шаблон директивы
├── directive.template.group — группа шаблонов (пакет директив)
├── directive.template.group.line — строки группы
├── directive.origin — связка «модель + ID»
├── directive.policy — правило автогенерации директив
├── directive.mixin — AbstractModel для подключения к любой модели
├── res.users (расширение) — авто-установка домашней страницы исполнителя
└── ExecutorBoard (OWL) — рабочий стол исполнителя (fullscreen)
Модели
directive.directive — Директива
Файл: models/directive.py
| Поле | Тип | Описание |
|---|---|---|
name |
Char (required) | Название директивы |
active |
Boolean | Признак активности |
is_simulation |
Boolean | Флаг тестовой/демо-директивы |
deadline_at |
Datetime (required) | Дедлайн исполнения |
planned_hours |
Float (required) | Трудоёмкость в часах |
load_percent |
Integer (default: 10) | Процент загрузки ресурса |
executor_type |
Selection | human / machine / software |
success_probability |
Float (default: 98.0) | Вероятность успешного выполнения, % |
resource_cost |
Float | Стоимость ресурса |
retry_policy |
Selection | none / retry_3 / backoff |
priority |
Selection | 0 — низкий / 1 — нормальный / 2 — высокий |
work_type_id |
Many2one → directive.work.type (required) |
Тип работ |
executor_role_id |
Many2one → res.users.role |
Роль исполнителя |
requester_role_id |
Many2one → res.users.role |
Роль заявителя |
stage_id |
Many2one → directive.stage |
Стадия (вид диспетчера) |
stage_code |
Char (related, stored) | Технический код текущей стадии |
executor_stage_id |
Many2one → directive.stage |
Стадия (вид исполнителя) |
requester_stage_id |
Many2one → directive.stage |
Стадия (вид заявителя) |
executor_traffic_light |
Selection | Светофор исполнителя: white/green/yellow/red/black |
resource_traffic_light |
Selection | Светофор ресурса: white/green/yellow/red/black |
executor_user_id |
Many2one → res.users |
Назначенный исполнитель |
origin_id |
Many2one → directive.origin (required, cascade) |
Объект-источник |
template_id |
Many2one → directive.template |
Шаблон директивы |
policy_id |
Many2one → directive.policy |
Политика создания |
generator_key |
Char (index) | Ключ дедупликации для режима create_once |
predecessor_ids |
Many2many → directive.directive |
Директивы-предшественники |
sequence |
Integer | Порядок в планировщике |
Вычисляемые поля:
| Поле | Описание |
|---|---|
deadline_display |
Дедлайн в формате дд.мм.гг Ч:ММ с учётом часового пояса пользователя |
time_left_display |
Оставшееся время в формате Xд Yч Zм (отрицательное, если просрочено) |
is_overdue |
Boolean: дедлайн прошёл |
priority_stars |
Строка ★ / ★★ / ★★★ |
Порядок сортировки по умолчанию: deadline_at asc, priority desc, id desc
Статусы
Допустимые переходы между стадиями:
| Текущая стадия | Допустимые следующие |
|---|---|
draft |
confirmed, planned, cancelled |
confirmed |
planned, in_progress, cancelled |
planned |
in_progress, cancelled |
in_progress |
paused, done |
paused |
in_progress, cancelled |
done |
— (финальная) |
cancelled |
— (финальная) |
Переходы контролируются методом _check_stage_transition(). Недопустимый переход вызывает UserError.
Методы
| Метод | Описание |
|---|---|
action_assign_to_me() |
Назначить на текущего пользователя (из стадий confirmed / planned) |
action_start() |
Перевести в in_progress |
action_pause() |
Перевести в paused (только из in_progress) |
action_done() |
Завершить директиву и вызвать callback в записи-источнике |
action_cancel() |
Отменить (обновляет все три stage-поля одновременно) |
action_open_delete_wizard() |
Открыть визард удаления |
_set_stage(code) |
Установить стадию по коду (обновляет stage_id, executor_stage_id, requester_stage_id) |
_get_origin_record() |
Получить запись-источник через origin_id.res_model + origin_id.res_id |
_build_payload(event) |
Сформировать словарь данных для передачи в callback |
action_quick_change_stage(id, code) |
@api.model — смена стадии по вызову из JS |
Механизм callback при завершении
При вызове action_done() выполняется следующая последовательность:
- Стадия устанавливается в
doneчерез_set_stage("done") - Из шаблона директивы читается
handler_key— имя метода в модели-источнике - Через
_get_origin_record()получается запись-источник - Метод
handler_keyвызывается на записи-источнике с payload
Состав payload:
event, directive_id, directive_name, template_id, template_code, work_type_id, executor_user_id, planned_start_at, planned_end_at, stage_code, priority, load_percent
directive.stage — Стадии
Файл: models/directive_stage.py
| Поле | Тип | Описание |
|---|---|---|
name |
Char (required) | Название стадии |
code |
Char (required, unique) | Технический код |
is_hidden |
Boolean | Скрыть колонку в канбане |
Предустановленные стадии (data/directive_stage_data.xml, noupdate="1"):
| Код | Название | Скрыта в канбане |
|---|---|---|
draft |
Черновик | Нет |
confirmed |
Подтверждена | Нет |
planned |
Запланирована | Нет |
in_progress |
В работе | Нет |
paused |
На паузе | Нет |
done |
Выполнена | Да |
cancelled |
Отменена | Да |
directive.template — Шаблон директивы
Файл: models/directive_template.py
| Поле | Тип | Описание |
|---|---|---|
name |
Char (required) | Название шаблона |
code |
Char (required, unique) | Технический код |
work_type_id |
Many2one → directive.work.type |
Тип работ |
planned_hours |
Float (required) | Трудоёмкость в часах |
load_percent |
Integer | Процент загрузки ресурса |
priority |
Selection 0/1/2 | Приоритет директив по этому шаблону |
executor_role_id |
Many2one → res.users.role |
Роль исполнителя по умолчанию |
requester_role_id |
Many2one → res.users.role |
Роль заявителя по умолчанию |
default_stage_id |
Many2one → directive.stage |
Стартовая стадия (по умолчанию: draft) |
handler_key |
Char | Имя метода в модели-источнике, вызываемого при завершении директивы |
description |
Text | Описание и инструкции для исполнителя |
directive.template.group — Группа шаблонов
Файл: models/directive_template_group.py
| Поле | Тип | Описание |
|---|---|---|
name |
Char (required) | Название группы |
code |
Char | Технический код |
applicable_model |
Char | Техническое имя модели, к которой применяется группа |
template_line_ids |
One2many → directive.template.group.line |
Строки группы |
directive.template.group.line:
| Поле | Тип | Описание |
|---|---|---|
group_id |
Many2one → directive.template.group (cascade) |
Родительская группа |
sequence |
Integer | Порядок создания директив из этой строки |
template_id |
Many2one → directive.template |
Шаблон директивы |
directive.origin — Источник директив
Файл: models/directive_origin.py
Хранит хранит пару (res_model, res_id) и агрегирует все директивы для данного объекта. На каждую запись создаётся ровно один directive.origin.
| Поле | Тип | Описание |
|---|---|---|
res_model |
Char (required, index) | Техническое имя модели Odoo |
res_id |
Integer (required, index) | ID записи |
res_ref |
Reference (computed, stored) | Прямая ссылка на запись |
directive_ids |
One2many → directive.directive |
Все директивы источника |
display_name |
Char (computed, stored) | Отображаемое имя в формате res_model,res_id |
directive.policy — Политика автогенерации
Файл: models/directive_policy.py
| Поле | Тип | Описание |
|---|---|---|
name |
Char (required) | Название политики |
active |
Boolean | Политика активна |
sequence |
Integer | Порядок проверки политик |
origin_model |
Char (required) | Модель, к которой применяется политика |
event |
Selection | Тип события: manual / on_create / on_write / on_stage / on_cron / on_callback |
mode |
Selection | create_once — однократно; create_each_time — при каждом срабатывании |
use_record_template_group |
Boolean | Брать группу шаблонов из поля записи, а не из фиксированной |
template_group_id |
Many2one → directive.template.group |
Фиксированная группа шаблонов |
trigger_field |
Char | Имя поля (для on_write) или метода (для on_callback) |
trigger_value_int |
Integer | Ожидаемое значение поля (ID записи для Many2one-полей) |
Логика _policy_should_fire()
Для события on_callback — всегда возвращает True. Для остальных событий: читает значение trigger_field у записи и сравнивает с trigger_value_int. Для Many2one-полей сравнивается .id.
Логика apply_to_records()
Для каждой записи: в режиме create_once проверяет, нет ли уже директив от этой политики для данного origin_id. Если нет — определяет группу шаблонов и вызывает _directive_create_from_group().
directive.mixin — Миксин для моделей
Файл: models/directive_mixin.py
Тип: models.AbstractModel
Подключается к модели через _inherit:
class ProjectTask(models.Model):
_name = 'project.task'
_inherit = ['project.task', 'directive.mixin']
Поля, добавляемые в модель
| Поле | Тип | Описание |
|---|---|---|
directive_origin_id |
Many2one → directive.origin (readonly) |
Origin-запись данного объекта |
directive_ids |
One2many (related) | Все директивы объекта |
directive_execution_status |
Char (computed, stored) | Текстовый статус исполнения |
directive_template_group_id |
Many2one → directive.template.group |
Группа шаблонов объекта |
Методы
_ensure_directive_origin() — находит или создаёт directive.origin для записи. Вызывается при create и write.
write() — перехватывает изменения полей. Фиксирует значения до изменения, выполняет super().write(), затем проверяет политики с event='on_write' и вызывает apply_to_records() при совпадении.
_directive_register_callback_wrappers() — вызывается при старте сервера (_register_hook). Оборачивает методы модели через functools.wraps для обработки политик с event='on_callback'. Флаг _directive_wrapped предотвращает повторное оборачивание.
_compute_directive_execution_status() — формирует строку статуса на основе наиболее приоритетной активной директивы. Приоритет стадий: paused > in_progress > planned/confirmed > draft. Результат: "{stage_name}, до {nearest_deadline}, ожидается завершение к {expected_end}".
action_choose_directive_group() — открывает визард directive.choose.group.wizard.
action_open_directives() — открывает список директив, отфильтрованных по origin_id текущей записи.
_directive_create_from_group(template_group, generator_key, policy) — создаёт директивы по строкам группы в порядке sequence. Каждая директива получает параметры из шаблона, ссылку на origin и policy.
Методы для переопределения в модели-источнике
| Метод | Вызывается |
|---|---|
_directive_on_assign(self, payload) |
При назначении исполнителя |
_directive_on_start(self, payload) |
При переходе в in_progress |
_directive_on_pause(self, payload) |
При переходе в paused |
_directive_on_done(self, payload) |
При завершении директивы |
_directive_generation_spec(self) |
Возвращает список спецификаций для автогенерации (альтернатива Policy UI) |
res.users — Расширение
Файл: models/res_users.py
При создании или изменении пользователя проверяет вхождение в группу role_directive_executor_res_groups. При совпадении устанавливает action_id = action_executor_dashboard_client — рабочий стол исполнителя становится домашней страницей пользователя.
Визарды
directive.choose.group.wizard
Файл: wizard/directive_choose_group_wizard.py
| Поле | Тип | Описание |
|---|---|---|
template_group_id |
Many2one → directive.template.group (required) |
Выбранная группа шаблонов |
Метод action_confirm() берёт active_model и active_id из контекста и записывает выбранную группу в поле directive_template_group_id исходной записи. Сами директивы не создаются — только сохраняется группа.
directive.delete.wizard
Файл: wizard/directive_delete_wizard.py
| Режим | Действие |
|---|---|
archive |
Мягкое удаление: active=False |
delete |
Жёсткое удаление: unlink() |
Представления и меню
Файл: views/directive_views.xml, views/directive_policy_views.xml
Структура меню
Директивы (menu_directive_root)
├── Все директивы (menu_directive_all)
│ → action_directive_all: directive.directive (kanban/list/form)
├── Политики (menu_directive_policy_action)
│ → action_directive_policy: directive.policy (list/form)
└── Настройки (menu_directive_settings_root)
├── Источники директив → directive.origin (list/form)
├── Статусы директив → directive.stage (list/form)
├── Виды работ → directive.work.type (list/form)
├── Шаблоны директив → directive.template (list/form)
└── Группы шаблонов директив → directive.template.group (list/form)
Рабочий стол исполнителя
Зарегистрирован как ir.actions.client (action_executor_dashboard_client) с тегом directive_layer.executor_dashboard_action. Открывается в режиме fullscreen. Не является пунктом меню — устанавливается как action_id (домашняя страница) пользователям с ролью исполнителя через res.users.
ExecutorBoard (OWL-компонент)
Файлы: static/src/components/executor_board/executor_board.{js,xml,scss}
Зарегистрирован как ir.actions.client с тегом directive_layer.executor_dashboard_action, открывается в режиме fullscreen.
Логика загрузки данных:
- Определяет
userIdизsession.user_idилиstoreData - Загружает все стадии (
directive.stage) - Загружает директивы с фильтром
executor_user_id = userId - Строит колонки канбана: стадия и директивы с совпадающим
executor_stage_id
Быстрые действия на карточке директивы:
| Действие | Переход |
|---|---|
| Начать | action_quick_change_stage(id, 'in_progress') |
| Пауза | action_quick_change_stage(id, 'paused') |
| Завершить | action_quick_change_stage(id, 'done') |
После каждого действия данные перезагружаются.
Безопасность
Роли (security/roles.xml)
Каждая роль создаётся одновременно в двух системах:
res.users.role(модульbase_user_role) — для назначения через интерфейс ролейres.groups— для использования в record rules иir.model.access
| Роль | Назначение |
|---|---|
role_directive_executor |
Исполнитель: видит свои директивы, берёт в работу, завершает |
role_directive_requester |
Заявитель: создаёт и отслеживает директивы |
role_directive_dispatcher |
Диспетчер: назначает исполнителей, управляет очередью |
Record Rules (security/directives_rules.xml)
Ограничивают видимость директив по ролям пользователя.
Права доступа (security/ir.model.access.csv)
CRUD-права для всех моделей модуля, распределённые по ролям.
Тесты
Файл: tests/test_directive_layer.py
| Что покрывает |
|---|
| Создание директив через шаблоны и группы |
| Статусная машина: допустимые и недопустимые переходы |
Политики: срабатывание on_write |
Миксин на модели project.task |