From dde556db2e2cf78426d59c468da7950e5b2f8d21 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 14 Nov 2011 17:58:23 +0530 Subject: [PATCH 1/6] [FIX] hr_attendance : timesheet - sign in - out : must not allow overlaps bzr revid: mdi@tinyerp.com-20111114122823-7ehd1axt0l0b2euz --- addons/hr_attendance/hr_attendance.py | 28 ++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/addons/hr_attendance/hr_attendance.py b/addons/hr_attendance/hr_attendance.py index a138d66a285..31061459426 100644 --- a/addons/hr_attendance/hr_attendance.py +++ b/addons/hr_attendance/hr_attendance.py @@ -64,19 +64,21 @@ class hr_attendance(osv.osv): } def _altern_si_so(self, cr, uid, ids, context=None): - for id in ids: - sql = ''' - SELECT action, name - FROM hr_attendance AS att - WHERE employee_id = (SELECT employee_id FROM hr_attendance WHERE id=%s) - AND action IN ('sign_in','sign_out') - AND name <= (SELECT name FROM hr_attendance WHERE id=%s) - ORDER BY name DESC - LIMIT 2 ''' - cr.execute(sql,(id,id)) - atts = cr.fetchall() - if not ((len(atts)==1 and atts[0][0] == 'sign_in') or (len(atts)==2 and atts[0][0] != atts[1][0] and atts[0][1] != atts[1][1])): - return False + current_attendance_data = self.browse(cr, uid, ids, context=context)[0] + obj_attendance_ids = self.search(cr, uid, [('employee_id', '=', current_attendance_data.employee_id.id)], context=context) + obj_attendance_ids.remove(ids[0]) + hr_attendance_data = self.browse(cr, uid, obj_attendance_ids, context=context) + + for old_attendance in hr_attendance_data: + if old_attendance.employee_id.id == current_attendance_data['employee_id'].id: + if old_attendance.action == current_attendance_data['action']: + return False + elif old_attendance.name >= current_attendance_data['name']: + return False + else: + return True + if current_attendance_data['action'] == 'sign_out': + return False return True _constraints = [(_altern_si_so, 'Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])] From 573227209615c0f30bb5b9d2efd28dd2ea36ed8d Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Tue, 20 Dec 2011 15:15:52 +0530 Subject: [PATCH 2/6] [FIX] hr_attendance : timesheet - sign in - out : must not allow overlaps lp bug: https://launchpad.net/bugs/885387 fixed bzr revid: mdi@tinyerp.com-20111220094552-jfwpgzy7f0lmig44 --- addons/hr_attendance/hr_attendance.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/addons/hr_attendance/hr_attendance.py b/addons/hr_attendance/hr_attendance.py index 31061459426..9987beae248 100644 --- a/addons/hr_attendance/hr_attendance.py +++ b/addons/hr_attendance/hr_attendance.py @@ -63,6 +63,14 @@ class hr_attendance(osv.osv): 'employee_id': _employee_get, } + def write(self, cr, uid, ids, vals, context=None): + current_attendance_data = self.browse(cr, uid, ids, context=context)[0] + obj_attendance_ids = self.search(cr, uid, [('employee_id', '=', current_attendance_data.employee_id.id)], context=context) + if obj_attendance_ids[0] != ids[0]: + if ('name' in vals) or ('action' in vals): + raise osv.except_osv(_('Warning !'), _('You can not modify existing entries. To modify the entry , remove the prior entries.')) + return super(hr_attendance, self).write(cr, uid, ids, vals, context=context) + def _altern_si_so(self, cr, uid, ids, context=None): current_attendance_data = self.browse(cr, uid, ids, context=context)[0] obj_attendance_ids = self.search(cr, uid, [('employee_id', '=', current_attendance_data.employee_id.id)], context=context) @@ -70,13 +78,13 @@ class hr_attendance(osv.osv): hr_attendance_data = self.browse(cr, uid, obj_attendance_ids, context=context) for old_attendance in hr_attendance_data: - if old_attendance.employee_id.id == current_attendance_data['employee_id'].id: - if old_attendance.action == current_attendance_data['action']: - return False - elif old_attendance.name >= current_attendance_data['name']: - return False - else: - return True + if old_attendance.action == current_attendance_data['action']: + return False + elif old_attendance.name >= current_attendance_data['name']: + return False + else: + return True + # First entry in the system must not be 'sign_out' if current_attendance_data['action'] == 'sign_out': return False return True From 20ed63d59809061118cb32c3734c874b4ddc966d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thibault=20Delavall=C3=A9e?= Date: Wed, 11 Jan 2012 14:01:21 +0100 Subject: [PATCH 3/6] [IMP] Refactored _altern_si_so code. Added limits to searches. Code now validates only new data, not all past data. Cleaned algorithm documentation. bzr revid: tde@openerp.com-20120111130121-8bcgnjt233nv0izj --- addons/hr_attendance/hr_attendance.py | 37 ++++++++++++--------------- 1 file changed, 16 insertions(+), 21 deletions(-) diff --git a/addons/hr_attendance/hr_attendance.py b/addons/hr_attendance/hr_attendance.py index 9987beae248..80741510785 100644 --- a/addons/hr_attendance/hr_attendance.py +++ b/addons/hr_attendance/hr_attendance.py @@ -63,30 +63,25 @@ class hr_attendance(osv.osv): 'employee_id': _employee_get, } - def write(self, cr, uid, ids, vals, context=None): - current_attendance_data = self.browse(cr, uid, ids, context=context)[0] - obj_attendance_ids = self.search(cr, uid, [('employee_id', '=', current_attendance_data.employee_id.id)], context=context) - if obj_attendance_ids[0] != ids[0]: - if ('name' in vals) or ('action' in vals): - raise osv.except_osv(_('Warning !'), _('You can not modify existing entries. To modify the entry , remove the prior entries.')) - return super(hr_attendance, self).write(cr, uid, ids, vals, context=context) - def _altern_si_so(self, cr, uid, ids, context=None): - current_attendance_data = self.browse(cr, uid, ids, context=context)[0] - obj_attendance_ids = self.search(cr, uid, [('employee_id', '=', current_attendance_data.employee_id.id)], context=context) - obj_attendance_ids.remove(ids[0]) - hr_attendance_data = self.browse(cr, uid, obj_attendance_ids, context=context) - - for old_attendance in hr_attendance_data: - if old_attendance.action == current_attendance_data['action']: + """ Alternance sign_in/sign_out check + Previous (if exists) must be of opposite action + Next (if exists) must be of opposite action + """ + atts = self.browse(cr, uid, ids, context=context) + for att in atts: + # search and browse for first previous and first next records + prev_att_ids = self.search(cr, uid, [('employee_id', '=', att.employee_id.id), ('name', '<', att.name), ('action', 'in', ('sign_in', 'sign_out'))], limit=1, order='name DESC') + next_add_ids = self.search(cr, uid, [('employee_id', '=', att.employee_id.id), ('name', '>', att.name), ('action', 'in', ('sign_in', 'sign_out'))], limit=1, order='name ASC') + prev_atts = self.browse(cr, uid, prev_att_ids, context=context) if (len(prev_att_ids) != 0) else [] + next_atts = self.browse(cr, uid, next_add_ids, context=context) if (len(next_add_ids) != 0) else [] + # perform alternance check, return False if at least one condition is not satisfied + if len(prev_atts) != 0 and prev_atts[0].action == att.action: # previous exists and is same action return False - elif old_attendance.name >= current_attendance_data['name']: + if len(next_atts) != 0 and next_atts[0].action == att.action: # next exists and is same action + return False + if len(prev_atts) == 0 and len(next_atts) == 0 and att.action != 'sign_in': # first attendance must be sign_in return False - else: - return True - # First entry in the system must not be 'sign_out' - if current_attendance_data['action'] == 'sign_out': - return False return True _constraints = [(_altern_si_so, 'Error: Sign in (resp. Sign out) must follow Sign out (resp. Sign in)', ['action'])] From 59cbdd3341721226ef6f8ddfdd8f143273077806 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Thu, 12 Jan 2012 15:19:04 +0530 Subject: [PATCH 4/6] [ADD] hr_attendance : Added Test Cases For Hr Attendance bzr revid: mdi@tinyerp.com-20120112094904-b10x1mx4c2uxitzj --- .../hr_attendance/test/attendance_process.yml | 55 ++++++++++++++++++- 1 file changed, 54 insertions(+), 1 deletion(-) diff --git a/addons/hr_attendance/test/attendance_process.yml b/addons/hr_attendance/test/attendance_process.yml index 1d8308c2f45..5c83ba25aa1 100644 --- a/addons/hr_attendance/test/attendance_process.yml +++ b/addons/hr_attendance/test/attendance_process.yml @@ -28,4 +28,57 @@ - !assert {model: hr.employee, id: hr.employee_al, severity: error, string: Employee should be in absent state}: - state == 'absent' - +- + In order to check that first attendance must be Sign In. +- + !python {model: hr.attendance}: | + try: + self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 09:59:25'), action: 'sign_out'}, None) + except Exception, e: + assert e, 'The first attendance must be Sign In' +- + First of all, Employee Sign's In. +- + !record {model: hr.attendance, id: hr_attendance_1}: + employee_id: hr.employee_fp + name: !eval time.strftime('%Y-%m-%d 09:59:25') + action: 'sign_in' +- + Now Employee is going to Sign In prior to First Sign In. +- + !python {model: hr.attendance}: | + try: + self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 08:59:25'), action: 'sign_in'}, None) + except Exception, e: + assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' +- + After that Employee is going to Sign In after First Sign In. +- + !python {model: hr.attendance}: | + try: + self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 10:59:25'), action: 'sign_in'}, None) + except Exception, e: + assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' +- + After two hours, Employee Sign's Out. +- + !record {model: hr.attendance, id: hr_attendance_4}: + employee_id: hr.employee_fp + name: !eval time.strftime('%Y-%m-%d 11:59:25') + action: 'sign_out' +- + Now Employee is going to Sign Out prior to First Sign Out. +- + !python {model: hr.attendance}: | + try: + self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 10:59:25'), action: 'sign_out'}, None) + except Exception, e: + assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' +- + After that Employee is going to Sign Out After First Sign Out. +- + !python {model: hr.attendance}: | + try: + self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 12:59:25'), action: 'sign_out'}, None) + except Exception, e: + assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' From 293ee2f2d457038c14df853733688175d0b2a02f Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 16 Jan 2012 12:15:26 +0530 Subject: [PATCH 5/6] [IMP] hr_attendance : Improved Test Cases. bzr revid: mdi@tinyerp.com-20120116064526-i1o9qwq4nxlv01yx --- .../hr_attendance/test/attendance_process.yml | 40 ++++++++++++++----- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/addons/hr_attendance/test/attendance_process.yml b/addons/hr_attendance/test/attendance_process.yml index 5c83ba25aa1..7d76ee79e74 100644 --- a/addons/hr_attendance/test/attendance_process.yml +++ b/addons/hr_attendance/test/attendance_process.yml @@ -43,42 +43,62 @@ employee_id: hr.employee_fp name: !eval time.strftime('%Y-%m-%d 09:59:25') action: 'sign_in' +- + In order to check that first attendance must be Sign In. +- + !python {model: hr.attendance}: | + import time + try: + self.create(cr, uid, {'employee_id': ref('hr.employee_niv'), 'name': time.strftime('%Y-%m-%d 09:59:25'), 'action': 'sign_out'}, None) + except Exception, e: + assert e[0]=='ValidateError', e +- + First of all, Employee Sign's In. +- + !record {model: hr.attendance, id: hr_attendance_0}: + employee_id: hr.employee_niv + name: !eval time.strftime('%Y-%m-%d 09:59:25') + action: 'sign_in' - Now Employee is going to Sign In prior to First Sign In. - !python {model: hr.attendance}: | + import time try: - self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 08:59:25'), action: 'sign_in'}, None) + self.create(cr, uid, {'employee_id': ref('hr.employee_niv'), 'name': time.strftime('%Y-%m-%d 08:59:25'), 'action': 'sign_in'}, None) except Exception, e: - assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' + assert e[0]=='ValidateError', e - After that Employee is going to Sign In after First Sign In. - !python {model: hr.attendance}: | + import time try: - self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 10:59:25'), action: 'sign_in'}, None) + self.create(cr, uid, {'employee_id': ref('hr.employee_niv'), 'name': time.strftime('%Y-%m-%d 10:59:25'), 'action': 'sign_in'}, None) except Exception, e: - assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' + assert e[0]=='ValidateError', e - After two hours, Employee Sign's Out. - - !record {model: hr.attendance, id: hr_attendance_4}: - employee_id: hr.employee_fp + !record {model: hr.attendance, id: hr_attendance_1}: + employee_id: hr.employee_niv name: !eval time.strftime('%Y-%m-%d 11:59:25') action: 'sign_out' - Now Employee is going to Sign Out prior to First Sign Out. - !python {model: hr.attendance}: | + import time try: - self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 10:59:25'), action: 'sign_out'}, None) + self.create(cr, uid, {'employee_id': ref('hr.employee_niv'), 'name': time.strftime('%Y-%m-%d 10:59:25'), 'action': 'sign_out'}, None) except Exception, e: - assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' + assert e[0]=='ValidateError', e - After that Employee is going to Sign Out After First Sign Out. - !python {model: hr.attendance}: | + import time try: - self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 12:59:25'), action: 'sign_out'}, None) + self.create(cr, uid, {'employee_id': ref('hr.employee_niv'), 'name': time.strftime('%Y-%m-%d 12:59:25'), 'action': 'sign_out'}, None) except Exception, e: - assert e, 'Sign In (resp. Sign Out) must follow Sign Out (resp. Sign In)' + assert e[0]=='ValidateError', e From 7db3e24af994a60cdb26cb8074717aa153e506c8 Mon Sep 17 00:00:00 2001 From: "Divyesh Makwana (Open ERP)" Date: Mon, 16 Jan 2012 12:30:12 +0530 Subject: [PATCH 6/6] [IMP] hr_attendance : Improved Test Cases. bzr revid: mdi@tinyerp.com-20120116070012-8blt2ov12b0qx74b --- addons/hr_attendance/test/attendance_process.yml | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/addons/hr_attendance/test/attendance_process.yml b/addons/hr_attendance/test/attendance_process.yml index 7d76ee79e74..4ad765d65df 100644 --- a/addons/hr_attendance/test/attendance_process.yml +++ b/addons/hr_attendance/test/attendance_process.yml @@ -30,21 +30,6 @@ - state == 'absent' - In order to check that first attendance must be Sign In. -- - !python {model: hr.attendance}: | - try: - self.create(cr, uid, {employee_id: hr.employee_fp, name: time.strftime('%Y-%m-%d 09:59:25'), action: 'sign_out'}, None) - except Exception, e: - assert e, 'The first attendance must be Sign In' -- - First of all, Employee Sign's In. -- - !record {model: hr.attendance, id: hr_attendance_1}: - employee_id: hr.employee_fp - name: !eval time.strftime('%Y-%m-%d 09:59:25') - action: 'sign_in' -- - In order to check that first attendance must be Sign In. - !python {model: hr.attendance}: | import time