+ Dear HR Team,
+
+
+
+ Here are the upcoming Employee Events:
+
+
+
+
+
+
+
+
+
+
+ Regards,
+
+
| Employee Name: | +Employee ID: | +||||
| Job Position: | +Department: | +||||
| Date Of Joining: | +Date Of Birth: | +||||
| Bank Account: | +PAN: | +||||
| UAN: | +ESIC: | +||||
|
+ Pay Period: + + Worked Days: |
+
+
+
+
+ |
+
| Income | +Amount | +Contribution | +Amount | +
|---|---|---|---|
|
+
+
+ + |
+
+
+
+ + |
+
+
+
+ + |
+
+
+
+ + |
+
| + |
+ + Deductions +
+
+ + |
+
+ +
+
+ + |
+ |
| Gross Salary | +Total Deduction | +||
| Net Salary: | +
+ |
+ ||
+ To pay ( only) to - Account : XXXXXXXXXXXX + + +
+NOTE: This is computer generated salary slip. Signature is not required.
+Dear Management,
++ Please find below the attendance summary for the period + %(from_date)s to %(to_date)s. +
+ + %(employee_tables)s + +Regards,
Odoo HR System
| Date | +Check In | +Check Out | +Hours Worked | +
|---|
No attendance data available for this period.
" + + # Final HTML body + body_html = html_template % { + 'from_date': last_monday.strftime('%Y-%m-%d'), + 'to_date': last_sunday.strftime('%Y-%m-%d'), + 'employee_tables': employee_tables_html + } + + # Send email to all management emails + for email in management_emails: + self.env['mail.mail'].create({ + 'email_to': email, + 'subject': f"Weekly Attendance Report: {last_monday} to {last_sunday}", + 'body_html': body_html, + }).send() diff --git a/addons_extensions/weekly_attendance_report/security/ir.model.access.csv b/addons_extensions/weekly_attendance_report/security/ir.model.access.csv new file mode 100644 index 000000000..44a7ad01c --- /dev/null +++ b/addons_extensions/weekly_attendance_report/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id,perm_read,perm_write,perm_create,perm_unlink +access_attendance_weekly_report,access_attendance_weekly_report,model_attendance_weekly_report,,1,1,1,1 diff --git a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py index 2629846cd..b42d201e5 100644 --- a/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py +++ b/third_party_addons/hr_biometric_attendance/models/biometric_device_details.py @@ -79,8 +79,6 @@ class BiometricDeviceDetails(models.Model): device_password = fields.Integer(string='Password', help='Enter the device password') - - def device_connect(self, zk): """Function for connecting the device with Odoo""" try: @@ -172,39 +170,40 @@ class BiometricDeviceDetails(models.Model): if conn: conn.disable_device() self.get_all_users() + # self.action_set_timezone() user = conn.get_users() - # fingers = conn.get_templates() - # for use in user: - # for finger in fingers: - # if finger.uid == use.uid: - # templates = conn.get_user_template(uid=use.uid, - # temp_id=finger.fid, - # user_id=use.user_id) - # hex_data = templates.template.hex() - # # Convert hex data to binary - # binary_data = binascii.unhexlify(hex_data) - # base64_data = base64.b64encode(binary_data).decode( - # 'utf-8') - # employee = self.env['hr.employee'].search( - # [('device_id_num', '=', use.user_id),('company_id', '=', self.env.company.id)],limit=1) - # employee.device_ids |= self - # if str(finger.fid) in employee.fingerprint_ids.mapped( - # 'finger_id'): - # employee.fingerprint_ids.search( - # [('finger_id', '=', finger.fid)]).update({ - # 'finger_template': base64_data, - # }) - # else: - # employee.fingerprint_ids.create({ - # 'finger_template': base64_data, - # 'finger_id': finger.fid, - # 'employee_bio_id': employee.id, - # 'filename': f'{employee.name}-finger-{finger.fid}' - # }) + # get All Fingerprints + fingers = conn.get_templates() + for use in user: + for finger in fingers: + if finger.uid == use.uid: + templates = conn.get_user_template(uid=use.uid, + temp_id=finger.fid, + user_id=use.user_id) + hex_data = templates.template.hex() + # Convert hex data to binary + binary_data = binascii.unhexlify(hex_data) + base64_data = base64.b64encode(binary_data).decode( + 'utf-8') + employee = self.env['hr.employee'].search( + [('device_id_num', '=', use.user_id), ('company_id', '=', self.env.company.id)], + limit=1) + employee.device_ids |= self + if str(finger.fid) in employee.fingerprint_ids.mapped( + 'finger_id'): + employee.fingerprint_ids.search( + [('finger_id', '=', finger.fid)]).update({ + 'finger_template': base64_data, + }) + else: + employee.fingerprint_ids.create({ + 'finger_template': base64_data, + 'finger_id': finger.fid, + 'employee_bio_id': employee.id, + 'filename': f'{employee.name}-finger-{finger.fid}' + }) # get all attendances - print(help(zk.get_attendance)) attendance = conn.get_attendance() - print(attendance) if attendance: filtered_attendance = [] @@ -240,50 +239,54 @@ class BiometricDeviceDetails(models.Model): for uid in user: if uid.user_id == each.user_id: - employee = self.env['hr.employee'].search([ - ('device_id_num', '=', each.user_id), - ('company_id', '=', self.env.company.id) - ], limit=1) + get_user_id = self.env['hr.employee'].search( + [('device_id_num', '=', each.user_id), ('company_id', '=', self.env.company.id)], + limit=1) + check_in_today = hr_attendance.search([( + 'employee_id', '=', get_user_id.id), + ('check_in', '!=', False), ('check_out', '=', False)]) + from datetime import timedelta - if not employee: - continue + # Define the tolerance (10 minutes) + tolerance = timedelta(minutes=10) - # Convert atten_time to datetime + # Convert the atten_time string to a datetime object + + # Calculate the lower and upper bounds with the tolerance atten_time_obj = datetime.datetime.strptime(atten_time, "%Y-%m-%d %H:%M:%S") - # Find existing attendance for today - start_of_day = atten_time_obj.replace(hour=0, minute=0, second=0, microsecond=0) - end_of_day = atten_time_obj.replace(hour=23, minute=59, second=59, microsecond=999999) + # Calculate the lower and upper bounds with the tolerance + lower_bound = atten_time_obj - tolerance + upper_bound = atten_time_obj + tolerance - attendance_today = self.env['hr.attendance'].search([ - ('employee_id', '=', employee.id), - ('check_in', '>=', start_of_day), - ('check_in', '<=', end_of_day) - ], limit=1) + # Ensure the 'check_in' and 'check_out' fields are datetime objects and compare them + next_in = hr_attendance.search([ + ('employee_id', '=', get_user_id.id), + ('check_in', '>=', lower_bound), + ('check_in', '<=', upper_bound) + ]) - # IN logic - if self.display_name == 'IN': - if not attendance_today: - # No attendance yet, create new with check_in only - self.env['hr.attendance'].create({ - 'employee_id': employee.id, - 'check_in': atten_time_obj, + next_out = hr_attendance.search([ + ('employee_id', '=', get_user_id.id), + ('check_out', '>=', lower_bound), + ('check_out', '<=', upper_bound) + ]) + if get_user_id: + if self.display_name == 'IN' and not check_in_today: + if next_in: + continue + hr_attendance.create({ + 'employee_id': get_user_id.id, + 'check_in': atten_time, }) - employee.attendance_state = 'checked_in' - else: - attendance_today.check_out = False - employee.attendance_state = 'checked_in' - continue - - # OUT logic - elif self.display_name != 'IN': - if attendance_today: - # Only update checkout if it's not set or is earlier than current atten_time - if not attendance_today.check_out or attendance_today.check_out < atten_time_obj: - attendance_today.write({ - 'check_out': atten_time_obj, - }) - employee.attendance_state = 'checked_out' + get_user_id.attendance_state = 'checked_in' + elif check_in_today and self.display_name != 'IN': + if fields.Datetime.to_string(check_in_today.check_in) > atten_time or next_out: + continue + check_in_today.write({ + 'check_out': atten_time, + }) + get_user_id.attendance_state = 'checked_out' else: pass @@ -293,7 +296,7 @@ class BiometricDeviceDetails(models.Model): ('company_id', '=', self.env.company.id)]) if not duplicate_atten_ids: zk_attendance.create({ - 'employee_id': employee.id, + 'employee_id': get_user_id.id, 'device_id_num': each.user_id, 'attendance_type': str(1), 'punch_type': '0' if self.display_name == 'IN' else '1', @@ -478,13 +481,12 @@ class BiometricDeviceDetails(models.Model): if conn: try: admin_user_id = 9 # You can implement a method for this - conn.unlock(admin_user_id) # Unlock using the admin user ID + conn.unlock(admin_user_id) # Unlock using the admin user ID except Exception as e: raise UserError(_("Failed to unlock door: %s") % str(e)) else: raise UserError(_("Unable to establish a connection to the device.")) - def get_all_users(self): """Function to get all user's details""" for info in self: @@ -504,7 +506,7 @@ class BiometricDeviceDetails(models.Model): users = conn.get_users() for user in users: employee = self.env['hr.employee'].search( - [('device_id_num', '=', user.user_id) ]) + [('device_id_num', '=', user.user_id)]) if employee: pass # employee.write({ @@ -563,7 +565,7 @@ class BiometricDeviceDetails(models.Model): "group_id: %s\n" "user_id: %s\n" "Here is the debugging information:\n%s\n" - "Try Reqing the device") + "Try Restarting the device") % (candidate_uid, employee.name, privilege, password, group_id, str(candidate_uid), e)) conn.enable_device() @@ -697,7 +699,7 @@ class ZKBioAttendance(Thread): live attendance data. """ - def __init__(self, machine_ip, port_no,password, record): + def __init__(self, machine_ip, port_no, password, record): """Function to Initialize the thread""" Thread.__init__(self) self.machine_ip = machine_ip