@ -0,0 +1,51 @@ |
|||
.. image:: https://img.shields.io/badge/license-LGPL--3-blue.svg |
|||
:target: https://www.gnu.org/licenses/lgpl-3.0-standalone.html |
|||
:alt: License: LGPL-3 |
|||
|
|||
Automatic Project Task Timer |
|||
============================ |
|||
App for running the timer automatically for the task comes in In Progress stage. |
|||
|
|||
Configuration |
|||
============= |
|||
* For configuring the timer, Go to Users --> Set the user group as Administrator |
|||
for Projects then, |
|||
|
|||
Projects --> Configuration --> Timer Configuration menu to configure the |
|||
project and stage. |
|||
|
|||
Company |
|||
------- |
|||
* `Cybrosys Techno Solutions <https://cybrosys.com/>`__ |
|||
|
|||
License |
|||
------- |
|||
General Public License, Version 3 (LGPL v3). |
|||
(https://www.gnu.org/licenses/lgpl-3.0-standalone.html) |
|||
|
|||
Credits |
|||
------- |
|||
* Developers: (V18) Mohammed Irfan T, Contact: odoo@cybrosys.com |
|||
* Developers: (V16) Prathyunnan R, Contact: odoo@cybrosys.com |
|||
|
|||
Contacts |
|||
-------- |
|||
* Mail Contact : odoo@cybrosys.com |
|||
* Website : https://cybrosys.com |
|||
|
|||
Bug Tracker |
|||
----------- |
|||
Bugs are tracked on GitHub Issues. In case of trouble, please check there if your issue has already been reported. |
|||
|
|||
Maintainer |
|||
========== |
|||
.. image:: https://cybrosys.com/images/logo.png |
|||
:target: https://cybrosys.com |
|||
|
|||
This module is maintained by Cybrosys Technologies. |
|||
|
|||
For support and more information, please visit `Our Website <https://cybrosys.com/>`__ |
|||
|
|||
Further information |
|||
=================== |
|||
HTML Description: `<static/description/index.html>`__ |
@ -0,0 +1,22 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import models |
@ -0,0 +1,53 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
{ |
|||
'name': 'Automatic Project Task Timer', |
|||
'version': '18.0.1.0.0', |
|||
'category': 'Project', |
|||
'summary': 'Automatic Running Timer for Project Tasks', |
|||
'description': "This module helps you to track time sheet in project " |
|||
"using a real timer, it's starts when task is in " |
|||
"configured stage and stops when its moves to any other " |
|||
"stage and the timesheet will be recorded.", |
|||
'author': 'Cybrosys Techno Solutions', |
|||
'company': 'Cybrosys Techno Solutions', |
|||
'maintainer': 'Cybrosys Techno Solutions', |
|||
'website': 'https://www.cybrosys.com', |
|||
'depends': ['hr_timesheet'], |
|||
'data': [ |
|||
'security/ir.model.access.csv', |
|||
'views/project_task_views.xml', |
|||
'views/res_config_settings_views.xml', |
|||
'views/timer_configuration_views.xml' |
|||
], |
|||
'assets': { |
|||
'web.assets_backend': [ |
|||
'automatic_project_task_timer/static/src/js/task_timer.js', |
|||
'automatic_project_task_timer/static/src/js/form_open.js', |
|||
'automatic_project_task_timer/static/src/xml/task_timer_templates.xml', |
|||
]}, |
|||
'images': ['static/description/banner.jpg'], |
|||
'license': 'LGPL-3', |
|||
'installable': True, |
|||
'auto_install': False, |
|||
'application': False, |
|||
} |
@ -0,0 +1,5 @@ |
|||
## Module <automatic_project_task_timer> |
|||
#### 08.07.2024 |
|||
#### Version 17.0.1.0.0 |
|||
#### ADD |
|||
- Initial commit for Automatic Project Task Timer |
@ -0,0 +1,25 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from . import account_analytic_line |
|||
from . import project_task |
|||
from . import res_config_settings |
|||
from . import timer_configuration |
@ -0,0 +1,37 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class AccountAnalyticLine(models.Model): |
|||
"""inherited the 'account.analytic.line' for showing the time records.""" |
|||
_inherit = 'account.analytic.line' |
|||
|
|||
date_start = fields.Datetime(string='Start Date', help='Shows the ' |
|||
'starting time of ' |
|||
'the timer') |
|||
date_end = fields.Datetime(string='End Date', help='Shows ' |
|||
'the ending' |
|||
' time of ' |
|||
'the timer') |
|||
timer_duration = fields.Float(string='Time Duration(Minutes)', |
|||
help='Shows the real time ') |
@ -0,0 +1,147 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from datetime import datetime |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class Task(models.Model): |
|||
"""inherited the 'project.task' for running the timer """ |
|||
_inherit = 'project.task' |
|||
|
|||
task_timer = fields.Boolean(string='Timer', default=False, |
|||
help='Activated when task configured stage') |
|||
is_user_working = fields.Boolean(string='Is Current User Working', |
|||
compute='_compute_is_user_working', |
|||
help="Technical field indicating whether " |
|||
"the current user is working. ") |
|||
duration = fields.Float(string='Real Duration', store=True, copy=False, |
|||
readonly=False, help='Shows the duration') |
|||
is_status_stage = fields.Boolean(string='Stage Status', |
|||
help='To set the status of task at the ' |
|||
'initial time.') |
|||
check_stage = fields.Integer(string='Stage', |
|||
compute='_compute_check_stage', |
|||
help='To check the stage whether it is ' |
|||
'configured or not') |
|||
|
|||
def _compute_check_stage(self): |
|||
"""To check the stage whether it is configured or not, for that |
|||
assigning the configured stage id to this field.""" |
|||
for rec in self: |
|||
rec.check_stage = self.env['timer.configuration'].search( |
|||
[('project_id.id', '=', rec.project_id.id)]).stage_id.id |
|||
|
|||
@api.constrains('stage_id') |
|||
def _task_timer(self): |
|||
"""Checks the status of timer setting and |
|||
toggle the task timer boolean to active """ |
|||
self.write({ |
|||
'is_status_stage': False |
|||
}) |
|||
timer_setting = self.env['ir.config_parameter'].sudo().get_param( |
|||
'automatic_project_task_timer.timer_setting') |
|||
if timer_setting: |
|||
for rec in self.env['timer.configuration'].search([]): |
|||
if self.project_id == rec.project_id: |
|||
if self.stage_id.id == rec.stage_id.id: |
|||
self.write({'task_timer': True}) |
|||
else: |
|||
self.write({'task_timer': False}) |
|||
|
|||
def _compute_is_user_working(self): |
|||
""" Checks whether the current user is working """ |
|||
for order in self: |
|||
if order.timesheet_ids.filtered( |
|||
lambda x: (x.user_id.id == self.env.user.id) and ( |
|||
not x.date_end)): |
|||
order.is_user_working = True |
|||
else: |
|||
order.is_user_working = False |
|||
|
|||
@api.model |
|||
@api.constrains('task_timer') |
|||
def toggle_start(self): |
|||
"""The time sheet record will be created |
|||
by checking all the conditions """ |
|||
time_line_obj = self.env['account.analytic.line'] |
|||
for rec in self: |
|||
if rec.task_timer is True: |
|||
rec.write({'is_user_working': True}) |
|||
for time_sheet in rec: |
|||
time_line_obj.create({ |
|||
'name': '%s : %s' % (self.env.user.name, |
|||
time_sheet.name), |
|||
'task_id': time_sheet.id, |
|||
'user_id': self.env.user.id, |
|||
'project_id': time_sheet.project_id.id, |
|||
'date_start': datetime.now(), |
|||
}) |
|||
else: |
|||
rec.write({'is_user_working': False}) |
|||
for time_line in time_line_obj.search( |
|||
[('task_id', 'in', self.ids), |
|||
('date_end', '=', False)]): |
|||
time_line.write({'date_end': fields.Datetime.now()}) |
|||
if time_line.date_start: |
|||
if time_line.date_end: |
|||
diff = fields.Datetime.from_string( |
|||
time_line.date_end) \ |
|||
- fields.Datetime.from_string( |
|||
time_line.date_start).replace( |
|||
second=0, microsecond=0) |
|||
time_line.timer_duration = \ |
|||
round(diff.total_seconds() / 60.0, 2) |
|||
time_line.unit_amount = \ |
|||
round(diff.total_seconds() / (60.0 * 60.0), 2) |
|||
else: |
|||
time_line.unit_amount = 0.0 |
|||
time_line.timer_duration = 0.0 |
|||
else: |
|||
time_line.write({'date_start': fields.Datetime.now()}) |
|||
if time_line.date_end: |
|||
diff = fields.Datetime.from_string( |
|||
time_line.date_end) \ |
|||
- fields.Datetime.from_string( |
|||
time_line.date_start).replace( |
|||
second=0, microsecond=0) |
|||
time_line.timer_duration = \ |
|||
round(diff.total_seconds() / 60.0, 2) |
|||
time_line.unit_amount = \ |
|||
round(diff.total_seconds() / (60.0 * 60.0), 2) |
|||
else: |
|||
time_line.unit_amount = 0.0 |
|||
time_line.timer_duration = 0.0 |
|||
|
|||
def get_working_duration(self): |
|||
"""Get the additional duration for 'open times' |
|||
i.e. productivity lines with no date_end.""" |
|||
self.ensure_one() |
|||
duration = 0 |
|||
for time in \ |
|||
self.timesheet_ids.filtered(lambda time: not time.date_end): |
|||
if type(time.date_start) != datetime: |
|||
time.date_start = datetime.now() |
|||
duration = 0 |
|||
else: |
|||
duration += \ |
|||
(datetime.now() - time.date_start).total_seconds() / 60 |
|||
return duration |
@ -0,0 +1,33 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import fields, models |
|||
|
|||
|
|||
class ResConfigSettings(models.TransientModel): |
|||
"""inherited the 'res.config.settings' |
|||
for activating timer in project module.""" |
|||
_inherit = 'res.config.settings' |
|||
|
|||
timer_setting = fields.Boolean( |
|||
string='Task Timer', |
|||
config_parameter='automatic_project_task_timer.timer_setting', |
|||
help='Enable to activate the timer') |
@ -0,0 +1,72 @@ |
|||
# -*- coding: utf-8 -*- |
|||
############################################################################# |
|||
# |
|||
# Cybrosys Technologies Pvt. Ltd. |
|||
# |
|||
# Copyright (C) 2025-TODAY Cybrosys Technologies(<https://www.cybrosys.com>) |
|||
# Author: Cybrosys Techno Solutions(<https://www.cybrosys.com>) |
|||
# |
|||
# You can modify it under the terms of the GNU LESSER |
|||
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
|||
# |
|||
# This program is distributed in the hope that it will be useful, |
|||
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|||
# GNU LESSER GENERAL PUBLIC LICENSE (LGPL v3) for more details. |
|||
# |
|||
# You should have received a copy of the GNU LESSER GENERAL PUBLIC LICENSE |
|||
# (LGPL v3) along with this program. |
|||
# If not, see <http://www.gnu.org/licenses/>. |
|||
# |
|||
############################################################################# |
|||
from odoo import api, fields, models |
|||
|
|||
|
|||
class TimerConfiguration(models.Model): |
|||
"""Timer configuration model for configure the automatic timer |
|||
stage to activate the timer, Each project have different stages, |
|||
based on the configured stage for each project the timer will be runs.""" |
|||
_name = 'timer.configuration' |
|||
_description = 'Timer Configuration' |
|||
|
|||
project_id = fields.Many2one('project.project', string='Project', |
|||
help='Configure the project to activate the ' |
|||
'timer') |
|||
stage_ids = fields.Many2many('project.task.type', |
|||
string='Stages', |
|||
help='To set the domain for stages') |
|||
stage_id = fields.Many2one('project.task.type', string='Stage', |
|||
help='Set the stage to activate the timer', |
|||
domain="[('id', 'in', stage_ids)]", |
|||
required=True) |
|||
|
|||
@api.onchange('project_id') |
|||
def _onchange_project_id(self): |
|||
"""To set the domain for stage_id field to select the stages |
|||
from the stages of the selected project""" |
|||
data = [] |
|||
for rec in self.env['project.task.type'].search([]): |
|||
if self.project_id.id in rec.project_ids.ids: |
|||
data.append(rec.id) |
|||
self.stage_ids = data |
|||
|
|||
@api.model |
|||
def create(self, vals): |
|||
"""To set the status stage for all the tasks in the corresponding |
|||
project, so that the warning message will pop-ups when the form |
|||
opens. For that supering the ORM Create method to get the corresponding |
|||
project. """ |
|||
projects = self.env['project.project'].sudo().browse(vals.get( |
|||
'project_id')) |
|||
tasks = self.env['project.task'].search([('project_id', '=' ,projects.id)]) |
|||
for records in tasks: |
|||
if records.stage_id.id == vals.get('stage_id'): |
|||
records.write({ |
|||
'is_status_stage': True |
|||
}) |
|||
else: |
|||
records.write({ |
|||
'is_status_stage': False |
|||
}) |
|||
res = super(TimerConfiguration, self).create(vals) |
|||
return res |
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 210 KiB |
After Width: | Height: | Size: 209 KiB |
After Width: | Height: | Size: 109 KiB |
After Width: | Height: | Size: 495 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 624 B |
After Width: | Height: | Size: 136 KiB |
After Width: | Height: | Size: 214 KiB |
After Width: | Height: | Size: 36 KiB |
After Width: | Height: | Size: 3.6 KiB |
After Width: | Height: | Size: 310 B |
After Width: | Height: | Size: 929 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 3.3 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 17 KiB |
After Width: | Height: | Size: 542 B |
After Width: | Height: | Size: 576 B |
After Width: | Height: | Size: 733 B |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 4.0 KiB |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 738 KiB |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 911 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 600 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 2.0 KiB |
After Width: | Height: | Size: 462 B |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 926 B |
After Width: | Height: | Size: 9.0 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 7.0 KiB |
After Width: | Height: | Size: 878 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 653 B |
After Width: | Height: | Size: 800 B |
After Width: | Height: | Size: 905 B |
After Width: | Height: | Size: 189 KiB |
After Width: | Height: | Size: 4.3 KiB |
After Width: | Height: | Size: 839 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 5.9 KiB |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 34 KiB |
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 23 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 2.3 KiB |
After Width: | Height: | Size: 427 B |
After Width: | Height: | Size: 627 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 988 B |
After Width: | Height: | Size: 3.7 KiB |
After Width: | Height: | Size: 5.0 KiB |
After Width: | Height: | Size: 875 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 912 KiB |
After Width: | Height: | Size: 1.3 MiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 44 KiB |
After Width: | Height: | Size: 46 KiB |
After Width: | Height: | Size: 85 KiB |
After Width: | Height: | Size: 147 KiB |
After Width: | Height: | Size: 86 KiB |
After Width: | Height: | Size: 47 KiB |
After Width: | Height: | Size: 100 KiB |
After Width: | Height: | Size: 126 KiB |
After Width: | Height: | Size: 121 KiB |
After Width: | Height: | Size: 151 KiB |
After Width: | Height: | Size: 162 KiB |
After Width: | Height: | Size: 106 KiB |
After Width: | Height: | Size: 197 KiB |
After Width: | Height: | Size: 24 KiB |
After Width: | Height: | Size: 96 KiB |
After Width: | Height: | Size: 58 KiB |
After Width: | Height: | Size: 31 KiB |
After Width: | Height: | Size: 66 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 82 KiB |
After Width: | Height: | Size: 102 KiB |
After Width: | Height: | Size: 118 KiB |