Mixin 和有用的类

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

消息传递功能

消息传递集成

基本消息系统

将消息传递功能集成到您的模型中非常容易。只需将“mail.thread` mixin and adding the chatter ``<chatter/>`”元素继承到表单视图即可立即启动并运行。 chatter 元素支持一些控制表单行为的选项:

  • open_attachments:显示默认展开的附件部分

  • reload_on_attachment:添加/删除附件时重新加载表单视图

  • reload_on_follower:更新关注者时重新加载表单视图

  • reload_on_post:发布新消息时重新加载表单视图

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="business_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 with options you might want to set -->
            <chatter open_attachments="True"/>
        </form>
    </field>
</record>

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

在服务器端,一些辅助功能可以帮助您轻松发送消息并管理记录中的关注者:

发布消息

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

在现有线程中发布新消息,返回新的 mail.message ID。

参数
  • body (str | Markup) – 消息正文。如果 str 将被转义。对 HTML 内容使用 Markup 对象。

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

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

  • attachments (list(tuple(str,str))) – 格式为“(name,content)”的附件元组列表,其中内容不是 base64 编码的

  • body_is_html (bool) – 指示 body 是否应被视为 HTML,即使 str 也是如此。

  • **kwargs – 额外的关键字参数将用作新 mail.message 记录的默认列值

返回

新创建的mail.message的ID

返回类型

int

message_post_with_view(views_or_xmlid, **kwargs):

使用 view_id 发送邮件/发布消息的帮助程序方法,以使用 ir.qweb 引擎进行渲染。此方法是独立的,因为模板和编辑器中没有任何内容允许批量处理视图。当模板处理 ui 视图时,此方法可能会消失。

参数

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

message_post_with_template(template_id, **kwargs)

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

参数
  • template_id – 要呈现以创建消息正文的模板的 ID

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

接收消息

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

message_new(msg_dict, custom_values=None)

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

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

参数
  • msg_dict (dict) – 包含电子邮件详细信息和附件的地图。有关详细信息,请参阅“message_process` and ``mail.message.parse`”

  • custom_values (dict) – 创建新线程记录时传递给 create() 的附加字段值的可选字典;请注意,这些值可能会覆盖来自消息的任何其他值

返回类型

int

返回

新创建的线程对象的id

message_update(msg_dict, update_vals=None)

由从传入电子邮件中获取的“message_process` when a new message is received for an existing thread. The default behavior is to update the record with ``update_vals`”调用。

可以通过重写此方法来实现其他行为。

参数
  • msg_dict (dict) – 包含电子邮件详细信息和附件的地图;有关详细信息,请参阅“message_process` and ``mail.message.parse()`”。

  • update_vals (dict) – 一个字典,包含根据给定的 id 更新记录的值;如果字典为 None 或 void,则不执行写入操作。

返回

真的

粉丝管理

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;如果没有,则取消订阅当前用户。

返回

真的

返回类型

bool

记录更改

mail 模块在字段上添加了强大的跟踪系统,允许您在记录的聊天记录中记录对特定字段的更改。要向字段添加跟踪,只需将跟踪属性设置为 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``(强制)- 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()`` function. This function is called by the tracking system to know which subtype should be used depending on the change currently being applied. In our case, we want to use our shiny new subtype when the ``state``字段从*草稿*更改为*已确认*:

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` (recipients linked to an employee user), portal (recipients linked to a portal user) and ``customer`”(未链接到任何用户的收件人)。覆盖使用的一个示例是添加一个链接到 res.groups 的组(例如 HrOffices),以为其设置特定的操作按钮。 group_func 是一个函数指针,以伙伴记录为参数。此方法将应用于收件人以了解他们是否属于给定组。仅保留第一个匹配组。评估顺序是列表顺序。 group_data 是一个包含通知电子邮件参数的字典,具有以下可能的键 - 值: - has_button_access 是否在电子邮件中显示 Access <Document>。对于新组默认为 True,对于门户/客户默认为 False。 -button_access 字典,包含按钮的 url 和标题 -has_button_follow 是否在电子邮件中显示“关注”(如果收件人当前未关注该线程)。对于新组默认为 True,对于门户/客户默认为 False。 -button_follow 字典,包含按钮的 url 和标题 -has_button_unfollow 是否在电子邮件中显示取消关注(如果收件人当前正在关注该线程)。 对于新组默认为 True,对于门户/客户默认为 False。 - Button_unfollow 字典,包含按钮的 url 和标题 - 要在通知电子邮件中显示的操作按钮的操作列表。 每个操作都是一个包含按钮的 url 和标题的字典。

返回

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

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

在当前记录上生成给定类型的链接(如果设置了 kwargs model and res_id,则在特定记录上生成链接)。

参数

link_type (str) – 要生成的链接类型;可以是以下任意值:view link to form view of the record assign assign the logged user to the user_id field of the record (if it exists) follow self-explanatory unfollow self-explanatory method call a method on the record; the method’s name should be provided as the kwarg method new open an empty form view for a new record; you can specify a specific action by providing its id (database id or fully resolved external id) in the kwarg action_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: any(
                user.sudo().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 属性

能够在模型上发布消息所需的访问权限;默认情况下还有 write access is needed, can be set to read

上下文键:

这些上下文键可用于在某种程度上控制``mail.thread`` features like auto-subscription or field tracking during calls to create() or ``write()``(或任何其他可能有用的方法)。

  • mail_create_nosubscribe:在create或message_post时,不要将当前用户订阅到记录线程

  • mail_create_nolog:创建时,不记录自动“<文档>已创建”消息

  • mail_notrack:在创建和写入时,不执行创建消息的值跟踪

  • tracking_disable:在创建和写入时,不执行 MailThread 功能(自动订阅、跟踪、发布……)

  • mail_auto_delete:自动删除邮件通知;默认为真

  • mail_notify_force_send:如果要发送的电子邮件通知少于 50 封,则直接发送,而不使用队列;默认为真

  • mail_notify_user_signature:在电子邮件通知中添加当前用户签名;默认为真

邮件别名

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

别名与传入邮件网关

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

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

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

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

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

别名支持集成

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

注解

将由别名创建的模型**必须**继承``mail_thread``模型。

通过继承在创建时初始化的``mail.alias.mixin``; this mixin will generate a new mail.alias record for each record of the parent class that gets created (for example, every project.project record having its ``mail.alias``记录来添加别名支持。

注解

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

与“mail.thread` inheritance, the ``mail.alias.mixin`”不同,**需要**一些特定的覆盖才能正常工作。这些覆盖将指定创建的别名的值,例如它必须创建的记录类型,以及这些记录可能具有的一些默认值,具体取决于父对象:

_get_alias_model_name(vals)

返回别名的模型名称。未回复现有记录的传入电子邮件将导致创建此别名模型的新记录。当创建该模型的记录时,该值可能取决于``vals``, the dict of values passed to create

参数

dict (vals) – 将保存别名的新创建记录的值

返回

型号名称

返回类型

str

_get_alias_values()

返回值以创建别名,或在创建后写入别名。虽然不是完全强制的,但通常需要通过在别名“alias_defaults”字段中设置默认值字典来确保新创建的记录将链接到别名的父级(即在正确的项目中创建的任务)。

返回

将写入新别名的值字典

返回类型

dict

_get_alias_values() 覆盖特别有趣,因为它允许您轻松修改别名的行为。在可以在别名上设置的字段中,以下内容特别令人感兴趣:

alias_name - Char

电子邮件别名的名称,例如如果您想接收 <jobs@example.odoo.com> 的电子邮件,请输入“jobs”

alias_user_id - Many2one (res.users)

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

alias_defaults - Text

为该别名创建新记录时将评估 Python 字典以提供默认值

alias_force_thread_id - Integer

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

alias_contact - Selection

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

  • 每个人:每个人都可以发帖

  • 合作伙伴:仅限经过身份验证的合作伙伴

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

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

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

活动追踪

活动是用户必须对文档执行的操作,例如打电话或组织会议。活动随邮件模块一起提供,因为它们集成在 Chatter 中,但*不与 mail.thread 捆绑在一起*。活动记录了“mail.activity` class, which have a type (``mail.activity.type`”)、名称、描述、预定时间(等等)。待处理的活动在聊天小部件的消息历史记录上方可见。

您可以分别使用“mail.activity.mixin` class on your object and the specific widgets to display them (via the field activity_ids) in the form view and kanban view of your records (mail_activity and ``kanban_activity`”小部件来集成活动。

Example

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

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

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

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

<record id="business_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 -->
            <chatter>
                <field name="message_follower_ids" widget="mail_followers"/>
                <field name="activity_ids" widget="mail_activity"/>
                <field name="message_ids" widget="mail_thread"/>
            </chatter>
        </form>
    </field>
</record>

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

  • CRM (crm) 应用程序中的“crm.lead

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

  • 项目 (project) 应用程序中的 project.task

网站特色

访客追踪

utm.mixin 类可用于通过指定资源链接中的参数来跟踪在线营销/传播活动。 mixin 向您的模型添加了 3 个字段:

  • campaign_id: Many2one field to a utm.campaign 对象(即 Christmas_Special、Fall_Collection 等)

  • source_id: Many2one field to a utm.source 对象(即搜索引擎、邮件列表等)

  • medium_id: Many2one field to a utm.medium 对象(即蜗牛邮件、电子邮件、社交网络更新等)

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

一旦客户使用在网址中设置的这些参数访问您的网站(即 https://www.chinaodoo.com/?campaign_id=mixin_talk&source_id=www.odoo.com&medium_id=website),就会在访问者的网站中为这些参数设置三个 cookie。一旦从网站创建了继承 utm.mixin 的对象(即潜在客户表单、工作申请等),utm.mixin 代码就会启动并从 cookie 中获取值以将它们设置在新记录中。完成此操作后,您可以在定义报告和视图(分组依据等)时将营销活动/来源/媒体字段用作任何其他字段。

要扩展此行为,只需将关系字段添加到简单模型(该模型应支持*快速创建*(即调用``create()`` with a single name value) and extend the function 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; once a new record of this model is created by a call from a website form, the generic override of the create() method of utm.mixin will fetch the default values for this field from the cookie (and the my_module.my_track 记录中找到,如果尚不存在,则将动态创建)。

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

  • CRM (crm) 应用程序中的“crm.lead

  • 招聘流程 (hr_recruitment) 申请中的“hr.applicant

  • 帮助台(帮助台 - 仅限 Odoo Enterprise)应用程序中的“helpdesk.ticket

网站知名度

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

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

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

这个 mixin 在你的模型上添加了 2 个字段:

  • website_published:代表发布状态的 Boolean 字段

  • website_urlChar 字段,表示访问对象的 URL

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

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` to the template; in this example, the blog.post record was passed as the blog_post variable to the qweb rendering engine, it is necessary to specify this to the publish management template. The publish_edit variable allow the frontend button to link to the backend (allowing you to switch from frontend to backend and vice-versa easily); if set, you must specify the full external id of the action you want to call in the backend in the ``action``变量传递(请注意,模型必须存在表单视图)。

操作``website_publish_button`` is defined in the mixin and adapts its behaviour to your object: if the class has a valid website_url compute function, the user is redirected to the frontend when he clicks on the button; the user can then publish the page directly from the frontend. This ensures that no online publication can happen by accident. If there is not compute function, the boolean ``website_published``被简单地触发。

网站元数据

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

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

这个 mixin 在你的模型上添加了 3 个字段:

  • website_meta_titleChar 字段,允许您为页面设置附加标题

  • website_meta_descriptionChar 字段,包含页面的简短描述(有时在搜索引擎结果中使用)

  • website_meta_keywordsChar 字段,包含一些关键字,以帮助搜索引擎更准确地对您的页面进行分类; “推广”工具将帮助您轻松选择词汇相关的关键字

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

其他的

客户评价

评级混合允许发送电子邮件询问客户评级、看板流程中的自动转换以及汇总评级统计数据。

为您的模型添加评级

要添加评级支持,只需继承“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 的行为会适应您的模型:

  • 模型的 rating.rating record will be linked to the partner_id 字段(如果该字段存在)。

    • 可以使用函数“rating_get_partner_id()` if you use another field than ``partner_id`”覆盖此行为

  • 模型的“rating.rating` record will be linked to the partner of the ``user_id`”字段(如果该字段存在)(即被评级的合作伙伴)

    • 可以使用函数``rating_get_rated_partner_id()`` if you use another field than user_id (note that the function must return a res.partner, for ``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>

请注意,评级有默认视图(看板、透视图、图表),可让您快速鸟瞰客户评级。

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

  • 项目 (* rating_project*) 应用程序中的 project.task

  • 帮助台(帮助台 - 仅限 Odoo Enterprise)应用程序中的“helpdesk.ticket