From 704ed75a1042c45fbab9f1356d81d796c0959da0 Mon Sep 17 00:00:00 2001 From: raman Date: Wed, 28 May 2025 13:00:38 +0530 Subject: [PATCH 1/2] payslip --- addons_extensions/hr_payroll/__manifest__.py | 7 +- .../hr_payroll/views/ftp_payslip.xml | 152 ++++++++++++++++++ .../hr_payroll/views/hr_payroll_report.xml | 13 ++ 3 files changed, 168 insertions(+), 4 deletions(-) create mode 100644 addons_extensions/hr_payroll/views/ftp_payslip.xml diff --git a/addons_extensions/hr_payroll/__manifest__.py b/addons_extensions/hr_payroll/__manifest__.py index 5e2dd208b..ab74ceec4 100644 --- a/addons_extensions/hr_payroll/__manifest__.py +++ b/addons_extensions/hr_payroll/__manifest__.py @@ -1,9 +1,7 @@ -# Part of Odoo. See LICENSE file for full copyright and licensing details. { 'name': 'Payroll', 'category': 'Human Resources/Payroll', - 'sequence': 290, 'summary': 'Manage your employee payroll records', 'installable': True, 'application': True, @@ -11,7 +9,7 @@ # 'hr_work_entry_contract_enterprise', 'mail', 'web_editor', - 'hr_work_entry_contract', + 'hr_work_entry_contract','web', # 'hr_gantt' ], 'data': [ @@ -59,6 +57,7 @@ 'views/hr_payroll_menu.xml', # 'views/hr_work_entry_views.xml', 'views/hr_work_entry_export_mixin_views.xml', + 'views/ftp_payslip.xml', 'report/hr_contract_history_report_views.xml', 'wizard/hr_payroll_payment_report_wizard.xml', ], @@ -82,6 +81,6 @@ # 'hr_payroll/static/tests/**/*.js', ], }, - 'license': 'OEEL-1', + 'license': 'LGPL-3', 'post_init_hook': '_post_init_hook', } diff --git a/addons_extensions/hr_payroll/views/ftp_payslip.xml b/addons_extensions/hr_payroll/views/ftp_payslip.xml new file mode 100644 index 000000000..21c69b79b --- /dev/null +++ b/addons_extensions/hr_payroll/views/ftp_payslip.xml @@ -0,0 +1,152 @@ + + + + + \ No newline at end of file diff --git a/addons_extensions/hr_payroll/views/hr_payroll_report.xml b/addons_extensions/hr_payroll/views/hr_payroll_report.xml index b130e9ce8..b55cde599 100644 --- a/addons_extensions/hr_payroll/views/hr_payroll_report.xml +++ b/addons_extensions/hr_payroll/views/hr_payroll_report.xml @@ -13,6 +13,19 @@ + + + Payslip Ftp + hr.payslip + qweb-pdf + hr_payroll.report_payslip_ftps + hr_payroll.report_payslip_ftps + 'Payslip - %s' % (object.name) + + report + + + Payslip (Light) hr.payslip From 0e99df97adde1a34c294f7a4c798b26435861ee3 Mon Sep 17 00:00:00 2001 From: raman Date: Fri, 30 May 2025 18:17:16 +0530 Subject: [PATCH 2/2] bio-metric cap issue --- .../models/biometric_device_details.py | 152 +++++++++--------- 1 file changed, 77 insertions(+), 75 deletions(-) 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