Skip to content

Instantly share code, notes, and snippets.

@litnimax
Last active August 4, 2026 09:45
Show Gist options
  • Select an option

  • Save litnimax/1d693fa5c261cd5c63c52728879b40a5 to your computer and use it in GitHub Desktop.

Select an option

Save litnimax/1d693fa5c261cd5c63c52728879b40a5 to your computer and use it in GitHub Desktop.
Пробелы в возможностях Angee для сохранения семантики аддонов Odoo

Пробелы в возможностях Angee для сохранения семантики аддонов Odoo

Статус: исходное исследование
Дата: 2026-08-04
Базовая версия Odoo: 19.0, коммит 7d3a7fac27332f440404383f41cc113bf3face53
Базовая версия Angee: коммит ee92a36cd2f6fc2e6c44f854648acdfe344cb18e
Базовая версия arpee: коммит aed6fba6fd474e2281c6f963f1742e426eed3307
Базовая версия pilot-target: коммит 65438afd8ec6e46d305b64a4da53fbf8cffaf202

1. Итог

В Angee уже есть значительная часть основы, необходимой для бизнес-приложений, похожих на Odoo: детерминированная композиция аддонов, расширение модели в той же таблице, персистентность и ограничения Django, REBAC, типизированные GraphQL CRUD/действия, агрегация и группировка, редактируемые дочерние строки, переходы состояний, представления list/form/board/calendar, ресурсы, realtime и Celery.

Поэтому главный риск — не отсутствие CRUD, а потеря поведения, которое Odoo реализует между персистентностью, черновиками форм и UI на основе метаданных. Первыми инвестициями во фреймворк должны стать:

  1. нативный для целевой платформы контракт вычисляемых значений и зависимостей;
  2. серверный контракт вычисления черновика для поведения onchange;
  3. единый шлюз инвариантов/записи для сгенерированного CRUD и написанных вручную операций;
  4. более богатый контракт реляционных черновиков для строк документов;
  5. сериализуемые, компонуемые при сборке спецификации действий и представлений;
  6. аналитические представления Pivot и Chart поверх существующего слоя группировки и агрегации данных.

Это точки расширения Angee, а не среда совместимости с Odoo. Angee не должен импортировать Odoo, предоставлять Odoo Environment, эмулировать recordset'ы или добавлять декораторы с единственным контрактом «вести себя в точности как Odoo». Факты из исходников Odoo остаются в IR odoo2angee; явные маппинги выбирают возможность Angee либо фиксируют пробел.

2. Доказательная база и метод

Эта оценка объединяет:

Число лексических совпадений помогает расставить приоритеты, но не измеряет семантическое покрытие. Совпадение может находиться в тесте или вспомогательном коде, а одно вхождение может иметь больший бизнес-вес, чем множество тривиальных.

Конструкция Odoo Совпадений Файлов
@api.depends(...) 3 278 926
@api.depends_context(...) 235 135
поля с compute= 4 333 925
поля с related= 1 695 536
поля с inverse= 267 140
поля с search= 282 154
@api.onchange(...) 485 257
@api.constrains(...) 509 321
@api.ondelete(...) 159 141
<pivot> 77 67
<graph> 93 75
<kanban> 183 147
<calendar> 32 28
<search> 353 282

Также часто встречаются сквозные конструкции: mail.thread — 420 раз в 178 файлах, message_post(...) — 548 раз в 203 файлах, translate=True — 317 раз в 185 файлах, ir.attachment — 1 004 раза в 292 файлах, Command.* — 7 774 раза в 979 файлах.

Текущая эталонная реализация arpee показывает практическую цену первого пробела. Строки продаж и закупок переопределяют save(), рассчитывают сохранённые промежуточные суммы, а затем вызывают у родителя метод recompute_totals(). Для налогов M2M требуется дополнительное исправление после загрузки ресурсов. Это аккуратный прикладной код, но он повторяет протокол межмодельной инвалидации, который владелец зависимостей во фреймворке должен сделать механическим.

3. Важные семантические различия

3.1 @api.depends — не просто декоратор

Odoo компилирует пути зависимостей в граф триггеров. Запись инвалидирует несохранённые значения, находит затронутые строки через обратные связи, ставит сохранённые поля в очередь на пересчёт, учитывает состояние связей до и после изменения и поддерживает ключи кеша, зависящие от контекста. Значимый для миграции контракт также включает store, precompute, compute_sudo, recursive, inverse и пользовательскую реализацию поиска.

Копирование синтаксиса декоратора без этого жизненного цикла создаст ложное ощущение совместимости.

3.2 @api.onchange — вычисление черновика, а не валидация

Odoo отправляет на сервер полный черновик формы, имена изменённых полей, контекст и спецификацию полей представления. Сервер создаёт несохранённую псевдозапись, применяет значения по умолчанию и вычисляемые зависимости, выполняет методы onchange до стабилизации черновика и возвращает разницу значений вместе с предупреждениями. Значения X2many остаются графом команд в памяти.

В Angee уже есть полезные локальные механизмы форм (showWhen, prefill, вычисление slug), а Django ValidationError при отправке формы преобразуется в ошибки полей. Но это не общий протокол серверного onchange. Бизнес-расчёты не должны дублироваться произвольными TypeScript-callback'ами только ради отзывчивости форм.

3.3 «Graph» в Odoo и GraphView в Angee — разные понятия

<graph> в Odoo — аналитический график по сгруппированным показателям. Существующий GraphView в Angee отображает узлы и рёбра для исследования топологии или workflow. Его не следует превращать во владельца столбчатых, линейных или круговых диаграмм.

3.4 Pivot — главным образом недостающий контракт представления

Слой данных Angee уже предоставляет несколько измерений группировки, временную гранулярность, агрегатные показатели (count, sum, avg, min, max), HAVING, сортировку, точное число групп и метаданные ключей группировки и показателей. Реализация Pivot должна переиспользовать этого владельца. Второй API аналитики следует добавлять только в том случае, если характеризующие тесты докажут, что существующий сгруппированный запрос не может эффективно получать разреженную сводную таблицу, подытоги или drill-down.

4. Реестр возможностей

Статусы означают:

  • есть — в Angee существует переиспользуемый владелец;
  • частично — полезные примитивы есть, но они не сохраняют полный семантический контракт Odoo;
  • нет — на изученной ревизии не найден общий переиспользуемый владелец;
  • маппинг — обычно это задача маппинга в odoo2angee, а не новый слой Angee.
ID Семантическая область Odoo Текущее состояние Angee Статус Приоритет
D1 вычисляемые/related-поля, @api.depends, inverse/search/context-зависимости обычные свойства, написанные вручную резолверы, сохранённые значения, поддерживаемые кодом аддона, вычисляемые Pydantic-ресурсы частично P0
D2 @api.onchange, значения по умолчанию, вычисление несохранённых псевдозаписей, предупреждения локальные prefill, showWhen и вычисление slug в форме; валидация только при отправке частично P0
D3 Python-ограничения и выполнение инвариантов на всех путях записи ограничения/clean() Django и стандартный GraphQL full_clean; написанные вручную manager/task/bulk-пути остаются ответственностью своих владельцев частично P0
D4 граф команд x2many и вложенные черновики документов одна объявленная связь с редактируемыми дочерними строками и транзакционным применением разницы частично P0
D5 расширение той же строки через _inherit композиция абстрактных доноров при сборке есть маппинг
D6 делегирование _inherits и полиморфные ссылки связи Django, MTI, канонические ссылки; нет причин глобально эмулировать делегирование Odoo частично маппинг
D7 workflow состояний/статусов и защищённые действия StateTransitions, типизированные действия, оптимистические проверки исходного состояния, аддон workflows есть маппинг
D8 ACL, record rules, группы полей область строк REBAC, разрешения на создание, авторизация полей есть, другая модель маппинг
U1 XML-описания форм/списков, наследование, модификаторы, именованные точки расширения React DSL и часть сериализуемых спецификаций форм; нет общего переносимого контракта композиции view-spec частично P0
U2 window actions: модель, упорядоченные режимы представления, domain/context/defaults, target маршруты/ресурсы/действия есть, но нет эквивалентного декларативного контракта workspace action частично P0
U3 kanban BoardView, группировка, объявленные дорожки, перенос для смены стадии есть маппинг
U4 calendar оконный CalendarView с точками расширения для drag/resize/select есть, требуется явный источник маппинг
U5 pivot backend группировки/агрегации есть; нет типа resource-view pivot и renderer'а сводной таблицы нет P1
U6 аналитический graph/chart backend агрегации есть; топологический GraphView не является диаграммой нет P1
U7 search views, фасеты, group-by, избранное типизированные фильтры, группировка, фасеты, URL-состояние и сериализация избранного; нет серверного владельца общих/личных сохранённых поисков частично P1
U8 drill-down и экспорт представлений/действий навигация по спискам и общие части экспорта есть; нет контракта drill-down/экспорта для pivot/chart частично P1
X1 chatter, подписчики, активности, временная шкала записи миксин обсуждаемых записей, подписчики, настройки уведомлений, активности/agenda, вложения, GraphQL-действия и общие UI-панели есть маппинг
X2 QWeb/report actions, печатный HTML/PDF, политика вложений общий владелец объявления/рендеринга/действий отчётов не найден нет P1
X3 переводимые сохранённые значения (translate=True) frontend-i18n есть; общего хранения/редактирования значений моделей по языкам не установлено частично P2
X4 cron/autovacuum точка расширения Celery task и композиция принадлежащего аддону CELERY_BEAT_SCHEDULE есть маппинг
X5 context defaults и company-dependent значения значения по умолчанию модели/формы и проектные паттерны компаний есть; общего контекстного слоя свойств нет частично P2
X6 последовательности angee.sequence предоставляет защищённую блокировкой нумерацию документов со сбросом по периодам есть маппинг
X7 ресурсы/XML-данные и внешние ID многоуровневые ресурсы, xrefs, adoption, grants, детерминированная загрузка есть маппинг
X8 controllers/client actions/assets расширения Django/ASGI, типизированные GraphQL/MCP-действия, компонуемые React-аддоны есть, другая модель маппинг

5. Необходимые инвестиции в Angee

D1 — Вычисляемые значения и граф зависимостей (P0)

Владелец: angee.base, с валидацией компилятором и интеграцией с персистентностью.

Публичный контракт должен выражать семантику целевой платформы, а не названия из Odoo:

  • выходное поле и владелец вычисления;
  • пути зависимостей, включая прямые и обратные связи to-one/to-many;
  • режим вычисления: выражение базы данных, аннотация/свойство запроса или сохранённая материализация;
  • поведение при создании/предварительном вычислении;
  • ключи request/actor/company/context для несохранённых результатов;
  • необязательный контракт обратной записи и проекция фильтра/поиска;
  • явная и узкая политика повышения привилегий;
  • политика пакетной обработки, рекурсии и циклов.

Фреймворк должен компилировать и проверять граф при сборке, а затем интегрировать его с create/update/delete/M2M и объявленной разницей строк. Пересчёт сохранённых значений должен быть атомарен с бизнес-операцией записи. Откат не может оставить материализованное значение в обновлённом состоянии, а массовые операции должны либо корректно участвовать в механизме, либо быстро и явно завершаться ошибкой, а не незаметно обходить граф.

Не следует использовать один механизм для всех расчётов. Django GeneratedField или аннотация предпочтительнее для выражения, принадлежащего одной строке/базе данных; граф Angee нужен для Python-вычислений и межстрочных зависимостей.

D2 — Вычисление черновика (P0)

Владельцы: правило модели/домена в аддоне; транспорт в angee.graphql; проекция метаданных в angee.graphql.data; привязка в FormView из @angee/ui.

Запрос черновика должен содержать:

  • идентификатор модели/ресурса и необязательный идентификатор сохранённой записи;
  • полный текущий скалярный и реляционный черновик;
  • пути изменённых полей;
  • выбранную спецификацию полей;
  • контекст actor/company/locale и монотонный токен клиентского запроса.

Результат должен содержать:

  • патчи полей, включая патчи вложенных строк со стабильными ID черновика;
  • ошибки полей и формы;
  • неблокирующие предупреждения/уведомления;
  • динамические изменения вариантов/фильтров, когда выбор связанной записи зависит от черновика;
  • явные эффекты readonly/required/visibility только тогда, когда они объявлены целевой спецификацией представления.

Вычисление не должно сохранять данные, ставить задачи в очередь или вызывать внешние побочные эффекты. Оно должно выполняться с той же предварительной проверкой авторизации create/write, что и итоговая мутация. Клиент должен игнорировать устаревший ответ, если уже завершён более новый запрос черновика.

Главное: код аддона должен предоставлять чистые доменные вычисления, которые используются и правилами черновика, и вычислениями сохранённых данных. Фреймворк не должен создавать вторую TypeScript-реализацию налога, цены, доступности или правила согласования.

D3 — Шлюз инвариантов/записи (P0)

Владелец: модели/менеджеры Django для инварианта; backend мутаций/записи Angee для оркестрации.

Стандартные GraphQL-операции записи уже вызывают full_clean и возвращают структурированные ошибки полей/формы. Не хватает гарантии единообразия между сгенерированным CRUD, редактируемыми строками, типизированными действиями, загрузкой ресурсов, сервисами менеджеров и разрешёнными массовыми обновлениями.

Нужно определить и протестировать единую политику записи:

  • какие точки входа всегда вызывают валидацию полей/модели/уникальности/БД;
  • как метаданные изменённых полей позволяют избегать не относящихся к операции дорогих проверок без ослабления корректности;
  • как участвуют инварианты M2M/post-save;
  • как написанные вручную доменные сервисы объявляют, что они владеют низкоуровневой записью;
  • чем защита от удаления отличается от каскадов БД и очистки при удалении/сборке аддона.

Это не реестр сигналов. Доменные операции записи должны оставаться явными и транзакционными.

D4 — Реляционные черновики документов (P0)

Владелец: существующая точка расширения HasuraLines/EditableLines, которую нужно обобщить, а не заменить.

Для документов продаж, закупок, бухгалтерии и складского учёта нужны:

  • более одной редактируемой связи у родителя;
  • стабильные клиентские ID для несохранённых строк;
  • добавление/изменение/удаление/перестановка и изменения M2M в одном черновике;
  • вычисление черновика строки и итогов родителя до сохранения;
  • межстрочные ошибки с адресацией до строки и поля;
  • единое атомарное сохранение с оптимистической проверкой конкурентности/версии;
  • явная политика глубины вложенности — ограниченный контракт предпочтительнее произвольного рекурсивного CRUD.

U1/U2 — Сериализуемые спецификации действий и представлений (P0)

Владельцы: декларации аддонов и компоновщик времени сборки; метаданные/codegen для транспорта; @angee/ui для отображения.

Сегодня написанный вручную JSX — эффективная цель для нестандартного UX, но плохой переносимый IR для миграции. Генерация большой копии TSX для каждого XML-представления Odoo потеряет смысл наследования и затруднит использование будущих улучшений фреймворка.

Нужно добавить нативную для целевой платформы сериализуемую декларацию для:

  • действия ресурса/workspace: маршрут, модель, разрешённые виды представлений, представление по умолчанию, базовый фильтр, значения по умолчанию, разрешения и режим открытия;
  • спецификаций form/list/board/calendar/pivot/chart с ID полей, группами, вкладками, колонками, показателями, фасетами, действиями и именованными слотами;
  • условных выражений в небольшой проверяемой грамматике, а не произвольных строк исходного кода;
  • расширения при сборке по именованному слоту/элементу с детерминированным порядком и немедленной ошибкой при коллизиях;
  • escape hatch для написанного вручную React-кода, который заменяет или заполняет именованный слот, но не становится владельцем общих механизмов представлений.

Это не XML-renderer. odoo2angee отображает исходную XML-цепочку/цепочку наследования в спецификацию Angee и фиксирует каждый неподдерживаемый модификатор или нестандартный виджет как решение/вывод.

U5 — Представление Pivot (P1)

Владелец: @angee/ui, использующий операции группировки @angee/metadata и @angee/refine.

Нужно добавить pivot как вид представления ресурса с декларацией осей строк, осей колонок, показателей, начального раскрытия, сортировки и политики переходов/экспорта. Представлению нужны:

  • несколько уровней строк и колонок;
  • подытоги и общие итоги;
  • временная гранулярность;
  • выбор показателей;
  • раскрытие/сворачивание, перестановка осей, сортировка и разреженная загрузка;
  • переход из ячейки к отфильтрованному списку;
  • экспорт CSV/XLSX через принадлежащий фреймворку сервис экспорта;
  • состояние URL/избранного и доступная клавиатурная/табличная семантика.

Сначала нужно доказать, что текущая сгруппированная GraphQL-операция может обслужить эту форму данных ограниченным числом запросов и с точными итогами. При необходимости следует расширить существующего владельца группировки, а не создавать отдельного для pivot.

U6 — Аналитическое представление Chart (P1)

Владелец: новый примитив представления chart в @angee/ui.

Нужно переиспользовать те же оси, показатели и состояние фильтров, что и в Pivot. Сначала следует поддержать базовый набор Odoo Community: bar, line, pie, stacked-режим, гранулярность дат, выбор показателя и drill-down сегмента. Топологический GraphView должен остаться отдельным.

U7 — Сохранённые поиски и пресеты представлений (P1)

Владелец: небольшой базовый аддон/ресурс и существующая модель состояния представления ресурса.

Нужно сохранять личные/общие фильтры, сортировку, стек группировок, выбранный вид представления, оси/показатели pivot и настройки chart. Доступом к общим пресетам должен управлять REBAC. Хранимый формат должен быть версионированными метаданными, а не исполняемыми Odoo-domain'ами или JavaScript.

X1/X2 — Маппинг совместной работы с записью и отчёты (P1)

В Angee уже есть сильный профиль совместной работы с записью: обсуждаемые записи, подписчики и настройки уведомлений, запланированные активности и agenda, вложения, GraphQL-операции в области записи и общие UI-компоненты chatter/activity. Перед маппингом аддона, наследующего mail.thread, нужно определить, какие именно возможности Odoo он использует, и сопоставить их с этим профилем. Существующего владельца следует расширять только тогда, когда поведенческий fixture докажет реальный пробел.

Для аддонов, объявляющих ir.actions.report, нужно определить возможность отчётов, а не создавать отдельные замены в каждом аддоне:

  • именованная декларация отчёта, типизированные входные данные/контекст, владелец HTML-шаблона, PDF-renderer, локализация, политика вложения/повторной печати, авторизация и результат UI-действия.

6. Что не является пробелом фреймворка Angee

Следующее обычно требует маппинга или кода аддона, а не Odoo-подобного ядра внутри Angee:

  • синтаксис _inherit Odoo: в Angee уже есть детерминированные доноры исходной модели;
  • синтаксис ACL/record-rule: нужно отображать смысл политики в REBAC и оставлять открытое решение, если domain невозможно безопасно представить;
  • recordset'ы и Environment Odoo: целевой моделью остаются обычные модели/queryset'ы Django и явные владельцы actor/context;
  • произвольные словари context: каждый значимый ключ нужно отображать в типизированного владельца целевой платформы вместо создания глобального bag;
  • специфичные для продукта server/client actions: реализовать типизированное GraphQL-действие или написанную вручную React-поверхность с общим контрактом действий;
  • OWL-патчи и JavaScript-реестры: отображать видимую пользователю возможность в именованную точку расширения Angee, а не механизм патчей;
  • QWeb как язык: отчётам нужен контракт отчёта, а не интерпретатор QWeb.

7. Порядок реализации

Фаза A — каталог возможностей и характеризация

  1. Добавить в odoo2angee версионированный каталог возможностей, относящийся только к целевой платформе. Хранить его отдельно от odoo2angee.ir.odoo и фиксировать исследованную ревизию Angee.
  2. Расширять статический инвентарь только там, где запланированному маппингу не хватает фактов исходника: связи compute/inverse/search полей, зависимости декораторов, значимые атрибуты полей представлений и порядок действий/ представлений. Не расширять область генерации.
  3. Построить поведенческие fixture'ы вокруг одного агрегата документа Odoo, а не всего дерева аддонов.

Рекомендуемый fixture: sale.order + sale.order.line с изменениями товара, количества и налогов, добавлением/удалением строк, скидками, значениями по умолчанию, предупреждениями, ограничениями, сохранёнными итогами и откатом.

Фаза B — доменные точки расширения P0

Реализовать D1, D2, D3 и D4 вместе на основе fixture'а документа. D2 должен переиспользовать D1/доменные вычисления; D4 должен запускать оба механизма. Не следует выпускать изолированные декораторы, неспособные реализовать весь жизненный цикл.

Фаза C — переносимый контракт UI/действий

Сначала реализовать U1/U2 для list/form/board, затем отобразить Odoo action, search и наследование view из fixture'а в явные принятые решения целевой платформы.

Фаза D — аналитика

Реализовать U5 Pivot, U6 Chart и U7 сохранённые пресеты поверх существующего слоя сгруппированных данных. Использовать один ресурс в list, pivot и chart, чтобы доказать наличие единого владельца фильтров и drill-down.

Фаза E — сквозные профили

Отобразить существующие возможности совместной работы с записью, последовательностей и задач. Добавлять отчётность, переводимые значения и контекстные/company-свойства только тогда, когда они понадобятся выбранному пилотному аддону. У каждого нового профиля должен быть именованный владелец и собственный ID возможности маппинга.

8. Приёмочные сценарии

Вычисляемые значения

  • Изменение в одной строке один раз пересчитывает сохранённое поле.
  • Добавление, изменение, удаление, перенос к другому родителю или M2M-изменение строки пересчитывает затронутых старого и нового родителей.
  • Межмодельные и составные зависимости находят только затронутые строки.
  • Откат транзакции восстанавливает и исходные, и материализованные значения.
  • Массовые операции либо корректно участвуют, либо явно отклоняются.
  • Циклы и неизвестные пути зависимостей приводят к ошибке во время сборки.
  • Несохранённые значения, зависящие от actor/company/context, не могут утечь через кеши.

Вычисление черновика

  • Черновики создания и редактирования получают значения по умолчанию и вычисленные значения без записи в БД.
  • Изменения вложенных строк обновляют итоги строки и родителя до сохранения.
  • Предупреждения, ошибки полей и фильтры вариантов связей привязываются к своим владельцам.
  • Устаревший ответ не может перезаписать более новый черновик клиента.
  • Вычисление черновика и итоговое сохранение возвращают один канонический результат расчёта.
  • Авторизация работает по fail-closed-принципу, а вычисление не создаёт внешних побочных эффектов.

Pivot/Chart

  • Фильтры, оси, показатели, гранулярность, null-сегменты, подытоги и общие итоги совпадают с прямыми агрегатами базы данных.
  • Drill-down ячейки/сегмента открывает точный набор составляющих записей.
  • Перестановка осей и сохранённые пресеты корректно проходят полный цикл через состояние URL/персистентности.
  • Большие разреженные данные не требуют загрузки всех исходных строк в браузер.
  • Экспорт использует тот же отфильтрованный/авторизованный запрос и не раскрывает скрытые поля или строки.

Сквозное доказательство миграции

  • Инвентарь odoo2angee сохраняет каждый значимый метод, декоратор, параметр поля, факт view/action и неподдерживаемую конструкцию.
  • Человек принимает маппинги на именованные ID возможностей.
  • Генерация потребляет только принятый целевой IR.
  • Сгенерированный аддон использует общие точки расширения Angee, не содержит runtime-эмуляции Odoo и не дублирует расчёт на Python и TypeScript.
  • Поведенческие тесты доказывают выбранные бизнес-сценарии Odoo, а не сходство исходного кода.

9. Правило принятия решений для будущих пробелов

Когда мигрируемому аддону требуется поведение, которого нет в Angee:

  1. определить видимый пользователю и транзакционный семантический контракт;
  2. проверить, не предоставляет ли его уже Django, зафиксированная библиотека или существующий владелец Angee;
  3. если паттерн переиспользуется между аддонами, добавить минимальную точку расширения у этого владельца и представить её через метаданные;
  4. оставить бизнес-вычисление в аддоне-потребителе;
  5. добавить версионированный ID возможности и приёмочный тест;
  6. только после этого разрешить odoo2angee предлагать такой маппинг.

Главное правило: сохранять бизнес-поведение и возможности UI, а не словарь реализации Odoo.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment