diff --git a/addons/hr_timesheet/__init__.py b/addons/hr_timesheet/__init__.py index 4e9b019bd7a..6aab6c417a5 100644 --- a/addons/hr_timesheet/__init__.py +++ b/addons/hr_timesheet/__init__.py @@ -5,3 +5,34 @@ from . import controllers from . import models from . import report from . import wizard + +from odoo import api, fields, SUPERUSER_ID, _ + + +def create_internal_project(cr, registry): + env = api.Environment(cr, SUPERUSER_ID, {}) + admin = env.ref('base.user_admin', raise_if_not_found=False) + if not admin: + return + project_vals = [] + for company in env['res.company'].search([]): + company = company.with_company(company) + project_vals += [{ + 'name': _('Internal'), + 'allow_timesheets': True, + 'company_id': company.id, + 'task_ids': [(0, 0, { + 'name': name, + 'company_id': company.id, + }) for name in [_('Training'), _('Meeting')]] + }] + project_ids = env['project.project'].create(project_vals) + + env['account.analytic.line'].create([{ + 'name': _("Analysis"), + 'user_id': admin.id, + 'date': fields.datetime.today(), + 'unit_amount': 0, + 'project_id': task.project_id.id, + 'task_id': task.id, + } for task in project_ids.task_ids.filtered(lambda t: t.company_id in admin.employee_ids.company_id)]) diff --git a/addons/hr_timesheet/__manifest__.py b/addons/hr_timesheet/__manifest__.py index 7af5ebb8404..841971e8836 100644 --- a/addons/hr_timesheet/__manifest__.py +++ b/addons/hr_timesheet/__manifest__.py @@ -20,7 +20,7 @@ It is completely integrated with the cost accounting module. It allows you to se up a management by affair. """, 'website': 'https://www.odoo.com/page/timesheet-mobile-app', - 'depends': ['hr', 'analytic', 'project', 'uom', 'timer'], + 'depends': ['hr', 'analytic', 'project', 'uom'], 'data': [ 'security/hr_timesheet_security.xml', 'security/ir.model.access.csv', @@ -42,4 +42,5 @@ up a management by affair. 'installable': True, 'application': False, 'auto_install': False, + 'post_init_hook': 'create_internal_project', } diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py index b61421191a1..dc41a4e5963 100644 --- a/addons/hr_timesheet/models/hr_timesheet.py +++ b/addons/hr_timesheet/models/hr_timesheet.py @@ -9,8 +9,7 @@ from odoo.exceptions import UserError, AccessError from odoo.osv import expression class AccountAnalyticLine(models.Model): - _name = 'account.analytic.line' - _inherit = ['account.analytic.line', 'timer.mixin'] + _inherit = 'account.analytic.line' @api.model def default_get(self, field_list): @@ -48,9 +47,6 @@ class AccountAnalyticLine(models.Model): employee_id = fields.Many2one('hr.employee', "Employee", check_company=True, domain=_domain_employee_id) department_id = fields.Many2one('hr.department', "Department", compute='_compute_department_id', store=True, compute_sudo=True) encoding_uom_id = fields.Many2one('uom.uom', compute='_compute_encoding_uom_id') - display_timer = fields.Boolean( - compute='_compute_display_timer', - help="Technical field used to display the timer if the encoding unit is 'Hours'.") def _compute_encoding_uom_id(self): for analytic_line in self: @@ -198,40 +194,3 @@ class AccountAnalyticLine(models.Model): 'amount': amount_converted, }) return result - - def _compute_display_timer(self): - uom_hour = self.env.ref('uom.product_uom_hour') - for analytic_line in self: - analytic_line.display_timer = analytic_line.encoding_uom_id == uom_hour - - def action_timer_start(self): - """ Start a timer if it isn't already started and the - timesheets allow to track time - """ - if not self.user_timer_id.timer_start and self.display_timer: - super().action_timer_start() - - def action_timer_stop(self): - """ Stop the current timer - """ - if self.user_timer_id.timer_start and self.display_timer: - minutes_spent = super().action_timer_stop() - self._add_timesheet_time(minutes_spent) - - def _add_timesheet_time(self, minutes_spent): - if self.unit_amount == 0 and minutes_spent < 1 and not self._context.get('prevent_deletion', False): - # Check if unit_amount equals 0 and minutes_spent is less than 1 minute, - # if yes, then remove the timesheet - self.unlink() - else: - if minutes_spent < 1: - amount = self.unit_amount - else: - minimum_duration = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_min_duration', 0)) - rounding = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_rounding', 0)) - minutes_spent = self._timer_rounding(minutes_spent, minimum_duration, rounding) - amount = self.unit_amount + minutes_spent * 60 / 3600 - self.write({'unit_amount': amount}) - - def _action_interrupt_user_timers(self): - self.action_timer_stop() \ No newline at end of file diff --git a/addons/hr_timesheet/models/project.py b/addons/hr_timesheet/models/project.py index 686acad3409..ed0ac3a5bad 100644 --- a/addons/hr_timesheet/models/project.py +++ b/addons/hr_timesheet/models/project.py @@ -16,12 +16,6 @@ class Project(models.Model): ('partner_id', '=?', partner_id), ]""" ) - allow_timesheet_timer = fields.Boolean( - 'Timesheet Timer', - compute='_compute_allow_timesheet_timer', - readonly=False, - store=True, - help="Use a timer to record timesheets on tasks") timesheet_ids = fields.One2many('account.analytic.line', 'project_id', 'Associated Timesheets') timesheet_encode_uom_id = fields.Many2one('uom.uom', related='company_id.timesheet_encode_uom_id') @@ -29,10 +23,6 @@ class Project(models.Model): compute='_compute_total_timesheet_time', help="Total number of time (in the proper UoM) recorded in the project, rounded to the unit.") - _sql_constraints = [ - ('timer_only_when_timesheet', "CHECK((allow_timesheets = 'f' AND allow_timesheet_timer = 'f') OR (allow_timesheets = 't'))", 'The timesheet timer can only be activated on project allowing timesheets.'), - ] - @api.onchange('analytic_account_id') def _onchange_analytic_account(self): if not self.analytic_account_id and self._origin: @@ -44,11 +34,6 @@ class Project(models.Model): if project.allow_timesheets and not project.analytic_account_id: raise ValidationError(_('To allow timesheet, your project %s should have an analytic account set.' % (project.name,))) - @api.depends('allow_timesheets') - def _compute_allow_timesheet_timer(self): - for project in self: - project.allow_timesheet_timer = project.allow_timesheets - @api.depends('timesheet_ids') def _compute_total_timesheet_time(self): for project in self: @@ -87,13 +72,7 @@ class Project(models.Model): for project in self: if not project.analytic_account_id and not values.get('analytic_account_id'): project._create_analytic_account() - result = super(Project, self).write(values) - if 'allow_timesheet_timer' in values and not values.get('allow_timesheet_timer'): - self.with_context(active_test=False).mapped('task_ids').write({ - 'timer_start': False, - 'timer_pause': False, - }) - return result + return super(Project, self).write(values) @api.model def _init_data_analytic_account(self): @@ -120,7 +99,7 @@ class Project(models.Model): class Task(models.Model): _name = "project.task" - _inherit = ["project.task", "timer.mixin"] + _inherit = "project.task" analytic_account_active = fields.Boolean("Active Analytic Account", compute='_compute_analytic_account_active') allow_timesheets = fields.Boolean("Allow timesheets", related='project_id.allow_timesheets', help="Timesheets can be logged on this task.", readonly=True) @@ -132,38 +111,6 @@ class Task(models.Model): subtask_effective_hours = fields.Float("Sub-tasks Hours Spent", compute='_compute_subtask_effective_hours', 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') - # YTI FIXME: Those field seems quite useless - 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_secondary = 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: - if not task.display_timesheet_timer: - task.update({ - 'display_timer_start_primary': False, - 'display_timer_start_secondary': False, - 'display_timer_stop': False, - 'display_timer_pause': False, - 'display_timer_resume': False, - }) - else: - super(Task, task)._compute_display_timer_buttons() - task.display_timer_start_secondary = task.display_timer_start_primary - if not task.timer_start: - task.update({ - 'display_timer_stop': False, - 'display_timer_pause': False, - 'display_timer_resume': False, - }) - if not task.total_hours_spent: - task.display_timer_start_secondary = False - else: - task.display_timer_start_primary = False - @api.depends('project_id.analytic_account_id.active') def _compute_analytic_account_active(self): """ Overridden in sale_timesheet """ @@ -204,11 +151,6 @@ class Task(models.Model): for task in self: task.subtask_effective_hours = sum(child_task.effective_hours + child_task.subtask_effective_hours for child_task in task.child_ids) - @api.depends('allow_timesheets', 'project_id.allow_timesheet_timer', 'analytic_account_active') - def _compute_display_timesheet_timer(self): - for task in self: - task.display_timesheet_timer = task.allow_timesheets and task.project_id.allow_timesheet_timer and task.analytic_account_active - def action_view_subtask_timesheet(self): self.ensure_one() tasks = self._get_all_subtasks() @@ -256,35 +198,6 @@ class Task(models.Model): result['arch'] = self.env['account.analytic.line']._apply_timesheet_label(result['arch']) return result - def action_timer_start(self): - if not self.user_timer_id.timer_start and self.display_timesheet_timer: - super(Task, self).action_timer_start() - - def action_timer_stop(self): - # timer was either running or paused - if self.user_timer_id.timer_start and self.display_timesheet_timer: - minutes_spent = super().action_timer_stop() - minimum_duration = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_min_duration', 0)) - rounding = int(self.env['ir.config_parameter'].sudo().get_param('hr_timesheet.timesheet_rounding', 0)) - minutes_spent = self._timer_rounding(minutes_spent, minimum_duration, rounding) - return self._action_create_timesheet(minutes_spent * 60 / 3600) - return False - - def _action_create_timesheet(self, time_spent): - return { - "name": _("Validate Spent Time"), - "type": 'ir.actions.act_window', - "res_model": 'project.task.create.timesheet', - "views": [[False, "form"]], - "target": 'new', - "context": { - **self.env.context, - 'active_id': self.id, - 'active_model': self._name, - 'default_time_spent': time_spent, - }, - } - def unlink(self): """ If some tasks to unlink have some timesheets entries, these diff --git a/addons/hr_timesheet/models/res_company.py b/addons/hr_timesheet/models/res_company.py index 8d48a0e4bb1..429799ec54e 100644 --- a/addons/hr_timesheet/models/res_company.py +++ b/addons/hr_timesheet/models/res_company.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from odoo import api, fields, models +from odoo import api, fields, models, _ class ResCompany(models.Model): @@ -36,3 +36,34 @@ class ResCompany(models.Model): default=_default_timesheet_encode_uom_id, domain=lambda self: [('category_id', '=', self.env.ref('uom.uom_categ_wtime').id)], help="""This will set the unit of measure used to encode timesheet. This will simply provide tools and widgets to help the encoding. All reporting will still be expressed in hours (default value).""") + + @api.model_create_multi + def create(self, values): + company = super(ResCompany, self).create(values) + # use sudo as the user could have the right to create a company + # but not to create a project. On the other hand, when the company + # is created, it is not in the allowed_company_ids on the env + company.sudo()._create_internal_project_task() + return company + + def _create_internal_project_task(self): + results = [] + for company in self: + company = company.with_company(company) + internal_project = company.env['project.project'].sudo().create({ + 'name': _('Internal'), + 'allow_timesheets': True, + 'company_id': company.id, + }) + + company.env['project.task'].sudo().create([{ + 'name': _('Training'), + 'project_id': internal_project.id, + 'company_id': company.id, + }, { + 'name': _('Meeting'), + 'project_id': internal_project.id, + 'company_id': company.id, + }]) + results.append(internal_project) + return results diff --git a/addons/hr_timesheet/static/src/js/timesheet_kanban_view.js b/addons/hr_timesheet/static/src/js/timesheet_kanban_view.js deleted file mode 100644 index 9a577a9895d..00000000000 --- a/addons/hr_timesheet/static/src/js/timesheet_kanban_view.js +++ /dev/null @@ -1,34 +0,0 @@ -odoo.define('hr_timesheet.timesheet_kanban_view', function (require) { -"use strict"; - -const KanbanController = require('web.KanbanController'); -const KanbanView = require('web.KanbanView'); -const viewRegistry = require('web.view_registry'); - -/** - * @override the KanbanController to add a trigger when a timer is launched or stopped - */ -const TimesheetKanbanController = KanbanController.extend({ - custom_events: _.extend({}, KanbanController.prototype.custom_events, { - 'timer_changed': '_onTimerChanged', - }), - /** - * When a timer is launched or stopped, we reload the view to see the updating. - * @param {Object} event - */ - _onTimerChanged: function (event) { - this.reload(); - } -}); - -const TimesheetKanbanView = KanbanView.extend({ - config: _.extend({}, KanbanView.prototype.config, { - Controller: TimesheetKanbanController - }) -}); - -viewRegistry.add('timesheet_kanban_view', TimesheetKanbanView); - -return { TimesheetKanbanController, TimesheetKanbanView }; - -}); diff --git a/addons/hr_timesheet/static/src/js/timesheet_list_view.js b/addons/hr_timesheet/static/src/js/timesheet_list_view.js deleted file mode 100644 index 903352a35de..00000000000 --- a/addons/hr_timesheet/static/src/js/timesheet_list_view.js +++ /dev/null @@ -1,34 +0,0 @@ -odoo.define('hr_timesheet.timesheet_list_view', function (require) { -"use strict"; - -const ListController = require('web.ListController'); -var ListView = require('web.ListView'); -var viewRegistry = require('web.view_registry'); - -/** - * @override the ListController to add a trigger when the timer is launched or stopped - */ -const TimesheetListController = ListController.extend({ - custom_events: _.extend({}, ListController.prototype.custom_events, { - 'timer_changed': '_onTimerChanged', - }), - /** - * When a timer is launched or stopped, we reload the view to see the updating. - * @param {Object} event - */ - _onTimerChanged: function (event) { - this.reload(); - } -}); - -const TimesheetListView = ListView.extend({ - config: _.extend({}, ListView.prototype.config, { - Controller: TimesheetListController - }) -}); - -viewRegistry.add('timesheet_tree', TimesheetListView); - -return { TimesheetListController, TimesheetListView }; - -}); diff --git a/addons/hr_timesheet/static/src/js/timesheet_uom.js b/addons/hr_timesheet/static/src/js/timesheet_uom.js index acfd9fc6e5b..45979121bfa 100644 --- a/addons/hr_timesheet/static/src/js/timesheet_uom.js +++ b/addons/hr_timesheet/static/src/js/timesheet_uom.js @@ -6,8 +6,6 @@ const fieldUtils = require('web.field_utils'); const fieldRegistry = require('web.field_registry'); -const Timer = require('timer.Timer'); - // We need the field registry to be populated, as we bind the // timesheet_uom widget on existing field widgets. require('web._field_registry'); @@ -74,8 +72,7 @@ const FieldTimesheetToggle = basicFields.FieldFloatToggle.extend({ /** - * Extend float time widget to add the using of a timer for duration - * (unit_amount) field. + * Extend float time widget */ const FieldTimesheetTime = basicFields.FieldFloatTime.extend({ init: function () { @@ -85,54 +82,7 @@ const FieldTimesheetTime = basicFields.FieldFloatTime.extend({ this.nodeOptions.factor = session.timesheet_uom_factor; this.parseOptions.factor = session.timesheet_uom_factor; } - }, - willstart() { - const timePromise = this._rpc({ - model: 'timer.timer', - method: 'get_server_time', - args: [] - }).then((time) => { - this.serverTime = time; - }); - return Promise.all([ - this._super(...arguments), - timePromise, - ]); - }, - - _render: async function () { - await this._super.apply(this, arguments); - // Check if the timer_start exists and it's not false - // In other word, when user clicks on play button, this button - // launches the "action_timer_start". - if (this.recordData.timer_start && !this.recordData.timer_pause) { - this.time = Timer.createTimer(this.recordData.unit_amount, this.recordData.timer_start, this.serverTime); - this._startTimeCounter(); - } - }, - /** - * @override - */ - destroy: function () { - clearTimeout(this.timer); - this._super.apply(this, arguments); - }, - _startTimeCounter: function () { - if (this.time) { - this.timer = setInterval(() => { - this.time.addSecond(); - if (this.$el.children().length) { - this.$el.contents()[1].replaceWith(this.time.toString()); - } else { - this.$el.text(this.time.toString()); - } - this.$el.addClass('font-weight-bold text-danger'); - }, 1000); - } else { - clearTimeout(this.timer); - this.$el.removeClass('font-weight-bold text-danger'); - } - }, + } }); diff --git a/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js b/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js index 165dfa6eb92..4eae761f444 100644 --- a/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js +++ b/addons/hr_timesheet/static/src/js/timesheet_uom_timer.js @@ -48,12 +48,11 @@ const FieldTimesheetTimeTimer = TimesheetUom.FieldTimesheetTime.extend({ _onToggleButton: async function (event) { const context = this.record.getContext(); - const prevent_deletion = this.attrs.options && this.attrs.options.prevent_deletion || false; event.stopPropagation(); const result = await this._rpc({ model: this.model, method: this._getActionButton(), - context: $.extend({}, context, {prevent_deletion: prevent_deletion}), + context: context, args: [this.res_id] }); diff --git a/addons/hr_timesheet/static/src/scss/timer_button.scss b/addons/hr_timesheet/static/src/scss/timer_button.scss deleted file mode 100644 index 8b269fa8b20..00000000000 --- a/addons/hr_timesheet/static/src/scss/timer_button.scss +++ /dev/null @@ -1,26 +0,0 @@ -.o-timer-button { - &:focus { - border: none !important; - outline: 0 !important; - } - - & > i { - transition: 0.3s; - - &.o-timer-play-button { - color: $link-color !important; - } - - &.o-timer-stop-button { - color: $red !important; - } - } - - &:hover > i.o-timer-play-button { - color: $link-hover-color !important; - } - - &:hover > i.o-timer-stop-button { - color: darken($red, 15%) !important; - } -} diff --git a/addons/hr_timesheet/tests/test_timesheet.py b/addons/hr_timesheet/tests/test_timesheet.py index f0cdc38a0da..463c0fdbdac 100644 --- a/addons/hr_timesheet/tests/test_timesheet.py +++ b/addons/hr_timesheet/tests/test_timesheet.py @@ -4,8 +4,6 @@ from odoo.tests.common import TransactionCase from odoo.exceptions import AccessError, UserError -from datetime import datetime, timedelta - class TestCommonTimesheet(TransactionCase): @@ -37,7 +35,6 @@ class TestCommonTimesheet(TransactionCase): 'allow_timesheets': True, 'partner_id': self.partner.id, 'analytic_account_id': self.analytic_account.id, - 'allow_timesheet_timer': True }) self.task1 = self.env['project.task'].create({ 'name': 'Task One', @@ -172,7 +169,6 @@ class TestTimesheet(TestCommonTimesheet): non_tracked_project = self.env['project.project'].create({ 'name': 'Project without timesheet', 'allow_timesheets': False, - 'allow_timesheet_timer': False, 'partner_id': self.partner.id, }) self.assertFalse(non_tracked_project.analytic_account_id, "A non time-tracked project shouldn't generate an analytic account") @@ -289,77 +285,3 @@ class TestTimesheet(TestCommonTimesheet): }, { 'amount': -12.0, }]) - - def test_minutes_computing_after_timer_stop(self): - """ Test if unit_amount is updated after stoping a timer """ - Timesheet = self.env['account.analytic.line'] - timesheet_1 = Timesheet.with_user(self.user_employee).create({ - 'project_id': self.project_customer.id, - 'task_id': self.task1.id, - 'name': '/', - 'unit_amount': 1, - }) - - # When the timer is less than 1 minute - now = datetime.now() - timesheet_1.with_user(self.user_employee).action_timer_start() - timesheet_1.with_user(self.user_employee).user_timer_id.timer_start = now - timedelta(seconds=28) - timesheet_1.with_user(self.user_employee).action_timer_stop() - - self.assertEqual(timesheet_1.unit_amount, 1, 'unit_amount still should have the same value') - - # When the timer is greater than 1 minute - now = datetime.now() - timesheet_1.with_user(self.user_employee).action_timer_start() - timesheet_1.with_user(self.user_employee).user_timer_id.timer_start = now - timedelta(minutes=1, seconds=28) - timesheet_1.with_user(self.user_employee).action_timer_stop() - - self.assertGreater(timesheet_1.unit_amount, 1, 'unit_amount should be greated than his last value') - - def test_allow_timesheets_and_timer(self): - """ - Check that a modification of the 'allow_timesheets' field updates correctly the - 'allow_timesheet_timer' field. - """ - Project = self.env['project.project'] - - # case 1: create a project with allow_timesheets set to FALSE - project_1 = Project.create({ - 'name': 'Project 1', - 'allow_timesheets': False, - 'partner_id': self.partner.id - }) - - self.assertFalse( - project_1.allow_timesheet_timer, - "On project creation with 'allow_timesheets' set to FALSE, 'allow_timesheet_timer' shall be set to FALSE") - - # case 2: create a project with allow_timesheets set to TRUE - project_2 = Project.create({ - 'name': 'Project 2', - 'allow_timesheets': True, - 'partner_id': self.partner.id, - 'analytic_account_id': self.analytic_account.id - }) - - self.assertTrue( - project_2.allow_timesheet_timer, - "On project creation with 'allow_timesheets' set to TRUE, 'allow_timesheet_timer' shall be set to TRUE") - - # case 3: change 'allow_timesheets' from FALSE to TRUE - project_1.write({ - 'allow_timesheets': True - }) - - self.assertTrue( - project_1.allow_timesheet_timer, - "On 'allow_timesheets' change to TRUE, 'allow_timesheet_timer' shall be set to TRUE") - - # case 4: change 'allow_timesheets' from TRUE to FALSE - project_2.write({ - 'allow_timesheets': False - }) - - self.assertFalse( - project_2.allow_timesheet_timer, - "On 'allow_timesheets' change to FALSE, 'allow_timesheet_timer' shall be set to FALSE") diff --git a/addons/hr_timesheet/views/assets.xml b/addons/hr_timesheet/views/assets.xml index b20bdf051b8..816f36f5c53 100644 --- a/addons/hr_timesheet/views/assets.xml +++ b/addons/hr_timesheet/views/assets.xml @@ -2,12 +2,8 @@ - - - - diff --git a/addons/hr_timesheet/views/hr_timesheet_views.xml b/addons/hr_timesheet/views/hr_timesheet_views.xml index 994b4ec80f7..bb3ecd7a776 100644 --- a/addons/hr_timesheet/views/hr_timesheet_views.xml +++ b/addons/hr_timesheet/views/hr_timesheet_views.xml @@ -21,16 +21,13 @@ account.analytic.line.tree.hr_timesheet account.analytic.line - + - - + + - - + - - @@ -93,9 +90,8 @@ - + - @@ -160,15 +156,13 @@ account.analytic.line.kanban account.analytic.line - + - - @@ -184,9 +178,7 @@ - - - + @@ -214,7 +206,6 @@ tree,form [('project_id', '!=', False), ('user_id', '=', uid)] { - "my_timesheet_display_timer": True, "search_default_week":1, } diff --git a/addons/hr_timesheet/views/project_views.xml b/addons/hr_timesheet/views/project_views.xml index a1cfd5eb110..4323bfb9ef4 100644 --- a/addons/hr_timesheet/views/project_views.xml +++ b/addons/hr_timesheet/views/project_views.xml @@ -71,39 +71,6 @@ - - project.view.form.inherit - project.project - - - - - - - - - - - Track your time using a timer - - - - - - - - - project.project.view.form.simplified.inherit - project.project - - 32 - - - - - - - project.task.form.inherited project.task @@ -263,40 +230,6 @@ - - project.task.view.form.inherit.sale.timesheet.enterprise - project.task - - - - - - - - - - - - - - - - - - - ml-2 - - - - - - - project.task.view.search.inherit.sale.timesheet.enterprise project.task @@ -308,24 +241,5 @@ - - - project.task.kanban.timer - project.task - - - - - - - - - - - - - - - diff --git a/addons/hr_timesheet/wizard/project_task_create_timesheet.py b/addons/hr_timesheet/wizard/project_task_create_timesheet.py index d24792804a1..df74d492a10 100644 --- a/addons/hr_timesheet/wizard/project_task_create_timesheet.py +++ b/addons/hr_timesheet/wizard/project_task_create_timesheet.py @@ -36,9 +36,4 @@ class ProjectTaskCreateTimesheet(models.TransientModel): 'user_id': self.env.uid, 'unit_amount': self.time_spent, } - self.task_id.write({ - 'timer_start': False, - 'timer_pause': False, - 'timesheet_timer_last_stop': fields.datetime.now(), - }) return self.env['account.analytic.line'].create(values) diff --git a/addons/project_timesheet_holidays/models/res_company.py b/addons/project_timesheet_holidays/models/res_company.py index c6e15eff9c2..bd50f4a0864 100644 --- a/addons/project_timesheet_holidays/models/res_company.py +++ b/addons/project_timesheet_holidays/models/res_company.py @@ -23,27 +23,39 @@ class Company(models.Model): raise ValidationError(_('The Internal Project of a company should be in that company.')) def init(self): - self.search([('leave_timesheet_project_id', '=', False)])._create_leave_project_task() - - @api.model_create_multi - def create(self, values): - company = super(Company, self).create(values) - # use sudo as the user could have the right to create a company - # but not to create a project. On the other hand, when the company - # is created, it is not in the allowed_company_ids on the env - company.sudo()._create_leave_project_task() - return company - - def _create_leave_project_task(self): - for company in self: + for company in self.search([('leave_timesheet_project_id', '=', False)]): company = company.with_company(company) - if not company.leave_timesheet_project_id: - project = company.env['project.project'].sudo().create({ - 'name': _('Internal Project'), + project = company.env['project.project'].search([ + ('name', '=', _('Internal')), + ('allow_timesheets', '=', True), + ('company_id', '=', company.id), + ], limit=1) + if not project: + project = company.env['project.project'].create({ + 'name': _('Internal'), 'allow_timesheets': True, + 'company_id': company.id, + }) + company.write({ + 'leave_timesheet_project_id': project.id, + }) + if not company.leave_timesheet_task_id: + task = company.env['project.task'].create({ + 'name': _('Time Off'), + 'project_id': company.leave_timesheet_project_id.id, 'active': False, 'company_id': company.id, }) + company.write({ + 'leave_timesheet_task_id': task.id, + }) + + def _create_internal_project_task(self): + projects = super()._create_internal_project_task() + for project in projects: + company = project.company_id + company = company.with_company(company) + if not company.leave_timesheet_project_id: company.write({ 'leave_timesheet_project_id': project.id, }) diff --git a/addons/sale_timesheet/models/project.py b/addons/sale_timesheet/models/project.py index 42ceb21ff93..4cf6b451ca6 100644 --- a/addons/sale_timesheet/models/project.py +++ b/addons/sale_timesheet/models/project.py @@ -176,14 +176,13 @@ class ProjectTask(models.Model): display_create_order = fields.Boolean(compute='_compute_display_create_order') @api.depends( - 'allow_billable', 'allow_timesheets', 'sale_order_id', 'display_timesheet_timer', - 'timer_start') + 'allow_billable', 'allow_timesheets', 'sale_order_id') 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): + task.sale_order_id: show = False task.display_create_order = show diff --git a/addons/sale_timesheet/models/sale_order.py b/addons/sale_timesheet/models/sale_order.py index 4078537eb7a..9ae4af48edd 100644 --- a/addons/sale_timesheet/models/sale_order.py +++ b/addons/sale_timesheet/models/sale_order.py @@ -114,7 +114,7 @@ class SaleOrderLine(models.Model): def _timesheet_create_project(self): project = super()._timesheet_create_project() - project.write({'allow_timesheets': True, 'allow_timesheet_timer': True}) + project.write({'allow_timesheets': True}) return project def _recompute_qty_to_invoice(self, date): diff --git a/addons/sale_timesheet/views/project_task_views.xml b/addons/sale_timesheet/views/project_task_views.xml index c57cfac4ca7..54d2a598a7b 100644 --- a/addons/sale_timesheet/views/project_task_views.xml +++ b/addons/sale_timesheet/views/project_task_views.xml @@ -52,9 +52,9 @@ project.project.view.form.simplified.inherit project.project - + - + diff --git a/addons/test_timer/__init__.py b/addons/test_timer/__init__.py deleted file mode 100644 index dc5e6b693d1..00000000000 --- a/addons/test_timer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import models diff --git a/addons/test_timer/__manifest__.py b/addons/test_timer/__manifest__.py deleted file mode 100644 index 12bd6e31e32..00000000000 --- a/addons/test_timer/__manifest__.py +++ /dev/null @@ -1,20 +0,0 @@ -# -*- coding: utf-8 -*- - -{ - 'name': 'Timer Tests', - 'version': '1.0', - 'category': 'Hidden/Tests', - 'sequence': 8765, - 'summary': 'Timer Tests: feature and performance tests for timer', - 'description': """This module contains tests related to timer. Those are -contained in a separate module as it contains models used only to perform -tests independently to functional aspects of other models. """, - 'depends': ['timer'], - 'data': [ - 'security/ir.model.access.csv', - ], - 'demo': [ - ], - 'installable': True, - 'application': False, -} diff --git a/addons/test_timer/models/__init__.py b/addons/test_timer/models/__init__.py deleted file mode 100644 index e2a77737638..00000000000 --- a/addons/test_timer/models/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import timer_test diff --git a/addons/test_timer/models/timer_test.py b/addons/test_timer/models/timer_test.py deleted file mode 100644 index 5eb26b7618d..00000000000 --- a/addons/test_timer/models/timer_test.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class TimerTest(models.Model): - """ A very simple model only inheriting from timer.mixin to test - timer features """ - _description = 'Timer Model' - _name = 'timer.test' - _inherit = ['timer.mixin'] - - name = fields.Char(required=1) - - -class OverrideInterruptionTimerTest(models.Model): - """ A very simple model inheriting from timer.mixin and - overriding _action_interrupt_user_timers() """ - _description = 'Interruption Timer Model' - _name = 'interruption.timer.test' - _inherit = ['timer.mixin'] - - def _action_interrupt_user_timers(self): - self.action_timer_stop() diff --git a/addons/test_timer/security/ir.model.access.csv b/addons/test_timer/security/ir.model.access.csv deleted file mode 100644 index d3c447a94e4..00000000000 --- a/addons/test_timer/security/ir.model.access.csv +++ /dev/null @@ -1,3 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_timer_test_user,timer.test.user,model_timer_test,base.group_user,1,1,1,1 -access_interruption_timer_test_user,timer.interruption.test.user,model_interruption_timer_test,base.group_user,1,1,1,1 diff --git a/addons/test_timer/tests/__init__.py b/addons/test_timer/tests/__init__.py deleted file mode 100644 index a52db405139..00000000000 --- a/addons/test_timer/tests/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import test_timer \ No newline at end of file diff --git a/addons/test_timer/tests/test_timer.py b/addons/test_timer/tests/test_timer.py deleted file mode 100644 index 427210524da..00000000000 --- a/addons/test_timer/tests/test_timer.py +++ /dev/null @@ -1,141 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo.exceptions import AccessError - -from odoo.tests.common import SavepointCase - - -class TestTimer(SavepointCase): - - @classmethod - def setUpClass(cls): - super(TestTimer, cls).setUpClass() - - # Setup mixin - cls.test_timer = cls.env['timer.test'].create({'name': 'Timer 1'}) - cls.test_timer_bis = cls.env['timer.test'].create({'name': 'Timer 2'}) - - # Setup users - cls.usr1 = cls.env['res.users'].create({ - 'name': 'Usr1', - 'login': 'Usr1', - 'email': 'usr1@test.com', - 'groups_id': [(6, 0, [cls.env.ref('base.group_user').id])], - }) - - cls.usr2 = cls.env['res.users'].create({ - 'name': 'Usr2', - 'login': 'Usr2', - 'email': 'usr2@test.com', - 'groups_id': [(6, 0, [cls.env.ref('base.group_user').id])], - }) - - - def test_timer_from_self_user(self): - - # Start and stop - self.test_timer.action_timer_start() - self.assertEqual(len(self.test_timer.user_timer_id), 1, 'It should have created one timer') - - self.test_timer.action_timer_stop() - self.assertEqual(len(self.test_timer.user_timer_id), 0, 'It should have delete the timer') - - # Start the first timer then the second one - self.test_timer.action_timer_start() - self.test_timer_bis.action_timer_start() - - self.assertEqual(len(self.env['timer.timer'].search([])), 2, 'It should have created 2 timers for the same user') - self.assertFalse(self.test_timer.is_timer_running, "The first timer should be in pause because the second one interrupt it") - self.assertTrue(self.test_timer_bis.is_timer_running, 'The second timer should be running') - - # Resume the first one - self.test_timer.action_timer_resume() - - self.assertTrue(self.test_timer.is_timer_running, 'The first timer should be running after being resumed') - self.assertFalse(self.test_timer_bis.is_timer_running, 'The second timer should be in pause when another timer has been started') - - # Start a new test timer with interruption override - override_test_timer = self.env['interruption.timer.test'].create({}) - override_test_timer.action_timer_start() - - self.assertEqual(len(self.env['timer.timer'].search([])), 3, 'A third timer should be created') - self.assertFalse(self.test_timer.is_timer_running, 'The first timer has been interrupted and should be in pause') - self.assertFalse(self.test_timer_bis.is_timer_running, 'The second timer has been interrupted and should be in pause') - self.assertTrue(override_test_timer.is_timer_running, 'The third timer should be running') - - # Resume another timer to interrupt the new one - self.test_timer_bis.action_timer_resume() - - self.assertEqual(len(self.env['timer.timer'].search([])), 2, 'It should remains only 2 timers') - self.assertEqual(len(override_test_timer.user_timer_id), 0, 'The third timer should be deleted because of his override method') - - def test_timer_with_many_users(self): - - # 2 users, 1 record = 2 timers - self.test_timer.with_user(self.usr1).action_timer_start() - self.test_timer.with_user(self.usr2).action_timer_start() - - self.assertEqual(len(self.env['timer.timer'].search([])), 2, 'It should have created two timers') - self.assertEqual(len(self.test_timer.with_user(self.usr1).user_timer_id), 1, 'It should exist only one timer for this user, model and record') - self.assertEqual(len(self.test_timer.with_user(self.usr2).user_timer_id), 1, 'It should exist only one timer for this user, model and record') - - # Stop one of them - self.test_timer.with_user(self.usr2).action_timer_stop() - - self.assertEqual(len(self.env['timer.timer'].search([])), 1, 'It should have deleted one timer') - self.assertEqual(len(self.test_timer.with_user(self.usr1).user_timer_id), 1, 'It should exist only one timer for this user, model and record') - self.assertEqual(len(self.test_timer.with_user(self.usr2).user_timer_id), 0, 'It shouldn\'t exit one timer for this user, model and record') - - def test_timer_rounding(self): - - minutes_spent, minimum, rounding = 4.5,10,5 - result = self.test_timer._timer_rounding(minutes_spent, minimum, rounding) - self.assertEqual(result, 10, 'It should have been round to the minimum amount') - - minutes_spent = 12.4 - result = self.test_timer._timer_rounding(minutes_spent, minimum, rounding) - self.assertEqual(result, 15, 'It should have been round to the next multiple of 15') - - def test_timer_access_security(self): - - # Create usr1's timer1 - timer1 = self.env['timer.timer'].with_user(self.usr1).create({ - 'timer_start' : False, - 'timer_pause' : False, - 'is_timer_running' : False, - 'res_model' : self.test_timer._name, - 'res_id' : self.test_timer.id, - 'user_id' : self.usr1.id, - }) - - # Create usr1's timer2 - timer2 = self.env['timer.timer'].with_user(self.usr1).create({ - 'timer_start' : False, - 'timer_pause' : False, - 'is_timer_running' : False, - 'res_model' : self.test_timer_bis._name, - 'res_id' : self.test_timer_bis.id, - 'user_id' : self.usr1.id, - }) - - # Start timer2 - timer2.action_timer_start() - - with self.assertRaises(AccessError): - - # Try to create a timer with usr1 for usr2 (Create) - self.env['timer.timer'].with_user(self.usr1).create({ - 'timer_start' : False, - 'timer_pause' : False, - 'is_timer_running' : False, - 'res_model' : self.test_timer._name, - 'res_id' : self.test_timer.id, - 'user_id' : self.usr2.id, - }) - - # Try to start the timer1 with another usr2 (Write) - timer1.with_user(self.usr2).action_timer_start() - - # Try to stop the timer2 with usr2 (Unlink) - timer2.with_user(self.usr2).action_timer_stop() diff --git a/addons/timer/__init__.py b/addons/timer/__init__.py deleted file mode 100644 index dc5e6b693d1..00000000000 --- a/addons/timer/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import models diff --git a/addons/timer/__manifest__.py b/addons/timer/__manifest__.py deleted file mode 100644 index ee8832dfe1b..00000000000 --- a/addons/timer/__manifest__.py +++ /dev/null @@ -1,27 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - - -{ - 'name': 'Timer', - 'version': '1.0', - 'sequence': 24, - 'summary': 'Record time', - 'category': 'Services/Timesheets', - 'description': """ -This module implements a timer. -========================================== - -It adds a timer to a view for time recording purpose - """, - 'depends': ['web', 'mail'], - 'data': [ - 'security/timer_security.xml', - 'security/ir.model.access.csv', - 'views/assets.xml', - ], - 'demo': [], - 'installable': True, - 'application': False, - 'auto_install': False, -} diff --git a/addons/timer/i18n/timer.pot b/addons/timer/i18n/timer.pot deleted file mode 100644 index e9ffea53b48..00000000000 --- a/addons/timer/i18n/timer.pot +++ /dev/null @@ -1,127 +0,0 @@ -# Translation of Odoo Server. -# This file contains the translation of the following modules: -# * timer -# -msgid "" -msgstr "" -"Project-Id-Version: Odoo Server saas~13.2\n" -"Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2020-02-13 07:24+0000\n" -"PO-Revision-Date: 2020-02-13 07:24+0000\n" -"Last-Translator: \n" -"Language-Team: \n" -"MIME-Version: 1.0\n" -"Content-Type: text/plain; charset=UTF-8\n" -"Content-Transfer-Encoding: \n" -"Plural-Forms: \n" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__create_uid -msgid "Created by" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__create_date -msgid "Created on" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__display_name -#: model:ir.model.fields,field_description:timer.field_timer_timer__display_name -msgid "Display Name" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__display_timer_pause -msgid "Display Timer Pause" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__display_timer_resume -msgid "Display Timer Resume" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__display_timer_start_primary -msgid "Display Timer Start Primary" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__display_timer_stop -msgid "Display Timer Stop" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__id -#: model:ir.model.fields,field_description:timer.field_timer_timer__id -msgid "ID" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__is_timer_running -#: model:ir.model.fields,field_description:timer.field_timer_timer__is_timer_running -msgid "Is Timer Running" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin____last_update -#: model:ir.model.fields,field_description:timer.field_timer_timer____last_update -msgid "Last Modified on" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__write_uid -msgid "Last Updated by" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__write_date -msgid "Last Updated on" -msgstr "" - -#. module: timer -#: model:ir.model.constraint,message:timer.constraint_timer_timer_unique_timer -msgid "Only one timer occurrence by model, record and user" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__res_id -msgid "Res" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__res_model -msgid "Res Model" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__timer_pause -#: model:ir.model.fields,field_description:timer.field_timer_timer__timer_pause -msgid "Timer Last Pause" -msgstr "" - -#. module: timer -#: model:ir.model,name:timer.model_timer_mixin -msgid "Timer Mixin" -msgstr "" - -#. module: timer -#: model:ir.model,name:timer.model_timer_timer -msgid "Timer Module" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__timer_start -#: model:ir.model.fields,field_description:timer.field_timer_timer__timer_start -msgid "Timer Start" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_timer__user_id -msgid "User" -msgstr "" - -#. module: timer -#: model:ir.model.fields,field_description:timer.field_timer_mixin__user_timer_id -msgid "User Timer" -msgstr "" diff --git a/addons/timer/models/__init__.py b/addons/timer/models/__init__.py deleted file mode 100644 index 66c7f179a40..00000000000 --- a/addons/timer/models/__init__.py +++ /dev/null @@ -1,5 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import timer -from . import timer_mixin diff --git a/addons/timer/models/timer.py b/addons/timer/models/timer.py deleted file mode 100644 index 0da18794cd2..00000000000 --- a/addons/timer/models/timer.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import models, fields, api -from odoo.exceptions import ValidationError - -class TimerTimer(models.Model): - _name = 'timer.timer' - _description = 'Timer Module' - - timer_start = fields.Datetime("Timer Start") - timer_pause = fields.Datetime("Timer Last Pause") - is_timer_running = fields.Boolean(compute="_compute_is_timer_running") - res_model = fields.Char(required=True) - res_id = fields.Integer(required=True) - user_id = fields.Many2one('res.users') - - _sql_constraints = [( - 'unique_timer', 'UNIQUE(res_model, res_id, user_id)', - 'Only one timer occurrence by model, record and user')] - - @api.depends('timer_start', 'timer_pause') - def _compute_is_timer_running(self): - for record in self: - record.is_timer_running = record.timer_start and not record.timer_pause - - @api.model - def create(self, vals): - # Reset the user_timer_id to force the recomputation - self.env[vals['res_model']].invalidate_cache(fnames=['user_timer_id'], ids=[vals['res_id']]) - return super().create(vals) - - def action_timer_start(self): - if not self.timer_start: - self.write({'timer_start': fields.Datetime.now()}) - - def action_timer_stop(self): - """ Stop the timer and return the spent minutes since it started - :return minutes_spent if the timer is started, - otherwise return False - """ - if not self.timer_start: - return False - minutes_spent = self._get_minutes_spent() - self.write({'timer_start': False, 'timer_pause': False}) - return minutes_spent - - def _get_minutes_spent(self): - start_time = self.timer_start - stop_time = fields.Datetime.now() - # timer was either running or paused - if self.timer_pause: - start_time += (stop_time - self.timer_pause) - return (stop_time - start_time).total_seconds() / 60 - - def action_timer_pause(self): - self.write({'timer_pause': fields.Datetime.now()}) - - def action_timer_resume(self): - new_start = self.timer_start + (fields.Datetime.now() - self.timer_pause) - self.write({'timer_start': new_start, 'timer_pause': False}) - - @api.model - def get_server_time(self): - """ Returns the server time. - The timer widget needs the server time instead of the client time - to avoid time desynchronization issues like the timer beginning at 0:00 - and not 23:59 and so on. - """ - return fields.Datetime.now() diff --git a/addons/timer/security/ir.model.access.csv b/addons/timer/security/ir.model.access.csv deleted file mode 100644 index 852a5f208df..00000000000 --- a/addons/timer/security/ir.model.access.csv +++ /dev/null @@ -1,2 +0,0 @@ -id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_timer_user,timer.timer.user,timer.model_timer_timer,base.group_user,1,1,1,1 diff --git a/addons/timer/security/timer_security.xml b/addons/timer/security/timer_security.xml deleted file mode 100644 index 8bad09e2641..00000000000 --- a/addons/timer/security/timer_security.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - timer.user.rule.create-write-delete - - [ - ('user_id', '=', user.id) - ] - - - - - - - - - timer.not.user.rule.create-write-delete - - [ - ('user_id', '!=', user.id) - ] - - - - - - - - diff --git a/addons/timer/static/src/js/timer.js b/addons/timer/static/src/js/timer.js deleted file mode 100644 index 0042ebfaead..00000000000 --- a/addons/timer/static/src/js/timer.js +++ /dev/null @@ -1,63 +0,0 @@ -odoo.define('timer.timer', function (require) { -"use strict"; - -var fieldRegistry = require('web.field_registry'); -var AbstractField = require('web.AbstractField'); -var Timer = require('timer.Timer'); - -var TimerFieldWidget = AbstractField.extend({ - - /** - * @override - * @private - */ - isSet: function () { - return true; - }, - /** - * @override - * @private - */ - _render: function () { - this._super.apply(this, arguments); - this._startTimeCounter(); - }, - /** - * @override - */ - destroy: function () { - this._super.apply(this, arguments); - clearInterval(this.timer); - }, - /** - * @private - */ - _startTimeCounter: async function () { - if (this.record.data.timer_start) { - const serverTime = this.record.data.timer_pause || await this._getServerTime(); - this.time = Timer.createTimer(0, this.record.data.timer_start, serverTime); - this.$el.text(this.time.toString()); - this.timer = setInterval(() => { - if (this.record.data.timer_pause) { - clearInterval(this.timer); - } else { - this.time.addSecond(); - this.$el.text(this.time.toString()); - } - }, 1000); - } else if (!this.record.data.timer_pause){ - clearInterval(this.timer); - } - }, - _getServerTime: function () { - return this._rpc({ - model: 'timer.timer', - method: 'get_server_time', - args: [] - }); - } -}); - -fieldRegistry.add('timer_timer', TimerFieldWidget); - -}); diff --git a/addons/timer/static/src/js/timer_mixin.js b/addons/timer/static/src/js/timer_mixin.js deleted file mode 100644 index 779d06dbe4a..00000000000 --- a/addons/timer/static/src/js/timer_mixin.js +++ /dev/null @@ -1,115 +0,0 @@ -odoo.define('timer.Timer', function (require) { -"use strict"; - - /** - * This class improves the timer display using the - * unit_amount field on account.analytic.line. - */ - class Timer { - constructor(hours, minutes, seconds) { - this.hours = hours; - this.minutes = minutes; - this.seconds = seconds; - } - - /** - * Convert float to time - * @param {number} float - */ - static convertFloatToTime(float) { - if (float === 0) { - return new Timer(0, 0, 0); - } - - let minutes = float % 1; - const hours = float - minutes; - minutes *= 60; - - return new Timer(hours, Math.round(minutes), 0); - } - - /** - * Create timer - * @param {number} unit_amount - * @param {String} timer_start - * @param {String} serverTime - */ - static createTimer(unit_amount, timer_start, serverTime) { - const timer = this.convertFloatToTime(unit_amount); - - timer.addTime( - moment.utc( - moment.utc(serverTime) - .diff(moment.utc(timer_start)) - ).format("HH:mm:ss") - ); - - return timer; - } - - addHours(hours) { - this.hours += hours; - } - - addMinutes(minutes) { - minutes += this.minutes; - - this.minutes = minutes % 60; - this.addHours(Math.floor(minutes / 60)); - } - - addSeconds(seconds) { - seconds += this.seconds; - this.seconds = seconds % 60; - this.addMinutes(Math.floor(seconds / 60)); - } - - display2digits(number) { - return number > 9 ? "" + number : "0" + number; - } - - addSecond() { - this.seconds += 1; - if (this.seconds === 60) { - this.minutes += 1; - this.seconds = 0; - - if (this.minutes === 60) { - this.hours += 1; - this.minutes = 0; - } - } - } - - addTime(time) { - if (typeof time == 'string' && time.indexOf(':') !== -1) { - let [hour, minute, second] = time.split(':'); - - hour = parseInt(hour); - minute = parseInt(minute); - second = parseInt(second); - - this.addSeconds(second); - this.addMinutes(minute); - this.addHours(hour); - } - } - - convertToFloat() { - return (this.hours * 60 + this.minutes) * 60 / 3600; - } - - toString() { - const time = { - hours: this.display2digits(this.hours), - minutes: this.display2digits(this.minutes), - seconds: this.display2digits(this.seconds) - }; - - return `${time.hours}:${time.minutes}:${time.seconds}`; - } - } - - return Timer; - -}); diff --git a/addons/timer/static/src/js/timer_toggle_button.js b/addons/timer/static/src/js/timer_toggle_button.js deleted file mode 100644 index 78274e05e0c..00000000000 --- a/addons/timer/static/src/js/timer_toggle_button.js +++ /dev/null @@ -1,80 +0,0 @@ -odoo.define('timer.timer_toggle_button', function (require) { -"use strict"; - -const fieldRegistry = require('web.field_registry'); -const { FieldToggleBoolean } = require("web.basic_fields"); -const { _lt } = require('web.core'); - -/** - * The TimerToggleButton is used to display correctly the button - * to start or stop a timer for a timesheet in kanban, list and grid - * views. - */ -const TimerToggleButton = FieldToggleBoolean.extend({ - /** - * @override - * @private - */ - _render: function () { - // When the is_timer_running field is false, then the button is used to start the timer - const title = this.value ? _lt('Stop') : _lt('Play'); - const name = this.value ? 'action_timer_stop' : 'action_timer_start'; - const label = this.value ? _lt('Stop') : _lt('Start'); - - this.$('i') - .addClass('fa') - .toggleClass('fa-stop-circle o-timer-stop-button', this.value) - .toggleClass('fa-play-circle o-timer-play-button', !this.value) - .attr('title', title); - - this.$el.addClass('o-timer-button'); - this.$el.attr('title', title); - this.$el.attr('name', name); - this.$el.attr('aria-label', label); - this.$el.attr('aria-pressed', this.value); - this.$el.attr('type', 'button'); - this.$el.attr('role', 'button'); - }, - /** - * Toggle the button - * - * When the user click on this button, - * - the action "action_timer_start" is called - * into the account.analytic.line model, - * if the value of is_timer_running field is set on false. - * - the action "action_timer_stop" is called - * into the account.analytic.line model, - * if the value of is_timer_running field is set on true. - * Then we change the value of the is_timer_running. - * @override - * @private - * @param {MouseEvent} event - */ - _onToggleButton: async function (event) { - const context = this.record.getContext(); - const prevent_deletion = this.attrs.options && this.attrs.options.prevent_deletion || false; - event.stopPropagation(); - const result = await this._rpc({ - model: this.model, - method: this._getActionButton(), - context: $.extend({}, context, {prevent_deletion: prevent_deletion}), - args: [this.res_id] - }); - - this.trigger_up('timer_changed', { - id: this.res_id, - is_timer_running: !this.value - }); - - this._setValue(!this.value); - }, - _getActionButton: function () { - return this.value ? 'action_timer_stop' : 'action_timer_start'; - } -}); - -fieldRegistry.add('timer_toggle_button', TimerToggleButton); - -return TimerToggleButton; - -}); diff --git a/addons/timer/static/src/scss/timer_button.scss b/addons/timer/static/src/scss/timer_button.scss deleted file mode 100644 index 8b269fa8b20..00000000000 --- a/addons/timer/static/src/scss/timer_button.scss +++ /dev/null @@ -1,26 +0,0 @@ -.o-timer-button { - &:focus { - border: none !important; - outline: 0 !important; - } - - & > i { - transition: 0.3s; - - &.o-timer-play-button { - color: $link-color !important; - } - - &.o-timer-stop-button { - color: $red !important; - } - } - - &:hover > i.o-timer-play-button { - color: $link-hover-color !important; - } - - &:hover > i.o-timer-stop-button { - color: darken($red, 15%) !important; - } -} diff --git a/addons/timer/views/assets.xml b/addons/timer/views/assets.xml deleted file mode 100644 index a8565a53515..00000000000 --- a/addons/timer/views/assets.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - -