Files
public/directive_common/TECHNICAL.md

12 KiB
Raw Blame History

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 слоя директив). Это демо-данные, не базовые справочники.