Skip to content

Commit

Permalink
[MERGE] hr_timesheet: Fix global UX
Browse files Browse the repository at this point in the history
### Goal of the PR

This PR is in the continuity of odoo#62360.

### Details

- Display remaining hours in task sales_line_id (name_get + task form view)
- Remove internal reference from services demo data
- Display sol in project timesheets list if project is billable
- Remove use of non_allow_billable in timesheets as it has been removed from project and task in previous PR (see above)
- Only recompute planned_hours for service product
- Determine the correct SOL for timesheet
- Set the last SOL of customer on timesheet if none is set on task or project
- Allow edition of so_line in timesheet
- Restrict SOL on project to sale lines with a service product
- Use same widget on partner_id many2One than in sale.order (using ranking)
- Remove timesheets table in SO and invoice portal.They were added in previous PR (see above). This introduces the use of links to /my/timesheets/.
- Review portal timesheets (my/timesheets/ and link from orders and invoices)
- Activate group_uom "Units of Measure" on sale_timesheet install
- Hide partner phone number in task form view
- Only determine SOL of task and timesheet if allow_billable=True
- Filter SOL in task so that it matches the SOL of the project SO
- Display red label if remaining hours is negative
- Change SO compute behavior
- Only open SO for salesman on project overview

task-2409761

--
I confirm I have signed the CLA and read the PR guidelines at www.odoo.com/submit-pr

closes odoo#62900

Signed-off-by: Yannick Tivisse (yti) <[email protected]>
  • Loading branch information
robodoo authored Dec 21, 2020
2 parents f5c0990 + 1712600 commit cde5aa7
Show file tree
Hide file tree
Showing 34 changed files with 678 additions and 226 deletions.
1 change: 0 additions & 1 deletion addons/event_sale/data/event_sale_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
<field name="uom_po_id" ref="uom.product_uom_unit"/>
<field name="name">Event Registration</field>
<field name="description_sale" eval="False"/>
<field name="default_code">EVENT_REG</field>
<field name="categ_id" ref="event_sale.product_category_events"/>
<field name="type">service</field>
</record>
Expand Down
2 changes: 0 additions & 2 deletions addons/event_sale/models/event_ticket.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,6 @@ def _init_column(self, column_name):
'list_price': 0,
'standard_price': 0,
'type': 'service',
'default_code': 'EVENT_REG',
'type': 'service',
}).id
self.env['ir.model.data'].create({
'name': 'product_product_event',
Expand Down
6 changes: 3 additions & 3 deletions addons/event_sale/static/tests/tours/event_configurator_ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,10 @@ tour.register('event_configurator_tour', {
}, {
trigger: 'div[name="product_id"] input, div[name="product_template_id"] input',
run: function (actions) {
actions.text('EVENT');
actions.text('Event');
}
}, {
trigger: 'ul.ui-autocomplete a:contains("EVENT")',
trigger: 'ul.ui-autocomplete a:contains("Event")',
run: 'click'
}, {
trigger: 'div[name="event_id"] input',
Expand Down Expand Up @@ -56,7 +56,7 @@ tour.register('event_configurator_tour', {
trigger: 'ul.nav a:contains("Order Lines")',
run: 'click'
}, {
trigger: 'td:contains("EVENT")',
trigger: 'td:contains("Event")',
run: 'click'
}, {
trigger: '.o_edit_product_configuration'
Expand Down
1 change: 0 additions & 1 deletion addons/hr_expense/data/hr_expense_data.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
<field name="list_price">0.0</field>
<field name="standard_price">1.0</field>
<field name="type">service</field>
<field name="default_code">EXP_GEN</field>
<field name="categ_id" ref="product.cat_expense"/>
<field name="can_be_expensed" eval="True"/>
</record>
Expand Down
2 changes: 0 additions & 2 deletions addons/hr_expense/data/hr_expense_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
<field name="standard_price">0.32</field>
<field name="type">service</field>
<field name="name">Car Travel Expenses</field>
<field name="default_code">EXP_CT</field>
<field name="uom_id" ref="uom.product_uom_km"/>
<field name="uom_po_id" ref="uom.product_uom_km"/>
<field name="categ_id" ref="product.cat_expense"/>
Expand All @@ -50,7 +49,6 @@
<field name="standard_price">700.0</field>
<field name="type">service</field>
<field name="name">Air Flight</field>
<field name="default_code">EXP_AF</field>
<field name="categ_id" ref="product.cat_expense"/>
<field name="can_be_expensed" eval="True" />
<field name="image_1920" type="base64" file="hr_expense/static/img/air_ticket-image.jpg"/>
Expand Down
79 changes: 46 additions & 33 deletions addons/hr_timesheet/controllers/portal.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,32 +23,59 @@ def _prepare_home_portal_values(self, counters):
values['timesheet_count'] = request.env['account.analytic.line'].sudo().search_count(domain)
return values

def _get_searchbar_inputs(self):
return {
'all': {'input': 'all', 'label': _('Search in All')},
'project': {'input': 'project', 'label': _('Search in Project')},
'name': {'input': 'name', 'label': _('Search in Description')},
'employee': {'input': 'employee', 'label': _('Search in Employee')},
'task': {'input': 'task', 'label': _('Search in Task')}
}

def _get_searchbar_groupby(self):
return {
'none': {'input': 'none', 'label': _('None')},
'project': {'input': 'project', 'label': _('Project')},
'task': {'input': 'task', 'label': _('Task')},
'date': {'input': 'date', 'label': _('Date')},
'employee': {'input': 'employee', 'label': _('Employee')}
}

def _get_search_domain(self, search_in, search):
search_domain = []
if search_in in ('project', 'all'):
search_domain = OR([search_domain, [('project_id', 'ilike', search)]])
if search_in in ('name', 'all'):
search_domain = OR([search_domain, [('name', 'ilike', search)]])
if search_in in ('employee', 'all'):
search_domain = OR([search_domain, [('employee_id', 'ilike', search)]])
if search_in in ('task', 'all'):
search_domain = OR([search_domain, [('task_id', 'ilike', search)]])
return search_domain

def _get_groupby_mapping(self):
return {
'project': 'project_id',
'task': 'task_id',
'employee': 'employee_id',
'date': 'date'
}

@http.route(['/my/timesheets', '/my/timesheets/page/<int:page>'], type='http', auth="user", website=True)
def portal_my_timesheets(self, page=1, sortby=None, filterby=None, search=None, search_in='all', groupby='none', **kw):
Timesheet_sudo = request.env['account.analytic.line'].sudo()
values = self._prepare_portal_layout_values()
domain = request.env['account.analytic.line']._timesheet_get_portal_domain()
_items_per_page = 100

searchbar_sortings = {
'date': {'label': _('Newest'), 'order': 'date desc'},
'name': {'label': _('Description'), 'order': 'name'},
}

searchbar_inputs = {
'all': {'input': 'all', 'label': _('Search in All')},
'project': {'input': 'project', 'label': _('Search in Project')},
'name': {'input': 'name', 'label': _('Search in Description')},
'employee': {'input': 'employee', 'label': _('Search in Employee')},
'task': {'input': 'task', 'label': _('Search in Task')},
}
searchbar_inputs = self._get_searchbar_inputs()

searchbar_groupby = {
'none': {'input': 'none', 'label': _('None')},
'project': {'input': 'project', 'label': _('Project')},
'task': {'input': 'task', 'label': _('Task')},
'date': {'input': 'date', 'label': _('Date')},
'employee': {'input': 'employee', 'label': _('Employee')},
}
searchbar_groupby = self._get_searchbar_groupby()

today = fields.Date.today()
quarter_start, quarter_end = date_utils.get_quarter(today)
Expand Down Expand Up @@ -77,16 +104,7 @@ def portal_my_timesheets(self, page=1, sortby=None, filterby=None, search=None,
domain = AND([domain, searchbar_filters[filterby]['domain']])

if search and search_in:
search_domain = []
if search_in in ('project', 'all'):
search_domain = OR([search_domain, [('project_id', 'ilike', search)]])
if search_in in ('name', 'all'):
search_domain = OR([search_domain, [('name', 'ilike', search)]])
if search_in in ('employee', 'all'):
search_domain = OR([search_domain, [('employee_id', 'ilike', search)]])
if search_in in ('task', 'all'):
search_domain = OR([search_domain, [('task_id', 'ilike', search)]])
domain += search_domain
domain += self._get_search_domain(search_in, search)

timesheet_count = Timesheet_sudo.search_count(domain)
# pager
Expand All @@ -95,27 +113,22 @@ def portal_my_timesheets(self, page=1, sortby=None, filterby=None, search=None,
url_args={'sortby': sortby, 'search_in': search_in, 'search': search, 'filterby': filterby, 'groupby': groupby},
total=timesheet_count,
page=page,
step=self._items_per_page
step=_items_per_page
)

def get_timesheets():
groupby_mapping = {
'project': 'project_id',
'task': 'task_id',
'employee': 'employee_id',
'date': 'date',
}
groupby_mapping = self._get_groupby_mapping()
field = groupby_mapping.get(groupby, None)
orderby = '%s, %s' % (field, order) if field else order
timesheets = Timesheet_sudo.search(domain, order=orderby, limit=self._items_per_page, offset=pager['offset'])
timesheets = Timesheet_sudo.search(domain, order=orderby, limit=_items_per_page, offset=pager['offset'])
if field:
if groupby == 'date':
time_data = Timesheet_sudo.read_group(domain, ['date', 'unit_amount:sum'], ['date:day'])
mapped_time = dict([(datetime.strptime(m['date:day'], '%d %b %Y').date(), m['unit_amount']) for m in time_data])
grouped_timesheets = [(Timesheet_sudo.concat(*g), mapped_time[k]) for k, g in groupbyelem(timesheets, itemgetter('date'))]
else:
time_data = time_data = Timesheet_sudo.read_group(domain, [field, 'unit_amount:sum'], [field])
mapped_time = dict([(m[field][0], m['unit_amount']) for m in time_data])
mapped_time = dict([(m[field][0] if m[field] else False, m['unit_amount']) for m in time_data])
grouped_timesheets = [(Timesheet_sudo.concat(*g), mapped_time[k.id]) for k, g in groupbyelem(timesheets, itemgetter(field))]
return timesheets, grouped_timesheets

Expand Down
100 changes: 75 additions & 25 deletions addons/hr_timesheet/views/hr_timesheet_portal_templates.xml
Original file line number Diff line number Diff line change
Expand Up @@ -36,46 +36,96 @@
<t t-foreach="grouped_timesheets" t-as="timesheets_with_hours">
<t t-set="timesheets" t-value="timesheets_with_hours[0]"/>
<t t-set="hours_spent" t-value="timesheets_with_hours[1]"/>
<thead>
<thead style="font-size: 0.8rem">
<tr t-if="not groupby =='none'" t-attf-class="{{'thead-light'}}">
<th t-if="groupby == 'project'" colspan="5">
<em class="font-weight-normal text-muted">Timesheets for project:</em>
<span t-field="timesheets[0].project_id.name"/>
</th>
<th t-elif="groupby == 'task'" colspan="5">
<em class="font-weight-normal text-muted">Timesheets for task:</em>
<span t-field="timesheets[0].task_id.name"/>
</th>
<th t-elif="groupby == 'date'" colspan="5">
<em class="font-weight-normal text-muted">Timesheets on </em>
<span t-field="timesheets[0].date"/>
</th>
<th t-elif="groupby == 'employee'" colspan="5">
<em class="font-weight-normal text-muted">Timesheets for employee:</em>
<span t-field="timesheets[0].employee_id.name"/>
</th>
<t t-if="groupby == 'project'">
<th t-if="groupby == 'project'" colspan="5">
<em class="font-weight-normal text-muted">Timesheets for project:</em>
<span t-field="timesheets[0].project_id.name"/>
</th>
<th colspan="1" class="text-right text-muted">
<t t-if="is_uom_day">
Total: <span class="text-muted" t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span class="text-muted" t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'task'">
<th colspan="5">
<em class="font-weight-normal text-muted">Timesheets for task:</em>
<span t-field="timesheets[0].task_id.name"/>
</th>
<th colspan="1" class="text-right text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'date'">
<th colspan="5">
<em class="font-weight-normal text-muted">Timesheets on </em>
<span t-field="timesheets[0].date"/>
</th>
<th colspan="1" class="text-right text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
<t t-elif="groupby == 'employee'">
<th colspan="5">
<em class="font-weight-normal text-muted">Timesheets for employee:</em>
<span t-field="timesheets[0].employee_id.name"/>
</th>
<th colspan="1" class="text-right text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</th>
</t>
</tr>
<tr t-else="">
<div style="text-align: right;" class="mr-2 mb-1 text-muted">
<t t-if="is_uom_day">
Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>
</t>
<t t-else="">
Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>
</t>
</div>
</tr>
<tr>
<th t-if="not groupby == 'date'">Date</th>
<th t-if="not groupby == 'employee'">Employee</th>
<th t-if="not groupby == 'project'">Project</th>
<th t-if="not groupby == 'task'">Task</th>
<th>Description</th>
<th t-if="is_uom_day" class="text-right">Days Spent <small class="text-muted">(Total: <span t-esc="timesheets[0]._convert_hours_to_days(hours_spent)" t-options='{"widget": "timesheet_uom"}'/>)</small></th>
<th t-else="" class="text-right">Hours Spent <small class="text-muted">(Total: <span t-esc="hours_spent" t-options='{"widget": "float_time"}'/>)</small></th>
<th t-if="is_uom_day" class="text-right">Days Spent</th>
<th t-else="" class="text-right">Hours Spent</th>
</tr>
</thead>
<tbody>
<tbody style="font-size: 0.8rem">
<t t-foreach="timesheets" t-as="timesheet">
<tr>
<td t-if="not groupby == 'date'"><span t-field="timesheet.date" t-options='{"widget": "date"}'/></td>
<td t-if="not groupby == 'employee'"><span t-field="timesheet.employee_id"/></td>
<td t-if="not groupby == 'project'"><span t-field="timesheet.project_id"/></td>
<td t-if="not groupby == 'task'"><span t-field="timesheet.task_id"/></td>
<td><span t-esc="timesheet.name"/></td>
<td t-if="not groupby == 'employee'"><span t-field="timesheet.employee_id" t-att-title="timesheet.employee_id.display_name" /></td>
<td t-if="not groupby == 'project'"><span t-field="timesheet.project_id" t-att-title="timesheet.project_id.display_name"/></td>
<td t-if="not groupby == 'task'"><span t-field="timesheet.task_id" t-att-title="timesheet.task_id.display_name"/></td>
<td><span t-esc="timesheet.name" t-att-title="timesheet.name"/></td>
<td class="text-right">
<span t-if="is_uom_day" t-esc="timesheet._get_timesheet_time_day()" t-options='{"widget": "timesheet_uom"}'/>
<span t-else= "" t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/>
<span t-else="" t-field="timesheet.unit_amount" t-options='{"widget": "float_time"}'/>
</td>
</tr>
</t>
Expand Down
2 changes: 1 addition & 1 deletion addons/portal/views/portal_templates.xml
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@
</div>
<t t-raw="0"/>
</div>
<form t-if="searchbar_inputs" class="form-inline o_portal_search_panel ml-lg-4">
<form t-if="searchbar_inputs" class="form-inline o_portal_search_panel ml-lg-4 col-xl-4 col-md-5">
<div class="input-group input-group-sm w-100">
<div class="input-group-prepend">
<button type="button" class="btn btn-secondary dropdown-toggle" data-toggle="dropdown"/>
Expand Down
2 changes: 0 additions & 2 deletions addons/product/data/product_demo.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
<field name="list_price">14.0</field>
<field name="standard_price">8.0</field>
<field name="type">service</field>
<field name="default_code">EXP_REST</field>
<field name="categ_id" ref="product.cat_expense"/>
</record>

Expand All @@ -46,7 +45,6 @@
<field name="list_price">400.0</field>
<field name="standard_price">400.0</field>
<field name="type">service</field>
<field name="default_code">EXP_HA</field>
<field name="uom_id" ref="uom.product_uom_day"/>
<field name="uom_po_id" ref="uom.product_uom_day"/>
<field name="categ_id" ref="cat_expense"/>
Expand Down
Loading

0 comments on commit cde5aa7

Please sign in to comment.