odoo中各字段的含义和用法

视频教程

# _*_ coding:utf-8 _*_
# author: zhy

import uuid
from itertools import groupby
from datetime import datetime, timedelta
from werkzeug.urls import url_encode
from odoo import api, fields, models, _
from odoo.exceptions import UserError, AccessError
from odoo.osv import expression
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT
from odoo.tools.misc import formatLang


class zhy_sale(models.Model):
    _name = "zhy.sale"
    _description = u"销售订单"

    # 隐藏创建人创建日期字段
    _log_access = False

    name = fields.Char(string=u'单据编号', default=lambda self: 'New')
    active = fields.Boolean(default=True, string=u'是否归档', track_visibility='onchange')
    sequence = fields.Integer(string=u'排序', default=10)
    # require=True表示的是必填
    price_unit = fields.Float(string=u'单价', require=True, default=0.0, digits=(10,2))
    note = fields.Text(string=u'备注', track_visibility='onchange')
    content = fields.Html(string=u'正文', strip_style=True)
    # track_visibility='onchange'表示的是会在更改信息的时候,message会提示相关的更改信息
    # 这个要和下面message社交中的应用联合使用
    delivary_date = fields.Date(string=u'发货日期', track_visibility='onchange')
    # default=fields.Datetime.now表示的是获取当前日期
    date_order = fields.Datetime(string=u'单据日期', required=True, readonly=True, index=True, copy=False, default=fields.Datetime.now)
    priority = fields.Selection([
        ('0', '低'),
        ('1', '正常'),
        ('2', '高'),
    ], default='0', index=True, string=u'优先级')
    # res.users表示的是关联的当前用户的表, default表示的是创建的时候默认成当前用户
    # lambda self: self.env.user中user表示的是当前的用户, 如果是想关联其他的用户,可以找个这个用户的id,然后再设置中 default=id即可
    # xml中(ir.actions.act_window)定义<field name="context">{"default_user_id":7}</field>
    user_id = fields.Many2one('res.users', string=u'负责人', index=True, track_visibility='onchange',
                                default=lambda self: self.env.user)
    # 关联字段,一对多的关联 第一个位置表示的是关联的表的名称,第二个字段是表中的字段名称 auto_join表示是否在搜索字段时,生成连接
    order_line = fields.One2many('zhy.sale.line', 'order_id', string=u'订单明细行', copy=True, auto_join=True)
    # 多对多的关系 a_b_rel  a表示的是表名,b表示的是关联字段的表名 a_id b_id可以自己定义 一般是关联表的名称,分别表示的是关联表的名称
    # some_ids = fields.Many2many('models', 'a_b_rel', 'a_id', 'b_id')
    # res.users表和zhy.sale关联的数据放在zhy_sale_res_users_rel中
    users_ids = fields.Many2many('res.users', 'zhy_sale_res_users_rel', 'sale_id', 'user_id', string=u'业务员')

    @api.model
    def _select_objects(self):
        records = self.env['ir.model'].search([])
        return [(record.model, record.name) for record in records] + [('','')]
    # 可以关联所有的字段
    id_object = fields.Reference(string=u'关联', selection='_select_objects')


class zhy_sale_line(models.Model):
    _name = "zhy.sale.line"
    _description = u"销售订单"

    name = fields.Char(string=u'产品名称')
    order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)

Python2.7中, string中文前要加u

  • odoo视图定义xml
<?xml version='1.0' encoding='utf-8'?>
<odoo>
    <!--菜单定义-->
    <!--sequence表示的是优先级,数值越小,优先级越高-->
    <!--销售一级菜单要和内在的对象(models中的zhy.sale)关联之后才能显示到odoo页面中-->
    <menuitem id="menu_2" name="销售" sequence="12" />
    <menuitem id="menu_2_0" name="CRM" sequence="1" parent="menu_2" />
    <menuitem id="menu_2_1" name="销售" sequence="2" parent="menu_2" />
    <menuitem id="menu_2_99" name="常用报表" sequence="99" parent="menu_2" />
    <menuitem id="menu_2_100" name="设置" sequence="100" parent="menu_2" />

</odoo>

  • odoo中基本视图定义(pycharm中快捷键)
<?xml version='1.0' encoding='utf-8'?>
<odoo>
    <record id="odoo_form_zhy_sale_views" model="ir.ui.view">
        <field name="name">zhy.sale.form</field>
        <field name="model">zhy.sale</field>
        <field name="arch" type="xml">
            <form string="表单">
                <sheet>
                    <div class="oe_button_box" name="button_box">
                    </div>
                    <group>
                        <group>
                            <field name="name"/>
                        </group>
                        <group>
                        </group>
                    </group>
                </sheet>
            </form>
        </field>
    </record>

    <record id="odoo_tree_zhy_sale_views" model="ir.ui.view" >
        <field name="name">zhy.sale.tree</field>
        <field name="model">zhy.sale</field>
        <field name="priority">1</field>
        <field name="arch" type="xml">
            <tree string="列表">
                <field name="name"/>
            </tree>
        </field>
    </record>

    <record id="odoo_action_zhy_sale_views" model="ir.actions.act_window">
        <field name="name">销售模块</field>
        <field name="type">ir.actions.act_window</field>
        <field name="res_model">zhy.sale</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form</field>
        <field name="help" type="html">
            <p class="oe_view_nocontent_create">
                Create the first course
            <p></p>
            </p>
        </field>
    </record>
    
    <!--odoo中的一级标签, 如果没有自定义name,会使用action中的name的名称-->
    <menuitem action="odoo_action_zhy_sale_views" name="zhy_销售" id="menu_zhy_sale_views" sequence="0" />

</odoo>

  • 添加字段之后的xml文件解析
<?xml version='1.0' encoding='utf-8'?>
<odoo>
    <record id="odoo_form_zhy_sale_views" model="ir.ui.view">
        <field name="name">zhy.sale.form</field>
        <field name="model">zhy.sale</field>
        <field name="arch" type="xml">
            <form string="表单">
                <sheet>
                    <div class="oe_button_box" name="button_box">
                    </div>
                    <!--一个大的group中包含两个小的group,在视图中显示的是两列数据-->
                    <group>
                        <group>
                            <field name="name"/>
                            <!--如果想要更改字段, 可以在name后面添加string, xml视图中的优先级是要高于.py文件中的优先级的-->
                            <!--在.py文件中readonly=True设置为只读状态, 在视图xml中 定义readonly='1'表示的是只读状态-->
                            <!--当点击active字段的时候, 金额设置为必填字段-->
                            <field name="price_unit" string="金额" attrs='{"invisible": [("active", "=", True)]}'/>
                            <!--many2many_tags表示是many2many字段中一个挂件, 具体的效果见项目-->
                            <field name="users_ids" widget="many2many_tags"/>
                        </group>
                        <group>
                            <field name="date_order"/>
                            <!--required="1"表示的是这个字段是必填的.相反是0;一般是在视图中定义, 视图中定义的级别是高于.py文件中字段定义的;-->
                            <field name="delivary_date"/>
                            <field name="priority"/>
                            <!--在列表中是关联的res.user表, 是一个many2one字段, 是一个下拉框, 我们可以选择创建用户-->
                            <field name="user_id"/>
                            <!--使用的是Reference, 可以关联所有的字段-->
                            <field name="id_object"/>
                            <!--是一个boolean, 默认为false-->
                            <field name="active"/>
                            <!--integer 整数类型, 只能输入一个整数-->
                            <!--invisible='1'设置为隐藏状态,后端.py文件中是没有这个属性的-->
                            <!--attrs='{"invisible": [("active", "=", True)]}', 作用是, 当我们点击一个字段的时候,另外一个字段为显示,隐藏,必填等状态 下面attrs中active表示的是我们点击的字段, invisible表示的字段的隐藏属性.-->
                            <field name="sequence" attrs='{"invisible": [("active", "=", True)]}'/>
                        </group>
                        <!--一个notebook,具体的见视图界面 colspan表示指定的是4行-->
                        <notebook colspan="4">
                            <page string="明细" autofocus="autofocus">
                                <!--多对一和一对多相关联, 这个是一个one2many的字段, 会和另外一张表中的一个many2one的表相关联的-->
                                <field name="order_line"/>
                                <!--我们可以通过制定widget="html", 来改变原有设置的视图形状-->
                                <field name="note"/>
                            </page>
                            <page string="正文">
                                <field name="content"/>
                            </page>
                        </notebook>
                    </group>
                </sheet>
            </form>
        </field>
    </record>

    <record id="odoo_tree_zhy_sale_views" model="ir.ui.view" >
        <field name="name">zhy.sale.tree</field>
        <field name="model">zhy.sale</field>
        <field name="priority">1</field>
        <field name="arch" type="xml">
            <tree string="列表">
                <field name="name"/>
            </tree>
        </field>
    </record>

    <record id="odoo_action_zhy_sale_views" model="ir.actions.act_window">
        <field name="name">销售模块</field>
        <field name="type">ir.actions.act_window</field>
        <field name="res_model">zhy.sale</field>
        <field name="view_type">form</field>
        <field name="view_mode">tree,form</field>
        <field name="help" type="html">
            <p class="oe_view_nocontent_create">
                Create the first course
            <p></p>
            </p>
        </field>
    </record>

    <menuitem action="odoo_action_zhy_sale_views" name="zhy_销售" id="menu_zhy_sale_views" sequence="0" />

</odoo>


  • 实际的项目中xml文件各字段的解析

<?xml version="1.0" encoding="UTF-8"?>
<odoo>
    <data>
        <!--制作表单视图,显示课程的名称和描述字段--> 
        <!--id的定义规则,如果是tree from视图view, 一般是xxx_view, 如果是视图actions, 一般是xxx_action-->
        <record model="ir.ui.view" id="course_form_view">
            <field name="name">course.form</field>                      <!--表单名-->
            <field name="model">openacademy.course</field>              <!-- 数据模型, 模块.模型-->
            <field name="arch" type="xml">                               <!--重点, 视图类型定义-->
                <form string="Course Form">                             <!--课程视图-->
                    <sheet>
                        <group>                                          <!--表单中第一行-->
                            <field name="name"/>                        <!--对应models中的字段名-->
                            <field name="responsible_id"/>
                        </group>
                         <!--notebook结构元素,讲描述字段放在一个选项卡中,然后再添加选项卡放置其他字段  tab 分页效果-->
                        <notebook>
                            <page string="描述">
                                <field name="description"/>
                            </page>
                            <page string="授课">
                                <field name="session_ids">
                                    <tree string="授课注册">
                                        <field name="name"/>
                                        <field name="instructor_id"/>
                                    </tree>
                                </field>
                            </page>
                            <page string="关于">
                                这是一个关于notebook的案例
                            </page>
                        </notebook>
                    </sheet>
                </form>
            </field>
        </record>

        <record model="ir.ui.view" id="course_search_view">
            <field name="name">course.search</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <search>
                    <field name="name"/>
                    <field name="description"/>
                </search>
            </field>
        </record>

        <!-- override the automatically generated list view for courses -->
        <record model="ir.ui.view" id="course_tree_view">
            <field name="name">course.tree</field>
            <field name="model">openacademy.course</field>
            <field name="arch" type="xml">
                <tree string="课程详情">
                    <field name="name"/>
                    <!--responsible 责任人-->
                    <field name="responsible_id"/>
                </tree>
            </field>
        </record>

        <!-- window action -->
        <!--
            The following tag is an action definition for a "window action",
            that is an action opening a view or a set of views
        -->
        <!--动作和菜单中, model为固定属性值, ir.actions.act_window  id是唯一的-->
        <record model="ir.actions.act_window" id="course_list_action">
            <field name="name">Courses</field>               <!--视图动作的名称-->
            <field name="res_model">openacademy.course</field>  <!--模块对象 视图要操作哪个数据模型 模块.数据模型-->
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
            <field name="context" eval="{'search_default_my_courses': 1}"/>
            <field name="help" type="html">
                <p class="oe_view_nocontent_create">Create the first course
                </p>
            </field>
        </record>

        <!-- top level menu: no parent -->      <!--顶部菜单-->
        <menuitem id="main_openacademy_menu" name="公开教程"/>
        <!-- A first level in the left side menu is needed
             before using action= attribute -->
        <menuitem id="openacademy_menu" name="公开教程"
                  parent="main_openacademy_menu"/>      <!-- 一级菜单 -->
        <!-- the following menuitem should appear *after*
             its parent openacademy_menu and *after* its
             action course_list_action -->
        <menuitem id="courses_menu" name="课程" parent="openacademy_menu"
                  action="course_list_action"/>
        <!-- Full id location:
             action="openacademy.course_list_action"
             It is not required when it is the same module -->
        <!-- id不能重复,一般是xxx_menu name表示是视图显示的名称, parent表示是关联的父类 -->
        <!-- 二级菜单 action表示 指定菜单的响应动作,依次用什么视图来操作数据模型 就是我点击这个按钮的时候跳转到哪个列表--> 
        <!--注意: action必须先于menu的连接使用定义, 数据文件在载入时顺序地执行, 所以动作的ID必须首先存在于数据库中才能使用.-->

        <!-- session form view -->
        <record model="ir.ui.view" id="session_form_view">
            <field name="name">session.form</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <form string="Session Form">
                    <sheet>
                        <group>
                            <group string="综合的">
                                <field name="course_id"/>
                                <field name="name"/>
                                <field name="instructor_id"/>
                                <field name="active"/>
                            </group>
                            <group string="时间表">
                                <field name="start_date"/>
                                <field name="duration"/>
                                <field name="seats"/>
                                <field name="taken_seats" widget="progressbar"/>
                            </group>
                        </group>
                        <label for="attendee_ids"/>
                        <field name="attendee_ids"/>
                    </sheet>
                </form>
            </field>
        </record>

        <!-- session tree/list view -->
        <record model="ir.ui.view" id="session_tree_view">
            <field name="name">session.tree</field>
            <field name="model">openacademy.session</field>
            <field name="arch" type="xml">
                <tree string="Session Tree" >
                    <field name="name"/>
                    <field name="course_id"/>
                    <field name="taken_seats" widget="progressbar"/>
                </tree>
            </field>
        </record>

        <record model="ir.actions.act_window" id="session_list_action">
            <field name="name">Sessions</field>
            <field name="res_model">openacademy.session</field>
            <field name="view_type">form</field>
            <field name="view_mode">tree,form</field>
        </record>

        <!--model="ir.actions.act_window" 表示包含链接视图 -->
        <menuitem id="session_menu" name="授课"
                  parent="openacademy_menu"
                  action="session_list_action"/>    <!-- 一级菜单  -->

    </data>
</odoo>


表单sequence
  • 作用:生成一个带时间的单据编号
  • 用法
    • 创建一个新的data文件夹, 文件夹中创建一个新的xml文件
<?xml version='1.0' encoding='utf-8'?>
<odoo>
    <record id="seq_zhy_sale_sequence" model="ir.sequence">
        <field name="name">zhy.sale</field>
        <field name="code">zhy.sale</field>
        <field name="prefix">A%(year)s%(month)s%(day)s</field>
        <field name="padding">5</field>
        <field name="company_id" eval="False" />
    </record>
    
</odoo>

# 从 ir.squence中取出self._name字段,然后给他生成一个编号
name = fields.Char(string=u'单据编号', default=lambda self: self.env['ir.sequence'].next_by_code(self._name) or 'New')

  • 一般这个单据编号是用在name,char字段中的.

表单日期
from datetime import datetime, timedelta
from odoo.tools import float_is_zero, float_compare, DEFAULT_SERVER_DATETIME_FORMAT

# 获取当前日期
default=fields.Datetime.now
date_order = fields.Datetime(string=u'单据日期', readonly=True, index=True, copy=False, default=fields.Datetime.now)

# 在当前日期上加上多少天(一般是单据日期和发货日期)
# days中的值可以是正值(向前加多少天),也可以是负值(向后减多少天)
default=lambda self: datetime.strptime(fields.Datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT) + timedelta(days=1)

widget="float_time"表示的是可以输入一个时间

hours = fields.Float(string=u’单价’, default=0.0, digits=(10, 2), readonly=False)


domain字段过滤
# 过滤字段, 可以让用户只能选择当前的用户
<field name="user_id" domain="[('id', '=', 1)]"/>

domain="[('id', '=', 1)]"


表单button
  • 在xml中添加
<!--在xml中添加header和fields-->
<header>
    <button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" />
    <button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" />
    <button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" />
    <button name="button_done" states="处理中" string="审核" type="object" class="highlight" />

    <field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
</header>

    state = fields.Selection([
            (u'草稿', u'草稿'),
            (u'处理中', u'处理中'),
            (u'已审核', u'已审核'),
        ], string='单据状态', readonly=True, copy=False, default=u'草稿', track_visibility='onchange')

    @api.multi
    def button_loading_draft(self):
        return self.write({'state': u'草稿'})

    @api.multi
    def button_done_draft(self):
        return self.write({'state': u'草稿'})

    @api.multi
    def button_loading(self):
        return self.write({'state': u'处理中'})

    @api.multi
    def button_done(self):
        return self.write({'state': u'已审核'})

    @api.multi
    def action_done(self):
        return self.write({'state': u'取消'})

注意: xml文件中button的name值要和.py文件中函数的名称相对应;

几种状态的对应,可以在视图是观察字段所对应的后端值来定


state 关联字段的只读状态
<!--具体的用法-->
<!--attrs="{'readonly': [('state', 'in', (u'处理中', u'已审核'))]}"-->
<!--一般是要结合name=state状态来定的-->
<record id="odoo_form_zhy_sale_views" model="ir.ui.view">
    <field name="name">zhy.sale.form</field>
    <field name="model">zhy.sale</field>
    <field name="arch" type="xml">
        <form string="表单">
            <header>
                <button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" />
                <button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" />
                <button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" />
                <button name="button_done" states="处理中" string="审核" type="object" class="highlight" />

                <field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
            </header>
            <sheet>
                <div class="oe_button_box" name="button_box">
                </div>
                <group>
                    <group>
                        <field name="name"/>
                        <field name="price_unit" string="金额" attrs="{'readonly': [('state', 'in', (u'处理中', u'已审核'))]}" />
                        <field name="users_ids" widget="many2many_tags" />
                    </group>
                    <group>
                        <field name="date_order"/>
                        <field name="delivary_date"/>
                        <field name="priority"/>
                        <field name="hours" widget="float_time"/>
                        <field name="user_id" />
                        <field name="id_object" />
                        <field name="active"/>
                        <field name="sequence" attrs='{"invisible": [("active", "=", True)]}'/>
                    </group>
                    <notebook colspan="4">
                        <page string="明细" autofocus="autofocus">
                            <field name="order_line"/>
                            <field name="note" widget="html"/>
                        </page>
                        <page string="正文">
                            <field name="content"/>
                        </page>
                    </notebook>
                </group>
            </sheet>
        </form>
    </field>
</record>

# 后端中添加只读状态
# 要结合后端中的state这个字段来进行设置的
price_unit = fields.Float(string=u'单价', default=0.0, digits=(10, 2),
    readonly=True, states={u'草稿': [('readonly', False)], u'处理中': [('readonly', False)]})

state = fields.Selection([
    (u'草稿', u'草稿'),
    (u'处理中', u'处理中'),
    (u'已审核', u'已审核'),
], string='单据状态', readonly=True, copy=False, default=u'草稿', track_visibility='onchange')

一般的情况下, xml的优先级都要高于.py文件中的优先级


权限管理security
  • 在目录下创建一个新的文件夹和文件security/security.xml
<!--在security.xml文件中定义-->
<!--一般id的名称要和关联的模块相对应-->
<!--权限设置之后可以在设置用户中看到-->
<?xml version='1.0' encoding='utf-8'?>
<odoo>
    <data>
        <record id="sale_groups" model="ir.module.category">
            <field name="name">销售z</field>
        </record>
        <record id="sale_users" model="res.groups">
            <field name="name">销售z-用户</field>
            <field name="category_id" ref="sale_groups" />
            <field name="users" eval="[(4, ref('base.user_root'))]" />
        </record>
        <record id="sale_manager" model="res.groups">
            <field name="name">销售z-经理</field>
            <field name="category_id" ref="sale_groups" />
            <field name="implied_ids" eval="[(4, ref('sale_users'))]" />
            <field name="users" eval="[(4, ref('base.user_root'))]" />
        </record>
    </data>
</odoo>

<!--给不用的用户设置不同的权限-->
<!--设置权限组, aa_zhy表示的是文件夹的名称, sale.users表示的是security.xml文件中设置的用户权限-->
<!--groups="aa_zhy.sale_users"-->
<header>
    <button name="button_loading_draft" states="处理中" string="重置草稿" type="object" class="highlight" groups="aa_zhy.sale_users" />
    <button name="button_done_draft" states="已审核" string="重置草稿" type="object" class="highlight" groups="aa_zhy.sale_manager" />
    <button name="button_loading" states="草稿" string="提交草稿" type="object" class="highlight" groups="aa_zhy.sale_users" />
    <button name="button_done" states="处理中" string="审核" type="object" class="highlight" groups="aa_zhy.sale_manager"/>

    <field name='state' widget='statusbar' statusbar_visible='草稿,处理中,已审核' />
</header>

在实际的项目中, 我们给用户设置了提交审核的权限之后,我们可以用另外的一个用户登录
去查看我们创建的模块,新创建的用户中,我们是没有办法看到我们创建的模块的,这个时候我们需要在管理员账户的设置群组中,找到我们谁知的权限管理模块, 然后进去,给我们的用户设置模块的访问权限即可.

  • 给某些字段设置权限
<!--设置权限组, aa_zhy表示的是文件夹的名称, sale_manager表示的是security.xml文件中设置的权限-->
<!--groups='aa_zhy.sale_manager'-->
price_unit = fields.Float(string=u'单价', default=0.0, digits=(10, 2), readonly=True, groups='aa_zhy.sale_manager')

同样,也可以把groups='aa_zhy.sale_manager'放到xml中也是可以的.

设置完成之后, 就可以用另外的一个账户登录,这个时候,就会发现, 不是管理员用户,就不能查看金额字段了。


表单message的使用
  • 主要是在社交方面的使用
  • 使用中我们会用到系统的mail模块,所以要先把mail模块集成到我们的depends继承中.
<!--在.py文件中,继承mail.thread-->
_inherit = ['mail.thread']

<div class="oe_chatter">
    <field name="message_follower_ids" widget="mail_followers" />
    <field name="message_ids" widget="mail_thread" />
</div>

  • track_visibility='onchange' 这个是结合message使用的, 是把更改的一些信息显示到下面的message中.
  • track_visibility='always' 用户不更改信息的是时候,message也会记录下这些信息.
<!--批量关注的两种方法-->
<!--users_ids是后端定义的多对多的关联字段; 8表示的是当前的这个用户的id-->
self.message_subscribe_user(users_ids=(self.user_id.id))
self.message_subscribe(partner_ids=[8])

<!--批量取消的两种方法-->
self.message_unsubscribe_user(users_ids=(self.user_id.id))
self.message_unsubscribe(partner_ids=[8])


表单onchange的使用
  • 省市区的三级联动
<!--主要是一个many2one字段-->
class res_city1(models.Model):
    _name = "res.city1"
    _description = u"省"

    name = fields.Char(string=u'省')

class res_city2(models.Model):
    _name = "res.city2"
    _description = u"市"

    name = fields.Char(string=u'市')
    city1_id = fields.Many2one('res.city1', string=u'明细', )

class res_city3(models.Model):
    _name = "res.city3"
    _description = u"区"

    name = fields.Char(string=u'区')
    city2_id = fields.Many2one('res.city2', string=u'明细', )

  • 省市区三级联动中提取字段
class zhy_sale_line(models.Model):
    _name = "zhy.sale.line"
    _description = u"销售订单"

    name = fields.Char(string=u'产品名称')
    order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)
    
    # 这个地方是定义三个多对一的id字段, 关联对应的表单,提取内容
    city1_id = fields.Many2one('res.city1', string=u'省')
    city2_id = fields.Many2one('res.city2', string=u'市')
    city3_id = fields.Many2one('res.city3', string=u'区')
    
    # onchang时间, 通过第三级(或则是最下级)一级一级向上查询
    @api.onchange('city3_id')
    def onchange_city3_id(self):
        values = {}
        if self.city3_id:
            # city2_id(市)表示的是通过三级的id,找到二级的id.(反向查找)
            values['city2_id'] = self.city3_id.city2_id.id
            # city1_id(省)表示的是通过三级区的id,找到二级市的id,再找到一级省的id.(反向查找)
            values['city1_id'] = self.city3_id.city2_id.city1_id.id
        self.update(values)


class res_city1(models.Model):
    _name = "res.city1"
    _description = u"省"

    name = fields.Char(string=u'省')


class res_city2(models.Model):
    _name = "res.city2"
    _description = u"市"

    name = fields.Char(string=u'市')
    city1_id = fields.Many2one('res.city1', string=u'明细', )


class res_city3(models.Model):
    _name = "res.city3"
    _description = u"区"

    name = fields.Char(string=u'区')
    city2_id = fields.Many2one('res.city2', string=u'明细', )

  • xml中视图展示
<!--把后端创建的字段,通过xml文件显示出来-->
<notebook colspan="4">
    <page string="明细" autofocus="autofocus">
        <field name="order_line">
            <!--editable='bottom'表示的是编译方式-->
            <tree string="列表" editable="bottom">
                <field name="city1_id" />
                <!--domain条件过滤 第一个city1_id表示的自己定义的关联字段 第二个city1_id表示的是上一级的id-->
                <!--作用是选择上级之后,下级自动做关联筛选-->
                <!--特别注意: 第二个city_id是不能够加分号的.-->
                <field name="city2_id" domain="[('city1_id', '=', city1_id)]" />
                <field name="city3_id" domain="[('city2_id', '=', city2_id)]" />
            </tree>
        </field>
        <field name="note" widget="html"/>
    </page>
    <page string="正文">
        <field name="content"/>
    </page>
</notebook>

表单中数量和金额的合计
# 新增数量,单价,和小计等字段.这些字段都是要float类型
# depends中依赖的是自己心定义的两个字段,如果这两个字段发生变化,则执行这个函数
@api.depends('product_uom_qty', 'price_unit')
def _compute_amout(self):
    for order in self:
        order.price_subtotal = order.product_uom_qty * order.price_unit

product_uom_qty = fields.Float(string=u'数量', required=True, default=1.0)
price_unit = fields.Float(string=u'单价', required=True, default=0.0, digits=(10, 2))
# compute是关联一个函数.我们在这个函数中可以写计算逻辑
price_subtotal = fields.Float(compute='_compute_amout', string=u'小计')


# 在其他的表中做关联和金额合计的时候
# 这个表中依赖的是外键表中的一个字段;通过外键表找到这个字段,然后对这个字段进行操作
@api.depends('order_line.price_subtotal')
def _compute_amount(self):
    for order in self:
        total = sum(line.price_subtotal for line in order.order_line)
        order.amount = total
        
amount = fields.Float(compute='_compute_amount', string=u'小计')
order_line = fields.One2many('zhy.sale.line', 'order_id', string=u'订单明细行', copy=True, auto_join=True)

表单unlink删除重写事件
  • odoo中已经做好了表单的删除功能, 我们只需要继承这个功能就好.
  • 我们做的这个案例中, 只有我们的表单处理草稿状态的时候,我们才能删除这个表单. 当我们的表单处理处理中或者审核状态的时候,我们是不能删除的.
# @api.multi表示的是可以选择多个 重写unlink()方法
# 当界面点击删除事件的时候会触发
@api.multi
def unlink(self):
    for order in self:
        # 判断当前的用户和表单的创建用户是否一致, 如果不一致不允许删除.
        if order.user_id:
            # user_id(字段名称)
            if order.user_id.id != self._uid:
                raise UserError(u'只能删除自己的单据')
        if order.state != u"草稿":
            raise UserError(u'只能删除草稿单据')
        # self.env['amos.workflow'].sudo.seach([('res_model', '=', self._name), ('res_id', '=', order.id)]).unlink()
        # self.env['if.message'].sudo.seach([('res_model', '=', self._name), ('res_id', '=', order.id)]).unlink()
    # 关联的是哪张表
    return super(zhy_sale, self).unlink
    
# 项目中是实际会用到的
@api.multi
def unlink(self):
    for order in self:
        if order.user_id:
            if order.user_id.id != self._uid:
                raise UserError(u'只能删除自己的单据aaaa')
        # 这个地方有问题, 当不能删除的时候, 注意下判断条件(可以使用debug),打印出判断条件对比下就可以了. 
        if order.state.encode('utf-8') != "草稿":
            print(order.state, type(order.state))
            raise UserError(u'只能删除草稿单据bbbb')
    return super(zhy_sale, self).unlink()

# 其他的写法
# @api.multi
# def unlink(self):
#     if any(order.state in (u'处理中', u'已审核') for order in self):
#         raise exceptions.UserError(u'只能删除草稿单据啊啊啊啊啊啊.')
#     super(zhy_sale, self).unlink()


表单create创建重写事件
  • 可以创建自己的公司的编号等
  • 可以修改字段的值
# vals表示的是界面传入进来的所有的值
# self._context表示的传入进来的上下文的值   
# 当创建的时候会触发
@api.model
def create(self, vals):
    # print(self._context, u'这是上下文')
    # print(vals, u'这是界面传入进来的值')
    if vals.get('name', 'New') == 'New':
        # 这个地方表示是判断传入进来的值中有没有company_id字段, 如果有的话, 设置一个单据编号
        if 'company_id' in vals:
            # with_context传上下文的 next_by_code下一个字段
            vals['name'] = self.env['ir.sequence'].with_context(force_company=vals['company_id']).next_by_code(self._name) or 'New'
        else:
            vals['name'] = self.env['ir.sequence'].next_by_code(self._name) or 'New'
    # 可以指定修改某个值
    vals['hours'] = 100.00
    line = super(zhy_sale, self).create(vals)
    return line
    
表单widget的使用
# odoo中常用的widget

widget="statusbar" 头部状态条标签
widget="email" 电子邮件地址标签
widget="selection" 下拉选择标签
widget="mail_followers" 关注者标签
widget="mail_thread" 消息标签
widget="progressbar" 进度条,按百分比标签
widget="one2many_list" 一对多列表标签
widget="many2many_tags" 多对多显示标签
widget="url" 网站链接标签
widget='image' 图片标签
widget="many2many_kanban" 看版标签
widget="handler" 触发标签
widget="radio" 单选标签
widget="char_domain" 字符域标签
widget="monetary" 价格(和精度位数相关)标签
widget="float_time" 单精度时间标签
widget="html" html相关标签
widget="pad" pad显示相关标签
widget="date" 日期标签
widget="monetary" 金额标签
widget='text' 文本标签
widget="sparkline_bar" 燃尽标签
widget="checkbox" 复选框标签
widget="reference" 关联标签

<field name="partner_type" nolabel="1" widget="selection" string="" \
attrs="{'required': [('payment_type', 'in', ('inbound', 'outbound'))], \
'invisible': [('payment_type', 'not in', ('inbound', 'outbound'))], \
'readonly': [('state', '!=', 'draft')]}"/>


表单write修改重写事件(时间的改写)
# 当修改表单中数据 的时候会被触发
@api.multi
def write(self, vals):
    print(vals, u'我是修改之后的值')
    if vals.has_key('price_unit'):
        print(vals.has_key('price_unit'), 1111111)
        if vals['price_unit'] > 100000:
            print(isinstance(vals['price_unit'], str))
            print(vals['price_unit'], type(vals['price_unit']))
            raise UserError(u'警告: 你没有权限修改')
    return super(zhy_sale, self).write(vals)


表单active(归档使用)
  • 当一件事情完结的时候, 点击归档,则这件事情就被隐藏掉了;当我们想再去查看的时候, 在筛选条件中是否归档选择为假即可.
<!--toggle_active是odoo底层的方法, 我们可以拦截odoo中的这个方法-->
<div class="oe_button_box" name="button_box">
    <button name="toggle_active" type="object" class="oe_stat_button" icon="fa-archive">
        <field name="active" widget="boolean_button" options='{"terminology": "archive"}' />
    </button>
</div>

<!--重写toggle_active方法来拦截, 之后我们可以自定义自己的方法-->
@api.multi
def toggle_active(self):
    for recode in self:
        print(recode, "我来了")
        recode.active = not recode.active


表单隐藏按钮

隐藏新建,隐藏修改,隐藏删除
create=“false”
edit=“false”
delete=“false”

<form string="表单" create="false" edit="false" delete="false"> 
</form>
或者
<form string="表单" create="0" edit="0" delete="0"> 
</form>

  • 想让哪部分按钮隐藏, 只需要把相应的代码中的字段设置为false或者0即可.

表单字段related
  • 一般是关联某个字段, 做实时同步等
# 两张变中的字段也是可以关联的
# 表zhy.sale中的字段
delivery_date = fields.Date(string=u'发货日期',track_visibility='onchange',)

# 表zhy.sale.line中的字段
# order_id是关联的其他表
order_id = fields.Many2one('zhy.sale', string=u'销售订单', require=True, ondelete='cascade', index=True, copy=False)
# related="表中外键表的字段" 一定要注意字段的类型Date一定要和关联的其他表的字段类型一致
delivery_date = fields.Date(related='order_id.delivery_date', string=u'发货日期', store=True)


api各个装饰器的区别
  • api.depends:当用到其他字段的时候需要用到depends(实时计算字段)
    • eg:金额的统计的时候, 小计需要关联其他的两个字段
  • api.onchange:实时更新用户界面,当用户在表单中更改某个字段的值时,其他相关字段可以在不需保存的情况下实时更新.
    • eg:城市的三级联动
  • odoo.api.model(method):在记录行方式下装饰一个内容不明确、但模型明确的方法
  • odoo.api.multi(method):在记录行方式下装饰一个对记录进行操作的方法
  • odoo.api.depends(*args):返回为compute方法指定依赖字段的装饰器,每个参数必须是字符串
  • odoo.api.constrains(*args):装饰一个约束检查方法,每个参数必须是需要检查的字段
    • 在检验失败时抛出ValidationError错误,且不支持关联字段检验
  • odoo.api.onchange(*args):返回一个监控指定字段的onchange方法的装饰器,每个参数必须是字段名称
    • 该函数可能会返回以数据字典形式组装的当前更改字段的domain表达式和一个警告消息,不支持关联字段处理
  • 12
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值