模块开发

简介

OpenERP 采用 `三层架构 <http://en.wikipedia.org/wiki/Multitier_architecture#Three-tier_architecture>`_。 应用层本身被设计成核心和多个附加的模块,这些模块可以安装,不用OpenERP创建一个特殊的配置。

OpenERP的核心和其他不同的模块都是用 Python 写的。 模块的功能通过XML-RPC(或是NET-RPC,这取决于服务器端的配置)显示出来。模块也采用OpenERP ORM在关系型数据库(PostgreSQL)中来持久化数据。当模块安装好后通过提供XML文件就可以在数据库中插入数据。

Although modules are a simple way to structure a complex application, OpenERP modules also extend the system. Modules are also called addons (they could also have been called plugins).

在OpenERP的典型配置中,以下模块是必须的:

  • base: 最基本的模块。它无论何种情况下都必须安装,被认为是OpenERP核心的一部分。定义了 ir.propertyres.companyres.requestres.currency,res.usersres.partner, 等等。

  • crm: 客户关系管理和供应商关系管理。

  • sale: 销售管理。

  • mrp: 制造资源管理。

通过使用Python, XML files,依赖OpenERP的ORM和它的延伸机制,新的模块可以很容易快速的写出。OpenERP的开源特性和它众多的模块也为新模块的开发提供很多的例子。

模块结构

模块

  1. 简介

  2. 文件和文件夹结构
    1. __openerp__.py

    2. __init__.py

    3. XML 文件
      1. 操作

      2. Menu Entries

      3. Reports

      4. 向导

  3. 配置文件

模块 - 文件和文件夹结构

所有模块都应放到 server/addons 文件夹下。

按下面步骤创建新模块:

  • 在 server/addons 文件夹下面创建模块子文件夹

  • 创建模块定义文件: __openerp__.py

  • 创建定义 objects 的 Python 文件

  • 创建包括(初始数据, 视图, 菜单, 演示数据)的 .xml 文件

  • 创建包含 reportswizards 及 workflows 的 xml文件(可选)

模块 - 文件和目录 - XML 文件

模块目录里的XML文件用于修改数据库结构,他们有很多的用途,我们可以列出来:

  • 初始化,实例化(demonstration)数据声明(declaration)

  • 视图声明

  • 报表声明

  • 向导声明

  • 工作流声明

OpenERP XML文件主要结构的更多细节在 XML 数据序列化 部分,如果你想学习更多关于*初始化* 和 示例数据 XML文件,可以查看这里。 下面的部分只是关于特定的XML文件,如 following section are only related to XML specific to actions, menu entries, *操作、菜单、报表、向导 和 工作流 定义。

Python 模块描述文件 __init__.py

** __init__.py 文件**

这个文件就像任何的Python模块中一样,在程序的开始运行。它负责导入程序所需的Python文件。

所以,如果你想创建一个“module.py”文件,它包括你的对象的描述,在这种情况下,你需要在__init__.py文件中写一行:

import module
OpenERP 模块描述文件 __openerp__.py

在已创建模块的目录下,你必须添加一个__openerp__.py文件。这个文件必须在Python的格式下,负责:

  1. 确定所需的XML文件,server在进行初始化时将从语法上分析这些文件。

  2. 确定该创建模块的依赖模块。

这个文件包括下面的值:

name

(英文)名称.

version

模块版本号, 2 位 (1.2 或 2.0).

description

模块的描述,包含模块的使用文档。

author

模块的作者

website

模块的网站

license

模块的授权协议(默认AGPL).

depends

列出该模块所依赖的其他模块,因为base模块包括模块必须的视图,报表等数据,所以base模块应该在其他所有模块的依赖中。

init

List of .xml files to load when the server is launched with the "--init=module" argument. Filepaths must be relative to the directory where the module is. OpenERP XML File Format is detailed in this section.

data

List of .xml files to load when the server is launched with the "--update=module" launched. Filepaths must be relative to the directory where the module is. OpenERP XML File Format is detailed in this section.

demo

List of .xml files to provide demo data. Filepaths must be relative to the directory where the module is. OpenERP XML File Format is detailed in this section.

installable

True或是False,决定这个模块是否可安装。

images

List of .png files to provide screenshots, used on http://apps.openerp.com.

active

True或是False(默认是False),决定这个模块在数据库创建时是否安装。

test

List of .yml files to provide YAML tests.

范例

以product模块中的__openerp__.py为例:

{
    "name" : "Products & Pricelists",
    "version" : "1.1",
    "author" : "Open",
    "category" : "Generic Modules/Inventory Control",
    "depends" : ["base", "account"],
    "init_xml" : [],
    "demo_xml" : ["product_demo.xml"],
    "update_xml" : ["product_data.xml", "product_report.xml", "product_wizard.xml",
                    "product_view.xml", "pricelist_view.xml"],
    "installable": True,
    "active": True
}

放置在init_xml中的文件必须要么是和工作流相关,要么是安装软件时装载数据相关,或是和示例数据相关。

update_xml中的文件涉及到视图,报表和向导。

Objects

所有OpenERP的资源都是对象,如menus,actions,reports,invoices,partners... OpenERP通过数据库的对象关系映射(ORM,object relational mapping of a database)来控制信息存储。OpenERP的对象名是层次结构的,例如:

  • account.transfer : a money transfer

  • account.invoice : an invoice

  • account.invoice.line : an invoice line

总之,第一个单词是模块的名字:account,stock,sale

ORM的其他优点有:

  • simpler relations : invoice.partner.address[0].city

  • objects have properties and methods: invoice.pay(3400 EUR),

  • inheritance, high level constraints, ...

操作一个对象比很多表要容易些。

/doc_static/6.1/_images/pom_3_0_3.png

The Physical Objects Model of [OpenERP version 3.0.3]

PostgreSQL

OpenERP的ORM是在PostgreSQL上构造的。在OpenERP上通过对象接口或是直接使用SQL语句查询一个对象是可行的。

在PostgreSQL数据库中直接进行读写是非常危险的,因为可能会漏掉重要的步骤如约束检查或是工作流的修改。

注解

The Physical Database Model of OpenERP

Pre-Installed Data

PostgreSQL表中的数据可以使用XML文件来进行插入或更新,使得于OpenERP对象数据一致。OpenERP XML文件的主要结构是:

<?xml version="1.0"?>
<openerp>
  <data>
    <record model="model.name_1" id="id_name_1">
      <field name="field1">
        "field1 content"
      </field>
      <field name="field2">
        "field2 content"
      </field>
      (...)
    </record>
    <record model="model.name_2" id="id_name_2">
        (...)
    </record>
    (...)
  </data>
</openerp>

Fields content are strings that must be encoded as UTF-8 in XML files.

下面是一个来自 OpenERP 的源码的例子 (base_demo.xml 在 base 模块中):

<record model="res.company" id="main_company">
    <field name="name">Tiny sprl</field>
    <field name="partner_id" ref="main_partner"/>
    <field name="currency_id" ref="EUR"/>
</record>
<record model="res.users" id="user_admin">
    <field name="login">admin</field>
    <field name="password">admin</field>
    <field name="name">Administrator</field>
    <field name="signature">Administrator</field>
    <field name="action_id" ref="action_menu_admin"/>
    <field name="menu_id" ref="action_menu_admin"/>
    <field name="address_id" ref="main_address"/>
    <field name="groups_id" eval="[(6,0,[group_admin])]"/>
    <field name="company_id" ref="main_company"/>
</record>

最后的字段定义的 admin user :

  • 登录,密码等字段比较直接.

  • ref属性用于在records之间建立关系

<field name="company_id" ref="main_company"/>

字段company_id是一个从user object到company object的many-to-one的关系,main_company是相关联的id。

  • eval 属性允许将一些 python 代码放进 xml 中: 这里 groups_id 字段是一个多对多(many2many)的关系. 对于这样一个字段, "[(6,0,[group_admin])]" 的意思为 : 删除所有与当前用户相关的组,并使用 [group_admin] 作为新的关联组 (and group_admin 是另一个记录的 id ).

  • search 属性允许你在不指定 xml id 的情况下. 查找相关的记录. 你可以指定一个搜索条目来寻找想要查询的字段. 条目是一个 tuple 的 lists 用于预定义的搜索方法, 如果有多个结果, 通常选中(第一个):

<field name="partner_id" search="[]" model="res.partner"/>

这是个在demo数据中使用search的典型例子。在这里我们并不是真正想知道是哪个partner,所以我们给出了一个空的list。注意model属性是在一般情况下必须要写的。

记录标签

Description

T新数据的添加是通过record标签实现的。它利用一个必备的属性:model。Model是一个对象名称,可以用来实现插入数据。record标签内还有一个可选择的属性:id。如果使用了这个属性,那么在相同文件中,这个名字可以代替新创建的资源ID。

record标签中包含field标签。他们指出record的字段值(record’s fields value)。如果这个field没有详细说明,那么它会使用默认值。

范例

<record model="ir.actions.report.xml" id="l0">
     <field name="model">account.invoice</field>
     <field name="name">Invoices List</field>
     <field name="report_name">account.invoice.list</field>
     <field name="report_xsl">account/report/invoice.xsl</field>
     <field name="report_xml">account/report/invoice.xml</field>
</record>
Field tag

对应的属性如下:

name : (必需)   :   mandatory

field name

eval : (可选)   :   optional

将指定值进行添加的python表达式

ref

这个文件中涉及到已定义的id

model

用于查找的model

search

查询

Function tag

一个功能标签包含其他的功能标签。

model : (必须有的)   :   mandatory

要调用的model

name : (必需)   :   mandatory

function的名称

eval

估值(evaluate)要调用的方法的参数列表,不计cr和uid

范例

<function model="ir.ui.menu" name="search" eval="[[('name','=','Operations')]]"/>
Getitem tag

采用标签最后一个子节点的子集.

type : (必需)   :   mandatory

int 或 list

index : (必需)   :   mandatory

int or string

范例

Evaluates to the first element of the list of ids returned by the function node

<getitem index="0" type="list">
    <function model="ir.ui.menu" name="search" eval="[[('name','=','Operations')]]"/>
</getitem>
i18n
改进翻译
Translating in launchpad

翻译由“Launchpad Web interface”管理。在这里你会找到可译项目的清单。

请在问问题前阅读 FAQ

Translating your own module

在 5.0 版更改.

和之前4.2.x的版本不同,现在翻译都是通过模块来做。所以和之前整个系统中有一个特殊i18n文件夹不同的是,现在每一个模块都有自己的i18n文件夹。此外,OpenERP可以处理.po文件作为导入导出格式。当我们安装或是更新一个模块时,安装语言的翻译文件可以自动装入系统中。OpenERP也可以产生一个.tgz文件归档,里面包括为每个选中模块组织很好的.po文件。

[1]

http://www.gnu.org/software/autoconf/manual/gettext/PO-Files.html#PO-Files

Process
Defining the process

通过界面(interface)或是模块recorder来定义进程。然后放置生成的XML文件在自己的模块中。

Views
Technical Specifications - Architecture - Views

视图是一种在客户端显示对象的方式。他们指示客户端如何在屏幕上显示对象数据。

视图有两种:

  • 表单视图

  • 列表视图

Lists是tree views中的特殊情形。

同一个对象有几种视图:首先定义的视图样式(tree,form,…)将会做为它默认的样式。那样的话,当你双击一个菜单项时,就有一个默认的tree view和一个特定的view显示差不多的信息。例如,products针对product变量有几种视图。

视图都是在XML文件中进行描述的。

如果一个对象没有定义视图,那么这个对象可以自己产生一个视图来显示它自己。这会限制开发者的工作,但是会导致较少的人们自己的视图设计(ergonomic views)。

Usage example

当我们打开一张发票时,接下来是在客户端上的操作:

  • 一个动作请求打开发票(它给出了一个对象的数据(account.invoice),视图,域(例如仅仅是还未付款的发票))

  • 客户端请求server,什么样的视图由发票对象定义,哪些数据要显示。

  • 客户端通过视图显示表单

/doc_static/6.1/_images/arch_view_use.png
To develop new objects

对新对象的设计限制到最低限度:创建对象并且有选择的创建视图来显示他们。PostgreSQL的table数据不用手写,因为对象会自动创建它们(除非它们已经存在)。

Reports

OpenERP使用一个非常灵活和强大的报表系统。报表以PDF或是HTML的形式生成。报表是以数据层和表现层分开的原理进行设计的。

关于报表更多的细节在 Reporting 章节。

向导

这里有个描述向导的.xml文件的例子:

<?xml version="1.0"?>
<openerp>
    <data>
     <wizard string="Employee Info"
             model="hr.employee"
             name="employee.info.wizard"
             id="wizard_employee_info"/>
    </data>
</openerp>

wizard用wizard标签来声明。想要知道更多关于wizard XML的信息可以查看“Add A New Wizard”这个章节。

或者你可以在菜单中通过使用下面的XML entry添加向导。

<?xml version="1.0"?>
</openerp>
     <data>
     <wizard string="Employee Info"
             model="hr.employee"
             name="employee.info.wizard"
             id="wizard_employee_info"/>
     <menuitem
             name="Human Resource/Employee Info"
             action="wizard_employee_info"
             type="wizard"
             id="menu_wizard_employee_info"/>
     </data>
</openerp>
Workflow

通过对象和视图,我们可以很简单的定义新的表单,lists/trees和它们间的交互。但是这还不够:你还得定义这些对象间的动态关系。

举个例子:

  • 在一般的情况下,一个已确定的销售订单必须生成一张发货单。

  • 只是在确认发货单已付款的前提下,才会开出运送清单。

工作流使用图表描述这些交互,一个或几个工作流相关到对象。工作流是非必须的;一些对象就没有工作流。

下面的工作流用于销售订单的例子。在一定的条件下,它必须产生发货单和出货。

/doc_static/6.1/_images/arch_workflow_sale.png

在这张图表中节点代表着要做的动作。

  • 创建发票

  • 取消销售订单

  • 生成装货单, ...

上面的箭头代表条件:

  • 等待订单获得批准

  • 发票支付

  • 点击取消按钮,。。。

方格样式的节点代表其他的工作流:

  • 发票

  • 发货

OpenERP 模块描述文件 : __openerp__.py

一般模块

在已创建模块的目录下,你必须添加一个__openerp__.py文件。这个文件必须在Python的格式下,负责:

  1. 确定所需的XML文件,server在进行初始化时将从语法上分析这些文件。

  2. 1.确定已创建模块的依赖。

这个文件包括下面的值:

name

(英文)名称.

version

版本

description

描述

author

模块的作者

website

模块的网站

license

模块的授权协议(默认AGPL).

depends

列出该模块所依赖的其他模块,因为base模块包括模块必须的视图,报表等数据,所以base模块应该在其他所有模块的依赖中。

init_xml

List of .xml files to load when the server is launched with the "--init=module" argument. Filepaths must be relative to the directory where the module is. OpenERP XML File Format is detailed in this section.

update_xml

List of .xml files to load when the server is launched with the "--update=module" launched. Filepaths must be relative to the directory where the module is. OpenERP XML File Format is detailed in this section.

installable

True或是False,决定这个模块是否可安装。

active

True或是False(默认是False),决定这个模块在数据库创建时是否安装。

例子

以product模块中的__openerp__.py为例:

{
    "name" : "Products & Pricelists",
    "version" : "1.1",
    "author" : "Open",
    "category" : "Generic Modules/Inventory Control",
    "depends" : ["base", "account"],
    "init_xml" : [],
    "demo_xml" : ["product_demo.xml"],
    "update_xml" : ["product_data.xml","product_report.xml", "product_wizard.xml","product_view.xml", "pricelist_view.xml"],
    "installable": True,
    "active": True
}

放置在init_xml中的文件必须要么是和工作流相关,要么是安装软件时装载数据相关,或是和示例数据相关。

update_xml中的文件涉及到视图,报表和向导。

Profile 模块

一个profile的目的是在数据库创建后直接使用一组模块来初始化OpenERP。这个profile是一种特殊的模块,它不包含代码,只是 依赖于其他的模块 。

为了创建一个新的profile,你需要在server/addons里建一个新目录(可以给它取名为profile_modulename)。在新目录里放一个空的__init__.py文件和__openerp__.py。这个文件的结构是:

{
     "name":"''Name of the Profile'',
     "version":"''Version String''",
     "author":"''Author Name''",
     "category":"Profile",
     "depends":[''List of the modules to install with the profile''],
     "demo_xml":[],
     "update_xml":[],
     "active":False,
     "installable":True,
}
例子

我们以文件server/bin/addons/profile_manufacturing/__openerp__.py中的代码为例,它对应着OpenERP中的manufacturing industry profile。

{
     "name":"Manufacturing industry profile",
     "version":"1.1",
     "author":"Open",
     "category":"Profile",
     "depends":["mrp", "crm", "sale", "delivery"],
     "demo_xml":[],
     "update_xml":[],
     "active":False,
     "installable":True,
}

创建模块

Getting the skeleton directory

你可以从其他任意模块中复制文件__openerp__.py和__init__.py到一个新目录来创建一个新模块。

Ubuntu中一个例子:

$ cd ~/workspace/stable/stable_addons_5.0/
$ mkdir travel
$ sudo cp ~/workspace/stable/stable_addons_5.0/hr/__openerp__.py ~/workspace/stable/stable_addons_5.0/travel
sudo cp ~/workspace/stable/stable_addons_5.0/hr/__init__.py ~/workspace/stable/stable_addons_5.0/travel

你如果想修改这个目录,你需要设置自己的权限在这个目录上:

$ sudo chown -R `whoami` travel

进入新模块的目录,里面有个框架结构,你仍需要去更改模块定义里面的东西。

Changing the default definition

为了更改模块“travel”里面的默认设置,我们需要进入“travel”目录,编辑__openerp__.py文件。

$ cd travel
$ gedit __openerp__.py

文件里面类似下面:

{
  "name" : "Human Resources",
  "version" : "1.1",
  "author" : "Tiny",
  "category" : "Generic Modules/Human Resources",
  "website" : "http://www.openerp.com",
  "description": """
  Module for human resource management. You can manage:
  * Employees and hierarchies
  * Work hours sheets
  * Attendances and sign in/out system

  Different reports are also provided, mainly for attendance statistics.
  """,
  'author': 'Tiny',
  'website': 'http://www.openerp.com',
  'depends': ['base', 'process'],
  'init_xml': [],
  'update_xml': [
      'security/hr_security.xml',
      'security/ir.model.access.csv',
      'hr_view.xml',
      'hr_department_view.xml',
      'process/hr_process.xml'
  ],
  'demo_xml': ['hr_demo.xml', 'hr_department_demo.xml'],
  'installable': True,
  'active': False,
  'certificate': '0086710558965',
}

你可能会更改任意你觉得正确的东西,像下面这样:

{
    "name" : "Travel agency module",
    "version" : "1.1",
    "author" : "Tiny",
    "category" : "Generic Modules/Others",
    "website" : "http://www.openerp.com",
    "description": "A module to manage hotel bookings and a few other useful features.",
    "depends" : ["base"],
    "init_xml" : [],
    "update_xml" : ["travel_view.xml"],
    "active": True,
    "installable": True
}

注意“active”字段变成了true。

Changing the main module file

Now you need to update the travel.py script to suit the needs of your module. We suggest you follow the Flash tutorial for this or download the travel agency module from the 20 minutes tutorial page.

The documentation below is overlapping the two next step in this wiki tutorial,
so just consider them as a help and head towards the next two pages first...

travel.py文件应该看起来是这样:

from osv import osv, fields

class travel_hostel(osv.osv):
       _name = 'travel.hostel'
       _inherit = 'res.partner'
       _columns = {
       'rooms_id': fields.one2many('travel.room', 'hostel_id', 'Rooms'),
       'quality': fields.char('Quality', size=16),
       }
       _defaults = {
       }
travel_hostel()

理想情况下,你会拷贝那些代码几次来创建你所需要的实体(travel_airport, travel_room, travel_flight)。这就是你的对象的数据库结构,但是你真的不需要担心数据库端。当你安装模块时,这个文件会为你创建系统架构。

Customizing the view

接下来你可以编辑视图。编辑custom_view.xml文件,像这样:

<openerp>
<data>
    <record model="res.groups" id="group_compta_user">
            <field name="name">grcompta</field>
    </record>
    <record model="res.groups" id="group_compta_admin">
            <field name="name">grcomptaadmin</field>
    </record>
    <menuitem name="Administration" groups="admin,grcomptaadmin"
                    icon="terp-stock" id="menu_admin_compta"/>
</data>
</openerp>

就像你看到的,这是个accounting系统的例子。

定义视图就是定义访问你的模块时的用户界面。这里定义的这些字段已经是一个完整的界面。然而,由于做这个的复杂性,我们建议,再一次,从链接http://www.openerp.com/download/modules/5.0/下载travel agent模块。

下次你可以使用其他的文件来定义不同的视图,并且在你的basic/admin视图中分开它们。

Action creation

Linking events to action

可用类型的事件是:

  • client_print_multi (print from a list or form)

  • client_action_multi (action from a list or form)

  • tree_but_open (double click on the item of a tree, like the menu)

  • tree_but_action (action on the items of a tree)

从事件到动作的映射是:

<record model="ir.values" id="ir_open_journal_period">
    <field name="key2">tree_but_open</field>
    <field name="model">account.journal.period</field>
    <field name="name">Open Journal</field>
    <field name="value" eval="'ir.actions.wizard,%d'%action_move_journal_line_form_select"/>
    <field name="object" eval="True"/>
</record>

如果你双击journal/period (object: account.journal.period),将会打开一个选中的向导(id=”action_move_journal_line_form_select”).

只是当用户点击特定的对象时,你可以使用res_id字段来允许这个动作。

<record model="ir.values" id="ir_open_journal_period">
    <field name="key2">tree_but_open</field>
    <field name="model">account.journal.period</field>
    <field name="name">Open Journal</field>
    <field name="value" eval="'ir.actions.wizard,%d'%action_move_journal_line_form_select"/>
    <field name="res_id" eval="3"/>
    <field name="object" eval="True"/>
</record>

当用户点击account.journal.period n°3时,这个动作将会触发。

当你声明向导,报表或是菜单时,ir.values的创建会自动由下面的标签完成:

  • <wizard... />

  • <menuitem... />

  • <report... />

所以一般不需要自己加映射。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值