odoo13学习---13 自动化、工作流和打印输出
在my_library中,我们有一个state字段来指示图书租赁记录的当前状态。此状态字段仅限于正在进行的状态或返回的状态,并且不可能向业务流程添加新状态。为了避免这种情况,我们可以使用many2one字段来灵活地设计用户选择的看板工作流。
1. 添加一个名为library.rent.stage的新模型,如下所示:
class LibraryRentStage(models.Model):
_name = 'library.rent.stage'
_order = 'sequence,name'
name = fields.Char()
sequence = fields.Integer()
fold = fields.Boolean()
book_state = fields.Selection(
[('available', 'Available'),
('borrowed', 'Borrowed'),
('lost', 'Lost')],
'State', default="available")
2. 在security/ir.model.access.csv文件中为这个新模块添加访问权限,如下所示:
acl_book_rent_stage,library.book_rent_stage_default,model_library_rent_stage,,1,0,0,0
acl_book_rent_librarian_stage,library.book_rent_stage_librarian,model_library_rent_stage,group_librarian,1,1,1,1
3.从library.book.rent模型中删除state字段,用一个新的stage_id字段替换它,它是一个many2one字段及其方法,如下面的示例所示:
class LibraryBookRent(models.Model):
_name = 'library.book.rent'
@api.model
def _default_rent_stage(self):
Stage = self.env['library.rent.stage']
return Stage.search([], limit=1)
book_id = fields.Many2one('library.book', 'Book', required=True)
borrower_id = fields.Many2one('res.partner', 'Borrower', required=True)
state = fields.Selection([('ongoing', 'Ongoing'), ('returned', 'Returned')],
'State', default='ongoing', required=True)
rent_date = fields.Date(default=fields.Date.today)
return_date = fields.Date()
stage_id = fields.Many2one(
'library.rent.stage',
default=_default_rent_stage
)
@api.model
def create(self, vals):
rent = super(LibraryBookRent, self).create(vals)
if rent.stage_id.book_state:
rent.book_id.state = rent.stage_id.book_state
return rent
def write(self, vals):
rent = super(LibraryBookRent, self).write(vals)
if self.stage_id.book_state:
self.book_id.state = self.stage_id.book_state
return rent
4. 用stage_id字段替换表单视图中的state字段,如下面的示例所示:
<header>
<field name="stage_id" widget="statusbar" options="{'clickable': '1', 'fold_field': 'fold'}"/>
</header>
5. 将树视图中的状态字段替换为stage_id字段,如下所示:
<tree>
<field name="book_id"/>
<field name="borrower_id"/>
<field name="stage_id"/>
</tree>
6. 从data/library_stage.xml文件中添加一些初始阶段。不要忘记在清单中添加此文件,如下面的示例所示:
<?xml version="1.0" encoding="utf-8"?>
<odoo noupdate="1">
<record id="stage_draft" model="library.rent.stage">
<field name="name">Draft</field>
<field name="sequence">1</field>
<field name="book_state">available</field>
</record>
<record id="stage_rent" model="library.rent.stage">
<field name="name">On rent</field>
<field name="sequence">5</field>
<field name="book_state">borrowed</field>
</record>
<record id="stage_due" model="library.rent.stage">
<field name="name">Due</field>
<field name="sequence">15</field>
<field name="book_state">borrowed</field>
</record>
<record id="stage_returned" model="library.rent.stage">
<field name="name">Completed</field>
<field name="sequence">25</field>
<field name="book_state">available</field>
</record>
<record id="stage_lost" model="library.rent.stage">
<field name="name">Lost</field>
<field name="sequence">35</field>
<field name="fold" eval="True"/>
<field name="book_state">lost</field>
</record>
</odoo>
由于我们想要动态地管理记录阶段,我们需要创建一个新模型。
在第1步中,我们创建了一个名为library.rent.stage的新模型来存储动态阶段。在这个模型中,我们添加了一些字段。其中一个是序列字段,它用于确定阶段的顺序。我们还添加了布尔字段fold,用于折叠阶段并将它们放入下拉列表中。当您的业务流程有许多阶段时,这非常有用,因为这意味着您可以通过设置此字段在下拉菜单中隐藏不重要的阶段。我们添加了一个book_state字段来将动态阶段映射到图书的状态。我们将在接下来的部分中使用这个字段。fold字段还用于看板视图中显示折叠的看板列。通常,在进行中的工作项目应该处于展开阶段,而已完成或取消的终止项目应该处于折叠阶段。默认情况下,fold是用于保存stage fold值的名称字段。但是,您可以通过添加类属性_fold_name = 'is_fold'来更改这一点。
在步骤2中,我们为新模型添加了基本的访问权限规则。
在第3步中,我们在library.book.rent模型中添加了stage_id many2one字段。在创建新的贷款记录时,我们希望将默认阶段值设置为Draft。为此,我们添加了一个_default_rent_stage()方法。此方法将获取具有最低序列号的library.rent.stage模型的记录,因此,在创建新记录时,具有最低序列号的阶段将在表单视图中显示为活动的。
在步骤4中,我们在表单视图中添加了stage_id字段。通过添加clickable选项,我们使状态栏可单击。我们还为fold字段添加了一个选项,它允许我们在下拉菜单中显示不重要的阶段。
在第5步中,我们在树视图中添加了stage_id。
在步骤6中,我们为阶段添加了默认数据。在安装模块之后,用户将看到这些基本阶段。如果你想了解更多关于XML数据语法的知识,请参考第7章模块数据中使用XML文件加载数据的方法。通过这个实现,用户可以动态地定义新的阶段。
您需要为library.rent.stage添加视图和菜单,这样就可以从用户界面添加新阶段。如果你不知道如何添加视图和菜单,请参考第10章,后端视图。
如果您不想这样做,看板视图提供了从看板视图本身添加、删除或修改阶段的内置特性,这将在下一个菜谱中介绍。
注意,在library.book模型上有state字段。该字段用于表示图书的状态,或者换句话说,表示图书是否可用。我们添加了一个book_state字段来映射书的状态和动态阶段。
为了从阶段反映图书状态,我们需要覆盖library.book.rent模型中的create和write方法,如下所示:
@api.model
def create(self, vals):
rent = super(LibraryBookRent, self).create(vals)
if rent.stage_id.book_state:
rent.book_id.state = rent.stage_id.book_state
return rent
def write(self, vals):
rent = super(LibraryBookRent, self).write(vals)
if self.stage_id.book_state:
self.book_id.state = self.stage_id.book_state
return rent
在此之后,无论用户何时更改任何贷款记录的阶段,它都将反映在图书记录中。
看板是管理工作流的一种简单方法。它被组织成列,每个列对应于阶段,工作项从左到右进行直到完成。带有阶段的看板视图提供了灵活性,因为它允许用户选择自己的工作流。它在单个屏幕中提供了记录的完整概览。
我们将使用上一个my_library模块。我们将为libary.book.rent模型添加看板,并将逐步对看板卡片进行分组
按照下面的步骤来启用工作流,比如book rent模型的看板。
1. 为libary_book_rent添加看板视图,如下所示:
<!-- Kanban View -->
<record id="library_book_rent_view_kanban" model="ir.ui.view">
<field name="name">Rent Kanban</field>
<field name="model">library.book.rent</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id">
<field name="stage_id" />
<templates>
<t t-name="kanban-box">
<div class="oe_kanban_global_click">
<div class="oe_kanban_content">
<div class="oe_kanban_card">
<div>
<i class="fa fa-user"/>
<b>
<field name="borrower_id" />
</b>
</div>
<div class="text-muted">
<i class="fa fa-book"/>
<field name="book_id" />
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
2. 在library_book_rent_action操作中添加看板,如下所示:
<field name="view_mode">kanban,tree,form</field>
3.在stage_id字段中添加_group_expand_stages()方法和group_expand属性,如下所示:
@api.model
def _group_expand_stages(self, stages, domain, order):
return stages.search([], order=order)
stage_id = fields.Many2one('library.rent.stage',
default=_default_rent_stage,
group_expand='_group_expand_stages'
)
重新启动服务器并更新模块以应用更改。这将启用看板板,如下图所示

在步骤1中,我们为library.book.rent模型添加了一个看板视图。请注意,我们使用stage_id作为看板的默认组,因此当用户打开看板时,看板卡片将按阶段分组。要了解更多关于看板的信息,请参阅第10章后端视图。
在步骤2中,我们在现有的操作中添加了看板关键字。
在步骤3中,我们在stage_id字段中添加了group_expand属性。我们还添加了一个新的_group_expand_stages()方法。group_expand更改字段分组的行为。默认情况下,字段分组显示正在使用的阶段。例如,如果没有包含丢失阶段的rent记录,分组将不会返回该阶段,因此看板将不会显示丢失的列。但是,在我们的示例中,我们希望显示所有的stage,而不管它们是否被使用。
使用_group_expand_stages()函数返回各阶段的所有记录。因此,看板视图将显示所有的阶段,您将能够通过拖放工作流来使用它们。
如果你玩这个创建的看板,你会发现很多不同的特性。其中一些已提到如下:
- 您可以通过单击add new column选项来创建一个新阶段。group_create选项可以用来禁用看板中的添加列选项。
- 您可以通过标题拖动列,以不同的顺序排列列。这将更新library.rent.stage模型的序列字段。
- 您可以使用看板列标题上的齿轮图标编辑或删除列。group_edit和group_delete选项可用于禁用此特性。
- 在fold字段中具有真实值的stage将折叠起来,列将显示为细长的条。如果你点击它,它会展开并显示看板卡片。
- 如果stage模型有布尔字段active,它将在看板列中显示archive和unarchive记录的选项。archivable选项可用于禁用此特性。
- 看板列上的加号图标可以用来直接从看板视图中创建记录。quick_create选项可用于禁用此特性。
分组看板视图提供了快速创建特性,它允许我们直接从看板视图生成记录。该列上的加号图标将在该列上显示一个可编辑的看板卡片,您可以使用它来创建记录。
我们将使用上一个菜谱中的my_library模块。我们将使用看板中的快速创建选项来创建library.book.rent模型。
按照下面的步骤为看板添加一个自定义快速创建表单。
1。为library.book.rent模型创建一个新的最小表单视图,如下所示:
<!-- Form View for kanban quick create -->
<record id="library_book_rent_view_form_minimal" model="ir.ui.view">
<field name="name">Library Rent Form</field>
<field name="model">library.book.rent</field>
<field name="arch" type="xml">
<form>
<group>
<field name="book_id" domain="[('state', '=', 'available')]"/>
<field name="borrower_id"/>
</group>
</form>
</field>
</record>
2. 在<kanban>标签上添加快速创建选项,如下所示:
<kanban default_group_by="stage_id" quick_create_view="my_library.library_book_rent_view_form_minimal" on_create="quick_create">
重新启动服务器并更新模块以应用更改。然后,单击列中的加号图标。这将启用看板表单,如下图所示:
要创建一个自定义快速创建选项,我们需要创建一个最小表单视图。我们在步骤1中做过。我们添加了两个必需的字段,因为如果创建记录时没有它们,将会导致错误。
在步骤2中,我们将这个新的表单视图添加到看板视图中。使用quick_create_View选项,您可以将一个自定义表单视图映射到看板视图。
我们还添加了一个额外的选项——on_create="quick_create"。当您单击控制面板中的Create按钮时,此选项将在第一列中显示一个快速创建表单。如果没有此选项,Create按钮将以可编辑模式打开表单视图。
您可以通过在看板标签中添加quick_create="false"来禁用快速创建特性。
在Odoo版本12中添加了quick_create_view选项。在以前的版本中,Quick Create选项只显示Name字段。如果您有其他一些必需字段,则需要覆盖name_create()来设置其他值。如果不想重写name_create()方法,可以为Required字段设置默认值。
看板卡片支持所有的HTML标签,这意味着你可以根据自己的喜好来设计它们。Odoo提供了一些使看板卡片更具交互性的内置方法。
我们将在看板卡片上添加颜色选项、星型小部件和many2many标签。
1.添加一个新模型来管理library.book.rent模型的标签,如下所示:
class LibraryRentTags(models.Model):
_name = 'library.rent.tag'
name = fields.Char()
color = fields.Integer()
2. 增加图书馆的基本访问权限。标签模型,如下:
acl_book_rent_tags,library.book_rent_tags_default,model_library_rent_tag,,1,0,0,0
acl_book_rent_librarian_tags,library.book_rent_tags_librarian,model_library_rent_tag,group_librarian,1,1,1,1
3.在library_book_rent模型中添加新字段,如下所示:
class LibraryBookRent(models.Model):
_name = 'library.book.rent'
@api.model
def _default_rent_stage(self):
Stage = self.env['library.rent.stage']
return Stage.search([], limit=1)
@api.model
def _group_expand_stages(self, stages, domain, order):
return stages.search([], order=order)
book_id = fields.Many2one('library.book', 'Book', required=True)
borrower_id = fields.Many2one('res.partner', 'Borrower', required=True)
state = fields.Selection([('ongoing', 'Ongoing'), ('returned', 'Returned')],
'State', default='ongoing', required=True)
rent_date = fields.Date(default=fields.Date.today)
return_date = fields.Date()
stage_id = fields.Many2one(
'library.rent.stage',
default=_default_rent_stage,
group_expand='_group_expand_stages'
)
color = fields.Integer()
popularity = fields.Selection([('no', 'No Demand'), ('low', 'Low Demand'), ('medium', 'Average Demand'), ('high', 'High Demand'),])
tag_ids = fields.Many2many('library.rent.tag')
@api.model
def create(self, vals):
rent = super(LibraryBookRent, self).create(vals)
if rent.stage_id.book_state:
rent.book_id.state = rent.stage_id.book_state
return rent
def write(self, vals):
rent = super(LibraryBookRent, self).write(vals)
if self.stage_id.book_state:
self.book_id.state = self.stage_id.book_state
return rent
4. 在表单视图中添加字段,如下:
<!-- Form View -->
<record id="library_book_rent_view_form" model="ir.ui.view">
<field name="name">Library Rent Form</field>
<field name="model">library.book.rent</field>
<field name="arch" type="xml">
<form>
<header>
<field name="stage_id" widget="statusbar" options="{'clickable': '1', 'fold_field': 'fold'}"/>
</header>
<sheet>
<group>
<group>
<field name="book_id" domain="[('state', '=', 'available')]"/>
<field name="borrower_id"/>
<field name="tag_ids" widget="many2many_tags"
options="{'color_field': 'color', 'no_create_edit': True}"/>
</group>
<group>
<field name="rent_date"/>
<field name="return_date"/>
<field name="popularity" widget="priority"/>
</group>
</group>
</sheet>
</form>
</field>
</record>
5. 更新看板视图以显示颜色、标签和优先级,如下面的例子所示:
<!-- Kanban View -->
<record id="library_book_rent_view_kanban" model="ir.ui.view">
<field name="name">Rent Kanban</field>
<field name="model">library.book.rent</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id" on_create="quick_create" quick_create_view="my_library.library_book_rent_view_form_minimal">
<field name="stage_id" />
<field name="color" />
<templates>
<t t-name="kanban-box">
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<t t-if="widget.editable">
<a role="menuitem" type="edit" class="dropdown-item">Edit</a>
</t>
<t t-if="widget.deletable">
<a role="menuitem" type="delete" class="dropdown-item">Delete</a>
</t>
<ul class="oe_kanban_colorpicker" data-field="color"/>
</div>
</div>
<div class="oe_kanban_content">
<div class="oe_kanban_card oe_kanban_global_click">
<div>
<i class="fa fa-user"/>
<b>
<field name="borrower_id" />
</b>
</div>
<div class="text-muted">
<i class="fa fa-book"/>
<field name="book_id" />
</div>
<span class="oe_kanban_list_many2many">
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</span>
<div>
<field name="popularity" widget="priority"/>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
粗体显示的代码应该添加到现有的看板视图中。
重新启动服务器并更新模块以应用更改。然后,单击列上的加号图标。它将显示看板,如下图所示:

在前两个步骤中,我们为标签添加了新的模型和安全规则。
在第三步中,我们在rent模型中添加了几个字段。
在步骤4中,我们在表单视图中添加了这些字段。
注意,我们在popularity字段上使用了priority小部件,它用星型图标显示选择字段。在tag_ids字段中,我们使用了many2many_tags小部件,它以标记的形式显示many2many字段。传递color_field选项来启用标记上的颜色特性。这个选项的值将是存储颜色索引的字段名。no_create_edit选项将禁用通过表单视图创建新标记的特性。
在第5步中,我们改进了很多东西。在看板卡上,我们添加了t-attfclass="#{kanban_color(record_color_raw_value)}。这将用于显示看板卡片的颜色。它使用颜色字段的值并基于该值生成一个类。例如,如果一个看板记录在颜色字段中值为2,它就会在类中添加kanban_color_2。在那之后,我们添加了一个下拉菜单来添加选项,比如编辑、删除和看板颜色选择器。编辑和删除选项只有在用户具有适当的访问权限时才会显示。
最后,我们给看板卡片添加了标签和优先级。在添加了所有这些之后,看板卡片看起来就像下面的截图:有了这个卡片设计,你就可以直接从看板卡片中设置流行星号和颜色。
有时,列中有大量记录,很难清楚地了解特定阶段。进度条可以用来显示任何列的状态。我们将根据名为popularity的字段在看板上显示进度条。
为了在看板栏中添加进度条,您需要在看板视图定义中添加进度条标签,如下所示:
<!-- Kanban View -->
<record id="library_book_rent_view_kanban" model="ir.ui.view">
<field name="name">Rent Kanban</field>
<field name="model">library.book.rent</field>
<field name="arch" type="xml">
<kanban default_group_by="stage_id" on_create="quick_create" quick_create_view="my_library.library_book_rent_view_form_minimal">
<field name="stage_id" />
<field name="color" />
<progressbar field="popularity" colors='{"low": "success", "medium": "warning", "high": "danger"}'/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="#{kanban_color(record.color.raw_value)} oe_kanban_global_click">
<div class="o_dropdown_kanban dropdown">
<a class="dropdown-toggle o-no-caret btn" role="button" data-toggle="dropdown">
<span class="fa fa-ellipsis-v"/>
</a>
<div class="dropdown-menu" role="menu">
<t t-if="widget.editable">
<a role="menuitem" type="edit" class="dropdown-item">Edit</a>
</t>
<t t-if="widget.deletable">
<a role="menuitem" type="delete" class="dropdown-item">Delete</a>
</t>
<ul class="oe_kanban_colorpicker" data-field="color"/>
</div>
</div>
<div class="oe_kanban_content">
<div class="oe_kanban_card oe_kanban_global_click">
<div>
<i class="fa fa-user"/>
<b>
<field name="borrower_id" />
</b>
</div>
<div class="text-muted">
<i class="fa fa-book"/>
<field name="book_id" />
</div>
<span class="oe_kanban_list_many2many">
<field name="tag_ids" widget="many2many_tags" options="{'color_field': 'color'}"/>
</span>
<div>
<field name="popularity" widget="priority"/>
</div>
</div>
</div>
</div>
</t>
</templates>
</kanban>
</field>
</record>
注意看板列进度条是在Odoo版本11中引入的。在此之前的版本将不会显示列进度条。
重新启动服务器并更新模块以应用更改。然后,单击列中的加号图标。这将在看板栏上显示进度条,如下图所示:

看板列上的进度条是根据字段的值显示的。
进度条支持四种颜色,因此您不能显示超过四种状态。可用的颜色是绿色(成功)、蓝色(信息)、红色(危险)和黄色(警告)。然后,需要将颜色映射到字段状态。在我们的示例中,我们映射了priority字段的三个状态,因为我们不想要没有需求的图书的任何进度条。
默认情况下,进度条在旁边显示记录的计数。您可以通过单击进度条中的某一状态来查看该状态的总数。单击进度条也会突出显示该状态的卡片。您还可以显示整数或浮点字段的和,而不是记录的计数。为此,需要使用字段值添加sum_field属性,比如sum_field="field_name"。
创建服务器行为
创建服务器行为服务器动作支持Odoo自动化工具。它们允许我们描述要执行的动作。然后,这些操作可以由事件触发器调用,或者在满足某些时间条件时自动触发。最简单的情况是让最终用户通过从More按钮中选择文档来执行操作。我们将为项目任务创建这种行动,以确定当前选择的任务的优先级,并为其设置从现在起三天的截止日期。
我们将需要一个Odoo实例与项目应用程序安装。我们还需要激活开发人员模式。如果它还没有被激活,激活它在Odoo设置仪表板。
怎么做呢?
要创建一个服务器动作并从More菜单中使用它,请遵循以下步骤:
1. 在设置顶部菜单中,选择Technical|Actions|Server Actions菜单项,点击记录列表顶部的Create按钮,如下图所示:

2. 用以下值填写服务器动作表单:
- 操作名称:设置为优先级
- 基本模型:任务
- 要做的动作:更新记录
3.在服务器操作中,在Data to write选项卡下,添加以下行:
- 作为第一个值,我们将输入以下参数:
- 字段:截止日期
- 估算类型:Python表达式
- 值:datetime.date.today () +datetime.timedelta(days= 3)
- 作为第二个值,我们将输入以下参数:
- 字段:优先级
- 估算类型: 值(value)
- 值:1
下面的屏幕截图显示了输入的值

4. 保存服务器操作并单击左上方的Create context操作按钮,使其在项目任务的More按钮中可用。
5. 要试用它,转到项目顶部菜单,选择Search|任务菜单项,并打开一个随机任务。通过单击More按钮,我们应该看到设置为优先级选项,如下图所示。选择这个将星级任务和更改截止日期为三天从现在:

它是如何工作的…
服务器操作在一个模型上工作,所以要做的第一件事就是选择我们想要使用的基本模型。在我们的示例中,我们使用了项目任务。接下来,我们应该选择要执行的操作类型。有几个选项可供选择:
- Execute Python代码允许您在其他选项都不够灵活的情况下编写任意要执行的代码。
- 创建新记录允许您在当前模型或其他模型上创建新记录。
- 更新记录允许您对当前记录或其他记录设置值。
- 发送电子邮件允许您选择一个电子邮件模板。当动作被触发时,它将被用来发送电子邮件。
- 执行多个操作可以用来触发客户端或窗口操作,就像单击菜单项时一样。
- 添加关注者允许用户或渠道订阅记录。
- 创建下一个活动允许您创建一个新活动。这将显示在聊天。
对于我们的示例,我们使用Update the Record来设置当前记录的一些值。我们将优先级设置为1以给任务加星,并在Deadline字段上设置一个值。
这个更有趣,因为要使用的值是从Python表达式求值的。我们的示例使用了datetime Python模块(https://docs.python.org/2/library/datetimehtml)来计算从今天开始三天的日期。
可以在那里使用任意的Python表达式,也可以在其他几种可用的操作类型中使用。出于安全原因,代码由在odoo/tools/safe_eval.py文件中实现的safe_eval函数检查。这意味着Python操作可能不被允许,但这很少被证明是一个问题。
有更多的…
Python代码是在一个受限制的上下文中计算的,其中可以使用以下对象:
env:这是环境对象的引用,就像self一样。类方法中的env。
model:这是对服务器操作所作用的模型类的引用。在我们的示例中,它等价于self.env['project.task]。
Warning: 这是对openerp.exception的引用。警告,允许验证阻止非预期的操作。它可以用作raise Warning('Message!')。
record或records):这提供了对当前记录的引用,允许您访问它们的字段值和方法。
log:这是一个在ir.logging模型中记录消息的函数,允许数据库端登录操作。
datetime、dateutil和time:它们提供对Python库的访问。
使用Python代码服务器操作
服务器操作有几种可用类型,但执行任意Python代码是最灵活的。如果使用得当,它为用户提供了从用户界面实现高级业务规则的能力,而不需要创建特定的附加模块来安装代码。
我们将通过实现一个向项目任务的追随者发送提醒通知的服务器操作来演示如何使用这种类型的服务器操作。
要创建Python代码服务器操作,请遵循以下步骤:
1. 创建一个新的服务器操作。在Settings菜单中,选择Technical | Actions | Server Actions菜单项,然后单击记录列表顶部的Create按钮。
2. 使用以下值填写服务器操作表单:
- 动作名称(Action Name):发送提醒
- 模型(Base Model):任务
- 待办的行动(Action To Do):执行Python代码
3.在Python代码文本区域,删除默认文本,并用以下代码替换它:
if not record.date_deadline: raise Warning('Task has no deadline!')
delta = record.date_deadline - datetime.date.today()
days = delta.days
if days==0: msg = 'Task is due today.' elif days < 0: msg = 'Task is %d day(s) late.' % abs(days) else: msg = 'Task will be due in %d day(s).' % days
record.message_post(body=msg, subject='Reminder',subtype='mt_comment')

4. 保存服务器操作并单击右上角的Create context操作,使其在项目任务的More按钮中可用。
5. 现在,单击项目顶部菜单并选择Search|任务菜单项。随机选择一个任务,设定一个截止日期,然后尝试更多按钮中的发送提醒选项。
它是如何工作的…
本章的创建服务器操作配方提供了如何创建服务器操作的详细说明。对于这种特定类型的操作,我们需要选择Execute Python Code选项,然后编写代码来运行文本区域。
代码可以有多行,它运行在引用了对象(如当前记录对象或会话用户)的上下文中。可用的引用在创建服务器操作部分中进行了描述。
我们使用的代码计算从当前日期到截止日期的天数,并使用该天数准备适当的通知消息。最后一行是在任务的消息墙上实际张贴消息。subtype='mt_comment'参数用于向关注者发送电子邮件通知,就像我们使用New Message按钮时一样。如果没有给出子类型,则默认使用mt_note,在没有通知的情况下发布内部通知,就好像我们使用了内部通知按钮日志一样。参考第23章,管理电子邮件在Odoo,以了解更多关于邮寄在Odoo。
有更多的…
Python代码服务器操作是一种强大而灵活的资源,但是与自定义附加模块相比,它们确实有一些限制。
因为Python代码是在运行时评估的,所以如果发生错误,堆栈跟踪的信息就不那么丰富,而且可能更难调试。使用第8章中所示的技术在服务器操作的代码中插入断点也是不可能的,
调试,所以调试需要使用日志语句来完成。另一个问题是,当试图跟踪模块代码中行为的原因时,可能找不到任何相关的东西。在本例中,它可能是由服务器操作引起的。
在更密集地使用服务器操作时,交互可能相当复杂,因此明智的做法是适当地计划并组织它们。
在时间条件下使用自动操作
自动操作可用于根据时间条件自动触发操作。我们可以使用它们来自动地对满足某些标准和时间条件的记录执行一些操作。例如,我们可以在项目任务截止日期的前一天触发提醒通知,如果他们有的话。让我们看看怎么做。
准备
要遵循这个配置,我们需要安装项目管理应用程序(技术名称为Project)和自动操作规则附加组件(技术名称为base_automation),并激活开发人员模式。我们还需要在本章使用Python代码服务器操作菜谱中创建的服务器操作。
怎么做呢?
要在任务上创建一个带有定时条件的自动动作,请遵循以下步骤:
1. 在设置菜单中,选择Technical|Automation|Automated Actions菜单项,然后单击Create按钮。
2. 在自动操作表格中填写基本信息:
- 操作名称:在截止日期附近发送通知
- 模型:任务
- 在触发器条件:基于时间条件
- 待办的行动:执行几个操作
3.要设置记录条件,单击Apply on部分中的Edit Domain按钮。在弹出的对话框中,在代码编辑器中设置一个有效的域表达式, ["&",["date_deadline","!=",False],["stage_id.fold","=",False]],然后点击保存按钮。当更改到另一个字段时,关于满足条件的记录数量的信息将被更新,并显示记录按钮。通过单击records按钮,我们可以检查满足域表达式的记录列表。
4. 要设置触发日期的时间条件,请选择要使用的字段Deadline,并将触发后的延迟日期为-1天。
5. 在Actions选项卡上,在Server Actions to run下,单击添加项并从之前应该创建的列表中选择"发送提醒(Send notification)"。如果没有,我们仍然可以使用create按钮创建服务器动作来运行,如下面的截图所示:

6. 单击Save保存自动操作。
7. 执行以下步骤来尝试它:
- 转到Project菜单,搜索|任务,用过去的日期为任务设置一个截止日期。
- 转到设置菜单,单击Technical|Automation|Scheduled Actions菜单项,在列表中找到基本操作规则:检查并执行操作,打开其表单视图,并按左上方的Run手动按钮。这迫使我们现在检查定时的自动操作。如下面的截图所示。注意,这应该工作在一个新创建的演示数据库,但可能不这样工作在一个现有的数据库:
-
- 同样,转到项目菜单,打开之前设置了截止日期的任务。检查留言板;您应该会看到由我们的自动操作触发的服务器操作生成的通知。
它是如何工作的…
自动操作作用于模型,可以由事件或时间条件触发。第一步是设置模型以及何时运行值。
这两种方法都可以使用筛选器缩小适合执行操作的记录的范围。我们可以用定义域表达式来表示。你可以在第10章后端视图中找到更多关于写域表达式的信息。
或者,您可以使用用户界面特性在项目任务上创建和保存一个筛选器,然后复制自动生成的域表达式,并基于搜索筛选器列表从集合选择中选择它。
我们使用的域表达式在没有选中折叠标志的阶段中选择所有具有非空截止日期的记录。没有折叠标志的阶段被认为是正在进行的工作。这样,我们就可以避免在完成、取消或关闭阶段的任务上触发通知。
然后,我们应该定义时间条件——要使用的日期字段以及触发操作的时间。时间周期的单位可以是分钟、小时、天或月,周期的数量可以是正数(表示日期之后的时间),也可以是负数(表示日期之前的时间)。当使用以天为单位的时间段时,我们可以提供一个资源日历,该日历定义工作日,并可用于日计数。
这些操作由检查操作规则计划作业进行检查。注意,默认情况下,它每4小时运行一次。这适用于以日或月为单位工作的操作,但是,如果您需要在较小的时间尺度上工作的操作,则需要将运行间隔更改为较小的值。
对于满足所有条件且触发日期条件(字段日期加上间隔)在最后一次操作执行之后的记录,将触发动作。这是为了避免重复触发相同的动作。另外,这也是为什么在调度操作尚未触发的数据库中手动运行前面的操作可以工作,但在调度程序已经运行的数据库中可能无法立即工作的原因。
一旦自动操作被触发,Actions选项卡会告诉您应该发生什么。
这可能是服务器操作的列表,这些操作执行更改记录上的值、发布通知或发送电子邮件等操作。
有更多的…
一旦达到一定的时间条件,就会触发这些类型的自动操作。这与在条件仍然为真时定期重复某个动作是不同的。例如,自动操作将不能在超过截止日期后的每一天发布提醒。
相反,这种类型的操作可以由存储在ir.cron模型中的预定操作执行。但是,计划操作不支持服务器操作;它们只能调用模型对象的现有方法。因此,要实现自定义操作,我们需要编写一个附加模块,添加底层Python方法。
作为参考,自动化操作模型的技术名称是base.action.rule。
业务应用程序为系统提供业务操作的记录,但也期望支持特定于组织用例的动态业务规则。
将这些规则嵌入自定义附加组件模块可能不太灵活,并且超出了功能性用户的能力范围。由事件条件触发的自动操作可以弥补这一差距,并提供一个强大的工具来自动化或强制组织的过程。例如,我们将对项目任务实施验证,以便只有项目经理可以将任务更改为完成阶段。
准备
要遵循此菜谱,您需要已经安装了项目管理应用程序。我们还需要激活开发人员模式。如果它还没有被激活,激活它在Odoo关于对话框。
怎么做呢?
要在任务上创建带有事件条件的自动操作,请遵循以下步骤:
1. 在设置菜单中,选择Technical|Automation |Automated Actions菜单项,然后单击Create按钮
2. 在自动操作表格中填写基本信息:
- 动作名称:验证关闭任务
- 模型:任务
- 条件选项卡|何时运行:在更新
- 待办的行动:执行多个动作
3.on update规则允许您设置两个记录过滤器,在更新操作之前和之后:
- 在更新前域表达式(Before Update Filter)字段上,点击编辑域按钮,设置一个有效的域表达式- [['stage_id.name','!=', 'Done']] -在代码编辑器中,并保存。
- 在应用于(Apply On)域中,点击编辑域按钮,在代码编辑器中设置 [['stage_id.name','=', 'Done']],并保存,如下图所示:
4. 在Actions选项卡中,单击Add a item。在列表对话框中,单击Create按钮创建一个新的服务器操作。
5. 用以下值填写服务器动作表单,然后单击Save按钮:
- 操作名称:验证关闭任务
- 基本模型:任务
- 要做的操作:执行Python代码
- Python代码:输入以下代码:
if user != record.project_id.user_id: raise Warning('Only the Project Manager can close Tasks')
6. 点击保存和关闭保存自动行动,并尝试它:
- 在具有演示数据并以管理员身份登录的数据库上,转到Project菜单并单击E-Learning集成项目,以打开任务的看板视图。
- 然后,尝试将其中一个任务拖放到Done stage列中。因为这个项目的经理是演示用户,而我们与管理员用户一起工作,我们的自动操作应该被触发,我们的警告消息应该阻止更改。
它是如何工作的…
我们首先为自动操作命名,并设置它应该使用的模型。对于我们需要的操作类型,我们应该在更新时选择,但在创建时,在创建和更新时,在删除时,以及基于表单修改选项也是可能的。
接下来,我们定义过滤器来决定何时触发操作。On Update操作允许我们定义两个过滤器—一个用于在对记录进行更改之前检查,另一个用于在对记录进行更改之后检查。这可以用来表示转换——检测记录何时从状态a更改到状态b。在我们的示例中,我们希望在未完成任务更改到已完成阶段时触发该操作。On Update操作是唯一允许这两个过滤器的操作;其他操作类型只允许使用一个过滤器。
需要注意的是,我们的示例条件只对英语用户有效。这是因为艺名是一个可翻译的字段,对于不同的语言可以有不同的值。因此,应该避免或小心使用可翻译字段上的过滤器。
最后,我们创建并添加一个(或多个)服务器操作,并在自动操作被触发时执行我们想要执行的操作。在本例中,我们选择演示如何实现定制验证,使用Python code server操作,该操作使用警告异常阻止用户的更改。
有更多的…
在第6章基本的服务器端开发中,我们看到了如何重新定义模型的write()方法来对记录更新执行操作。记录更新的自动化操作提供了另一种实现此目标的方法,但也有一些优点和缺点。
它的优点之一是,很容易定义由更新存储的计算字段触发的操作,而这在纯代码中是需要技巧的。还可以在记录上定义过滤器,并对不同的记录或匹配不同条件的记录使用不同的规则,这些条件可以用搜索域表示。
但是,与模块内的Python业务逻辑代码相比,自动化操作可能有缺点。如果没有良好的计划,所提供的灵活性会导致复杂的交互,难于维护和调试。此外,写入过滤器之前和之后操作会带来一些开销,如果您正在执行敏感的操作,这可能会成为一个问题。
在与外部世界通信时,常常需要从数据库中的记录生成PDF文档。Odoo使用与用于表单视图QWeb相同的模板语言.我们将创建一个QWeb报告来打印关于某本书的信息,该图书目前正由合作伙伴借阅。
准备
安装wkhtmltopdf,安装Odoo开发环境.另外,再次检查配置参数web.base.url(或者report.url)是一个URL,可以从您的Odoo实例访问;否则,生成报告将花费很长时间,结果看起来也会很奇怪。
怎么做呢?
1. 我们添加了一个关于res.partner的报告,该报告打印合作伙伴所借的图书列表。我们需要在合作伙伴模型上添加一个one2many字段,这个字段与模型library.book.rent有关,如下面的示例所示:
class ResPartner(models.Model):
_inherit = 'res.partner'
rent_ids = fields.One2many('library.book.rent', 'borrower_id')
2. 在reports/book_rent_templates中为您的报表定义一个视图。xml,如下:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<template id="book_rents_template">
<t t-call="web.html_container">
<t t-foreach="docs" t-as="doc">
<t t-call="web.internal_layout">
<div class="page">
<h1>Book Rent for <t t-esc="doc.name"/></h1>
<table class="table table-condensed">
<thead>
<tr>
<th>Title</th>
<th>Expected return date</th>
</tr>
</thead>
<tbody>
<tr t-foreach="doc.rent_ids" t-as="rent" >
<td><t t-esc="rent.book_id.name" /></td>
<td><t t-esc="rent.return_date" /></td>
</tr>
</tbody>
</table>
</div>
</t>
</t>
</t>
</template>
</odoo>
3.在reports/book_loan_report的报表标签中使用此视图。xml格式,如下例所示:
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<report id="report_book_rent"
name="my_library.book_rents_template"
model="res.partner"
string="Book Rents"
report_type="qweb-pdf" />
</odoo>
4. 在Add -on的清单中添加这两个文件,并在depends中添加联系人,这样您就可以打开合作伙伴的表单视图,如下面的示例所示:
...
'depends': ['base'],
'data': [
'security/groups.xml',
'security/ir.model.access.csv',
'views/library_book.xml',
'views/library_book_categ.xml',
'views/library_book_rent.xml',
'data/library_stage.xml',
'reports/book_rent_templates.xml',
'reports/book_rent_report.xml'
],
...
现在,在打开合作伙伴表单视图或在列表视图中选择合作伙伴时,应该会在下拉菜单中提供打印图书贷款的选项,如下图所示:

它是如何工作的…
在步骤1中,我们使用QWeb模板语言定义了报告的结构。
我们没有像前面那样使用记录语法,而是在这里使用模板元素。这完全是为了方便;QWeb报告只是像其他所有报告一样的视图。使用这个元素的原因是QWeb views的arch字段必须遵守非常严格的规则,模板元素生成一个满足这些规则的arch内容。
现在不要担心模板元素中的语法。这个主题将在第16章“Web客户端开发”的QWeb中详细讨论。
在步骤2中,我们在另一个XML文件中声明了这个报告。报告元素是ir.actions.report.xml类型操作的另一个快捷方式。这里的关键部分是将name字段设置为您定义的模板的完整XML ID(即modulename_record_id),否则报表生成将失败。model属性决定报表操作的记录类型,string属性是在打印菜单中显示给用户的名称。
通过将report_type设置为qweb-pdf,我们请求通过wkhtmltopdf运行视图生成的HTML,以便向用户交付PDF。在某些情况下,您可能希望使用qweb-html在浏览器中呈现HTML。
有更多的…
报告的HTML中有一些标记类对布局非常重要。
确保您将所有内容包装在类页面设置的元素中。如果您忘记了这一点,您将什么也看不到。要向记录添加页眉或页脚,请使用页眉或页脚类。
另外,请记住这是HTML,因此要使用CSS属性,如pagebreak- before、page-break-after和page-break-inside。
你会注意到我们所有的模板主体都被包裹在两个带有t-call属性集的元素中。我们将在第十五章,CMS网站开发中研究这个属性的机制,但是在你的报告中做同样的事情是至关重要的。
这些元素负责让HTML生成指向所有必要CSS文件的链接,并包含生成报告所需的一些其他数据。虽然web.html_container没有真正的替代品,第二个t-call可以是web.external_layout。所不同的是,外部布局已经附带了一个页眉和页脚显示公司的商标,公司名称,和一些其他信息您期望从一个公司的外部沟通,而内部布局与分页只是给你一个头,打印日期和公司的名字。为了保持一致性,总是使用其中之一。
请注意,web_internal_layout、web_external_layout、web_external_layout_header和web_external_layout_footer(最后两个由外部布局调用)本身只是视图,而且您已经知道如何通过继承来更改它们。要继承模板元素,请使用inherit_id属性。
在Odoo的早期版本中,这些模板是在报表模块中定义的,而不是在web中。在将模块移植到Odoo 11时,不要忘记将调用中的标识符更改为t-call。


浙公网安备 33010602011771号