混合类和有用的类

Odoo 实现了一些有用的类和 mixin,使您可以轻松地在对象上添加经常使用的行为。本指南将详细介绍其中大部分,包括示例和用例。

消息功能

消息集成

基本消息系统

将消息功能集成到您的模型非常容易。只需继承 mail.thread 模型并将消息字段(及其适当的小部件)添加到您的表单视图中,即可立即运行。

Example

让我们创建一个简单的模型来代表商务旅行。由于组织这种旅行通常涉及到很多人和很多讨论,因此让我们在模型上添加消息交换的支持。

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread']
    _description = 'Business Trip'

    name = fields.Char()
    partner_id = fields.Many2one('res.partner', 'Responsible')
    guest_ids = fields.Many2many('res.partner', 'Participants')

在表单视图中:

<record id="businness_trip_form" model="ir.ui.view">
    <field name="name">business.trip.form</field>
    <field name="model">business.trip</field>
    <field name="arch" type="xml">
        <form string="Business Trip">
            <!-- Your usual form view goes here
            ...
            Then comes chatter integration -->
            <div class="oe_chatter">
                <field name="message_follower_ids" widget="mail_followers"/>
                <field name="message_ids" widget="mail_thread"/>
            </div>
        </form>
    </field>
</record>

一旦在您的模型上添加了聊天支持,用户可以轻松地在您的模型的任何记录上添加消息或内部备注;每个消息都会发送通知(对于消息,发送给所有关注者;对于内部备注,发送给员工(base.group_user)用户)。如果您的邮件网关和catchall地址配置正确,这些通知将通过电子邮件发送,并可以直接从您的邮件客户端回复;自动路由系统将把答复路由到正确的线程。

服务器端,有一些辅助函数可帮助您轻松发送消息并管理记录的关注者:

发布消息

message_post(self, body='', subject=None, message_type='notification', subtype=None, parent_id=False, attachments=None, **kwargs)

在现有的线程中发布新消息,返回新的邮件消息 ID。

参数
  • body (str) – 消息正文,通常是将被清理的原始HTML

  • message_type (str) – 请参阅 mail_message.message_type 字段

  • parent_id (int) – 在私人讨论中,通过将父级合作伙伴添加到消息中来处理对先前消息的回复

  • attachments (list(tuple(str,str))) – 以 (name,content) 形式的附件元组列表,其中 content 未经过 base64 编码

  • **kwargs – 额外的关键字参数将用作新邮件消息记录的默认列值

返回

新创建的邮件消息的ID

返回类型

int

message_post_with_view(views_or_xmlid, **kwargs):

使用 view_id 渲染 ir.qweb 引擎来发送邮件/发布消息的辅助方法。这个方法是独立的,因为在模板和 composer 中没有任何处理批量视图的方法。当模板处理 ir ui 视图时,这个方法可能会消失。

参数

record (str or ir.ui.view) – 应发送的视图的外部ID或记录

message_post_with_template(template_id, **kwargs)

使用模板发送邮件的辅助方法

参数
  • template_id – 用于创建消息正文的模板的ID

  • **kwargs – 创建一个继承自 mail.message 的 mail.compose.message 向导的参数

接收消息

这些方法在邮件网关处理新邮件时调用。 这些邮件可以是新线程(如果它们通过 alias 到达)或者只是现有线程的回复。重写它们允许您根据电子邮件本身的某些值(例如更新日期或电子邮件地址,将 CC 地址添加为关注者等)在线程记录上设置值。

message_new(msg_dict, custom_values=None)

当给定线程模型收到新消息且该消息不属于现有线程时,由 message_process 调用。

默认行为是创建相应模型的新记录(基于从消息中提取的一些基本信息)。可以通过覆盖此方法来实现其他行为。

参数
  • msg_dict (dict) – 一个包含电子邮件详细信息和附件的映射。有关详细信息,请参见 message_processmail.message.parse

  • custom_values (dict) – 可选的字典类型参数,用于在创建新的线程记录时传递给 create() 方法;请注意,这些值可能会覆盖来自消息的任何其他值

返回类型

int

返回

新创建的线程对象的ID

message_update(msg_dict, update_vals=None)

当现有线程收到新消息时,由 message_process 调用。默认行为是使用来自传入电子邮件的 update_vals 更新记录。

可以通过覆盖此方法来实现其他行为。

参数
  • msg_dict (dict) – 一个包含电子邮件详细信息和附件的映射;有关详细信息,请参见 message_processmail.message.parse()

  • update_vals (dict) – 一个包含根据其ID更新记录的值的字典;如果该字典为None或为空,则不执行写操作。

返回

关注者管理

message_subscribe(partner_ids=None, channel_ids=None, subtype_ids=None, force=True)

将合作伙伴添加到记录的关注者中。

参数
  • partner_ids (list(int)) – 将订阅该记录的合作伙伴的ID

  • channel_ids (list(int)) – 将订阅记录的频道的ID

  • subtype_ids (list(int)) – 订阅频道/合作伙伴的子类型的ID(如果为 None ,则默认为默认子类型)

  • force – 如果为True,则在使用参数中给定的子类型创建新的关注者之前删除现有的关注者

返回

成功/失败

返回类型

bool

message_unsubscribe(partner_ids=None, channel_ids=None)

从记录的关注者中移除合作伙伴。

参数
  • partner_ids (list(int)) – 将订阅该记录的合作伙伴的ID

  • channel_ids (list(int)) – 将订阅记录的频道的ID

返回

返回类型

bool

message_unsubscribe_users(user_ids=None)

使用用户的 message_subscribe 包装器。

参数

user_ids (list(int)) – 将取消订阅记录的用户的ID;如果为None,则取消订阅当前用户。

返回

返回类型

bool

记录更改

The mail module adds a powerful tracking system on fields, allowing you to log changes to specific fields in the record’s chatter. To add tracking to a field, simple set the tracking attribute to True.

Example

让我们跟踪商务旅行的名称和负责人的变化:

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread']
    _description = 'Business Trip'

    name = fields.Char(tracking=True)
    partner_id = fields.Many2one('res.partner', 'Responsible',
                                 tracking=True)
    guest_ids = fields.Many2many('res.partner', 'Participants')

从现在开始,对于行程名称或负责人的任何更改都将在记录上记录一条注释。 name 字段将在通知中显示,以提供有关通知的更多上下文(即使名称没有更改)。

子类型

子类型可以更细粒度地控制消息。子类型作为通知的分类系统,允许文档的订阅者自定义他们希望接收的通知的子类型。

子类型是作为模块中的数据创建的;该模型具有以下字段:

name (mandatory) - Char

子类型名称,将显示在通知自定义弹出窗口中

description - Char

将添加到此子类型发布的消息中的描述。如果为空,则将添加名称。

internal - Boolean

具有内部子类型的消息仅对员工可见,也就是 base.group_user 组的成员可见。

parent_id - Many2one

自动订阅的链接子类型;例如,项目子类型通过此链接与任务子类型相关联。当有人订阅项目时,他将订阅此项目下所有使用父子类型找到的任务子类型。

relation_field - Char

例如,当链接项目和任务子类型时,关系字段是任务的 project_id 字段。

res_model - Char

适用于的模型子类型;如果为 False,则适用于所有模型

default - Boolean

订阅时是否默认激活子类型

sequence - Integer

用于在通知自定义弹出窗口中排序子类型

hidden - Boolean

通知自定义弹出窗口中是否隐藏子类型

将子类型与字段跟踪接口相结合,可以订阅不同类型的通知,具体取决于用户可能感兴趣的内容。为此,您可以覆盖 _track_subtype() 函数:

_track_subtype(init_values)

根据已更新的值,给出由记录更改触发的子类型。

参数

init_values (dict) – 记录的原始值;字典中仅包含修改过的字段

返回

一个子类型的完整外部 ID,如果没有触发子类型,则为 False

Example

让我们在示例类上添加一个 state 字段,并在该字段更改值时触发具有特定子类型的通知。

首先,让我们定义我们的子类型:

<record id="mt_state_change" model="mail.message.subtype">
    <field name="name">Trip confirmed</field>
    <field name="res_model">business.trip</field>
    <field name="default" eval="True"/>
    <field name="description">Business Trip confirmed!</field>
</record>

然后,我们需要重写 track_subtype() 函数。这个函数由跟踪系统调用,以确定应该使用哪个子类型,具体取决于当前应用的更改。在我们的情况下,当 state 字段从 draft 更改为 confirmed 时,我们想要使用我们闪亮的新子类型:

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread']
    _description = 'Business Trip'

    name = fields.Char(tracking=True)
    partner_id = fields.Many2one('res.partner', 'Responsible',
                                 tracking=True)
    guest_ids = fields.Many2many('res.partner', 'Participants')
    state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
                             tracking=True)

    def _track_subtype(self, init_values):
        # init_values contains the modified fields' values before the changes
        #
        # the applied values can be accessed on the record as they are already
        # in cache
        self.ensure_one()
        if 'state' in init_values and self.state == 'confirmed':
            return self.env.ref('my_module.mt_state_change')
        return super(BusinessTrip, self)._track_subtype(init_values)

自定义通知

当向关注者发送通知时,在模板中添加按钮以允许直接从电子邮件中进行快速操作非常有用。 即使是一个简单的按钮直接链接到记录的表单视图也很有用;但是在大多数情况下,您不希望向门户用户显示这些按钮。

通知系统可以通过以下方式自定义通知模板:

  • 显示 访问按钮:这些按钮在通知电子邮件的顶部可见,允许收件人直接访问记录的表单视图

  • 显示 关注按钮:这些按钮允许收件人直接从记录中快速订阅

  • 显示 取消关注按钮:这些按钮允许收件人直接快速取消对记录的关注

  • 显示 自定义操作按钮:这些按钮是对特定路由的调用,允许您直接从电子邮件中执行一些有用的操作(例如将潜在客户转换为机会,为费用经理验证费用报销单等)

这些按钮设置可以应用于不同的组,您可以通过覆盖函数 _notify_get_groups 来自定义定义自己的组。

_notify_get_groups(message, groups)

根据已更新的值,给出由记录更改触发的子类型。

参数
  • message (record) – 正在发送的 mail.message 记录

  • groups (list(tuple)) – 列表的元组形式 (group_name, group_func,group_data) 的列表,其中: group_name 是一个标识符,仅用于能够覆盖和操作组。默认组是 user (与员工用户相关的收件人), portal (与门户用户相关的收件人)和 customer (与任何用户都没有关联的收件人)。覆盖的示例用法是添加一个与 res.groups 类似的组,如 Hr Officers,以为其设置特定的操作按钮。group_func 是一个函数指针,以合作伙伴记录作为参数。此方法将应用于收件人,以确定他们是否属于给定的组。只保留第一个匹配的组。评估顺序是列表顺序。group_data 是一个包含通知电子邮件参数的字典,具有以下可能的键值对: - has_button_access 是否在电子邮件中显示 Access <Document>。默认为 True(对于新组),对于门户/客户为 False。 - button_access 包含按钮的 URL 和标题的字典 - has_button_follow 是否在电子邮件中显示 Follow(如果收件人当前没有关注该线程)。默认为 True(对于新组),对于门户/客户为 False。 - button_follow 包含按钮的 URL 和标题的字典 - has_button_unfollow 是否在电子邮件中显示 Unfollow(如果收件人当前正在关注该线程)。默认为 True(对于新组),对于门户/客户为 False。 - button_unfollow 包含按钮的 URL 和标题的字典 - actions 要在通知电子邮件中显示的操作按钮的列表。每个操作都是一个包含按钮的 URL 和标题的字典

返回

一个子类型的完整外部 ID,如果没有触发子类型,则为 False

操作列表中的URL可以通过调用 _notify_get_action_link() 函数自动生成:

为当前记录(或如果设置了 kwargs modelres_id 则为特定记录)生成给定类型的链接。

参数

link_type (str) –

要生成的链接类型;可以是以下任何一个值:

view

链接到记录的表单视图

assign

将登录用户分配给记录的 user_id 字段(如果存在)

follow

不言自明

unfollow

不言自明

method

调用记录上的方法;方法的名称应作为关键字参数 method 提供

new

打开一个空的表单视图以创建新记录;您可以通过在 kwarg action_id 中提供其 id(数据库 id 或完全解析的外部 id)来指定特定的操作

返回

所选记录类型的链接

返回类型

str

Example

让我们给出差状态更改通知添加一个自定义按钮;此按钮将将状态重置为草稿,并且只对(虚构的)旅行经理组的成员可见(business.group_trip_manager

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread', 'mail.alias.mixin']
    _description = 'Business Trip'

    # Pevious code goes here

    def action_cancel(self):
        self.write({'state': 'draft'})

    def _notify_get_groups(self, message, groups):
        """ Handle Trip Manager recipients that can cancel the trip at the last
        minute and kill all the fun. """
        groups = super(BusinessTrip, self)._notify_get_groups(message, groups)

        self.ensure_one()
        if self.state == 'confirmed':
            app_action = self._notify_get_action_link('method',
                                method='action_cancel')
            trip_actions = [{'url': app_action, 'title': _('Cancel')}]

        new_group = (
            'group_trip_manager',
            lambda partner: bool(partner.user_ids) and
            any(user.has_group('business.group_trip_manager')
            for user in partner.user_ids),
            {
                'actions': trip_actions,
            })

        return [new_group] + groups

请注意,我可以在此方法之外定义我的评估函数,并定义全局函数来代替lambda,但为了在这些有时会很无聊的文档文件中更加简洁和不冗长,我选择前者而不是后者。

覆盖默认值

您可以通过多种方式自定义 mail.thread 模型的行为,包括但不限于以下方式:

_mail_post_access - Model attribute

需要发布模型消息的必要访问权限;默认情况下需要 write 访问权限,也可以设置为 read

上下文键:

这些上下文键可用于在调用 create()write() (或任何其他可能有用的方法)期间,有些控制 mail.thread 功能,如自动订阅或字段跟踪。

  • mail_create_nosubscribe: at create or message_post, do not subscribe the current user to the record thread

  • mail_create_nolog: at create, do not log the automatic ‘<Document> created’ message

  • mail_notrack: at create and write, do not perform the value tracking creating messages

  • tracking_disable: at create and write, perform no MailThread features (auto subscription, tracking, post, …)

  • mail_auto_delete: auto delete mail notifications; True by default

  • mail_notify_force_send: if less than 50 email notifications to send, send them directly instead of using the queue; True by default

  • mail_notify_user_signature: add the current user signature in email notifications; True by default

邮件别名

别名是可配置的电子邮件地址,与特定记录关联(通常继承 mail.alias.mixin 模型),当通过电子邮件联系时将创建新记录。它们是使您的系统从外部可访问的一种简单方式,允许用户或客户快速在数据库中创建记录,而无需直接连接到 Odoo。

别名 vs. 邮件入口网关

有些人使用传入邮件网关来实现同样的目的。但是,您仍然需要一个正确配置的邮件网关来使用别名,但是一个单独的万能域名就足够了,因为所有路由都将在Odoo内部完成。别名比邮件网关具有几个优点:

  • 更容易配置
    • 一个入站网关可以被多个别名使用;这样可以避免在您的域名上配置多个电子邮件(所有配置都在Odoo内部完成)。

    • 配置别名无需系统访问权限

  • 更连贯
    • 可在相关记录上配置,而不是在设置子菜单中

  • 更容易在服务器端进行覆盖
    • Mixin模型从一开始就被构建为可扩展的,使您可以比邮件网关更轻松地从传入的电子邮件中提取有用的数据。

别名支持集成

通常情况下,别名是在父模型上配置的,当通过电子邮件联系时,将创建特定的记录。例如,项目具有用于创建任务或问题的别名,销售团队具有用于生成潜在客户的别名。

注解

别名创建的模型 必须 继承 mail_thread 模型。

通过继承 mail.alias.mixin 来添加别名支持;这个 mixin 会为每个父类记录生成一个新的 mail.alias 记录(例如,每个 project.project 记录在创建时都会初始化其 mail.alias 记录)。

注解

也可以手动创建别名,并由一个简单的 Many2one 字段支持。本指南假设您希望更完整地集成,包括自动创建别名、记录特定的默认值等。

mail.thread 继承不同, mail.alias.mixin 需要 一些特定的重写才能正常工作。这些重写将指定创建的别名的值,例如它必须创建的记录类型,以及根据父对象可能具有的一些默认值。

_get_alias_model_name(vals)

返回别名的模型名称。不是回复现有记录的传入电子邮件将导致创建此别名模型的新记录。该值可能取决于 vals,即在创建此模型的记录时传递给 create 的值字典。

参数

dict (vals) – 新创建的记录将持有别名的值

返回

模型名称

返回类型

str

_get_alias_values()

返回值以创建别名,或在创建后对别名进行写入。虽然不是完全必需的,但通常需要确保新创建的记录将与别名的父级链接(即在正确的项目中创建任务),方法是在别名的 alias_defaults 字段中设置默认值字典。

返回

将写入新别名的值的字典

返回类型

dict

The _get_alias_values() override is particularly interesting as it allows you to modify the behaviour of your aliases easily. Among the fields that can be set on the alias, the following are of particular interest:

alias_name - Char

电子邮件别名的名称,例如,如果您想捕获 <jobs@example.odoo.com> 的电子邮件,则为“jobs”

alias_user_id - Many2one (res.users)

在收到此别名的电子邮件时创建记录的所有者;如果未设置此字段,则系统将尝试根据发件人(From)地址找到正确的所有者,如果找不到该地址的系统用户,则将使用管理员帐户。

alias_defaults - Text

Python 字典,用于在为此别名创建新记录时提供默认值

alias_force_thread_id - Integer

可选的线程(记录)ID,所有传入的消息都将附加到其中,即使它们没有回复它;如果设置,这将完全禁用新记录的创建

alias_contact - Selection

使用邮件网关在文档上发布消息的策略

  • everyone: 任何人都可以发布

  • partners: 仅限已认证的合作伙伴

  • followers: 仅限于相关文档的关注者或者以下频道的成员

请注意,别名使用了 委托继承,这意味着虽然别名存储在另一个表中,但您可以直接从父对象访问所有这些字段。这使得您可以轻松地从记录的表单视图中配置别名。

Example

让我们在出差类上添加别名,通过电子邮件即可即时创建费用。

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread', 'mail.alias.mixin']
    _description = 'Business Trip'

    name = fields.Char(tracking=True)
    partner_id = fields.Many2one('res.partner', 'Responsible',
                                 tracking=True)
    guest_ids = fields.Many2many('res.partner', 'Participants')
    state = fields.Selection([('draft', 'New'), ('confirmed', 'Confirmed')],
                             tracking=True)
    expense_ids = fields.One2many('business.expense', 'trip_id', 'Expenses')
    alias_id = fields.Many2one('mail.alias', string='Alias', ondelete="restrict",
                               required=True)

    def _get_alias_model_name(self, vals):
    """ Specify the model that will get created when the alias receives a message """
        return 'business.expense'

    def _get_alias_values(self):
    """ Specify some default values that will be set in the alias at its creation """
        values = super(BusinessTrip, self)._get_alias_values()
        # alias_defaults holds a dictionary that will be written
        # to all records created by this alias
        #
        # in this case, we want all expense records sent to a trip alias
        # to be linked to the corresponding business trip
        values['alias_defaults'] = {'trip_id': self.id}
        # we only want followers of the trip to be able to post expenses
        # by default
        values['alias_contact'] = 'followers'
        return values

class BusinessExpense(models.Model):
    _name = 'business.expense'
    _inherit = ['mail.thread']
    _description = 'Business Expense'

    name = fields.Char()
    amount = fields.Float('Amount')
    trip_id = fields.Many2one('business.trip', 'Business Trip')
    partner_id = fields.Many2one('res.partner', 'Created by')

我们希望在出差表单视图中轻松配置我们的别名,因此让我们将以下内容添加到我们的表单视图中:

<page string="Emails">
    <group name="group_alias">
        <label for="alias_name" string="Email Alias"/>
        <div name="alias_def">
            <!-- display a link while in view mode and a configurable field
            while in edit mode -->
            <field name="alias_id" class="oe_read_only oe_inline"
                    string="Email Alias" required="0"/>
            <div class="oe_edit_only oe_inline" name="edit_alias"
                 style="display: inline;" >
                <field name="alias_name" class="oe_inline"/>
                @
                <field name="alias_domain" class="oe_inline" readonly="1"/>
            </div>
        </div>
        <field name="alias_contact" class="oe_inline"
                string="Accept Emails From"/>
    </group>
</page>

现在我们可以直接从表单视图更改别名地址,并更改可以向别名发送电子邮件的人。

我们可以在我们的费用模型上重写 message_new() 方法,以便在创建费用时从我们的电子邮件中获取值:

class BusinessExpense(models.Model):
    # Previous code goes here
    # ...

    def message_new(self, msg, custom_values=None):
        """ Override to set values according to the email.

        In this simple example, we simply use the email title as the name
        of the expense, try to find a partner with this email address and
        do a regex match to find the amount of the expense."""
        name = msg_dict.get('subject', 'New Expense')
        # Match the last occurrence of a float in the string
        # Example: '50.3 bar 34.5' becomes '34.5'. This is potentially the price
        # to encode on the expense. If not, take 1.0 instead
        amount_pattern = '(\d+(\.\d*)?|\.\d+)'
        expense_price = re.findall(amount_pattern, name)
        price = expense_price and float(expense_price[-1][0]) or 1.0
        # find the partner by looking for it's email
        partner = self.env['res.partner'].search([('email', 'ilike', email_address)],
                                                 limit=1)
        defaults = {
            'name': name,
            'amount': price,
            'partner_id': partner.id
        }
        defaults.update(custom_values or {})
        res = super(BusinessExpense, self).message_new(msg, custom_values=defaults)
        return res

活动跟踪

Activities are actions users have to take on a document like making a phone call or organizing a meeting. Activities come with the mail module as they are integrated in the Chatter but are not bundled with mail.thread. Activities are records of the mail.activity class, which have a type (mail.activity.type), name, description, scheduled time (among others). Pending activities are visible above the message history in the chatter widget.

您可以在您的对象上使用 mail.activity.mixin 类来集成活动,并使用特定的小部件来显示它们(通过字段 activity_ids)在表单视图和看板视图中的记录上(分别使用 mail_activitykanban_activity 小部件)。

Example

组织商务旅行是一个繁琐的过程,跟踪需要的活动,如订购飞机票或机场出租车可能会很有用。为此,我们将在我们的模型上添加活动mixin,并在我们的旅行消息历史记录中显示下一个计划的活动。

class BusinessTrip(models.Model):
    _name = 'business.trip'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _description = 'Business Trip'

    name = fields.Char()
    # [...]

我们修改旅行的表单视图以显示它们的下一个活动:

<record id="businness_trip_form" model="ir.ui.view">
    <field name="name">business.trip.form</field>
    <field name="model">business.trip</field>
    <field name="arch" type="xml">
        <form string="Business Trip">
            <!-- Your usual form view goes here -->
            <div class="oe_chatter">
                <field name="message_follower_ids" widget="mail_followers"/>
                <field name="activity_ids" widget="mail_activity"/>
                <field name="message_ids" widget="mail_thread"/>
            </div>
        </form>
    </field>
</record>

您可以在以下模型中找到具体的集成示例:

  • crm.lead in the CRM (crm) Application

  • 销售( sale )应用程序中的 sale.order

  • project.task in the Project (project) Application

网站功能

访客追踪

The utm.mixin class can be used to track online marketing/communication campaigns through arguments in links to specified resources. The mixin adds 3 fields to your model:

  • campaign_id: Many2one 字段指向 utm.campaign 对象(例如 Christmas_Special, Fall_Collection 等)

  • source_idMany2one 字段指向 utm.source 对象(即搜索引擎、邮件列表等)

  • medium_idMany2one 字段指向 utm.medium 对象(即邮件、电子邮件、社交网络更新等)

这些模型只有一个字段 name (即它们只是用来区分活动,但没有任何特定的行为)。

一旦客户使用这些参数访问您的网站(即 https://www.odoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website),这些参数将在访问者的网站上设置三个cookie。一旦从网站(即潜在客户表单、职位申请等)创建了继承utm.mixin的对象,utm.mixin代码将启动并从cookie中获取值以将其设置在新记录中。完成后,您可以像定义报告和视图(按组等)时使用其他字段一样使用campaign/source/medium字段。

要扩展此行为,只需将关系字段添加到简单模型中(模型应支持 快速创建 (即使用单个 name 值调用 create() )并扩展函数 tracking_fields()

class UtmMyTrack(models.Model):
    _name = 'my_module.my_track'
    _description = 'My Tracking Object'

    name = fields.Char(string='Name', required=True)


class MyModel(models.Models):
    _name = 'my_module.my_model'
    _inherit = ['utm.mixin']
    _description = 'My Tracked Object'

    my_field = fields.Many2one('my_module.my_track', 'My Field')

    @api.model
    def tracking_fields(self):
        result = super(MyModel, self).tracking_fields()
        result.append([
        # ("URL_PARAMETER", "FIELD_NAME_MIXIN", "NAME_IN_COOKIES")
            ('my_field', 'my_field', 'odoo_utm_my_field')
        ])
        return result

这将告诉系统创建一个名为 odoo_utm_my_field 的 cookie,其值来自于 url 参数 my_field ;一旦通过网站表单的调用创建了此模型的新记录, utm.mixin 的通用 create() 方法将从 cookie 中获取此字段的默认值(如果尚不存在,则会动态创建 my_module.my_track 记录)。

您可以在以下模型中找到具体的集成示例:

  • crm.lead in the CRM (crm) Application

  • hr.applicant in the Recruitment Process (hr_recruitment) Application

  • helpdesk.ticket in the Helpdesk (helpdesk - Odoo Enterprise only) Application

网站可见性

您可以很容易地在任何记录上添加网站可见性切换。虽然这个 mixin 手动实现起来很容易,但它是继 mail.thread 继承之后最常用的 mixin;这证明了它的实用性。这个 mixin 的典型用例是任何具有前端页面的对象;能够控制页面的可见性允许您在编辑页面时花费时间,并在满意时才发布它。

要包含此功能,您只需要继承 website.published.mixin:

class BlogPost(models.Model):
    _name = "blog.post"
    _description = "Blog Post"
    _inherit = ['website.published.mixin']

此 mixin 在您的模型上添加了 2 个字段:

  • website_published: Boolean field which represents the status of the publication

  • website_url: Char field which represents the URL through which the object is accessed

注意,最后一个字段是计算字段,必须为您的类实现:

def _compute_website_url(self):
    for blog_post in self:
        blog_post.website_url = "/blog/%s" % (log_post.blog_id)

一旦机制到位,您只需调整前端和后端视图即可使其可访问。在后端,通常是在按钮框中添加一个按钮:

<button class="oe_stat_button" name="website_publish_button"
    type="object" icon="fa-globe">
    <field name="website_published" widget="website_button"/>
</button>

在前端,需要进行一些安全检查,以避免向网站访问者显示“编辑”按钮:

<div id="website_published_button" class="float-right"
     groups="base.group_website_publisher"> <!-- or any other meaningful group -->
    <t t-call="website.publish_management">
      <t t-set="object" t-value="blog_post"/>
      <t t-set="publish_edit" t-value="True"/>
      <t t-set="action" t-value="'blog.blog_post_action'"/>
    </t>
</div>

请注意,您必须将您的对象作为变量 object 传递给模板;在此示例中, blog.post 记录被传递为 blog_post 变量到 qweb 渲染引擎,因此在发布管理模板中必须指定。 publish_edit 变量允许前端按钮链接到后端(允许您轻松地在前端和后端之间切换);如果设置了该变量,则必须在 action 变量中指定要在后端调用的完整外部 ID(请注意,模型必须存在一个表单视图)。

在Mixin中定义了 website_publish_button 操作,并根据您的对象调整其行为:如果类具有有效的 website_url 计算函数,则用户单击按钮时将被重定向到前端;然后用户可以直接从前端发布页面。这确保不会发生意外的在线发布。如果没有计算函数,则简单地触发布尔值 website_published

网站元数据

这个简单的mixin允许您轻松地在前端页面中注入元数据。

class BlogPost(models.Model):
    _name = "blog.post"
    _description = "Blog Post"
    _inherit = ['website.seo.metadata', 'website.published.mixin']

此 mixin 在您的模型上添加了 3 个字段:

  • website_meta_title: Char field that allow you to set an additional title to your page

  • website_meta_description: Char field that contains a short description of the page (sometimes used in search engines results)

  • website_meta_keywords: Char field that contains some keywords to help your page to be classified more precisely by search engines; the “Promote” tool will help you select lexically-related keywords easily

这些字段可以使用编辑器工具栏中的“Promote”工具在前端进行编辑。设置这些字段可以帮助搜索引擎更好地索引您的页面。请注意,搜索引擎不仅仅基于这些元数据来生成结果;最佳的SEO实践仍然是获得可靠来源的引用。

其他

客户评分

评分 mixin 允许发送电子邮件请求客户评分,在看板流程中自动转换并聚合您的评分统计信息。

在您的模型上添加评分

要添加评分支持,只需继承 rating.mixin 模型:

class MyModel(models.Models):
    _name = 'my_module.my_model'
    _inherit = ['rating.mixin', 'mail.thread']

    user_id = fields.Many2one('res.users', 'Responsible')
    partner_id = fields.Many2one('res.partner', 'Customer')

Mixin的行为会根据您的模型进行调整:

  • 如果您的模型中存在 partner_id 字段,则 rating.rating 记录将链接到该字段。

    • 此行为可以通过函数 rating_get_partner_id() 覆盖,如果您使用的字段不是 partner_id

  • The rating.rating 记录将链接到您模型的 user_id 字段的合作伙伴(如果该字段存在)(即被评价的合作伙伴)

    • 此行为可以通过函数 rating_get_rated_partner_id() 覆盖,如果您使用的是 user_id 以外的字段 (请注意,该函数必须返回一个 res.partner,对于 user_id,系统会自动获取用户的合作伙伴)

  • 聊天记录将显示评级事件(如果您的模型继承自 mail.thread

通过电子邮件发送评分请求

如果您希望发送电子邮件请求评分,只需生成一封电子邮件,其中包含指向评分对象的链接。一个非常基本的电子邮件模板可能如下所示:

<record id="rating_my_model_email_template" model="mail.template">
            <field name="name">My Model: Rating Request</field>
            <field name="email_from">${object.rating_get_rated_partner_id().email or '' | safe}</field>
            <field name="subject">Service Rating Request</field>
            <field name="model_id" ref="my_module.model_my_model"/>
            <field name="partner_to" >${object.rating_get_partner_id().id}</field>
            <field name="auto_delete" eval="True"/>
            <field name="body_html"><![CDATA[
% set access_token = object.rating_get_access_token()
<p>Hi,</p>
<p>How satsified are you?</p>
<ul>
    <li><a href="/rate/${access_token}/5">Satisfied</a></li>
    <li><a href="/rate/${access_token}/3">Okay</a></li>
    <li><a href="/rate/${access_token}/1">Dissatisfied</a></li>
</ul>
]]></field>
</record>

然后您的客户将收到一封电子邮件,其中包含指向一个简单网页的链接,允许他们提供有关与您的用户的交互的反馈(包括自由文本反馈消息)。

您可以通过为评分定义一个操作,轻松地将评分与表单视图集成起来:

<record id="rating_rating_action_my_model" model="ir.actions.act_window">
    <field name="name">Customer Ratings</field>
    <field name="res_model">rating.rating</field>
    <field name="view_mode">kanban,pivot,graph</field>
    <field name="domain">[('res_model', '=', 'my_module.my_model'), ('res_id', '=', active_id), ('consumed', '=', True)]</field>
</record>

<record id="my_module_my_model_view_form_inherit_rating" model="ir.ui.view">
    <field name="name">my_module.my_model.view.form.inherit.rating</field>
    <field name="model">my_module.my_model</field>
    <field name="inherit_id" ref="my_module.my_model_view_form"/>
    <field name="arch" type="xml">
        <xpath expr="//div[@name='button_box']" position="inside">
            <button name="%(rating_rating_action_my_model)d" type="action"
                    class="oe_stat_button" icon="fa-smile-o">
                <field name="rating_count" string="Rating" widget="statinfo"/>
            </button>
        </xpath>
    </field>
</record>

请注意,评级有默认视图(看板、透视图、图表),可以快速查看客户评级的总体情况。

您可以在以下模型中找到具体的集成示例:

  • project.task in the Project (rating_project) Application

  • helpdesk.ticket in the Helpdesk (helpdesk - Odoo Enterprise only) Application