14 changed files with 377 additions and 0 deletions
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
import models |
@ -0,0 +1,43 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
{ |
||||
|
'name': 'Project Task Timer', |
||||
|
'version': '10.0.1.0', |
||||
|
'summary': """Task Timer With Start & Stop""", |
||||
|
'description': """"This module helps you to track time sheet in project automatically.""", |
||||
|
'category': 'Project', |
||||
|
'author': 'Cybrosys Techno Solutions', |
||||
|
'company': 'Cybrosys Techno Solutions', |
||||
|
'website': "http://www.cybrosys.com", |
||||
|
'depends': ['base', 'project', 'hr_timesheet'], |
||||
|
'data': [ |
||||
|
'views/project_task_timer_view.xml', |
||||
|
'views/project_timer_static.xml', |
||||
|
], |
||||
|
'images': ['static/description/banner.jpg'], |
||||
|
'license': 'AGPL-3', |
||||
|
'demo': [], |
||||
|
'installable': True, |
||||
|
'auto_install': False, |
||||
|
'application': False, |
||||
|
} |
@ -0,0 +1,23 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
import project_task_timer |
@ -0,0 +1,91 @@ |
|||||
|
# -*- coding: utf-8 -*- |
||||
|
############################################################################## |
||||
|
# |
||||
|
# Cybrosys Technologies Pvt. Ltd. |
||||
|
# Copyright (C) 2017-TODAY Cybrosys Technologies(<http://www.cybrosys.com>). |
||||
|
# Author: Jesni Banu(<http://www.cybrosys.com>) |
||||
|
# you can modify it under the terms of the GNU LESSER |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3), Version 3. |
||||
|
# |
||||
|
# It is forbidden to publish, distribute, sublicense, or sell copies |
||||
|
# of the Software or modified copies of the Software. |
||||
|
# |
||||
|
# 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 |
||||
|
# GENERAL PUBLIC LICENSE (LGPL v3) along with this program. |
||||
|
# If not, see <http://www.gnu.org/licenses/>. |
||||
|
# |
||||
|
############################################################################## |
||||
|
from datetime import datetime |
||||
|
from odoo import models, fields, api |
||||
|
|
||||
|
|
||||
|
class ProjectTaskTimeSheet(models.Model): |
||||
|
_inherit = 'account.analytic.line' |
||||
|
|
||||
|
date_start = fields.Datetime(string='Start Date') |
||||
|
date_end = fields.Datetime(string='End Date', readonly=1) |
||||
|
|
||||
|
|
||||
|
class ProjectTaskTimer(models.Model): |
||||
|
_inherit = 'project.task' |
||||
|
|
||||
|
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.multi |
||||
|
@api.depends('timesheet_ids.unit_amount') |
||||
|
def _compute_duration(self): |
||||
|
self.duration = 0 |
||||
|
for each in self: |
||||
|
each.duration = sum(each.timesheet_ids.mapped('unit_amount')) |
||||
|
|
||||
|
@api.multi |
||||
|
def toggle_start(self): |
||||
|
for record in self: |
||||
|
record.task_timer = not record.task_timer |
||||
|
if self.task_timer: |
||||
|
self.write({'is_user_working': True}) |
||||
|
time_line = self.env['account.analytic.line'] |
||||
|
for time_sheet in self: |
||||
|
time_line.create({ |
||||
|
'name': 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: |
||||
|
self.write({'is_user_working': False}) |
||||
|
time_line_obj = self.env['account.analytic.line'] |
||||
|
domain = [('task_id', 'in', self.ids), ('date_end', '=', False)] |
||||
|
for time_line in time_line_obj.search(domain): |
||||
|
time_line.write({'date_end': 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) |
||||
|
time_line.unit_amount = round(diff.total_seconds() / 60.0, 2) |
||||
|
else: |
||||
|
time_line.unit_amount = 0.0 |
||||
|
|
||||
|
task_timer = fields.Boolean() |
||||
|
is_user_working = fields.Boolean( |
||||
|
'Is Current User Working', compute='_compute_is_user_working', |
||||
|
help="Technical field indicating whether the current user is working. ") |
||||
|
duration = fields.Float( |
||||
|
'Real Duration', compute='_compute_duration', |
||||
|
readonly=True, store=True) |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
|
After Width: | Height: | Size: 108 KiB |
After Width: | Height: | Size: 50 KiB |
After Width: | Height: | Size: 34 KiB |
@ -0,0 +1,87 @@ |
|||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h2 class="oe_slogan">Project Task Timer</h2> |
||||
|
<h3 class="oe_slogan">Task Timer with Start & Stop</h3> |
||||
|
<h4 class="oe_slogan" style="font-size: 23px;">Author : Cybrosys Techno Solutions , www.cybrosys.com</h4> |
||||
|
<div> |
||||
|
<h4><p>Features:</p></h4> |
||||
|
<ul> |
||||
|
<li style="list-style:none !important;"><span style="color:green;"> ☑</span> Timer in Task.</li> |
||||
|
<li style="list-style:none !important;"><span style="color:green;"> ☑</span> Automatic Time sheet Calculation.</li> |
||||
|
</ul> |
||||
|
</div> |
||||
|
<p class="oe_mt32"> |
||||
|
You have a smart button to start & stop your task. When you start your task it will automatically makes |
||||
|
a time sheet entry for that task with starting time. When you toggle it to pause it's end date will be update |
||||
|
and duration will calculate automatically. |
||||
|
</p> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h3 class="oe_slogan">Timer in Task</h3> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class="oe_mt32"> |
||||
|
Project -> Search -> Task <br> |
||||
|
Here you can a toggle button to start and stop your task. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span12"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img style="border:10px solid white;" class="oe_picture oe_screenshot" src="timer1.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
<div class="oe_span12"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img style="border:10px solid white;" class="oe_picture oe_screenshot" src="timer2.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container"> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<h3 class="oe_slogan">Automatic Time sheet Calculation</h3> |
||||
|
<div class="oe_row oe_spaced"> |
||||
|
<div class="oe_span12"> |
||||
|
<p class="oe_mt32"> |
||||
|
Here you can see the Automatic time sheet entries. |
||||
|
</p> |
||||
|
</div> |
||||
|
<div class="oe_span12"> |
||||
|
<div class="oe_row_img oe_centered"> |
||||
|
<img style="border:10px solid white;" class="oe_picture oe_screenshot" src="timer3.png"> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
||||
|
|
||||
|
<section class="oe_container oe_dark"> |
||||
|
<h2 class="oe_slogan" style="margin-top:20px;" >Need Any Help?</h2> |
||||
|
<div class="oe_slogan" style="margin-top:10px !important;"> |
||||
|
<div> |
||||
|
<a class="btn btn-primary btn-lg mt8" |
||||
|
style="color: #FFFFFF !important;border-radius: 0;" href="https://www.cybrosys.com"><i |
||||
|
class="fa fa-envelope"></i> Email </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/contact/"><i |
||||
|
class="fa fa-phone"></i> Contact Us </a> <a |
||||
|
class="btn btn-primary btn-lg mt8" style="color: #FFFFFF !important;border-radius: 0;" |
||||
|
href="https://www.cybrosys.com/odoo-customization-and-installation/"><i |
||||
|
class="fa fa-check-square"></i> Request Customization </a> |
||||
|
</div> |
||||
|
<br> |
||||
|
<img src="cybro_logo.png" style="width: 190px; margin-bottom: 20px;" class="center-block"> |
||||
|
<div> |
||||
|
<a href="https://twitter.com/cybrosys" target="_blank"><i class="fa fa-2x fa-twitter" style="color:white;background: #00a0d1;width:35px;"></i></a></td> |
||||
|
<a href="https://www.linkedin.com/company/cybrosys-technologies-pvt-ltd" target="_blank"><i class="fa fa-2x fa-linkedin" style="color:white;background: #31a3d6;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
<a href="https://www.facebook.com/cybrosystechnologies" target="_blank"><i class="fa fa-2x fa-facebook" style="color:white;background: #3b5998;width:35px;padding-left: 8px;"></i></a></td> |
||||
|
<a href="https://plus.google.com/106641282743045431892/about" target="_blank"><i class="fa fa-2x fa-google-plus" style="color:white;background: #c53c2c;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
<a href="https://in.pinterest.com/cybrosys" target="_blank"><i class="fa fa-2x fa-pinterest" style="color:white;background: #ac0f18;width:35px;padding-left: 3px;"></i></a></td> |
||||
|
</div> |
||||
|
</div> |
||||
|
</section> |
After Width: | Height: | Size: 89 KiB |
After Width: | Height: | Size: 93 KiB |
After Width: | Height: | Size: 96 KiB |
@ -0,0 +1,57 @@ |
|||||
|
odoo.define('timer_widget.timer', function (require) { |
||||
|
"use strict"; |
||||
|
|
||||
|
var core = require('web.core'); |
||||
|
var common = require('web.form_common'); |
||||
|
var Model = require('web.Model'); |
||||
|
var time = require('web.time'); |
||||
|
var utils = require('web.utils'); |
||||
|
|
||||
|
var _t = core._t; |
||||
|
|
||||
|
var TimeCounter = common.AbstractField.extend(common.ReinitializeFieldMixin, { |
||||
|
start: function() { |
||||
|
this._super(); |
||||
|
var self = this; |
||||
|
this.field_manager.on("view_content_has_changed", this, function () { |
||||
|
self.render_value(); |
||||
|
}); |
||||
|
}, |
||||
|
start_time_counter: function(){ |
||||
|
var self = this; |
||||
|
clearTimeout(this.timer); |
||||
|
if (this.field_manager.datarecord.is_user_working) { |
||||
|
this.duration += 1000; |
||||
|
this.timer = setTimeout(function() { |
||||
|
self.start_time_counter(); |
||||
|
}, 1000); |
||||
|
} else { |
||||
|
clearTimeout(this.timer); |
||||
|
} |
||||
|
this.$el.html($('<span>' + moment.utc(this.duration).format("HH:mm:ss") + '</span>')); |
||||
|
}, |
||||
|
render_value: function() { |
||||
|
this._super.apply(this, arguments); |
||||
|
var self = this; |
||||
|
this.duration; |
||||
|
var productivity_domain = [['task_id', '=', this.field_manager.datarecord.id], ['user_id', '=', self.session.uid]]; |
||||
|
new Model('account.analytic.line').call('search_read', [productivity_domain, []]).then(function(result) { |
||||
|
if (self.get("effective_readonly")) { |
||||
|
self.$el.removeClass('o_form_field_empty'); |
||||
|
var current_date = new Date(); |
||||
|
self.duration = 0; |
||||
|
_.each(result, function(data) { |
||||
|
self.duration += data.date_end ? self.get_date_difference(data.date_start, data.date_end) : self.get_date_difference(time.auto_str_to_date(data.date_start), current_date); |
||||
|
}); |
||||
|
self.start_time_counter(); |
||||
|
} |
||||
|
}); |
||||
|
}, |
||||
|
get_date_difference: function(date_start, date_deadline) { |
||||
|
var difference = moment(date_deadline).diff(moment(date_start)); |
||||
|
return moment.duration(difference); |
||||
|
}, |
||||
|
}); |
||||
|
|
||||
|
core.form_widget_registry.add('time_counter', TimeCounter); |
||||
|
}); |
@ -0,0 +1,45 @@ |
|||||
|
<?xml version="1.0" encoding="utf-8"?> |
||||
|
<odoo> |
||||
|
<data> |
||||
|
<record id="project_task_timer_inherit_view" model="ir.ui.view"> |
||||
|
<field name="name">project task timer</field> |
||||
|
<field name="model">project.task</field> |
||||
|
<field name="inherit_id" ref="project.view_task_form2"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<div name="button_box" position="inside"> |
||||
|
<button name="toggle_start" type="object" |
||||
|
class="oe_stat_button" icon="fa-clock-o"> |
||||
|
<field name="task_timer" widget="boolean_button" |
||||
|
options='{"terminology": { |
||||
|
"string_true": "Started", |
||||
|
"hover_true": "Pause", |
||||
|
"string_false": "Timer", |
||||
|
"hover_false": "Start" |
||||
|
}}'/> |
||||
|
</button> |
||||
|
</div> |
||||
|
<field name="progress" position="after"> |
||||
|
<field name="is_user_working" invisible="1"/> |
||||
|
<label for="duration"/> |
||||
|
<div> |
||||
|
<button style="pointer-events: none;" class="oe_inline label label-default"> |
||||
|
<field name="duration" widget="time_counter"/> |
||||
|
</button> |
||||
|
</div> |
||||
|
</field> |
||||
|
</field> |
||||
|
</record> |
||||
|
|
||||
|
<record id="project_task_timer_inherit_view1" model="ir.ui.view"> |
||||
|
<field name="name">project task timer1</field> |
||||
|
<field name="model">project.task</field> |
||||
|
<field name="inherit_id" ref="hr_timesheet.view_task_form2_inherited"/> |
||||
|
<field name="arch" type="xml"> |
||||
|
<xpath expr="//field[@name='timesheet_ids']/tree/field[@name='unit_amount']" position="before"> |
||||
|
<field name="date_start" required="1"/> |
||||
|
<field name="date_end"/> |
||||
|
</xpath> |
||||
|
</field> |
||||
|
</record> |
||||
|
</data> |
||||
|
</odoo> |
@ -0,0 +1,8 @@ |
|||||
|
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
<odoo> |
||||
|
<template id="assets_backend1" name="web_widget_color assets1" inherit_id="web.assets_backend"> |
||||
|
<xpath expr="." position="inside"> |
||||
|
<script type="text/javascript" src="/project_task_timer/static/src/js/timer.js"></script> |
||||
|
</xpath> |
||||
|
</template> |
||||
|
</odoo> |
Loading…
Reference in new issue