osbzr/gooderp_addons

View on GitHub
good_process/models/mail_thread.py

Summary

Maintainability
D
1 day
Test Coverage
# -*- coding: utf-8 -*-
from odoo import models, fields, api
from odoo.exceptions import ValidationError
from odoo.tools.safe_eval import safe_eval


class MailThread(models.AbstractModel):
    '''
    针对系统内的单据增加审批流程控制
    增加两个字段:_to_approver_ids 记录还有谁需要审批(用来判断审批是否结束)
                 _approver_num 整个流程涉及的审批者数量(用来判断审批是否开始)
    '''
    _inherit = 'mail.thread'

    @api.one
    @api.depends('_to_approver_ids', '_approver_num')
    def _get_approve_state(self):
        """计算审批状态"""
        to_approver = len(self._to_approver_ids)
        if not to_approver:
            self._approve_state = u'已审批'
        elif to_approver == self._approver_num:
            self._approve_state = u'已提交'
        else:
            self._approve_state = u'审批中'

    _to_approver_ids = fields.One2many('good_process.approver',  'res_id', readonly='1',
                                       domain=lambda self: [('model', '=', self._name)], auto_join=True, string='待审批人')
    _approver_num = fields.Integer(string='总审批人数')
    _approve_state = fields.Char(u'审批状态',
                                 compute='_get_approve_state')

    def __get_groups__(self, process_rows):
        groups = []
        if process_rows:
            [groups.append((line.group_id, line.sequence)) for line in self.env['good_process.process_line'].search(
                [('process_id', '=', process_rows.id)], order='sequence')]
        return groups

    def __get_users__(self, groups):
        users = []
        for group, sequence in groups:
            [(users.append((user, sequence, group.id)))
             for user in group.users]
        return users

    def __get_user_manager__(self, thread_row, process_rows):
        '''
        如此流程需要记录创建者的部门经理审批,取得部门经理用户
        '''
        return_vals = False
        if process_rows.is_department_approve:
            staff_row = self.env['staff'].search(
                [('user_id', '=', thread_row.create_uid.id)])
            if staff_row and getattr(staff_row, 'parent_id', False):
                return_vals = staff_row.parent_id.user_id
        return return_vals

    def __add_approver__(self, thread_row, model_name, active_id):
        # TODO 加上当前用户的部门经理
        approver_rows = []
        users = []
        process_rows = self.env['good_process.process'].search([('model_id.model', '=', model_name),
                                                                ('type', '=', getattr(thread_row, 'type', False))],
                                                               order='sequence')
        process_row = False
        for process in process_rows:
            domain = [('id', '=', active_id)]
            if process.applicable_domain:
                domain += safe_eval(process.applicable_domain)
            if self.env[model_name].search(domain):
                process_row = process
                break
        if not process_row:
            return []

        groups = self.__get_groups__(process_row)
        department_manager = self.__get_user_manager__(
            thread_row, process_row)
        if department_manager:
            users.append((department_manager, 0, False))
        users.extend(self.__get_users__(groups))
        [approver_rows.append(self.env['good_process.approver'].create(
            {'user_id': user.id,
             'res_id': thread_row.id,
             'model_type': thread_row._description,
             'record_name': getattr(thread_row, 'name', ''),
             'creator': thread_row.create_uid.id,
             'sequence': sequence,
             'group_id': groud_id,
             'model': thread_row._name})) for user, sequence, groud_id in users]
        return [{'id': row.id, 'display_name': row.user_id.name} for row in approver_rows]

    def __good_approver_send_message__(self, active_id, active_model, message):
        mode_row = self.env[active_model].browse(active_id)
        user_row = self.env['res.users'].browse(self.env.uid)
        message_text = u"%s %s %s %s" % (
            user_row.name, message, mode_row._name, mode_row.name)
        return message_text

    def __is_departement_manager__(self, department_row):
        return_vals = department_row.id
        if department_row:
            department_row.unlink()
        return return_vals

    def __has_manager__(self, active_id, active_model):
        department_row = self.env['good_process.approver'].search([('model', '=', active_model),
                                                                   ('res_id', '=',
                                                                    active_id),
                                                                   ('sequence', '=', 0), ('group_id', '=', False)])
        return department_row

    @api.model
    def good_process_approve(self, active_id, active_model):
        return_vals = []
        message = ''
        manger_row = self.__has_manager__(active_id, active_model)
        model_row = self.env[active_model].browse(active_id)
        if (manger_row and manger_row.user_id.id == self.env.uid) or not manger_row:
            manger_user = []
            if manger_row:
                manger_user = [manger_row.user_id.id]
                return_vals.append(self.__is_departement_manager__(manger_row))
            users, can_clean_groups = (self.__get_user_group__(
                active_id, active_model, manger_user, model_row))
            return_vals.extend(self.__remove_approver__(
                active_id, active_model, users, can_clean_groups))
            if return_vals:
                message = self.__good_approver_send_message__(
                    active_id, active_model, u'同意')
            else:
                return_vals = u'您不是这张单据的下一个审批者'
        else:
            return_vals = u'您不是这张单据的下一个审批者'
        return return_vals, message or ''

    @api.model
    def good_process_refused(self, active_id, active_model):
        message = ''
        mode_row = self.env[active_model].browse(active_id)
        users, groups = self.__get_user_group__(
            active_id, active_model, [], mode_row)
        approver_rows = self.env['good_process.approver'].search([('model', '=', active_model),
                                                                  ('res_id', '=', active_id)])
        if mode_row._approver_num == len(mode_row._to_approver_ids):
            return_vals = u'您是第一批需要审批的人,无需拒绝!'
        elif approver_rows and users:
            approver_rows.unlink()
            message = self.__good_approver_send_message__(
                active_id, active_model, u'拒绝')
            return_vals = self.__add_approver__(mode_row, active_model, active_id)

        else:
            return_vals = u'已经通过不能拒绝!'
        return return_vals, message or ''

    def is_current_model(self):
        """检查是否是当前对象"""
        action_id = self.env.context.get('params', False) \
                    and self.env.context['params'].get('action', False) \
                    or False
        if not action_id:
            return True
        current_model = self.env['ir.actions.act_window'].browse(action_id).res_model
        # 排除good_process.approver是因为从审批向导进去所有单据current_model != self._name导致跳过审批流程
        if current_model != self._name and current_model != 'good_process.approver':
            return False
        else:
            return True

    @api.model
    def create(self, vals):
        thread_row = super(MailThread, self).create(vals)
        approvers = self.__add_approver__(thread_row, self._name, thread_row.id)
        thread_row._approver_num = len(approvers)
        return thread_row

    @api.multi
    def write(self, vals):
        '''
        如果单据的审批流程已经开始(第一个人同意了才算开始) —— 至少一个审批人已经审批通过,不允许对此单据进行修改。
        '''
        if not self.is_current_model():
            return super(MailThread, self).write(vals)
        for th in self:
            ignore_fields = ['_approver_num',
                             '_to_approver_ids',
                             'message_ids',
                             'message_follower_ids',
                             'message_partner_ids',
                             'message_channel_ids',
                             'approve_uid',
                             'approve_date',
                             ]
            if any([vals.has_key(x) for x in ignore_fields]) or not th._approver_num:
                continue
            change_state = vals.get('state', False)

            if change_state == 'cancel':    # 作废时移除待审批人
                if not len(th._to_approver_ids) and th._approver_num:
                    raise ValidationError(u"已审批不可作废")
                if len(th._to_approver_ids) < th._approver_num:
                    raise ValidationError(u"审批中不可作废")
                for approver in th._to_approver_ids:
                    approver.unlink()
                return super(MailThread, self).write(vals)

            # 已提交,确认时报错
            if len(th._to_approver_ids) == th._approver_num and change_state == 'done':
                raise ValidationError(u"审批后才能确认")
            # 已审批
            if not len(th._to_approver_ids):
                if not change_state:
                    raise ValidationError(u'已审批不可修改')
                if change_state == 'draft':
                    vals.update({
                        '_approver_num': len(self.__add_approver__(th, th._name, th.id)),
                    })
            # 审批中,确认时报错,修改其他字段报错
            elif len(th._to_approver_ids) < th._approver_num:
                if change_state == 'done':
                    raise ValidationError(u"审批后才能确认")
                raise ValidationError(u"审批中不可修改")

        thread_row = super(MailThread, self).write(vals)
        return thread_row

    @api.multi
    def unlink(self):
        if not self.is_current_model():
            return super(MailThread, self).unlink()
        for th in self:
            if not len(th._to_approver_ids) and th._approver_num:
                raise ValidationError(u"已审批不可删除")
            if len(th._to_approver_ids) < th._approver_num:
                raise ValidationError(u"审批中不可删除")
            for Approver in th._to_approver_ids:
                Approver.unlink()
        return super(MailThread, self).unlink()

    def __get_user_group__(self, active_id,  active_model, users, mode_row):
        all_groups = []
        process_rows = self.env['good_process.process'].search([('model_id.model', '=', active_model),
                                                                ('type', '=', getattr(mode_row, 'type', False))],
                                                               order='sequence')
        process_row = False
        for process in process_rows:
            domain = [('id', '=', active_id)]
            if process.applicable_domain:
                domain += safe_eval(process.applicable_domain)
            if self.env[active_model].search(domain):
                process_row = process
                break
        if not process_row:
            return users, []

        line_rows = self.env['good_process.process_line'].search(
            [('process_id', '=', process_row.id)], order='sequence')
        least_num = 'default_vals'
        for line in line_rows:
            approver_s = self.env['good_process.approver'].search([('model', '=', active_model),
                                                                   ('group_id', '=',
                                                                    line.group_id.id),
                                                                   ('res_id', '=', active_id)])

            if least_num == 'default_vals' and approver_s:
                least_num = line.sequence
            if least_num == line.sequence and self.env.uid in [user.id for user in line.group_id.users]:
                users = [self.env.uid]
            if not line.is_all_approve:
                all_groups.append(line.group_id)
        can_clean_groups = []
        for group in all_groups:
            all_group_user = [user.id for user in group.users]
            if len(list(set(all_group_user).difference(users))) != len(all_group_user):
                can_clean_groups.append(group.id)
        return users, can_clean_groups

    def __get_remove_approver__(self, thread_row, user_ids, can_clean_groups):
        """
        去除审批人 - if 判断当前用户是否属于当前状态的审批人 里面
                        取得 当前用户在审批人里面的记录 (或者全组一人审批即可的 情况下  这个组里面其他人)
                    else:
                        判断 是否轮到当前用户审批 否则跳出循环

                    如果是全组审批并且取得一个审批人记录 就跳出循环

        :param thread_row:
        :param user_ids: 当前状态的审批人们
        :param is_all_approve:
        :param sequence:
        :return: 审批人 对应的记录
        """
        remove_approve, return_vals = [], []
        for Approver in thread_row._to_approver_ids:
            if Approver.user_id.id in user_ids or Approver.group_id.id in can_clean_groups:
                remove_approve.append(Approver)
                return_vals.append(Approver.id)
        return return_vals, remove_approve

    @api.model
    def __remove_approver__(self, active_id, active_model, user_ids, can_clean_groups):
        return_vals = False
        if active_id:
            thread_row = self.env[active_model].browse(active_id)
            return_vals, remove_approvers = self.__get_remove_approver__(
                thread_row, user_ids, can_clean_groups)
            if remove_approvers:
                [Approver.unlink() for Approver in remove_approvers]
        return return_vals


class Approver(models.Model):
    '''
    单据的待审批者
    '''
    _name = 'good_process.approver'
    _rec_name = 'user_id'
    _order = 'model, res_id, sequence'

    model_type = fields.Char(u'单据类型')
    record_name = fields.Char(u'编号')
    creator = fields.Many2one('res.users', u'申请人')
    model = fields.Char('模型', index=True)
    res_id = fields.Integer('ID', index=True)
    group_id = fields.Many2one('res.groups', string=u'审批组')
    group_name = fields.Char(related='group_id.name', string=u'名字')
    user_id = fields.Many2one('res.users', string=u'用户')
    sequence = fields.Integer(string=u'顺序')

    @api.multi
    def goto(self):
        self.ensure_one()
        views = self.env['ir.ui.view'].search(
            [('model', '=', self.model), ('type', '=', 'form')])
        model_obj = self.env[self.model]
        rec = model_obj.browse(self.res_id)
        if getattr(rec, 'is_return', False):
            for v in views:
                if '_return_' in v.xml_id:
                    vid = v.id
                    break
        else:
            vid = views[0].id

        return_vals = {
            'name': u'审批',
            'view_type': 'form',
            'view_mode': 'form',
            'res_model': self.model,
            'view_id': False,
            'views': [(vid, 'form')],
            'type': 'ir.actions.act_window',
            'res_id': self.res_id,
        }
        # 如果单据存在 type,根据type传回context
        if hasattr(model_obj, 'type'):
            rec_ctx = {}
            if rec.type == 'get':
                rec_ctx = {'default_get': 1}
            if rec.type == 'pay':
                rec_ctx = {'default_pay': 1}
            return_vals['context'] = rec_ctx
        return return_vals

    @api.model_cr
    def init(self):
        self._cr.execute(
            """SELECT indexname FROM pg_indexes WHERE indexname = 'good_process_approver_model_res_id_idx'""")
        if not self._cr.fetchone():
            self._cr.execute(
                """CREATE INDEX good_process_approver_model_res_id_idx ON good_process_approver (model, res_id)""")


class Process(models.Model):
    '''
    可供用户自定义的审批流程,可控制是否需部门经理审批。注意此规则只对修改之后新建(或被拒绝)的单据有效
    '''
    _name = 'good_process.process'
    _description = u'审批规则'
    _rec_name = 'model_id'
    model_id = fields.Many2one('ir.model', u'单据', required=True)
    type = fields.Char(u'类型', help=u'有些单据根据type字段区分具体流程')
    is_department_approve = fields.Boolean(string=u'部门经理审批')
    line_ids = fields.One2many(
        'good_process.process_line', 'process_id', string=u'审批组')
    active = fields.Boolean(u'启用', default=True)
    applicable_domain = fields.Char(u'适用条件')
    sequence = fields.Integer(u'优先级')

    @api.one
    @api.constrains('model_id', 'type', 'applicable_domain')
    def check_model_id(self):
        records = self.search([
            ('model_id', '=', self.model_id.id),
            ('type', '=', self.type),
            ('id', '!=', self.id),
            ('applicable_domain', '=', self.applicable_domain)])
        if records:
            raise ValidationError(u'审批规则必须唯一')

    @api.model
    def create(self, vals):
        """
        新建审批配置规则,如果配置的模型有type字段而规则未输入type,保存时给出提示
        """
        process_id = super(Process, self).create(vals)
        model = self.env[process_id.model_id.model]
        if hasattr(model, 'type') and not process_id.type:
            raise ValidationError(u'请输入类型')
        return process_id


class ProcessLine(models.Model):
    '''
    可控制由哪些审批组审批,各自的审批顺序是什么,组内用户都需要审还是一位代表审批即可
    '''
    _name = 'good_process.process_line'
    _description = u'审批规则行'
    _order = 'sequence'

    sequence = fields.Integer(string='序号')
    group_id = fields.Many2one('res.groups', string=u'审批组', required=True)
    is_all_approve = fields.Boolean(string=u'是否需要本组用户全部审批')
    process_id = fields.Many2one('good_process.process', u'审批规则', ondelete='cascade')