Odoo多公司指南

2023-11-11

启动环境

首先新建一个odoo12环境,并在应用列表中搜索sales并安装。

开启多公司功能

打开settings页面,然后点击点击General Settings, 接着再勾选Multi-companies之后点击保存。页面会重新加载,再次回到设定那栏,选择如图中蓝色方框中的companies进入公司列表,再创建一个名为company sub的公司,并把下面选项中的parent字段的值设置成系统默认自带的YourCompany,这代表company subYourCompany的子公司,在多公司规则中,子公司记录之前是独立的,同时父公司可以看到所有子公司的记录。

alt

再次刷新页面,此时页面的右上角便会出现一个可点击的下拉列表允许你切换当前使用的公司。

多公司的基本操作

多公司就如字面意思一样简单直了,所以展示起来也很直接。

单据操作

打开sales模块,默认进来的时候可以发现模块自带了一些demo数据,我们随便点击一个单据进去,由于我们开启了多公司功能并且是用Mitchell Admin登录,系统会为我们当前用户分配多公司权利(只有开启了多公司权利的用户才能使用多公司功能,稍后会讲解这个。),在Other Information中会出现Company字段,这允许我们通过设置这个字段来让单据数据归属到对应公司。

现在我们设置Company字段的值为我们刚创建的公司company sub,并回到列表视图,在右上角切换当前的公司为company sub,页面刷新好后我们便会发现刚刚编辑的单据出现在了company sub公司的列表视图中,并且我们再也看不到其他单据内容。

此时我们再新建一个单据,并随机填入数据,再通过右上角切换回YourCompany公司,我们会发现刚刚在company sub公司新建的单据也在列表中,这就是刚刚提到的,父公司可以看到子公司全部数据。

用户相关操作

Settings中打开用户列表,并点击我们当前的用户,可以看到视图中多了Multi Companies的操作项,其中Allowed Companies就是允许该用户能切换到哪些公司,Current Company则代表用户当前是归属到哪个公司。

alt

再往下拉点,在Extra Rights中还有一项与多公司有关的选项Multi Companies,也就是多公司权利,这是odoo中实际控制用户是否可以切换公司,是否在页面显示Company字段的重要选项。如果你勾选了Multi Companies,并且用户的Allowed Companies数量 > 1,那么即使不在settings中开启多公司功能,该用户也可以使用多公司功能。

那么读者可能就会好奇了,如果是这样,那么在settings中开启多公司功能有什么作用呢?它们之前的区别在于,如果设定中开启了多公司功能,那么全用户都拥有改多公司权利,可以查看到页面中Company相关字段,同时,除非Allowed Companies数量 = 1,右上角的多公司切换选项才会消失。如果不通过settings中开启多公司的话,那么可以在用户页面主动分配多公司权利和Allowed Companies,这样就可以只允许特定用户使用多公司的功能。需要注意的是单独Allowed Companies是没有效果的,需要同时勾选中Multi Companies

为自定义模块实现多公司功能

让项目支持多公司不算复杂,因为odoo已经为我们完成了大量基础代码,我们只要通过两个步骤,即可实现基本的多公司功能。

  1. 在自定的相关模型中新增company_id字段
    在所有需要多公司功能相关的字段中都需要加上以下示例代码
    1
    
    company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id)
    
    当然如果单据之前是存在明显关联的外键关系,比如订单和订单项,那么订单项定义该字段时可以通过related属性自动从单据身上获取。
  2. 增加ir.rule过滤规则
    security中新建xml,并定义相关过滤规则并引入,以下是相关的示例代码
    1
    2
    3
    4
    5
    6
    
    <record id="account_comp_rule" model="ir.rule">
         <field name="name">Account multi-company</field>
         <field name="model_id" ref="model_account_account"/>
         <field name="global" eval="True"/>
         <field name="domain_force">['|',('company_id','=',False),('company_id','child_of',[user.company_id.id])]</field>
     </record>
    
    其中domain_forceglobal不需要改懂,只要略微修改其余选项对应你的模型即可。

对模块下模型依次执行以上两个步骤,就基本实现多公司功能了。但是这并不意味着万事大吉了。
在上面的定义代码中我们显然可以知道创建记录的默认公司是用户当前的公司,那么在以下情景中就可能产生问题:

  1. 比如我们在A公司创建订单,那么在父公司B是可以查看并操作A公司的订单,如果此时在B公司中对A公司执行的订单生成送货单的操作,如果没相关处理,那么这个送货单的就Company_id字段的值就是B公司的了,这显然不是我们想要的。
  2. 模块中有相关cron时,基本执行cron的对应的用户是基本是超级管理员,那么cron处理的单据,也可能归属错误的公司。
  3. 有些程序员可能会写sql语句来提高查询性能,如果没注意处理多公司,也会导致只想要单个公司相关数据时,查询了多个公司的数据。

所以实行了多公司功能的模块,在相关的方法处理中需要更小心,一个比较好的解决方案就是生产相关单据时,主动带上当前单据的Company_id,在写sql相关操作时,也主动获取当前用户公司(可能还有子公司),去查询。

odoo与多公司相关的实现

之前的段落已经介绍了Odoo多公司的基本逻辑与操作,接下来的我再介绍的一部分多公司内部代码实现,以便更进一步了解多公司和方便定制扩展。

权限组相关

odoo/addons/base/security/base_groups.xml中有个一个group_multi_company的权限组,也就是多公司权利。

1
2
3
<record model="res.groups" id="group_multi_company">
    <field name="name">Multi Companies</field>
</record>

它的定义十分简单,但是作用很大,多公司相关功能都是围绕着这个权限组做的。简单的就是属于这个权限组,就可以可以使用多公司的全部功能。
比如许多视图文件中定义Company_id字段都是用如下方式:

1
<field name="company_id" groups="base.group_multi_company"/>

也就是只有拥有多公司权利的用户才可以查看并编辑这个字段,不属于这个权限组的用户看不到这个字段。

odoo也在代码中为这个权限组做了一些自动加入取消的处理:

1
2
3
4
5
6
group_multi_company = self.env.ref('base.group_multi_company', False)
if group_multi_company and 'company_ids' in values:
    if len(user.company_ids) <= 1 and user.id in group_multi_company.users.ids:
        user.write({'groups_id': [(3, group_multi_company.id)]})
    elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids:
        user.write({'groups_id': [(4, group_multi_company.id)]})

这也是上文中提到的settingsAllowed Companies之间关系的逻辑,这里就不多做介绍了。

session相关

odoo也在web/models/ir_http.py文件中重写了session_info方法,这个方法中提供了用户的公司列表,以及是否显示切换公司的下拉框参数:
我们可以在后台以request.session或者前台编写js模块时以var session = require('web.session');方式来接触session以便获取相关变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
def session_info(self):
        user = request.env.user
        display_switch_company_menu = user.has_group('base.group_multi_company') and len(user.company_ids) > 1
        version_info = odoo.service.common.exp_version()
        return {
            "session_id": request.session.sid,
            "uid": request.session.uid,
            "is_system": user._is_system() if request.session.uid else False,
            "is_admin": user._is_admin() if request.session.uid else False,
            "user_context": request.session.get_context() if request.session.uid else {},
            ....
            "company_id": user.company_id.id if request.session.uid else None,
            "partner_id": user.partner_id.id if request.session.uid and user.partner_id else None,
            "user_companies": {'current_company': (user.company_id.id, user.company_id.name), 'allowed_companies': [(comp.id, comp.name) for comp in user.company_ids]} if display_switch_company_menu else False,
            ....
        }

前台显示相关

web/static/src/js/widgets/switch_company_menu.js文件包含了多公司下拉框的处理逻辑,主要逻辑也很简单:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
tart: function () {
        var companiesList = '';
        if (this.isMobile) {
            companiesList = '<li class="bg-info">' +
                _t('Tap on the list to change company') + '</li>';
        }
        else {
            this.$('.oe_topbar_name').text(session.user_companies.current_company[1]);
        }
        _.each(session.user_companies.allowed_companies, function(company) {
            var a = '';
            if (company[0] === session.user_companies.current_company[0]) {
                a = '<i class="fa fa-check mr8"></i>';
            } else {
                a = '<span style="margin-right: 24px;"/>';
            }
            companiesList += '<a role="menuitem" href="#" class="dropdown-item" data-menu="company" data-company-id="' +
                            company[0] + '">' + a + company[1] + '</a>';
        });
        this.$('.dropdown-menu').html(companiesList);
        return this._super();
    },

    //--------------------------------------------------------------------------
    // Handlers
    //--------------------------------------------------------------------------

    /**
     * @private
     * @param {MouseEvent} ev
     */
    _onClick: function (ev) {
        ev.preventDefault();
        var companyID = $(ev.currentTarget).data('company-id');
        this._rpc({
            model: 'res.users',
            method: 'write',
            args: [[session.uid], {'company_id': companyID}],
        })
        .then(function() {
            location.reload();
        });
    },

start方法中根据刚刚提到的session获取公司列表并渲染,_onClick则表视用户选了某个公司就把对应公司id写入到用户的company_id字段中,由此也可以用户当前使用的公司是通过company_id来判断的。

这里渲染出的下拉项是列表的,有的用户可能是希望可以呈现出公司之间上下级关系,那么这时可以重写后台session_info的方法,再配合第三方组件库如ztree重写前台中的js逻辑实现。

ir.rule相关

ir.rule规则,除了我在前文提到在xml中定义的相关数据,odoo还特意写了一个特殊的处理。
base/models/res_users.pyUsers模型中的write方法,有这么一小段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
if 'company_id' in values:
    for user in self:
        # if partner is global we keep it that way
        if user.partner_id.company_id and user.partner_id.company_id.id != values['company_id']:
            user.partner_id.write({'company_id': user.company_id.id})
    # clear default ir values when company changes
    self.env['ir.default'].clear_caches()

# clear caches linked to the users
if 'groups_id' in values:
    self.env['ir.model.access'].call_cache_clearing_methods()
    self.env['ir.rule'].clear_caches()
    self.has_group.clear_cache(self)

这里可能是因为ir.rule相关权限的domain_forece只会计算一次,然后会缓存下来,所以当相关值变更的时候需要执行self.env['ir.default'].clear_caches()方法。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Odoo多公司指南 的相关文章

随机推荐

  • 第十二讲:强化学习(Reinforcement Learning)和控制(Control)

    这一章我们就要学习强化学习 reinforcement learning 和适应性控制 adaptive control 了 在监督学习 supervised learning 中 我们已经见过的一些算法 输出的标签类 y y y 都是在训
  • 【Linux】对Linux操作系统中进程的理解

    需要云服务器等云产品来学习Linux的同学可以移步 gt 腾讯云 lt gt 阿里云 lt gt 华为云 lt 官网 轻量型云服务器低至112元 年 新用户首次下单享超低折扣 目录 一 冯 诺依曼体系结构 硬件方面 举个例子加深理解 二 操
  • 如何使定时器1ms进入中断一次?

    STM32F103中APB1最大时钟频率72Mhz APB2最大时钟频率36Mhz 所以 1 把定时器设置自动重装载寄存器 ARR 的值为 1000 2 设置时钟预分频器为 71 3 则驱动计数器的时钟 CK CNT CK INT 71 1
  • SpringBoot实现RESTful服务接口

    在实际使用SpringBoot开发项目的过程中 后端服务经常会遇到跨模块访问的需求 这就需要我们的服务接口有同一的开发规范 这样在跨模块调用时才能更方便 高效 而Spring Boot也为我们创建Web服务提供了非常强大的组件化支持 简单而
  • 华为OD机试 - 拔河比赛(Python)

    题目描述 公司最近准备进行拔河比赛 需要在全部员工中进行挑选 选拔的规则如下 按照身高优先 体重次优先的方式准备比赛阵容 规定参赛的队伍派出10名选手 请实现一个选拔队员的小程序 输入为一个数组 记录了部门人员的身高 体重信息 如 身高 体
  • Javaweb

    1 Cookie对象 cookie是一种会话技术 它用于将会话过程中的数据保存到用户的浏览器中 从而使浏览器和服务器可以更好的地进行数据交互 实现获取Cookie信息并将当前时间发送给客户端 代码如下 protected void doGe
  • MultipartFile文件上传报错

    MultipartFile文件上传报错 Postman上传文件访问接口 直接报错了 报错如下 Failed to parse multipart servlet request nested exception is java io IOE
  • SonarQube SonarLint 安装使用教程

    目录 SonarQube 概念 什么是 SonarQube SonarQube 架构 SonaQuge 是如何工作的 现有问题 如何判断代码好坏 为什么要使用 SonarQube 代码可靠性 应用安全 技术债务 SonarQube 安装及配
  • sigmoid和softmax总结

    sigmoid函数 也叫逻辑斯谛函数 引用wiki百科的定义 A logistic function or logistic curve is a common S shape sigmoid curve 其实逻辑斯谛函数也就是经常说的si
  • Spring三级缓存解决循环依赖

    有两个Bean对象 A对象中有一个属性的B对象 B对象中有一个属性的A对象 他们都需要依赖注入 但是map单例池中都没有这个对象 正常情况下在进行注入时 如果map单例池中没有需要的Bean对象B就会去创建这个Bean对象B 但如果需要创建
  • onnxruntime和pytorch时间对比

    https github com microsoft onnxruntime issues 2404 https github com microsoft onnxruntime issues 1621 https github com m
  • ASCII、Unicode和UTF-8编码的区别

    归纳 编码 大小 支持语言 ASCII 1个字节 英文 Unicode 2个字节 生僻字4个 所有语言 UTF 8 1 6个字节 英文字母1个字节 汉字3个字节 生僻字4 6个字节 所有语言 具体解释 最早只有127个字母被编码到计算机里
  • jar包源码修改

    jar包源码修改 我相信大家在做项目的时候有时候看源码 或者想改源码代码的时候 一定是修改不了滴 今天在这里就告诉大家如何修改源码里面的代码并且从新打成jar包 一 反编译首先是要下个反编译工具 网上有很多啊Jadclipse XJad J
  • 精美博客源码

    这是一个精美的博客源码 原作者找不到了 因为现在跑路了 话不多说 上源码 https qsnb lanzouq com b020llczc 密码 0000 注意 本人非作者 发生一切法律问题不要找我 搭建教程 1 上传源码至文件管理器 2
  • Spring Boot + Vue的网上商城之商品管理

    Spring Boot Vue的网上商城之商品管理 在网上商城中 商品管理是一个非常重要的功能 它涉及到商品的添加 编辑 删除和展示等操作 本文将介绍如何使用Spring Boot和Vue来实现一个简单的商品管理系统 下面是一个实现Spri
  • 实战项目心得

    项目一 电商页面整站开发 HTML css整站开发 编写一个满足电商需求的各个页面 一 用div附以一些dd dt dl ul li的应用布局 整合css 二 用margin padding text indent border等距离进行计
  • 基于ELK的项目日志收集系统

    前言 Beats 是作为ELK技术栈前端数据收集平台的一个开源软件 Beats is the platform for building lightweight open source data shippers for many type
  • 旋转矩阵中6保6_双色球旋转矩阵公式中6保4的

    满意答案 liuerxing 推荐于 2017 10 10 采纳率 51 等级 12 已帮助 6602人 双色球旋转矩阵公式中6保4的选法共是38注 1 2 3 4 5 6 7 8 9 10 11 12 代表选的数字 1 6 8 9 10
  • 4种GC方法+分代回收+触发GC情况+内存申请过程

    引用计数 对象增加一个引用时 引用数 1 减少一个时 引用数 1 当进行垃圾回收时 只回收引用数为0的对象 面对互相引用无解 方法比较老 基本弃用 标记 清除 从根节点遍历标记对象 然后遍历整个堆 清除没有标记的对象 缺点 运行效率不高 产
  • Odoo多公司指南

    启动环境 首先新建一个odoo12环境 并在应用列表中搜索sales并安装 开启多公司功能 打开settings页面 然后点击点击General Settings 接着再勾选Multi companies之后点击保存 页面会重新加载 再次回