# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import ast import datetime from odoo import api, fields, models, _ from odoo.tools.safe_eval import safe_eval class Team(models.Model): _name = 'crm.team' _inherit = ['mail.alias.mixin', 'crm.team'] _description = 'Sales Team' use_leads = fields.Boolean('Leads', help="Check this box to filter and qualify incoming requests as leads before converting them into opportunities and assigning them to a salesperson.") use_opportunities = fields.Boolean('Pipeline', default=True, help="Check this box to manage a presales process with opportunities.") alias_id = fields.Many2one( 'mail.alias', string='Alias', ondelete="restrict", required=True, help="The email address associated with this channel. New emails received will automatically create new leads assigned to the channel.") # statistics about leads / opportunities / both lead_unassigned_count = fields.Integer( string='# Unassigned Leads', compute='_compute_lead_unassigned_count') lead_all_assigned_month_count = fields.Integer( string='# Leads/Opps assigned this month', compute='_compute_lead_all_assigned_month_count', help="Number of leads and opportunities assigned this last month.") opportunities_count = fields.Integer( string='# Opportunities', compute='_compute_opportunities_data') opportunities_amount = fields.Monetary( string='Opportunities Revenues', compute='_compute_opportunities_data') opportunities_overdue_count = fields.Integer( string='# Overdue Opportunities', compute='_compute_opportunities_overdue_data') opportunities_overdue_amount = fields.Monetary( string='Overdue Opportunities Revenues', compute='_compute_opportunities_overdue_data',) # alias: improve fields coming from _inherits, use inherited to avoid replacing them alias_user_id = fields.Many2one( 'res.users', related='alias_id.alias_user_id', inherited=True, domain=lambda self: [('groups_id', 'in', self.env.ref('sales_team.group_sale_salesman_all_leads').id)]) def _compute_lead_unassigned_count(self): leads_data = self.env['crm.lead'].read_group([ ('team_id', 'in', self.ids), ('type', '=', 'lead'), ('user_id', '=', False), ], ['team_id'], ['team_id']) counts = {datum['team_id'][0]: datum['team_id_count'] for datum in leads_data} for team in self: team.lead_unassigned_count = counts.get(team.id, 0) def _compute_lead_all_assigned_month_count(self): limit_date = datetime.datetime.now() - datetime.timedelta(days=30) leads_data = self.env['crm.lead'].read_group([ ('team_id', 'in', self.ids), ('date_open', '>=', fields.Datetime.to_string(limit_date)), ('user_id', '!=', False), ], ['team_id'], ['team_id']) counts = {datum['team_id'][0]: datum['team_id_count'] for datum in leads_data} for team in self: team.lead_all_assigned_month_count = counts.get(team.id, 0) def _compute_opportunities_data(self): opportunity_data = self.env['crm.lead'].read_group([ ('team_id', 'in', self.ids), ('probability', '<', 100), ('type', '=', 'opportunity'), ], ['expected_revenue:sum', 'team_id'], ['team_id']) counts = {datum['team_id'][0]: datum['team_id_count'] for datum in opportunity_data} amounts = {datum['team_id'][0]: datum['expected_revenue'] for datum in opportunity_data} for team in self: team.opportunities_count = counts.get(team.id, 0) team.opportunities_amount = amounts.get(team.id, 0) def _compute_opportunities_overdue_data(self): opportunity_data = self.env['crm.lead'].read_group([ ('team_id', 'in', self.ids), ('probability', '<', 100), ('type', '=', 'opportunity'), ('date_deadline', '<', fields.Date.to_string(fields.Datetime.now())) ], ['expected_revenue', 'team_id'], ['team_id']) counts = {datum['team_id'][0]: datum['team_id_count'] for datum in opportunity_data} amounts = {datum['team_id'][0]: (datum['expected_revenue']) for datum in opportunity_data} for team in self: team.opportunities_overdue_count = counts.get(team.id, 0) team.opportunities_overdue_amount = amounts.get(team.id, 0) @api.onchange('use_leads', 'use_opportunities') def _onchange_use_leads_opportunities(self): if not self.use_leads and not self.use_opportunities: self.alias_name = False # ------------------------------------------------------------ # ORM # ------------------------------------------------------------ def write(self, vals): result = super(Team, self).write(vals) if 'use_leads' in vals or 'use_opportunities' in vals: for team in self: alias_vals = team._alias_get_creation_values() team.write({ 'alias_name': alias_vals.get('alias_name', team.alias_name), 'alias_defaults': alias_vals.get('alias_defaults'), }) return result # ------------------------------------------------------------ # MESSAGING # ------------------------------------------------------------ def _alias_get_creation_values(self): values = super(Team, self)._alias_get_creation_values() values['alias_model_id'] = self.env['ir.model']._get('crm.lead').id if self.id: if not self.use_leads and not self.use_opportunities: values['alias_name'] = False values['alias_defaults'] = defaults = ast.literal_eval(self.alias_defaults or "{}") has_group_use_lead = self.env.user.has_group('crm.group_use_lead') defaults['type'] = 'lead' if has_group_use_lead and self.use_leads else 'opportunity' defaults['team_id'] = self.id return values # ------------------------------------------------------------ # ACTIONS # ------------------------------------------------------------ #TODO JEM : refactor this stuff with xml action, proper customization, @api.model def action_your_pipeline(self): action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_action_pipeline") user_team_id = self.env.user.sale_team_id.id if user_team_id: # To ensure that the team is readable in multi company user_team_id = self.search([('id', '=', user_team_id)], limit=1).id else: user_team_id = self.search([], limit=1).id action['help'] = _("""

Add new opportunities

Looks like you are not a member of a Sales Team. You should add yourself as a member of one of the Sales Team.

""") if user_team_id: action['help'] += _("

As you don't belong to any Sales Team, Odoo opens the first one by default.

") action_context = safe_eval(action['context'], {'uid': self.env.uid}) if user_team_id: action_context['default_team_id'] = user_team_id action['context'] = action_context return action def _compute_dashboard_button_name(self): super(Team, self)._compute_dashboard_button_name() team_with_pipelines = self.filtered(lambda el: el.use_opportunities) team_with_pipelines.update({'dashboard_button_name': _("Pipeline")}) def action_primary_channel_button(self): if self.use_opportunities: return self.env["ir.actions.actions"]._for_xml_id("crm.crm_case_form_view_salesteams_opportunity") return super(Team,self).action_primary_channel_button() def _graph_get_model(self): if self.use_opportunities: return 'crm.lead' return super(Team,self)._graph_get_model() def _graph_date_column(self): if self.use_opportunities: return 'create_date' return super(Team,self)._graph_date_column() def _graph_y_query(self): if self.use_opportunities: return 'count(*)' return super(Team,self)._graph_y_query() def _extra_sql_conditions(self): if self.use_opportunities: return "AND type LIKE 'opportunity'" return super(Team,self)._extra_sql_conditions() def _graph_title_and_key(self): if self.use_opportunities: return ['', _('New Opportunities')] # no more title return super(Team,self)._graph_title_and_key()