[IMP] project: follow up the project overview removal

The project overview is a significant technical debt
as it is a custom qweb view. It is quite limited:

It is not possible to group the data, to filter on dates,
SOs or Field Service projects.
Improving this is very difficult and would require weeks
of development that are not worth it.
In any case, all the information provided by the project overview
can be found elsewhere. The stat buttons of the report are basically
duplicates of the ones from the project form view.
Therefore, we are removing this report and its twin,
the Project Costs and Revenues.
In addition, analytic items lack context for the user to understand
what generated a certain cost or revenue:

there is no link to the source document;
the entries are not categorized (e.g. it is not easy to understand
if a cost comes from a timesheet cost, a purchase order or an expense);
the billable type group by works fine for timesheets but then all
the other entries are flagged as Undefined, which is not very helpful;
there is no option to easily isolate costs from revenues.

task-2646234
See odoo/enterprise#20936

closes odoo/odoo#76553

Signed-off-by: Laurent Stukkens (ltu) <ltu@odoo.com>
This commit is contained in:
Cedric Prieels (cpr)
2021-11-23 11:12:14 +00:00
parent 1e25946a76
commit ea038c6b19
19 changed files with 84 additions and 43 deletions
@@ -22,6 +22,6 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_open_project_invoices',
'show': bool(self.analytic_account_id) and self.invoice_count > 0,
'sequence': 11,
'sequence': 30,
})
return buttons
@@ -5,8 +5,9 @@
<field name="name">project.project.form.inherit</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="priority">50</field>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(project.action_project_task_burndown_chart_report)d']" position="after">
<xpath expr="//button[@name='action_view_analytic_account_entries']" position="after">
<button name="action_open_project_invoices" type="object" class="oe_stat_button" icon="fa-pencil-square-o"
attrs="{'invisible': ['|', ('invoice_count', '=', 0), ('analytic_account_id', '=', False)]}"
groups="account.group_account_readonly">
+1 -1
View File
@@ -224,7 +224,7 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_show_timesheets_by_employee_invoice_type',
'show': self.allow_timesheets,
'sequence': 4,
'sequence': 6,
})
return buttons
+17
View File
@@ -33,6 +33,20 @@
<field name="name">External</field>
</record>
<!-- Analytic Accounts -->
<!-- Needed so that we can have the same analytic accounts on hr_timesheet and project_account_budget -->
<record id="analytic_office_design" model="account.analytic.account">
<field name="name">Office Design</field>
</record>
<record id="analytic_research_development" model="account.analytic.account">
<field name="name">Research &amp; Development</field>
</record>
<record id="analytic_renovations" model="account.analytic.account">
<field name="name">Renovations</field>
</record>
<!-- Task Stages -->
<record id="project_stage_0" model="project.task.type">
<field name="sequence">1</field>
@@ -71,6 +85,7 @@
<field name="favorite_user_ids" eval="[(4, ref('base.user_admin'))]"/>
<field name="tag_ids" eval="[(4, ref('project.project_tags_05'))]"/>
<field name="stage_id" ref="project.project_project_stage_1"/>
<field name="analytic_account_id" ref="project.analytic_office_design"/>
</record>
<record id="project_project_2" model="project.project">
@@ -81,6 +96,7 @@
<field name="favorite_user_ids" eval="[(4, ref('base.user_admin'))]"/>
<field name="tag_ids" eval="[(4, ref('project.project_tags_04'))]"/>
<field name="stage_id" ref="project.project_project_stage_1"/>
<field name="analytic_account_id" ref="project.analytic_research_development"/>
</record>
<record id="project_project_3" model="project.project">
@@ -92,6 +108,7 @@
<field name="favorite_user_ids" eval="[(4, ref('base.user_admin'))]"/>
<field name="tag_ids" eval="[(4, ref('project_tags_04')), (4, ref('project_tags_02'))]"/>
<field name="stage_id" ref="project.project_project_stage_2"/>
<field name="analytic_account_id" ref="project.analytic_renovations"/>
</record>
<!-- Personal Stages: Mitchell Admin-->
+5 -5
View File
@@ -703,7 +703,7 @@ class Project(models.Model):
'active_id': self.id,
}),
'show': True,
'sequence': 2,
'sequence': 3,
}]
if self.user_has_groups('project.group_project_rating'):
buttons.append({
@@ -713,7 +713,7 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_view_all_rating',
'show': self.rating_active and self.rating_percentage_satisfaction > -1,
'sequence': 5,
'sequence': 15,
})
if self.user_has_groups('project.group_project_manager'):
buttons.append({
@@ -725,7 +725,7 @@ class Project(models.Model):
'active_id': self.id,
}),
'show': True,
'sequence': 7,
'sequence': 60,
})
buttons.append({
'icon': 'users',
@@ -737,7 +737,7 @@ class Project(models.Model):
'active_id': self.id,
}),
'show': True,
'sequence': 23,
'sequence': 66,
})
if self.user_has_groups('analytic.group_analytic_accounting'):
buttons.append({
@@ -747,7 +747,7 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_view_analytic_account_entries',
'show': True,
'sequence': 18,
'sequence': 24,
})
return buttons
+13 -13
View File
@@ -322,19 +322,6 @@
</span>
</div>
</button>
<button name="%(action_project_task_burndown_chart_report)d" type="action" class="oe_stat_button" icon="fa-area-chart" groups="project.group_project_manager">
<span class="o_stat_text">
Burndown Chart
</span>
</button>
<button class="oe_stat_button" type="object" name="action_view_analytic_account_entries" icon="fa-usd" attrs="{'invisible': [('analytic_account_id', '=', False)]}" groups="analytic.group_analytic_accounting">
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
<field name="analytic_account_balance"/>
</span>
<span class="o_stat_text">Gross Margin</span>
</div>
</button>
<button class="oe_stat_button" name="%(project.project_update_all_action)d" type="action" groups="project.group_project_manager">
<div class="pl-4">
<field name="last_update_color" invisible="1"/>
@@ -347,6 +334,19 @@
<field name="last_update_status" readonly="1" widget="status_with_color" options="{'color_field': 'last_update_color', 'no_quick_edit': 1}"/>
</div>
</button>
<button class="oe_stat_button" type="object" name="action_view_analytic_account_entries" icon="fa-usd" attrs="{'invisible': [('analytic_account_id', '=', False)]}" groups="analytic.group_analytic_accounting">
<div class="o_form_field o_stat_info">
<span class="o_stat_value">
<field name="analytic_account_balance" widget="monetary"/>
</span>
<span class="o_stat_text">Gross Margin</span>
</div>
</button>
<button name="%(action_project_task_burndown_chart_report)d" type="action" class="oe_stat_button" icon="fa-area-chart" groups="project.group_project_manager">
<span class="o_stat_text">
Burndown Chart
</span>
</button>
<button class="oe_stat_button" name="%(project.project_collaborator_action)d" type="action" icon="fa-users" groups="project.group_project_manager">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
+1 -1
View File
@@ -54,6 +54,6 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_open_project_expenses',
'show': self.expenses_count > 0,
'sequence': 10,
'sequence': 33,
})
return buttons
@@ -5,8 +5,9 @@
<field name="name">project.project.form.inherit</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="priority">40</field>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(project.action_project_task_burndown_chart_report)d']" position="after">
<xpath expr="//button[@name='action_view_analytic_account_entries']" position="after">
<button name="action_open_project_expenses" type="object" class="oe_stat_button" icon="fa-money" attrs="{'invisible': [('expenses_count', '=', 0)]}" groups="hr_expense.group_hr_expense_team_approver">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
+3 -3
View File
@@ -46,7 +46,7 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_view_mrp_production',
'show': self.production_count > 0,
'sequence': 19,
'sequence': 39,
},
{
'icon': 'cog',
@@ -55,7 +55,7 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_view_workorder',
'show': self.workorder_count > 0,
'sequence': 20,
'sequence': 42,
},
{
'icon': 'flask',
@@ -64,6 +64,6 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_view_mrp_bom',
'show': self.bom_count > 0,
'sequence': 21,
'sequence': 45,
}])
return buttons
+1 -1
View File
@@ -4,7 +4,7 @@
<field name="name">project.project.view.inherited</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project" />
<field eval="14" name="priority"/>
<field eval="20" name="priority"/>
<field name="arch" type="xml">
<xpath expr="//button[@name='action_view_analytic_account_entries']" position="after">
<button class="oe_stat_button" type="object" name="action_view_mrp_production"
+1 -1
View File
@@ -57,6 +57,6 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_open_project_purchase_orders',
'show': self.purchase_orders_count > 0,
'sequence': 13,
'sequence': 36,
})
return buttons
@@ -4,8 +4,9 @@
<field name="name">project.project.view.inherited</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="priority">30</field>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(project.action_project_task_burndown_chart_report)d']" position="after">
<xpath expr="//button[@name='action_view_analytic_account_entries']" position="after">
<button name="action_open_project_purchase_orders" type="object" class="oe_stat_button" icon="fa-credit-card" attrs="{'invisible': [('purchase_orders_count', '=', 0)]}" groups="purchase.group_purchase_user">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
+15 -3
View File
@@ -18,6 +18,7 @@ class Project(models.Model):
sale_order_id = fields.Many2one(string='Sales Order', related='sale_line_id.order_id', help="Sales order to which the project is linked.")
has_any_so_to_invoice = fields.Boolean('Has SO to Invoice', compute='_compute_has_any_so_to_invoice')
sale_order_count = fields.Integer(compute='_compute_sale_order_count')
has_any_so_with_nothing_to_invoice = fields.Boolean('Has a SO with an invoice status of No', compute='_compute_has_any_so_with_nothing_to_invoice')
@api.model
def _map_tasks_default_valeus(self, task, project):
@@ -56,7 +57,6 @@ class Project(models.Model):
project.sale_order_count = len(project._get_all_sales_orders())
def action_view_sos(self):
# Opens all the sales orders linked to the project and the project's tasks
self.ensure_one()
all_sale_orders = self._get_all_sales_orders()
action_window = {
@@ -78,15 +78,27 @@ class Project(models.Model):
})
return action_window
@api.depends('sale_order_id.invoice_status', 'tasks.sale_order_id.invoice_status')
def _compute_has_any_so_with_nothing_to_invoice(self):
"""Has any Sale Order whose invoice_status is set as No"""
project_nothing_to_invoice = self.env['project.project'].search([
'|',
('sale_order_id.invoice_status', '=', 'no'),
('tasks.sale_order_id.invoice_status', '=', 'no'),
('id', 'in', self.ids)
])
project_nothing_to_invoice.has_any_so_with_nothing_to_invoice = True
(self - project_nothing_to_invoice).has_any_so_with_nothing_to_invoice = False
def action_create_invoice(self):
if not self.has_any_so_to_invoice:
raise UserError(_("There is nothing to invoice in this project."))
action = self.env["ir.actions.actions"]._for_xml_id("sale.action_view_sale_advance_payment_inv")
so_ids = (self.sale_order_id | self.task_ids.sale_order_id).filtered(lambda so: so.invoice_status == 'to invoice').ids
action['context'] = {
'active_id': so_ids[0] if len(so_ids) == 1 else False,
'active_ids': so_ids
}
if not self.has_any_so_to_invoice:
action['context']['default_advance_payment_method'] = 'percentage'
return action
# ----------------------------
@@ -30,9 +30,11 @@
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<field name="has_any_so_to_invoice" invisible="1"/>
<button name="action_create_invoice" string="Create Invoice" type="object" class="oe_highlight oe_read_only" groups="sales_team.group_sale_salesman_all_leads" attrs="{'invisible': [('has_any_so_to_invoice', '=', False)]}"/>
<field name="has_any_so_with_nothing_to_invoice" invisible="1"/>
<button name="action_create_invoice" string="Create Invoice" type="object" class="btn-primary" groups="sales_team.group_sale_salesman_all_leads" attrs="{'invisible': [('has_any_so_to_invoice', '=', False)]}"/>
<button name="action_create_invoice" string="Create Invoice" type="object" class="btn-secondary" groups="sales_team.group_sale_salesman_all_leads" attrs="{'invisible': ['|', ('has_any_so_with_nothing_to_invoice', '=', False), ('has_any_so_to_invoice', '=', True)]}"/>
</xpath>
<xpath expr="//button[@name='%(project.act_project_project_2_project_task_all)d']" position="before">
<xpath expr="//button[@name='%(project.project_update_all_action)d']" position="after">
<button class="d-none d-md-inline oe_stat_button"
type="object" name="action_view_sos" icon="fa-dollar"
attrs="{'invisible': [('sale_order_count', '=', 0)]}"
@@ -43,6 +43,6 @@ class Project(models.Model):
'action_type': 'object',
'action': 'action_open_project_vendor_bills',
'show': self.vendor_bill_count > 0,
'sequence': 14,
'sequence': 48,
})
return buttons
@@ -5,8 +5,9 @@
<field name="name">project.project.form.inherit</field>
<field name="model">project.project</field>
<field name="inherit_id" ref="project.edit_project"/>
<field name="priority">10</field>
<field name="arch" type="xml">
<xpath expr="//button[@name='%(project.action_project_task_burndown_chart_report)d']" position="after">
<xpath expr="//button[@name='action_view_analytic_account_entries']" position="after">
<button name="action_open_project_vendor_bills" type="object" class="oe_stat_button" icon="fa-pencil-square-o" attrs="{'invisible': [('vendor_bill_count', '=', 0)]}" groups="account.group_account_readonly">
<div class="o_field_widget o_stat_info">
<span class="o_stat_value">
+10
View File
@@ -408,6 +408,16 @@ class Project(models.Model):
'show': self.allow_timesheets and bool(self.analytic_account_id),
'sequence': 9,
})
if self.user_has_groups('sales_team.group_sale_salesman_all_leads'):
buttons.append({
'icon': 'dollar',
'text': _('Sales Orders'),
'number': self.sale_order_count,
'action_type': 'object',
'action': 'action_view_sos',
'show': self.sale_order_count > 0,
'sequence': 18,
})
return buttons
class ProjectTask(models.Model):
@@ -294,8 +294,8 @@ tour.register('sale_timesheet_tour', {
content: 'Check the user sees Total Sold section',
run: function () {},
}, {
trigger: ".oe_button_box .o_stat_text:contains('Sales Order')",
content: 'Check the user sees Sales Order Stat Button',
trigger: ".oe_button_box .o_stat_text:contains('Sales Orders')",
content: 'Check the user sees Sales Orders Stat Button',
run: function () {},
}, {
trigger: ".o_rightpanel_header:eq(1) .o_rightpanel_right_col:contains('Hours')",
@@ -15,7 +15,7 @@
<xpath expr="//button[@name='action_view_sos']" position="attributes">
<attribute name="attrs">{'invisible': ['|', ('allow_billable', '=', False), ('sale_order_count', '=', 0)]}</attribute>
</xpath>
<xpath expr="//button[@name='%(project.action_project_task_burndown_chart_report)d']" position="after">
<xpath expr="//button[@name='action_show_timesheets_by_employee_invoice_type']" position="after">
<button name="action_billable_time_button" type="object" class="oe_stat_button" icon="fa-clock-o" attrs="{'invisible': ['|', ('allow_timesheets', '=', False), ('analytic_account_id', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_approver">
<div class="o_field_widget o_stat_info">
<div class="oe_inline">
@@ -29,10 +29,6 @@
</div>
</button>
</xpath>
<xpath expr="//header" position="inside">
<!-- To be removed in master -->
<button name="action_make_billable" string="Create Sales Order" type="object" groups="sales_team.group_sale_salesman" invisible="1"/>
</xpath>
<xpath expr="//page[@name='settings']" position="after">
<page name="billing_employee_rate" string="Invoicing" attrs="{'invisible': ['|', ('allow_billable', '=', False), ('partner_id', '=', False)]}">
<group>