Dre4m Shell
Server IP : 127.0.0.2  /  Your IP : 3.23.59.191
Web Server : Apache/2.4.18 (Ubuntu)
System :
User : www-data ( )
PHP Version : 7.0.33-0ubuntu0.16.04.16
Disable Function : disk_free_space,disk_total_space,diskfreespace,dl,exec,fpaththru,getmyuid,getmypid,highlight_file,ignore_user_abord,leak,listen,link,opcache_get_configuration,opcache_get_status,passthru,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,php_uname,phpinfo,posix_ctermid,posix_getcwd,posix_getegid,posix_geteuid,posix_getgid,posix_getgrgid,posix_getgrnam,posix_getgroups,posix_getlogin,posix_getpgid,posix_getpgrp,posix_getpid,posix,_getppid,posix_getpwnam,posix_getpwuid,posix_getrlimit,posix_getsid,posix_getuid,posix_isatty,posix_kill,posix_mkfifo,posix_setegid,posix_seteuid,posix_setgid,posix_setpgid,posix_setsid,posix_setuid,posix_times,posix_ttyname,posix_uname,pclose,popen,proc_open,proc_close,proc_get_status,proc_nice,proc_terminate,shell_exec,source,show_source,system,virtual
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : ON  |  Pkexec : ON
Directory :  /opt/odoo/addons/project_issue/models/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ HOME SHELL ]     

Current File : /opt/odoo/addons/project_issue/models/project_issue.py
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.

from odoo import api, fields, models, tools, _
from odoo.exceptions import AccessError
from odoo.tools.safe_eval import safe_eval


class ProjectIssue(models.Model):
    _name = "project.issue"
    _description = "Project Issue"
    _inherit = ['mail.thread', 'ir.needaction_mixin']
    _order = "priority desc, create_date desc"
    _mail_post_access = 'read'

    @api.model
    def _get_default_stage_id(self):
        project_id = self.env.context.get('default_project_id')
        if not project_id:
            return False
        return self.stage_find(project_id, [('fold', '=', False)])

    name = fields.Char(string='Issue', required=True)
    active = fields.Boolean(default=True)
    days_since_creation = fields.Integer(compute='_compute_inactivity_days', string='Days since creation date',
                                         help="Difference in days between creation date and current date")
    date_deadline = fields.Date(string='Deadline')
    partner_id = fields.Many2one('res.partner', string='Contact', index=True)
    company_id = fields.Many2one('res.company', string='Company', default=lambda self: self.env.user.company_id)
    description = fields.Text('Private Note')
    kanban_state = fields.Selection([('normal', 'Normal'), ('blocked', 'Blocked'), ('done', 'Ready for next stage')], string='Kanban State',
                                    track_visibility='onchange', required=True, default='normal',
                                    help="""An Issue's kanban state indicates special situations affecting it:\n
                                           * Normal is the default situation\n
                                           * Blocked indicates something is preventing the progress of this issue\n
                                           * Ready for next stage indicates the issue is ready to be pulled to the next stage""")
    email_from = fields.Char(string='Email', help="These people will receive email.", index=True)
    email_cc = fields.Char(string='Watchers Emails', help="""These email addresses will be added to the CC field of all inbound
        and outbound emails for this record before being sent. Separate multiple email addresses with a comma""")
    date_open = fields.Datetime(string='Assigned', readonly=True, index=True)
    date_closed = fields.Datetime(string='Closed', readonly=True, index=True)
    date = fields.Datetime('Date')
    date_last_stage_update = fields.Datetime(string='Last Stage Update', index=True, default=fields.Datetime.now)
    channel = fields.Char(string='Channel', help="Communication channel.")  # TDE note: is it still used somewhere ?
    tag_ids = fields.Many2many('project.tags', string='Tags')
    priority = fields.Selection([('0', 'Low'), ('1', 'Normal'), ('2', 'High')], 'Priority', index=True, default='0')
    stage_id = fields.Many2one('project.task.type', string='Stage', track_visibility='onchange', index=True,
                               domain="[('project_ids', '=', project_id)]", copy=False,
                               group_expand='_read_group_stage_ids',
                               default=_get_default_stage_id)
    project_id = fields.Many2one('project.project', string='Project', track_visibility='onchange', index=True)
    duration = fields.Float('Duration')
    task_id = fields.Many2one('project.task', string='Task', domain="[('project_id','=',project_id)]",
                              help="You can link this issue to an existing task or directly create a new one from here")
    day_open = fields.Float(compute='_compute_day', string='Days to Assign', store=True)
    day_close = fields.Float(compute='_compute_day', string='Days to Close', store=True)

    user_id = fields.Many2one('res.users', string='Assigned to', index=True, track_visibility='onchange', default=lambda self: self.env.uid)
    working_hours_open = fields.Float(compute='_compute_day', string='Working Hours to assign the Issue', store=True)
    working_hours_close = fields.Float(compute='_compute_day', string='Working Hours to close the Issue', store=True)
    inactivity_days = fields.Integer(compute='_compute_inactivity_days', string='Days since last action',
                                     help="Difference in days between last action and current date")
    color = fields.Integer('Color Index')
    user_email = fields.Char(related='user_id.email', string='User Email', readonly=True)
    date_action_last = fields.Datetime(string='Last Action', readonly=True)
    date_action_next = fields.Datetime(string='Next Action', readonly=True)
    legend_blocked = fields.Char(related="stage_id.legend_blocked", string='Kanban Blocked Explanation', readonly=True)
    legend_done = fields.Char(related="stage_id.legend_done", string='Kanban Valid Explanation', readonly=True)
    legend_normal = fields.Char(related="stage_id.legend_normal", string='Kanban Ongoing Explanation', readonly=True)

    @api.model
    def _read_group_stage_ids(self, stages, domain, order):
        search_domain = [('id', 'in', stages.ids)]
        # retrieve project_id from the context, add them to already fetched columns (ids)
        if 'default_project_id' in self.env.context:
            search_domain = ['|', ('project_ids', '=', self.env.context['default_project_id'])] + search_domain
        # perform search
        return stages.search(search_domain, order=order)

    @api.multi
    @api.depends('create_date', 'date_closed', 'date_open')
    def _compute_day(self):
        for issue in self:
            # if the working hours on the project are not defined, use default ones (8 -> 12 and 13 -> 17 * 5)
            calendar = issue.project_id.resource_calendar_id

            dt_create_date = fields.Datetime.from_string(issue.create_date)
            if issue.date_open:
                dt_date_open = fields.Datetime.from_string(issue.date_open)
                issue.day_open = (dt_date_open - dt_create_date).total_seconds() / (24.0 * 3600)
                issue.working_hours_open = calendar.get_working_hours(dt_create_date, dt_date_open,
                    compute_leaves=True, resource_id=False, default_interval=(8, 16))

            if issue.date_closed:
                dt_date_closed = fields.Datetime.from_string(issue.date_closed)
                issue.day_close = (dt_date_closed - dt_create_date).total_seconds() / (24.0 * 3600)
                issue.working_hours_close = calendar.get_working_hours(dt_create_date, dt_date_closed,
                    compute_leaves=True, resource_id=False, default_interval=(8, 16))

    @api.multi
    @api.depends('create_date', 'date_action_last', 'date_last_stage_update')
    def _compute_inactivity_days(self):
        current_datetime = fields.Datetime.from_string(fields.Datetime.now())
        for issue in self:
            dt_create_date = fields.Datetime.from_string(issue.create_date) or current_datetime
            issue.days_since_creation = (current_datetime - dt_create_date).days

            if issue.date_action_last:
                issue.inactivity_days = (current_datetime - fields.Datetime.from_string(issue.date_action_last)).days
            elif issue.date_last_stage_update:
                issue.inactivity_days = (current_datetime - fields.Datetime.from_string(issue.date_last_stage_update)).days
            else:
                issue.inactivity_days = (current_datetime - dt_create_date).days

    @api.onchange('partner_id')
    def _onchange_partner_id(self):
        """ This function sets partner email address based on partner
        """
        self.email_from = self.partner_id.email

    @api.onchange('project_id')
    def _onchange_project_id(self):
        if self.project_id:
            if not self.partner_id and not self.email_from:
                self.partner_id = self.project_id.partner_id.id
                self.email_from = self.project_id.partner_id.email
            self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
        else:
            self.partner_id = False
            self.email_from = False
            self.stage_id = False

    @api.onchange('task_id')
    def _onchange_task_id(self):
        self.user_id = self.task_id.user_id

    @api.multi
    def copy(self, default=None):
        if default is None:
            default = {}
        default.update(name=_('%s (copy)') % (self.name))
        return super(ProjectIssue, self).copy(default=default)

    @api.model
    def create(self, vals):
        context = dict(self.env.context)
        if vals.get('project_id') and not self.env.context.get('default_project_id'):
            context['default_project_id'] = vals.get('project_id')
        if vals.get('user_id') and not vals.get('date_open'):
            vals['date_open'] = fields.Datetime.now()
        if 'stage_id' in vals:
            vals.update(self.update_date_closed(vals['stage_id']))

        # context: no_log, because subtype already handle this
        context['mail_create_nolog'] = True
        return super(ProjectIssue, self.with_context(context)).create(vals)

    @api.multi
    def write(self, vals):
        # stage change: update date_last_stage_update
        if 'stage_id' in vals:
            vals.update(self.update_date_closed(vals['stage_id']))
            vals['date_last_stage_update'] = fields.Datetime.now()
            if 'kanban_state' not in vals:
                vals['kanban_state'] = 'normal'
        # user_id change: update date_open
        if vals.get('user_id') and 'date_open' not in vals:
            vals['date_open'] = fields.Datetime.now()
        return super(ProjectIssue, self).write(vals)

    @api.model
    def get_empty_list_help(self, help):
        return super(ProjectIssue, self.with_context(
            empty_list_help_model='project.project',
            empty_list_help_id=self.env.context.get('default_project_id'),
            empty_list_help_document_name=_("issues")
        )).get_empty_list_help(help)

    # -------------------------------------------------------
    # Stage management
    # -------------------------------------------------------

    def update_date_closed(self, stage_id):
        project_task_type = self.env['project.task.type'].browse(stage_id)
        if project_task_type.fold:
            return {'date_closed': fields.Datetime.now()}
        return {'date_closed': False}

    def stage_find(self, project_id, domain=None, order='sequence'):
        """ Override of the base.stage method
            Parameter of the stage search taken from the issue:
            - project_id: if set, stages must belong to this project or
              be a default case
        """
        search_domain = list(domain) if domain else []
        if project_id:
            search_domain += [('project_ids', '=', project_id)]
        project_task_type = self.env['project.task.type'].search(search_domain, order=order, limit=1)
        return project_task_type.id

    # -------------------------------------------------------
    # Mail gateway
    # -------------------------------------------------------

    @api.multi
    def _track_template(self, tracking):
        self.ensure_one()
        res = super(ProjectIssue, self)._track_template(tracking)
        changes, dummy = tracking[self.id]
        if 'stage_id' in changes and self.stage_id.mail_template_id:
            res['stage_id'] = (self.stage_id.mail_template_id, {'composition_mode': 'mass_mail'})
        return res

    def _track_subtype(self, init_values):
        self.ensure_one()
        if 'kanban_state' in init_values and self.kanban_state == 'blocked':
            return 'project_issue.mt_issue_blocked'
        elif 'kanban_state' in init_values and self.kanban_state == 'done':
            return 'project_issue.mt_issue_ready'
        elif 'user_id' in init_values and self.user_id:  # assigned -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values and self.stage_id and self.stage_id.sequence <= 1:  # start stage -> new
            return 'project_issue.mt_issue_new'
        elif 'stage_id' in init_values:
            return 'project_issue.mt_issue_stage'
        return super(ProjectIssue, self)._track_subtype(init_values)

    @api.multi
    def _notification_recipients(self, message, groups):
        """
        """
        groups = super(ProjectIssue, self)._notification_recipients(message, groups)

        self.ensure_one()
        if not self.user_id:
            take_action = self._notification_link_helper('assign')
            project_actions = [{'url': take_action, 'title': _('I take it')}]
        else:
            new_action_id = self.env.ref('project_issue.project_issue_categ_act0').id
            new_action = self._notification_link_helper('new', action_id=new_action_id)
            project_actions = [{'url': new_action, 'title': _('New Issue')}]

        new_group = (
            'group_project_user', lambda partner: bool(partner.user_ids) and any(user.has_group('project.group_project_user') for user in partner.user_ids), {
                'actions': project_actions,
            })

        return [new_group] + groups

    @api.model
    def message_get_reply_to(self, res_ids, default=None):
        """ Override to get the reply_to of the parent project. """
        issues = self.browse(res_ids)
        project_ids = set(issues.mapped('project_id').ids)
        aliases = self.env['project.project'].message_get_reply_to(list(project_ids), default=default)
        return dict((issue.id, aliases.get(issue.project_id and issue.project_id.id or 0, False)) for issue in issues)

    @api.multi
    def message_get_suggested_recipients(self):
        recipients = super(ProjectIssue, self).message_get_suggested_recipients()
        try:
            for issue in self:
                if issue.partner_id:
                    issue._message_add_suggested_recipient(recipients, partner=issue.partner_id, reason=_('Customer'))
                elif issue.email_from:
                    issue._message_add_suggested_recipient(recipients, email=issue.email_from, reason=_('Customer Email'))
        except AccessError:  # no read access rights -> just ignore suggested recipients because this imply modifying followers
            pass
        return recipients

    @api.multi
    def email_split(self, msg):
        email_list = tools.email_split((msg.get('to') or '') + ',' + (msg.get('cc') or ''))
        # check left-part is not already an alias
        return filter(lambda x: x.split('@')[0] not in self.mapped('project_id.alias_name'), email_list)

    @api.model
    def message_new(self, msg, custom_values=None):
        """ Overrides mail_thread message_new that is called by the mailgateway
            through message_process.
            This override updates the document according to the email.
        """
        # remove default author when going through the mail gateway. Indeed we
        # do not want to explicitly set user_id to False; however we do not
        # want the gateway user to be responsible if no other responsible is
        # found.
        create_context = dict(self.env.context or {})
        create_context['default_user_id'] = False
        defaults = {
            'name':  msg.get('subject') or _("No Subject"),
            'email_from': msg.get('from'),
            'email_cc': msg.get('cc'),
            'partner_id': msg.get('author_id', False),
        }
        if custom_values:
            defaults.update(custom_values)

        res_id = super(ProjectIssue, self.with_context(create_context)).message_new(msg, custom_values=defaults)
        issue = self.browse(res_id)
        email_list = issue.email_split(msg)
        partner_ids = filter(None, issue._find_partner_from_emails(email_list))
        issue.message_subscribe(partner_ids)
        return res_id

    @api.multi
    def message_update(self, msg, update_vals=None):
        """ Override to update the issue according to the email. """
        email_list = self.email_split(msg)
        partner_ids = filter(None, self._find_partner_from_emails(email_list))
        self.message_subscribe(partner_ids)
        return super(ProjectIssue, self).message_update(msg, update_vals=update_vals)

    @api.multi
    @api.returns('mail.message', lambda value: value.id)
    def message_post(self, subtype=None, **kwargs):
        """ Overrides mail_thread message_post so that we can set the date of last action field when
            a new message is posted on the issue.
        """
        self.ensure_one()
        mail_message = super(ProjectIssue, self).message_post(subtype=subtype, **kwargs)
        if subtype:
            self.sudo().write({'date_action_last': fields.Datetime.now()})
        return mail_message

    @api.multi
    def message_get_email_values(self, notif_mail=None):
        self.ensure_one()
        res = super(ProjectIssue, self).message_get_email_values(notif_mail=notif_mail)
        headers = {}
        if res.get('headers'):
            try:
                headers.update(safe_eval(res['headers']))
            except Exception:
                pass
        if self.project_id:
            current_objects = filter(None, headers.get('X-Odoo-Objects', '').split(','))
            current_objects.insert(0, 'project.project-%s, ' % self.project_id.id)
            headers['X-Odoo-Objects'] = ','.join(current_objects)
        if self.tag_ids:
            headers['X-Odoo-Tags'] = ','.join(self.tag_ids.mapped('name'))
        res['headers'] = repr(headers)
        return res

Anon7 - 2022
AnonSec Team