odoo框架开发汇总

本文详细介绍了如何在Odoo中创建模块、后端返回视图的两种方式、瞬态视图的应用、视图继承、搜索与分组、字段与按钮布局、权限控制的_fields_view_get、文件转附件、邮件通知、Kanban布局、序列号生成、计算字段搜索、多线程与线程池、HTML链接设计,以及接口设计的实例。

1.创建模块命令

    示例:D:\env\python\python.exe D:\odoo-14\server\odoo-bin scafflod module_name D:\odoo-  14\server\addons

2.后端返回视图

   写法1: 找视图id,返回动作窗口字典,方法与视图示例如下:    

def open_tree_or_form(self):
    res_ids = self.env['qc.dmr'].sudo().search([('name', '=', '203382')])
    tree_view_id = self.env.ref('xunde_quality_contorl.qc_dmr_tree_view')
    form_view_id = self.env.ref('xunde_quality_control.qc_dmr_form_view')
    if len(res_ids) == 1:
       return {
            'name' _(质检),
            'view_mode': 'form',
            'res_model': 'qc.dmr',
            'views': [[form_view_id.id, 'form']],
            'type': 'ir.actions.act_window',
            'res_id': dmr_ids.id,
            'target': 'current',
            'context': {}
       }
    elif len(res_ids) > 1:
       return {
            'name' _(质检),
            'view_mode': 'tree',
            'res_model': 'qc.dmr',
            'views': [(tree_view_id.id, 'tree'), (form_view_id.id, 'form')],
            'type': 'ir.actions.act_window',
            'domain': [('id', 'in', res_ids.ids)],
            'target': 'current',
            'context': {}
       }
    else:
        return {'type': 'ir.actions.act_window_close'}
<!-- 以button方式触发方法1 -->
<header>
    <button string="打开视图" name="open_tree_or_form" type="object" class='btn-primary'                   
        groups="xunde_account.group_apply_sale"/>
</header>

<!-- 以button方式触发方法2 -->
<sheet>
     <div class="oe_button_box" name="button_box">
          <button type="object" name="open_tree_or_form" class="oe_stat_button" icon="fa-            
                  pencil-square-o">
                 <field name="invoice_count" widget="statinfo" string="Paypal发票"/>
          </button>
     </div>
</sheet>
<...>

写法2:找特定action的id,并在方法里返回,示例代码如下:

def open_tree_or_form(self):
    ...
    action = self.env['ir.actions.act_window']._for_xml_id(f"account.{action.name}")
    action['context'] = {}
    action['domain'] = [(...)]
    action['name'] = _("...")
    action['views'] = [[False, 'form']]
    ...
    return action

3. 瞬态视图

   用途:可以用于审批驳回功能,可以用于导入文件向导, 一般是在视图添加按钮,通过按钮打开瞬态视图,瞬态视图模型,一般有个default_get函数和关联主表的字段,示例代码如下:

<!-- 打开瞬态视图按钮 -->
<header>
    <button name='%(action_open_reject_form)d' type='action' string='驳回' class='btn-        
            warning' />
</header>

<!-- 瞬态模型视图 -->
<record>
    ...
    <footer>
       <button name='action_reject' type='object' class='btn-primary' />
       <button special='cancel' string='关闭' class='btn-primary' />
    </footer>
</record>
# 瞬态模型示例
from odoo import models, fields, api, _
from odoo.exceptions import UserError


class TransientModelExample(models.TransientModel):
     _name = 'transientmodel.example'
     _description = '瞬态模型示例'
    
     def default_get(self, fields_list):
         ....
     
     origin_id = fields.Many2one(...)

     def action_reject(self):
         ...

 4.视图继承

   利用xpath语法,对原有视图进行修改,不建议过多继承视图,因为会导致视图继承混乱,后期不好维护。

<!-- xpath语法示例 -->
<xpath expr='//field[@name='field_name']' position='after'>
    <field ... />
</path>

<xpath expr='...' position='attributes'>
   <attribute name='invisible'>1</attribute>
</path>

<xpath expr='//notebook/page[1]' position='before'>
   <page .../>
</path>

5.搜索设置默认分组

 需求:打开tree视图,就有默认分组,修改搜索视图和客户端动作的上下文,示例代码如下:

<!-- 搜索视图 -->
<record id='search_view_template' model='ir.ui.view'>
    <field name='' />
    <filter string='' name='' domain='' />
    <separator/>
    <group expand='0' string='group by'>
       <filter string='客户' name='by_partner_id' domain='[]' 
               context='{'group_by': 'name'}' />
    </group>
    <searchpanel>
       <field name='' select='multi' string='fa-cutlery' icon='' color='#875A7B' 
              enable_counters='1' />
    </searchpanel>
</record>

<!-- 动作 -->
<record id='action_template' model='ir.actions.act_window'>
    <field name='name'>...</field>
    <field name='model'>...</field>
    <field name='view_mode'>tree,form</field>
    <field name='view_ids' eval='[(5,0,0),
           (0,0,{'view_mode': 'tree', 'view_id': ref('tree_id')}),
           (0,0,{'view_mode': 'form', 'view_id': ref('form_id')})] />
    <field name='search_view_id' ref='search_id' />
    <field name='context'>{'search_default_by_parent_id':1}</field>
</record>

6. 字段与按钮在一行

   实现字段与按钮在一行,代码如下:

<labe for='field_name' />
<div class='o_row'>
   <field ... />
   <button ... />
</div>

7._fields_view_get函数

       该函数是视图加载时会自动调用的函数,使用场景示例:根据权限组动态控制字段的属性,包括可见性、只读性等,示例代码如下:

@api.model
def _fields_view_get(self, view_id=None, view_type='form', toolbar=False, submenu=False):
    result = super(XdBusinessExample,self)._fields_view_get(view_id=view_id,
                                                            view_type=view_type,
                                                            toolbar=toolbar, 
                                                            submenu=submenu)
    if view_type == 'form':
        if self.env.user.has_group('xd.business.purchase_group'):
           doc = etree.XML(result.get('arch', ''))
           page_purchase = doc.xpath('page[@name='purchase']')
           for i in page_purchase:
               i.set('invisible', '1')
           result['arch'] = etree.tostring(doc)
    return result
 

8. binary转附件

   需求:将明细中的bianry文件全部转化为附件存储,示例代码如下:

def binary_to_attachments(self):
    documemnts = self.env['related.documents'].search([('assmenbly_id', '=',         
                                                       self.assembly_id.id)])
    files = []
    for doc in documents:
        attachment = self.env['ir.attacnment'].create({'datas': doc.file, 'name': 
                                                        doc.filename})
        files.appned((4, attachment.id))
    self.attachment_ids = files
     

9. search_read函数

       search_read函数是再打开tree、看板视图时自动触发的函数,可以重写该方法,以控制要显示的记录,示例代码如下:

@api.model
def search_read(self, domian=None, fields=None, offset=0, limit=80, order=None):
    sql = ''' select row_number() over() as id,
                     model_name,
                     user_id,
                     count(*) as wait_approval_count,
                     string_agg(res_id || '', ',') as res_ids,
                     state     
              from wait_approval wher user_id = %s and state = '%s'
              group by model_name, user_id, state''' %(self.env.user.id, state)
   self.env.cr.excute(sql)
   records = self.env.cr.fetchall()
   vals = []
   for r in records:
       vals.append({'model_name': r[1], 'user_id': r[2]})
   res.create(vals)
   domain = [('id', 'in', res.ids)]
   return super(XdSearchExample, self).search_read(domain=domain, fields=fields, 
                                                   offset=offset, limit=limit,
                                                   order=order)

10. 邮件通知

        odoo框架自带的邮件通知模块mail.message ,示例代码如下:

def mail_message_example(self):
    ...
    user_id = self.env['res.users'].search([('name', '=', 'xxx')], limit=1)
    self.env['mail.message'].create({
        'subject': '通知:xxx',
        'model': xxx,
        'res_id': self.id,
        'record_name': xxx,
        'body': u'有单据%s需要您审批' % sheet_name,
        'partner_ids': [(6, 0, usre_id.partner_id.id)], # 收件人
        'notification_ids': [(5, 0, 0), (0, 0, {'res_partner_id': user_id.partner_id.id,
                            'notification_type': 'inbox'})],
        'subtype_id': self.env['ir.model.data'].xmlid_to_res_id('mail.mt_commet'),
        'message_type': 'notification',
        'author_id': self.env.user.partner_id.id,
        'replay_to': False,
        'email_from': False,
        })
    ...

11. kanban

    kanban写法示例如下:

<?xml version="1.0"?>
<kanban default_group_by="stage_id" class="o_kanban_small_column o_opportunity_kanban" 
        on_create="quick_create" quick_create_view="crm.quick_create_opportunity_form" 
        archivable="false" sample="1">
           <field name="stage_id" options="{&quot;group_by_tooltip&quot;: 
                 {&quot;requirements&quot;: &quot;Description&quot;}}"/>
           <field name="color"/>
           <field name="priority"/>
           <field name="expected_revenue"/>
           <field name="kanban_state"/>
           <field name="activity_date_deadline"/>
           <field name="user_email"/>
           <field name="user_id"/>
           <field name="partner_id"/>
           <field name="activity_summary"/>
           <field name="active"/>
           <field name="company_currency"/>
           <field name="activity_state"/>
           <field name="activity_ids"/>
           <progressbar field="activity_state" colors="{&quot;planned&quot;: 
                        &quot;success&quot;, &quot;today&quot;: &quot;warning&quot;, 
                        &quot;overdue&quot;: &quot;danger&quot;}" 
                        sum_field="expected_revenue" help="This bar allows to filter the 
                        opportunities based on scheduled activities."/>
           <templates>
                 <t t-name="kanban-box">
                    <div t-attf-class="#{!selection_mode ? 
                         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" data-display="static" href="#" aria- 
                               label="Dropdown menu" title="Dropdown menu">
                               <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="o_kanban_record_title">
                                 <strong><field name="name"/></strong>
                            </div>
                            <div class="o_kanban_record_subtitle">
                                 <t t-if="record.expected_revenue.raw_value">
                                    <field name="expected_revenue" widget="monetary" 
                                        options="{'currency_field': 
                                        'company_currency'}"/>
                                        <span t-if="record.recurring_revenue and 
                                              record.recurring_revenue.raw_value"> + 
                                        </span>
                                  </t>
                                  <t t-if="record.recurring_revenue and 
                                       record.recurring_revenue.raw_value">
                                       <field name="recurring_revenue" 
                                              widget="monetary" options=" 
                                              {'currency_field': 
                                              'company_currency'}"/>
                                       <field name="recurring_plan"/>
                                   </t>
                            </div>
                            <div>
                                  <span t-if="record.partner_id.value" t- 
                                        esc="record.partner_id.value"/>
                            </div>
                            <div>
                                  <field name="tag_ids" widget="many2many_tags" options=" 
                                         {'color_field': 'color'}"/>
                            </div>
                            <div class="o_kanban_record_bottom">
                                 <div class="oe_kanban_bottom_left">
                                      <field name="priority" widget="priority" 
                                             groups="base.group_user"/>
                                      <field name="activity_ids" 
                                             widget="kanban_activity"/>
                                 </div>
                                      <div class="oe_kanban_bottom_right">
                                           <field name="user_id" 
                                           widget="many2one_avatar_user"/>
                                      </div>
                                 </div>
                            </div>
                            <div class="oe_clear"/>
                         </div>
                  </t>
           </templates>
</kanban>
            

 

12. 自动生成序列号

  利用ir.sequence模型实现,示例代码如下:

...
  name = fields.Char(string='单号', default='New')
...
@api.model
def create(self, vals):
    if vals.get('name', 'New') == 'New':
       vals['name'] = self.env['ir.sequence'].next_by_code('customer.complaint.sheet') or 
                      '/'
    return super(XdSequenceExapmle, self).create(vals)
<record id='xxx' model='ir.sequence'>
     <field name='name'>xxx</field>
     <field name='code'>customer.complaint.sheet</field>
     <field name='prefix'>CCS%(year)s%(month)s%(day)s</field>
     <field name='padding'>3</field>
     <field name='company_id' eval='False' />
</record>

13. 计算字段可搜索 

      通常情况下,计算字段如果不存储在数据库中,是无法搜索的,但是可以指定字段的search属性,实现可搜索,示例代码如下;

...
saleman = fields.Many2one('res.users', compute='xxx', string='业务员', 
                          search='_search_saleman_example)
...
def xxx(self):
   todo
...
def _search_saleman_example(self, operator, value):
    ids = []
    mrps = self.env['mrp'].search([])
    users = self.env['res.users'].search([('name', '=', value)]
    for r in mrps:
       if r.saleman.id in users.ids:
            ids.append(r.id)
    if ids:
       return [('id', 'in', tuple(ids))]
    return [('id', '=', '0')]
    

14.线程与线程池 

在导入导出大批量数据时,使用多线程模型,会明显加快速度,使用示例代码如下:

线程:

...
max_connections = 10
pool_sema = threading.BoundedSemaphore(max_connections)
for i in range(4):
    pool_sema.acquire()
    thread = threading.Thread(target=self.supplier_process, args=(arg1, arg2...))
    thread.daemon = True
    thread.start()
    thread.join()
...
def supplier_process(self,args,**kwargs):
    pass

线程池:

...
max_connections = 10
pool_sema = threading.BoundedSemaphore(max_connections)
with ThreadPoolExecutor(max_workers=5) as pool:
    for i in range(4):
        pool_sema.acquire()
        thread = pool.submit(self.supplier_process, (pool_sema ,b), kwargs={'id': 1})
        result = thread.result()
...
def supplier_process(self, *args, **kwargs):
    ...
    pool_sema = arg[0][0]
    ...
    pool_sema.release()

15.Html链接 

可以做一个订单的外部链接,示例代码如下:

...
order_link = fields.Html(string='订单链接')
...
@api.onchange('xxx)
def _compute_order_link(self):
    self.ensure_one()
    href = '/mail/view?model=%s&res_id=%s' %('sale.order', rec.order_id.id)
    rec.order_link = '<a href="%s" target="_blank">%s</a>' %(href, rec.order_id.name)
       

16. 接口设计

odoo可以提供对外的接口,以实现与其它系统的互联互通,示例代码如下:

class Main(http.Controller):
     @http.route('/xunde_bids/update/<db>', type='json', methods=['POST'], auth="none", 
                 csrf=False)
     def update_bids(self, db=None, **post):
         if not db and request.session.db and http.db_filter([request.session.db]):
            db = request.session.db
         if not db:
            db = db_monodb(request.httprequest)
         if not db:
            raise exceptions.UserError(_('d参数不能为空'))
         request.session.db = db
         result = False
         with registry(db).cursor() as cr:
              env = Environment(cr, SUPERUSER_ID, {})
              payload = json.loads(request.httprequest.data) or {}
              type = payload.get('type', [])
              if type:
                 raise exceptions.ValidationError(_('api失效!'))
              data = payload.get('data', [])
              result = env['purchase.requisition.line']._update_data(data)
        return {'success': result}  # result为True or False

    

1 ERP 简介 1 1.1 Odoo 历史 1 1.2 ERPⅡ 或商业智能化 2 1.2.1 什么是商业智能 4 2 Odoo 框架简介 7 2.1 python 模块分析 8 2.2 python2 还是 python3 8 3 Odoo 的安装和配置 9 3.1 PostgreSQL 数据库 10 3.2 Ubuntu14.04 下可能缺失的软件包 11 3.3 网页显示 node.js 方面 11 3.4 其他问题 12 3.5 通过命令行运行时的配置 12 3.5.1 –xmlrpc-port=8888 12 3.5.2 –addons-path=addons 12 3.5.3 数据库的一些配置 13 3.5.4 –save 13 3.6 将安装环境封装起来 13 3.7 文档编译 14 4 初入 Odoo 17 4.1 管理数据库 17 4.2 登录界面 18 4.3 Administrator 首选项 19 4.4 导入一个翻译 20 4.5 新的 Demo 用户 20 4.6 模块管理 21 4.7 修改公司信息 21 4.8 打开技术特性支持之后 22 4.9 进销存和财务系统的抽象讨论 22 4.9.1 以采购部门为例 23 4.10 安装和配置模块 24 5 创建自己的模块 27 5.1 快速生成模块骨架 27 5.1.1 python 模块的 init 文件 28 5.1.2 作为 Odoo 模块的说明文件 29 5.2 安装自定义模块 32 5.2.1 模块文件夹管理 32 5.3 一个简单的演示模块 33 5.3.1 controllers 33 5.3.2 views 33 5.3.3 models 35 5.3.4 security 37 5.3.5 美化网页 38 5.4 加分项: 通过 pgadmin3 来查看数据库 39 5.4.1 安装 39 5.4.2 连接服务器 39 5.4.3 图形化查询 40 6 Odoo 开发基础: 请假模块第一谈 43 6.1 纯理论讨论 43 6.2 定义模型 45 6.3 加入菜单 46 6.3.1 act_window 的属性 48 6.3.2 menuitem 的属性 48 6.4 视图优化 48 6.4.1 修改 tree 视图 49 6.4.2 修改 form 视图 49 6.5 完整的 views.xml 51 6.6 给模块加个图标 53 7 Odoo 开发基础: 工作计划模块第一谈 55 7.1 数据访问权限管理 61 7.1.1 access rule 62 7.1.2 record rule 62 8 扩展现有模块-继承机制 65 8.1 给模块增加 field 65 8.2 修改已有的 field 66 8.3 重载原模型的方法 66 8.3.1 什么是 Recordset 67 8.3.2 Odoo 里面的 domain 语法 68 8.3.3 recordset 的 search 方法 69 8.4 视图 xml 文件的继承式修改 70 8.4.1 视图元素添加 71 8.4.2 原视图元素属性修改 71 8.5 多态继承 72 8.6 修改其他数据文件 73 8.6.1 删除记录 73 8.6.2 更新数据 73 8.7 委托继承 74 9 理解模型内的数据文件 75 9.1 理解外部 id 75 9.2 使用外部 id 77 9.3 导出或导入数据文件 77 9.4 快捷输入标签 78 9.5 用 field 标签设置值 78 9.5.1 eval 语法 78 9.5.2 ref 属性 79 9.5.3 One2many 和 Many2many 的 eval 赋值 79 10 Odoo 开发基础: 请假模块第二谈 81 10.1 本例涉及到的数据库表格简介 89 10.2 工作流概念入门 89 10.2.1 定义工作流对象 90 10.2.2 创建节点 91 10.2.3 创建连接 91 11 Odoo 模型层详解 93 11.1 _name 93 11.2 各个表头属性 93 11.3 name 字段 94 11.4 具体模型的数据 94 11.5 模型间的关系 95 11.6 工作流 95 12 Odoo 视图层详解 97 13 附录 99 13.1 Odoo 里老的 API 99 13.2 PostgreSQL 数据库命令行操作 99 13.2.1 命令行数据库备份 99 13.3 反向代理 (reverse proxy) 99 13.3.1 安装 ngnix 软件 100 13.3.2 强制 https 连接 102 13.3.3 nginx 优化 102 13.3.4 轮询机制 102 13.4 跟踪项目源码初始化进程 102 13.4.1 base 模块 104 13.4.2 web 模块 105 13.4.3 web_kanban 模块 105 13.5 配置会计科目 105 13.5.1 配置会计科目类型 105 13.5.2 配置会计科目 106 13.6 分录 106 13.7 新建业务伙伴 106 13.7.1 新建业务伙伴标签 106 13.7.2 新建客户 106 13.8 创建新的产品 107 13.9 设置会计年度 107 13.10向供应商下单 107 13.11会计学入门 107 13.11.1财务报表 108 13.11.2原始凭证 108 13.11.3账户 108 13.11.4分类帐 109 13.11.5会计科目表 109 13.11.6报告期间 110 13.12参考资料 110
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值