diff options
| author | qmo-odoo <qmo@odoo.com> | 2018-10-16 08:43:33 +0000 |
|---|---|---|
| committer | qmo-odoo <qmo@odoo.com> | 2018-12-05 15:32:53 +0000 |
| commit | 1da63f0ab0fb4212bee46f960c6bbe8ca9251cac (patch) | |
| tree | f324d0acc78a8c0f8c14a90edd303539399f71d5 | |
| parent | e1f9499d2672fb0a346f8ac74552f038f5204a64 (diff) | |
[IMP] salesteam: Simplify sales team config and improved team dashboard
Reason:
*Sales team configuration is too complex with fields you
don't understand and that bring no added value
*Remove the team type, every team should be able to handle
a POS or a website for example
Contains:
*Removal of the team_type, use_quotations, use_leads fields
*Removal of the graph configuration fields
*Adapting the filters that were based on the team_type
*Clear distinction between the team dashboard behavior in crm
from the one in sales
*Cleansing of now dead code due to the removal of the previously
mentionned fields
Task: #1830105
Enterprise PR: #3006
Closes: #28063
52 files changed, 432 insertions, 697 deletions
diff --git a/addons/crm/__init__.py b/addons/crm/__init__.py index 3398a4b470d..853b0b01f31 100644 --- a/addons/crm/__init__.py +++ b/addons/crm/__init__.py @@ -8,8 +8,3 @@ from . import wizard from odoo import api, SUPERUSER_ID - -def uninstall_hook(cr, registry): - env = api.Environment(cr, SUPERUSER_ID, {}) - teams = env['crm.team'].search([('dashboard_graph_model', '=', 'crm.opportunity.report')]) - teams.update({'dashboard_graph_model': None}) diff --git a/addons/crm/__manifest__.py b/addons/crm/__manifest__.py index 4223c2c1ad6..61b2e19d0a0 100644 --- a/addons/crm/__manifest__.py +++ b/addons/crm/__manifest__.py @@ -54,6 +54,5 @@ 'css': ['static/src/css/crm.css'], 'installable': True, 'application': True, - 'auto_install': False, - 'uninstall_hook': 'uninstall_hook', + 'auto_install': False } diff --git a/addons/crm/data/crm_data.xml b/addons/crm/data/crm_data.xml index a2e1f84498a..006c982c03d 100644 --- a/addons/crm/data/crm_data.xml +++ b/addons/crm/data/crm_data.xml @@ -1,13 +1,16 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> <data noupdate="1"> - <record id="sales_team.team_sales_department" model="crm.team"> - <field name="use_opportunities">True</field> - <field name="use_leads">True</field> - <field name="alias_name">sales</field> - <field name="dashboard_graph_model">crm.lead</field> - <field name="company_id" eval="False"/> - <field name="alias_user_id" ref="base.user_admin"/> + <record id="sales_team.salesteam_website_sales" model="crm.team"> + <field name="use_opportunities" eval="False"/> + </record> + + <record id="sales_team.pos_sales_team" model="crm.team"> + <field name="use_opportunities" eval="False"/> + </record> + + <record id="sales_team.ebay_sales_team" model="crm.team"> + <field name="use_opportunities" eval="False"/> </record> <!--default alias for leads--> @@ -29,5 +32,4 @@ <field name="name">Not enough stock</field> </record> </data> - </odoo> diff --git a/addons/crm/data/crm_demo.xml b/addons/crm/data/crm_demo.xml index 1d5e38c586e..d57758c9106 100644 --- a/addons/crm/data/crm_demo.xml +++ b/addons/crm/data/crm_demo.xml @@ -6,14 +6,9 @@ </record> <record id="sales_team.team_sales_department" model="crm.team"> - <field name="member_ids" eval="[(4, ref('base.user_demo'))]"/> - </record> - - <record id="sales_team.crm_team_1" model="crm.team"> - <field name="use_opportunities">True</field> - <field name="use_leads">True</field> - <field name="dashboard_graph_model">crm.lead</field> - <field name="member_ids" eval="[(4, ref('base.user_demo'))]"/> + <field name="alias_name">sales</field> + <field name="alias_user_id" ref="base.user_admin"/> + <field name="company_id" eval="False"/> </record> </data> </odoo> diff --git a/addons/crm/data/crm_lead_data.xml b/addons/crm/data/crm_lead_data.xml index 4a287afb4e8..c5b83c47682 100644 --- a/addons/crm/data/crm_lead_data.xml +++ b/addons/crm/data/crm_lead_data.xml @@ -58,12 +58,7 @@ <field name="default" eval="False"/> <field name="parent_id" eval="ref('mt_lead_lost')"/> <field name="relation_field">team_id</field> - </record> - - <function model="crm.team" name="message_unsubscribe" - eval="[ref('sales_team.team_sales_department'),ref('sales_team.salesteam_website_sales')], [ref('base.partner_root')]"/> - <function model="crm.team" name="message_subscribe" - eval="[ref('sales_team.team_sales_department'),ref('sales_team.salesteam_website_sales')], [ref('base.partner_root')]"/> + </record> <!--Definition of an email template with an empty body that will be used in opportunity mailing. Used to give a basis for email recipients, name and to ease the definition of a further diff --git a/addons/crm/models/crm_team.py b/addons/crm/models/crm_team.py index 622afc23a9a..30d7cc945ea 100644 --- a/addons/crm/models/crm_team.py +++ b/addons/crm/models/crm_team.py @@ -12,33 +12,24 @@ class Team(models.Model): _name = 'crm.team' _inherit = ['mail.alias.mixin', 'crm.team'] _description = 'Sales Channels' + use_opportunities = fields.Boolean('Manage a 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.") - 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', 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.") unassigned_leads_count = fields.Integer( compute='_compute_unassigned_leads_count', - string='Unassigned Leads', readonly=True) + string='Unassigned Leads') opportunities_count = fields.Integer( compute='_compute_opportunities', - string='Number of open opportunities', readonly=True) + string='Number of open opportunities') + overdue_opportunities_count = fields.Integer( + compute='_compute_overdue_opportunities', + string='Number of overdue opportunities') opportunities_amount = fields.Integer( compute='_compute_opportunities', - string='Opportunities Revenues', readonly=True) - dashboard_graph_model = fields.Selection(selection_add=[('crm.lead', 'Pipeline')]) - dashboard_graph_period_pipeline = fields.Selection([ - ('week', 'Within a Week'), - ('month', 'Within a Month'), - ('year', 'Within a Year'), - ], string='Expected to Close', help="The time period this channel's dashboard graph will consider.", - compute="_compute_dashboard_graph_period_pipeline", inverse="_inverse_dashboard_graph_period_pipeline") - dashboard_graph_group_pipeline = fields.Selection([ - ('day', 'Expected Closing Day'), - ('week', 'Expected Closing Week'), - ('month', 'Expected Closing Month'), - ('user', 'Salesperson'), - ('stage', 'Stage'), - ], string='Grouping Method', default='day', help="How this channel's dashboard graph will group the results.") + string='Opportunities Revenues') + overdue_opportunities_amount = fields.Integer( + compute='_compute_overdue_opportunities', + string='Overdue Opportunities Revenues') alias_user_id = fields.Many2one(domain=lambda self: [ ('groups_id', 'in', self.env.ref('sales_team.group_sale_salesman_all_leads').id)]) @@ -58,20 +49,30 @@ class Team(models.Model): ('team_id', 'in', self.ids), ('probability', '<', 100), ('type', '=', 'opportunity'), - ], ['planned_revenue', 'probability', 'team_id'], ['team_id']) + ], ['planned_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['planned_revenue'] * datum['probability'] / 100) for datum in opportunity_data} + amounts = {datum['team_id'][0]: (datum['planned_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_dashboard_graph_period_pipeline(self): - for channel in self: - channel.dashboard_graph_period_pipeline = channel.dashboard_graph_period - - def _inverse_dashboard_graph_period_pipeline(self): - for channel in self.filtered(lambda ch: ch.dashboard_graph_model == 'crm.lead'): - channel.dashboard_graph_period = channel.dashboard_graph_period_pipeline + def _compute_overdue_opportunities(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())) + ], ['planned_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['planned_revenue']) for datum in opportunity_data} + for team in self: + team.overdue_opportunities_count = counts.get(team.id, 0) + team.overdue_opportunities_amount = amounts.get(team.id, 0) + + @api.onchange('use_opportunities') + def _onchange_opportunities(self): + if not self.use_opportunities: + self.alias_name = False def get_alias_model_name(self, vals): return 'crm.lead' @@ -80,54 +81,14 @@ class Team(models.Model): has_group_use_lead = self.env.user.has_group('crm.group_use_lead') values = super(Team, self).get_alias_values() values['alias_defaults'] = defaults = safe_eval(self.alias_defaults or "{}") - defaults['type'] = 'lead' if has_group_use_lead and self.use_leads else 'opportunity' + defaults['type'] = 'lead' if has_group_use_lead else 'opportunity' defaults['team_id'] = self.id - return values - - @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 - if not self.use_opportunities and self.use_leads: - self.use_leads = False - - @api.onchange('team_type') - def _onchange_team_type(self): - if self.team_type == 'sales': - self.use_opportunities = True - self.use_leads = lambda self: self.user_has_groups('crm.group_use_lead') - self.dashboard_graph_model = 'crm.lead' - else: - self.use_opportunities = False - self.use_leads = False - return super(Team, self)._onchange_team_type() - - @api.onchange('dashboard_graph_model') - def _onchange_dashboard_graph_model(self): - if self.dashboard_graph_model == 'crm.lead': - self.dashboard_graph_period_pipeline = self.dashboard_graph_period - self.dashboard_graph_group_pipeline = self.dashboard_graph_group - else: - self.dashboard_graph_period = self.dashboard_graph_period_pipeline - if not self.dashboard_graph_group: - self.dashboard_graph_group = self._fields['dashboard_graph_group'].default(self) - - @api.onchange('dashboard_graph_group_pipeline') - def _onchange_dashboard_graph_group_pipeline(self): - if self.dashboard_graph_group_pipeline == 'stage': - self.dashboard_graph_group = False - else: - self.dashboard_graph_group = self.dashboard_graph_group_pipeline - - @api.constrains('dashboard_graph_model', 'use_opportunities') - def _check_graph_model(self): - if not self.use_opportunities and self.dashboard_graph_model == 'crm.lead': - raise ValidationError(_("You have to enable the Pipeline on your Sales Team to be able to set it as a content for the graph")) + return values @api.multi def write(self, vals): result = super(Team, self).write(vals) - if 'use_leads' in vals or 'alias_defaults' in vals: + if 'alias_defaults' in vals: for team in self: team.alias_id.write(team.get_alias_values()) return result @@ -154,75 +115,36 @@ class Team(models.Model): return action def _compute_dashboard_button_name(self): - opportunity_teams = self.filtered('use_opportunities') - opportunity_teams.update({'dashboard_button_name': _("Pipeline")}) - super(Team, self - opportunity_teams)._compute_dashboard_button_name() + 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: - action = self.env.ref('crm.crm_case_form_view_salesteams_opportunity').read()[0] - return action - return super(Team, self).action_primary_channel_button() - - def _graph_get_dates(self, today): - """ return a coherent start and end date for the dashboard graph according to the graph settings. - """ - if self.dashboard_graph_model == 'crm.lead': - if self.dashboard_graph_group == 'month': - start_date = today.replace(day=1) - elif self.dashboard_graph_group == 'week': - start_date = today - relativedelta(days=today.isocalendar()[2] - 1) - else: - start_date = today - - if self.dashboard_graph_period == 'week': - end_date = today + relativedelta(weeks=1) - elif self.dashboard_graph_period == 'year': - end_date = today + relativedelta(years=1) - else: - end_date = today + relativedelta(months=1) - - # we take the end of the preceding month/week/day if we group by month/week/day - # (to avoid having twice the same month/week/day from different years/month/week) - if self.dashboard_graph_group == 'month': - end_date = end_date.replace(day=1) - relativedelta(days=1) - elif self.dashboard_graph_group == 'week': - end_date -= relativedelta(days=end_date.isocalendar()[2]) - else: - end_date -= relativedelta(days=1) - - return [start_date, end_date] - return super(Team, self)._graph_get_dates(today) - - def _get_graph(self): - graph_datas = super(Team, self)._get_graph() - if self.dashboard_graph_model == 'crm.lead' and self.dashboard_graph_group_pipeline == 'stage': - stage_ids = [d['label'] for d in graph_datas[0]['values'] if d['label'] is not None] - stage_data = self.env['crm.stage'].browse(stage_ids).read(['sequence', 'name']) - stage_data = {d['id']: {'name': d['name'], 'sequence': d['sequence']} for d in stage_data} - # use "Undefined" stage for unset stage records - stage_data[None] = {'name': _('Undefined'), 'sequence': -1} - graph_datas[0]['values'] = sorted(graph_datas[0]['values'], key=lambda el: stage_data[el['label']]['sequence']) - for gdata in graph_datas[0]['values']: - gdata['label'] = stage_data[gdata['label']]['name'] - return graph_datas + return self.env.ref('crm.crm_case_form_view_salesteams_opportunity').read()[0] + 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.dashboard_graph_model == 'crm.lead': - return 'date_deadline' - return super(Team, self)._graph_date_column() - - def _graph_x_query(self): - if self.dashboard_graph_model == 'crm.lead' and self.dashboard_graph_group_pipeline == 'stage': - return 'stage_id' - return super(Team, self)._graph_x_query() + if self.use_opportunities: + return 'create_date' + return super(Team,self)._graph_date_column() def _graph_y_query(self): - if self.dashboard_graph_model == 'crm.lead': - return 'SUM(expected_revenue)' - return super(Team, self)._graph_y_query() + 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.dashboard_graph_model == 'crm.lead': - return ['', _('Pipeline: Expected Revenue')] # no more title - return super(Team, self)._graph_title_and_key() + if self.use_opportunities: + return ['', _('New Opportunities')] # no more title + return super(Team,self)._graph_title_and_key() diff --git a/addons/crm/views/crm_lead_views.xml b/addons/crm/views/crm_lead_views.xml index 99413f9eae3..3d02eed3e04 100644 --- a/addons/crm/views/crm_lead_views.xml +++ b/addons/crm/views/crm_lead_views.xml @@ -114,7 +114,7 @@ <group> <field name="user_id" domain="[('share', '=', False)]" context="{'default_groups_ref': ['base.group_user', 'base.group_partner_manager', 'sales_team.group_sale_salesman_all_leads'], 'team_id': team_id}"/> - <field name="team_id" widget="selection" domain="[('use_leads','=',True)]"/> + <field name="team_id" widget="selection"/> <field name="type" invisible="1"/> </group> <group> @@ -887,7 +887,7 @@ for rec in records: <menuitem id="sales_team_menu_team_pipeline" name="Team Pipelines" parent="crm_menu_sales" - action="sales_team.crm_team_salesteams_act" + action="sales_team.crm_team_salesteams_pipelines_act" sequence="4"/> <menuitem id="res_partner_menu_customer" diff --git a/addons/crm/views/crm_menu_views.xml b/addons/crm/views/crm_menu_views.xml new file mode 100644 index 00000000000..cbcfe85b201 --- /dev/null +++ b/addons/crm/views/crm_menu_views.xml @@ -0,0 +1,26 @@ +<?xml version="1.0"?> +<odoo> + <menuitem + id="crm_menu_sales" + name="Sales" + parent="crm_menu_root" + sequence="1"/> + + <menuitem id="sales_team_menu_team_pipeline" + name="Team Pipelines" + parent="crm_menu_sales" + action="crm_team_salesteams_pipelines_act" + sequence="4"/> + + <menuitem id="res_partner_menu_customer" + name="Customers" + parent="crm_menu_sales" + action="base.action_partner_form" + sequence="5"/> + + <menuitem id="crm_menu_leads" + name="Leads" + parent="crm_menu_root" + groups="crm.group_use_lead" + sequence="5"/> +</odoo>
\ No newline at end of file diff --git a/addons/crm/views/crm_team_views.xml b/addons/crm/views/crm_team_views.xml index 30c7a299b4d..2a128080bf4 100644 --- a/addons/crm/views/crm_team_views.xml +++ b/addons/crm/views/crm_team_views.xml @@ -59,13 +59,29 @@ </field> </record> + <record id="crm_lead_action_team_overdue_opportunity" model="ir.actions.act_window"> + <field name="name">Overdue Opportunities</field> + <field name="res_model">crm.lead</field> + <field name="view_mode">kanban,tree,graph,form,calendar,pivot</field> + <field name="domain">[('type','=','opportunity')]</field> + <field name="view_id" ref="crm.crm_case_kanban_view_leads"/> + <field name="search_view_id" ref="crm.view_crm_case_opportunities_filter"/> + <field name="context">{ + 'search_default_team_id': [active_id], + 'search_default_overdue_opp': 1, + 'default_team_id': active_id, + 'default_type': 'opportunity', + 'default_user_id': uid, + } + </field> + </record> + <record id="action_report_crm_lead_salesteam" model="ir.actions.act_window"> <field name="name">Leads Analysis</field> <field name="res_model">crm.lead</field> - <field name="context">{"search_default_month":1}</field> + <field name="context">{'search_default_team_id': [active_id], 'time_ranges': {'field':'create_date', 'range':'last_30_days'}}</field> <field name="view_mode">graph,pivot,tree,form</field> - <field name="domain">[('type', '=', 'lead'),('team_id', '=', active_id)]</field> - <field name="context">{'active_test': True}</field> + <field name="search_view_id" ref="crm.crm_opportunity_report_view_search"/> <field name="help">Leads Analysis allows you to check different CRM related information like the treatment delays or number of leads per state. You can sort out your leads analysis by different groups to get accurate grained analysis.</field> </record> @@ -73,8 +89,8 @@ <field name="name">Pipeline Analysis</field> <field name="res_model">crm.lead</field> <field name="view_mode">graph,pivot,tree,form</field> - <field name="domain">[('type', '=', 'opportunity'), ('team_id', '=', active_id)]</field> - <field name="context">{'active_test': True}</field> + <field name="search_view_id" ref="crm.crm_opportunity_report_view_search"/> + <field name="context">{'search_default_team_id': [active_id], 'form_view_ref': 'crm.crm_case_form_view_oppor', 'tree_view_ref': 'crm.crm_case_tree_view_oppor','search_default_opportunity': True, 'time_ranges': {'field':'create_date', 'range':'last_365_days'}}</field> <field name="help">Opportunities Analysis gives you an instant access to your opportunities with information such as the expected revenue, planned cost, missed deadlines or the number of interactions per opportunity. This report is mainly used by the sales manager in order to do the periodic review with the channels of the sales pipeline.</field> </record> @@ -85,20 +101,16 @@ <field name="priority">12</field> <field name="arch" type="xml"> <xpath expr="//div[@name='options_active']" position="inside"> - <div class="o_row"> + <div name="opportunities" class="o_row" invisible="1"> <field name="use_opportunities"/> <label for="use_opportunities"/> - <span groups="crm.group_use_lead" class="o_row" attrs="{'invisible': [('use_opportunities', '=', False)]}"> - <field name="use_leads"/> - <label for="use_leads" string="Leads"/> - </span> </div> </xpath> <xpath expr="//field[@name='user_id']" position="after"> <label for="alias_name" string="Email Alias" - attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/> + attrs="{'invisible': [('use_opportunities', '=', False)]}"/> <div class="oe_inline" name="alias_def" - attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"> + attrs="{'invisible': [('use_opportunities', '=', False)]}"> <field name="alias_id" class="oe_read_only oe_inline" string="Email Alias" required="0"/> <div class="oe_edit_only oe_inline" name="edit_alias" style="display: inline;" > @@ -106,23 +118,11 @@ </div> </div> <field name="alias_contact" class="oe_inline" - groups="base.group_no_one" string="Accept Emails From" - attrs="{'invisible': [('use_leads', '=', False), ('use_opportunities', '=', False)]}"/> + attrs="{'invisible': [('use_opportunities', '=', False)]}"/> <field name="alias_user_id" string="Assign to"/> </xpath> - <field name="dashboard_graph_period" position="attributes"> - <attribute name="attrs">{'invisible': [('dashboard_graph_model', '=', 'crm.lead')]}</attribute> - </field> - <field name="dashboard_graph_period" position="after"> - <field name="dashboard_graph_period_pipeline" attrs="{'invisible': [('dashboard_graph_model', '!=', 'crm.lead')]}"/> - </field> - <field name="dashboard_graph_group" position="attributes"> - <attribute name="attrs">{'invisible': [('dashboard_graph_model', '=', 'crm.lead')]}</attribute> - </field> - <field name="dashboard_graph_group" position="after"> - <field name="dashboard_graph_group_pipeline" attrs="{'invisible': [('dashboard_graph_model', '!=', 'crm.lead')]}"/> - </field> + </field> </record> @@ -183,23 +183,24 @@ <field name="alias_id"/> <field name="alias_name"/> <field name="alias_domain"/> - <field name="use_leads"/> <field name="use_opportunities"/> </xpath> <xpath expr="//div[hasclass('o_primary')]" position="after"> - <div t-if="record.use_leads.raw_value and record.alias_name.value and record.alias_domain.value"> + <div t-if="record.alias_name.value and record.alias_domain.value"> <small t-translation="off"><i class="fa fa-envelope-o" aria-label="Leads" title="Leads" role="img"></i>&nbsp; <field name="alias_id"/></small> </div> </xpath> <xpath expr="//t[@name='first_options']" position="after"> <div class="row" t-if="record.unassigned_leads_count.raw_value"> - <a name="%(crm_case_form_view_salesteams_lead)d" type="action" context="{'search_default_unassigned': True}"> - <field name="unassigned_leads_count"/> - <t t-if="record.unassigned_leads_count.raw_value == 1">Unassigned Lead</t> - <t t-else="">Unassigned Leads</t> - </a> + <div class="col-8"> + <a name="%(crm_case_form_view_salesteams_lead)d" type="action" context="{'search_default_unassigned': True}"> + <field name="unassigned_leads_count"/> + <t t-if="record.unassigned_leads_count.raw_value == 1">Unassigned Lead</t> + <t t-else="">Unassigned Leads</t> + </a> + </div> </div> <div class="row" t-if="record.opportunities_count.raw_value"> <div class="col-8"> @@ -209,14 +210,26 @@ <t t-else="">Open Opportunities</t> </a> </div> - <div class="col-4 text-right"> + <div class="col-4 text-right text-truncate"> <field name="opportunities_amount" widget="monetary"/> </div> </div> + <div class="row" t-if="record.overdue_opportunities_count.raw_value"> + <div class="col-8"> + <a name="%(crm_lead_action_team_overdue_opportunity)d" type="action"> + <field name="overdue_opportunities_count"/> + <t t-if="record.overdue_opportunities_count.raw_value == 1">Overdue Opportunity</t> + <t t-else="">Overdue Opportunities</t> + </a> + </div> + <div class="col-4 text-right text-truncate"> + <field name="overdue_opportunities_amount" widget="monetary"/> + </div> + </div> </xpath> <xpath expr="//div[hasclass('o_kanban_manage_view')]" position="inside"> - <div t-if="record.use_leads.raw_value" groups="crm.group_use_lead"> + <div groups="crm.group_use_lead"> <a name="%(crm_case_form_view_salesteams_lead)d" type="action"> Leads </a> @@ -237,7 +250,7 @@ </xpath> <xpath expr="//div[hasclass('o_kanban_manage_reports')]" position="inside"> - <div t-if="record.use_leads.raw_value" groups="crm.group_use_lead"> + <div groups="crm.group_use_lead"> <a name="%(action_report_crm_lead_salesteam)d" type="action"> Leads </a> @@ -247,9 +260,9 @@ Opportunities </a> </div> - <div class="mb4" t-if="record.use_opportunities.raw_value"> + <div t-if="record.use_opportunities.raw_value"> <a name="%(crm.crm_activity_report_action_team)d" type="action"> - Activities Analysis + Activities </a> </div> </xpath> diff --git a/addons/point_of_sale/views/pos_config_view.xml b/addons/point_of_sale/views/pos_config_view.xml index f549fbc2d2f..5669bc6ce85 100644 --- a/addons/point_of_sale/views/pos_config_view.xml +++ b/addons/point_of_sale/views/pos_config_view.xml @@ -466,7 +466,7 @@ </div> </div> <h2>Accounting</h2> - <div class="row mt16 o_settings_container"> + <div class="row mt16 o_settings_container" id="accounting_section"> <div class="col-12 col-lg-6 o_setting_box"> <div class="o_setting_right_pane"> <span class="o_form_label">Journal Entries</span> diff --git a/addons/pos_sale/data/pos_sale_data.xml b/addons/pos_sale/data/pos_sale_data.xml index 08ff06db59d..c57f8d7dccf 100644 --- a/addons/pos_sale/data/pos_sale_data.xml +++ b/addons/pos_sale/data/pos_sale_data.xml @@ -1,13 +1,11 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> <data noupdate="1"> - <record id="pos_sales_team" model="crm.team"> - <field name="name">Point of Sale</field> - <field name="team_type" eval="'pos'"/> - <field name="dashboard_graph_model">sale.report</field> + <record id="sales_team.pos_sales_team" model="crm.team"> + <field name="active" eval="True"/> </record> <record id="point_of_sale.pos_config_main" model="pos.config"> - <field name="crm_team_id" ref="pos_sales_team"/> + <field name="crm_team_id" ref="sales_team.pos_sales_team"/> </record> </data> </odoo> diff --git a/addons/pos_sale/models/crm_team.py b/addons/pos_sale/models/crm_team.py index 76b4e298080..a0cc50b5d6d 100644 --- a/addons/pos_sale/models/crm_team.py +++ b/addons/pos_sale/models/crm_team.py @@ -2,6 +2,7 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from odoo import api, fields, models, _ +from odoo.exceptions import UserError, ValidationError from datetime import datetime import pytz @@ -9,112 +10,16 @@ import pytz class CrmTeam(models.Model): _inherit = 'crm.team' - team_type = fields.Selection(selection_add=[('pos', 'Point of Sale')]) pos_config_ids = fields.One2many('pos.config', 'crm_team_id', string="Point of Sales") pos_sessions_open_count = fields.Integer(string='Open POS Sessions', compute='_compute_pos_sessions_open_count') pos_order_amount_total = fields.Float(string="Session Sale Amount", compute='_compute_pos_order_amount_total') - dashboard_graph_group_pos = fields.Selection([ - ('day', 'Day'), - ('week', 'Week'), - ('month', 'Month'), - ('user', 'Salesperson'), - ('pos', 'Point of Sale'), - ], string='POS Grouping', default='day', help="How this channel's dashboard graph will group the results.") - - @api.onchange('dashboard_graph_group_pos') - def _onchange_dashboard_graph_group_pos(self): - if self.dashboard_graph_group_pos == 'pos': - self.dashboard_graph_group = False - else: - self.dashboard_graph_group = self.dashboard_graph_group_pos def _compute_pos_sessions_open_count(self): - for team in self.filtered(lambda t: t.team_type == 'pos'): + for team in self: team.pos_sessions_open_count = self.env['pos.session'].search_count([('config_id.crm_team_id', '=', team.id), ('state', '=', 'opened')]) def _compute_pos_order_amount_total(self): - for team in self.filtered(lambda t: t.team_type == 'pos'): + for team in self: team.pos_order_amount_total = sum(self.env['report.pos.order'].search( [('session_id', 'in', team.pos_config_ids.mapped('session_ids').filtered(lambda s: s.state == 'opened').ids)] ).mapped('price_total')) - - def _graph_data(self, start_date, end_date): - """ If the type of the sales team is point of sale ('pos'), the graph will display the sales data. - The override here is to get data from pos.order instead of sale.order. - """ - offset = datetime.now(pytz.timezone(self.env.user.tz or 'UTC')).utcoffset() - min_date = fields.Datetime.to_string(datetime.combine(start_date, datetime.min.time()) - offset) - max_date = fields.Datetime.to_string(datetime.combine(end_date, datetime.max.time()) - offset) - if self.team_type == 'pos': - result = [] - if self.dashboard_graph_group_pos == 'pos': - order_data = self.env['report.pos.order'].read_group( - domain=[ - ('date', '>=', min_date), - ('date', '<=', max_date), - ('config_id', 'in', self.pos_config_ids.ids), - ('state', 'in', ['paid', 'done', 'invoiced'])], - fields=['config_id', 'price_total'], - groupby=['config_id'] - ) - appended_config_ids = set() - for data_point in order_data: - result.append({'x_value': self.env['pos.config'].browse(data_point.get('config_id')[0]).name, 'y_value': data_point.get('price_total')}) - appended_config_ids.add(data_point.get('config_id')) - for config_id in set(self.pos_config_ids.ids) - appended_config_ids: - result.append({'x_value': self.env['pos.config'].browse(config_id).name, 'y_value': 0}) - - elif self.dashboard_graph_group_pos == 'user': - order_data = self.env['report.pos.order'].read_group( - domain=[ - ('date', '>=', min_date), - ('date', '<=', max_date), - ('config_id', 'in', self.pos_config_ids.ids), - ('state', 'in', ['paid', 'done', 'invoiced'])], - fields=['user_id', 'price_total'], - groupby=['user_id'] - ) - for data_point in order_data: - result.append({'x_value': data_point.get('user_id')[0], 'y_value': data_point.get('price_total')}) - - elif self.dashboard_graph_group_pos in ['day', 'week', 'month']: - # locale en_GB is used to be able to obtain the datetime from the string returned by read_group - # /!\ do not use en_US as it's not ISO-standard and does not match datetime's library - order_data = self.env['report.pos.order'].with_context(lang='en_GB').read_group( - domain=[ - ('date', '>=', min_date), - ('date', '<=', max_date), - ('config_id', 'in', self.pos_config_ids.ids), - ('state', 'in', ['paid', 'done', 'invoiced'])], - fields=['date', 'price_total'], - groupby=['date:' + self.dashboard_graph_group_pos] - ) - if self.dashboard_graph_group_pos == 'day': - for data_point in order_data: - result.append({'x_value': fields.Date.to_date(datetime.strptime(data_point.get('date:day'), "%d %b %Y")), 'y_value': data_point.get('price_total')}) - elif self.dashboard_graph_group_pos == 'week': - for data_point in order_data: - result.append({'x_value': int(data_point.get('date:week')[1:3]), 'y_value': data_point.get('price_total')}) - elif self.dashboard_graph_group_pos == 'month': - for data_point in order_data: - result.append({'x_value': fields.datetime.strptime(data_point.get('date:month'), "%B %Y").month, 'y_value': data_point.get('price_total')}) - return result - - return super(CrmTeam, self)._graph_data(start_date, end_date) - - def _compute_dashboard_button_name(self): - pos_teams = self.filtered(lambda team: team.team_type == 'pos') - pos_teams.update({'dashboard_button_name': _("Dashboard")}) - super(CrmTeam, self - pos_teams)._compute_dashboard_button_name() - - def action_primary_channel_button(self): - if self.team_type == 'pos': - action = self.env.ref('point_of_sale.action_pos_config_kanban').read()[0] - action['context'] = {'search_default_crm_team_id': self.id} - return action - return super(CrmTeam, self).action_primary_channel_button() - - def _graph_title_and_key(self): - if self.team_type == 'pos': - return ['', _('Sales: Untaxed Amount')] # no more title - return super(CrmTeam, self)._graph_title_and_key() diff --git a/addons/pos_sale/models/pos_config.py b/addons/pos_sale/models/pos_config.py index 559499fcd08..76283f0549c 100644 --- a/addons/pos_sale/models/pos_config.py +++ b/addons/pos_sale/models/pos_config.py @@ -7,7 +7,14 @@ from odoo import fields, models class PosConfig(models.Model): _inherit = 'pos.config' + def _get_default_pos_team(self): + try: + team = self.env.ref('sales_team.pos_sales_team') + return team if team.active else None + except ValueError: + return None + crm_team_id = fields.Many2one( - 'crm.team', string="Sales Team", domain=[('team_type', '=', 'pos')], - default=lambda self: self.env['crm.team'].search([('team_type', '=', 'pos')], limit=1).id, - help="This Point of sale's sales will be related to this Sales Team.") + 'crm.team', string="Sales Team", + help="This Point of sale's sales will be related to this Sales Team.", + default=_get_default_pos_team) diff --git a/addons/pos_sale/report/sale_report.py b/addons/pos_sale/report/sale_report.py index 2c147e97083..23fa127b0cd 100644 --- a/addons/pos_sale/report/sale_report.py +++ b/addons/pos_sale/report/sale_report.py @@ -100,7 +100,6 @@ class SaleReport(models.Model): u.factor, config.crm_team_id ''' - current = '(SELECT %s FROM %s GROUP BY %s)' % (select_, from_, groupby_) return '%s UNION ALL %s' % (res, current) diff --git a/addons/pos_sale/security/pos_sale_security.xml b/addons/pos_sale/security/pos_sale_security.xml index 62f9afaa838..435ff8ad5f1 100644 --- a/addons/pos_sale/security/pos_sale_security.xml +++ b/addons/pos_sale/security/pos_sale_security.xml @@ -3,7 +3,6 @@ <record id="pos_sale_rule_pos_channel_pos_manager" model="ir.rule"> <field name="name">POS Sales Team</field> <field name="model_id" ref="sales_team.model_crm_team"/> - <field name="domain_force">[('team_type', '=', 'pos')]</field> <field name="groups" eval="[(4, ref('point_of_sale.group_pos_manager'))]"/> </record> </odoo> diff --git a/addons/pos_sale/views/pos_config_views.xml b/addons/pos_sale/views/pos_config_views.xml index 70b14788a02..1a8750163e3 100644 --- a/addons/pos_sale/views/pos_config_views.xml +++ b/addons/pos_sale/views/pos_config_views.xml @@ -6,14 +6,18 @@ <field name="model">pos.config</field> <field name="inherit_id" ref="point_of_sale.pos_config_view_form"/> <field name="arch" type="xml"> - <div id="company" position="after"> - <div class="row mt16 o_settings_container" id="crm_team_id" groups="base.group_no_one"> - <div class="col-12 col-lg-6 o_setting_box"> - <div class="o_setting_right_pane"> - <label for="crm_team_id" class="col-lg-3"/> - <field name="crm_team_id"/> + <div id="accounting_section" position="after"> + <h2>Sales Reporting</h2> + <div class="row mt16 o_settings_container"> + <div class="col-12 col-lg-6 o_setting_box"> + <div class="o_setting_right_pane"> + <span class="o_form_label">Sales Team</span> + <div class="text-muted"> + Sales are reported to the following sales team + </div> + <field name="crm_team_id"/> + </div> </div> - </div> </div> </div> </field> diff --git a/addons/pos_sale/views/sales_team_views.xml b/addons/pos_sale/views/sales_team_views.xml index 52883ea2849..06567e5f7c3 100644 --- a/addons/pos_sale/views/sales_team_views.xml +++ b/addons/pos_sale/views/sales_team_views.xml @@ -1,22 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> - <record id="crm_team_view_form_inherit_pos_sale" model="ir.ui.view"> - <field name="name">crm.team.form</field> - <field name="model">crm.team</field> - <field name="inherit_id" ref="sales_team.crm_team_view_form"/> - <field name="arch" type="xml"> - <xpath expr="//field[@name='dashboard_graph_model']" position="attributes"> - <attribute name="attrs">{'readonly': [('team_type', 'in', ['pos', 'ebay'])]}</attribute> - </xpath> - <xpath expr="//field[@name='dashboard_graph_group']" position="attributes"> - <attribute name="attrs">{'invisible': ['|', ('dashboard_graph_model', '=', 'crm.lead'), ('team_type', '=', 'pos')]}</attribute> - </xpath> - <xpath expr="//field[@name='dashboard_graph_group']" position="after"> - <field name="dashboard_graph_group_pos" string="Group by" attrs="{'invisible': [('team_type', '!=', 'pos')]}"/> - </xpath> - </field> - </record> - <record id="view_pos_session_search_inherit_pos_sale" model="ir.ui.view"> <field name="name">pos.session.search.view</field> <field name="model">pos.session</field> @@ -54,40 +37,8 @@ <field name="inherit_id" ref="sales_team.crm_team_salesteams_view_kanban"/> <field name="arch" type="xml"> <data> - <xpath expr="//templates" position="before"> - <field name="team_type"/> - </xpath> - - <xpath expr="//div[@name='orders_to_invoice']" position="attributes"> - <attribute name="attrs">{'invisible': [('team_type', '=', 'pos')]}</attribute> - </xpath> - - <xpath expr="//div[@name='sales_report']" position="attributes"> - <attribute name="attrs">{'invisible': [('team_type', '=', 'pos')]}</attribute> - </xpath> - - <xpath expr="//div[@name='invoices_report']" position="attributes"> - <attribute name="attrs">{'invisible': [('team_type', '=', 'pos')]}</attribute> - </xpath> - - <xpath expr="//div[hasclass('o_kanban_manage_view')]" position="attributes"> - <attribute name="attrs">{'invisible': [('team_type', '=', 'pos')]}</attribute> - </xpath> - - <xpath expr="//div[hasclass('o_kanban_manage_new')]" position="attributes"> - <attribute name="attrs">{'invisible': [('team_type', '=', 'pos')]}</attribute> - </xpath> - - <xpath expr="//div[hasclass('o_kanban_manage_reports')]" position="inside"> - <t t-if="record.team_type.raw_value == 'pos'"> - <a name="%(point_of_sale.action_report_pos_order_all)d" type="action"> - Sales - </a> - </t> - </xpath> - <xpath expr="//t[@name='first_options']" position="before"> - <div class="row" t-if="record.team_type.raw_value == 'pos'"> + <div class="row" t-if="record.pos_sessions_open_count.raw_value"> <div class="col-8"> <a name="%(pos_session_action_from_crm_team)d" type="action"> <field name="pos_sessions_open_count"/> diff --git a/addons/sale/__init__.py b/addons/sale/__init__.py index abfa039b9ef..a92bcece423 100644 --- a/addons/sale/__init__.py +++ b/addons/sale/__init__.py @@ -11,14 +11,3 @@ import odoo from odoo import api, SUPERUSER_ID -def uninstall_hook(cr, registry): - def update_dashboard_graph_model(dbname): - db_registry = odoo.modules.registry.Registry.new(dbname) - with api.Environment.manage(), db_registry.cursor() as cr: - env = api.Environment(cr, SUPERUSER_ID, {}) - if 'crm.team' in env: - recs = env['crm.team'].search([]) - for rec in recs: - rec._onchange_team_type() - - cr.after("commit", partial(update_dashboard_graph_model, cr.dbname)) diff --git a/addons/sale/data/sale_data.xml b/addons/sale/data/sale_data.xml index 2b8b4ebd0b2..8abe2f432d8 100644 --- a/addons/sale/data/sale_data.xml +++ b/addons/sale/data/sale_data.xml @@ -1,12 +1,6 @@ <?xml version="1.0" encoding="utf-8"?> <odoo> <data noupdate="1"> - <record model="crm.team" id="sales_team.team_sales_department"> - <field name="use_quotations" eval="True"/> - <field name="use_invoices" eval="True"/> - <field name="dashboard_graph_model">sale.report</field> - </record> - <!-- Share Button in action menu --> <record id="model_sale_order_action_share" model="ir.actions.server"> <field name="name">Share</field> diff --git a/addons/sale/data/sale_demo.xml b/addons/sale/data/sale_demo.xml index acb8e8b688b..9f7106b6e71 100644 --- a/addons/sale/data/sale_demo.xml +++ b/addons/sale/data/sale_demo.xml @@ -17,10 +17,7 @@ </record> <record model="crm.team" id="sales_team.crm_team_1"> - <field name="use_quotations" eval="True"/> - <field name="use_invoices" eval="True"/> <field name="invoiced_target">40000</field> - <field name="dashboard_graph_model">sale.report</field> </record> <record id="sale_order_1" model="sale.order"> @@ -733,11 +730,6 @@ Thanks!</field> <field name="confirmation_date" eval="datetime.now() - relativedelta(months=1)"/> </record> - <function model="crm.team" name="message_unsubscribe" - eval="[ref('sales_team.team_sales_department'),ref('sales_team.salesteam_website_sales')], [ref('base.partner_root')]"/> - <function model="crm.team" name="message_subscribe" - eval="[ref('sales_team.team_sales_department'),ref('sales_team.salesteam_website_sales')], [ref('base.partner_root')]"/> - <record id="product_product_1_product_template" model="product.template"> <field name="name">Chair floor protection</field> <field name="categ_id" ref="product.product_category_5"/> diff --git a/addons/sale/models/sale.py b/addons/sale/models/sale.py index 0945fade787..43caf3a306c 100644 --- a/addons/sale/models/sale.py +++ b/addons/sale/models/sale.py @@ -734,7 +734,7 @@ class SaleOrder(models.Model): return report_pages def has_to_be_signed(self, also_in_draft=False): - return (self.state == 'sent' or (self.state == 'draft' and also_in_draft)) and not self.is_expired and self.require_signature and not self.signature and self.team_id.team_type != 'website' + return (self.state == 'sent' or (self.state == 'draft' and also_in_draft)) and not self.is_expired and self.require_signature and not self.signature def has_to_be_paid(self, also_in_draft=False): transaction = self.get_portal_last_transaction() diff --git a/addons/sale/models/sales_team.py b/addons/sale/models/sales_team.py index bb0686c50c9..852162bfd8c 100644 --- a/addons/sale/models/sales_team.py +++ b/addons/sale/models/sales_team.py @@ -9,9 +9,6 @@ from odoo import api, fields, models, _ class CrmTeam(models.Model): _inherit = 'crm.team' - use_quotations = fields.Boolean(string='Quotations', help="Check this box if you send quotations to your customers rather than confirming orders straight away. " - "This will add specific action buttons to your dashboard.") - use_invoices = fields.Boolean('Set Invoicing Target', help="Check this box to set an invoicing target for this Sales Team.") invoiced = fields.Integer( compute='_compute_invoiced', string='Invoiced This Month', readonly=True, @@ -31,36 +28,31 @@ class CrmTeam(models.Model): sales_to_invoice_count = fields.Integer( compute='_compute_sales_to_invoice', string='Number of sales to invoice', readonly=True) - dashboard_graph_model = fields.Selection(selection_add=[ - ('sale.report', 'Sales'), - ('account.invoice.report', 'Invoices'), - ]) + def _compute_quotations_to_invoice(self): - non_website_teams = self.filtered(lambda team: team.team_type != 'website') - if non_website_teams: - query = self.env['sale.order']._where_calc([ - ('team_id', 'in', non_website_teams.ids), - ('state', 'in', ['draft', 'sent']), - ]) - self.env['sale.order']._apply_ir_rules(query, 'read') - _, where_clause, where_clause_args = query.get_sql() - select_query = """ - SELECT team_id, count(*), sum(amount_total / - CASE COALESCE(currency_rate, 0) - WHEN 0 THEN 1.0 - ELSE currency_rate - END - ) as amount_total - FROM sale_order - WHERE %s - GROUP BY team_id - """ % where_clause - self.env.cr.execute(select_query, where_clause_args) - quotation_data = self.env.cr.dictfetchall() - for datum in quotation_data: - self.browse(datum['team_id']).quotations_amount = datum['amount_total'] - self.browse(datum['team_id']).quotations_count = datum['count'] + query = self.env['sale.order']._where_calc([ + ('team_id', 'in', self.ids), + ('state', 'in', ['draft', 'sent']), + ]) + self.env['sale.order']._apply_ir_rules(query, 'read') + _, where_clause, where_clause_args = query.get_sql() + select_query = """ + SELECT team_id, count(*), sum(amount_total / + CASE COALESCE(currency_rate, 0) + WHEN 0 THEN 1.0 + ELSE currency_rate + END + ) as amount_total + FROM sale_order + WHERE %s + GROUP BY team_id + """ % where_clause + self.env.cr.execute(select_query, where_clause_args) + quotation_data = self.env.cr.dictfetchall() + for datum in quotation_data: + self.browse(datum['team_id']).quotations_amount = datum['amount_total'] + self.browse(datum['team_id']).quotations_count = datum['count'] @api.multi def _compute_sales_to_invoice(self): @@ -68,8 +60,9 @@ class CrmTeam(models.Model): ('team_id', 'in', self.ids), ('order_line.qty_to_invoice', '>', 0), ], ['team_id'], ['team_id']) - for datum in sale_order_data: - self.browse(datum['team_id'][0]).invoiced = datum['team_id_count'] + data_map = {datum['team_id'][0]: datum['team_id_count'] for datum in sale_order_data } + for team in self: + team.sales_to_invoice_count = data_map.get(team.id,0.0) @api.multi def _compute_invoiced(self): @@ -80,67 +73,45 @@ class CrmTeam(models.Model): ('date', '>=', date.today().replace(day=1)), ('type', 'in', ['out_invoice', 'out_refund']), ], ['amount_untaxed_signed', 'team_id'], ['team_id']) - for datum in invoice_data: - self.browse(datum['team_id'][0]).invoiced = datum['amount_untaxed_signed'] + data_map = { datum['team_id'][0]: datum['amount_untaxed_signed'] for datum in invoice_data} + for team in self: + team.invoiced = data_map.get(team.id,0.0) + + def _graph_get_model(self): + if self._context.get('in_sales_app'): + return 'sale.report' + return super(CrmTeam,self)._graph_get_model() def _graph_date_column(self): - if self.dashboard_graph_model == 'sale.report': + if self._context.get('in_sales_app'): return 'confirmation_date' - elif self.dashboard_graph_model == 'account.invoice.report': - return 'date' - return super(CrmTeam, self)._graph_date_column() + return super(CrmTeam,self)._graph_date_column() def _graph_y_query(self): - if self.dashboard_graph_model == 'sale.report': + if self._context.get('in_sales_app'): return 'SUM(price_subtotal)' - elif self.dashboard_graph_model == 'account.invoice.report': - return 'SUM(price_total)' - return super(CrmTeam, self)._graph_y_query() + return super(CrmTeam,self)._graph_y_query() def _extra_sql_conditions(self): - if self.dashboard_graph_model == 'sale.report': - return "AND state in ('sale', 'done')" - elif self.dashboard_graph_model == 'account.invoice.report': - return "AND state in ('open', 'in_payment', 'paid')" - return super(CrmTeam, self)._extra_sql_conditions() + if self._context.get('in_sales_app'): + return "AND state in ('sale', 'done', 'pos_done')" + return super(CrmTeam,self)._extra_sql_conditions() def _graph_title_and_key(self): - if self.dashboard_graph_model == 'sale.report': + if self._context.get('in_sales_app'): return ['', _('Sales: Untaxed Total')] # no more title - elif self.dashboard_graph_model == 'account.invoice.report': - return ['', _('Invoices: Untaxed Total')] return super(CrmTeam, self)._graph_title_and_key() def _compute_dashboard_button_name(self): - quotation_teams = self.filtered('use_quotations') - quotation_teams.update({'dashboard_button_name': _("Quotations")}) - (self - quotation_teams).update({'dashboard_button_name': _("Sales Orders")}) + super(CrmTeam,self)._compute_dashboard_button_name() + if self._context.get('in_sales_app'): + self.update({'dashboard_button_name': _("Sales Analysis")}) def action_primary_channel_button(self): - if hasattr(self, 'use_opportunities') and self.use_opportunities: - return super(CrmTeam, self).action_primary_channel_button() - elif self.use_quotations: - action = self.env.ref('sale.action_quotations_salesteams').read()[0] - action['context'] = {'search_default_team_id': self.id} - return action - else: - action = self.env.ref('sale.action_orders_salesteams').read()[0] - action['context'] = {'search_default_team_id': self.id} - return action - - @api.onchange('team_type') - def _onchange_team_type(self): - if self.team_type == 'sales': - self.use_quotations = True - self.use_invoices = True - if not self.dashboard_graph_model: - self.dashboard_graph_model = 'sale.report' - else: - self.use_quotations = False - self.use_invoices = False - self.dashboard_graph_model = 'sale.report' - return super(CrmTeam, self)._onchange_team_type() - + if self._context.get('in_sales_app'): + return self.env.ref('sale.action_order_report_so_salesteam').read()[0] + return super(CrmTeam, self).action_primary_channel_button() + @api.multi def update_invoiced_target(self, value): return self.write({'invoiced_target': round(float(value or 0))}) diff --git a/addons/sale/tests/test_access_rights.py b/addons/sale/tests/test_access_rights.py index 11f9faa3a57..a58a6b94c03 100644 --- a/addons/sale/tests/test_access_rights.py +++ b/addons/sale/tests/test_access_rights.py @@ -86,8 +86,8 @@ class TestAccessRights(TestCommonSaleNoChart): }) self.assertIn(india_channel.id, self.env['crm.team'].search([]).ids, 'Sales manager should be able to create a Sales Team') # Manager can edit a Sales Team - india_channel.sudo(self.user_manager).write({'dashboard_graph_group': 'week'}) - self.assertEquals(india_channel.dashboard_graph_group, 'week', 'Sales manager should be able to edit a Sales Team') + india_channel.sudo(self.user_manager).write({'name': 'new_india'}) + self.assertEquals(india_channel.name, 'new_india', 'Sales manager should be able to edit a Sales Team') # Manager can delete a Sales Team india_channel.sudo(self.user_manager).unlink() self.assertNotIn(india_channel.id, self.env['crm.team'].search([]).ids, 'Sales manager should be able to delete a Sales Team') diff --git a/addons/sale/views/sale_views.xml b/addons/sale/views/sale_views.xml index 3b6372a111e..9fd93e2b419 100644 --- a/addons/sale/views/sale_views.xml +++ b/addons/sale/views/sale_views.xml @@ -1239,8 +1239,8 @@ <field name="name">Sales Analysis</field> <field name="res_model">sale.report</field> <field name="view_mode">graph</field> - <field name="domain">[('state','not in',('draft','cancel')),('team_id', '=', active_id)]</field> - <field name="context">{'search_default_order_month':1}</field> + <field name="domain">[('state','not in',('draft','cancel'))]</field> + <field name="context">{'search_default_Sales': 1, 'time_ranges': {'field':'confirmation_date', 'range':'last_30_days'}, 'search_default_team_id': [active_id]}</field> <field name="help">This report performs analysis on your sales orders. Analysis check your sales revenues and sort it by different group criteria (salesman, partner, product, etc.) Use this report to perform analysis on sales not having invoiced yet. If you want to analyse your turnover, you should use the Invoice Analysis report in the Accounting application.</field> </record> @@ -1248,8 +1248,8 @@ <field name="name">Invoices Analysis</field> <field name="res_model">account.invoice.report</field> <field name="view_mode">graph</field> - <field name="domain">[('team_id', '=', active_id),('state', 'not in', ['draft', 'cancel'])]</field> - <field name="context">{'search_default_month':1}</field> + <field name="domain">[('state', 'not in', ['draft', 'cancel'])]</field> + <field name="context">{'search_default_month':1, 'search_default_team_id': [active_id]}</field> <field name="help">From this report, you can have an overview of the amount invoiced to your customer. The search tool can also be used to personalise your Invoices reports and so, match this analysis to your needs.</field> </record> </odoo> diff --git a/addons/sale/views/sales_team_views.xml b/addons/sale/views/sales_team_views.xml index 14324d170a2..49b2963217f 100644 --- a/addons/sale/views/sales_team_views.xml +++ b/addons/sale/views/sales_team_views.xml @@ -6,17 +6,8 @@ <field name="priority">9</field> <field name="inherit_id" ref="sales_team.crm_team_view_form"/> <field name="arch" type="xml"> - <xpath expr="//div[@name='options_active']" position="inside"> - <div class="o_row" style="display:inherit"> - <field name="use_quotations"/><label for="use_quotations"/> - </div> - </xpath> - - <xpath expr="//group[@name='graph']" position="before"> - <group name="invoicing" groups="account.group_account_invoice" attrs="{'invisible': [('team_type', 'not in', ['sales', 'website'])]}"> - <field name="use_invoices" groups="base.group_no_one"/> - <field name="invoiced_target" attrs="{'invisible': [('use_invoices', '=', False)]}"/> - </group> + <xpath expr="//field[@name='user_id']" position="after"> + <field name="invoiced_target"/> </xpath> </field> </record> @@ -58,8 +49,6 @@ <field name="arch" type="xml"> <data> <xpath expr="//templates" position="before"> - <field name="use_quotations"/> - <field name="use_invoices"/> <field name="invoiced_target"/> </xpath> @@ -89,17 +78,17 @@ <xpath expr="//div[hasclass('bottom_block')]" position="inside"> <t groups="sales_team.group_sale_manager"> - <t t-if="record.use_invoices.raw_value && record.invoiced_target.raw_value" class="col-12 o_kanban_primary_bottom"> + <t t-if="record.invoiced_target.raw_value" class="col-12 o_kanban_primary_bottom"> <field name="invoiced" widget="progressbar" title="Invoicing" options="{'current_value': 'invoiced', 'max_value': 'invoiced_target', 'editable': true, 'edit_max_value': true, 'on_change': 'update_invoiced_target'}"/> </t> - <t t-if="record.use_invoices.raw_value && !record.invoiced_target.raw_value" class="col-12 o_kanban_primary_bottom text-center"> - <a href="#" class="sales_team_target_definition o_inline_link">Click to define a target</a> + <t t-if="!record.invoiced_target.raw_value" class="col-12 o_kanban_primary_bottom text-center"> + <a href="#" class="sales_team_target_definition o_inline_link">Click to define an invoicing target</a> </t> </t> </xpath> <xpath expr="//div[hasclass('o_kanban_manage_view')]" position="inside"> - <div t-if="record.use_quotations.raw_value"> + <div> <a name="%(action_quotations_salesteams)d" type="action" class="o_quotation_view_button">Quotations</a> </div> <div> @@ -111,7 +100,7 @@ </xpath> <xpath expr="//div[hasclass('o_kanban_manage_new')]" position="inside"> - <div t-if="record.use_quotations.raw_value"> + <div> <a name="%(action_quotation_form)d" type="action"> Quotation </a> @@ -119,11 +108,6 @@ </xpath> <xpath expr="//div[hasclass('o_kanban_manage_reports')]" position="inside"> - <div t-if="record.use_quotations.raw_value"> - <a name="%(action_order_report_quotation_salesteam)d" type="action"> - Quotation - </a> - </div> <div name="sales_report"> <a name="%(action_order_report_so_salesteam)d" type="action"> Sales diff --git a/addons/sale_crm/__init__.py b/addons/sale_crm/__init__.py index dc5e6b693d1..e3e519761b8 100644 --- a/addons/sale_crm/__init__.py +++ b/addons/sale_crm/__init__.py @@ -2,3 +2,10 @@ # Part of Odoo. See LICENSE file for full copyright and licensing details. from . import models + +from odoo import api, SUPERUSER_ID + +def uninstall_hook(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + teams = env['crm.team'].search([('use_opportunities', '=', False)]) + teams.write({'use_opportunities': True})
\ No newline at end of file diff --git a/addons/sale_crm/__manifest__.py b/addons/sale_crm/__manifest__.py index 4fb6af3d937..9620ea428e7 100644 --- a/addons/sale_crm/__manifest__.py +++ b/addons/sale_crm/__manifest__.py @@ -22,6 +22,8 @@ modules. 'views/partner_views.xml', 'views/sale_order_views.xml', 'views/crm_lead_views.xml', + 'views/crm_team_views.xml' ], 'auto_install': True, + 'uninstall_hook': 'uninstall_hook' } diff --git a/addons/sale_crm/models/crm_team.py b/addons/sale_crm/models/crm_team.py index b7e91e1b697..183f6cc03a2 100644 --- a/addons/sale_crm/models/crm_team.py +++ b/addons/sale_crm/models/crm_team.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import models, _ +from odoo import models,fields, api, _ class CrmTeam(models.Model): @@ -9,4 +9,36 @@ class CrmTeam(models.Model): def _compute_dashboard_button_name(self): super(CrmTeam, self)._compute_dashboard_button_name() - self.filtered(lambda team: team.use_opportunities and team.team_type in ('sales', 'website')).update({'dashboard_button_name': _("Pipeline")}) + teams_with_opp = self.filtered(lambda team: team.use_opportunities) + if self._context.get('in_sales_app'): + teams_with_opp.update({'dashboard_button_name': _("Sales Analysis")}) + + def action_primary_channel_button(self): + if self._context.get('in_sales_app') and self.use_opportunities: + return self.env.ref('sale.action_order_report_so_salesteam').read()[0] + return super(CrmTeam,self).action_primary_channel_button() + + def _graph_get_model(self): + if self.use_opportunities and self._context.get('in_sales_app') : + return 'sale.report' + return super(CrmTeam,self)._graph_get_model() + + def _graph_date_column(self): + if self.use_opportunities and self._context.get('in_sales_app'): + return 'confirmation_date' + return super(CrmTeam,self)._graph_date_column() + + def _graph_y_query(self): + if self.use_opportunities and self._context.get('in_sales_app'): + return 'SUM(price_subtotal)' + return super(CrmTeam,self)._graph_y_query() + + def _graph_title_and_key(self): + if self.use_opportunities and self._context.get('in_sales_app'): + return ['', _('Sales: Untaxed Total')] + return super(CrmTeam,self)._graph_title_and_key() + + def _extra_sql_conditions(self): + if self.use_opportunities and self._context.get('in_sales_app'): + return "AND state in ('sale', 'done', 'pos_done')" + return super(CrmTeam,self)._extra_sql_conditions() diff --git a/addons/sale_crm/views/crm_team_views.xml b/addons/sale_crm/views/crm_team_views.xml new file mode 100644 index 00000000000..7cd6d72129c --- /dev/null +++ b/addons/sale_crm/views/crm_team_views.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<odoo> + <record id="crm_team_salesteams_view_form_in_sale_crm" model="ir.ui.view"> + <field name="name">crm.team.form</field> + <field name="model">crm.team</field> + <field name="inherit_id" ref="crm.sales_team_form_view_in_crm"/> + <field name="arch" type="xml"> + <data> + <xpath expr="//div[@name='opportunities']" position="attributes"> + <attribute name="invisible" value="0"/> + </xpath> + </data> + </field> + </record> + + <record id="sales_team.crm_team_salesteams_act" model="ir.actions.act_window"> + <field name="context">{'in_sales_app': True}</field> + </record> + + <record id="sales_team.crm_team_salesteams_pipelines_act" model="ir.actions.act_window"> + <field name="domain">[('use_opportunities', '=', True)]</field> + </record> +</odoo> diff --git a/addons/sales_team/data/sales_team_data.xml b/addons/sales_team/data/sales_team_data.xml index 133233e2963..12d92efd92e 100644 --- a/addons/sales_team/data/sales_team_data.xml +++ b/addons/sales_team/data/sales_team_data.xml @@ -11,8 +11,25 @@ <field name="name">Website</field> <field name="company_id" eval="False"/> <field name="active" eval="False"/> - <field name="team_type" eval="'website'"/> </record> + + <record id="pos_sales_team" model="crm.team"> + <field name="name">Point of Sale</field> + <field name="active" eval="False"/> + </record> + + <record id="ebay_sales_team" model="crm.team"> + <field name="name">eBay</field> + <field name="active" eval="False"/> + </record> + + <function model="crm.team" name="message_unsubscribe" + eval="[ + ref('sales_team.team_sales_department'), + ref('sales_team.salesteam_website_sales'), + ref('sales_team.ebay_sales_team'), + ref('sales_team.pos_sales_team')], + [ref('base.partner_root')]"/> </data> <record id="sales_team.group_sale_salesman" model="res.groups"> diff --git a/addons/sales_team/data/sales_team_demo.xml b/addons/sales_team/data/sales_team_demo.xml index 91ea15a5470..cbe77090d97 100644 --- a/addons/sales_team/data/sales_team_demo.xml +++ b/addons/sales_team/data/sales_team_demo.xml @@ -4,7 +4,7 @@ <!-- rename for demo-data --> <record model="crm.team" id="team_sales_department"> <field name="name">Europe</field> - <field name="dashboard_graph_group">week</field> + <field name="member_ids" eval="[(4, ref('base.user_demo'))]"/> <field name="company_id" eval="False"/> </record> <record id="base.user_demo" model="res.users"> @@ -17,8 +17,8 @@ <record model="crm.team" id="crm_team_1"> <field name="name">America</field> - <field name="dashboard_graph_group">week</field> <field name="company_id" eval="False"/> + <field name="member_ids" eval="[(4, ref('base.user_demo'))]"/> </record> </data> </odoo> diff --git a/addons/sales_team/models/crm_team.py b/addons/sales_team/models/crm_team.py index 476f8a9e991..19ed667dea4 100644 --- a/addons/sales_team/models/crm_team.py +++ b/addons/sales_team/models/crm_team.py @@ -38,10 +38,11 @@ class CrmTeam(models.Model): default_team_id.check_access_rule('read') except AccessError: return self.env['crm.team'] - if self.env.context.get('default_type') != 'lead' or default_team_id.use_leads and default_team_id.active: + if self.env.context.get('default_type') != 'lead' and default_team_id.active: team_id = default_team_id return team_id + def _get_default_favorite_user_ids(self): return [(6, 0, [self.env.uid])] @@ -53,7 +54,7 @@ class CrmTeam(models.Model): "res.currency", related='company_id.currency_id', string="Currency", readonly=True) user_id = fields.Many2one('res.users', string='Team Leader') - member_ids = fields.One2many('res.users', 'sale_team_id', string='Channel Members') + member_ids = fields.One2many('res.users', 'sale_team_id', string='Channel Members', domain= lambda self: [('groups_id', 'in', self.env.ref('base.group_user').id)], help="Add members to automatically assign their documents to this sales team. You can only be member of one team.") favorite_user_ids = fields.Many2many( 'res.users', 'team_favorite_user_rel', 'team_id', 'user_id', string='Favorite Members', @@ -65,35 +66,11 @@ class CrmTeam(models.Model): reply_to = fields.Char(string='Reply-To', help="The email address put in the 'Reply-To' of all emails sent by Odoo about cases in this Sales Team") color = fields.Integer(string='Color Index', help="The color of the channel") - team_type = fields.Selection([('sales', 'Sales'), ('website', 'Website')], string='Team Type', default='sales', required=True, - help="The type of this channel, it will define the resources this channel uses.") dashboard_button_name = fields.Char(string="Dashboard Button", compute='_compute_dashboard_button_name') dashboard_graph_data = fields.Text(compute='_compute_dashboard_graph') - dashboard_graph_type = fields.Selection([ - ('line', 'Line'), - ('bar', 'Bar'), - ], string='Type', compute='_compute_dashboard_graph', help='The type of graph this channel will display in the dashboard.') - dashboard_graph_model = fields.Selection([], string="Content", help='The graph this channel will display in the Dashboard.\n') - dashboard_graph_group = fields.Selection([ - ('day', 'Day'), - ('week', 'Week'), - ('month', 'Month'), - ('user', 'Salesperson'), - ], string='Group by', default='day', help="How this channel's dashboard graph will group the results.") - dashboard_graph_period = fields.Selection([ - ('week', 'Last Week'), - ('month', 'Last Month'), - ('year', 'Last Year'), - ], string='Scale', default='month', help="The time period this channel's dashboard graph will consider.") - - @api.depends('dashboard_graph_group', 'dashboard_graph_model', 'dashboard_graph_period') + def _compute_dashboard_graph(self): - for team in self.filtered('dashboard_graph_model'): - if team.dashboard_graph_group in (False, 'user') or team.dashboard_graph_period == 'week' and team.dashboard_graph_group != 'day' \ - or team.dashboard_graph_period == 'month' and team.dashboard_graph_group != 'day': - team.dashboard_graph_type = 'bar' - else: - team.dashboard_graph_type = 'line' + for team in self: team.dashboard_graph_data = json.dumps(team._get_graph()) def _compute_is_favorite(self): @@ -107,45 +84,26 @@ class CrmTeam(models.Model): (sudoed_self - to_fav).write({'favorite_user_ids': [(3, self.env.uid)]}) return True - def _graph_get_dates(self, today): - """ return a coherent start and end date for the dashboard graph according to the graph settings. + def _graph_get_model(self): + """ skeleton function defined here because it'll be called by crm and/or sale """ - if self.dashboard_graph_period == 'week': - start_date = today - relativedelta(weeks=1) - elif self.dashboard_graph_period == 'year': - start_date = today - relativedelta(years=1) - else: - start_date = today - relativedelta(months=1) - - # we take the start of the following month/week/day if we group by month/week/day - # (to avoid having twice the same month/week/day from different years/month/week) - if self.dashboard_graph_group == 'month': - start_date = date(start_date.year + start_date.month // 12, start_date.month % 12 + 1, 1) - # handle period=week, grouping=month for silly managers - if self.dashboard_graph_period == 'week': - start_date = today.replace(day=1) - elif self.dashboard_graph_group == 'week': - start_date += relativedelta(days=8 - start_date.isocalendar()[2]) - # add a week to make sure no overlapping is possible in case of year period (will display max 52 weeks, avoid case of 53 weeks in a year) - if self.dashboard_graph_period == 'year': - start_date += relativedelta(weeks=1) - else: - start_date += relativedelta(days=1) + raise UserError(_('Undefined graph model for Sales Team: %s') % self.name) + + def _graph_get_dates(self, today): + """ return a coherent start and end date for the dashboard graph covering a month period grouped by week. + """ + start_date = today - relativedelta(months=1) + # we take the start of the following week if we group by week + # (to avoid having twice the same week from different month) + start_date += relativedelta(days=8 - start_date.isocalendar()[2]) return [start_date, today] def _graph_date_column(self): return 'create_date' def _graph_x_query(self): - if self.dashboard_graph_group == 'user': - return 'user_id' - elif self.dashboard_graph_group == 'week': - return 'EXTRACT(WEEK FROM %s)' % self._graph_date_column() - elif self.dashboard_graph_group == 'month': - return 'EXTRACT(MONTH FROM %s)' % self._graph_date_column() - else: - return 'DATE(%s)' % self._graph_date_column() + return 'EXTRACT(WEEK FROM %s)' % self._graph_date_column() def _graph_y_query(self): raise UserError(_('Undefined graph model for Sales Team: %s') % self.name) @@ -162,7 +120,7 @@ class CrmTeam(models.Model): def _graph_data(self, start_date, end_date): """ return format should be an iterable of dicts that contain {'x_value': ..., 'y_value': ...} - x_values should either be dates, weeks, months or user_ids depending on the self.dashboard_graph_group value. + x_values should be weeks. y_values are floats. """ query = """SELECT %(x_query)s as x_value, %(y_query)s as y_value @@ -174,9 +132,8 @@ class CrmTeam(models.Model): GROUP BY x_value;""" # apply rules - if not self.dashboard_graph_model: - raise UserError(_('Undefined graph model for Sales Team: %s') % self.name) - GraphModel = self.env[self.dashboard_graph_model] + dashboard_graph_model = self._graph_get_model() + GraphModel = self.env[dashboard_graph_model] graph_table = GraphModel._table extra_conditions = self._extra_sql_conditions() where_query = GraphModel._where_calc([]) @@ -195,6 +152,7 @@ class CrmTeam(models.Model): 'end_date': "%s", 'extra_conditions': extra_conditions } + self._cr.execute(query, [self.id, start_date, end_date] + where_clause_params) return self.env.cr.dictfetchall() @@ -217,51 +175,20 @@ class CrmTeam(models.Model): today = fields.Date.from_string(fields.Date.context_today(self)) start_date, end_date = self._graph_get_dates(today) graph_data = self._graph_data(start_date, end_date) - - # line graphs and bar graphs require different labels - if self.dashboard_graph_type == 'line': - x_field = 'x' - y_field = 'y' - else: - x_field = 'label' - y_field = 'value' + x_field = 'label' + y_field = 'value' # generate all required x_fields and update the y_values where we have data for them locale = self._context.get('lang') or 'en_US' - if self.dashboard_graph_group == 'day': - for day in range(0, (end_date - start_date).days + 1): - short_name = format_date(start_date + relativedelta(days=day), 'd MMM', locale=locale) - values.append({x_field: short_name, y_field: 0}) - for data_item in graph_data: - index = (data_item.get('x_value') - start_date).days - values[index][y_field] = data_item.get('y_value') - - elif self.dashboard_graph_group == 'week': - weeks_in_start_year = int(date(start_date.year, 12, 28).isocalendar()[1]) # This date is always in the last week of ISO years - for week in range(0, (end_date.isocalendar()[1] - start_date.isocalendar()[1]) % weeks_in_start_year + 1): - short_name = get_week_name(start_date + relativedelta(days=7 * week), locale) - values.append({x_field: short_name, y_field: 0}) - - for data_item in graph_data: - index = int((data_item.get('x_value') - start_date.isocalendar()[1]) % weeks_in_start_year) - values[index][y_field] = data_item.get('y_value') - - elif self.dashboard_graph_group == 'month': - for month in range(0, (end_date.month - start_date.month) % 12 + 1): - short_name = format_date(start_date + relativedelta(months=month), 'MMM', locale=locale) - values.append({x_field: short_name, y_field: 0}) - - for data_item in graph_data: - index = int((data_item.get('x_value') - start_date.month) % 12) - values[index][y_field] = data_item.get('y_value') - - elif self.dashboard_graph_group == 'user': - for data_item in graph_data: - values.append({x_field: self.env['res.users'].browse(data_item.get('x_value')).name or _('Not Defined'), y_field: data_item.get('y_value')}) - - else: - for data_item in graph_data: - values.append({x_field: data_item.get('x_value'), y_field: data_item.get('y_value')}) + + weeks_in_start_year = int(date(start_date.year, 12, 28).isocalendar()[1]) # This date is always in the last week of ISO years + for week in range(0, (end_date.isocalendar()[1] - start_date.isocalendar()[1]) % weeks_in_start_year + 1): + short_name = get_week_name(start_date + relativedelta(days=7 * week), locale) + values.append({x_field: short_name, y_field: 0}) + + for data_item in graph_data: + index = int((data_item.get('x_value') - start_date.isocalendar()[1]) % weeks_in_start_year) + values[index][y_field] = data_item.get('y_value') [graph_title, graph_key] = self._graph_title_and_key() color = '#875A7B' if '+e' in version else '#7c7bad' @@ -279,11 +206,6 @@ class CrmTeam(models.Model): """ return False - def _onchange_team_type(self): - """ skeleton function defined here because it'll be called by crm and/or sale - """ - self.ensure_one() - @api.model def create(self, values): team = super(CrmTeam, self.with_context(mail_create_nosubscribe=True)).create(values) @@ -298,6 +220,18 @@ class CrmTeam(models.Model): self._add_members_to_favorites() return res + @api.multi + def unlink(self): + default_teams = [ + self.env.ref('sales_team.salesteam_website_sales'), + self.env.ref('sales_team.pos_sales_team'), + self.env.ref('sales_team.ebay_sales_team') + ] + for team in self: + if team in default_teams: + raise UserError(_('Cannot delete default team "%s"' % (team.name))) + return super(CrmTeam,self).unlink() + def _add_members_to_favorites(self): for team in self: team.favorite_user_ids = [(4, member.id) for member in team.member_ids] diff --git a/addons/sales_team/models/res_users.py b/addons/sales_team/models/res_users.py index 7a31376f887..0714af03dce 100644 --- a/addons/sales_team/models/res_users.py +++ b/addons/sales_team/models/res_users.py @@ -9,14 +9,4 @@ class ResUsers(models.Model): sale_team_id = fields.Many2one( 'crm.team', "User's Sales Team", - help='Sales Team the user is member of. Used to compute the members of a Sales Team through the inverse one2many') - - @api.model - def create(self, vals): - # Assign the new user in the sales team if there's only one sales team of type `Sales` - user = super(ResUsers, self).create(vals) - if user.has_group('sales_team.group_sale_salesman') and not user.sale_team_id: - teams = self.env['crm.team'].search([('team_type', '=', 'sales')]) - if len(teams.ids) == 1: - user.sale_team_id = teams.id - return user + help='Sales Team the user is member of. Used to compute the members of a Sales Team through the inverse one2many')
\ No newline at end of file diff --git a/addons/sales_team/security/sales_team_security.xml b/addons/sales_team/security/sales_team_security.xml index 0633f75c0c0..2ca3879d012 100644 --- a/addons/sales_team/security/sales_team_security.xml +++ b/addons/sales_team/security/sales_team_security.xml @@ -25,7 +25,6 @@ <record id="crm_rule_all_salesteam" model="ir.rule"> <field name="name">All Salesteam</field> <field ref="sales_team.model_crm_team" name="model_id"/> - <field name="domain_force">[('team_type','in', ['sales', 'website'])]</field> <field name="groups" eval="[(4, ref('sales_team.group_sale_salesman_all_leads'))]"/> </record> diff --git a/addons/sales_team/views/crm_team_views.xml b/addons/sales_team/views/crm_team_views.xml index 285390fd16c..8c3a0d8433c 100644 --- a/addons/sales_team/views/crm_team_views.xml +++ b/addons/sales_team/views/crm_team_views.xml @@ -8,8 +8,6 @@ <field name="model">crm.team</field> <field name="arch" type="xml"> <search string="Salesteams Search"> - <filter name="my_favorites" string="My Favorites" domain="[('favorite_user_ids', 'in', uid)]"/> - <separator/> <filter string="Archived" name="inactive" domain="[('active','=',False)]"/> <field name="name"/> <field name="user_id"/> @@ -22,11 +20,27 @@ <!-- Case Teams Action --> <record id="crm_team_salesteams_act" model="ir.actions.act_window"> + <field name="name">Sales Teams</field> + <field name="res_model">crm.team</field> + <field name="view_type">form</field> + <field name="view_mode">kanban,form</field> + <field name="view_id" ref="crm_team_salesteams_search"/> + <field name="context">{'in_sales_app': True}</field> + <field name="help" type="html"> + <p class="o_view_nocontent_smiling_face"> + Define a new Sales Team + </p><p> + Use Sales Teams to organize your sales departments. + Each channel will work with a separate pipeline. + </p> + </field> + </record> + + <record id="crm_team_salesteams_pipelines_act" model="ir.actions.act_window"> <field name="name">Team Pipelines</field> <field name="res_model">crm.team</field> <field name="view_type">form</field> <field name="view_mode">kanban,form</field> - <field name="context">{'search_default_my_favorites': 1}</field> <field name="view_id" ref="crm_team_salesteams_search"/> <field name="help" type="html"> <p class="o_view_nocontent_smiling_face"> @@ -56,11 +70,10 @@ <h1> <field name="name" placeholder="Salesteam name..."/> </h1> - <div name="options_active" attrs="{'invisible': ['&', ('team_type', '!=', 'sales'), ('team_type', '!=', 'website')]}"/> + <div name="options_active" /> </div> <group> <group name="left"> - <field name="team_type" groups="base.group_no_one"/> <field name="user_id" domain="[('share', '=', False)]"/> <field name="company_id" options="{'no_create': True}" groups="base.group_multi_company"/> </group> @@ -68,7 +81,7 @@ </group> </group> <notebook> - <page name="members" string="Team Members" attrs="{'invisible': [('team_type', 'in', ['pos', 'ebay'])]}"> + <page name="members" string="Team Members" > <field name="member_ids" widget="many2many" options="{'not_delete': True}"> <kanban quick_create="false" create="true" delete="true"> <field name="id"/> @@ -88,13 +101,6 @@ </kanban> </field> </page> - <page string="Dashboard" name="dashboard"> - <group string="Graph" name="graph"> - <field name="dashboard_graph_model" attrs="{'readonly': [('team_type', 'in', ['pos', 'ebay'])]}"/> - <field name="dashboard_graph_period"/> - <field name="dashboard_graph_group"/> - </group> - </page> </notebook> </sheet> <div class="oe_chatter"> diff --git a/addons/sales_team/views/sales_team_dashboard.xml b/addons/sales_team/views/sales_team_dashboard.xml index 6d9dcb28454..c940e235073 100644 --- a/addons/sales_team/views/sales_team_dashboard.xml +++ b/addons/sales_team/views/sales_team_dashboard.xml @@ -9,7 +9,6 @@ <field name="user_id"/> <field name="member_ids"/> <field name="color"/> - <field name="dashboard_graph_type"/> <field name="currency_id"/> <field name="is_favorite"/> <templates> @@ -25,10 +24,10 @@ </div> <div class="container o_kanban_card_content"> <div class="row o_kanban_card_upper_content"> - <div class="col-6 o_kanban_primary_left" name="to_replace_in_sale_crm"> + <div class="col-5 o_kanban_primary_left" name="to_replace_in_sale_crm"> <button type="object" class="btn btn-primary" name="action_primary_channel_button"><field name="dashboard_button_name"/></button> </div> - <div class="col-6 o_kanban_primary_right" style="padding-bottom:0;"> + <div class="col-7 o_kanban_primary_right" style="padding-bottom:0;"> <t name="first_options"/> <t name="second_options"/> <t name="third_options"/> @@ -68,17 +67,12 @@ <a type="edit">Settings</a> </div> </div> - <div class="row mb16"> - <div role="menuitem" class="col-6"> - <field name="is_favorite" widget="boolean_favorite" /> - </div> - </div> </div> </div> </t> <t t-name="SalesTeamDashboardGraph"> <div t-if="record.dashboard_graph_data.raw_value" class="o_sales_team_kanban_graph_section"> - <field name="dashboard_graph_data" widget="dashboard_graph" t-att-graph_type="record.dashboard_graph_type.raw_value"/> + <field name="dashboard_graph_data" widget="dashboard_graph" t-att-graph_type="'bar'"/> </div> </t> </templates> diff --git a/addons/website_crm/models/crm_lead.py b/addons/website_crm/models/crm_lead.py index b680e8599fc..f3b739101da 100644 --- a/addons/website_crm/models/crm_lead.py +++ b/addons/website_crm/models/crm_lead.py @@ -22,10 +22,9 @@ class Website(models.Model): _inherit = 'website' def _get_crm_default_team_domain(self): - if self.env.user.has_group('crm.group_use_lead'): - return [('use_leads', '=', True)] - else: + if not self.env.user.has_group('crm.group_use_lead'): return [('use_opportunities', '=', True)] + return [] crm_default_team_id = fields.Many2one( 'crm.team', string='Default Sales Teams', diff --git a/addons/website_crm/models/res_config_settings.py b/addons/website_crm/models/res_config_settings.py index f9b3f1c2c33..11cd082db8c 100644 --- a/addons/website_crm/models/res_config_settings.py +++ b/addons/website_crm/models/res_config_settings.py @@ -7,10 +7,9 @@ class ResConfigSettings(models.TransientModel): _inherit = 'res.config.settings' def _get_crm_default_team_domain(self): - if self.env.user.has_group('crm.group_use_lead'): - return [('use_leads', '=', True)] - else: + if not self.env.user.has_group('crm.group_use_lead'): return [('use_opportunities', '=', True)] + return [] crm_default_team_id = fields.Many2one( 'crm.team', string='Default Sales Team', related='website_id.crm_default_team_id', readonly=False, diff --git a/addons/website_crm_partner_assign/tests/test_partner_assign.py b/addons/website_crm_partner_assign/tests/test_partner_assign.py index 52ddbe427e1..dde78e0d410 100644 --- a/addons/website_crm_partner_assign/tests/test_partner_assign.py +++ b/addons/website_crm_partner_assign/tests/test_partner_assign.py @@ -105,8 +105,6 @@ class TestPartnerLeadPortal(TestCrmCases): # Sales Team of crm_salesman self.team = self.env['crm.team'].with_context(mail_notrack=True).create({ 'name': 'Test Team FOR THE WIN', - 'use_leads': True, - 'use_opportunities': True, 'member_ids': [(6, 0, [self.crm_salesman.id])], }) diff --git a/addons/website_sale/controllers/backend.py b/addons/website_sale/controllers/backend.py index ea828d289fc..deaf5e9786e 100644 --- a/addons/website_sale/controllers/backend.py +++ b/addons/website_sale/controllers/backend.py @@ -43,7 +43,6 @@ class WebsiteSaleBackend(WebsiteBackend): report_product_lines = request.env['sale.report'].read_group( domain=[ ('website_id', '=', current_website.id), - ('team_id.team_type', '=', 'website'), ('state', 'in', ['sale', 'done']), ('confirmation_date', '>=', date_from), ('confirmation_date', '<=', fields.Datetime.now())], @@ -61,7 +60,7 @@ class WebsiteSaleBackend(WebsiteBackend): # Sale-based results computation sale_order_domain = [ ('website_id', '=', current_website.id), - ('team_id', 'in', request.env['crm.team'].search([('team_type', '=', 'website')]).ids), + ('team_id', 'in', request.env['crm.team'].search([('website_ids', '!=', False)]).ids), ('date_order', '>=', fields.Datetime.to_string(datetime_from)), ('date_order', '<=', fields.Datetime.to_string(datetime_to))] so_group_data = request.env['sale.order'].read_group(sale_order_domain, fields=['state'], groupby='state') @@ -75,7 +74,6 @@ class WebsiteSaleBackend(WebsiteBackend): report_price_lines = request.env['sale.report'].read_group( domain=[ ('website_id', '=', current_website.id), - ('team_id.team_type', '=', 'website'), ('state', 'in', ['sale', 'done']), ('date', '>=', date_from), ('date', '<=', date_to)], @@ -116,7 +114,6 @@ class WebsiteSaleBackend(WebsiteBackend): sales_domain = [ ('website_id', '=', current_website.id), - ('team_id.team_type', '=', 'website'), ('state', 'in', ['sale', 'done']), ('confirmation_date', '>=', date_from), ('confirmation_date', '<=', fields.Datetime.now()) @@ -132,7 +129,6 @@ class WebsiteSaleBackend(WebsiteBackend): return results def _compute_sale_graph(self, date_from, date_to, sales_domain, previous=False): - days_between = (date_to - date_from).days date_list = [(date_from + timedelta(days=x)) for x in range(0, days_between + 1)] diff --git a/addons/website_sale/data/data.xml b/addons/website_sale/data/data.xml index 81639c17cd0..4c56e978edb 100644 --- a/addons/website_sale/data/data.xml +++ b/addons/website_sale/data/data.xml @@ -27,11 +27,8 @@ <field name="html_class">oe_image_full</field> </record> - <record model="crm.team" id="sales_team.salesteam_website_sales"> + <record id="sales_team.salesteam_website_sales" model="crm.team"> <field name="active" eval="True"/> - <field name="dashboard_graph_model">sale.report</field> - <field name="dashboard_graph_period">week</field> - <field name="company_id" eval="False"/> </record> <record model="website" id="website.default_website"> diff --git a/addons/website_sale/models/crm_team.py b/addons/website_sale/models/crm_team.py index 85206db7b3a..ca6429c9e20 100644 --- a/addons/website_sale/models/crm_team.py +++ b/addons/website_sale/models/crm_team.py @@ -4,7 +4,8 @@ from datetime import datetime from dateutil.relativedelta import relativedelta -from odoo import fields, models, _ +from odoo import fields,api, models, _ +from odoo.exceptions import UserError, ValidationError class CrmTeam(models.Model): @@ -22,7 +23,7 @@ class CrmTeam(models.Model): # abandoned carts to recover are draft sales orders that have no order lines, # a partner other than the public user, and created over an hour ago # and the recovery mail was not yet sent - website_teams = self.filtered(lambda team: team.team_type == 'website') + website_teams = self.filtered(lambda team: team.website_ids) if website_teams: abandoned_carts_data = self.env['sale.order'].read_group([ ('is_abandoned_cart', '=', True), @@ -34,11 +35,6 @@ class CrmTeam(models.Model): team.abandoned_carts_count = counts.get(team.id, 0) team.abandoned_carts_amount = amounts.get(team.id, 0) - def _compute_dashboard_button_name(self): - website_teams = self.filtered(lambda team: team.team_type == 'website' and not team.use_quotations) - website_teams.update({'dashboard_button_name': _("Online Sales")}) - super(CrmTeam, self - website_teams)._compute_dashboard_button_name() - def get_abandoned_carts(self): self.ensure_one() return { diff --git a/addons/website_sale/models/digest.py b/addons/website_sale/models/digest.py index 70ae0b9bfb4..aed881ce5fb 100644 --- a/addons/website_sale/models/digest.py +++ b/addons/website_sale/models/digest.py @@ -20,7 +20,7 @@ class Digest(models.Model): ('confirmation_date', '>=', start), ('confirmation_date', '<', end), ('state', 'not in', ['draft', 'cancel', 'sent']), - ('team_id.team_type', '=', 'website'), + ('website_id', '!=', False), ('company_id', '=', company.id) ]) record.kpi_website_sale_total_value = sum(confirmed_website_sales.mapped('amount_total')) diff --git a/addons/website_sale/models/res_config_settings.py b/addons/website_sale/models/res_config_settings.py index a3613339aed..2322645b4e1 100644 --- a/addons/website_sale/models/res_config_settings.py +++ b/addons/website_sale/models/res_config_settings.py @@ -16,7 +16,7 @@ class ResConfigSettings(models.TransientModel): return False salesperson_id = fields.Many2one('res.users', related='website_id.salesperson_id', string='Salesperson', readonly=False) - salesteam_id = fields.Many2one('crm.team', related='website_id.salesteam_id', string='Sales Team', domain=[('team_type', '!=', 'pos')], readonly=False) + salesteam_id = fields.Many2one('crm.team', related='website_id.salesteam_id', string='Sales Team', readonly=False) module_website_sale_delivery = fields.Boolean("eCommerce Shipping Costs") # field used to have a nice radio in form view, resuming the 2 fields above sale_delivery_settings = fields.Selection([ diff --git a/addons/website_sale/models/res_partner.py b/addons/website_sale/models/res_partner.py index 8c5119db323..8a9980240c7 100644 --- a/addons/website_sale/models/res_partner.py +++ b/addons/website_sale/models/res_partner.py @@ -19,7 +19,6 @@ class ResPartner(models.Model): if request and hasattr(request, 'website') and not is_public: partner.last_website_so_id = SaleOrder.search([ ('partner_id', '=', partner.id), - ('team_id.team_type', '=', 'website'), ('website_id', '=', request.website.id), ('state', '=', 'draft'), ], order='write_date desc', limit=1) diff --git a/addons/website_sale/models/sale_order.py b/addons/website_sale/models/sale_order.py index 48173deecd2..dba1a992d58 100644 --- a/addons/website_sale/models/sale_order.py +++ b/addons/website_sale/models/sale_order.py @@ -41,12 +41,12 @@ class SaleOrder(models.Model): order.only_services = all(l.product_id.type in ('service', 'digital') for l in order.website_order_line) @api.multi - @api.depends('team_id.team_type', 'date_order', 'order_line', 'state', 'partner_id') + @api.depends('website_id', 'date_order', 'order_line', 'state', 'partner_id') def _compute_abandoned_cart(self): abandoned_delay = self.website_id and self.website_id.cart_abandoned_delay or 1.0 abandoned_datetime = datetime.utcnow() - relativedelta(hours=abandoned_delay) for order in self: - domain = order.date_order and order.date_order <= abandoned_datetime and order.team_id.team_type == 'website' and order.state == 'draft' and order.partner_id.id != self.env.ref('base.public_partner').id and order.order_line + domain = order.date_order and order.date_order <= abandoned_datetime and order.state == 'draft' and order.partner_id.id != self.env.ref('base.public_partner').id and order.order_line order.is_abandoned_cart = bool(domain) def _search_abandoned_cart(self, operator, value): @@ -54,7 +54,7 @@ class SaleOrder(models.Model): abandoned_datetime = fields.Datetime.to_string(datetime.utcnow() - relativedelta(hours=abandoned_delay)) abandoned_domain = expression.normalize_domain([ ('date_order', '<=', abandoned_datetime), - ('team_id.team_type', '=', 'website'), + ('website_id', '!=', False), ('state', '=', 'draft'), ('partner_id', '!=', self.env.ref('base.public_partner').id), ('order_line', '!=', False) diff --git a/addons/website_sale/models/website.py b/addons/website_sale/models/website.py index 50a8ab084ff..e848067f1b2 100644 --- a/addons/website_sale/models/website.py +++ b/addons/website_sale/models/website.py @@ -16,7 +16,17 @@ class Website(models.Model): pricelist_id = fields.Many2one('product.pricelist', compute='_compute_pricelist_id', string='Default Pricelist') currency_id = fields.Many2one('res.currency', related='pricelist_id.currency_id', related_sudo=False, string='Default Currency', readonly=False) salesperson_id = fields.Many2one('res.users', string='Salesperson') - salesteam_id = fields.Many2one('crm.team', string='Sales Team') + + def _get_default_website_team(self): + try: + team = self.env.ref('sales_team.salesteam_website_sales') + return team if team.active else None + except ValueError: + return None + + salesteam_id = fields.Many2one('crm.team', + string='Sales Team', + default=_get_default_website_team) pricelist_ids = fields.One2many('product.pricelist', compute="_compute_pricelist_ids", string='Price list available for this Ecommerce/Website') diff --git a/addons/website_sale/views/crm_team_views.xml b/addons/website_sale/views/crm_team_views.xml index dda9efe8c3e..f3070f10c92 100644 --- a/addons/website_sale/views/crm_team_views.xml +++ b/addons/website_sale/views/crm_team_views.xml @@ -7,11 +7,8 @@ <field name="inherit_id" ref="sales_team.crm_team_salesteams_view_kanban"/> <field name="arch" type="xml"> <data> - <xpath expr="//templates" position="before"> - <field name="team_type"/> - </xpath> <xpath expr="//t[@name='third_options']" position="after"> - <div class="row" t-if="record.team_type.raw_value == 'website' && record.abandoned_carts_count.raw_value"> + <div class="row" t-if="record.abandoned_carts_count.raw_value"> <div class="col-8"> <div> <a name="get_abandoned_carts" type="object"> diff --git a/addons/website_sale/views/res_config_settings_views.xml b/addons/website_sale/views/res_config_settings_views.xml index 04410eb05d0..6a2daeee7da 100644 --- a/addons/website_sale/views/res_config_settings_views.xml +++ b/addons/website_sale/views/res_config_settings_views.xml @@ -288,7 +288,7 @@ <div class="content-group"> <div class="row mt16"> <label class="o_light_label col-lg-3" string="Sales Team" for="salesteam_id"/> - <field name="salesteam_id" context="{'default_team_type': 'website'}"/> + <field name="salesteam_id" /> </div> <div class="row"> <label class="o_light_label col-lg-3" for="salesperson_id"/> diff --git a/addons/website_sale/views/sale_order_views.xml b/addons/website_sale/views/sale_order_views.xml index 689cf888131..07ee626c17d 100644 --- a/addons/website_sale/views/sale_order_views.xml +++ b/addons/website_sale/views/sale_order_views.xml @@ -8,12 +8,12 @@ <field name="arch" type="xml"> <xpath expr="//filter[@name='my_sale_orders_filter']" position="replace"> <filter string="Confirmed Orders" name="order_confirmed" domain="[('state', 'in', ('sale', 'done'))]"/> - <filter string="Unpaid Orders" name="order_unpaid" domain="[('state', '=', 'sent'), ('team_id.team_type', '=', 'website')]"/> + <filter string="Unpaid Orders" name="order_unpaid" domain="[('state', '=', 'sent'), ('website_id', '!=', False)]"/> <filter string="Abandoned Carts" name="order_abandoned" domain="[('is_abandoned_cart', '=', True)]"/> <separator/> <filter string="Order Date" name="order_date" date="date_order"/> <separator/> - <filter string="From Website" name="from_website" domain="[('team_id.team_type', '=', 'website')]"/> + <filter string="From Website" name="from_website" domain="[('website_id', '!=', False)]"/> <separator/> </xpath> </field> @@ -72,7 +72,7 @@ <field name="name">Unpaid Orders</field> <field name="res_model">sale.order</field> <field name="view_mode">tree,form,kanban,activity</field> - <field name="domain">[('state', '=', 'sent'), ('team_id.team_type', '=', 'website')]</field> + <field name="domain">[('state', '=', 'sent'), ('website_id', '!=', False)]</field> <field name="context">{'show_sale': True, 'create': False}</field> <field name="search_view_id" ref="view_sales_order_filter_ecommerce"/> <field name="help" type="html"> @@ -119,7 +119,7 @@ <field name="name">Orders To Invoice</field> <field name="res_model">sale.order</field> <field name="view_mode">tree,form,kanban</field> - <field name="domain">[('state', 'in', ('sale', 'done')), ('invoice_status', '=', 'to invoice'), ('team_id.team_type', '=', 'website')]</field> + <field name="domain">[('state', 'in', ('sale', 'done')), ('invoice_status', '=', 'to invoice'), ('website_id', '!=', False)]</field> <field name="context">{'show_sale': True, 'search_default_order_confirmed': 1, 'create': False}</field> <field name="search_view_id" ref="view_sales_order_filter_ecommerce"/> <field name="help" type="html"> @@ -147,7 +147,7 @@ <field name="res_model">sale.order</field> <field name="view_mode">tree,kanban,form,activity</field> <field name="view_type">form</field> - <field name="domain">[('state', '=', 'sent'), ('team_id.team_type', '=', 'website')]</field> + <field name="domain">[('state', '=', 'sent'), ('website_id', '!=', False)]</field> <field name="context" eval="{'show_sale': True, 'create': False}"/> <field name="view_id" ref="sale.view_quotation_tree"/> <field name="search_view_id" ref="view_sales_order_filter_ecommerce_unpaid"/> @@ -165,7 +165,7 @@ <field name="res_model">sale.order</field> <field name="view_type">form</field> <field name="view_mode">tree,kanban,form,activity</field> - <field name="domain">[('state', '=', 'draft'), ('order_line', '!=', False), ('partner_id', '!=', context.get('public_partner_id')), ('team_id.team_type', '=', 'website'), ('date_order', '<=', (datetime.datetime.utcnow() - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:%M:%S'))]</field> + <field name="domain">[('state', '=', 'draft'), ('order_line', '!=', False), ('partner_id', '!=', context.get('public_partner_id')), ('website_id', '!=', False), ('date_order', '<=', (datetime.datetime.utcnow() - datetime.timedelta(hours=1)).strftime('%Y-%m-%d %H:%M:%S'))]</field> <field name="context" eval="{'show_sale': True, 'create': False, 'public_partner_id': ref('base.public_partner'), 'search_default_recovery_email': True}"/> <field name="view_id" ref="sale.view_quotation_tree"/> <field name="search_view_id" ref="view_sales_order_filter_ecommerce_abondand"/> diff --git a/addons/website_sale/views/sale_report_views.xml b/addons/website_sale/views/sale_report_views.xml index 3635577243e..c8876ed6416 100644 --- a/addons/website_sale/views/sale_report_views.xml +++ b/addons/website_sale/views/sale_report_views.xml @@ -54,7 +54,7 @@ <field name="name">Online Sales Analysis</field> <field name="res_model">sale.report</field> <field name="view_mode">pivot,graph</field> - <field name="domain">[('state','in',('sale', 'done')), ('team_id.team_type', '=', 'website')]</field> + <field name="domain">[('state','in',('sale', 'done')), ('website_id', '!=', False)]</field> <field name="search_view_id" ref="sale_report_view_search_website"/> <field name="help" type="html"> <p class="o_view_nocontent_empty_folder"> @@ -81,7 +81,7 @@ <field name="name">Sales</field> <field name="res_model">sale.report</field> <field name="view_mode">pivot,graph</field> - <field name="domain">[('team_id.team_type', '=', 'website')]</field> + <field name="domain">[('website_id', '!=', False)]</field> <field name="search_view_id" ref="sale_report_view_search_website"/> <field name="help" type="html"> <p class="o_view_nocontent_empty_folder"> diff --git a/addons/website_sale_link_tracker/controllers/backend.py b/addons/website_sale_link_tracker/controllers/backend.py index f301d1c4a2b..79e3aa586f3 100644 --- a/addons/website_sale_link_tracker/controllers/backend.py +++ b/addons/website_sale_link_tracker/controllers/backend.py @@ -16,7 +16,7 @@ class WebsiteSaleLinkTrackerBackend(WebsiteSaleBackend): def fetch_utm_data(self, date_from, date_to): sale_utm_domain = [ - ('team_id.team_type', '=', 'website'), + ('website_id', '!=', False), ('state', 'in', ['sale', 'done']), ('confirmation_date', '>=', date_from), ('confirmation_date', '<=', date_to) |
