(一)数据模型

部分表名称

res_users 用户
res_groups 用户组(角色)
res_lang 语言
res_partner 供应商/客户/联系人
res_font 字体
res_company 公司
res_bank 银行
res_country 国家
res_country_state 州/省
res_currency 货币
res_currency_rate 汇率
ir_ui_menu 菜单 
ir_act_window 菜单动作
ir_act_window_view 菜单动作与视图的对应关系
ir_ui_view 视图
ir_cron 计划的动作
wkf 工作流
wkf_activity 活动
wkf_transition 迁移
sale_order 报价单/销售订单
sale_order_line 销售订单明细行
purchase_order 询价单/采购订单
purchase_order_line 采购订单明细行
product_category 产品分类
product_product 产品
product_template 产品信息
product_uom 计量单位
product_pricelist 价格表
product_pricelist_version 价格表版本
product_price_type 计价类型
stock_warehouse 仓库
stock_location 库位
stock_picking 分拣单(出库/入库/内部移动)
stock_move 库存移动(移库单)
stock_quant 库存分析
stock_inventory 库存盘点
stock_inventory_line 库存盘点明细
stock_production_lot 序列号(产品批次)
stock_warehouse_orderpoint 再订货规则
procurement_rule 补货规则(拉式流)
procurement_order 补货单
stock_location_route 路线
account_asset_category 固定资产类别
account_asset_asset 固定资产
account_account 会计科目
account_account_type 科目类型
account_tax 税
account_fiscalyear 会计年度
account_period 会计期间
account_invoice 发票
account_invoice_line 发票明细
account_move 会计凭证(会计分录)
account_move_line 会计凭证明细
account_voucher 记账凭证(收付款凭证)
account_voucher_line 记账明细
account_journal 凭证类型

创建数据模型 =========

  # -*- coding: utf-8 -*- 
  from odoo import models, fields 
  class TodoTask(models.Model): 
   _name = 'todo.task' 
   _description = 'To-do Task'
   name = fields.Char('Description', required=True) 
   is_done = fields.Boolean('Done?') 
   active = fields.Boolean('Active?', default=True) 

测试 =========

# -*- coding: utf-8 -*-
from odoo.tests.common import TransactionCase
class TestTodo(TransactionCase):
def test_create(self):
    "Create a simple Todo"
    Todo = self.env['todo.task']
    task = Todo.create({'name': 'Test Task'})
    self.assertEqual(task.is_done, False)

执行 $ ./odoo-bin -d todo -i todo_app –test-enable进行测试

下面动作的测试函数

   # def test_create(self):
        # ...
        # Test Toggle Done
        task.do_toggle_done()
        self.assertTrue(task.is_done)
        # Test Clear Done
        Todo.do_clear_done()
        self.assertFalse(task.active)

测试安全访问权限

  #class TestTodo(TransactionCase):
        def setUp(self,* args,** kwargs):
            result = super(TestTodo,self).setUp(* args,\
            ** kwargs)
            user_demo = self.env.ref('base.user_demo')
            self.env = self.env(user = user_demo)
            return result

向模型添加字段

类继承

# -*- coding: utf-8 -*-
  from odoo import models, fields, api
  class TodoTask(models.Model):
  _inherit = 'todo.task'
  user_id = fields.Many2one('res.users', 'Responsible')
  date_deadline = fields.Date('Deadline')

接下来在model文件夹里面还需要创建一个__init__.py文件,代码如下:

#-*- coding:utf-8 -*-
#!/usr/bin/env python
from . import todo_task

通过继承方式也可以对父类的字段进行属性修改,它是通过向子类添加和父类具有相同名称的字段来完成的,但是只能对字段进行属性的设置。 举例,如果我们要给当前例子的父类的name field添加一个帮助信息,我们可以在子类的todo_task.py文件里面类的成员添加如下代码: name = fields.Char(help="What needs to be done?") 注意:这个name是父类的属性

继承也在业务逻辑级别起作用。添加新方法很简单:只需在继承类中声明新的函数。
要扩展或更改现有逻辑,可以通过声明具有完全相同名称的方法来覆盖相应的方法。新方法将替换前一个方法,它也可以只是扩展继承类的代码,使用Python的super()方法来调用父方法。然后,可以在调用super()方法之前和之后,在原有逻辑周围添加新逻辑。

 from odoo.exceptions import ValidationError  
 # ...
 # class TodoTask(models.Model):
 # ...
 @api.one
 def do_toggle_done(self):
   if self.user_id != self.env.user:
     raise Exception('Only the responsible can do this!')
   else:
     return super(TodoTask, self).do_toggle_done()

原型继承复制特征

from odoo import models 
class TodoTask(models.Model): 
_name = 'todo.task' 
_inherit = 'mail.thread'

使用委托继承嵌入模型

from odoo import models, fields 
class User(models.Model): 
_name = 'res.users' 
_inherits = {'res.partner': 'partner_id'} 
partner_id = fields.Many2one('res.partner')

请注意,使用委托继承,字段是继承的,但方法不是。

(一)视图

添加菜单

<?xml version="1.0"?> 
<odoo> 
 <act_window id="action_todo_task" 
   name="To-do Task" 
   res_model="todo.task" 
   view_mode="tree,form" /> 
 <menuitem id="menu_todo_task" 
   name="Todos" 
   action="action_todo_task" /> 
</odoo> 

修改菜单和操作记录

   < ! — — 修改菜单项-->
   <record id="todo_app.menu_todo_task" model="ir.ui.menu">
       <field name="name">My To-Do</field>
   </record>
   <record model="ir.actions.act_window"
    id="todo_app.action_todo_task">
       <field name="context">
          {'search_default_filter_my_tasks': True}
       </field>
   </record>   

(一)视图 =========

<?xml version="1.0"?> 
<odoo> 
 <record id="view_form_todo_task" model="ir.ui.view"> 
   <field name="name">To-do Task Form</field> 
   <field name="model">todo.task</field> 
   <field name="arch" type="xml"> 
   <form>
       <header>
           <button name="do_toggle_done" type="object" string="Toggle Done" class="oe_highlight" />
           <button name="do_clear_done" type="object" string="Clear All Done" />
       </header>
       <sheet>
           <group name="group_top">
               <group name="group_left">
                   <field name="name"/>
               </group>
               <group name="group_right">
                   <field name="is_done"/>
                   <field name="active" readonly="1" />
               </group>
           </group>
       </sheet>
   </form>
   </field> 
 </record> 
</odoo>

列表视图(tree)

<record id="view_tree_todo_task" model="ir.ui.view"> 
 <field name="name">To-do Task Tree</field> 
 <field name="model">todo.task</field> 
 <field name="arch" type="xml"> 
   <tree decoration-muted="is_done==True"> 
     <field name="name"/> 
     <field name="is_done"/> 
   </tree> 
 </field> 
</record>

搜索视图

<record id="view_filter_todo_task" model="ir.ui.view"> 
 <field name="name">To-do Task Filter</field> 
 <field name="model">todo.task</field> 
 <field name="arch" type="xml"> 
   <search> 
     <field name="name"/> 
     <filter string="Not Done" 
       domain="[('is_done','=',False)]"/> 
     <filter string="Done" 
       domain="[('is_done','!=',False)]"/> 
   </search> 
 </field> 
</record> 

扩展视图

   <record id="view_form_todo_task_inherited" model="ir.ui.view">
       <field name="name">Todo Task form - User extension</field>
       <field name="model">todo.task</field>
       <field name="inherit_id" ref="todo_app.view_form_todo_task"/>
       <field name="arch" type="xml">
           <field name="name" position="after">
               <field name="user_id" />
           </field>
           <field name="is_done" position="before">
               <field name="date_deadline" />
           </field>
           <field name="active" position="attributes">
               <attribute name="invisible">1</attribute>
           </field>
       </field>
    </record> 

下面是一个写在arch中的实现在is_done字段之前添加date_deadline字段的具体例子:

    <xpath expr="//field[@name='is_done']" position="before">
       <field name="date_deadline" />
   </xpath> 

快捷方式

    <field name="is_done" position="before">
      <field name="date_deadline" />
   </field> 

after:将内容添加到父元素之中,匹配的节点之后。
before:添加内容在匹配节点之前。
inside(默认值):匹配节点内的追加内容。
replace:替换匹配的节点。如果使用空内容,它将删除该匹配的元素。从Odoo 10开始,它还允许用其他标记包装一个元素,通过在内容中使用$0来表示被替换的元素。
attributes:修改匹配元素的XML属性。在元素内容使用<attribute name =“attr-name”>实现给属性name设置新属性值attr-name。

扩展树视图

   <record id="view_tree_todo_task_inherited" model="ir.ui.view">
       <field name="name">Todo Task tree - User extension</field>
       <field name="model">todo.task</field>
       <field name="inherit_id" ref="todo_app.view_tree_todo_task"/>
       <field name="arch" type="xml">
            <field name="name" position="after"> 
                <field name="user_id" />
            </field>
       </field>
    </record>

扩展搜索视图

   <record id="view_filter_todo_task_inherited" model="ir.ui.view">
       <field name="name">Todo Task tree - User extension</field>
       <field name="model">todo.task</field>
       <field name="inherit_id" ref="todo_app.view_filter_todo_task"/>
       <field name="arch" type="xml">
           <field name="name" position="after">
               <field name="user_id" />
               <filter name="filter_my_tasks" string="My Tasks" domain="[('user_id','in',[uid,False])]" />
               <filter name="filter_not_assigned" string="Not Assigned" domain="[('user_id','=',False)]" />
           </field> 
       </field>
    </record> 

(三)业务逻辑

from odoo import models, fields, api
@api.multi 
def do_toggle_done(self): 
   for task in self: 
       task.is_done = not task.is_done 
   return True

通常表单按钮只能对选定的记录起作用,但在这种情况下,我们希望它也对除当前记录之外的记录起作用,所以使用@api.model装饰符

from odoo import models, fields, api
@api.model 
def do_clear_done(self): 
   dones = self.search([('is_done', '=', True)]) 
   dones.write({'active': False}) 
   return True

写入方法同时对记录集的所有元素设置值。 要写入的值使用字典进行描述。 在这里使用write比遍历记录集更有效率,以便逐一为每个记录集赋值。

访问控制安全

security/ir.model.access.csv。添加以下内容︰

id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
acess_todo_task_group_user,todo.task.user,model_todo_task,base.group_user,1,1,1,1

Row-level访问规则

security/todo_access_rules.xml

<?xml version="1.0" encoding="utf-8"?>
<odoo>
    
        <record id="todo_task_user_rule" model="ir.rule">
            <field name="name">ToDo Tasks only for owner</field>
            <field name="model_id" ref="model_todo_task"/>
            <field name="domain_force">[('create_uid','=',user.id)]</field>
            <field name="groups" eval="[(4,ref('base.group_user'))]"/>
        </record>
    
</odoo>

小心noupdate =”1”属性。这意味着此数据在模块的升级时将不会更新。这就使它能够进行定制,因为后面模块的升级不会破坏用户进行的更改。但请注意,在开发的时候也会这样,所以在开发的时候你可以设置noupdate =”0” ,直到你对你的数据文件满意为止。
在groups字段,你还会发现一个特殊的表达式。它是一个一对多的关系字段,他们有特殊的操作语法。在这种情况下, (4,x) 元组指要追加 x 记录,这里 的x 是关联的员工组,用base.group_user标识的。
修改安全记录规则
覆盖todo_app.todo_task_user_rule以将domain_force字段修改为新值

 <?xml version =“1.0”encoding =“utf-8”?>
   <odoo>
    <data noupdate =“1”>
        <record id =“todo_app.todo_task_per_user_rule”model =“ir.rule”>
           <field name =“name”>所有者和关注者的ToDo任务</ field>
           <field name =“model_id”ref =“model_todo_task”/>
           <field name =“groups”eval =“[(4,ref('base.group_user'))]”/>
            <field name =“domain_force”>['|',('user_id','in',[user.id,False]),('message_follower_ids','in',[user.partner_id.id])]  </field>
        </ record>
    </ data>
  </ odoo>

  本人的更多原创文章请加入个人微信公众号。