[IMP] hr_timesheet,industry_fsm: Display CTA buttons with computed fields

Purpose
=======

The various buttons in FSM are currently displayed based on a set
of attributes. As there are many different configurations possible
for projects, it makes a lot of conditions (= the code is a living
nightmare). This task aims at replacing these attributes by computed
fields in order to gain clarity and to avoid the various bugs we
recently faced + add python tests to ensure their robustness.

Specification
=============

START/STOP/PAUSE/RESUME
-----------------------

- should not be visible if the task is marked as done
- should be visible in non-primary if Time > 0
- should be visible in primary if Time = 0
- when the timer is running, do not display the following
  buttons: Create Sales Order, Mark as done, Create Invoice,
  Sign report, Send report
- only visible if the 'Timesheet Timer' feature is enabled
  on the project

SIGN REPORT
-----------

- should not be visible
    - if Time = 0 AND Worksheet = 0 AND Products = 0
    - OR if the report already is signed
- should be visible in non primary if Time > 0 OR
  Worksheet > 0 OR Products > 0
- should be visible in primary if Time > 0 AND Worksheet > 0
  AND Products > 0
- once the report is signed, display an 'fa-globe Customer Preview'
  stat button redirecting to the webpage with the report
- only visible if the 'worksheets' feature is enabled on the project

SEND REPORT
-----------

- should not be visible if Time = 0 AND Worksheet = 0 AND
  Products = 0
- should be visible in non primary if Time > 0 OR Worksheet > 0
  OR Products > 0 OR if the report was already sent
- should be visible in primary if
    - Time > 0 AND Worksheet > 0 AND Products > 0
    - AND the report is signed
- only visible if the 'worksheets' feature is enabled on the project

MARK AS DONE
------------

- should not be visible if the task is marked as done
- should be visible in non primary if
    - Time > 0 OR Worksheet > 0 OR Products > 0
    - OR if Time = 0 OR Worksheet = 0 OR Products = 0
- should be visible in primary if Time > 0 AND Worksheet > 0 AND
  Products > 0
- only visible if is_fsm is true on the project

CREATE INVOICE
--------------

- should not be visible if the task is not marked as done OR if
  the SO linked to the task has the 'Fully invoiced' invoice status
- should be visible in non primary if the SO linked to the task has
  the 'Nothing to invoice' invoice status
- should be visible in primary if the SO linked to the task has one
  of the following invoice status: To Invoice, Upselling
- only visible if the Bill from tasks feature is enabled on
  the project

EXTRA QUOTATION + CREATE SALES ORDER
------------------------------------

no change of behavior

closes odoo/odoo#42508

Taskid: 2126857
Related: odoo/enterprise#6918
Signed-off-by: Yannick Tivisse (yti) <yti@odoo.com>
Co-authored-by: Yannick Tivisse <yti@odoo.com>
This commit is contained in:
laa
2020-01-24 13:16:07 +00:00
committed by Yannick Tivisse
co-authored by Yannick Tivisse
parent abbbeb5714
commit 7e09f931ff
4 changed files with 68 additions and 7 deletions
+32
View File
@@ -98,6 +98,38 @@ class Task(models.Model):
timesheet_timer_first_start = fields.Datetime("Timesheet Timer First Use", readonly=True)
timesheet_timer_last_stop = fields.Datetime("Timesheet Timer Last Use", readonly=True)
display_timesheet_timer = fields.Boolean("Display Timesheet Time", compute='_compute_display_timesheet_timer')
display_timer_start_primary = fields.Boolean(compute='_compute_display_timer_buttons')
display_timer_start_secondary = fields.Boolean(compute='_compute_display_timer_buttons')
display_timer_stop = fields.Boolean(compute='_compute_display_timer_buttons')
display_timer_pause = fields.Boolean(compute='_compute_display_timer_buttons')
display_timer_resume = fields.Boolean(compute='_compute_display_timer_buttons')
@api.depends('display_timesheet_timer', 'timer_start', 'timer_pause', 'total_hours_spent')
def _compute_display_timer_buttons(self):
for task in self:
start_p, start_s, stop, pause, resume = True, True, True, True, True
if not task.display_timesheet_timer:
start_p, start_s, stop, pause, resume = False, False, False, False, False
else:
if task.timer_start:
start_p, start_s = False, False
if task.timer_pause:
pause = False
else:
resume = False
else:
stop, pause, resume = False, False, False
if not task.total_hours_spent:
start_s = False
else:
start_p = False
task.write({
'display_timer_start_primary': start_p,
'display_timer_start_secondary': start_s,
'display_timer_stop': stop,
'display_timer_pause': pause,
'display_timer_resume': resume,
})
@api.depends('timesheet_ids.unit_amount')
def _compute_effective_hours(self):
+10 -5
View File
@@ -261,16 +261,21 @@
<field name="company_id" invisible="1"/>
<field name="display_timesheet_timer" invisible="1"/>
<field name="timer_pause" invisible="1" />
<field name="display_timer_start_primary" invisible="1"/>
<field name="display_timer_start_secondary" invisible="1"/>
<field name="display_timer_stop" invisible="1"/>
<field name="display_timer_pause" invisible="1"/>
<field name="display_timer_resume" invisible="1"/>
<button class="btn-primary" name="action_timer_start" type="object" string="Start"
attrs="{'invisible': ['|', '|', ('total_hours_spent', '!=', 0), ('timer_start', '!=', False), ('display_timesheet_timer', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
attrs="{'invisible': [('display_timer_start_primary', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
<button class="btn-secondary" name="action_timer_start" type="object" string="Start"
attrs="{'invisible': ['|', '|', ('total_hours_spent', '=', 0), ('timer_start', '!=', False), ('display_timesheet_timer', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
attrs="{'invisible': [('display_timer_start_secondary', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
<button class="btn-primary btn-danger o_fsm_stop" name="action_timer_stop" type="object" string="Stop"
attrs="{'invisible': ['|', ('timer_start', '=', False), ('display_timesheet_timer', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
attrs="{'invisible': [('display_timer_stop', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
<button class="btn-primary" name="action_timer_pause" type="object" string="Pause"
attrs="{'invisible': ['|', '|', ('timer_pause', '!=', False), ('timer_start', '=', False), ('display_timesheet_timer', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
attrs="{'invisible': [('display_timer_pause', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
<button class="btn-primary btn-info" name="action_timer_resume" type="object" string="Resume"
attrs="{'invisible': ['|', '|', ('timer_pause', '=', False), ('timer_start', '=', False), ('display_timesheet_timer', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
attrs="{'invisible': [('display_timer_resume', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
</xpath>
<xpath expr="//field[@name='stage_id']" position="attributes">
<attribute name="class">ml-2</attribute>
+22
View File
@@ -21,6 +21,15 @@ class Project(models.Model):
help="Employee/Sale Order Item Mapping:\n Defines to which sales order item an employee's timesheet entry will be linked."
"By extension, it defines the rate at which an employee's time on the project is billed.")
allow_billable = fields.Boolean("Bill from Tasks")
display_create_order = fields.Boolean(compute='_compute_display_create_order')
@api.depends('billable_type', 'allow_billable', 'sale_order_id', 'partner_id')
def _compute_display_create_order(self):
for project in self:
show = True
if not project.partner_id or project.billable_type != 'no' or project.allow_billable or project.sale_order_id:
show = False
project.display_create_order = show
@api.depends('sale_order_id', 'sale_line_id', 'sale_line_employee_ids')
def _compute_billable_type(self):
@@ -125,6 +134,19 @@ class ProjectTask(models.Model):
], string="Billable Type", compute='_compute_billable_type', compute_sudo=True, store=True)
is_project_map_empty = fields.Boolean("Is Project map empty", compute='_compute_is_project_map_empty')
allow_billable = fields.Boolean(related="project_id.allow_billable")
display_create_order = fields.Boolean(compute='_compute_display_create_order')
@api.depends(
'allow_billable', 'allow_timesheets', 'sale_order_id', 'display_timesheet_timer',
'timer_start')
def _compute_display_create_order(self):
for task in self:
show = True
if not task.allow_billable or not task.allow_timesheets or \
task.billable_type == 'employee_rate' or not task.partner_id or \
task.sale_order_id or (task.display_timesheet_timer and task.timer_start):
show = False
task.display_create_order = show
@api.onchange('sale_line_id')
def _onchange_sale_line_id(self):
@@ -7,11 +7,12 @@
<field name="inherit_id" ref="project.edit_project"/>
<field name="arch" type="xml">
<xpath expr="//header" position="inside">
<button name="action_make_billable" string="Create Sales Order" type="object" attrs="{'invisible': ['|', ('sale_order_id', '!=', False), ('billable_type', '!=', 'no')]}" groups="sales_team.group_sale_salesman"/>
<button name="action_make_billable" string="Create Sales Order" type="object" attrs="{'invisible': [('display_create_order', '=', False)]}" groups="sales_team.group_sale_salesman"/>
</xpath>
<xpath expr="//page[@name='emails']" position="after">
<page name="billing_employee_rate" string="Invoicing" attrs="{'invisible': [('billable_type', '=', 'no')]}">
<group>
<field name="display_create_order" invisible="1"/>
<field name="billable_type" invisible="1"/>
<field name="sale_order_id" attrs="{'invisible': [('billable_type', '=', 'no')]}"/>
<field name="sale_line_id" attrs="{'invisible': [('billable_type', '=', 'no')]}" context="{'create': False, 'edit': False, 'delete': False}"/>
@@ -74,7 +75,8 @@
<field name="arch" type="xml">
<xpath expr="//header" position='inside'>
<field name="allow_billable" invisible="1"/>
<button name="action_make_billable" string="Create Sales Order" type="object" attrs="{'invisible': ['|', '|', '|', '|', '|', ('allow_billable', '=', False), ('allow_timesheets', '=', False), ('billable_type', '=', 'employee_rate'), ('partner_id', '=', False), ('sale_order_id', '!=', False), '&amp;', ('display_timesheet_timer', '=', True), ('timer_start', '!=', False)]}" groups="sales_team.group_sale_salesman"/>
<field name="display_create_order" invisible="1"/>
<button name="action_make_billable" string="Create Sales Order" type="object" attrs="{'invisible': [('display_create_order', '=', False)]}" groups="sales_team.group_sale_salesman"/>
</xpath>
<xpath expr="//group[@name='sales_order_group']" position="inside">
<field name="billable_type" invisible="1"/>