[IMP] sale,sale_project: show Project Overview
The project overview statbutton will only be shown when there's a proper Project Overview to display (when there are timesheets, etc.) closes odoo/odoo#57698 Taskid: 2257764 Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
This commit is contained in:
@@ -810,6 +810,21 @@
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="action_sale_order_form_view" model="ir.actions.act_window">
|
||||
<field name="name">Sales Order</field>
|
||||
<field name="type">ir.actions.act_window</field>
|
||||
<field name="res_model">sale.order</field>
|
||||
<field name="view_mode">form,tree,kanban,calendar,pivot,graph,activity</field>
|
||||
<field name="search_view_id" ref="sale_order_view_search_inherit_sale"/>
|
||||
<field name="help" type="html">
|
||||
<p class="o_view_nocontent_smiling_face">
|
||||
Create a new quotation, the first step of a new sale!
|
||||
</p><p>
|
||||
Once the quotation is confirmed, it becomes a sales order.<br/> You will be able to create an invoice and collect the payment.
|
||||
</p>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record id="sale_order_action_view_order_tree" model="ir.actions.act_window.view">
|
||||
<field name="sequence" eval="1"/>
|
||||
<field name="view_mode">tree</field>
|
||||
|
||||
@@ -16,6 +16,7 @@ class Project(models.Model):
|
||||
help="Sales order item to which the project is linked. Link the timesheet entry to the sales order item defined on the project. "
|
||||
"Only applies on tasks without sale order item defined, and if the employee is not in the 'Employee/Sales Order Item Mapping' of the project.")
|
||||
sale_order_id = fields.Many2one('sale.order', 'Sales Order', domain="[('partner_id', '=', partner_id)]", copy=False, help="Sales order to which the project is linked.")
|
||||
project_overview = fields.Boolean('Show Project Overview', compute='_compute_project_overview')
|
||||
|
||||
_sql_constraints = [
|
||||
('sale_order_required_if_sale_line', "CHECK((sale_line_id IS NOT NULL AND sale_order_id IS NOT NULL) OR (sale_line_id IS NULL))", 'The project should be linked to a sale order to select a sale order item.'),
|
||||
@@ -27,6 +28,13 @@ class Project(models.Model):
|
||||
defaults['sale_line_id'] = False
|
||||
return defaults
|
||||
|
||||
@api.depends('analytic_account_id')
|
||||
def _compute_project_overview(self):
|
||||
overview = self.env['project.project']
|
||||
if self.user_has_groups('analytic.group_analytic_accounting'):
|
||||
overview = self.filtered(lambda p: p.analytic_account_id)
|
||||
overview.project_overview = True
|
||||
(self - overview).project_overview = False
|
||||
|
||||
class ProjectTask(models.Model):
|
||||
_inherit = "project.task"
|
||||
|
||||
@@ -17,6 +17,8 @@ class SaleOrder(models.Model):
|
||||
'project.project', 'Project', readonly=True, states={'draft': [('readonly', False)], 'sent': [('readonly', False)]},
|
||||
help='Select a non billable project on which tasks can be created.')
|
||||
project_ids = fields.Many2many('project.project', compute="_compute_project_ids", string='Projects', copy=False, groups="project.group_project_user", help="Projects used in this sales order.")
|
||||
project_overview = fields.Boolean('Show Project Overview', compute='_compute_project_overview')
|
||||
project_count = fields.Integer(string='Number of Projects', compute='_compute_project_ids', groups='project.group_project_user')
|
||||
|
||||
@api.depends('order_line.product_id.project_id')
|
||||
def _compute_tasks_ids(self):
|
||||
@@ -33,6 +35,12 @@ class SaleOrder(models.Model):
|
||||
service_tracking == 'task_in_project' for service_tracking in order.order_line.mapped('product_id.service_tracking')
|
||||
)
|
||||
|
||||
@api.depends('project_ids')
|
||||
def _compute_project_overview(self):
|
||||
for order in self:
|
||||
billable_projects = order.project_ids.filtered('sale_line_id')
|
||||
order.project_overview = len(order.project_ids) == 1 and len(billable_projects) == 1 and billable_projects.project_overview
|
||||
|
||||
@api.depends('order_line.product_id', 'order_line.project_id')
|
||||
def _compute_project_ids(self):
|
||||
for order in self:
|
||||
@@ -40,6 +48,7 @@ class SaleOrder(models.Model):
|
||||
projects |= order.order_line.mapped('project_id')
|
||||
projects |= order.project_id
|
||||
order.project_ids = projects
|
||||
order.project_count = len(projects)
|
||||
|
||||
@api.onchange('project_id')
|
||||
def _onchange_project_id(self):
|
||||
@@ -92,16 +101,22 @@ class SaleOrder(models.Model):
|
||||
|
||||
def action_view_project_ids(self):
|
||||
self.ensure_one()
|
||||
if len(self.project_ids) == 1 and self.project_overview:
|
||||
return self.project_id.action_view_account_analytic_line()
|
||||
|
||||
view_form_id = self.env.ref('project.edit_project').id
|
||||
view_kanban_id = self.env.ref('project.view_project_kanban').id
|
||||
action = {
|
||||
'type': 'ir.actions.act_window',
|
||||
'domain': [('id', 'in', self.project_ids.ids)],
|
||||
'views': [(view_kanban_id, 'kanban'), (view_form_id, 'form')],
|
||||
'view_mode': 'kanban,form',
|
||||
'name': _('Projects'),
|
||||
'res_model': 'project.project',
|
||||
}
|
||||
if len(self.project_ids) == 1:
|
||||
action.update({'views': [(view_form_id, 'form')], 'res_id': self.project_ids.id})
|
||||
else:
|
||||
action['views'] = [(view_kanban_id, 'kanban'), (view_form_id, 'form')]
|
||||
return action
|
||||
|
||||
def write(self, values):
|
||||
|
||||
@@ -10,9 +10,12 @@
|
||||
<button type="object" name="action_view_task" class="oe_stat_button" icon="fa-tasks" attrs="{'invisible': [('tasks_count', '=', 0)]}" groups="project.group_project_user">
|
||||
<field name="tasks_count" widget="statinfo" string="Tasks"/>
|
||||
</button>
|
||||
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" string="Project" attrs="{'invisible': [('project_ids', '=', [])]}" groups="project.group_project_manager">
|
||||
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" attrs="{'invisible': ['|', ('project_ids', '=', []), ('project_overview', '=', True)]}" groups="project.group_project_manager">
|
||||
<field name="project_ids" invisible="1"/>
|
||||
<field name="project_overview" invisible="1"/>
|
||||
<field name="project_count" widget="statinfo" string="Projects"/>
|
||||
</button>
|
||||
<button type="object" name="action_view_project_ids" class="oe_stat_button" icon="fa-puzzle-piece" string="Project Overview" attrs="{'invisible': ['|', ('project_ids', '=', []), ('project_overview', '=', False)]}" groups="project.group_project_manager"/>
|
||||
</xpath>
|
||||
<xpath expr="//field[@name='analytic_account_id']" position="after">
|
||||
<field name="visible_project" invisible="1"/>
|
||||
|
||||
@@ -92,6 +92,12 @@ class Project(models.Model):
|
||||
|
||||
(self - projects).warning_employee_rate = False
|
||||
|
||||
@api.depends('analytic_account_id', 'allow_billable', 'allow_timesheets')
|
||||
def _compute_project_overview(self):
|
||||
super()._compute_project_overview()
|
||||
for project in self.filtered(lambda p: not p.project_overview):
|
||||
project.project_overview = project.allow_billable or project.allow_timesheets
|
||||
|
||||
@api.constrains('sale_line_id', 'pricing_type')
|
||||
def _check_sale_line_type(self):
|
||||
for project in self:
|
||||
|
||||
@@ -498,15 +498,24 @@ class Project(models.Model):
|
||||
|
||||
sale_orders = self.mapped('sale_line_id.order_id') | self.env['sale.order'].browse(task_so_ids)
|
||||
if sale_orders:
|
||||
so_action = dict(
|
||||
context={'create': False, 'edit': False, 'delete': False},
|
||||
domain=[('id', 'in', sale_orders.ids)],
|
||||
)
|
||||
|
||||
if len(sale_orders) == 1:
|
||||
so_action.update({
|
||||
'action': self.env.ref('sale.action_sale_order_form_view').sudo(),
|
||||
'res_id': sale_orders.id,
|
||||
})
|
||||
else:
|
||||
so_action['action'] = self.env.ref('sale.action_orders').sudo()
|
||||
|
||||
stat_buttons.append({
|
||||
'name': _('Sales Orders'),
|
||||
'count': len(sale_orders),
|
||||
'icon': 'fa fa-dollar',
|
||||
'action': _to_action_data(
|
||||
action=self.env.ref('sale.action_orders').sudo(),
|
||||
domain=[('id', 'in', sale_orders.ids)],
|
||||
context={'create': False, 'edit': False, 'delete': False}
|
||||
)
|
||||
'action': _to_action_data(**so_action),
|
||||
})
|
||||
|
||||
invoice_ids = self.env['sale.order'].search_read([('id', 'in', sale_orders.ids)], ['invoice_ids'])
|
||||
@@ -533,6 +542,10 @@ class Project(models.Model):
|
||||
else:
|
||||
timesheet_label = [_('Hours'), _('Recorded')]
|
||||
|
||||
default_project_ctx = {}
|
||||
if len(self) == 1:
|
||||
default_project_ctx = {'default_project_id': self.id}
|
||||
|
||||
stat_buttons.append({
|
||||
'name': timesheet_label,
|
||||
'count': sum(self.mapped('total_timesheet_time')),
|
||||
@@ -541,6 +554,7 @@ class Project(models.Model):
|
||||
'account.analytic.line',
|
||||
domain=[('project_id', 'in', self.ids)],
|
||||
views=[(ts_tree.id, 'list'), (ts_form.id, 'form')],
|
||||
context=default_project_ctx,
|
||||
)
|
||||
})
|
||||
|
||||
|
||||
@@ -38,14 +38,13 @@ class SaleOrder(models.Model):
|
||||
total_time += timesheet.unit_amount * timesheet.product_uom_id.factor_inv
|
||||
# Now convert to the proper unit of measure
|
||||
total_time *= sale_order.timesheet_encode_uom_id.factor
|
||||
sale_order.timesheet_total_duration = total_time
|
||||
sale_order.timesheet_total_duration = round(total_time)
|
||||
|
||||
def action_view_project_ids(self):
|
||||
self.ensure_one()
|
||||
# redirect to form or kanban view
|
||||
billable_projects = self.project_ids.filtered(lambda project: project.sale_line_id)
|
||||
if len(billable_projects) == 1 and self.env.user.has_group('project.group_project_manager'):
|
||||
action = billable_projects[0].action_view_timesheet_plan()
|
||||
if len(self.project_ids) == 1 and self.project_ids.project_overview and self.env.user.has_group('project.group_project_manager'):
|
||||
action = self.project_ids.action_view_timesheet_plan()
|
||||
else:
|
||||
action = super().action_view_project_ids()
|
||||
return action
|
||||
|
||||
@@ -6,9 +6,6 @@
|
||||
<field name="inherit_id" ref="sale_project.view_order_form_inherit_sale_project"/>
|
||||
<field name="arch" type="xml">
|
||||
<data>
|
||||
<xpath expr="//button[@name='action_view_project_ids']" position="attributes">
|
||||
<attribute name="string">Project Overview</attribute>
|
||||
</xpath>
|
||||
<xpath expr="//button[@name='action_view_invoice']" position="before">
|
||||
<field name="timesheet_count" invisible="1" />
|
||||
<button type="object"
|
||||
|
||||
Reference in New Issue
Block a user