diff --git a/addons/hr_attendance/views/hr_employee_view.xml b/addons/hr_attendance/views/hr_employee_view.xml
new file mode 100644
index 00000000000..fd709ddcee6
--- /dev/null
+++ b/addons/hr_attendance/views/hr_employee_view.xml
@@ -0,0 +1,86 @@
+
+
+
+ hr.employee
+ hr.employee
+
+ 10
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ hr.employee.kanban
+ hr.employee
+ 99
+
+
+
+
+
+
+
![]()
+
+
+
+
+
+
+
+
+
+
+ hr.employee
+ hr.employee
+
+ 1
+
+
+
+
+
+
+
+
+ Employees
+ hr.employee
+ kanban
+
+
+
+
\ No newline at end of file
diff --git a/addons/hr_attendance/views/report_attendanceerrors.xml b/addons/hr_attendance/views/report_attendanceerrors.xml
deleted file mode 100644
index 8ee00a60534..00000000000
--- a/addons/hr_attendance/views/report_attendanceerrors.xml
+++ /dev/null
@@ -1,45 +0,0 @@
-
-
-
-
-
-
-
-
-
-
Attendance Errors:
-
-
-
-
- | Operation |
- Date Signed |
- Date Recorded |
- Delay |
- Min Delay |
-
-
-
-
- |
- |
- |
- |
- |
-
-
- | Total period |
- |
- |
-
-
-
-
(*) A positive delay means that the employee worked less than recorded.
-(*) A negative delay means that the employee worked more than encoded.
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/addons/hr_attendance/views/res_config_view.xml b/addons/hr_attendance/views/res_config_view.xml
new file mode 100644
index 00000000000..66224e5d4aa
--- /dev/null
+++ b/addons/hr_attendance/views/res_config_view.xml
@@ -0,0 +1,30 @@
+
+
+
+ base.config.setting.hr.form.attendance
+ base.config.settings
+ 99
+
+
+
+
+
+
+ Attendance Settings
+ base.config.settings
+ form
+ inline
+
+
+
+
+
\ No newline at end of file
diff --git a/addons/hr_attendance/views/web_asset_backend_template.xml b/addons/hr_attendance/views/web_asset_backend_template.xml
new file mode 100644
index 00000000000..1041d410e1c
--- /dev/null
+++ b/addons/hr_attendance/views/web_asset_backend_template.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/addons/hr_attendance/wizard/__init__.py b/addons/hr_attendance/wizard/__init__.py
deleted file mode 100644
index 1a210eb01d4..00000000000
--- a/addons/hr_attendance/wizard/__init__.py
+++ /dev/null
@@ -1,4 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-
-import hr_attendance_error
diff --git a/addons/hr_attendance/wizard/hr_attendance_error.py b/addons/hr_attendance/wizard/hr_attendance_error.py
deleted file mode 100644
index cdc6be21f4d..00000000000
--- a/addons/hr_attendance/wizard/hr_attendance_error.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# -*- coding: utf-8 -*-
-# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import time
-
-from openerp.osv import fields, osv
-from openerp.tools.translate import _
-from openerp.exceptions import UserError
-
-
-class hr_attendance_error(osv.osv_memory):
-
- _name = 'hr.attendance.error'
- _description = 'Print Error Attendance Report'
- _columns = {
- 'init_date': fields.date('Starting Date', required=True),
- 'end_date': fields.date('Ending Date', required=True),
- 'max_delay': fields.integer('Maximum Tolerance (in minutes)', required=True,
- help="Allowed difference in minutes between the signin/signout and the timesheet computation for one sheet. Set this to 0 for no tolerance.")
- }
- _defaults = {
- 'init_date': lambda *a: time.strftime('%Y-%m-%d'),
- 'end_date': lambda *a: time.strftime('%Y-%m-%d'),
- 'max_delay': 120,
- }
-
- def print_report(self, cr, uid, ids, context=None):
- emp_ids = []
- data_error = self.read(cr, uid, ids, context=context)[0]
- date_from = data_error['init_date']
- date_to = data_error['end_date']
- cr.execute("SELECT id FROM hr_attendance WHERE employee_id IN %s AND to_char(name,'YYYY-mm-dd')<=%s AND to_char(name,'YYYY-mm-dd')>=%s AND action IN %s ORDER BY name" ,(tuple(context['active_ids']), date_to, date_from, tuple(['sign_in','sign_out'])))
- attendance_ids = [x[0] for x in cr.fetchall()]
- if not attendance_ids:
- raise UserError(_('No records are found for your selection!'))
- attendance_records = self.pool.get('hr.attendance').browse(cr, uid, attendance_ids, context=context)
-
- for rec in attendance_records:
- if rec.employee_id.id not in emp_ids:
- emp_ids.append(rec.employee_id.id)
- data_error['emp_ids'] = emp_ids
- datas = {
- 'ids': [],
- 'model': 'hr.employee',
- 'form': data_error
- }
- return self.pool['report'].get_action(
- cr, uid, [], 'hr_attendance.report_attendanceerrors', data=datas, context=context
- )
diff --git a/addons/hr_attendance/wizard/hr_attendance_error_view.xml b/addons/hr_attendance/wizard/hr_attendance_error_view.xml
deleted file mode 100644
index bb538952f20..00000000000
--- a/addons/hr_attendance/wizard/hr_attendance_error_view.xml
+++ /dev/null
@@ -1,43 +0,0 @@
-
-
-
-
-
- Attendance Report Error
- hr.attendance.error
-
-
-
-
-
-
- Attendance Error Report
- ir.actions.act_window
- hr.attendance.error
- form
- form
- new
-
-
-
-
- Attendance Error Report
- client_print_multi
-
- action
- hr.employee
-
-
-
-
diff --git a/addons/hr_timesheet/hr_timesheet_demo.yml b/addons/hr_timesheet/hr_timesheet_demo.yml
index a6e3276adb0..4225d319f01 100644
--- a/addons/hr_timesheet/hr_timesheet_demo.yml
+++ b/addons/hr_timesheet/hr_timesheet_demo.yml
@@ -2,11 +2,12 @@
Creating demo data that require looking for accounts
-
!python {model: ir.model.data} : |
- import time
+ from datetime import datetime
+ from dateutil.relativedelta import relativedelta
vals = {
'name': 'Requirements analysis and specification',
'user_id': ref('base.user_root'),
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': (datetime.now()+relativedelta(days=-1)).strftime('%Y-%m-%d'),
'unit_amount': 2.00,
'product_id': ref('product.product_product_1'),
'product_uom_id': ref('product.product_uom_hour'),
@@ -17,7 +18,7 @@
vals = {
'name': 'Design and specification',
'user_id': ref('base.user_root'),
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': (datetime.now()+relativedelta(days=-1)).strftime('%Y-%m-%d'),
'unit_amount': 1.00,
'product_id': ref('product.product_product_1'),
'product_uom_id': ref('product.product_uom_hour'),
@@ -28,7 +29,7 @@
vals = {
'name': 'Coding and module testing',
'user_id': ref('base.user_root'),
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': (datetime.now()+relativedelta(days=-1)).strftime('%Y-%m-%d'),
'unit_amount': 3.00,
'product_id': ref('product.product_product_1'),
'product_uom_id': ref('product.product_uom_hour'),
@@ -39,7 +40,7 @@
vals = {
'name': 'Integration and system testing',
'user_id': ref('base.user_root'),
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': (datetime.now()+relativedelta(days=-1)).strftime('%Y-%m-%d'),
'unit_amount': 1.00,
'product_id': ref('product.product_product_1'),
'product_uom_id': ref('product.product_uom_hour'),
@@ -50,7 +51,7 @@
vals = {
'name': 'Delivery and maintenance',
'user_id': ref('base.user_root'),
- 'date': time.strftime('%Y-%m-%d'),
+ 'date': (datetime.now()+relativedelta(days=-1)).strftime('%Y-%m-%d'),
'unit_amount': 1.00,
'product_id': ref('product.product_product_1'),
'product_uom_id': ref('product.product_uom_hour'),
diff --git a/addons/hr_timesheet_attendance/__init__.py b/addons/hr_timesheet_attendance/__init__.py
index 006edd3fd74..c2abfbdfea8 100644
--- a/addons/hr_timesheet_attendance/__init__.py
+++ b/addons/hr_timesheet_attendance/__init__.py
@@ -1,3 +1,5 @@
# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
-from . import report
+import models
+import report
\ No newline at end of file
diff --git a/addons/hr_timesheet_attendance/__openerp__.py b/addons/hr_timesheet_attendance/__openerp__.py
index e8f66a2284e..5524e569ba2 100644
--- a/addons/hr_timesheet_attendance/__openerp__.py
+++ b/addons/hr_timesheet_attendance/__openerp__.py
@@ -2,12 +2,15 @@
{
'name': "Timesheets/attendances reporting",
'description': """
+ Module linking the attendance module to the timesheet app.
""",
'category': 'Hidden',
- 'version': '0.1',
+ 'version': '1.0',
- # any module necessary for this one to work correctly
- 'depends': ['hr_timesheet', 'hr_attendance'],
- 'data': ['report/hr_timesheet_attendance_report_view.xml'],
+ 'depends': ['hr_timesheet_sheet', 'hr_attendance'],
+ 'data': [
+ 'report/hr_timesheet_attendance_report_view.xml',
+ 'views/hr_timesheet_sheet_views.xml',
+ ],
'auto_install': True,
}
diff --git a/addons/hr_attendance/report/__init__.py b/addons/hr_timesheet_attendance/models/__init__.py
similarity index 60%
rename from addons/hr_attendance/report/__init__.py
rename to addons/hr_timesheet_attendance/models/__init__.py
index c9ab3c9b3bf..e9110f22b7c 100644
--- a/addons/hr_attendance/report/__init__.py
+++ b/addons/hr_timesheet_attendance/models/__init__.py
@@ -1,4 +1,6 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
-import attendance_errors
+import hr_attendance
+import hr_timesheet_sheet
+import res_company
diff --git a/addons/hr_timesheet_attendance/models/hr_attendance.py b/addons/hr_timesheet_attendance/models/hr_attendance.py
new file mode 100644
index 00000000000..ecc0ba4b01f
--- /dev/null
+++ b/addons/hr_timesheet_attendance/models/hr_attendance.py
@@ -0,0 +1,120 @@
+# -*- 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/hr_timesheet_sheet.py b/addons/hr_timesheet_attendance/models/hr_timesheet_sheet.py
new file mode 100644
index 00000000000..7a9da24eb10
--- /dev/null
+++ b/addons/hr_timesheet_attendance/models/hr_timesheet_sheet.py
@@ -0,0 +1,177 @@
+# -*- coding: utf-8 -*-
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+from odoo import api, fields, models
+from odoo.tools.translate import _
+from odoo.exceptions import UserError
+
+
+class HrTimesheetSheet(models.Model):
+ _inherit = "hr_timesheet_sheet.sheet"
+
+ attendances_ids = fields.One2many('hr.attendance', 'sheet_id', 'Attendances')
+ total_attendance = fields.Integer(string='Total Attendance', compute='_compute_total')
+ total_timesheet = fields.Float(string='Total Timesheet', compute="_compute_total")
+ total_difference = fields.Float(string='Difference', compute="_compute_total")
+ period_ids = fields.One2many('hr_timesheet_sheet.sheet.day', 'sheet_id', string='Period', readonly=True)
+ attendance_count = fields.Integer(compute='_compute_attendances', string="Attendances")
+
+ @api.depends('period_ids.total_attendance', 'period_ids.total_timesheet', 'period_ids.total_difference')
+ def _compute_total(self):
+ """ Compute the attendances, analytic lines timesheets and differences
+ between them for all the days of a timesheet and the current day
+ """
+ if len(self.ids) == 0:
+ return
+
+ self.env.cr.execute("""
+ SELECT sheet_id as id,
+ sum(total_attendance) as total_attendance,
+ sum(total_timesheet) as total_timesheet,
+ sum(total_difference) as total_difference
+ FROM hr_timesheet_sheet_sheet_day
+ WHERE sheet_id IN %s
+ GROUP BY sheet_id
+ """, (tuple(self.ids), ))
+
+ for x in self.env.cr.dictfetchall():
+ sheet = self.browse(x.pop('id'))
+ sheet.total_attendance = x.pop('total_attendance')
+ sheet.total_timesheet = x.pop('total_timesheet')
+ sheet.total_difference = x.pop('total_difference')
+
+ @api.depends('attendances_ids')
+ def _compute_attendances(self):
+ for sheet in self:
+ sheet.attendance_count = len(sheet.attendances_ids)
+
+ @api.multi
+ def unlink(self):
+ sheets = self.read(['total_attendance'])
+ for sheet in sheets:
+ if sheet['total_attendance'] > 0.00:
+ raise UserError(_('You cannot delete a timesheet that has attendance entries.'))
+ return super(HrTimesheetSheet, self).unlink()
+
+ @api.multi
+ def action_sheet_report(self):
+ self.ensure_one()
+ return {
+ 'type': 'ir.actions.act_window',
+ 'name': 'HR Timesheet/Attendance Report',
+ 'res_model': 'hr.timesheet.attendance.report',
+ 'domain': [('date', '>=', self.date_from), ('date', '<=', self.date_to)],
+ 'view_mode': 'pivot',
+ 'context': {'search_default_user_id': self.user_id.id, }
+ }
+
+ @api.multi
+ def button_confirm(self):
+ for sheet in self:
+ sheet.check_employee_attendance_state()
+ di = sheet.user_id.company_id.timesheet_max_difference
+ if (abs(sheet.total_difference) <= di) or not di:
+ return super(HrTimesheetSheet, self).button_confirm()
+ else:
+ raise UserError(_('Please verify that the total difference of the sheet is lower than %.2f.') % (di,))
+
+ @api.multi
+ def check_employee_attendance_state(self):
+ """ Checks the attendance records of the timesheet, make sure they are all closed
+ (by making sure they have a check_out time)
+ """
+ self.ensure_one()
+ if any(self.attendances_ids.filtered(lambda r: not r.check_out)):
+ raise UserError(_("The timesheet cannot be validated as it contains an attendance record with no Check Out)."))
+ return True
+
+
+class hr_timesheet_sheet_sheet_day(models.Model):
+ _name = "hr_timesheet_sheet.sheet.day"
+ _description = "Timesheets by Period"
+ _auto = False
+ _order = 'name'
+
+ name = fields.Date('Date', readonly=True)
+ sheet_id = fields.Many2one('hr_timesheet_sheet.sheet', 'Sheet', readonly=True, index=True)
+ total_timesheet = fields.Float('Total Timesheet', readonly=True)
+ total_attendance = fields.Float('Attendance', readonly=True)
+ total_difference = fields.Float('Difference', readonly=True)
+
+ _depends = {
+ 'account.analytic.line': ['date', 'unit_amount'],
+ 'hr.attendance': ['check_in', 'check_out', 'sheet_id'],
+ 'hr_timesheet_sheet.sheet': ['attendances_ids', 'timesheet_ids'],
+ }
+
+ def init(self, cr):
+ cr.execute("""create or replace view %s as
+ SELECT
+ id,
+ name,
+ sheet_id,
+ total_timesheet,
+ total_attendance,
+ cast(round(cast(total_attendance - total_timesheet as Numeric),2) as Double Precision) AS total_difference
+ FROM
+ ((
+ SELECT
+ MAX(id) as id,
+ name,
+ sheet_id,
+ timezone,
+ SUM(total_timesheet) as total_timesheet,
+ CASE WHEN SUM(orphan_attendances) != 0
+ THEN (SUM(total_attendance) +
+ CASE WHEN current_date <> name
+ THEN 1440
+ ELSE (EXTRACT(hour FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC')) * 60) + EXTRACT(minute FROM current_time AT TIME ZONE 'UTC' AT TIME ZONE coalesce(timezone, 'UTC'))
+ END
+ )
+ ELSE SUM(total_attendance)
+ END /60 as total_attendance
+ FROM
+ ((
+ select
+ min(l.id) as id,
+ p.tz as timezone,
+ l.date::date as name,
+ s.id as sheet_id,
+ sum(l.unit_amount) as total_timesheet,
+ 0 as orphan_attendances,
+ 0.0 as total_attendance
+ from
+ account_analytic_line l
+ LEFT JOIN hr_timesheet_sheet_sheet s ON s.id = l.sheet_id
+ JOIN hr_employee e ON s.employee_id = e.id
+ JOIN resource_resource r ON e.resource_id = r.id
+ LEFT JOIN res_users u ON r.user_id = u.id
+ LEFT JOIN res_partner p ON u.partner_id = p.id
+ group by l.date::date, s.id, timezone
+ ) union (
+ select
+ -min(a.id) as id,
+ p.tz as timezone,
+ (a.check_in AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date as name,
+ s.id as sheet_id,
+ 0.0 as total_timesheet,
+ 0 as orphan_attendances,
+ SUM((EXTRACT(hour FROM (a.check_out AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))) * 60 + EXTRACT(minute FROM (a.check_out AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))))
+ - (EXTRACT(hour FROM (a.check_in AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))) * 60 + EXTRACT(minute FROM (a.check_in AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))))) as total_attendance
+ from
+ hr_attendance a
+ LEFT JOIN hr_timesheet_sheet_sheet s
+ ON s.id = a.sheet_id
+ JOIN hr_employee e
+ ON a.employee_id = e.id
+ JOIN resource_resource r
+ ON e.resource_id = r.id
+ LEFT JOIN res_users u
+ ON r.user_id = u.id
+ LEFT JOIN res_partner p
+ ON u.partner_id = p.id
+ WHERE check_out IS NOT NULL
+ group by (a.check_in AT TIME ZONE 'UTC' AT TIME ZONE coalesce(p.tz, 'UTC'))::date, s.id, timezone
+ )) AS foo
+ GROUP BY name, sheet_id, timezone
+ )) AS bar""" % self._table)
diff --git a/addons/hr_timesheet_attendance/models/res_company.py b/addons/hr_timesheet_attendance/models/res_company.py
new file mode 100644
index 00000000000..972ce91f58b
--- /dev/null
+++ b/addons/hr_timesheet_attendance/models/res_company.py
@@ -0,0 +1,13 @@
+# -*- 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/__init__.py b/addons/hr_timesheet_attendance/report/__init__.py
index a32b4f5eea7..18d5feae891 100644
--- a/addons/hr_timesheet_attendance/report/__init__.py
+++ b/addons/hr_timesheet_attendance/report/__init__.py
@@ -1,2 +1,4 @@
# -*- coding: utf-8 -*-
-from . import hr_timesheet_attendance_report
+# Part of Odoo. See LICENSE file for full copyright and licensing details.
+
+import hr_timesheet_attendance_report
diff --git a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py
index c93ea4abf1d..5ab1b632ec1 100644
--- a/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py
+++ b/addons/hr_timesheet_attendance/report/hr_timesheet_attendance_report.py
@@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
-from openerp import api, fields, models, tools
+from openerp import fields, models
+
class TimesheetAttendance(models.Model):
_name = 'hr.timesheet.attendance.report'
@@ -27,11 +28,10 @@ class TimesheetAttendance(models.Model):
resource_resource.user_id AS user_id,
hr_attendance.worked_hours AS attendance,
NULL AS timesheet,
- date_trunc('day', hr_attendance.name) AS date
+ date_trunc('day', hr_attendance.check_in) AS date
FROM hr_attendance
LEFT JOIN hr_employee ON hr_employee.id = hr_attendance.employee_id
LEFT JOIN resource_resource on resource_resource.id = hr_employee.resource_id
- WHERE hr_attendance.action = 'sign_out'
UNION ALL
SELECT
ts.id AS id,
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 34164187167..ba48dceb8f2 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
@@ -5,11 +5,12 @@
hr.timesheet.attendance.report
+
{'search_default_week': True}
-