diff --git a/addons/hr_timesheet/models/project_project.py b/addons/hr_timesheet/models/project_project.py index c322f5f28eb..6e0a54c96e6 100644 --- a/addons/hr_timesheet/models/project_project.py +++ b/addons/hr_timesheet/models/project_project.py @@ -1,27 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -import re from collections import defaultdict from odoo import models, fields, api, _ -from odoo.exceptions import UserError, ValidationError, RedirectWarning -from odoo.addons.rating.models.rating_data import OPERATOR_MAPPING - -PROJECT_TASK_READABLE_FIELDS = { - 'allow_subtasks', - 'allow_timesheets', - 'analytic_account_active', - 'effective_hours', - 'encode_uom_in_days', - 'planned_hours', - 'progress', - 'overtime', - 'remaining_hours', - 'subtask_effective_hours', - 'subtask_planned_hours', - 'timesheet_ids', - 'total_hours_spent', -} +from odoo.exceptions import ValidationError, RedirectWarning class Project(models.Model): _inherit = "project.project" @@ -231,227 +213,3 @@ class Project(models.Model): uom_from = self.company_id.project_time_mode_id uom_to = self.env.company.timesheet_encode_uom_id return round(uom_from._compute_quantity(time, uom_to, raise_if_failure=False), 2) - - -class Task(models.Model): - _name = "project.task" - _inherit = "project.task" - - project_id = fields.Many2one(domain=[('is_internal_project', '=', False)]) - analytic_account_active = fields.Boolean("Active Analytic Account", compute='_compute_analytic_account_active', compute_sudo=True) - allow_timesheets = fields.Boolean("Allow timesheets", related='project_id.allow_timesheets', help="Timesheets can be logged on this task.", readonly=True) - remaining_hours = fields.Float("Remaining Hours", compute='_compute_remaining_hours', store=True, readonly=True, help="Number of allocated hours minus the number of hours spent.") - remaining_hours_percentage = fields.Float(compute='_compute_remaining_hours_percentage', search='_search_remaining_hours_percentage') - effective_hours = fields.Float("Hours Spent", compute='_compute_effective_hours', compute_sudo=True, store=True) - total_hours_spent = fields.Float("Total Hours", compute='_compute_total_hours_spent', store=True, help="Time spent on this task and its sub-tasks (and their own sub-tasks).") - progress = fields.Float("Progress", compute='_compute_progress_hours', store=True, group_operator="avg") - overtime = fields.Float(compute='_compute_progress_hours', store=True) - subtask_effective_hours = fields.Float("Hours Spent on Sub-Tasks", compute='_compute_subtask_effective_hours', recursive=True, store=True, help="Time spent on the sub-tasks (and their own sub-tasks) of this task.") - timesheet_ids = fields.One2many('account.analytic.line', 'task_id', 'Timesheets') - encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days', default=lambda self: self._uom_in_days()) - display_name = fields.Char(help="""Use these keywords in the title to set new tasks:\n - 30h Allocate 30 hours to the task - #tags Set tags on the task - @user Assign the task to a user - ! Set the task a high priority\n - Make sure to use the right format and order e.g. Improve the configuration screen 5h #feature #v16 @Mitchell !""", - ) - @property - def SELF_READABLE_FIELDS(self): - return super().SELF_READABLE_FIELDS | PROJECT_TASK_READABLE_FIELDS - - def _uom_in_days(self): - return self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day') - - def _compute_encode_uom_in_days(self): - self.encode_uom_in_days = self._uom_in_days() - - @api.depends('analytic_account_id.active', 'project_id.analytic_account_id.active') - def _compute_analytic_account_active(self): - """ Overridden in sale_timesheet """ - for task in self: - task.analytic_account_active = task._get_task_analytic_account_id().active - - @api.depends('timesheet_ids.unit_amount') - def _compute_effective_hours(self): - if not any(self._ids): - for task in self: - task.effective_hours = round(sum(task.timesheet_ids.mapped('unit_amount')), 2) - return - timesheet_read_group = self.env['account.analytic.line'].read_group([('task_id', 'in', self.ids)], ['unit_amount', 'task_id'], ['task_id']) - timesheets_per_task = {res['task_id'][0]: res['unit_amount'] for res in timesheet_read_group} - for task in self: - task.effective_hours = round(timesheets_per_task.get(task.id, 0.0), 2) - - @api.depends('effective_hours', 'subtask_effective_hours', 'planned_hours') - def _compute_progress_hours(self): - for task in self: - if (task.planned_hours > 0.0): - task_total_hours = task.effective_hours + task.subtask_effective_hours - task.overtime = max(task_total_hours - task.planned_hours, 0) - if task_total_hours > task.planned_hours: - task.progress = 100 - else: - task.progress = round(100.0 * task_total_hours / task.planned_hours, 2) - else: - task.progress = 0.0 - task.overtime = 0 - - @api.depends('planned_hours', 'remaining_hours') - def _compute_remaining_hours_percentage(self): - for task in self: - if task.planned_hours > 0.0: - task.remaining_hours_percentage = task.remaining_hours / task.planned_hours - else: - task.remaining_hours_percentage = 0.0 - - def _search_remaining_hours_percentage(self, operator, value): - if operator not in OPERATOR_MAPPING: - raise NotImplementedError(_('This operator %s is not supported in this search method.', operator)) - query = f""" - SELECT id - FROM {self._table} - WHERE remaining_hours > 0 - AND planned_hours > 0 - AND remaining_hours / planned_hours {operator} %s - """ - return [('id', 'inselect', (query, (value,)))] - - @api.depends('effective_hours', 'subtask_effective_hours', 'planned_hours') - def _compute_remaining_hours(self): - for task in self: - task.remaining_hours = task.planned_hours - task.effective_hours - task.subtask_effective_hours - - @api.depends('effective_hours', 'subtask_effective_hours') - def _compute_total_hours_spent(self): - for task in self: - task.total_hours_spent = task.effective_hours + task.subtask_effective_hours - - @api.depends('child_ids.effective_hours', 'child_ids.subtask_effective_hours') - def _compute_subtask_effective_hours(self): - for task in self.with_context(active_test=False): - task.subtask_effective_hours = sum(child_task.effective_hours + child_task.subtask_effective_hours for child_task in task.child_ids) - - def _get_group_pattern(self): - return { - **super()._get_group_pattern(), - 'planned_hours': r'\s(\d+(?:\.\d+)?)[hH]', - } - - def _get_groups_patterns(self): - return ['(?:%s)*' % self._get_group_pattern()['planned_hours']] + super()._get_groups_patterns() - - def _get_cannot_start_with_patterns(self): - return super()._get_cannot_start_with_patterns() + [r'(?!\d+(?:\.\d+)?(?:h|H))'] - - def _extract_planned_hours(self): - planned_hours_group = self._get_group_pattern()['planned_hours'] - if self.allow_timesheets: - self.planned_hours = sum(float(num) for num in re.findall(planned_hours_group, self.display_name)) - self.display_name, dummy = re.subn(planned_hours_group, '', self.display_name) - - def _get_groups(self): - return [lambda task: task._extract_planned_hours()] + super()._get_groups() - - def action_view_subtask_timesheet(self): - self.ensure_one() - tasks = self.with_context(active_test=False)._get_all_subtasks() - action = self.env["ir.actions.actions"]._for_xml_id("hr_timesheet.timesheet_action_all") - graph_view_id = self.env.ref("hr_timesheet.view_hr_timesheet_line_graph_by_employee").id - new_views = [] - for view in action['views']: - if view[1] == 'graph': - view = (graph_view_id, 'graph') - new_views.insert(0, view) if view[1] == 'tree' else new_views.append(view) - action.update({ - 'display_name': _('Timesheets'), - 'context': {'default_project_id': self.project_id.id, 'grid_range': 'week'}, - 'domain': [('project_id', '!=', False), ('task_id', 'in', tasks.ids)], - 'views': new_views, - }) - return action - - def _get_timesheet(self): - # Is override in sale_timesheet - return self.timesheet_ids - - def write(self, values): - # a timesheet must have an analytic account (and a project) - if 'project_id' in values and not values.get('project_id') and self._get_timesheet(): - raise UserError(_('This task must be part of a project because there are some timesheets linked to it.')) - res = super(Task, self).write(values) - - if 'project_id' in values: - project = self.env['project.project'].browse(values.get('project_id')) - if project.allow_timesheets: - # We write on all non yet invoiced timesheet the new project_id (if project allow timesheet) - self._get_timesheet().write({'project_id': values.get('project_id')}) - - return res - - def name_get(self): - if self.env.context.get('hr_timesheet_display_remaining_hours'): - name_mapping = dict(super().name_get()) - for task in self: - if task.allow_timesheets and task.planned_hours > 0 and task.encode_uom_in_days: - days_left = _("(%s days remaining)") % task._convert_hours_to_days(task.remaining_hours) - name_mapping[task.id] = name_mapping.get(task.id, '') + u"\u00A0" + days_left - elif task.allow_timesheets and task.planned_hours > 0: - hours, mins = (str(int(duration)).rjust(2, '0') for duration in divmod(abs(task.remaining_hours) * 60, 60)) - hours_left = _( - "(%(sign)s%(hours)s:%(minutes)s remaining)", - sign='-' if task.remaining_hours < 0 else '', - hours=hours, - minutes=mins, - ) - name_mapping[task.id] = name_mapping.get(task.id, '') + u"\u00A0" + hours_left - return list(name_mapping.items()) - return super().name_get() - - @api.model - def _get_view_cache_key(self, view_id=None, view_type='form', **options): - """The override of _get_view changing the time field labels according to the company timesheet encoding UOM - makes the view cache dependent on the company timesheet encoding uom""" - key = super()._get_view_cache_key(view_id, view_type, **options) - return key + (self.env.company.timesheet_encode_uom_id,) - - @api.model - def _get_view(self, view_id=None, view_type='form', **options): - """ Set the correct label for `unit_amount`, depending on company UoM """ - arch, view = super()._get_view(view_id, view_type, **options) - # Use of sudo as the portal user doesn't have access to uom - arch = self.env['account.analytic.line'].sudo()._apply_timesheet_label(arch) - - if view_type in ['tree', 'pivot', 'graph', 'form'] and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'): - arch = self.env['account.analytic.line']._apply_time_label(arch, related_model=self._name) - - return arch, view - - @api.ondelete(at_uninstall=False) - def _unlink_except_contains_entries(self): - """ - If some tasks to unlink have some timesheets entries, these - timesheets entries must be unlinked first. - In this case, a warning message is displayed through a RedirectWarning - and allows the user to see timesheets entries to unlink. - """ - timesheet_data = self.env['account.analytic.line'].sudo()._read_group( - [('task_id', 'in', self.ids)], - ['task_id'], - ['task_id'], - ) - task_with_timesheets_ids = [res['task_id'][0] for res in timesheet_data] - if task_with_timesheets_ids: - if len(task_with_timesheets_ids) > 1: - warning_msg = _("These tasks have some timesheet entries referencing them. Before removing these tasks, you have to remove these timesheet entries.") - else: - warning_msg = _("This task has some timesheet entries referencing it. Before removing this task, you have to remove these timesheet entries.") - raise RedirectWarning( - warning_msg, self.env.ref('hr_timesheet.timesheet_action_task').id, - _('See timesheet entries'), {'active_ids': task_with_timesheets_ids}) - - @api.model - def _convert_hours_to_days(self, time): - uom_hour = self.env.ref('uom.product_uom_hour') - uom_day = self.env.ref('uom.product_uom_day') - return round(uom_hour._compute_quantity(time, uom_day, raise_if_failure=False), 2) diff --git a/addons/hr_timesheet/models/project_task.py b/addons/hr_timesheet/models/project_task.py index 2a4dbca2fae..dee648f593b 100644 --- a/addons/hr_timesheet/models/project_task.py +++ b/addons/hr_timesheet/models/project_task.py @@ -1,10 +1,9 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import re -from collections import defaultdict from odoo import models, fields, api, _ -from odoo.exceptions import UserError, ValidationError, RedirectWarning +from odoo.exceptions import UserError, RedirectWarning from odoo.addons.rating.models.rating_data import OPERATOR_MAPPING PROJECT_TASK_READABLE_FIELDS = { @@ -23,222 +22,11 @@ PROJECT_TASK_READABLE_FIELDS = { 'total_hours_spent', } -class Project(models.Model): - _inherit = "project.project" - - allow_timesheets = fields.Boolean( - "Timesheets", compute='_compute_allow_timesheets', store=True, readonly=False, - default=True) - analytic_account_id = fields.Many2one( - # note: replaces ['|', ('company_id', '=', False), ('company_id', '=', company_id)] - domain="""[ - '|', ('company_id', '=', False), ('company_id', '=', company_id), - ('partner_id', '=?', partner_id), - ]""" - ) - - timesheet_ids = fields.One2many('account.analytic.line', 'project_id', 'Associated Timesheets') - timesheet_count = fields.Integer(compute="_compute_timesheet_count", groups='hr_timesheet.group_hr_timesheet_user') - timesheet_encode_uom_id = fields.Many2one('uom.uom', related='company_id.timesheet_encode_uom_id') - total_timesheet_time = fields.Integer( - compute='_compute_total_timesheet_time', groups='hr_timesheet.group_hr_timesheet_user', - help="Total number of time (in the proper UoM) recorded in the project, rounded to the unit.") - encode_uom_in_days = fields.Boolean(compute='_compute_encode_uom_in_days') - is_internal_project = fields.Boolean(compute='_compute_is_internal_project', search='_search_is_internal_project') - remaining_hours = fields.Float(compute='_compute_remaining_hours', string='Remaining Invoiced Time', compute_sudo=True) - is_project_overtime = fields.Boolean('Project in Overtime', compute='_compute_remaining_hours', search='_search_is_project_overtime', compute_sudo=True) - allocated_hours = fields.Float(string='Allocated Hours') - - def _compute_encode_uom_in_days(self): - self.encode_uom_in_days = self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day') - - @api.depends('analytic_account_id') - def _compute_allow_timesheets(self): - without_account = self.filtered(lambda t: not t.analytic_account_id and t._origin) - without_account.update({'allow_timesheets': False}) - - @api.depends('company_id') - def _compute_is_internal_project(self): - for project in self: - project.is_internal_project = project == project.company_id.internal_project_id - - @api.model - def _search_is_internal_project(self, operator, value): - if not isinstance(value, bool): - raise ValueError(_('Invalid value: %s', value)) - if operator not in ['=', '!=']: - raise ValueError(_('Invalid operator: %s', operator)) - - query = """ - SELECT C.internal_project_id - FROM res_company C - WHERE C.internal_project_id IS NOT NULL - """ - if (operator == '=' and value is True) or (operator == '!=' and value is False): - operator_new = 'inselect' - else: - operator_new = 'not inselect' - return [('id', operator_new, (query, ()))] - - @api.model - def _get_view_cache_key(self, view_id=None, view_type='form', **options): - """The override of _get_view changing the time field labels according to the company timesheet encoding UOM - makes the view cache dependent on the company timesheet encoding uom""" - key = super()._get_view_cache_key(view_id, view_type, **options) - return key + (self.env.company.timesheet_encode_uom_id,) - - @api.model - def _get_view(self, view_id=None, view_type='form', **options): - arch, view = super()._get_view(view_id, view_type, **options) - if view_type in ['tree', 'form'] and self.env.company.timesheet_encode_uom_id == self.env.ref('uom.product_uom_day'): - arch = self.env['account.analytic.line']._apply_time_label(arch, related_model=self._name) - return arch, view - - @api.depends('allow_timesheets', 'timesheet_ids') - def _compute_remaining_hours(self): - timesheets_read_group = self.env['account.analytic.line']._read_group( - [('project_id', 'in', self.ids)], - ['project_id', 'unit_amount'], - ['project_id'], - lazy=False) - timesheet_time_dict = {res['project_id'][0]: res['unit_amount'] for res in timesheets_read_group} - for project in self: - project.remaining_hours = project.allocated_hours - timesheet_time_dict.get(project.id, 0) - project.is_project_overtime = project.remaining_hours < 0 - - @api.model - def _search_is_project_overtime(self, operator, value): - if not isinstance(value, bool): - raise ValueError(_('Invalid value: %s') % value) - if operator not in ['=', '!=']: - raise ValueError(_('Invalid operator: %s') % operator) - - query = """ - SELECT P.id - FROM project_project P - LEFT JOIN project_task T ON P.id = T.project_id - WHERE p.allocated_hours != 0 AND p.allow_timesheets - GROUP BY P.id - HAVING P.allocated_hours - SUM(T.effective_hours) < 0 - """ - if (operator == '=' and value is True) or (operator == '!=' and value is False): - operator_new = 'inselect' - else: - operator_new = 'not inselect' - return [('id', operator_new, (query, ()))] - - @api.constrains('allow_timesheets', 'analytic_account_id') - def _check_allow_timesheet(self): - for project in self: - if project.allow_timesheets and not project.analytic_account_id: - raise ValidationError(_('You cannot use timesheets without an analytic account.')) - - @api.depends('timesheet_ids') - def _compute_total_timesheet_time(self): - timesheets_read_group = self.env['account.analytic.line'].read_group( - [('project_id', 'in', self.ids)], - ['project_id', 'unit_amount', 'product_uom_id'], - ['project_id', 'product_uom_id'], - lazy=False) - timesheet_time_dict = defaultdict(list) - uom_ids = set(self.timesheet_encode_uom_id.ids) - - for result in timesheets_read_group: - uom_id = result['product_uom_id'] and result['product_uom_id'][0] - if uom_id: - uom_ids.add(uom_id) - timesheet_time_dict[result['project_id'][0]].append((uom_id, result['unit_amount'])) - - uoms_dict = {uom.id: uom for uom in self.env['uom.uom'].browse(uom_ids)} - for project in self: - # Timesheets may be stored in a different unit of measure, so first - # we convert all of them to the reference unit - # if the timesheet has no product_uom_id then we take the one of the project - total_time = 0.0 - for product_uom_id, unit_amount in timesheet_time_dict[project.id]: - factor = uoms_dict.get(product_uom_id, project.timesheet_encode_uom_id).factor_inv - total_time += unit_amount * (1.0 if project.encode_uom_in_days else factor) - # Now convert to the proper unit of measure set in the settings - total_time *= project.timesheet_encode_uom_id.factor - project.total_timesheet_time = int(round(total_time)) - - @api.depends('timesheet_ids') - def _compute_timesheet_count(self): - timesheet_read_group = self.env['account.analytic.line']._read_group( - [('project_id', 'in', self.ids)], - ['project_id'], - ['project_id'] - ) - timesheet_project_map = {project_info['project_id'][0]: project_info['project_id_count'] for project_info in timesheet_read_group} - for project in self: - project.timesheet_count = timesheet_project_map.get(project.id, 0) - - @api.model_create_multi - def create(self, vals_list): - """ Create an analytic account if project allow timesheet and don't provide one - Note: create it before calling super() to avoid raising the ValidationError from _check_allow_timesheet - """ - defaults = self.default_get(['allow_timesheets', 'analytic_account_id']) - for vals in vals_list: - allow_timesheets = vals.get('allow_timesheets', defaults.get('allow_timesheets')) - analytic_account_id = vals.get('analytic_account_id', defaults.get('analytic_account_id')) - if allow_timesheets and not analytic_account_id: - analytic_account = self._create_analytic_account_from_values(vals) - vals['analytic_account_id'] = analytic_account.id - return super().create(vals_list) - - def write(self, values): - # create the AA for project still allowing timesheet - if values.get('allow_timesheets') and not values.get('analytic_account_id'): - for project in self: - if not project.analytic_account_id: - project._create_analytic_account() - return super(Project, self).write(values) - - def name_get(self): - res = super().name_get() - if len(self.env.context.get('allowed_company_ids', [])) <= 1: - return res - name_mapping = dict(res) - for project in self: - if project.is_internal_project: - name_mapping[project.id] = f'{name_mapping[project.id]} - {project.company_id.name}' - return list(name_mapping.items()) - - @api.model - def _init_data_analytic_account(self): - self.search([('analytic_account_id', '=', False), ('allow_timesheets', '=', True)])._create_analytic_account() - - @api.ondelete(at_uninstall=False) - def _unlink_except_contains_entries(self): - """ - If some projects to unlink have some timesheets entries, these - timesheets entries must be unlinked first. - In this case, a warning message is displayed through a RedirectWarning - and allows the user to see timesheets entries to unlink. - """ - projects_with_timesheets = self.filtered(lambda p: p.timesheet_ids) - if projects_with_timesheets: - if len(projects_with_timesheets) > 1: - warning_msg = _("These projects have some timesheet entries referencing them. Before removing these projects, you have to remove these timesheet entries.") - else: - warning_msg = _("This project has some timesheet entries referencing it. Before removing this project, you have to remove these timesheet entries.") - raise RedirectWarning( - warning_msg, self.env.ref('hr_timesheet.timesheet_action_project').id, - _('See timesheet entries'), {'active_ids': projects_with_timesheets.ids}) - - def _convert_project_uom_to_timesheet_encode_uom(self, time): - uom_from = self.company_id.project_time_mode_id - uom_to = self.env.company.timesheet_encode_uom_id - return round(uom_from._compute_quantity(time, uom_to, raise_if_failure=False), 2) - - class Task(models.Model): _name = "project.task" _inherit = "project.task" project_id = fields.Many2one(domain=[('is_internal_project', '=', False)]) - display_project_id = fields.Many2one(domain=[('is_internal_project', '=', False)]) analytic_account_active = fields.Boolean("Active Analytic Account", compute='_compute_analytic_account_active', compute_sudo=True) allow_timesheets = fields.Boolean("Allow timesheets", related='project_id.allow_timesheets', help="Timesheets can be logged on this task.", readonly=True) remaining_hours = fields.Float("Remaining Hours", compute='_compute_remaining_hours', store=True, readonly=True, help="Number of allocated hours minus the number of hours spent.") diff --git a/addons/hr_timesheet/report/hr_timesheet_report_view.xml b/addons/hr_timesheet/report/hr_timesheet_report_view.xml index eb0bba937a0..764834750a8 100644 --- a/addons/hr_timesheet/report/hr_timesheet_report_view.xml +++ b/addons/hr_timesheet/report/hr_timesheet_report_view.xml @@ -185,35 +185,5 @@ - - - - - - - - - - - diff --git a/addons/hr_timesheet/views/hr_department_views.xml b/addons/hr_timesheet/views/hr_department_views.xml index 3b75a451f55..d5c814bdbc6 100644 --- a/addons/hr_timesheet/views/hr_department_views.xml +++ b/addons/hr_timesheet/views/hr_department_views.xml @@ -1,62 +1,5 @@ - - - Timesheets - account.analytic.line - - [('project_id', '!=', False), ('employee_id', '=', active_id)] - { - 'default_employee_id': active_id, - "is_timesheet": 1, - } - - - - - tree - - - - - - - form - - - - - - hr.employee.form.timesheet - hr.employee - - - - - 0 - - - - - - - - - - hr.employee.tree.timesheet - hr.employee - - - - 0 - - - - hr.department.kanban.inherit hr.department @@ -73,13 +16,4 @@ - - - Delete - - - form,list - code - action = records.action_unlink_wizard() - diff --git a/addons/hr_timesheet/views/hr_employee_views.xml b/addons/hr_timesheet/views/hr_employee_views.xml index 3b75a451f55..e1144a93a1f 100644 --- a/addons/hr_timesheet/views/hr_employee_views.xml +++ b/addons/hr_timesheet/views/hr_employee_views.xml @@ -1,24 +1,5 @@ - - - Timesheets - account.analytic.line - - [('project_id', '!=', False), ('employee_id', '=', active_id)] - { - 'default_employee_id': active_id, - "is_timesheet": 1, - } - - - - - tree - - - - form diff --git a/addons/hr_timesheet/views/hr_timesheet_menus.xml b/addons/hr_timesheet/views/hr_timesheet_menus.xml index 327a6298da3..f6f5cd87554 100644 --- a/addons/hr_timesheet/views/hr_timesheet_menus.xml +++ b/addons/hr_timesheet/views/hr_timesheet_menus.xml @@ -1,432 +1,63 @@ - - - - - + - - - - account.analytic.line.tree.hr_timesheet - account.analytic.line - - - - - - - - - - - - - - - - account.analytic.line.view.tree.with.user - account.analytic.line - - primary - 10 - - - 0 - 1 - many2one_avatar_employee - {'active_test': True} - - - - - - account.analytic.line.pivot - account.analytic.line - - - - - - - - - - - - account.analytic.line.pivot - account.analytic.line - - - - - - - - - - - account.analytic.line.graph - account.analytic.line - - - - - - - - - - - - - account.analytic.line.graph - account.analytic.line - - - - - - - - - - - - - account.analytic.line.graph - account.analytic.line - - - - - - - - - - - - account.analytic.line.graph.by.employee - account.analytic.line - - primary - - - - - - - account.analytic.line.form - account.analytic.line - 1 - - -
- - - - - - - - - - - - - - - - - -
-
-
- - - account.analytic.line.tree.with.user - account.analytic.line - - primary - 10 - - - - - - - - - - - account.analytic.line.search - account.analytic.line - - - - - - - - - - - - - - - - - - - - - - - - - - - - view.search.my.timesheet.menu - account.analytic.line - - primary - - - - - - - - - - - - - view.search.my.timesheet.menu - account.analytic.line - - primary - - - - - - - account.analytic.line.kanban - account.analytic.line - - - - - - - - - - - -
-
-
- -
-
- - - -
-
- -
-
- -
-
-
-
-
- - - - - - Duration: - -
-
-
-
-
-
-
-
- - - - My Timesheets - account.analytic.line - tree,form,kanban,pivot,graph - [('project_id', '!=', False), ('user_id', '=', uid)] - { - "search_default_week":1, - "is_timesheet": 1, - } - - -

- No activities found. Let's start a new one! -

-

- Track your working hours by projects every day and invoice this time to your customers. -

-
-
- - - tree - - - - - - - form - - - - - - - kanban - 6 - - - - - - pivot - 7 - - - - - - graph - 8 - - - - - - - - - - Task's Timesheets - account.analytic.line - { - 'is_timesheet': 1, - } - [('task_id', 'in', active_ids)] - tree - - - - - Project's Timesheets - account.analytic.line - { - 'is_timesheet': 1, - } - [('project_id', 'in', active_ids)] - tree - - - - - All Timesheets - account.analytic.line - tree,form,kanban,pivot,graph - - [('project_id', '!=', False)] - { - 'search_default_week':1, - 'is_timesheet': 1, - } - -

- No activities found. Let's start a new one! -

-

- Track your working hours by projects every day and invoice this time to your customers. -

-
-
- - - - tree - - - - - - - form - - - - - - kanban - 6 - - - - - - - pivot - - - - - - - graph - - - - - - -
+ name="Timesheets" + groups="group_hr_timesheet_approver" + sequence="5" + > + + + + + + + + + + + + +
diff --git a/addons/hr_timesheet/views/hr_timesheet_portal_templates.xml b/addons/hr_timesheet/views/hr_timesheet_portal_templates.xml index 23977eb485d..574a34bc5a2 100644 --- a/addons/hr_timesheet/views/hr_timesheet_portal_templates.xml +++ b/addons/hr_timesheet/views/hr_timesheet_portal_templates.xml @@ -1,6 +1,5 @@ - + diff --git a/addons/hr_timesheet/views/hr_timesheet_views.xml b/addons/hr_timesheet/views/hr_timesheet_views.xml index 327a6298da3..13080293ac8 100644 --- a/addons/hr_timesheet/views/hr_timesheet_views.xml +++ b/addons/hr_timesheet/views/hr_timesheet_views.xml @@ -1,23 +1,6 @@ - - - - - - - account.analytic.line.tree.hr_timesheet account.analytic.line @@ -276,7 +259,7 @@ My Timesheets @@ -333,18 +316,6 @@ - - - - Task's Timesheets account.analytic.line @@ -422,11 +393,70 @@ - + + Timesheets + account.analytic.line + + [('project_id', '!=', False), ('employee_id', '=', active_id)] + { + 'default_employee_id': active_id, + "is_timesheet": 1, + } + + + Timesheets + account.analytic.line + tree,kanban,pivot,graph,form + + [('project_id', '=', active_id)] + { + "default_project_id": active_id, + "is_timesheet": 1, + } + + +

+ Record a new activity +

+ Track your working hours by projects every day and invoice this time to your customers. +

+
+
+ + + tree + + + + + + + kanban + + + + + + + pivot + + + + + + + graph + + + + + + + form + + + +
diff --git a/addons/hr_timesheet/views/project_project_views.xml b/addons/hr_timesheet/views/project_project_views.xml index f72051134f9..e5ea4bee6c5 100644 --- a/addons/hr_timesheet/views/project_project_views.xml +++ b/addons/hr_timesheet/views/project_project_views.xml @@ -1,61 +1,6 @@ - - Timesheets - account.analytic.line - tree,kanban,pivot,graph,form - - [('project_id', '=', active_id)] - { - "default_project_id": active_id, - "is_timesheet": 1, - } - - -

- Record a new activity -

- Track your working hours by projects every day and invoice this time to your customers. -

-
-
- - - tree - - - - - - - kanban - - - - - - - pivot - - - - - - - graph - - - - - - - form - - - - - project.project.view.form.simplified.inherit.timesheet project.project @@ -90,154 +35,6 @@
- - project.task.form.inherited - project.task - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-
- - -
-
- -
-
-
-
- -
-
- - - -
-
-
-
-
-
-
- - - - - - - - - - - - -
-
- - - - - - - - - - - - - - - -
-
- - - - - - - - - - -
-
- project.project.tree.inherit.sale.timesheet project.project @@ -249,24 +46,6 @@
- - project.task.tree.inherited - project.task - - - - - - - - - - - - - - - project.project.timesheet.kanban.inherited project.project @@ -299,47 +78,6 @@ - - project.task.timesheet.kanban.inherited.progress - project.task - - - - - - - - - -
- - - - - - -
- -
-
-
-
-
- - - project.task.view.search.inherit.sale.timesheet.enterprise - project.task - - 10 - - - - - - - - - project.project.view.inherit.timesheet project.project @@ -351,44 +89,8 @@ - - project.task.view.graph.inherited - project.task - - - - hr_timesheet_graphview - - - - - - - - - - - - - - project.task.view.pivot.inherited - project.task - - - - - - - - - - - - - [('is_internal_project', '=', False)] - diff --git a/addons/hr_timesheet/views/project_task_portal_templates.xml b/addons/hr_timesheet/views/project_task_portal_templates.xml index a87655414fe..1c0344476f1 100644 --- a/addons/hr_timesheet/views/project_task_portal_templates.xml +++ b/addons/hr_timesheet/views/project_task_portal_templates.xml @@ -68,47 +68,4 @@ - - diff --git a/addons/hr_timesheet/views/project_task_sharing_views.xml b/addons/hr_timesheet/views/project_task_sharing_views.xml index 08751c0ad61..074b05e6214 100644 --- a/addons/hr_timesheet/views/project_task_sharing_views.xml +++ b/addons/hr_timesheet/views/project_task_sharing_views.xml @@ -1,6 +1,5 @@ - project.sharing.project.task.view.form.inherit project.task @@ -139,5 +138,4 @@ - diff --git a/addons/hr_timesheet/views/project_task_views.xml b/addons/hr_timesheet/views/project_task_views.xml index f72051134f9..c8524a124a3 100644 --- a/addons/hr_timesheet/views/project_task_views.xml +++ b/addons/hr_timesheet/views/project_task_views.xml @@ -1,95 +1,6 @@ - - Timesheets - account.analytic.line - tree,kanban,pivot,graph,form - - [('project_id', '=', active_id)] - { - "default_project_id": active_id, - "is_timesheet": 1, - } - - -

- Record a new activity -

- Track your working hours by projects every day and invoice this time to your customers. -

-
-
- - - tree - - - - - - - kanban - - - - - - - pivot - - - - - - - graph - - - - - - - form - - - - - - - project.project.view.form.simplified.inherit.timesheet - project.project - - 24 - - - - - - - - - - - Inherit project form : Invoicing Data - project.project - - 24 - - - - - - 0 - - - - - - - - - project.task.form.inherited project.task @@ -238,17 +149,6 @@ - - project.project.tree.inherit.sale.timesheet - project.project - - - - - - - - project.task.tree.inherited project.task @@ -267,38 +167,6 @@ - - project.project.timesheet.kanban.inherited - project.project - - 24 - - - - - - - - - - - - - - - - - -
- -
-
-
-
- project.task.timesheet.kanban.inherited.progress project.task @@ -340,17 +208,6 @@ - - project.project.view.inherit.timesheet - project.project - - - - - - - - project.task.view.graph.inherited project.task @@ -385,10 +242,5 @@ - - - [('is_internal_project', '=', False)] - -
diff --git a/addons/hr_timesheet/views/res_config_settings_views.xml b/addons/hr_timesheet/views/res_config_settings_views.xml index 203487af60f..ffb3a0d4e1c 100644 --- a/addons/hr_timesheet/views/res_config_settings_views.xml +++ b/addons/hr_timesheet/views/res_config_settings_views.xml @@ -44,7 +44,4 @@ inline {'module' : 'hr_timesheet', 'bin_size': False} - -