Odoo
МодульЯк думає Odoo — моделі, поля, view, workflow
Архітектурна основа: ORM, models, fields, views, actions, security. Без цього кастомізація — шаманство.
Що це і навіщо
Odoo — це не просто ERP, а фреймворк для побудови ERP. У ньому своя ORM, своя система view, своя модель прав і свій спосіб організувати бізнес-логіку. Якщо підходиш до Odoo як до SAP-альтернативи з GUI-конфігуратором — швидко впираєшся у стіну. Усе цікаве починається на рівні моделей і Python-коду.
Ця стаття — про базову ментальну модель. Що таке model, чим regular field різниться від computed, що показує form view, де живе workflow, як працює security. Без цього не зрозуміти, чому модулі мають саме таку структуру, чому не вийде «просто додати поле в таблицю» і чому одна група прав здатна зламати весь модуль.
Як це працює
Model — клас на Python, що мапиться на таблицю в PostgreSQL. Робить це Odoo ORM. Кожна модель — нащадок models.Model (звичайна), models.TransientModel (тимчасова — для wizard’ів) або models.AbstractModel (для міксинів).
class SaleOrder(models.Model):
_name = 'sale.order' # технічне ім'я → таблиця sale_order
_description = 'Sales Order'
_inherit = ['mail.thread', 'portal.mixin'] # підмішуємо chatter і portal
_order = 'date_order desc, name desc'
name = fields.Char(string='Order Reference', required=True, default='New')
partner_id = fields.Many2one('res.partner', string='Customer', required=True)
amount_total = fields.Monetary(compute='_compute_amount', store=True)
Fields бувають різні. Найважливіші типи:
- Char, Text, Html — текстові.
- Integer, Float, Monetary — числові. Monetary тягне валюту.
- Date, Datetime — час.
- Boolean, Selection — checkbox і dropdown.
- Many2one, One2many, Many2many — реляції.
- Binary — файли, картинки.
- Computed — вираховуються через метод (
compute='_compute_X'), можуть зберігатися в БД (store=True) або щоразу рахуватися (store=False).
Inheritance буває трьома способами:
- Class inheritance (
_name = 'sale.order', _inherit = 'mail.thread') — підмішує міксин у новий клас. - Extension inheritance (
_inherit = 'sale.order'без_name) — додає поля/методи до існуючої моделі. Найпоширеніший спосіб. - Delegation inheritance (
_inherits = {'res.partner': 'partner_id'}) — створює новий клас, що проксує поля до батьківського запису.
Views — як модель показується в інтерфейсі. Окремий XML-запис у ir.ui.view:
form— форма однієї картки.tree(теперlist) — таблиця списку.kanban— карточки.search— фільтри і групування.pivot/graph— аналітика.calendar,gantt— спеціальні.
Один і той же запис показується через ту view, яка викликана action’ом.
Actions — точки входу в інтерфейс. ir.actions.act_window — «відкрити вікно з такою моделлю і такими view-ями». Кнопки в меню — це action’и. Кнопки на формі — теж action’и.
Security — два шари:
ir.model.access(ACL). Хто (група) має право CRUD на модель. Обов’язково: для кожної моделі — хоч один запис уir.model.access.csv.ir.rule(record rules). Які саме records видно/доступні для редагування. Через домени. Наприклад: «менеджер бачить тільки SO своєї компанії».
Workflow в сучасному Odoo — це не окрема система (старий workflow engine викинули в Odoo 11), а просто:
- Поле
state(Selection) на моделі. - Кнопки з методами, які міняють state.
- Computed-поля, що залежать від state (наприклад,
editable). @api.constrainsі@api.dependsдля логіки.
def action_confirm(self):
self.ensure_one()
if not self.order_line:
raise UserError(_('Cannot confirm empty order'))
self.state = 'sale'
self._create_picking()
return True
Шлях у меню для дослідження: Developer mode → Settings → Technical → Models (всі моделі бази), → Views (всі views), → Security → Access Rights / Record Rules.
У контексті українського обліку
У стандартному Odoo моделі під українські реалії спроєктовані поверхово. Тому, коли кастомізуємо систему під UA, ми постійно:
- Розширюємо
account.moveдодатковими полями для ПН: тип причини, дата складання, статус ЄРПН, ІПН партнера. - Створюємо нові моделі для українських сутностей:
tax.invoice.ua(ПН як окремий документ),sea.vat.balance(ліміт СЕА),m11.report(накладна-вимога М-11),eu.export.declaration(МД). - Додаємо аналітичні поля.
analytic_distributionстандартний, але часто треба щеdefense_contract_id,military_unit_id— наш specific.
Принцип: ніколи не міняй стандартну модель напряму. Завжди роби нову з _inherit. Так оновлюєш Odoo без втрати кастомізації.
Як ми робимо (defense-контекст)
ТОВ «УкрПром-Тех» має шар власних модулів поверх Oblik-ERP:
kit_defense_contract— модельdefense.contractз номером А-XXXX, статусом, фінансами.kit_defense_so— розширення sale.order зdefense_contract_id, перевірками при підтвердженні.kit_defense_mrp— розширення mrp.production з обов’язковими серійниками і прив’язкою до контракту.kit_defense_blanks— кастомні QWeb-форми для М-11 з defense-смугою, сертифіката відповідності, EUC.
Security: окрема група Defense Officer, яка бачить тільки контракти зі статусом «Активний». Інші групи (наприклад, звичайні продажники) не бачать defense-документів — record rule на defense_contract_id != False.
Типові граблі
_inheritзабули — створили нову модель. Замість розширення sale.order отримав свою sale.order.custom, ніяк не пов’язану зі стандартом. Інвойси не зв’язані з SO. Лікувати — переробити.store=Trueна computed-полі без@api.depends. Значення розраховується раз і ніколи не оновлюється. Обов’язково: для зберігання — depends.- Поле без security. Створив model, але забув
ir.model.access.csv. Інтерфейс показує «Access Denied» для всіх. Базове правило: на нову модель — хоча б два рядки в ACL (для users і для managers). - Запис у БД, але не у view. Поле є в моделі, але не виведено в form view. Користувач бачить «зник чи що». Не забувай оновлювати views при додаванні полів.
- Record rule «всім видно». За замовчуванням після створення моделі бачить кожен, у кого є ACL. Якщо потрібен
multi-companyчи розмежування —ir.ruleна основі компанії. computeважка для БД. Метод робить агрегацію по 100k записів при кожному перегляді списку. Гальмує інтерфейс. Якщо потрібна агрегація — абоstore=Trueі триггери, або окрема віртуальна модель з SQL view.- Workflow через зовнішній сервіс. Замість state-машини і кнопок — REST-API, який щось «рухає». Працює рівно до першого збою. У Odoo workflow має бути всередині, з відкатами через
action_cancel.
Дотичне
- Документ-модель — як моделі складаються у бізнес-ланцюги
- Accounting — account.move як ключовий приклад моделі
- QWeb — як моделі рендеряться у звіти
- Cron — фонові процеси на моделях
Де цей модуль зустрічається6
- ГайдПерший день в OdooОрієнтація бухгалтера, який щойно зайшов в Odoo з-під 1С чи BAS: де живуть проводки, журнали, план рахунків і …
- OdooДокумент-модельЯк один SO запускає ланцюг операцій: реальний приклад руху бізнес-документа крізь моделі Odoo.
- OdooAccountingЯк Odoo веде подвійний запис: account.move як універсальний документ-проводка, журнали як шафи, account.move.l…
- OdooAnalytic AccountЧому аналітика окрема від плану рахунків і як нею вести проєктний облік оборонних контрактів, рахувати маржу з…
- OdooCronir.cron, model_id, function_id. Які стандартні cron важливі. Як писати власні — для перевірки ЄРПН-статусу, на…
- OdooQWebЯк Odoo рендерить PDF через QWeb: синтаксис шаблонів, директиви t-set/t-if/t-foreach, кастомізація ПН з типом …