summaryrefslogtreecommitdiff
path: root/addons
diff options
context:
space:
mode:
authorjbm-odoo <jbm@odoo.com>2020-06-05 17:13:53 +0200
committerjbm-odoo <jbm@odoo.com>2020-06-15 15:51:20 +0200
commitd6a23144d5c5b85c2a02097bcbb3e006a9c2c52a (patch)
tree2eb930372884160d675497ce415279374d21b48a /addons
parente7dda3fab002f5db930b4af340787177d597d59e (diff)
[MOV] timer: Move to Enterprise
taskId: 2217930
Diffstat (limited to 'addons')
-rw-r--r--addons/hr_timesheet/__init__.py49
-rw-r--r--addons/hr_timesheet/__manifest__.py2
-rw-r--r--addons/hr_timesheet/models/hr_timesheet.py51
-rw-r--r--addons/hr_timesheet/models/project.py91
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_kanban_view.js34
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_list_view.js34
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_uom.js54
-rw-r--r--addons/hr_timesheet/static/src/js/timesheet_uom_timer.js3
-rw-r--r--addons/hr_timesheet/static/src/scss/timer_button.scss26
-rw-r--r--addons/hr_timesheet/tests/test_timesheet.py78
-rw-r--r--addons/hr_timesheet/views/assets.xml4
-rw-r--r--addons/hr_timesheet/views/hr_timesheet_views.xml23
-rw-r--r--addons/hr_timesheet/views/project_views.xml86
-rw-r--r--addons/hr_timesheet/wizard/project_task_create_timesheet.py5
-rw-r--r--addons/sale_timesheet/models/project.py5
-rw-r--r--addons/sale_timesheet/models/sale_order.py2
-rw-r--r--addons/sale_timesheet/views/project_task_views.xml4
-rw-r--r--addons/test_timer/__init__.py4
-rw-r--r--addons/test_timer/__manifest__.py20
-rw-r--r--addons/test_timer/models/__init__.py4
-rw-r--r--addons/test_timer/models/timer_test.py25
-rw-r--r--addons/test_timer/security/ir.model.access.csv3
-rw-r--r--addons/test_timer/tests/__init__.py4
-rw-r--r--addons/test_timer/tests/test_timer.py141
-rw-r--r--addons/timer/__init__.py4
-rw-r--r--addons/timer/__manifest__.py27
-rw-r--r--addons/timer/i18n/timer.pot127
-rw-r--r--addons/timer/models/__init__.py5
-rw-r--r--addons/timer/models/timer.py70
-rw-r--r--addons/timer/security/ir.model.access.csv2
-rw-r--r--addons/timer/security/timer_security.xml30
-rw-r--r--addons/timer/static/src/js/timer.js63
-rw-r--r--addons/timer/static/src/js/timer_mixin.js119
-rw-r--r--addons/timer/static/src/js/timer_toggle_button.js3
-rw-r--r--addons/timer/static/src/scss/timer_button.scss26
-rw-r--r--addons/timer/views/assets.xml11
36 files changed, 40 insertions, 1199 deletions
diff --git a/addons/hr_timesheet/__init__.py b/addons/hr_timesheet/__init__.py
index 67486585c98..6aab6c417a5 100644
--- a/addons/hr_timesheet/__init__.py
+++ b/addons/hr_timesheet/__init__.py
@@ -11,37 +11,28 @@ from odoo import api, fields, SUPERUSER_ID, _
def create_internal_project(cr, registry):
env = api.Environment(cr, SUPERUSER_ID, {})
- companies_with_internal = env['project.project'].read_group([
- ('name', '=', _('Internal')), ('allow_timesheets', '=', True)
- ], ['company_id'], ['company_id'])
- company_ids_with_internal = [c['company_id'][0] for c in companies_with_internal]
- for company in env['res.company'].search([('id', 'not in', company_ids_with_internal)]):
+ 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)
- internal_project = company.env['project.project'].create({
+ project_vals += [{
'name': _('Internal'),
'allow_timesheets': True,
'company_id': company.id,
- })
- admin = env.ref('base.user_admin', raise_if_not_found=False)
- if not admin:
- continue
- tasks = company.env['project.task'].create([{
- 'name': _('Training'),
- 'project_id': internal_project.id,
- 'company_id': company.id,
- }, {
- 'name': _('Meeting'),
- 'project_id': internal_project.id,
- '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)
- if not admin.employee_ids.filtered(lambda e: e.company_id == company):
- continue
- company.env['account.analytic.line'].create([{
- 'name': _("Analysis"),
- 'user_id': admin.id,
- 'date': fields.datetime.today(),
- 'unit_amount': 0,
- 'project_id': internal_project.id,
- 'task_id': task.id,
- } for task in tasks])
+ 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 e529653a5e6..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',
diff --git a/addons/hr_timesheet/models/hr_timesheet.py b/addons/hr_timesheet/models/hr_timesheet.py
index 820d7a694ce..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,48 +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()
-
- def unlink(self):
- res = super(AccountAnalyticLine, self).unlink()
- self.env['timer.timer'].search([
- ('res_model', '=', self._name),
- ('res_id', 'in', self.ids)
- ]).unlink()
- return res
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/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 @@
<odoo>
<template id="assets_backend" name="timesheet assets" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
- <link type="text/scss" href="/hr_timesheet/static/src/scss/timer_button.scss" rel="stylesheet"/>
<script type="text/javascript" src="/hr_timesheet/static/src/js/task_with_hours.js"></script>
<script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_uom.js"/>
- <script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_uom_timer.js"/>
- <script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_list_view.js"/>
- <script type="text/javascript" src="/hr_timesheet/static/src/js/timesheet_kanban_view.js"/>
</xpath>
</template>
</odoo>
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 @@
<field name="name">account.analytic.line.tree.hr_timesheet</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
- <tree editable="top" string="Timesheet Activities" js_class="timesheet_tree">
+ <tree editable="top" string="Timesheet Activities">
<field name="date"/>
- <field name="project_id" required="1" context="{'form_view_ref': 'project.project_project_view_form_simplified',}"/>
- <field name="task_id" optional="show" widget="task_with_hours" context="{'default_project_id': project_id}" domain="[('project_id', '=', project_id)]"/>
+ <field name="project_id" required="1" options="{'no_create_edit': True}" context="{'form_view_ref': 'project.project_project_view_form_simplified',}"/>
+ <field name="task_id" optional="show" options="{'no_create_edit': True}" widget="task_with_hours" context="{'default_project_id': project_id}" domain="[('project_id', '=', project_id)]"/>
<field name="name" optional="show" required="0"/>
- <field name="is_timer_running" invisible="1"/>
- <field name="unit_amount" optional="show" widget="timesheet_uom_timer" sum="Total" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}" decoration-danger="unit_amount &gt; 24"/>
+ <field name="unit_amount" optional="show" widget="timesheet_uom" sum="Total" decoration-danger="unit_amount &gt; 24"/>
<field name="company_id" invisible="1"/>
- <field name="timer_start" invisible="1"/>
- <field name="display_timer" invisible="1"/>
<field name="user_id" invisible="1"/>
</tree>
</field>
@@ -93,9 +90,8 @@
</group>
<group>
<field name="amount"/>
- <field name="unit_amount" widget="timesheet_uom" attrs="{'readonly': [('id', '!=', False), ('timer_start', '!=', False)]}" decoration-danger="unit_amount &gt; 24"/>
+ <field name="unit_amount" widget="timesheet_uom" decoration-danger="unit_amount &gt; 24"/>
<field name="currency_id" invisible="1"/>
- <field name="timer_start" invisible="1"/>
</group>
</group>
</sheet>
@@ -160,15 +156,13 @@
<field name="name">account.analytic.line.kanban</field>
<field name="model">account.analytic.line</field>
<field name="arch" type="xml">
- <kanban class="o_kanban_mobile" js_class="timesheet_kanban_view">
+ <kanban class="o_kanban_mobile">
<field name="date"/>
<field name="user_id"/>
<field name="name"/>
<field name="project_id"/>
<field name="task_id" context="{'default_project_id': project_id}" domain="[('project_id', '=', project_id)]"/>
<field name="unit_amount" widget="timesheet_uom"/>
- <field name="timer_start" invisible="1"/>
- <field name="display_timer" invisible="1"/>
<templates>
<t t-name="kanban-box">
<div t-attf-class="oe_kanban_global_click">
@@ -184,9 +178,7 @@
<span>
<t t-esc="record.name.value"/>
</span>
- <div class="float-right">
- <field name="is_timer_running" widget="timer_toggle_button" options="{'prevent_deletion': True}" attrs="{ 'invisible': [('display_timer', '=', False)] }" />
- </div>
+ <div class="float-right" id="bottom_right"/>
</div>
</div>
</div>
@@ -214,7 +206,6 @@
<field name="view_mode">tree,form</field>
<field name="domain">[('project_id', '!=', False), ('user_id', '=', uid)]</field>
<field name="context">{
- "my_timesheet_display_timer": True,
"search_default_week":1,
}</field>
<field name="search_view_id" ref="hr_timesheet_line_my_timesheet_search"/>
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 @@
</field>
</record>
- <record id="project_view_form_inherit" model="ir.ui.view">
- <field name="name">project.view.form.inherit</field>
- <field name="model">project.project</field>
- <field name="inherit_id" ref="hr_timesheet.project_invoice_form"/>
- <field name="arch" type="xml">
- <xpath expr="//div[@id='timesheet_settings']" position="after">
- <div class="col-lg-6 o_setting_box" attrs="{'invisible': [('allow_timesheets', '=', False)]}" id="timer_setting">
- <div class="o_setting_left_pane">
- <field name="allow_timesheet_timer"/>
- </div>
- <div class="o_setting_right_pane">
- <label for="allow_timesheet_timer"/>
- <div class="text-muted">
- Track your time using a timer
- </div>
- </div>
- </div>
- </xpath>
- </field>
- </record>
-
- <record id="project_project_view_form_simplified_inherit" model="ir.ui.view">
- <field name="name">project.project.view.form.simplified.inherit</field>
- <field name="model">project.project</field>
- <field name="inherit_id" ref="project.project_project_view_form_simplified"/>
- <field name="priority">32</field>
- <field name="arch" type="xml">
- <xpath expr="//field[@name='allow_timesheets']" position="after">
- <field name="allow_timesheet_timer" invisible="1"/>
- </xpath>
- </field>
- </record>
-
<record model="ir.ui.view" id="view_task_form2_inherited">
<field name="name">project.task.form.inherited</field>
<field name="model">project.task</field>
@@ -263,40 +230,6 @@
</field>
</record>
- <record id="project_task_view_form" model="ir.ui.view">
- <field name="name">project.task.view.form.inherit.sale.timesheet.enterprise</field>
- <field name="model">project.task</field>
- <field name="inherit_id" ref="project.view_task_form2"/>
- <field name="arch" type="xml">
- <xpath expr="//header" position='inside'>
- <field name="company_id" invisible="1"/>
- <field name="display_timesheet_timer" invisible="1"/>
- <field name="timer_pause" invisible="1" />
- <field name="display_timer_start_primary" invisible="1"/>
- <field name="display_timer_start_secondary" invisible="1"/>
- <field name="display_timer_stop" invisible="1"/>
- <field name="display_timer_pause" invisible="1"/>
- <field name="display_timer_resume" invisible="1"/>
- <button class="btn-primary" name="action_timer_start" type="object" string="Start"
- attrs="{'invisible': [('display_timer_start_primary', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
- <button class="btn-secondary" name="action_timer_start" type="object" string="Start"
- attrs="{'invisible': [('display_timer_start_secondary', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
- <button class="btn-primary btn-danger o_fsm_stop" name="action_timer_stop" type="object" string="Stop"
- attrs="{'invisible': [('display_timer_stop', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
- <button class="btn-primary" name="action_timer_pause" type="object" string="Pause"
- attrs="{'invisible': [('display_timer_pause', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
- <button class="btn-primary btn-info" name="action_timer_resume" type="object" string="Resume"
- attrs="{'invisible': [('display_timer_resume', '=', False)]}" groups="hr_timesheet.group_hr_timesheet_user"/>
- </xpath>
- <xpath expr="//field[@name='stage_id']" position="attributes">
- <attribute name="class">ml-2</attribute>
- </xpath>
- <xpath expr="//field[@name='stage_id']" position="before">
- <field name="timer_start" widget="timer_timer" class="text-danger ml-auto h2 ml-4 font-weight-bold" groups="hr_timesheet.group_hr_timesheet_user"/>
- </xpath>
- </field>
- </record>
-
<record id="project_task_view_search" model="ir.ui.view">
<field name="name">project.task.view.search.inherit.sale.timesheet.enterprise</field>
<field name="model">project.task</field>
@@ -308,24 +241,5 @@
</field>
</record>
- <!-- Improve the task kanban in Project -->
- <record id="project_task_view_kanban" model="ir.ui.view">
- <field name="name">project.task.kanban.timer</field>
- <field name="model">project.task</field>
- <field name="inherit_id" ref="project.view_task_kanban"/>
- <field name="arch" type="xml">
- <xpath expr="//div[@name='date_deadline']" position="before">
- <field name="display_timesheet_timer" invisible="1"/>
- <field name="timer_start" invisible="1"/>
- <field name="timer_pause" invisible="1"/>
- <t t-if="record.display_timesheet_timer &amp;&amp; record.timer_pause.raw_value">
- <i class="fa fa-pause text-warning" title="Timer is Paused"></i>
- </t>
- <t t-elif="record.display_timesheet_timer &amp;&amp; record.timer_start.raw_value">
- <i class="fa fa-play text-success" title="Timer is Running"></i>
- </t>
- </xpath>
- </field>
- </record>
</data>
</odoo>
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/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 @@
<record id="project_project_view_form_simplified_inherit" model="ir.ui.view">
<field name="name">project.project.view.form.simplified.inherit</field>
<field name="model">project.project</field>
- <field name="inherit_id" ref="hr_timesheet.project_project_view_form_simplified_inherit"/>
+ <field name="inherit_id" ref="hr_timesheet.project_project_view_form_simplified_inherit_timesheet"/>
<field name="arch" type="xml">
- <field name="allow_timesheet_timer" position="after">
+ <field name="allow_timesheets" position="after">
<field name="billable_type" invisible="1"/>
<field name="company_id" invisible="1"/>
<field name="allow_billable" attrs="{'invisible': [('billable_type', '=', 'employee_rate')]}"/>
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 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<odoo noupdate="1">
-
- <record id="timer_user_rule_create-write-delete" model="ir.rule">
- <field name="name">timer.user.rule.create-write-delete</field>
- <field name="model_id" ref="timer.model_timer_timer"/>
- <field name="domain_force">[
- ('user_id', '=', user.id)
- ]</field>
- <field name="groups" eval="[(4, ref('base.group_user'))]"/>
- <field name="perm_create" eval="1"/>
- <field name="perm_write" eval="1"/>
- <field name="perm_unlink" eval="1"/>
- <field name="perm_read" eval="1"/>
- </record>
-
- <record id="timer_not_user_rule_create-write-delete" model="ir.rule">
- <field name="name">timer.not.user.rule.create-write-delete</field>
- <field name="model_id" ref="timer.model_timer_timer"/>
- <field name="domain_force">[
- ('user_id', '!=', user.id)
- ]</field>
- <field name="groups" eval="[(4, ref('base.group_user'))]"/>
- <field name="perm_create" eval="0"/>
- <field name="perm_write" eval="0"/>
- <field name="perm_unlink" eval="0"/>
- <field name="perm_read" eval="1"/>
- </record>
-
-</odoo>
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 7a70e81341e..00000000000
--- a/addons/timer/static/src/js/timer_mixin.js
+++ /dev/null
@@ -1,119 +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(
- Math.abs(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 = (second === undefined) ? 0 : parseInt(second);
-
- this.addSeconds(second);
- this.addMinutes(minute);
- this.addHours(hour);
- }
- }
-
- convertToFloat() {
- return (this.hours * 60 + this.minutes) * 60 / 3600;
- }
-
- convertToSeconds() {
- return (this.hours * 60 + this.minutes) * 60 + this.seconds;
- }
-
- 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
index 78274e05e0c..d189d996502 100644
--- a/addons/timer/static/src/js/timer_toggle_button.js
+++ b/addons/timer/static/src/js/timer_toggle_button.js
@@ -52,12 +52,11 @@ const TimerToggleButton = FieldToggleBoolean.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/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 @@
-<?xml version="1.0"?>
-<odoo>
- <template id="assets_backend" name="timer assets" inherit_id="web.assets_backend">
- <xpath expr="." position="inside">
- <link type="text/scss" href="/timer/static/src/scss/timer_button.scss" rel="stylesheet"/>
- <script type="text/javascript" src="/timer/static/src/js/timer_mixin.js"/>
- <script type="text/javascript" src="/timer/static/src/js/timer.js"/>
- <script type="text/javascript" src="/timer/static/src/js/timer_toggle_button.js"/>
- </xpath>
- </template>
-</odoo>