summaryrefslogtreecommitdiff
path: root/addons/project_issue/models
diff options
context:
space:
mode:
authorYannick Tivisse <yti@odoo.com>2017-06-06 11:04:31 +0200
committerYannick Tivisse <yti@odoo.com>2017-08-07 11:24:56 +0200
commitcdd5f470dd00a71893d9f3d9cce5cd894a54095d (patch)
treef3a51c15acf299aebab5de4deb0a5f3175d4b4fd /addons/project_issue/models
parent088b9b2a2a4e4a86fc9338e105adf0f27e520d04 (diff)
[REM] project_issue: Remove module, merge into project.task
Diffstat (limited to 'addons/project_issue/models')
-rw-r--r--addons/project_issue/models/__init__.py9
-rw-r--r--addons/project_issue/models/account_analytic_account.py22
-rw-r--r--addons/project_issue/models/project_issue.py361
-rw-r--r--addons/project_issue/models/project_issue_config_settings.py11
-rw-r--r--addons/project_issue/models/project_project.py47
-rw-r--r--addons/project_issue/models/project_task_type.py12
-rw-r--r--addons/project_issue/models/res_partner.py15
7 files changed, 0 insertions, 477 deletions
diff --git a/addons/project_issue/models/__init__.py b/addons/project_issue/models/__init__.py
deleted file mode 100644
index 3a4e6c286d8..00000000000
--- a/addons/project_issue/models/__init__.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from . import project_issue
-from . import account_analytic_account
-from . import project_project
-from . import project_task_type
-from . import res_partner
-from . import project_issue_config_settings
diff --git a/addons/project_issue/models/account_analytic_account.py b/addons/project_issue/models/account_analytic_account.py
deleted file mode 100644
index 76d9439abce..00000000000
--- a/addons/project_issue/models/account_analytic_account.py
+++ /dev/null
@@ -1,22 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models, _
-from odoo.exceptions import UserError
-
-
-class AccountAnalyticAccount(models.Model):
- _inherit = 'account.analytic.account'
-
- use_issues = fields.Boolean(string='Use Issues', help="Check this box to manage customer activities through this project")
-
- @api.multi
- def unlink(self):
- if self.env['project.issue'].search_count([('project_id.analytic_account_id', 'in', self.ids)]):
- raise UserError(_('Please remove existing issues in the project linked to the accounts you want to delete.'))
- return super(AccountAnalyticAccount, self).unlink()
-
- @api.model
- def _trigger_project_creation(self, vals):
- res = super(AccountAnalyticAccount, self)._trigger_project_creation(vals)
- return res or (vals.get('use_issues') and not 'project_creation_in_progress' in self.env.context)
diff --git a/addons/project_issue/models/project_issue.py b/addons/project_issue/models/project_issue.py
deleted file mode 100644
index 0a1c838e18f..00000000000
--- a/addons/project_issue/models/project_issue.py
+++ /dev/null
@@ -1,361 +0,0 @@
-# -*- 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', 'mail.activity.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.Html('Private Note')
- kanban_state = fields.Selection([
- ('normal', 'Grey'),
- ('blocked', 'Red'),
- ('done', 'Green')], string='Kanban State',
- copy=False, default='normal', required=True, track_visibility='onchange',
- help="An Issue's kanban state indicates special situations affecting it:\n"
- " * Grey is the default situation\n"
- " * Red indicates something is preventing the progress of this issue\n"
- " * Green 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, group_operator="avg")
- day_close = fields.Float(compute='_compute_day', string='Days to Close', store=True, group_operator="avg")
-
- 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, group_operator="avg")
- working_hours_close = fields.Float(compute='_compute_day', string='Working Hours to close the Issue', store=True, group_operator="avg")
- 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)
- 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:
- 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)
- if issue.project_id.resource_calendar_id:
- issue.working_hours_open = issue.project_id.resource_calendar_id.get_work_hours_count(
- dt_create_date, dt_date_open, False, compute_leaves=True)
- else:
- issue.working_hours_open = 0
-
- 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)
- if issue.project_id.resource_calendar_id:
- issue.working_hours_close = issue.project_id.resource_calendar_id.get_work_hours_count(
- dt_create_date, dt_date_closed, False, compute_leaves=True)
- else:
- issue.working_hours_close = 0
-
- @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):
- default_partner_id = self.env.context.get('default_partner_id')
- default_partner = self.env['res.partner'].browse(default_partner_id) if default_partner_id else self.env['res.partner']
- 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
- if self.project_id not in self.stage_id.project_ids:
- self.stage_id = self.stage_find(self.project_id.id, [('fold', '=', False)])
- else:
- self.partner_id = default_partner
- self.email_from = default_partner.email
- 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:
- project_actions = []
-
- 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 [x for x in email_list if x.split('@')[0] not in self.mapped('project_id.alias_name')]
-
- @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)
-
- issue = super(ProjectIssue, self.with_context(create_context)).message_new(msg, custom_values=defaults)
- email_list = issue.email_split(msg)
- partner_ids = [p for p in issue._find_partner_from_emails(email_list) if p]
- issue.message_subscribe(partner_ids)
- return issue
-
- @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 = [p for p in self._find_partner_from_emails(email_list) if p]
- 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
-
- def _message_post_after_hook(self, message):
- if self.email_from and not self.partner_id:
- # we consider that posting a message with a specified recipient (not a follower, a specific one)
- # on a document without customer means that it was created through the chatter using
- # suggested recipients. This heuristic allows to avoid ugly hacks in JS.
- new_partner = message.partner_ids.filtered(lambda partner: partner.email == self.email_from)
- if new_partner:
- self.search([
- ('partner_id', '=', False),
- ('email_from', '=', new_partner.email),
- ('stage_id.fold', '=', False)]).write({'partner_id': new_partner.id})
- return super(ProjectIssue, self)._message_post_after_hook(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 = [h for h in headers.get('X-Odoo-Objects', '').split(',') if h]
- 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
diff --git a/addons/project_issue/models/project_issue_config_settings.py b/addons/project_issue/models/project_issue_config_settings.py
deleted file mode 100644
index f4c73ae4abd..00000000000
--- a/addons/project_issue/models/project_issue_config_settings.py
+++ /dev/null
@@ -1,11 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import models, fields
-
-
-class ProjectIssueConfiguration(models.TransientModel):
- _inherit = 'project.config.settings'
-
- module_project_issue_sheet = fields.Boolean(string="Timesheets on Issues")
- module_rating_project_issue = fields.Boolean("Rating on Issues")
diff --git a/addons/project_issue/models/project_project.py b/addons/project_issue/models/project_project.py
deleted file mode 100644
index 583e9319e4b..00000000000
--- a/addons/project_issue/models/project_project.py
+++ /dev/null
@@ -1,47 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import api, fields, models
-
-
-class Project(models.Model):
- _inherit = 'project.project'
-
- issue_count = fields.Integer(compute='_compute_issue_count', string="Issues")
- issue_ids = fields.One2many('project.issue', 'project_id', string="Issues", domain=['|', ('stage_id.fold', '=', False), ('stage_id', '=', False)])
- label_issues = fields.Char(string='Use Issues as', help="Customize the issues label, for example to call them cases.", default='Issues')
- use_issues = fields.Boolean(related="analytic_account_id.use_issues", default=True)
- issue_needaction_count = fields.Integer(compute="_issue_needaction_count", string="Issues")
-
- @api.model
- def _get_alias_models(self):
- res = super(Project, self)._get_alias_models()
- res.append(("project.issue", "Issues"))
- return res
-
- @api.multi
- def _compute_issue_count(self):
- for project in self:
- project.issue_count = self.env['project.issue'].search_count([('project_id', '=', project.id), '|', ('stage_id.fold', '=', False), ('stage_id', '=', False)])
-
- def _issue_needaction_count(self):
- issue_data = self.env['project.issue'].read_group([('project_id', 'in', self.ids), ('message_needaction', '=', True)], ['project_id'], ['project_id'])
- result = dict((data['project_id'][0], data['project_id_count']) for data in issue_data)
- for project in self:
- project.issue_needaction_count = int(result.get(project.id, 0))
-
- @api.onchange('use_issues', 'use_tasks')
- def _on_change_use_tasks_or_issues(self):
- if self.use_tasks and not self.use_issues:
- self.alias_model = 'project.task'
- elif not self.use_tasks and self.use_issues:
- self.alias_model = 'project.issue'
-
- @api.multi
- def write(self, vals):
- res = super(Project, self).write(vals)
- if 'active' in vals:
- # archiving/unarchiving a project does it on its issues, too
- issues = self.with_context(active_test=False).mapped('issue_ids')
- issues.write({'active': vals['active']})
- return res
diff --git a/addons/project_issue/models/project_task_type.py b/addons/project_issue/models/project_task_type.py
deleted file mode 100644
index 07515890ba0..00000000000
--- a/addons/project_issue/models/project_task_type.py
+++ /dev/null
@@ -1,12 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import models
-
-
-class ProjectStage(models.Model):
- _inherit = ['project.task.type']
-
- def _get_mail_template_id_domain(self):
- domain = super(ProjectStage, self)._get_mail_template_id_domain()
- return ['|'] + domain + [('model', '=', 'project.issue')]
diff --git a/addons/project_issue/models/res_partner.py b/addons/project_issue/models/res_partner.py
deleted file mode 100644
index 91dd0af3eec..00000000000
--- a/addons/project_issue/models/res_partner.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-from odoo import fields, models
-
-
-class ResPartner(models.Model):
- _inherit = 'res.partner'
-
- issue_count = fields.Integer(compute='_compute_issue_count', string='# Issues')
-
- def _compute_issue_count(self):
- Issue = self.env['project.issue']
- for partner in self:
- partner.issue_count = Issue.search_count([('partner_id', 'child_of', partner.commercial_partner_id.id)])