odoo 底层 用户界面详解(拆下作为一个权限申请模块功能了)

15 篇文章 4 订阅
15 篇文章 1 订阅
odoo 用户界面的详情本质上是一个 res.groups 群组的一个具象化,通过动态生成xml 视图文件来显示配置权限页面,个人感觉 odoo 的底层还是比较容易理解和好拆下来单独做功能的。

所见即所得!

# -*- coding: utf-8 -*-
import itertools
from odoo import models, fields, api, _

from lxml.builder import E
from itertools import chain, repeat
from lxml import etree

from odoo.tools import partition

def name_boolean_group(id):
    return 'in_group_' + str(id)


def name_selection_groups(ids):
    return 'sel_groups_' + '_'.join(str(it) for it in ids)


def is_boolean_group(name):
    return name.startswith('in_group_')


def is_selection_groups(name):
    return name.startswith('sel_groups_')


def is_reified_group(name):
    return is_boolean_group(name) or is_selection_groups(name)


def get_boolean_group(name):
    return int(name[9:])


def get_selection_groups(name):
    return [int(v) for v in name[11:].split('_')]


def parse_m2m(commands):
    "return a list of ids corresponding to a many2many value"
    ids = []
    for command in commands:
        if isinstance(command, (tuple, list)):
            if command[0] in (1, 4):
                ids.append(command[1])
            elif command[0] == 5:
                ids = []
            elif command[0] == 6:
                ids = list(command[2])
        else:
            ids.append(command)
    return ids

ACCESS_STATES = {
    u'审批中': [('readonly', True)],
    u'审批通过': [('readonly', True)],
}


class access_request(models.Model):
    _name = "access.request"
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _description = '权限申请'
    _check_worflow_auto = True


    def _default_groups(self):
        group_ids = self.env.user.groups_id.ids
        return [(4, rec) for rec in group_ids]

    def _default_department(self):
        department_ids = self.env.user.employee_ids
        name = ''
        for rec in department_ids:
            name += rec.department_id.name + '|'
        return name

    def _default_job(self):
        department_ids = self.env.user.employee_ids
        name = ''
        for rec in department_ids:
            if rec.job_id:
                name += rec.job_id.name + '|'
        return name

    name = fields.Char(string='单据编号', default=lambda self: self.env['ir.sequence'].next_by_code(self._name) or 'New')
    groups_id = fields.Many2many('res.groups', string='权限组',
                                 default=_default_groups, track_visibility='onchange',)
    user_id = fields.Many2one('res.users', default=lambda self: self.env.user, string="申请人")
    share = fields.Boolean(compute='_compute_share', compute_sudo=True, string='共享用户',)
    department_ids = fields.Char(string="所在部门", default=_default_department)
    job_ids = fields.Char(string='所在岗位', default=_default_job)
    state = fields.Selection([
        ('新建', '新建'),
        ('审批中', '审批中'),
        ('审批通过', '审批通过'),
    ], string="状态", default="新建", track_visibility='onchange',)

    @api.depends('groups_id')
    def _compute_share(self):
        for user in self:
            user.share = not user.has_group('base.group_user')
    category_id = fields.Many2many('res.partner.category', string='标签', index=True)

    def action_home(self):
        self.write({
            'state': '新建'
        })


    def button_a1(self, context=None):
        if not self:
            self = self.browse(context.get('active_id'))

        #::::::::创作并分配一个任务
        activity = {
            'type': '',
            'line': self,
            'user_id': self.user_id.id,
            'summary': '授权申请',
            'note': '请审批授权申请!',
            'id_object': '%s,%s' % (self._name, self.id),
            'ref_menu': 'Base.menu_0',  # 顶部菜单
        }
        self.env.user.add_mail_activity(activity)

        if self.state == '审批通过':
            self.user_id.groups_id = [(6, 0, self.groups_id.ids)]

    @api.model
    def create(self, values):
        values, body = self._remove_reified_groups(values)
        user = super(access_request, self).create(values)
        user.message_post(body=body, message_type='notification')
        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)]})
        return user

    def write(self, values):
        values, body = self._remove_reified_groups(values)
        res = super(access_request, self).write(values)
        self.message_post(body=body, message_type='notification')
        group_multi_company = self.env.ref('base.group_multi_company', False)
        if group_multi_company and 'company_ids' in values:
            for user in self:
                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)]})
        return res

    @api.model
    def new(self, values={}, origin=None, ref=None):
        values,body = self._remove_reified_groups(values)
        user = super().new(values=values, origin=origin, ref=ref)
        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.update({'groups_id': [(3, group_multi_company.id)]})
            elif len(user.company_ids) > 1 and user.id not in group_multi_company.users.ids:
                user.update({'groups_id': [(4, group_multi_company.id)]})
        return user

    def _remove_reified_groups(self, values):
        """ return `values` without reified group fields """
        add, rem = [], []
        values1 = {}

        for key, val in values.items():
            if is_boolean_group(key):
                (add if val else rem).append(get_boolean_group(key))
            elif is_selection_groups(key):
                rem += get_selection_groups(key)
                if val:
                    add.append(val)
            else:
                values1[key] = val
        body = ''
        if 'groups_id' not in values and (add or rem):
            # remove group ids in `rem` and add group ids in `add`
            values1['groups_id'] = list(itertools.chain(
                zip(repeat(3), rem),
                zip(repeat(4), add)
            ))

            if add != []:
                for rec in add:
                    group_name = self.env['res.groups'].sudo().browse(rec)
                    body += '修改权限组:%s, 为:%s<br>' % (group_name.display_name, group_name.name)
            else:
                for rec in rem:
                    group_id = self.env['res.groups'].sudo().browse(rec)
                    body += '修改权限组:%s, 为:%s<br>' % (group_id.display_name, 'False')
            # if body=='':
            #     body=None
            # print(body)
        return values1, body

    @api.model
    def default_get(self, fields):
        group_fields, fields = partition(is_reified_group, fields)
        fields1 = (fields + ['groups_id']) if group_fields else fields
        values = super(access_request, self).default_get(fields1)
        self._add_reified_groups(group_fields, values)
        return values

    def read(self, fields=None, load='_classic_read'):
        # determine whether reified groups fields are required, and which ones
        fields1 = fields or list(self.fields_get())
        group_fields, other_fields = partition(is_reified_group, fields1)

        # read regular fields (other_fields); add 'groups_id' if necessary
        drop_groups_id = False
        if group_fields and fields:
            if 'groups_id' not in other_fields:
                other_fields.append('groups_id')
                drop_groups_id = True
        else:
            other_fields = fields

        res = super(access_request, self).read(other_fields, load=load)

        # post-process result to add reified group fields
        if group_fields:
            for values in res:
                self._add_reified_groups(group_fields, values)
                if drop_groups_id:
                    values.pop('groups_id', None)
        return res

    def _add_reified_groups(self, fields, values):
        """ add the given reified group fields into `values` """
        gids = set(parse_m2m(values.get('groups_id') or []))
        for f in fields:
            if is_boolean_group(f):
                values[f] = get_boolean_group(f) in gids
            elif is_selection_groups(f):
                selected = [gid for gid in get_selection_groups(f) if gid in gids]
                # if 'Internal User' is in the group, this is the "User Type" group
                # and we need to show 'Internal User' selected, not Public/Portal.
                if self.env.ref('base.group_user').id in selected:
                    values[f] = self.env.ref('base.group_user').id
                else:
                    values[f] = selected and selected[-1] or False

    @api.model
    def fields_get(self, allfields=None, attributes=None):
        res = super(access_request, self).fields_get(allfields, attributes=attributes)
        # add reified groups fields
        for app, kind, gs, category_name in self.env['res.groups'].sudo().get_groups_by_application():
            if kind == 'selection':
                # 'User Type' should not be 'False'. A user is either 'employee', 'portal' or 'public' (required).
                selection_vals = [(False, '')]
                if app.xml_id == 'base.module_category_user_type':
                    selection_vals = []
                field_name = name_selection_groups(gs.ids)
                if allfields and field_name not in allfields:
                    continue
                # selection group field
                tips = ['%s: %s' % (g.name, g.comment) for g in gs if g.comment]
                res[field_name] = {
                    'type': 'selection',
                    'string': app.name or _('Other'),
                    'selection': selection_vals + [(g.id, g.name) for g in gs],
                    'help': '\n'.join(tips),
                    'exportable': False,
                    'selectable': False,
                    'track_visibility': 'onchange',
                    'states': ACCESS_STATES
                }
            else:
                # boolean group fields
                for g in gs:
                    field_name = name_boolean_group(g.id)
                    if allfields and field_name not in allfields:
                        continue
                    res[field_name] = {
                        'type': 'boolean',
                        'string': g.name,
                        'help': g.comment,
                        'exportable': False,
                        'selectable': False,
                        'track_visibility': 'onchange',
                        'states': ACCESS_STATES
                    }
        return res

    def action_pass(self):
        if self.groups_id:
            self.user_id.groups_id = [(4, rec) for rec in self.groups_id.ids]


class res_groups(models.Model):
    _inherit = 'res.groups'

    @api.model
    def create(self, values):
        user = super(res_groups, self).create(values)
        self._update_user_groups_view1()

        return user

    def write(self, values):
        res = super(res_groups, self).write(values)
        self._update_user_groups_view1()

        return res

    def unlink(self):
        res = super(res_groups, self).unlink()
        self._update_user_groups_view1()

        return res

    @api.model
    def _update_user_groups_view1(self):
        """ Modify the view with xmlid ``base.user_groups_view``, which inherits
            the user form view, and introduces the reified group fields.
        """

        # remove the language to avoid translations, it will be handled at the view level
        self = self.with_context(lang=None)

        # We have to try-catch this, because at first init the view does not
        # exist but we are already creating some basic groups.
        view = self.env.ref('access_request.access_request_user_groups_view', raise_if_not_found=False)
        if view and view.exists() and view._name == 'ir.ui.view':
            group_no_one = view.env.ref('base.group_no_one')
            group_employee = view.env.ref('base.group_user')
            xml1, xml2, xml3 = [], [], []
            xml_by_category = {}
            xml1.append(E.separator(string='User Type', colspan="2", groups='base.group_no_one'))

            user_type_field_name = ''
            user_type_readonly = str({})
            sorted_tuples = sorted(self.get_groups_by_application(),
                                   key=lambda t: t[0].xml_id != 'base.module_category_user_type')
            for app, kind, gs, category_name in sorted_tuples:  # we process the user type first
                attrs = {}
                # hide groups in categories 'Hidden' and 'Extra' (except for group_no_one)
                if app.xml_id in self._get_hidden_extra_categories():
                    attrs['groups'] = 'base.group_no_one'

                # User type (employee, portal or public) is a separated group. This is the only 'selection'
                # group of res.groups without implied groups (with each other).
                if app.xml_id == 'base.module_category_user_type':
                    # application name with a selection field
                    field_name = name_selection_groups(gs.ids)
                    user_type_field_name = field_name
                    user_type_readonly = str({'readonly': ['|', (user_type_field_name, '!=', group_employee.id),('state', '!=', '新建')]})
                    attrs['widget'] = 'radio'
                    attrs['groups'] = 'base.group_no_one'
                    xml1.append(E.field(name=field_name, **attrs, track_visibility='onchange'))
                    xml1.append(E.newline())

                elif kind == 'selection':
                    # application name with a selection field
                    field_name = name_selection_groups(gs.ids)
                    attrs['attrs'] = user_type_readonly
                    if category_name not in xml_by_category:
                        xml_by_category[category_name] = []
                        xml_by_category[category_name].append(E.newline())
                    xml_by_category[category_name].append(E.field(name=field_name, **attrs, track_visibility='onchange'))
                    xml_by_category[category_name].append(E.newline())

                else:
                    # application separator with boolean fields
                    app_name = app.name or 'Other'
                    xml3.append(E.separator(string=app_name, colspan="4", **attrs, track_visibility='onchange'))
                    attrs['attrs'] = user_type_readonly
                    for g in gs:
                        field_name = name_boolean_group(g.id)
                        if g == group_no_one:
                            # make the group_no_one invisible in the form view
                            xml3.append(E.field(name=field_name, invisible="1", **attrs, track_visibility='onchange'))
                        else:
                            xml3.append(E.field(name=field_name, **attrs, track_visibility='onchange'))

            xml3.append({'class': "o_label_nowrap"})
            if user_type_field_name:
                user_type_attrs = {'invisible': [(user_type_field_name, '!=', group_employee.id)]}
            else:
                user_type_attrs = {}

            for xml_cat in sorted(xml_by_category.keys(), key=lambda it: it[0]):
                xml_cat_name = xml_cat[1]
                master_category_name = (_(xml_cat_name))
                xml2.append(E.group(*(xml_by_category[xml_cat]), col="2", string=master_category_name))

            xml = E.field(
                E.group(*(xml1), col="2"),
                E.group(*(xml2), col="2", attrs=str(user_type_attrs)),
                E.group(*(xml3), col="4", attrs=str(user_type_attrs)), name="groups_id", position="replace")
            xml.addprevious(etree.Comment("GENERATED AUTOMATICALLY BY GROUPS"))
            xml_content = etree.tostring(xml, pretty_print=True, encoding="unicode")

            new_context = dict(view._context)
            new_context.pop('install_filename', None)  # don't set arch_fs for this computed view
            new_context['lang'] = None
            view.with_context(new_context).write({'arch': xml_content})


class module_category(models.Model):
    _inherit = "ir.module.category"

    def write(self, values):
        res = super().write(values)
        if "name" in values:
            self.env["res.groups"]._update_user_groups_view1()
        return res

    def unlink(self):
        res = super().unlink()
        self.env["res.groups"]._update_user_groups_view1()
        return res

# 视图
<?xml version="1.0" encoding="utf-8"?>
<odoo>
    <data noupdate="1">

        <record id="access_request_user_groups_view" model="ir.ui.view">
            <field name="name">access.request.user.groups.view</field>
            <field name="model">access.request</field>
            <field name="inherit_id" ref="access_request_form_view"/>
            <field name="arch" type="xml">
                <!-- dummy, will be modified by groups -->
                <field name="groups_id" position="after"/>
            </field>
        </record>

    </data>
</odoo>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值