[IMP] crm: implement the forecast feature for opportunities

Add a new submenu "Forecast" in "Reporting" for CRM

The Forecast shows 4 views:
  1. Kanban
  2. Graph
  3. Pivot
  4. List

Objective
  Shows the short term forecast for opportunities (crm_lead) based on their
  expected closing (date_deadline) and have an overview of the prorated
  revenues for each period (defaults to month).

Implementation
1. Kanban
- the progressbar is using the prorated_revenue
- the forecast_field is date_deadline
- the default groupby is date_deadline

2.3. Graph, Pivot
- the measure defaults to the prorated_revenue
- the row defaults to date_deadline

4. List
- shows the prorated_revenue

The special filter "Forecast" is defined to be used by the
forecastModelExtension (JS). (define the forecast_filter context key)

The forecast_field context key is defined in the action to be used by the
forecastModelExtension and the forecastService (JS).

The demo data for opportunities is updated to spread date_deadline over 4 months

Enable drag&drop and quickCreate features for the kanban view with the use of
the allow_group_range_value  <field> xml attribute

Add a "Won" flag for the forecast view to better distinguish opportunities that
need to be worked on

Test tour

Task-ID: 2243913
PR odoo/odoo#69380
ENT PR odoo/enterprise#18547
This commit is contained in:
abd-msyukyu-odoo
2021-06-30 09:51:42 +00:00
parent 3df9f5d689
commit 2a48b989c1
10 changed files with 324 additions and 15 deletions
+1
View File
@@ -33,6 +33,7 @@
'data/crm_stage_data.xml',
'data/crm_team_data.xml',
'data/digest_data.xml',
'data/ir_action_data.xml',
'data/ir_cron_data.xml',
'data/mail_data.xml',
'data/crm_recurring_plan_data.xml',
+12 -10
View File
@@ -353,7 +353,7 @@ Andrew</p>]]></field>
<field name="street">Kensington Road 189</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor1')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=4)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead1"/>
@@ -379,7 +379,7 @@ Andrew</p>]]></field>
<field name="phone">+32 10 588 558</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor2')])]"/>
<field name="priority">2</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead1"/>
@@ -402,7 +402,7 @@ Andrew</p>]]></field>
<field name="phone">+32 10 588 558</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor1')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead2"/>
@@ -495,7 +495,7 @@ Andrew</p>]]></field>
<field name="street">Union Road</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor5')])]"/>
<field name="priority">2</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead2"/>
@@ -520,7 +520,7 @@ Andrew</p>]]></field>
<field name="street">Rue des Palais 51, bte 33</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor3'),ref('sales_team.categ_oppor4')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=3)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead2"/>
@@ -553,7 +553,7 @@ Andrew</p>]]></field>
<field name="partner_id" ref="base.res_partner_4"/>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor4'),ref('sales_team.categ_oppor6')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=3)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead2"/>
@@ -628,6 +628,7 @@ Andrew</p>]]></field>
<field name="phone">+1 312 349 2324</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor4'),ref('sales_team.categ_oppor8')])]"/>
<field name="priority">2</field>
<field name="date_deadline" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead3"/>
@@ -684,6 +685,7 @@ Andrew</p>]]></field>
<field name="partner_id" ref="base.res_partner_18"/>
<field name="priority">0</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor3')])]"/>
<field name="date_deadline" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead3"/>
@@ -702,6 +704,7 @@ Andrew</p>]]></field>
<field name="country_id" ref="base.pe"/>
<field name="city">Lima</field>
<field name="priority">0</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor7')])]"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
@@ -750,7 +753,6 @@ Andrew</p>]]></field>
<field name="zip">1300</field>
<field name="priority">2</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor2')])]"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=6)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead3"/>
@@ -774,7 +776,7 @@ Andrew</p>]]></field>
<field name="street">Rue de Namur 69</field>
<field name="priority">2</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor2')])]"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(days=6)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="DateTime.today().strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.crm_team_1"/>
<field name="user_id" ref="base.user_demo"/>
<field name="stage_id" ref="crm.stage_lead3"/>
@@ -865,7 +867,7 @@ Andrew</p>]]></field>
<field name="street">1920 Del Dew Drive</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor1')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=1)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead1"/>
@@ -887,7 +889,7 @@ Andrew</p>]]></field>
<field name="street">1920 Del Dew Drive</field>
<field name="tag_ids" eval="[(6, 0, [ref('sales_team.categ_oppor1')])]"/>
<field name="priority">1</field>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(weeks=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="date_deadline" eval="(DateTime.today() + relativedelta(months=2)).strftime('%Y-%m-%d %H:%M')"/>
<field name="team_id" ref="sales_team.team_sales_department"/>
<field name="user_id" ref="base.user_admin"/>
<field name="stage_id" ref="crm.stage_lead2"/>
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<odoo>
<record id="action_opportunity_forecast" model="ir.actions.server">
<field name="name">Crm: Forecast</field>
<field name="model_id" ref="crm.model_crm_team"/>
<field name="state">code</field>
<field name="groups_id" eval="[(4, ref('base.group_user'))]"/>
<field name="code">action = model.action_opportunity_forecast()</field>
</record>
</odoo>
+9
View File
@@ -587,6 +587,15 @@ class Team(models.Model):
@api.model
def action_your_pipeline(self):
action = self.env["ir.actions.actions"]._for_xml_id("crm.crm_lead_action_pipeline")
return self._action_update_to_pipeline(action)
@api.model
def action_opportunity_forecast(self):
action = self.env['ir.actions.actions']._for_xml_id('crm.crm_lead_action_forecast')
return self._action_update_to_pipeline(action)
@api.model
def _action_update_to_pipeline(self, action):
user_team_id = self.env.user.sale_team_id.id
if user_team_id:
# To ensure that the team is readable in multi company
@@ -104,7 +104,7 @@
name="Activities"
parent="crm_menu_report"
action="crm_activity_report_action"
sequence="3"/>
sequence="4"/>
<record id="crm_activity_report_action_team" model="ir.actions.act_window">
<field name="name">Pipeline Activities</field>
+1 -1
View File
@@ -21,7 +21,7 @@
}
}
.oe_kanban_card_lost_ribbon {
.oe_kanban_card_ribbon {
min-height: 105px;
.o_kanban_record_title {
max-width: calc(100% - 65px);
@@ -0,0 +1,98 @@
/** @odoo-module */
import tour from 'web_tour.tour';
const today = moment();
tour.register('crm_forecast', {
test: true,
url: "/web",
}, [
tour.stepUtils.showAppsMenuItem(),
{
trigger: ".o_app[data-menu-xmlid='crm.crm_menu_root']",
content: "open crm app",
}, {
trigger: '.o_dropdown_toggler[data-menu-xmlid="crm.crm_menu_report"]',
content: 'Open Reporting menu',
run: 'click',
}, {
trigger: '.o_dropdown_item[data-menu-xmlid="crm.crm_menu_forecast"]',
content: 'Open Forecast menu',
run: 'click',
}, {
trigger: '.o_column_quick_create:contains(Add next month)',
content: 'Wait page loading'
}, {
trigger: ".o-kanban-button-new",
content: "click create",
run: 'click',
}, {
trigger: "input[name=name]",
content: "complete name",
run: "text Test Opportunity 1",
}, {
trigger: "div[name=expected_revenue] > input",
content: "complete expected revenue",
run: "text 999999",
}, {
trigger: "button.o_kanban_edit",
content: "edit lead",
}, {
trigger: "input[name=date_deadline]",
content: "complete expected closing",
run: `text ${today.format("MM/DD/YYYY")}`,
}, {
trigger: "input[name=date_deadline]",
content: "click to make the datepicker disappear",
run: "click"
}, {
trigger: "body:not(:has(div.bootstrap-datetimepicker-widget))",
content: "wait for date_picker to disappear",
run: function () {},
}, {
trigger: '.o_back_button',
content: 'navigate back to the kanban view',
position: "bottom",
run: "click"
}, {
trigger: ".o_kanban_record .o_kanban_record_title:contains('Test Opportunity 1')",
content: "move to the next month",
run: function (actions) {
const undefined_groups = $('.o_column_title:contains("Undefined")').length;
actions.drag_and_drop(` .o_opportunity_kanban .o_kanban_group:eq(${1 + undefined_groups})`, this.$anchor);
}
}, {
trigger: ".o_kanban_record .o_kanban_record_title:contains('Test Opportunity 1')",
content: "edit lead",
run: "click"
}, {
trigger: `span[name=date_deadline]:contains("${moment(today).add(2, 'months').startOf('month').subtract(1, 'days').format("MM/DD/YYYY")}")`,
content: "edit datetime",
position: "bottom",
run: "click"
}, {
trigger: "input[name=date_deadline]",
content: "complete expected closing",
run: `text ${moment(today).add(5, 'months').startOf('month').subtract(1, 'days').format("MM/DD/YYYY")}`
}, {
trigger: "body:not(:has(div.bootstrap-datetimepicker-widget))",
content: "wait for date_picker to disappear",
run: function () {},
}, {
trigger: "input[name=probability]",
content: "max out probability",
run: "text 100"
}, {
trigger: '.o_back_button',
content: 'navigate back to the kanban view',
position: "bottom",
run: "click"
}, {
trigger: '.o_kanban_add_column',
content: "add next month",
run: "click"
}, {
trigger: ".o_kanban_record:contains('Test Opportunity 1'):contains('Won')",
content: "assert that the opportunity has the Won banner",
run: function () {},
}
]);
+2
View File
@@ -25,6 +25,8 @@ class TestUi(HttpCase):
})
self.start_tour("/web", 'crm_rainbowman', login="temp_crm_user")
def test_03_crm_tour_forecast(self):
self.start_tour("/web", 'crm_forecast', login="admin")
class TestCRMLeadMisc(TestCrmCommon):
+181 -1
View File
@@ -535,7 +535,7 @@
<templates>
<t t-name="kanban-box">
<t t-set="lost_ribbon" t-value="!record.active.raw_value and record.probability and record.probability.raw_value == 0"/>
<div t-attf-class="#{!selection_mode ? kanban_color(record.color.raw_value) : ''} #{lost_ribbon ? 'oe_kanban_card_lost_ribbon' : ''} oe_kanban_global_click oe_kanban_card d-flex flex-column">
<div t-attf-class="#{!selection_mode ? kanban_color(record.color.raw_value) : ''} #{lost_ribbon ? 'oe_kanban_card_ribbon' : ''} oe_kanban_global_click oe_kanban_card d-flex flex-column">
<div class="ribbon ribbon-top-right"
attrs="{'invisible': ['|', ('probability', '&gt;', 0), ('active', '=', True)]}">
<span class="bg-danger">Lost</span>
@@ -591,6 +591,59 @@
</field>
</record>
<record id="crm_lead_view_kanban_forecast" model="ir.ui.view">
<field name="name">crm.lead.view.kanban.forecast</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_kanban_view_leads"/>
<field name="mode">primary</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<xpath expr="//kanban" position="attributes">
<attribute name="default_group_by">date_deadline</attribute>
<attribute name="js_class">forecast_kanban</attribute>
</xpath>
<xpath expr="//kanban" position="inside">
<field name="date_deadline" allow_group_range_value="true"/>
</xpath>
<xpath expr="//field[@name='expected_revenue']" position="replace">
<field name="prorated_revenue"/>
</xpath>
<xpath expr="//progressbar" position="attributes">
<attribute name="sum_field">prorated_revenue</attribute>
</xpath>
<xpath expr="//t[@t-set='lost_ribbon']" position="after">
<t t-set="won_ribbon" t-value="record.active.raw_value and record.probability and record.probability.raw_value == 100"/>
</xpath>
<xpath expr="//div[contains(@t-attf-class, 'oe_kanban_card')]" position="attributes">
<attribute name="t-attf-class">
#{!selection_mode ? kanban_color(record.color.raw_value) : ''}
#{lost_ribbon || won_ribbon ? 'oe_kanban_card_ribbon' : ''}
oe_kanban_global_click oe_kanban_card d-flex flex-column
</attribute>
</xpath>
<xpath expr="//div[hasclass('ribbon')]" position="replace">
<div class="ribbon ribbon-top-right"
attrs="{'invisible': ['&amp;', '|', ('probability', '&gt;', 0), ('active', '=', True),
'|', ('probability', '&lt;', 100), ('active', '=', False)]}">
<span t-if="won_ribbon" class="bg-success">Won</span>
<span t-elif="lost_ribbon" class="bg-danger">Lost</span>
</div>
</xpath>
<xpath expr="//div[hasclass('o_kanban_record_subtitle')]" position="replace">
<div class="o_kanban_record_subtitle">
<t t-if="record.prorated_revenue.raw_value">
<field name="prorated_revenue" widget="monetary" options="{'currency_field': 'company_currency'}"/>
<span t-if="record.recurring_revenue and record.recurring_revenue.raw_value"> + </span>
</t>
<t t-if="record.recurring_revenue and record.recurring_revenue.raw_value">
<field name="recurring_revenue" widget="monetary" options="{'currency_field': 'company_currency'}"/>
<field name="recurring_plan"/>
</t>
</div>
</xpath>
</field>
</record>
<record id="view_crm_case_leads_filter" model="ir.ui.view">
<field name="name">crm.lead.search.lead</field>
<field name="model">crm.lead</field>
@@ -738,6 +791,25 @@
</field>
</record>
<record id="crm_lead_view_tree_forecast" model="ir.ui.view">
<field name="name">crm.lead.view.tree.forecast</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.crm_case_tree_view_oppor"/>
<field name="mode">primary</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<xpath expr="//tree" position="attributes">
<attribute name="js_class">forecast_list</attribute>
</xpath>
<xpath expr="//field[@name='expected_revenue']" position="attributes">
<attribute name="optional">hide</attribute>
</xpath>
<xpath expr="//field[@name='expected_revenue']" position="after">
<field name="prorated_revenue" sum="Prorated Revenues" optional="show" widget="monetary" options="{'currency_field': 'company_currency'}"/>
</xpath>
</field>
</record>
<record id="crm_lead_view_list_activities" model="ir.ui.view">
<field name="name">crm.lead.list.activities</field>
<field name="model">crm.lead</field>
@@ -797,6 +869,24 @@
</field>
</record>
<record id="crm_lead_view_graph_forecast" model="ir.ui.view">
<field name="name">crm.lead.view.graph.forecast</field>
<field name="model">crm.lead</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<graph string="Opportunities Forecast" sample="1" js_class="forecast_graph">
<field name="date_deadline" type="row"/>
<field name="prorated_revenue" type="measure"/>
<field name="automated_probability" invisible="1"/>
<field name="color" invisible="1"/>
<field name="day_open" invisible="1"/>
<field name="day_close" invisible="1"/>
<field name="message_bounce" invisible="1"/>
<field name="probability" invisible="1"/>
</graph>
</field>
</record>
<record id="crm_lead_view_pivot" model="ir.ui.view">
<field name="name">crm.lead.view.pivot</field>
<field name="model">crm.lead</field>
@@ -811,6 +901,25 @@
</field>
</record>
<record id="crm_lead_view_pivot_forecast" model="ir.ui.view">
<field name="name">crm.lead.view.pivot.forecast</field>
<field name="model">crm.lead</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<pivot string="Forecast Analysis" sample="1" js_class="forecast_pivot">
<field name="date_deadline" interval="month" type="row"/>
<field name="stage_id" type="col"/>
<field name="prorated_revenue" type="measure"/>
<field name="automated_probability" invisible="1"/>
<field name="color" invisible="1"/>
<field name="day_open" invisible="1"/>
<field name="day_close" invisible="1"/>
<field name="message_bounce" invisible="1"/>
<field name="probability" invisible="1"/>
</pivot>
</field>
</record>
<record id="view_crm_case_opportunities_filter" model="ir.ui.view">
<field name="name">crm.lead.search.opportunity</field>
<field name="model">crm.lead</field>
@@ -887,6 +996,24 @@
</field>
</record>
<record id="crm_lead_view_search_forecast" model="ir.ui.view">
<field name="name">crm.lead.view.search.forecast</field>
<field name="model">crm.lead</field>
<field name="inherit_id" ref="crm.view_crm_case_opportunities_filter"/>
<field name="mode">primary</field>
<field name="priority">32</field>
<field name="arch" type="xml">
<xpath expr="//filter[@name='assigned_to_me']" position="before">
<filter name="forecast" string="Upcoming Closings" context="{'forecast_filter':1}"/>
<separator/>
</xpath>
<xpath expr="//filter[@name='date_deadline']" position="replace"/>
<filter name="month" position="before">
<filter string="Expected Closing" name="date_deadline" context="{'group_by':'date_deadline:month'}"/>
</filter>
</field>
</record>
<!--
'Mark as Lost' in action dropdown
-->
@@ -1095,9 +1222,62 @@ if record:
<field name="act_window_id" ref="crm_lead_action_pipeline"/>
</record>
<record id="crm_lead_action_forecast" model="ir.actions.act_window">
<field name="name">Forecast</field>
<field name="res_model">crm.lead</field>
<field name="view_mode">kanban,graph,pivot,tree,form</field>
<field name="domain">[('type', '=', 'opportunity')]</field>
<field name="context">{
'default_type': 'opportunity',
'search_default_assigned_to_me': 1,
'search_default_forecast': 1,
'search_default_date_deadline': 1,
'forecast_field': 'date_deadline'
}</field>
<field name="help" type="html">
<p class="o_view_nocontent_smiling_face">
No opportunity to display!
</p><p>
Easily set expected closing dates and overview your revenue streams.
</p>
</field>
<field name="search_view_id" ref="crm.crm_lead_view_search_forecast"/>
</record>
<record id="crm_lead_action_forecast_view_kanban" model="ir.actions.act_window.view">
<field name="sequence" eval="0"/>
<field name="view_mode">kanban</field>
<field name="view_id" ref="crm_lead_view_kanban_forecast"/>
<field name="act_window_id" ref="crm_lead_action_forecast"/>
</record>
<record id="crm_lead_action_forecast_view_graph" model="ir.actions.act_window.view">
<field name="sequence" eval="1"/>
<field name="view_mode">graph</field>
<field name="view_id" ref="crm_lead_view_graph_forecast"/>
<field name="act_window_id" ref="crm_lead_action_forecast"/>
</record>
<record id="crm_lead_action_forecast_view_pivot" model="ir.actions.act_window.view">
<field name="sequence" eval="2"/>
<field name="view_mode">pivot</field>
<field name="view_id" ref="crm_lead_view_pivot_forecast"/>
<field name="act_window_id" ref="crm_lead_action_forecast"/>
</record>
<record id="crm_lead_action_forecast_view_tree" model="ir.actions.act_window.view">
<field name="sequence" eval="3"/>
<field name="view_mode">tree</field>
<field name="view_id" ref="crm_lead_view_tree_forecast"/>
<field name="act_window_id" ref="crm_lead_action_forecast"/>
</record>
<record id="menu_crm_opportunities" model="ir.ui.menu">
<field name="action" ref="crm.action_your_pipeline"/>
</record>
<record id="crm_menu_forecast" model="ir.ui.menu">
<field name="action" ref="crm.action_opportunity_forecast"/>
</record>
<record id="crm_menu_root" model="ir.ui.menu">
<field name="action" ref="crm.action_your_pipeline"/>
</record>
+7 -2
View File
@@ -60,17 +60,22 @@
parent="crm_menu_root"
sequence="20"
groups="sales_team.group_sale_salesman"/>
<menuitem
id="crm_menu_forecast"
name="Forecast"
parent="crm_menu_report"
sequence="1"/>
<menuitem
id="crm_opportunity_report_menu_lead"
name="Leads"
parent="crm_menu_report"
groups="crm.group_use_lead"
sequence="1"/>
sequence="2"/>
<menuitem
id="crm_opportunity_report_menu"
name="Pipeline"
parent="crm_menu_report"
sequence="2"/>
sequence="3"/>
<!-- CONFIGURATION -->
<menuitem