From 7c317c494355b4508036f139dbc33a44d7d8316c Mon Sep 17 00:00:00 2001 From: Bakry Date: Mon, 7 Jul 2025 14:21:03 +0300 Subject: [PATCH] fix new Attendance calculation --- .../models/hr_attendance_transactions.py | 875 ++++++++++++------ 1 file changed, 587 insertions(+), 288 deletions(-) diff --git a/odex25_hr/attendances/models/hr_attendance_transactions.py b/odex25_hr/attendances/models/hr_attendance_transactions.py index 1adab0ca2..b4f7dbf7f 100644 --- a/odex25_hr/attendances/models/hr_attendance_transactions.py +++ b/odex25_hr/attendances/models/hr_attendance_transactions.py @@ -12,16 +12,16 @@ class HrAttendanceTransactions(models.Model): _order = 'date DESC' date = fields.Date(string='Day') - lateness = fields.Float(compute='get_hours', widget='float_time') - early_exit = fields.Float(compute='get_hours', widget='float_time') - is_absent = fields.Boolean(string='Absent', compute='get_hours', store=True) + lateness = fields.Float(widget='float_time') + early_exit = fields.Float(widget='float_time') + is_absent = fields.Boolean(string='Absent', store=True) sign_in = fields.Float() sign_out = fields.Float() - approve_exit_out = fields.Boolean(string='Approve Early Exit', compute='get_hours') - approve_lateness = fields.Boolean(string='Approve Lateness', compute='get_hours') + approve_exit_out = fields.Boolean(string='Approve Early Exit',) + approve_lateness = fields.Boolean(string='Approve Lateness',) employee_id = fields.Many2one('hr.employee', 'Employee Name', default=lambda item: item.get_user_id(), index=True) break_duration = fields.Float(string='Break Duration', default=0) - total_absent_hours = fields.Float(compute='get_hours') + total_absent_hours = fields.Float() calendar_id = fields.Many2one('resource.calendar', 'Calendar', readonly=True) office_hours = fields.Float(string='Attending Hours', default=0) official_hours = fields.Float(string='Official Hours', default=0) @@ -41,9 +41,6 @@ class HrAttendanceTransactions(models.Model): has_sign_in = fields.Boolean(readonly=True) has_sign_out = fields.Boolean(readonly=True) - total_mission_hours = fields.Float() - total_permission_hours = fields.Float() - '''to_date = fields.Boolean(string='Today',compute='_attendance_today', store=True) @@ -56,103 +53,318 @@ class HrAttendanceTransactions(models.Model): else: item.to_date = False''' + @api.depends('employee_id', 'plan_hours', 'office_hours') def get_additional_hours(self): for rec in self: rec.additional_hours = 0 + if not rec.sign_in: + rec.office_hours = rec.total_mission_hours + rec.official_hours = rec.total_mission_hours if rec.office_hours > rec.plan_hours: rec.additional_hours = rec.office_hours - rec.plan_hours + # rec.carried_hours = rec.office_hours - rec.plan_hours + def get_shift_timings(self, item): + calendar = item.calendar_id - @api.depends('employee_id') - def get_hours(self): - module = self.env['ir.module.module'].sudo() - official_mission_module = module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')]) - holidays_module = module.search([('state', '=', 'installed'), ('name', '=', 'hr_holidays_public')]) - for item in self: - item.is_absent = False - item.approve_exit_out = False - # item.is_official = False - # item.total_absent_hours = 0 - # item.official_id = False - # item.total_mission_hours = 0.0 - # item.approve_personal_permission = False - # item.personal_permission_id = False - # item.total_permission_hours = 0.0 - item.approve_lateness = False - item.lateness = False - item.early_exit = False - item.total_absent_hours = 0 - - if item.attending_type == 'out_cal' \ - or holidays_module and (item.public_holiday or item.normal_leave) \ - or official_mission_module and item.is_official and item.official_id.mission_type.duration_type == 'days': - item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'break_duration': 0.0, 'is_absent': False}) - if holidays_module and (item.public_holiday or item.normal_leave): - item.write({'is_official': False, 'official_id': False, 'total_mission_hours': 0.0, - 'approve_personal_permission': False, 'personal_permission_id': False, - 'total_permission_hours': 0.0}) + if calendar.is_full_day: + min_sign_in = calendar.full_min_sign_in + full_max_sign_in = calendar.full_max_sign_in + max_sign_out = calendar.full_max_sign_out + working_hours = calendar.working_hours + else: + if item.sequence == 1: + min_sign_in = calendar.shift_one_min_sign_in + full_max_sign_in = calendar.shift_one_max_sign_in + max_sign_out = calendar.shift_one_max_sign_out + working_hours = calendar.shift_one_working_hours + elif item.sequence == 2: + min_sign_in = calendar.shift_two_min_sign_in + full_max_sign_in = calendar.shift_two_max_sign_in + max_sign_out = calendar.shift_two_max_sign_out + working_hours = calendar.shift_two_working_hours else: - # noke - # item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'break_duration': 0.0, 'is_absent': False}) - day_trans = self.search([('date', '=', item.date), - ('attending_type', '=', 'in_cal'), - ('employee_id', '=', item.employee_id.id)]) - working_hours = sum(day_trans.mapped('official_hours')) \ - + sum(day_trans.mapped('total_mission_hours')) \ - + sum(day_trans.mapped('total_permission_hours')) + min_sign_in = max_sign_out = working_hours = None # or default fallback - if not item.public_holiday: - if working_hours < item.calendar_id.end_sign_in and not item.calendar_id.is_flexible \ - or item.calendar_id.is_flexible and working_hours == 0.0: - day_trans.filtered(lambda t: t.public_holiday == False).update({'is_absent': True}) - if working_hours == 0.0 and item.sign_in == 0.0 and item.sign_out == 0.0 and not item.calendar_id.is_flexible: - day_trans.filtered(lambda t: t.public_holiday == False).update({'is_absent': True}) - if item.calendar_id.is_flexible: - item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'official_hours': item.office_hours}) - # if item.temp_lateness: - # item.approve_lateness = True - # if item.temp_exit: - # item.approve_exit_out = True - #################### Fix lateness,exit_out start ####################### - if item.temp_lateness or item.temp_exit: # solve one cases and add other case #TODO - full_max_sign_in = item.calendar_id.full_max_sign_in - full_min_sign_in = item.calendar_id.full_min_sign_in - working_hours = item.calendar_id.working_hours - if not item.calendar_id.is_full_day: - if item.sequence == 1: - full_max_sign_in = item.calendar_id.shift_one_max_sign_in - full_min_sign_in = item.calendar_id.shift_one_min_sign_in - working_hours = item.calendar_id.shift_one_working_hours - if item.sequence == 2: - full_max_sign_in = item.calendar_id.shift_two_max_sign_in - full_min_sign_in = item.calendar_id.shift_two_min_sign_in - working_hours = item.calendar_id.shift_two_working_hours + return min_sign_in, full_max_sign_in , max_sign_out, working_hours - is_late_sign_in = item.sign_in > full_max_sign_in - if item.sign_in < full_min_sign_in: - expected_sign_in = full_min_sign_in - if item.sign_in >= full_min_sign_in and item.sign_in <= full_max_sign_in: - expected_sign_in = item.sign_in - if is_late_sign_in: - expected_sign_in = full_max_sign_in - expected_sign_out = expected_sign_in + working_hours - is_early_sign_out = item.sign_out > 0.0 and item.sign_out < expected_sign_out - item.temp_lateness = item.sign_out > 0.0 and is_late_sign_in and item.sign_in - expected_sign_in or 0 - item.temp_exit = is_early_sign_out and expected_sign_out - item.sign_out or 0 - item.approve_lateness = is_late_sign_in - item.approve_exit_out = is_early_sign_out + def _compute_expected_times(self, item): + """Helper function to compute expected sign-in and sign-out times based on calendar settings.""" + expected_sign_in = 0 + expected_sign_out = 0 - # if item.sign_out == 0.0: - # item.is_absent = True - #################### Fix end ####################### - if item.break_duration and item.calendar_id.break_duration: - # item.write({'break_duration': item.break_duration - item.calendar_id.break_duration}) #TODO - item.write({'break_duration': item.break_duration}) - if item.break_duration < 0: - item.break_duration = 0 - item.lateness = item.temp_lateness - item.early_exit = item.temp_exit + full_max_sign_in = item.calendar_id.full_max_sign_in + full_min_sign_in = item.calendar_id.full_min_sign_in + working_hours = item.calendar_id.working_hours + if not item.calendar_id.is_full_day: + if item.sequence == 1: + full_max_sign_in = item.calendar_id.shift_one_max_sign_in + full_min_sign_in = item.calendar_id.shift_one_min_sign_in + working_hours = item.calendar_id.shift_one_working_hours + elif item.sequence == 2: + full_max_sign_in = item.calendar_id.shift_two_max_sign_in + full_min_sign_in = item.calendar_id.shift_two_min_sign_in + working_hours = item.calendar_id.shift_two_working_hours + + if item.sign_in < full_min_sign_in: + expected_sign_in = full_min_sign_in + elif full_min_sign_in <= item.sign_in <= full_max_sign_in: + expected_sign_in = item.sign_in + elif item.sign_in > full_max_sign_in: + expected_sign_in = full_max_sign_in + + expected_sign_out = expected_sign_in + working_hours + return expected_sign_in, expected_sign_out + + def set_lateness_and_exit(self, item , expected_sign_in=None , expected_sign_out=None): + if not expected_sign_in and not expected_sign_out: + expected_sign_in, expected_sign_out = self._compute_expected_times(item) + + is_late_sign_in = item.sign_in > expected_sign_in + is_early_sign_out = item.sign_out > 0.0 and item.sign_out < expected_sign_out + # Calculate lateness and exit time + temp_lateness = (item.sign_out > 0.0 and is_late_sign_in) and (item.sign_in - expected_sign_in) or 0 + temp_exit = is_early_sign_out and (expected_sign_out - item.sign_out) or 0 + # Determine whether to approve lateness and exit + approve_lateness = is_late_sign_in + approve_exit_out = is_early_sign_out + item.write({ + 'temp_lateness': temp_lateness, + 'lateness': temp_lateness, + 'temp_exit': temp_exit, + 'early_exit': temp_exit, + # 'office_hours': office_hours, + 'approve_lateness': approve_lateness, + 'approve_exit_out': approve_exit_out + }) + + def set_lateness_and_exit_zero(self, item , sign_in=None , sign_out=None): + item.write({'sign_in': sign_in, 'sign_out': sign_out}) + self.set_lateness_and_exit(item) + item.write({'sign_in': 0, 'sign_out': 0}) + + def update_absence_status(self, item): + working_hours = self._calculate_working_hours(item) + item.update({'official_hours': working_hours}) + item.update({'is_absent': False}) + if not item.public_holiday and not item.is_official and not item.approve_personal_permission and not item.normal_leave: + # # Mark as absent if working hours are less than the expected sign-in time + if self._is_absent_due_to_working_hours(item, working_hours) or self._is_absent_due_to_no_sign_in_sign_out(item, working_hours): + # self._update_absent_status(item) + item.update({'is_absent': True}) + if item.official_id and item.sign_in == 0.0 and item.sign_out == 0.0 \ + and not item.public_holiday \ + and not item.approve_personal_permission \ + and not item.normal_leave \ + and item.official_id.mission_type.absent_attendance: + item.update({'is_absent': True}) + + self.get_additional_hours() + + def _get_day_transactions(self, item): + """ Helper method to fetch day transactions for an employee on a given date """ + return self.env['hr.attendance.transaction'].search([ + ('date', '=', item.date), + ('attending_type', '=', 'in_cal'), + ('employee_id', '=', item.employee_id.id) + ]) + + def _calculate_working_hours(self, day_trans): + """ Helper method to calculate total working hours """ + day_trans.update({'office_hours': sum(day_trans.mapped('office_hours')) + sum(day_trans.mapped('total_mission_hours'))}) + return ( + sum(day_trans.mapped('office_hours')) + sum(day_trans.mapped('total_permission_hours')) + ) + + def _is_absent_due_to_working_hours(self, item, working_hours): + """ Check if the employee is absent based on working hours """ + return (working_hours < item.calendar_id.end_sign_in and not item.calendar_id.is_flexible) \ + or (item.calendar_id.is_flexible and working_hours == 0.0) + + def _is_absent_due_to_no_sign_in_sign_out(self, item, working_hours): + """ Check if the employee is absent based on missing sign-in and sign-out """ + return (working_hours == 0.0 and item.sign_in == 0.0 and item.sign_out == 0.0 + and not item.calendar_id.is_flexible) + + def _update_absent_status(self, day_trans): + """ Helper method to mark transactions as absent """ + day_trans.filtered(lambda t: not t.public_holiday and not t.is_official and not t.normal_leave and not t.approve_personal_permission).update({'is_absent': True}) + + def old(self): + # @api.depends('employee_id') + # def get_hours(self): + # module = self.env['ir.module.module'].sudo() + # official_mission_module = module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')]) + # holidays_module = module.search([('state', '=', 'installed'), ('name', '=', 'hr_holidays_public')]) + + # for item in self: + # item.is_absent = False + # item.total_absent_hours = 0 + + # # Skip attendance calculations for holidays, leaves, official missions + # if (item.attending_type == 'out_cal' + # or (holidays_module and (item.public_holiday or item.normal_leave)) + # or (official_mission_module and item.is_official and item.official_id.mission_type.duration_type == 'days')): + + # item.write({ + # 'temp_lateness': 0.0, + # 'temp_exit': 0.0, + # 'break_duration': 0.0, + # 'is_absent': False + # }) + # if holidays_module and (item.public_holiday or item.normal_leave): + # item.write({ + # 'is_official': False, + # 'official_id': False, + # 'total_mission_hours': 0.0, + # 'approve_personal_permission': False, + # 'personal_permission_id': False, + # 'total_permission_hours': 0.0 + # }) + # continue + + # # Normal attendance processing + # day_trans = self.env['hr.attendance.transaction'].search([ + # ('date', '=', item.date), + # ('attending_type', '=', 'in_cal'), + # ('employee_id', '=', item.employee_id.id) + # ]) + + # working_hours = ( + # sum(day_trans.mapped('office_hours')) + + # sum(day_trans.mapped('total_mission_hours')) + + # sum(day_trans.mapped('total_permission_hours')) + # ) + # item.update({'official_hours':working_hours}) + + # if not item.public_holiday: + # if (working_hours < item.calendar_id.end_sign_in and not item.calendar_id.is_flexible) \ + # or (item.calendar_id.is_flexible and working_hours == 0.0): + # day_trans.filtered(lambda t: not t.public_holiday).update({'is_absent': True}) + # if (working_hours == 0.0 and item.sign_in == 0.0 and item.sign_out == 0.0 + # and not item.calendar_id.is_flexible): + # day_trans.filtered(lambda t: not t.public_holiday).update({'is_absent': True}) + + # if item.calendar_id.is_flexible: + # item.write({ + # 'temp_lateness': 0.0, + # 'temp_exit': 0.0, + # 'official_hours': item.office_hours + # }) + + # ################### Break Duration Fix #################### + # if item.break_duration and item.calendar_id.break_duration: + # item.write({'break_duration': item.break_duration}) + + # if item.break_duration < 0: + # item.break_duration = 0 + + # # Call additional hour calculations + # self.get_additional_hours() + + # @api.depends('employee_id') + # def get_hours(self): + # module = self.env['ir.module.module'].sudo() + # official_mission_module = module.search([('state', '=', 'installed'), ('name', '=', 'exp_official_mission')]) + # holidays_module = module.search([('state', '=', 'installed'), ('name', '=', 'hr_holidays_public')]) + # expected_sign_in = 0 + # expected_sign_out = 0 + # for item in self: + # item.is_absent = False + # item.approve_exit_out = False + # # item.is_official = False + # # item.total_absent_hours = 0 + # # item.official_id = False + # # item.total_mission_hours = 0.0 + # # item.approve_personal_permission = False + # # item.personal_permission_id = False + # # item.total_permission_hours = 0.0 + # item.approve_lateness = False + # item.lateness = False + # item.early_exit = False + # item.total_absent_hours = 0 + + # if item.attending_type == 'out_cal' \ + # or holidays_module and (item.public_holiday or item.normal_leave) \ + # or official_mission_module and item.is_official and item.official_id.mission_type.duration_type == 'days': + # item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'break_duration': 0.0, 'is_absent': False}) + # if holidays_module and (item.public_holiday or item.normal_leave): + # item.write({'is_official': False, 'official_id': False, 'total_mission_hours': 0.0, + # 'approve_personal_permission': False, 'personal_permission_id': False, + # 'total_permission_hours': 0.0}) + # else: + # # noke + # # item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'break_duration': 0.0, 'is_absent': False}) + # day_trans = self.env['hr.attendance.transaction'].search([('date', '=', item.date), + # ('attending_type', '=', 'in_cal'), + # ('employee_id', '=', item.employee_id.id)]) + # working_hours = sum(day_trans.mapped('official_hours')) \ + # + sum(day_trans.mapped('total_mission_hours')) \ + # + sum(day_trans.mapped('total_permission_hours')) + + # if not item.public_holiday: + # if working_hours < item.calendar_id.end_sign_in and not item.calendar_id.is_flexible \ + # or item.calendar_id.is_flexible and working_hours == 0.0: + # day_trans.filtered(lambda t: t.public_holiday == False).update({'is_absent': True}) + # if working_hours == 0.0 and item.sign_in == 0.0 and item.sign_out == 0.0 and not item.calendar_id.is_flexible: + # day_trans.filtered(lambda t: t.public_holiday == False).update({'is_absent': True}) + # if item.calendar_id.is_flexible: + # item.write({'temp_lateness': 0.0, 'temp_exit': 0.0, 'official_hours': item.office_hours}) + # # if item.temp_lateness: + # # item.approve_lateness = True + # # if item.temp_exit: + # # item.approve_exit_out = True + # #################### Fix lateness,exit_out start ####################### + # if item.temp_lateness or item.temp_exit: # solve one cases and add other case #TODO + # full_max_sign_in = item.calendar_id.full_max_sign_in + # full_min_sign_in = item.calendar_id.full_min_sign_in + # working_hours = item.calendar_id.working_hours + # if not item.calendar_id.is_full_day: + # if item.sequence == 1: + # full_max_sign_in = item.calendar_id.shift_one_max_sign_in + # full_min_sign_in = item.calendar_id.shift_one_min_sign_in + # working_hours = item.calendar_id.shift_one_working_hours + # if item.sequence == 2: + # full_max_sign_in = item.calendar_id.shift_two_max_sign_in + # full_min_sign_in = item.calendar_id.shift_two_min_sign_in + # working_hours = item.calendar_id.shift_two_working_hours + + # is_late_sign_in = item.sign_in > full_max_sign_in + # if item.sign_in < full_min_sign_in: + # expected_sign_in = full_min_sign_in + # if item.sign_in >= full_min_sign_in and item.sign_in <= full_max_sign_in: + # expected_sign_in = item.sign_in + # if is_late_sign_in: + # expected_sign_in = full_max_sign_in + # expected_sign_out = expected_sign_in + working_hours + # is_early_sign_out = item.sign_out > 0.0 and item.sign_out < expected_sign_out + # item.temp_lateness = item.sign_out > 0.0 and is_late_sign_in and item.sign_in - expected_sign_in or 0 + # item.temp_exit = is_early_sign_out and expected_sign_out - item.sign_out or 0 + # item.approve_lateness = is_late_sign_in + # item.approve_exit_out = is_early_sign_out + + # # if item.sign_out == 0.0: + # # item.is_absent = True + # #################### Fix end ####################### + # if item.break_duration and item.calendar_id.break_duration: + # # item.write({'break_duration': item.break_duration - item.calendar_id.break_duration}) #TODO + # item.write({'break_duration': item.break_duration}) + # if item.break_duration < 0: + # item.break_duration = 0 + # item.manage_mission( + # item.id, + # self.convert_float_2time(expected_sign_in, fields.Date.from_string(item.date)), + # self.convert_float_2time(expected_sign_out, fields.Date.from_string(item.date)), + # self.convert_float_2time(item.sign_in,fields.Date.from_string(item.date)), + # self.convert_float_2time(item.sign_out,fields.Date.from_string(item.date)), + # False + # ) + # item.lateness = item.temp_lateness + # item.early_exit = item.temp_exit + # self.get_additional_hours() + pass + def get_sign_time(self, sign): ''' Func: return time as float considering timezone(fixed 3)''' @@ -161,15 +373,15 @@ class HrAttendanceTransactions(models.Model): TIME_ZONE_FORMAT = '%Y-%m-%d %H:%M:%S.%f' sign_offsit = datetime.strptime(str(sign), DATETIME_FORMAT) + timedelta(hours=3) sign_zone = datetime.strptime(sign_offsit.strftime(TIME_ZONE_FORMAT), TIME_ZONE_FORMAT) - return round(datetime.strptime(sign_zone.strftime(TIME_FORMAT), TIME_FORMAT).time().hour + + return round(datetime.strptime(sign_zone.strftime(TIME_FORMAT), TIME_FORMAT).time().hour + datetime.strptime(sign_zone.strftime(TIME_FORMAT), TIME_FORMAT).time().minute / 60.0, 2), sign_zone def convert_float_2time(self, time, date=None): hour, minute = divmod(time * 60, 60) if date: if not isinstance(date, datetime): date = fields.Datetime.from_string(date) - return date.replace(hour=int(hour), minute=int(minute), second=0) - timedelta(hours=3) - return hour, minute + return date.replace(hour=int(hour), minute=int(round(minute)), second=0) - timedelta(hours=3) + return hour, round(minute) def get_day_timing(self, calendar, weekday, wkd_date): planed_hours = {'one': 0, 'two': 0} @@ -186,10 +398,10 @@ class HrAttendanceTransactions(models.Model): time_list = [0 for i in range(8)] one_sp_timing = self.get_speacial_day_timing(calendar, weekday, wkd_date, 'one') planed_hours['one'] = (one_sp_timing and one_sp_timing.working_hours or calendar.shift_one_working_hours) \ - - calendar.shift_one_break_duration + -calendar.shift_one_break_duration two_sp_timing = self.get_speacial_day_timing(calendar, weekday, wkd_date, 'two') planed_hours['two'] = (two_sp_timing and two_sp_timing.working_hours or calendar.shift_two_working_hours) \ - - calendar.shift_two_break_duration + -calendar.shift_two_break_duration time_list[0] = one_sp_timing and one_sp_timing.start_sign_in or calendar.shift_one_min_sign_in time_list[1] = one_sp_timing and one_sp_timing.end_sign_in or calendar.shift_one_max_sign_in time_list[2] = one_sp_timing and one_sp_timing.start_sign_out or calendar.shift_one_min_sign_out @@ -319,7 +531,7 @@ class HrAttendanceTransactions(models.Model): out_dt = fields.Datetime.from_string(out.name) if out_dt < next_min_in_dt: one_out_dt = out_dt - checkout_device = out.device_id if at_device else False + checkout_device = out.device_id linked_out_ids.append(out.id) signed = True else: @@ -419,10 +631,19 @@ class HrAttendanceTransactions(models.Model): return {'shift': shift_dict, 'out_ids': linked_out_ids, 'creep': creep} def manage_permission(self, trans_id, shift_in, shift_out, sign_in, sign_out, breaks, state=None): - trans, feedback = self.browse(trans_id)[0], [] + print(trans_id, "*********************manage_permission***********************************") + trans, feedback = self.browse(trans_id)[0], [] + # حساب التوقيع المتوقع للدخول والخروج بناء علي بصمة الدخول + expected_sign_in, expected_sign_out = self._compute_expected_times(trans) + # الحصول على أوقات الحضور والانصراف المسموح بها وعدد ساعات العمل + full_min_sign_in, full_max_sign_in, full_max_sign_out, working_hours = self.get_shift_timings(trans) + # تحديد الشفت من اعلي زمن دخول الي اعلي زمن خروج للبحث عن اذن خلال الشفت + shift_in = self.convert_float_2time(full_min_sign_in, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(full_max_sign_out, fields.Date.from_string(trans.date)) + # إعادة تعيين أي بيانات إذن شخصي قديمة if trans.personal_permission_id: - trans.update( - {'approve_personal_permission': False, 'personal_permission_id': False, 'total_permission_hours': 0.0}) + trans.update({'approve_personal_permission': False, 'personal_permission_id': False, 'total_permission_hours': 0.0}) + # البحث عن الأذونات الشخصية المعتمدة في نفس يوم الحضور permissions = self.env['hr.personal.permission'].search( [('state', '=', 'approve'), ('employee_id', '=', trans.employee_id.id), '|', '|', @@ -431,69 +652,100 @@ class HrAttendanceTransactions(models.Model): '&', ('date_from', '<=', str(shift_in)), ('date_to', '>=', str(shift_out)) ]) if permissions: + # إعادة ضبط أوقات بداية ونهاية الشفت بناءً على التوقعات من بصمة الدخول لمعالجة حساب الاذن ان لايتعدي الاذن فتره خارج الدوام + shift_in = self.convert_float_2time(expected_sign_in, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(expected_sign_out, fields.Date.from_string(trans.date)) for perm in permissions: perm_time = 0 + perm_time = 0 + time_perm_df = self.get_sign_time(perm.date_from)[0] + time_perm_dt = self.get_sign_time(perm.date_to)[0] perm_df = fields.Datetime.from_string(perm.date_from) perm_dt = fields.Datetime.from_string(perm.date_to) - if trans.sign_in == 0.0 or trans.sign_out == 0.0: - perm_dic = {'approve_personal_permission': True, - 'personal_permission_id': perm.id, - 'total_permission_hours': perm.duration} + # معالجة حالات الغياب الكامل اذا كان هناك اذن ,يتم حساب التاخير قبل وقت بداية الاذن ويتم حساب الخروج المبكر بعد نهاية الاذن + if trans.sign_in == 0.0 and trans.sign_out == 0.0: + total_permission_hours = min (perm.duration, trans.plan_hours) + trans.set_lateness_and_exit_zero(trans, time_perm_df, time_perm_dt) + perm_dic = {'approve_personal_permission': True, 'personal_permission_id': perm.id, 'total_permission_hours': total_permission_hours } if state != 'check': trans.update(perm_dic) if state is not None: - # perm_dic.update({ 'personal_permission_id': perm.id,'perm_start': perm_start, 'perm_end': perm_end}) - feedback.append( - {'perm_id': perm.id, 'perm_start': perm_df, 'perm_end': perm_dt, 'type': 'all'}) + feedback.append({'perm_id': perm.id, 'perm_start': perm_df, 'perm_end': perm_dt, 'type': 'all'}) continue - if trans.temp_lateness: - if perm_df <= shift_in and perm_dt >= sign_in: - perm_time = round((sign_in - shift_in).seconds / 60 / 60, 2) - perm_start, perm_end = shift_in, sign_in - elif perm_df <= shift_in and perm_dt < sign_in and perm_dt > shift_in: - perm_time = round((perm_dt - shift_in).seconds / 60 / 60, 2) - perm_start, perm_end = shift_in, perm_dt - elif (perm_df > shift_in and perm_dt < sign_in) or sign_in == 0.0 or sign_out == 0.0: - perm_time = round((perm_dt - perm_df).seconds / 60 / 60, 2) - perm_start, perm_end = perm_df, perm_dt - elif perm_df < sign_in and perm_df > shift_in and perm_dt >= sign_in: - perm_time = round((sign_in - perm_df).seconds / 60 / 60, 2) - perm_start, perm_end = perm_df, sign_in - if perm_time: - perm_dic = {'approve_personal_permission': True, - 'personal_permission_id': perm.id, - 'total_permission_hours': perm_time, - 'temp_lateness': trans.temp_lateness - perm_time, - } - perm_time = 0 - if state != 'check': trans.update(perm_dic) - if state is not None: - feedback.append( - {'perm_id': perm.id, 'perm_start': perm_start, 'perm_end': perm_end, 'type': 'late'}) - if trans.temp_exit: - if perm_df <= sign_out and perm_dt >= shift_out: - perm_time = round((shift_out - sign_out).seconds / 60 / 60, 2) - perm_start, perm_end = sign_out, shift_out - elif perm_df <= sign_out and perm_dt < shift_out and perm_dt > sign_out: - perm_time = round((perm_dt - sign_out).seconds / 60 / 60, 2) - perm_start, perm_end = sign_out, perm_dt - elif perm_df > sign_out and perm_dt >= shift_out and perm_df < shift_out: - perm_time = round((shift_out - perm_df).seconds / 60 / 60, 2) - perm_start, perm_end = perm_df, shift_out - elif perm_df > sign_out and perm_dt < shift_out: - perm_time = round((perm_dt - perm_df).seconds / 60 / 60, 2) - perm_start, perm_end = perm_df, perm_dt - if perm_time: - perm_dic = {'approve_personal_permission': True, - 'personal_permission_id': perm.id, - 'total_permission_hours': perm_time, - 'temp_exit': trans.temp_exit - perm_time, - } - perm_time = 0 - if state != 'check': trans.update(perm_dic) - if state is not None: - feedback.append( - {'perm_id': perm.id, 'perm_start': perm_start, 'perm_end': perm_end, 'type': 'exit'}) - if breaks: + elif trans.sign_in == 0.0 or trans.sign_out == 0.0: + total_permission_hours = min (perm.duration, trans.plan_hours) + perm_dic = {'approve_personal_permission': True, 'personal_permission_id': perm.id, 'total_permission_hours': total_permission_hours } + if state != 'check': trans.update(perm_dic) + if state is not None: + feedback.append({'perm_id': perm.id, 'perm_start': perm_df, 'perm_end': perm_dt, 'type': 'all'}) + continue + # Handling lateness and exit time adjustments + elif trans.temp_lateness or trans.temp_exit: + # التحقق مما إذا كان وقت بداية الإذن الشخصي (time_perm_df) + # يقع بعد بداية الدوام المسموح بها (full_min_sign_in)، + # وقبل توقيت توقيع الدخول الفعلي (trans.sign_in)، + # وضمن فترة الحضور المسموح بها (حتى full_max_sign_in). + # لحساب دوام الموظف من بداية الاذن الي نهاية الاذن + ساعات العمل بمعني الاذن فتره صباحية قبل بصمة دخول الموظف + if time_perm_df < trans.sign_in and time_perm_df > full_min_sign_in and time_perm_df <= full_max_sign_in: + trans.set_lateness_and_exit(trans, time_perm_df, time_perm_df + working_hours) # expected_sign_out) + # اعاده احتساب الشفت , بما ان الاذن فتره صباحية اذا الشفت يبداء من بداية الاذن الي بداية الاذن +ساعات العمل + shift_in = self.convert_float_2time(time_perm_df, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(time_perm_df + working_hours, fields.Date.from_string(trans.date)) + if trans.temp_lateness: + if perm_df <= shift_in and perm_dt >= sign_in: + perm_time = round((sign_in - shift_in).seconds / 3600 , 2) + perm_start, perm_end = shift_in, sign_in + elif perm_df <= shift_in and perm_dt < sign_in and perm_dt > shift_in: + perm_time = round((perm_dt - shift_in).seconds / 3600 , 2) + perm_start, perm_end = shift_in, perm_dt + elif (perm_df > shift_in and perm_dt < sign_in) or sign_in == 0.0 or sign_out == 0.0: + perm_time = round((perm_dt - perm_df).seconds / 3600 , 2) + perm_start, perm_end = perm_df, perm_dt + elif perm_df < sign_in and perm_df > shift_in and perm_dt >= sign_in: + perm_time = round((sign_in - perm_df).seconds / 3600 , 2) + perm_start, perm_end = perm_df, sign_in + if perm_time: + perm_lateness_remaining = trans.lateness - perm_time + perm_dic = {'approve_personal_permission': True, + 'personal_permission_id': perm.id, + 'total_permission_hours': perm_time, + 'temp_lateness': max(perm_lateness_remaining , 0), + 'lateness': max (perm_lateness_remaining , 0), + 'approve_lateness': perm_lateness_remaining > 0 + } + + perm_time = 0 + if state != 'check': trans.update(perm_dic) + if state is not None: + feedback.append({'perm_id': perm.id, 'perm_start': perm_start, 'perm_end': perm_end, 'type': 'late'}) + if trans.temp_exit: + if perm_df <= sign_out and perm_dt >= shift_out: + perm_time = round((shift_out - sign_out).seconds / 60 / 60, 2) + perm_start, perm_end = sign_out, shift_out + elif perm_df <= sign_out and perm_dt < shift_out and perm_dt > sign_out: + perm_time = round((perm_dt - sign_out).seconds / 60 / 60, 2) + perm_start, perm_end = sign_out, perm_dt + elif perm_df > sign_out and perm_dt >= shift_out and perm_df < shift_out: + perm_time = round((shift_out - perm_df).seconds / 60 / 60, 2) + perm_start, perm_end = perm_df, shift_out + elif perm_df > sign_out and perm_dt < shift_out: + perm_time = round((perm_dt - perm_df).seconds / 60 / 60, 2) + perm_start, perm_end = perm_df, perm_dt + if perm_time: + exit_remaining = trans.early_exit - perm_time + perm_dic = { + 'total_permission_hours': perm_time, + 'personal_permission_id': perm.id, + 'approve_personal_permission': True, + 'temp_exit': max(exit_remaining, 0), + 'early_exit':max(exit_remaining, 0), + 'approve_exit_out': exit_remaining > 0 + } + perm_time = 0 + if state != 'check': trans.update(perm_dic) + if state is not None: + feedback.append( + {'perm_id': perm.id, 'perm_start': perm_start, 'perm_end': perm_end, 'type': 'exit'}) + elif breaks: for brk in breaks: if not (perm_df < perm_dt and brk['break_start'] < brk['break_end']): continue if brk['break_start'] <= perm_df and brk['break_end'] >= perm_dt: @@ -520,133 +772,171 @@ class HrAttendanceTransactions(models.Model): if state is not None: feedback.append( {'perm_id': perm.id, 'perm_start': perm_start, 'perm_end': perm_end, - 'type': 'break'}) - perm_time = 0 + 'type': 'break'}) if state is not None: return feedback - + def manage_mission(self, trans_id, shift_in, shift_out, sign_in, sign_out, breaks, state=None): - trans, feedback = self.browse(trans_id)[0], [] + print(trans_id, "*********************manage_mission***********************************") + trans , feedback = self.browse(trans_id)[0] , [] + expected_sign_in, expected_sign_out = self._compute_expected_times(trans) + full_min_sign_in, full_max_sign_in, full_max_sign_out, working_hours = self.get_shift_timings(trans) + shift_in = self.convert_float_2time(full_min_sign_in, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(full_max_sign_out, fields.Date.from_string(trans.date)) if trans.official_id: trans.update({'is_official': False, 'official_id': False, 'total_mission_hours': 0.0}) date_from_time = (shift_in + timedelta(hours=3)).time() date_to_time = (shift_out + timedelta(hours=3)).time() hour_from = date_from_time.hour + date_from_time.minute / 60.0 - hour_to = date_to_time.hour + date_to_time.minute / 60.0 - missions = self.env['hr.official.mission'].search([('state', '=', 'approve'), - ('employee_ids.employee_id', 'in', - [trans.employee_id.id]), - ('date_from', '<=', str(shift_in.date())), - ('date_to', '>=', str(shift_in.date())), - '|', '|', - '&', ('hour_from', '<=', hour_from), - ('hour_to', '>=', hour_from), - '&', ('hour_from', '<=', hour_to), - ('hour_to', '>=', hour_to), - '&', ('hour_from', '>=', hour_from), - ('hour_to', '<=', hour_to), - ]) + hour_to = date_to_time.hour + date_to_time.minute / 60 + missions = self.env['hr.official.mission'].search([ + ('state', '=', 'approve'), + ('employee_ids.employee_id', 'in', [trans.employee_id.id]), + ('employee_ids.date_from', '<=', str(shift_in.date())), + ('employee_ids.date_to', '>=', str(shift_in.date())), + '|', '|', + '&', ('employee_ids.hour_from', '<=', hour_from), ('employee_ids.hour_to', '>=', hour_from), + '&', ('employee_ids.hour_from', '<=', hour_to), ('employee_ids.hour_to', '>=', hour_to), + '&', ('employee_ids.hour_from', '>=', hour_from), ('employee_ids.hour_to', '<=', hour_to), + ]) if missions: - for mission in missions: + shift_in = self.convert_float_2time(expected_sign_in, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(expected_sign_out, fields.Date.from_string(trans.date)) + for mission in missions: emp_mission = mission.employee_ids.filtered(lambda m: m.employee_id.id == trans.employee_id.id)[0] + mission_df = self.convert_float_2time(emp_mission.hour_from, str(trans.date)) + mission_dt = self.convert_float_2time(emp_mission.hour_to, str(trans.date)) + temp_hour = 0 miss_time = 0 - # mission_date = datetime.combine(fields.Datetime.from_string(mission.date), datetime.min.time()) - mission_df = self.convert_float_2time(emp_mission.hour_from, emp_mission.date_from) - mission_dt = self.convert_float_2time(emp_mission.hour_to, emp_mission.date_from) - if trans.sign_in == 0.0 or trans.sign_out == 0.0: - ## chick plan hours and mision hours - mission_hours = mission.hour_duration - if trans.plan_hours < mission.hour_duration: - mission_hours = trans.plan_hours - miss_dic = {'is_official': True, - 'official_id': mission.id, - 'total_mission_hours': mission_hours #mission.hour_duration, - } - if state != 'check': trans.update(miss_dic) - if state is not None: - feedback.append( - {'mission_id': mission.id, 'miss_start': mission_df, 'miss_end': mission_dt, 'type': 'all'}) + # If no sign_in or sign_out, update official mission hours + # if trans.sign_in == 0.0 or trans.sign_out == 0.0: + if trans.sign_in == 0.0 and trans.sign_out == 0.0: + mission_hours = min(trans.plan_hours, emp_mission.hours) + if not mission.mission_type.absent_attendance: + trans.set_lateness_and_exit_zero(trans, emp_mission.hour_from, emp_mission.hour_to) + miss_dic = {'is_official': True, 'official_id': mission.id, 'total_mission_hours': mission_hours} + if state != 'check':trans.update(miss_dic) + if state is not None:feedback.append({'mission_id': mission.id, 'miss_start': mission_df, 'miss_end': mission_dt, 'type': 'all'}) continue - if trans.temp_lateness: - if mission_df <= shift_in and mission_dt >= sign_in: - miss_time = round((sign_in - shift_in).seconds / 60 / 60, 2) - miss_start, miss_end = shift_in, sign_in - elif mission_df <= shift_in and mission_dt < sign_in and mission_dt > shift_in: - miss_time = round((mission_dt - shift_in).seconds / 60 / 60, 2) - miss_start, miss_end = shift_in, mission_dt - elif (mission_df > shift_in and mission_dt < sign_in) or sign_in == 0.0 or sign_out == 0.0: - miss_time = round((mission_dt - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, mission_dt - elif mission_df < sign_in and mission_df > shift_in and mission_dt >= sign_in: - miss_time = round((sign_in - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, sign_in - if miss_time: - miss_dic = {'is_official': True, - 'official_id': mission.id, - 'total_mission_hours': miss_time, - 'temp_lateness': trans.temp_lateness - miss_time, - } - miss_time = 0 - if state != 'check': trans.update(miss_dic) - if state is not None: - # perm_dic.update({ 'personal_permission_id': perm.id,'perm_start': perm_start, 'perm_end': perm_end}) - feedback.append( - {'mission_id': mission.id, 'miss_start': miss_start, 'miss_end': miss_end, - 'type': 'late'}) - if trans.temp_exit: - if mission_df <= sign_out and mission_dt >= shift_out: - miss_time = round((shift_out - sign_out).seconds / 60 / 60, 2) - miss_start, miss_end = sign_out, shift_out - elif mission_df <= sign_out and mission_dt < shift_out and mission_dt > sign_out: - miss_time = round((mission_dt - sign_out).seconds / 60 / 60, 2) - miss_start, miss_end = sign_out, mission_dt - elif mission_df > sign_out and mission_dt >= shift_out and mission_df < shift_out: - miss_time = round((shift_out - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, shift_out - elif mission_df > sign_out and mission_dt < shift_out: - miss_time = round((mission_dt - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, mission_dt - if miss_time: - miss_dic = {'is_official': True, - 'official_id': mission.id, - 'total_mission_hours': miss_time, - 'temp_exit': trans.temp_exit - miss_time, - } - miss_time = 0 - if state != 'check': trans.update(miss_dic) - if state is not None: - feedback.append( - {'mission_id': mission.id, 'miss_start': miss_start, 'miss_end': miss_end, - 'type': 'exit'}) - if breaks: - for brk in breaks: - if not (mission_df < mission_dt and brk['break_start'] < brk['break_end']): continue - if brk['break_start'] <= mission_df and brk['break_end'] >= mission_dt: - miss_time = round((mission_dt - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, mission_dt - elif brk['break_start'] > mission_df and brk['break_end'] < mission_dt: - miss_time = round((brk['break_end'] - brk['break_start']).seconds / 60 / 60, 2) - miss_start, miss_end = brk['break_start'], brk['break_end'] - elif brk['break_start'] <= mission_df and brk['break_end'] < mission_dt and brk[ - 'break_end'] > mission_df: - miss_time = round((brk['break_end'] - mission_df).seconds / 60 / 60, 2) - miss_start, miss_end = mission_df, brk['break_end'] - elif brk['break_start'] > mission_df and brk['break_end'] >= mission_dt and brk[ - 'break_start'] < mission_dt: - miss_time = round((mission_dt - brk['break_start']).seconds / 60 / 60, 2) - miss_start, miss_end = brk['break_start'], mission_dt - if miss_time: - miss_dic = {'is_official': True, - 'official_id': mission.id, - 'total_mission_hours': miss_time, - 'break_duration': trans.break_duration - miss_time, - } - if state != 'check': trans.update(miss_dic) + elif trans.sign_in == 0.0 or trans.sign_out == 0.0: + mission_hours = min(trans.plan_hours, emp_mission.hours) + miss_dic = {'is_official': True, 'official_id': mission.id, 'total_mission_hours': mission_hours, } + if state != 'check':trans.update(miss_dic) + if state is not None:feedback.append({'mission_id': mission.id, 'miss_start': mission_df, 'miss_end': mission_dt, 'type': 'all'}) + continue + # Handling lateness and exit time adjustments + elif trans.temp_lateness or trans.temp_exit: + if emp_mission.hour_from <= trans.sign_in and emp_mission.hour_from >= full_min_sign_in and emp_mission.hour_from <= full_max_sign_in: + temp_sign_out = emp_mission.hour_from + working_hours + trans.set_lateness_and_exit(trans, emp_mission.hour_from, temp_sign_out) # expected_sign_out) + shift_in = self.convert_float_2time(emp_mission.hour_from, fields.Date.from_string(trans.date)) + shift_out = self.convert_float_2time(emp_mission.hour_from + working_hours, fields.Date.from_string(trans.date)) + if temp_sign_out <= trans.sign_out <= full_max_sign_out: + temp_hour = trans.sign_out - temp_sign_out + if temp_sign_out < trans.sign_out >= full_max_sign_out: + temp_hour = full_max_sign_out - temp_sign_out + # Handle lateness + if trans.temp_lateness: + if mission_df <= shift_in and mission_dt >= sign_in: + miss_time_late = round((sign_in - shift_in).seconds / 3600, 2) + miss_start_late, miss_end_late = shift_in, sign_in + elif mission_df <= shift_in and mission_dt < sign_in and mission_dt > shift_in: + miss_time_late = round((mission_dt - shift_in).seconds / 3600, 2) + miss_start_late, miss_end_late = shift_in, mission_dt + elif mission_df > shift_in and mission_dt < sign_in: + miss_time_late = round((mission_dt - mission_df).seconds / 3600, 2) + miss_start_late, miss_end_late = mission_df, mission_dt + elif mission_df < sign_in and mission_df > shift_in and mission_dt >= sign_in: + miss_time_late = round((sign_in - mission_df).seconds / 3600, 2) + miss_start_late, miss_end_late = mission_df, sign_in + else: + miss_time_late = 0 + if miss_time_late : + miss_dic = {'is_official': True, 'official_id': mission.id} + lateness_remaining = (trans.lateness - miss_time_late) - temp_hour + miss_dic.update({ + 'total_mission_hours': miss_time_late, + 'temp_lateness': max(lateness_remaining , 0), + 'lateness': max (lateness_remaining , 0), + 'approve_lateness': lateness_remaining > 0 + }) + if state != 'check': + trans.update(miss_dic) if state is not None: - feedback.append( - {'mission_id': mission.id, 'miss_start': miss_start, 'miss_end': miss_end, - 'type': 'break'}) - miss_time = 0 - if state is not None: return feedback + feedback.append({ + 'mission_id': mission.id, + 'miss_start': miss_start_late, + 'miss_end': miss_end_late, + 'type': 'late' + }) + # Handle early exit + if trans.temp_exit: + if mission_df <= sign_out and mission_dt >= shift_out: + miss_time_exit = round((shift_out - sign_out).seconds / 3600, 2) + miss_start_exit, miss_end_exit = sign_out, shift_out + elif mission_df <= sign_out and mission_dt < shift_out and mission_dt > sign_out: + miss_time_exit = round((mission_dt - sign_out).seconds / 3600, 2) + miss_start_exit, miss_end_exit = sign_out, mission_dt + elif mission_df > sign_out and mission_dt >= shift_out and mission_df < shift_out: + miss_time_exit = round((shift_out - mission_df).seconds / 3600, 2) + miss_start_exit, miss_end_exit = mission_df, shift_out + elif mission_df > sign_out and mission_dt < shift_out: + miss_time_exit = round((mission_dt - mission_df).seconds / 3600, 2) + miss_start_exit, miss_end_exit = mission_df, mission_dt + else: + miss_time_exit = 0 + if miss_time_exit : + miss_dic = {'is_official': True, 'official_id': mission.id} + exit_remaining = trans.early_exit - miss_time_exit + miss_dic.update({ + 'total_mission_hours': miss_time_exit, + 'temp_exit': exit_remaining if exit_remaining > 0 else 0, + 'early_exit':exit_remaining if exit_remaining > 0 else 0, + 'approve_exit_out': exit_remaining > 0 + }) + + if state != 'check': + trans.update(miss_dic) + if state is not None: + feedback.append({ + 'mission_id': mission.id, + 'miss_start': miss_start_exit, + 'miss_end': miss_end_exit, + 'type': 'exit' + }) + # Handling breaks + elif breaks: + for brk in breaks: + if mission_df < mission_dt and brk['break_start'] < brk['break_end']: + if brk['break_start'] <= mission_df and brk['break_end'] >= mission_dt: + miss_time = round((mission_dt - mission_df).seconds / 3600, 2) + miss_start, miss_end = mission_df, mission_dt + elif brk['break_start'] > mission_df and brk['break_end'] < mission_dt: + miss_time = round((brk['break_end'] - brk['break_start']).seconds / 3600, 2) + miss_start, miss_end = brk['break_start'], brk['break_end'] + elif brk['break_start'] <= mission_df and brk['break_end'] < mission_dt and brk['break_end'] > mission_df: + miss_time = round((brk['break_end'] - mission_df).seconds / 3600, 2) + miss_start, miss_end = mission_df, brk['break_end'] + elif brk['break_start'] > mission_df and brk['break_end'] >= mission_dt and brk['break_start'] < mission_dt: + miss_time = round((mission_dt - brk['break_start']).seconds / 3600, 2) + miss_start, miss_end = brk['break_start'], mission_dt + + if miss_time: + miss_dic = { + 'is_official': True, + 'official_id': mission.id, + 'total_mission_hours': miss_time, + 'break_duration': trans.break_duration - miss_time, + } + if state != 'check': + trans.update(miss_dic) + if state is not None: + feedback.append( + {'mission_id': mission.id, 'miss_start': miss_start, 'miss_end': miss_end, + 'type': 'break'} + ) + trans.update_absence_status(trans) + if state is not None: + return feedback @api.model def process_attendance_scheduler_queue(self, attendance_date=None, attendance_employee=None, send_email=False): @@ -654,13 +944,13 @@ class HrAttendanceTransactions(models.Model): ('name', '=', 'to_attendance_device_custom')]) \ and True or False attendance_pool = self.env['attendance.attendance'] - ##remove today from not to day in attendance + # #remove today from not to day in attendance today = datetime.now().date() - not_today = attendance_pool.search([('is_today', '=', True),('action_date', '<', today)]) + not_today = attendance_pool.search([('is_today', '=', True), ('action_date', '<', today)]) if not_today: for att in not_today: att._compute_is_today() - ##end + # #end low_date = (datetime.utcnow()).date() if not attendance_date else attendance_date if isinstance(low_date, datetime): low_date = low_date.date() @@ -690,7 +980,7 @@ class HrAttendanceTransactions(models.Model): for employee in employee_list: hire = employee.contract_id.hiring_date if employee.contract_id.hiring_date else employee.contract_id.date_start - if not hire: + if not hire: continue datetime_object = datetime.strptime(str(hire), '%Y-%m-%d').date() if employee.contract_id.state != 'end_contract' and datetime_object <= day_item: @@ -698,7 +988,8 @@ class HrAttendanceTransactions(models.Model): day_item = datetime_object else: day_item = low_date - check_trans = self.search([('date', '=', day_item), ('employee_id', '=', employee.id)]) + check_trans = self.env['hr.attendance.transaction'].search([ + ('date', '=', day_item), ('employee_id', '=', employee.id)]) emp_calendar = check_trans and check_trans[0].calendar_id and check_trans[0].calendar_id \ or employee.resource_calendar_id if emp_calendar.is_full_day: @@ -794,6 +1085,7 @@ class HrAttendanceTransactions(models.Model): exist_trans.filtered( lambda t: t.sequence == 1 and t.attending_type == 'out_cal') or False if one_extra_dlt and not shift_one_extra: one_extra_dlt.unlink() + one_trans.set_lateness_and_exit(one_trans) if personal_permission_module: if one_trans.temp_lateness or one_trans.temp_exit or one_breaks \ or one_trans.sign_in == 0.0 or one_trans.sign_out == 0.0: @@ -968,10 +1260,12 @@ class HrAttendanceTransactions(models.Model): if personal_permission_module: if one_trans.temp_lateness or one_trans.temp_exit or one_breaks \ or one_trans.sign_in == 0.0 or one_trans.sign_out == 0.0: + one_perm = self.manage_permission(one_trans.id, one_min_in_dt, one_max_out_dt, one_in_dt, one_out_dt, one_breaks, 'inform') if two_trans.temp_lateness or two_trans.temp_exit or two_breaks \ or two_trans.sign_in == 0.0 or two_trans.sign_out == 0.0: + two_perm = self.manage_permission(two_trans.id, two_min_in_dt, two_max_out_dt, two_in_dt, two_out_dt, two_breaks, 'inform') if official_mission_module: @@ -1068,7 +1362,10 @@ class HrAttendanceTransactions(models.Model): tr.plan_hours = planed_hours['one'] elif tr.sequence == 2: tr.plan_hours = planed_hours['two'] - + + tr.update_absence_status(tr) + + # tr.update_absence_status(tr) # for employee in employee_list: # emp_tr = day_trans.filtered(lambda t: t.employee_id == employee) # template = self.env.ref('attendences.email_template_transaction_state') @@ -1149,28 +1446,30 @@ class HrAttendanceTransactions(models.Model): def send_lateness_notifications(self): today = datetime.now().date() yesterday = today - timedelta(days=1) - yesterday_records = self.search([('date', '=', yesterday),('employee_id.finger_print','=',True)]) + yesterday_records = self.env['hr.attendance.transaction'].search([ + ('date', '=', yesterday), ('employee_id.finger_print', '=', True)]) - email_values={} + email_values = {} yesterday_template_id = self.env.ref('attendances.attendance_notification_email_template').id for record in yesterday_records: - if ( record.lateness > 0 or + if (record.lateness > 0 or record.early_exit > 0 or record.is_absent or (record.sign_out == 0 and record.sign_in != 0) or - (record.sign_out != 0 and record.sign_in == 0) ): + (record.sign_out != 0 and record.sign_in == 0)): if yesterday_template_id: template = self.env['mail.template'].browse(yesterday_template_id) template.send_mail(record.id, force_send=True) """ Notification Today No Sign In On time """ - today_records = self.search([('date', '=', today),('employee_id.finger_print','=',True), - ('public_holiday','=',False),('normal_leave','=',False), - ('is_official','=',False)]) + today_records = self.env['hr.attendance.transaction'].search([ + ('date', '=', today), ('employee_id.finger_print', '=', True), + ('public_holiday', '=', False), ('normal_leave', '=', False), + ('is_official', '=', False)]) today_template_id = self.env.ref('attendances.attendance_notification_today_sign_in').id for item in today_records: - if item.sign_in==0: + if item.sign_in == 0: if today_template_id: template = item.env['mail.template'].browse(today_template_id) template.send_mail(item.id, force_send=True)