diff --git a/addons/hr_timesheet_attendance/__init__.py b/addons/hr_timesheet_attendance/__init__.py index 0bedb4deb09995890f312c8c056899a0a461748b..abbf32cef4cfce52bfae3db2619ebd8428889790 100644 --- a/addons/hr_timesheet_attendance/__init__.py +++ b/addons/hr_timesheet_attendance/__init__.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. -from . import models -from . import report \ No newline at end of file +from . import report diff --git a/addons/hr_timesheet_attendance/__manifest__.py b/addons/hr_timesheet_attendance/__manifest__.py index f3af1ac4711f5aee58d1154206ea565abfaa7f63..d04a6a1a3172acb9d109d7ccd0ad3e0a77b1586b 100644 --- a/addons/hr_timesheet_attendance/__manifest__.py +++ b/addons/hr_timesheet_attendance/__manifest__.py @@ -1,4 +1,6 @@ # -*- coding: utf-8 -*- +# Part of Odoo. See LICENSE file for full copyright and licensing details. + { 'name': "Timesheets/attendances reporting", 'description': """ @@ -7,11 +9,10 @@ 'category': 'Hidden', 'version': '1.0', - 'depends': ['hr_timesheet_sheet', 'hr_attendance'], + 'depends': ['hr_timesheet', 'hr_attendance'], 'data': [ 'security/ir.model.access.csv', 'report/hr_timesheet_attendance_report_view.xml', - 'views/hr_timesheet_sheet_views.xml', ], 'auto_install': True, } diff --git a/addons/hr_timesheet_attendance/models/__init__.py b/addons/hr_timesheet_attendance/models/__init__.py deleted file mode 100644 index 8c881d5bce38e132caae7f4b8fbeb7156378acdd..0000000000000000000000000000000000000000 --- a/addons/hr_timesheet_attendance/models/__init__.py +++ /dev/null @@ -1,6 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from . import hr_attendance -from . import hr_timesheet_sheet -from . import res_company diff --git a/addons/hr_timesheet_attendance/models/hr_attendance.py b/addons/hr_timesheet_attendance/models/hr_attendance.py deleted file mode 100644 index ecc0ba4b01f3b5a905e0639dc48ecb88090485fe..0000000000000000000000000000000000000000 --- a/addons/hr_timesheet_attendance/models/hr_attendance.py +++ /dev/null @@ -1,120 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -import time -from datetime import datetime -from pytz import timezone -import pytz - -from odoo import api, fields, models, _ -from odoo.tools import DEFAULT_SERVER_DATE_FORMAT, DEFAULT_SERVER_DATETIME_FORMAT -from odoo.exceptions import UserError - - -class HrAttendance(models.Model): - _inherit = "hr.attendance" - - sheet_id_computed = fields.Many2one('hr_timesheet_sheet.sheet', string='Sheet', compute='_compute_sheet', index=True, ondelete='cascade', - search='_search_sheet') - sheet_id = fields.Many2one('hr_timesheet_sheet.sheet', compute='_compute_sheet', string='Sheet', store=True) - - @api.depends('employee_id', 'check_in', 'check_out', 'sheet_id_computed.date_to', 'sheet_id_computed.date_from', 'sheet_id_computed.employee_id') - def _compute_sheet(self): - """Links the attendance to the corresponding sheet - """ - for attendance in self: - corresponding_sheet = self.env['hr_timesheet_sheet.sheet'].search( - [('date_to', '>=', attendance.check_in), ('date_from', '<=', attendance.check_in), - ('employee_id', '=', attendance.employee_id.id), - ('state', 'in', ['draft', 'new'])], limit=1) - if corresponding_sheet: - attendance.sheet_id_computed = corresponding_sheet[0] - attendance.sheet_id = corresponding_sheet[0] - - def _search_sheet(self, operator, value): - assert operator == 'in' - ids = [] - for ts in self.env['hr_timesheet_sheet.sheet'].browse(value): - self._cr.execute(""" - SELECT a.id - FROM hr_attendance a - WHERE %(date_to)s >= a.check_in - AND %(date_from)s <= a.check_in - AND %(employee_id)s = a.employee_id - GROUP BY a.id""", {'date_from': ts.date_from, - 'date_to': ts.date_to, - 'employee_id': ts.employee_id.id, }) - ids.extend([row[0] for row in self._cr.fetchall()]) - return [('id', 'in', ids)] - - def _get_attendance_employee_tz(self, employee_id, date): - """ Simulate timesheet in employee timezone - - Return the attendance date in string format in the employee - tz converted from utc timezone as we consider date of employee - timesheet is in employee timezone - """ - tz = False - if employee_id: - employee = self.env['hr.employee'].browse(employee_id) - tz = employee.user_id.partner_id.tz - - if not date: - date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - - att_tz = timezone(tz or 'utc') - - attendance_dt = datetime.strptime(date, DEFAULT_SERVER_DATETIME_FORMAT) - att_tz_dt = pytz.utc.localize(attendance_dt) - att_tz_dt = att_tz_dt.astimezone(att_tz) - # We take only the date omiting the hours as we compare with timesheet - # date_from which is a date format thus using hours would lead to - # be out of scope of timesheet - att_tz_date_str = datetime.strftime(att_tz_dt, DEFAULT_SERVER_DATE_FORMAT) - return att_tz_date_str - - def _get_current_sheet(self, employee_id, date=False): - if not date: - date = time.strftime(DEFAULT_SERVER_DATETIME_FORMAT) - - att_tz_date_str = self._get_attendance_employee_tz(employee_id, date=date) - sheet = self.env['hr_timesheet_sheet.sheet'].search( - [('date_from', '<=', att_tz_date_str), - ('date_to', '>=', att_tz_date_str), - ('employee_id', '=', employee_id)], limit=1) - return sheet or False - - @api.model - def create(self, vals): - if self.env.context.get('sheet_id'): - sheet = self.env['hr_timesheet_sheet.sheet'].browse(self.env.context.get('sheet_id')) - else: - sheet = self._get_current_sheet(vals.get('employee_id'), vals.get('check_in')) - if sheet: - att_tz_date_str = self._get_attendance_employee_tz(vals.get('employee_id'), date=vals.get('check_in')) - if sheet.state not in ('draft', 'new'): - raise UserError(_('You can not enter an attendance in a submitted timesheet. Ask your manager to reset it before adding attendance.')) - elif sheet.date_from > att_tz_date_str or sheet.date_to < att_tz_date_str: - raise UserError(_('You can not enter an attendance date outside the current timesheet dates.')) - return super(HrAttendance, self).create(vals) - - @api.multi - def unlink(self): - self._check() - return super(HrAttendance, self).unlink() - - @api.multi - def write(self, vals): - self._check() - res = super(HrAttendance, self).write(vals) - if 'sheet_id' in self.env.context: - for attendance in self: - if self.env.context['sheet_id'] != attendance.sheet_id.id: - raise UserError(_('You cannot enter an attendance date outside the current timesheet dates.')) - return res - - def _check(self): - for att in self: - if att.sheet_id and att.sheet_id.state not in ('draft', 'new'): - raise UserError(_('You cannot modify an entry in a confirmed timesheet')) - return True diff --git a/addons/hr_timesheet_attendance/models/res_company.py b/addons/hr_timesheet_attendance/models/res_company.py deleted file mode 100644 index 972ce91f58bd9952cc2eca8eda7dbfd2c509d62e..0000000000000000000000000000000000000000 --- a/addons/hr_timesheet_attendance/models/res_company.py +++ /dev/null @@ -1,13 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import fields, models - - -class ResCompany(models.Model): - _inherit = 'res.company' - - timesheet_max_difference = fields.Float(string='Timesheet allowed difference(Hours)', - help="Allowed difference in hours between the sign in/out and the timesheet " \ - "computation for one sheet. Set this to 0 if you do not want any control.", - default=0.0) diff --git a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report_view.xml b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report_view.xml index a307bd47ddd2086f4fde52724054850072f7e041..d2b335d99ea91dc24d7aaa8d110bc8e1c035eb7e 100644 --- a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report_view.xml +++ b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report_view.xml @@ -1,3 +1,4 @@ +<?xml version="1.0" encoding="utf-8"?> <odoo> <data> <record id="view_hr_timesheet_attendance_report_search" model="ir.ui.view"> diff --git a/addons/hr_timesheet_attendance/security/ir.model.access.csv b/addons/hr_timesheet_attendance/security/ir.model.access.csv index 5ae999d3a4acfe0b64767672bc46d89a1f016bc9..9cc5208a04e6440e8f57ef42d4b1b93760075fd9 100644 --- a/addons/hr_timesheet_attendance/security/ir.model.access.csv +++ b/addons/hr_timesheet_attendance/security/ir.model.access.csv @@ -1,4 +1,2 @@ id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink -access_hr_timesheet_sheet_sheet_day,hr_timesheet_sheet.sheet.day,model_hr_timesheet_sheet_sheet_day,hr_timesheet.group_hr_timesheet_user,1,1,1,1 -access_hr_timesheet_sheet_sheet_day_user,hr.timesheet.sheet.sheet.day.user,model_hr_timesheet_sheet_sheet_day,base.group_user,1,1,1,0 access_hr_timesheet_attendance_report,access_hr_timesheet_attendance_report,model_hr_timesheet_attendance_report,hr_timesheet.group_hr_timesheet_user,1,0,0,0 diff --git a/addons/hr_timesheet_attendance/tests/__init__.py b/addons/hr_timesheet_attendance/tests/__init__.py deleted file mode 100644 index 50a24fb186f48f609f2c37d679d2bbad6d444232..0000000000000000000000000000000000000000 --- a/addons/hr_timesheet_attendance/tests/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -# -*- coding: utf-8 -*- - -from . import test_hr_timesheet_sheet diff --git a/addons/hr_timesheet_attendance/tests/test_hr_timesheet_sheet.py b/addons/hr_timesheet_attendance/tests/test_hr_timesheet_sheet.py deleted file mode 100644 index 443c9d9339e0d31525e1952ecbf9697bfe5e9182..0000000000000000000000000000000000000000 --- a/addons/hr_timesheet_attendance/tests/test_hr_timesheet_sheet.py +++ /dev/null @@ -1,79 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo.tests.common import TransactionCase -import time - - -class TestHrTimesheetSheet(TransactionCase): - """Test for hr_timesheet_sheet.sheet""" - - def setUp(self): - super(TestHrTimesheetSheet, self).setUp() - self.attendance = self.env['hr.attendance'] - self.timesheet_sheet = self.env['hr_timesheet_sheet.sheet'] - self.test_employee = self.browse_ref('hr.employee_qdp') - self.company = self.browse_ref('base.main_company') - self.company.timesheet_max_difference = 1.00 - - def test_hr_timesheet_sheet(self): - - # I create a timesheet for employee "Gilles Gravie". - self.test_timesheet_sheet = self.timesheet_sheet.create({ - 'date_from': time.strftime('%Y-%m-11'), - 'date_to': time.strftime('%Y-%m-17'), - 'name': 'Gilles Gravie', - 'state': 'new', - 'user_id': self.browse_ref('base.user_demo').id, - 'employee_id': self.test_employee.id, - }) - - # I check Gilles in at around 9:00 and out at 17:30 - self.attendance.create({ - 'employee_id': self.test_employee.id, - 'check_in': time.strftime('%Y-%m-11 09:12:37'), - 'check_out': time.strftime('%Y-%m-11 17:30:00'), - }) - - # I add 6 hours of work to Gilles' timesheet - self.test_timesheet_sheet.write({'timesheet_ids': [(0, 0, { - 'project_id': self.browse_ref('project.project_project_2').id, - 'date': time.strftime('%Y-%m-11'), - 'name': 'Develop yaml for hr module(1)', - 'user_id': self.browse_ref('base.user_demo').id, - 'unit_amount': 6.00, - 'amount': -90.00, - 'product_id': self.browse_ref('product.product_product_1').id, - })]}) - - # I confirm Gilles' timesheet with over 1 hour difference - # in attendance and actual worked hours - try: - self.test_timesheet_sheet.action_timesheet_confirm() - except Exception: - pass - - # I add another 2 hours of work to Gilles' timesheet - self.test_timesheet_sheet.write({'timesheet_ids': [(0, 0, { - 'project_id': self.browse_ref('project.project_project_2').id, - 'date': time.strftime('%Y-%m-11'), - 'name': 'Develop yaml for hr module(2)', - 'user_id': self.browse_ref('base.user_demo').id, - 'unit_amount': 2.00, - 'amount': -90.00, - 'product_id': self.browse_ref('product.product_product_1').id, - })]}) - # I invalidate the cache, otherwise total_timesheet and total_difference doesn't get updated... /this is a disgrace/ - self.test_timesheet_sheet.invalidate_cache(['total_attendance', 'total_timesheet', 'total_difference']) - - # I confirm Gilles' timesheet with less than 1 hour difference - # in attendance and actual worked hours - self.test_timesheet_sheet.action_timesheet_confirm() - - # I check the state is confirmed - assert self.test_timesheet_sheet.state == 'confirm' - - # the manager accepts the timesheet - self.test_timesheet_sheet.write({'state': 'done'}) - - # I check the state is indeed done - assert self.test_timesheet_sheet.state == 'done'