12 KiB
Directive Common
name: directive_common
version: 19.0.1.0.0
author: Mk.Lab
depends: directive_layer, base_user_role, sale, mail, account, account_payment, mrp, project, stock
Назначение
Модуль-компаньон подключает directive_layer к штатным моделям Odoo (project.task, sale.order, account.move, account.payment, stock.picking, mrp.workorder + mrp.production) и несёт крон-симулятор программных/машинных агентов. Служит референсом для разработчиков при подключении директив к новым моделям.
Паттерн интеграции
Каждая интегрируемая модель подключается к directive.mixin через _inherit. Автогенерация директив настраивается декларативно через directive.policy (UI «Директивы → Политики»), а не через код модели-источника: метод-заглушка _directive_generation_spec удалён, доменная фильтрация перенесена в поле directive.policy.domain_filter.
class SomeModel(models.Model):
_name = 'some.model'
_inherit = ['some.model', 'directive.mixin']
# handler завершения директивы (путь B, вызывается из directive.directive.action_done)
def action_executor_handler(self, payload: dict) -> bool:
self.message_post(body=f"Результат: {payload.get('directive_name')}")
return True
Два пути on_callback
Путь A — политика on_callback на штатном методе источника. Модель-источник
переопределяет штатный метод Odoo через super() и явно вызывает публичный
хелпер self._directive_run_callback_policies("<имя_метода>"). Это штатный
override вместо прежнего monkey-patch — никакого setattr, _register_hook
или оборачивания методов. Единственный штатный метод с этим путём —
stock.picking.button_validate (см. ниже). Для демо-задачи этот же хелпер
вызывается из собственного метода project.task.action_demo_directive_callback.
Намеренный отказ от
directive_skip_generation. Старый monkey-patch wrapper уважал контекстdirective_skip_generationи гардif result is not False. Контекст использовался только внутри самого удалённого wrapper'а и ни в одном бизнес-потоке — поэтому он не переносится. Гард «не False / не возврат визарда» сохранён только там, где он нужен — вbutton_validate.
Путь B — handler_key при action_done. directive.directive.action_done
читает handler_key из шаблона и вызывает одноимённый метод на записи-источнике
с payload. Это штатный механизм directive_layer, не monkey-patch. Через него
работают демо-handler'ы источников:
| handler_key | Модель-источник |
|---|---|
directive_on_done_post_message |
project.task |
action_wms_response |
sale.order |
action_audit_response |
account.move |
action_bank_gateway_response |
account.payment |
action_agv_callback |
stock.picking |
action_iot_signal |
mrp.workorder |
Интегрированные модели
project.task + directive.mixin
Файл: models/project_task.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_write по stage_id |
| Тип исполнителя | human |
Handler завершения (путь B) directive_on_done_post_message(payload) — публикует в чат задачи сообщение: «Директива выполнена: {directive_name}».
Демо-метод action_demo_directive_callback() (путь A) — публикует «Demo callback executed» и затем явно вызывает self._directive_run_callback_policies("action_demo_directive_callback"). Используется тестом on_callback в directive_layer. Это не no-op переименование: именно эта строка-вызов заменяет прежний monkey-patch.
account.move + directive.mixin
Файл: models/account_move.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_write по state |
| Переход | draft → posted |
| Domain | directive.policy.domain_filter, напр. [("move_type", "=", "in_invoice")] (только входящие счета) |
| Тип исполнителя | software (аудит-бот) |
Handler завершения (путь B) action_audit_response(payload) — публикует в чат счёта результат проверки с оценкой риска.
sale.order + directive.mixin
Файл: models/sale_order.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_write по state |
| Переход | draft → sale |
| Тип исполнителя | software (WMS-агент) |
Handler завершения (путь B) action_wms_response(payload) — публикует в чат заказа информацию о резервировании товара с указанием ячейки.
account.payment + directive.mixin
Файл: models/account_payment.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_write по state |
| Переход | draft → in_process |
| Domain | directive.policy.domain_filter, напр. [("payment_type", "=", "outbound")] (только исходящие) |
| Тип исполнителя | software (банковский шлюз) |
Handler завершения (путь B) action_bank_gateway_response(payload) — публикует в чат платежа банковский референс в формате REF-{id}-X99.
stock.picking + directive.mixin
Файл: models/stock_picking.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_callback на методе button_validate |
| Тип исполнителя | software (AGV-робот) |
Путь A. button_validate переопределён: res = super().button_validate(),
затем — только если res is not False и результат не является возвратом
визарда (ir.actions.act_window для backorder/immediate transfer) — вызывается
self._directive_run_callback_policies("button_validate"). Гард сохраняет
семантику прежнего wrapper'а: политика не срабатывает на промежуточном возврате
визарда, когда перемещение ещё не проведено.
Handler завершения (путь B) action_agv_callback(payload) — публикует в чат перемещения информацию о доставке в зону производства.
mrp.workorder + directive.mixin
Файл: models/mrp_workorder.py
| Параметр | Значение |
|---|---|
| Событие (политика) | on_write по state |
| Переход | ready → progress (через button_start(); в Odoo 19 состояния pending нет, дефолт — ready) |
| Domain | directive.policy.domain_filter, напр. [("name", "ilike", "резка")] |
| Тип исполнителя | machine (IoT/CNC) |
Handler завершения (путь B) action_iot_signal(payload) — публикует в чат рабочей операции телеметрию оборудования (температура шпинделя, вибрация) и обновляет заголовок производственного заказа.
mrp.production (расширение без mixin)
Файл: models/mrp_workorder.py
Добавляет метод action_choose_directive_group() в модель производственного заказа. Метод берёт первый рабочий центр из workorder_ids и открывает визард directive.choose.group.wizard с active_model='mrp.workorder'. При отсутствии рабочих центров выводит предупреждение.
Имитация программных агентов (cron)
Метод: directive.directive._cron_simulate_system_agents() (models/directive_directive.py)
Запись cron: data/cron.xml → ir_cron_simulate_system_agents
Запись ir.cron перенесена сюда из directive_layer (прежний битый
ir_cron_generate_simulation_data, указывавший на несуществующий метод,
удалён). Крон указывает на directive_layer.model_directive_directive
(модель живёт в directive_layer), по умолчанию active=False (симулятор,
не включать в проде), интервал — 1 минута. Регистрируется в манифесте через
ключ data.
Метод автоматически продвигает директивы программных и машинных исполнителей по стадиям:
| Текущая стадия | Действие |
|---|---|
planned |
_set_stage('in_progress') |
in_progress |
action_done() |
Фильтр: executor_type in ['software', 'machine'], stage_code in ['draft', 'planned', 'in_progress'].
Представления
Файл: views/project_task_views.xml
Добавляет единообразный набор элементов в формы шести объектов: project.task, sale.order, account.move, account.payment, stock.picking, mrp.production.
| Элемент | Расположение | Описание |
|---|---|---|
| Кнопка «Выбрать тип» | header |
Открывает визард выбора группы шаблонов |
| Кнопка «Директивы» | oe_button_box |
Открывает список директив объекта |
Поле directive_execution_status |
sheet |
Текущий статус исполнения (readonly) |
Для задачи (project.task) дополнительно добавляется кнопка «Demo: Callback» для ручного тестирования механизма callback.
Демо-пользователи
Файл: data/users.xml (ключ манифеста demo — грузятся только в demo-БД)
| XML ID | Логин | Имя | Бизнес-роль |
|---|---|---|---|
user_directive_executor |
directive_executor |
Directive Executor | directive_layer.role_directive_executor |
user_directive_requester |
directive_requester |
Directive Requester | directive_layer.role_directive_requester |
user_directive_dispatcher |
directive_dispatcher |
Directive Dispatcher | directive_layer.role_directive_dispatcher |
Каждому пользователю назначается base.group_user плюс бизнес-роль через
role_line_ids. Назначение роли триггерит set_groups_from_roles
(base_user_role), который через implied_ids помещает пользователя в
соответствующую техническую группу _res_groups (ACL и record rules слоя
директив). Это демо-данные, не базовые справочники.