Есть много способов решить проблему как в JavaScript, так и в Odoo. Тем не менее, фреймворк Odoo был разработан для того чтобы быть расширяемым (это довольно большое ограничение), и некоторые распространенные проблемы имеют хорошее стандартное решение. Стандартное решение имеет преимущество в том, что его легко понять разработчикам odoo, и, скорее всего, оно будет работать, когда Odoo изменится в процессе развития.
Этот документ пытается объяснить, как можно решить некоторые из этих проблем. Обратите внимание, что это не справка. Это просто случайная коллекция рецептов или объяснения того, как действовать в некоторых случаях.
Прежде всего, помните, что первое правило настройки odoo с помощью JS: попробуйте сделать это в python. Это может показаться странным, но среда Python достаточно расширяема, и многие варианты поведения могут быть реализованы просто с помощью xml или python. Как правило, такое решение имеет более низкую стоимость обслуживания, чем работа с JS:
- сам JS фреймворк имеет тенденцию меняться больше,что приводит к необходимости чаще менять JS код
- часто бывает сложнее реализовать настраиваемое поведение, если ему необходимо взаимодействовать с сервером и должным образом интегрироваться во фреймворк javascript. Фреймворк требует много мелких деталей, которые нужно реплицировать в коде. Например, отзывчивость, или обновление URL, или отображение данных без мерцания.
Примечание
Этот документ на самом деле не объясняет никаких концепций. Это больше поваренная книга. Для более подробной информации, пожалуйста, обратитесь к справочной странице по javascript (см. Javascript в Odoo)
Создание нового поля виджета
Скорее всего это очень распространенный сценарий использования: мы хотим отображать информацию в представлении Form действительно особенным(возможно, бизнес-зависимым) способом. Например, мы хотим изменить цвет текста в зависимости от бизнес-условия.
Это можно сделать в три этапа: создать новый виджет, зарегистрировать его в реестре полей, затем добавить виджет в поле в представления Form
- создание нового виджета:
Это можно сделать, расширив виджет:
var FieldChar = require('web.basic_fields').FieldChar; var CustomFieldChar = Fieldchar.extend({ renderReadonly: function () { // implement some custom logic here }, });
- регистрация в реестре полей:
Веб-клиент должен знать как сопоставить имя виджета и его класс. Это делается реестром:
var fieldRegistry = require('web.field_registry'); fieldRegistry.add('my-custom-field', CustomFieldChar);
- добавляем виджет в представление Form
<field name="somefield" widget="my-custom-field"/>
Обратите внимание, что только поля представлений Form, List и Kanban используют это поле реестра виджетов. Эти представления тесно интегрированы, поскольку представления List и Kanban могут отображаться внутри представления Form).
Изменение существующего виджета поля
Другой вариант использования - мы хотим изменить существующий виджет поля. Например, аддон voip в odoo должен изменить виджет FieldPhone, чтобы добавить возможность легко позвонить по указанному номеру в voip. Это делается путем including виджета FieldPhone, поэтому нет необходимости изменять какое-либо существующее представление Form.
Виджеты полей (экземпляры (подкласса) AbstractField), как и все остальные виджеты, могут быть «пропатчены по обезьяньи». Это выглядит так:
var basic_fields = require('web.basic_fields');
var Phone = basic_fields.FieldPhone;
Phone.include({
events: _.extend({}, Phone.prototype.events, {
'click': '_onClick',
}),
_onClick: function (e) {
if (this.mode === 'readonly') {
e.preventDefault();
var phoneNumber = this.value;
// call the number on voip...
}
},
});
Обратите внимание, что нет необходимости добавлять виджет в реестр, так как он уже зарегистрирован.
Модификация основного виджета из интерфейса
Другим распространенным вариантом использования является необходимость настройки некоторых элементов из пользовательского интерфейса. Например, добавить сообщение в домашнее меню. Обычный процесс в этом случае снова включает include виджета. Это единственный способ сделать это, поскольку для этих виджетов нет реестров.
Обычно это делается с помощью подобного кода:
var AppSwitcher = require('web_enterprise.AppSwitcher');
AppSwitcher.include({
render: function () {
this._super();
// do something else here...
},
});
Добавление действия клиента
Действие клиента - это виджет, который будет управлять частью экрана под строкой меню. При необходимости может иметь панель управления. Определение действия клиента может быть выполнено в два этапа: внедрение нового виджета и регистрация виджета в реестре действий.
- Реализация нового клиентского действия:
Это делается путем создания виджета:
var ControlPanelMixin = require('web.ControlPanelMixin'); var Widget = require('web.Widget'); var ClientAction = Widget.extend(ControlPanelMixin, { ... });
Do not add the controlpanel mixin if you do not need it. Note that some code is needed to interact with the control panel (via the
update_control_panel
method given by the mixin).
- Регистрация действия клиента:
Как обычно, нам нужно, чтобы веб-клиент знал о сопоставлении действий клиента и нужного класса:
var core = require('web.core'); core.action_registry.add('my-custom-action', ClientAction);
Затем, чтобы использовать действие клиента в веб-клиенте, нам нужно создать запись действия клиента (запись модели
ir.actions.client
) с соответствующим атрибутомtag
:<record id="my_client_action" model="ir.actions.client"> <field name="name">Some Name</field> <field name="tag">my-custom-action</field> </record>
Создание нового представления (с нуля)
Создание нового представления - более сложная тема. Этот рецепт содержит только те шаги, которые нужно будет выполнить (порядок выполнения не обязательно такой же):
добавление нового типа представления в поле
type
в модель``ir.ui.view``:class View(models.Model): _inherit = 'ir.ui.view' type = fields.Selection(selection_add=[('map', "Map")])
добавление нового типа представления в поле
view_mode
моделиir.actions.act_window.view
:class ActWindowView(models.Model): _inherit = 'ir.actions.act_window.view' view_mode = fields.Selection(selection_add=[('map', "Map")])
- Создание четырех основных частей, из которых создается представление (в JavaScript):
нам нужно представление (субкласс
AbstractView
, это фабрика), рендерер (отAbstractRenderer
), контроллер (отAbstractController
) и модель (fromAbstractModel
). Я предлагаю начать с простого расширения суперклассов:var AbstractController = require('web.AbstractController'); var AbstractModel = require('web.AbstractModel'); var AbstractRenderer = require('web.AbstractRenderer'); var AbstractView = require('web.AbstractView'); var MapController = AbstractController.extend({}); var MapRenderer = AbstractRenderer.extend({}); var MapModel = AbstractModel.extend({}); var MapView = AbstractView.extend({ config: { Model: MapModel, Controller: MapController, Renderer: MapRenderer, }, });
- добавление представления в реестр:
Как обычно, сопоставление между типом представления и его классом должно быть обновлено:
var viewRegistry = require('web.view_registry'); viewRegistry.add('map', MapView);
- реализация четырех основных классов:
- Классу
View
необходимо проанализировать полеarch
и настроить остальные три класса.Renderer
отвечает за представление данных в пользовательском интерфейсе,Model
должен общаться с сервером, загружать данные и обрабатывать их. ИController
предназначен для координации, общения с веб-клиентом, …
- создание представлений в базе данных:
<record id="customer_map_view" model="ir.ui.view"> <field name="name">customer.map.view</field> <field name="model">res.partner</field> <field name="arch" type="xml"> <map latitude="partner_latitude" longitude="partner_longitude"> <field name="name"/> </map> </field> </record>
Кастомизация существующего представления
Предположим, нам нужно создать пользовательскую версию универсального представления. Например, представление Kanban с дополнительным * ribbon-like* виджетом сверху (для отображения пользовательской информации). В этом случае это можно сделать за 3 шага: расширить представление Kanban (что также, вероятно, означает расширение контроллеров/рендеров и/или моделей), затем зарегистрировать представление в реестре представлений и, наконец, использовать представление в атрибуте arch. (конкретный пример - панель инструментов службы поддержки).
- расширение представления:
Вот как это может выглядеть:
var HelpdeskDashboardRenderer = KanbanRenderer.extend({ ... }); var HelpdeskDashboardModel = KanbanModel.extend({ ... }); var HelpdeskDashboardController = KanbanController.extend({ ... }); var HelpdeskDashboardView = KanbanView.extend({ config: _.extend({}, KanbanView.prototype.config, { Model: HelpdeskDashboardModel, Renderer: HelpdeskDashboardRenderer, Controller: HelpdeskDashboardController, }), });
- добавление в реестр представлений:
как обычно, мы должны информировать веб-клиента о сопоставлении имени представления и его класса.
var viewRegistry = require('web.view_registry'); viewRegistry.add('helpdesk_dashboard', HelpdeskDashboardView);
- используем в действующем представлении:
Теперь нам нужно сообщить веб-клиенту, что конкретному
ir.ui.view
необходимо использовать наш новый класс. Обратите внимание, что это особая потребность веб-клиента. С точки зрения сервера у нас все еще есть представление Kanban. Правильный способ сделать это - использовать специальный атрибутjs_class
(который когда-нибудь будет переименован вwidget
, потому что это действительно не очень хорошее имя) в корневом узле arch:<record id="helpdesk_team_view_kanban" model="ir.ui.view" > ... <field name="arch" type="xml"> <kanban js_class="helpdesk_dashboard"> ... </kanban> </field> </record>
Примечание
Примечание: вы можете изменить способ, которым представление интерпретирует структуру arch. Однако, с точки зрения сервера, это все еще представление того же базового типа, подчиняющийся тем же правилам (например, валидация rng). Таким образом, ваши представления все еще должны иметь валидное поле arch.