diff --git a/project_timesheet_detailed/README.rst b/project_timesheet_detailed/README.rst new file mode 100644 index 00000000..e9845237 --- /dev/null +++ b/project_timesheet_detailed/README.rst @@ -0,0 +1,61 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +======================== +Project TimeSheet Detail +======================== + +This module extends the function of project timesheet, allows the manager to +review, runbot, approve or reject the timesheet one by one. + +Installation +============ + +To install this module, you need to: + +#. have basic module installed (project_issue_sheet) + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +Miya Xing +Joseph Wang + +Maintainer +---------- + +.. image:: https://www.elico-corp.com/logo.png + :alt: Elico Corp + :target: https://www.elico-corp.com + +This module is maintained by Elico Corporation. + +Elico Corp is an innovative actor in China, Hong-Kong and Singapore servicing +well known international companies and as well as local mid-sized businesses. +Since 2010, our seasoned Sino-European consultants have been providing full +range Odoo services: + +* Business consultancy for Gap analysis, BPM, operational work-flows review. +* Ready-to-use ERP packages aimed at starting businesses. +* Odoo implementation for manufacturing, international trading, service industry + and e-commerce. +* Connectors and integration with 3rd party software (Magento, Taobao, Coswin, + Joomla, Prestashop, Tradevine etc...). +* Odoo Support services such as developments, training, maintenance and hosting. + +Our headquarters are located in Shanghai with branch in Singapore servicing +customers from all over Asia Pacific. + +Contact information: `Sales `__ diff --git a/project_timesheet_detailed/__init__.py b/project_timesheet_detailed/__init__.py new file mode 100644 index 00000000..cde864ba --- /dev/null +++ b/project_timesheet_detailed/__init__.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- + +from . import models diff --git a/project_timesheet_detailed/__manifest__.py b/project_timesheet_detailed/__manifest__.py new file mode 100644 index 00000000..7a182f99 --- /dev/null +++ b/project_timesheet_detailed/__manifest__.py @@ -0,0 +1,25 @@ +# -*- coding: utf-8 -*- +# © 2017 Elico Corp (www.elico-corp.com). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +{ + 'name': "project_timesheet_detailed", + 'summary': """This module extends the function of project timesheet, + allows the manager to review, approve or reject the timesheet by every + single record.""", + 'author': "Elico Corp", + "support": "support@elico-corp.com", + "license": "AGPL-3", + 'website': "https://www.elico-corp.com", + 'category': 'Project Management', + 'version': '10.0.1.0.0', + 'depends': ['project_issue_sheet'], + 'data': [ + 'security/project_timesheet_detail.xml', + 'security/ir.model.access.csv', + 'views/project_inherit_view.xml', + 'views/project_timesheet_detail_view.xml', + 'views/function_views.xml', + ], + 'installable': True, + 'application': False, +} diff --git a/project_timesheet_detailed/models/__init__.py b/project_timesheet_detailed/models/__init__.py new file mode 100644 index 00000000..0ea28680 --- /dev/null +++ b/project_timesheet_detailed/models/__init__.py @@ -0,0 +1,4 @@ +# -*- coding: utf-8 -*- + +from . import models +from . import project_inherit diff --git a/project_timesheet_detailed/models/models.py b/project_timesheet_detailed/models/models.py new file mode 100644 index 00000000..e8f36900 --- /dev/null +++ b/project_timesheet_detailed/models/models.py @@ -0,0 +1,133 @@ +# -*- coding: utf-8 -*- +# © 2017 Elico Corp (www.elico-corp.com). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from odoo import models, fields, api +from odoo.exceptions import UserError + + +class AccountAnalyticLine(models.Model): + + _inherit = 'account.analytic.line' + + stage = fields.Selection( + selection=[ + ('fixed', 'Fixed'), + ('to review', 'To review'), + ('approved', 'Approved'), + ('rejected', 'Rejected'), + ], + default='to review', + + ) + estimated_time = fields.Float( + 'Estimated time', compute='_compute_estimated_time') + approved_time = fields.Float( + 'Approved time', compute='_compute_approved_time') + + @api.multi + @api.depends('task_id') + def _compute_estimated_time(self): + for rec in self: + if rec.task_id: + rec.estimated_time = rec.task_id.planned_hours + + @api.multi + @api.depends('task_id') + def _compute_approved_time(self): + for rec in self: + if rec.task_id: + approved_tms = rec.env['account.analytic.line'].search( + [('task_id', '=', rec.task_id.id)]).filtered( + lambda rec: rec.stage == 'approved') + rec.approved_time = \ + sum([tms.unit_amount for tms in approved_tms]) + + @api.multi + def set_approved(self): + current_user = self.env.user + for rec in self: + if (current_user == rec.project_id.user_id) or ( + current_user == + rec.user_id.employee_ids.parent_id.user_id): + rec.stage = 'approved' + else: + raise UserError( + 'You do not have permission to approve it') + + @api.multi + def set_rejected(self): + current_user = self.env.user + for rec in self: + if (current_user == rec.project_id.user_id) or ( + current_user == + rec.user_id.employee_ids.parent_id.user_id): + rec.stage = 'rejected' + mail = self.env['mail.mail'].create({ + 'body_html': 'Your TMS %s was turn down' % (rec.name), + 'email_to': rec.user_id.login, + 'subject': 'Reject TMS', + }) + mail.send() + else: + raise UserError( + 'You do not have permission to reject it') + + @api.multi + def set_review(self): + current_user = self.env.user + for rec in self: + if (current_user == rec.project_id.user_id) or ( + current_user == + rec.user_id.employee_ids.parent_id.user_id): + rec.stage = 'to review' + else: + raise UserError( + 'You do not have permission to change the state') + + @api.onchange('project_id') + def onchange_project_id(self): + eml = self.task_id + res = super(AccountAnalyticLine, self).onchange_project_id() + self.task_id = eml + return res + + @api.onchange('task_id') + def task_id_on_change(self): + if self.task_id: + self.project_id = self.task_id.project_id.id + + @api.onchange('issue_id') + def issue_id_on_change(self): + if self.issue_id: + self.project_id = self.issue_id.project_id.id + + @api.multi + def write(self, vals): + for rec in self: + if rec.stage == 'rejected' and vals: + vals['stage'] = 'fixed' + if vals.get('task_id'): + vals.update({ + 'project_id': + self.env['project.task'].search([ + ('id', '=', vals.get('task_id'))]).project_id.id}) + if vals.get('issue_id'): + vals.update( + {'project_id': self.env['project.issue'].search( + [('id', '=', vals.get('issue_id'))]).project_id.id}) + return super(AccountAnalyticLine, self).write(vals) + + @api.multi + def unlink(self): + for rec in self: + if rec.stage != 'to review': + raise UserError('Only records in to review can be deleted!') + return super(AccountAnalyticLine, self).unlink() + + @api.multi + @api.constrains('task_id', 'issue_id') + def _check_task_and_issue(self): + if self.task_id and self.issue_id: + raise ValueError( + 'It is not allowed to have task and issue at the same time.') diff --git a/project_timesheet_detailed/models/project_inherit.py b/project_timesheet_detailed/models/project_inherit.py new file mode 100644 index 00000000..90957cb1 --- /dev/null +++ b/project_timesheet_detailed/models/project_inherit.py @@ -0,0 +1,15 @@ +# -*- coding: utf-8 -*- +# © 2017 Elico Corp (www.elico-corp.com). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import models, fields + + +class ProjectInherit(models.Model): + _inherit = 'project.project' + + check_timesheet = fields.Boolean( + string="Check TMS", + default=True, + help='If true, the TMS will be assigned to the manager of the' + ' employee, if false, the TMS will be assigned to the manager ' + 'of the project.') diff --git a/project_timesheet_detailed/security/ir.model.access.csv b/project_timesheet_detailed/security/ir.model.access.csv new file mode 100644 index 00000000..1f637dc0 --- /dev/null +++ b/project_timesheet_detailed/security/ir.model.access.csv @@ -0,0 +1,4 @@ +"id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" +access_check_timesheet_manager,check_timesheet_manager,model_account_analytic_line,group_timesheet_manager,1,1,1,1 +access_timesheet_project_manager,timesheet_project_manager,model_account_analytic_line,group_timesheet_project_manager,1,1,1,1 +access_group_timesheet_employee,group_timesheet_employee,model_account_analytic_line,base.group_user,1,1,1,0 diff --git a/project_timesheet_detailed/security/project_timesheet_detail.xml b/project_timesheet_detailed/security/project_timesheet_detail.xml new file mode 100644 index 00000000..8d1102f3 --- /dev/null +++ b/project_timesheet_detailed/security/project_timesheet_detail.xml @@ -0,0 +1,10 @@ + + + + Timesheet Manager + + + + Timesheet Project Manager + + diff --git a/project_timesheet_detailed/tests/__init__.py b/project_timesheet_detailed/tests/__init__.py new file mode 100644 index 00000000..711dcf85 --- /dev/null +++ b/project_timesheet_detailed/tests/__init__.py @@ -0,0 +1,3 @@ +# -*- coding:utf-8 -*- + +from . import test_models diff --git a/project_timesheet_detailed/tests/test_models.py b/project_timesheet_detailed/tests/test_models.py new file mode 100644 index 00000000..4263c79b --- /dev/null +++ b/project_timesheet_detailed/tests/test_models.py @@ -0,0 +1,98 @@ +# -*- coding:utf-8 -*- +# © 2017 Elico Corp (www.elico-corp.com). +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo.tests import common +from odoo.exceptions import UserError + + +class TestTimesheetDetail(common.TransactionCase): + + def setUp(self): + super(TestTimesheetDetail, self).setUp() + self.account = self.env.ref('analytic.analytic_millennium_industries') + self.task_1 = self.env.ref('project.project_task_1') + self.task_2 = self.env.ref('project.project_task_20') + self.issue_1 = self.env.ref( + 'project_issue.crm_case_buginaccountsmodule0') + self.issue_2 = self.env.ref( + 'project_issue.crm_case_programnotgivingproperoutput0') + self.timesheet_1 = self.env['account.analytic.line'].create({ + 'name': 'test timesheet', + 'task_id': self.task_1.id, + 'date': '2018-1-10', + 'unit_amount': 2, + 'stage': 'to review', + 'account_id': self.account.id + }) + self.timesheet_2 = self.env['account.analytic.line'].create({ + 'name': 'test test timesheet', + 'task_id': self.task_2.id, + 'date': '2018-1-10', + 'unit_amount': 2, + 'stage': 'to review', + 'account_id': self.account.id + }) + self.timesheet_3 = self.env['account.analytic.line'].create({ + 'name': 'test timesheet', + 'issue_id': self.issue_1.id, + 'date': '2018-1-10', + 'unit_amount': 2, + 'stage': 'approved', + 'account_id': self.account.id + }) + try: + self.env['account.analytic.line'].create({ + 'name': 'test timesheet', + 'issue_id': self.issue_1.id, + 'task_id': self.task_1.id, + 'date': '2018-1-10', + 'unit_amount': 2, + 'stage': 'to review', + 'account_id': self.account.id + }) + except ValueError: + pass + + def test_set_approved(self): + try: + self.timesheet_1.set_approved() + self.timesheet_2.set_approved() + except UserError as e: + self.assertEquals(e.name, + 'Sorry,you do not have permission to approve it') + + def test_set_rejected(self): + try: + self.timesheet_1.set_rejected() + self.timesheet_2.set_rejected() + except UserError as e: + self.assertEquals(e.name, + 'Sorry,you do not have permission to reject it') + + def test_set_review(self): + try: + self.timesheet_1.set_review() + self.timesheet_2.set_review() + except UserError as e: + self.assertEquals( + e.name, 'Sorry,you do not have permission to change the state') + + def test_onchange_project_id(self): + self.timesheet_1.task_id = self.task_2.id + self.assertEquals(self.timesheet_1.project_id, self.task_2.project_id) + + def test_issue_id_on_change(self): + self.timesheet_3.issue_id = self.issue_2.id + self.assertEquals(self.timesheet_3.project_id, self.issue_2.project_id) + + def test_write(self): + vals_1 = {'task_id': self.task_2.id} + self.timesheet_1.write(vals_1) + self.assertEquals(self.timesheet_1.project_id, self.task_2.project_id) + + def test_unlink(self): + try: + self.timesheet_3.unlink() + except UserError as e: + self.assertEquals(e.name, + 'Only records in to review can be deleted!') diff --git a/project_timesheet_detailed/views/function_views.xml b/project_timesheet_detailed/views/function_views.xml new file mode 100644 index 00000000..d6e96cbb --- /dev/null +++ b/project_timesheet_detailed/views/function_views.xml @@ -0,0 +1,59 @@ + + + + Approvel + True + ir.actions.server + + code + records.set_approved() + + + + action_module_approval + + + action + + account.analytic.line + client_action_multi + + + + Reject + True + ir.actions.server + + code + records.set_rejected() + + + + action_module_reject + + + action + + account.analytic.line + client_action_multi + + + + Set to review + True + ir.actions.server + + code + records.set_review() + + + + action_module_review + + + action + + account.analytic.line + client_action_multi + + diff --git a/project_timesheet_detailed/views/project_inherit_view.xml b/project_timesheet_detailed/views/project_inherit_view.xml new file mode 100644 index 00000000..ebfe68ca --- /dev/null +++ b/project_timesheet_detailed/views/project_inherit_view.xml @@ -0,0 +1,59 @@ + + + + project.project.form + project.project + + + +
+ +
+
+
+
+ + + project.task.form.inherited + project.task + + + + + + + + + + + + + + + + + + + + project.issue.form.inherited + project.issue + + + + + + + + + + + + + + + + + +
diff --git a/project_timesheet_detailed/views/project_timesheet_detail_view.xml b/project_timesheet_detailed/views/project_timesheet_detail_view.xml new file mode 100644 index 00000000..23ce1bc4 --- /dev/null +++ b/project_timesheet_detailed/views/project_timesheet_detail_view.xml @@ -0,0 +1,127 @@ + + + + account.analytic.line + timesheet.detail.view.employee.tree + + + + + + + + + + + + + + + + timesheet.detail.line.search + account.analytic.line + + + + + + + + + + + + + + + + + + + + + + account.analytic.line + timesheet.detail.view.manager.tree + tree + + + + + + + + + + + +