# -*- coding: utf-8 -*-
from datetime import datetime, timedelta
from odoo import models, fields, api, _
from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT, float_round
from odoo.exceptions import UserError


class HrAttendanceTransactions(models.Model):
    _name = 'hr.attendance.transaction'
    _description = 'Attendance Transaction'
    _inherit = ['mail.thread', 'mail.activity.mixin']
    _rec_name = 'employee_id'
    _order = 'date DESC'

    date = fields.Date(string='Day')
    lateness = fields.Float()
    early_exit = fields.Float()
    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',)
    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()
    calendar_id = fields.Many2one('resource.calendar', 'Calendar', ondelete="restrict",readonly=True)
    office_hours = fields.Float(string='Attending Hours', default=0)
    official_hours = fields.Float(string='Official Hours', default=0)
    plan_hours = fields.Float(string='Planned Hours', default=0)
    carried_hours = fields.Float(string='Carried Hours', default=0)
    temp_exit = fields.Float(string='Temporary Exit Hours')
    temp_lateness = fields.Float(string='Temporary Lateness Hours')
    additional_hours = fields.Float(compute='get_additional_hours', string='Additional Hours', default=0, store=True)

    sequence = fields.Integer()
    attending_type = fields.Selection([('in_cal', 'within Calendar'),
                                       ('out_cal', 'out Calendar')], string='Attending Type', default="in_cal")
    company_id = fields.Many2one(related='employee_id.company_id')
    employee_number = fields.Char(related='employee_id.emp_no', string='Employee Number', store=True)
    department_id = fields.Many2one(related='employee_id.department_id', string='Department Name', store=True)
    is_branch = fields.Many2one(related='department_id.branch_name', store=True, readonly=True)

    has_sign_in = fields.Boolean(readonly=True)
    has_sign_out = fields.Boolean(readonly=True)
    justification = fields.Boolean(string='Justification',default=False)
    total_mission_hours = fields.Float()
    '''to_date = fields.Boolean(string='Today',compute='_attendance_today', store=True)

    @api.depends('date','employee_id')
    def _attendance_today(self):
        today = fields.Datetime.now().date()
        for item in self:
            if item.date==today:
               item.to_date = True
            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

        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:
                min_sign_in = max_sign_out = working_hours = None  # or default fallback

        return min_sign_in, full_max_sign_in , max_sign_out, working_hours

    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

        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)'''

        # DATETIME_FORMAT = "%Y-%m-%d %H:%M:%S"
        TIME_FORMAT = "%H:%M:%S"
        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 + 
                     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(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}
        if calendar.is_full_day:
            time_list = [0 for i in range(4)]
            sp_timing = self.get_speacial_day_timing(calendar, weekday, wkd_date)
            planed_hours['one'] = (
                                          sp_timing and sp_timing.working_hours or calendar.working_hours) - calendar.break_duration
            time_list[0] = sp_timing and sp_timing.start_sign_in or calendar.full_min_sign_in
            time_list[1] = sp_timing and sp_timing.end_sign_in or calendar.full_max_sign_in
            time_list[2] = sp_timing and sp_timing.start_sign_out or calendar.full_min_sign_out
            time_list[3] = sp_timing and sp_timing.end_sign_out or calendar.full_max_sign_out
        else:
            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
            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
            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
            time_list[3] = one_sp_timing and one_sp_timing.end_sign_out or calendar.shift_one_max_sign_out
            time_list[4] = two_sp_timing and two_sp_timing.start_sign_in or calendar.shift_two_min_sign_in
            time_list[5] = two_sp_timing and two_sp_timing.end_sign_in or calendar.shift_two_max_sign_in
            time_list[6] = two_sp_timing and two_sp_timing.start_sign_out or calendar.shift_two_min_sign_out
            time_list[7] = two_sp_timing and two_sp_timing.end_sign_out or calendar.shift_two_max_sign_out
        return time_list, planed_hours

    # def get_speacial_day_timing(self, calender, weekday, at_date, shift=None):
    #     sp_days = shift and calender.special_days_partcial or calender.special_days
    #     for spday in sp_days:
    #         if spday.name.lower() == weekday and ((shift and spday.shift == shift) or (not shift and True)):
    #             if spday.date_from and spday.date_to \
    #                     and str(at_date) >= spday.date_from and str(at_date) <= spday.date_to:
    #                 return spday
    #             elif spday.date_from and not spday.date_to and str(at_date) >= spday.date_from:
    #                 return spday
    #             elif not spday.date_from and spday.date_to and str(at_date) <= spday.date_to:
    #                 return spday
    #             elif not spday.date_from and not spday.date_to:
    #                 return spday

    def get_speacial_day_timing(self, calender, weekday, at_date, shift=None):
        # Ensure at_date is a date object
        if isinstance(at_date, str):
            # Adjust the format string to match your date string format
            at_date = datetime.strptime(at_date, "%Y-%m-%d").date()
        sp_days = shift and calender.special_days_partcial or calender.special_days
        for spday in sp_days:
            if spday.name.lower() == weekday and ((shift and spday.shift == shift) or (not shift and True)):
                if spday.date_from and spday.date_to \
                        and at_date >= spday.date_from and at_date <= spday.date_to:
                    return spday
                elif spday.date_from and not spday.date_to and at_date >= spday.date_from:
                    return spday
                elif not spday.date_from and spday.date_to and at_date <= spday.date_to:
                    return spday
                elif not spday.date_from and not spday.date_to:
                    return spday

    def one_day_noke(self, noke_calendar):
        if noke_calendar.is_full_day:
            if noke_calendar.full_min_sign_in <= noke_calendar.full_max_sign_in \
                    < noke_calendar.full_min_sign_out <= noke_calendar.full_max_sign_out: return True
            return False
        else:
            if noke_calendar.shift_one_min_sign_in <= noke_calendar.shift_one_max_sign_in \
                    < noke_calendar.shift_one_min_sign_out <= noke_calendar.shift_one_max_sign_out \
                    < noke_calendar.shift_two_min_sign_in <= noke_calendar.shift_two_max_sign_in \
                    < noke_calendar.shift_two_min_sign_out <= noke_calendar.shift_two_max_sign_out: return True
            return False

    def noke_time_2date(self, time, noke_dt, calendar):
        last_nt = calendar[0]
        if calendar[0] < last_nt:
            noke_dt += timedelta(1)
        if time == 'one_max_in':
            return self.convert_float_2time(calendar[1], noke_dt)
        last_nt = calendar[1]
        if calendar[2] < last_nt:
            noke_dt += timedelta(1)
        if time == 'one_min_out':
            return self.convert_float_2time(calendar[2], noke_dt)
        last_nt = calendar[2]
        if calendar[3] < last_nt:
            noke_dt += timedelta(1)
        if time == 'one_max_out':
            return self.convert_float_2time(calendar[3], noke_dt)
        last_nt = calendar[3]
        if calendar[4] < last_nt:
            noke_dt += timedelta(1)
        if time == 'two_min_in':
            return self.convert_float_2time(calendar[4], noke_dt)
        last_nt = calendar[4]
        if calendar[5] < last_nt:
            noke_dt += timedelta(1)
        if time == 'two_max_in':
            return self.convert_float_2time(calendar[5], noke_dt)
        last_nt = calendar[5]
        if calendar[6] < last_nt:
            noke_dt += timedelta(1)
        if time == 'two_min_out':
            return self.convert_float_2time(calendar[6], noke_dt)
        last_nt = calendar[6]
        if calendar[7] < last_nt:
            noke_dt += timedelta(1)
        if time == 'two_max_out':
            return self.convert_float_2time(calendar[7], noke_dt)

    def prepare_shift(self, at_device, dt, signs_in, signs_out, min_in_dt, max_in_dt, min_out_dt, max_out_dt,
                      shift_dict, next_min_in_dt=None):
        attending_periods, linked_out_ids = [], []
        office_hours, official_hours, break_hours = 0, 0, 0
        at_dict = {'in': 0.0, 'out': 0.0, 'creep': 0}
        if signs_in:
            if not signs_out:
                at_dict['in'] = fields.Datetime.from_string(signs_in[0].name)
                if at_device:
                    at_dict['checkin_device_id'] = signs_in[0].device_id and signs_in[0].device_id.id or False
                    at_dict['checkout_device_id'] = False
                attending_periods.append(at_dict)
                linked_out_ids.append(signs_in[0].id)
            else:
                signs_time = []
                last_official_sign = True
                for sin in signs_in:

                    in_dt = fields.Datetime.from_string(sin.name)
                    for out in signs_out:

                        out_dt = fields.Datetime.from_string(out.name)

                        if signs_time and in_dt < last_out:
                            continue
                        if out_dt >= in_dt and out_dt >= min_in_dt \
                                and ((linked_out_ids and out.id not in linked_out_ids) or not linked_out_ids):

                            creep = next_min_in_dt \
                                    and (out_dt > next_min_in_dt and (out_dt - next_min_in_dt).seconds / 60 / 60) or 0.0
                            at_dic = {'in': in_dt, 'out': out_dt, 'creep': creep}
                            if at_device:
                                at_dic['checkin_device_id'] = sin.device_id and sin.device_id.id or False
                                at_dic['checkout_device_id'] = out.device_id and out.device_id.id or False
                            attending_periods.append(at_dic)

                            at_dic = {}
                            last_out = out_dt
                            linked_out_ids.append(out.id)
                            signs_time.append(in_dt)
                            signs_time.append(out_dt)
                            office_hours += (out_dt - in_dt).seconds / 60 / 60
                            if out_dt <= max_out_dt:
                                official_hours += (out_dt - in_dt).seconds / 60 / 60
                            elif out_dt > max_out_dt and last_official_sign:
                                official_hours += (max_out_dt - in_dt).seconds / 60 / 60
                                last_official_sign = False
                            break

        else:
            if next_min_in_dt:
                signed = False
                for out in signs_out:
                    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
                        checkout_device = out.device_id if at_device else False
                        linked_out_ids.append(out.id)
                        signed = True
                    else:
                        break
                if not signed:
                    return
                at_dict['out'] = one_out_dt
                if at_device:
                    at_dict['checkin_device_id'] = False
                    at_dict['checkout_device_id'] = checkout_device and checkout_device.id or False
                attending_periods.append(at_dict)

            else:
                at_dict['out'] = fields.Datetime.from_string(signs_out[-1].name)
                if at_device:
                    at_dict['checkin_device_id'] = False
                    at_dict['checkout_device_id'] = signs_out[-1].device_id and signs_out[-1].device_id.id or False
                attending_periods.append(at_dict)

                linked_out_ids.append(signs_out[-1].id)
        if at_device:
            if attending_periods:
                shift_dict['checkin_device_id'] = attending_periods[0]['checkin_device_id']
                shift_dict['checkout_device_id'] = attending_periods[-1]['checkout_device_id']
            else:
                shift_dict['checkin_device_id'] = False
                shift_dict['checkout_device_id'] = False

        if attending_periods:
            sign_in = attending_periods[0]['in']
            sign_out = attending_periods[-1]['out']
        else:
            sign_in = 0.0
            sign_out = 0.0

        # sign_in, sign_out = attending_periods[0]['in'], attending_periods[-1]['out']

        shift_dict['sign_in'] = sign_in and self.get_sign_time(fields.Datetime.to_string(sign_in))[0] or 0.0
        shift_dict['has_sign_in'] = bool(signs_in)
        shift_dict['sign_out'] = sign_out and self.get_sign_time(fields.Datetime.to_string(sign_out))[0] or 0.0
        shift_dict['has_sign_out'] = bool(signs_out)
        shift_dict['sign_in_dt'] = sign_in and sign_in or 0.0
        shift_dict['sign_out_dt'] = sign_out and sign_out or 0.0
        '''shift_dict['temp_lateness'] = sign_in and sign_out and (
                sign_in > max_in_dt and round((sign_in - min_in_dt).seconds / 60 / 60, 2)) or 0.0
        shift_dict['temp_exit'] = sign_in and sign_out and (
                sign_out < min_out_dt and round((max_out_dt - sign_out).seconds / 60 / 60, 2)) or 0.0'''
        ####################
        if sign_in and not sign_out and max_in_dt and sign_in > max_in_dt:
            shift_dict['temp_lateness'] = round((sign_in - max_in_dt).seconds / 60 / 60, 2)
        elif sign_in and sign_out and max_out_dt and \
                sign_in > min_in_dt and min_out_dt <= sign_out <= max_out_dt:
            temp_lateness = round((sign_in - min_in_dt).seconds / 60 / 60, 2) - round(
                (sign_out - min_out_dt).seconds / 60 / 60, 2)
            shift_dict['temp_lateness'] = temp_lateness if temp_lateness > 0 else 0.0
        elif sign_in and sign_out and max_out_dt and \
                sign_in > min_in_dt and sign_out <= min_out_dt:
            temp_lateness = round((sign_in - min_in_dt).seconds / 60 / 60, 2)
            shift_dict['temp_lateness'] = temp_lateness if temp_lateness > 0 else 0.0
        # elif sign_in and sign_out and max_in_dt and max_out_dt and sign_in > max_in_dt and sign_out >= max_out_dt:
        #     temp_lateness = round((sign_in - max_in_dt).seconds / 60 / 60, 2)
        #     shift_dict['temp_lateness'] = temp_lateness if temp_lateness > 0 else 0.0
        elif sign_in and sign_out and max_in_dt and max_out_dt and sign_in > max_in_dt and sign_out >= max_out_dt:
            temp_lateness = round((sign_in - max_in_dt).seconds / 60 / 60, 2)
            shift_dict['temp_lateness'] = temp_lateness
        elif sign_in and sign_out and not max_out_dt and sign_in > min_in_dt and min_out_dt <= sign_out <= max_out_dt:
            shift_dict['temp_lateness'] = round((sign_in - min_in_dt).seconds / 60 / 60, 2)
        else:
            shift_dict['temp_lateness'] = 0.0

        shift_dict['temp_exit'] = sign_in and sign_out and (sign_out < min_out_dt and
                                                            round((min_out_dt - sign_out).seconds / 60 / 60, 2)) or 0.0
        ##############################
        shift_dict['break_duration'] = 0.0
        shift_dict['office_hours'], shift_dict['official_hours'] = office_hours, official_hours
        if sign_in and sign_out and ((next_min_in_dt and sign_in > max_out_dt and sign_out < next_min_in_dt)
                                     or (not next_min_in_dt and sign_in > max_out_dt)):
            shift_dict['attending_type'] = 'out_cal'
        if sign_in or sign_out == 0: shift_dict['is_absent'] = True
        if len(attending_periods) > 1:
            break_periods = []
            del signs_time[0]
            del signs_time[-1]
            for inx, sign_time in enumerate(signs_time):
                if inx % 2 == 0:
                    break_start = sign_time
                else:
                    break_hours += round((sign_time - break_start).seconds / 60 / 60, 2)
                    break_periods.append({'break_start': break_start, 'break_end': sign_time})
            shift_dict['break_duration'] = break_hours
        if attending_periods:
            creep = round(attending_periods[-1]['creep'], 2)
        else:
            creep = 0.0
        if break_hours:
            return {'shift': shift_dict, 'out_ids': linked_out_ids, 'creep': creep, 'break_periods': break_periods}
        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):
        #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})
        # البحث عن الأذونات الشخصية المعتمدة في نفس يوم الحضور
        permissions = self.env['hr.personal.permission'].search(
            [('state', '=', 'approve'), ('employee_id', '=', trans.employee_id.id),
             '|', '|',
             '&', ('date_to', '<=', str(shift_out)), ('date_to', '>=', str(shift_in)),
             '&', ('date_from', '<=', str(shift_out)), ('date_from', '>=', str(shift_in)),
             '&', ('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 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:
                        feedback.append({'perm_id': perm.id, 'perm_start': perm_df, 'perm_end': perm_dt, 'type': 'all'})
                    continue
                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:
                            perm_time = round((perm_dt - perm_df).seconds / 60 / 60, 2)
                            perm_start, perm_end = perm_df, perm_dt
                        elif brk['break_start'] > perm_df and brk['break_end'] < perm_dt:
                            perm_time = round((brk['break_end'] - brk['break_start']).seconds / 60 / 60, 2)
                            perm_start, perm_end = brk['break_start'], brk['break_end']
                        elif brk['break_start'] <= perm_df and brk['break_end'] < perm_dt and brk[
                            'break_end'] > perm_df:
                            perm_time = round((brk['break_end'] - perm_df).seconds / 60 / 60, 2)
                            perm_start, perm_end = perm_df, brk['break_end']
                        elif brk['break_start'] > perm_df and brk['break_end'] >= perm_dt and brk[
                            'break_start'] < perm_dt:
                            perm_time = round((perm_dt - brk['break_start']).seconds / 60 / 60, 2)
                            perm_start, perm_end = brk['break_start'], perm_dt
                        if perm_time:
                            perm_dic = {'approve_personal_permission': True,
                                        'personal_permission_id': perm.id,
                                        'total_permission_hours': perm_time,
                                        'break_duration': trans.break_duration - perm_time,
                                        }
                            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': '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):
        #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
        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:
            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
                # 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
                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_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):
        at_device = self.env['ir.module.module'].sudo().search([('state', '=', 'installed'),
                                                                ('name', '=', 'to_attendance_device_custom')]) \
                    and True or False
        attendance_pool = self.env['attendance.attendance']
        # #remove today from not to day in attendance
        today = fields.Datetime.now().date()
        not_today = attendance_pool.search([('is_today', '=', True), ('action_date', '<', today)])
        if not_today:
           for att in not_today:
               att._compute_is_today()
        # #end
        low_date = (fields.Datetime.now()).date() if not attendance_date else attendance_date
        if isinstance(low_date, datetime):
            low_date = low_date.date()
        if low_date:
            module = self.env['ir.module.module'].sudo()
            official_mission_module = module.search(
                [('state', '=', 'installed'), ('name', '=', 'exp_official_mission')])
            personal_permission_module = module.search(
                [('state', '=', 'installed'), ('name', '=', 'employee_requests')])
            holidays_module = module.search([('state', '=', 'installed'), ('name', '=', 'hr_holidays_public')])
            if holidays_module:
                holiday_pool = self.env['hr.holidays']
            transactions = self.env['hr.attendance.transaction']

            day_item = low_date
            weekday = day_item.strftime('%A').lower()
            prv_day_item = day_item - timedelta(1)
            prv_weekday = prv_day_item.strftime('%A').lower()
            nxt_day_item = day_item + timedelta(1)
            nxt_weekday = nxt_day_item.strftime('%A').lower()
            if holidays_module:
                public = self.env['hr.holiday.officials'].search([('active', '=', True), ('state', '=', 'confirm'),
                                                                  ('date_from', '<=', day_item),
                                                                  ('date_to', '>=', day_item)])
            employee_list = self.env['hr.employee'].search([('state', '=', 'open')]) \
                if not attendance_employee else attendance_employee

            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: 
                    continue
                datetime_object = datetime.strptime(str(hire), '%Y-%m-%d').date()
                if employee.contract_id.state != 'end_contract' and datetime_object <= day_item:
                    if datetime_object == day_item:
                        day_item = datetime_object
                    else:
                        day_item = low_date
                    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:
                        attendance_dt = datetime.combine(day_item, datetime.min.time())
                        day_times, planed_hours = self.get_day_timing(emp_calendar, weekday, day_item)
                        one_min_in, one_max_in = day_times[0], day_times[1]
                        one_min_out, one_max_out = day_times[2], day_times[3]
                        one_min_in_dt = self.convert_float_2time(one_min_in, attendance_dt)
                        domain = [('employee_id', '=', employee.id)]
                        if emp_calendar.noke and not self.one_day_noke(emp_calendar):
                            one_max_in_dt = self.noke_time_2date('one_max_in', attendance_dt, day_times)
                            one_min_out_dt = self.noke_time_2date('one_min_out', attendance_dt, day_times)
                            one_max_out_dt = self.noke_time_2date('one_max_out', attendance_dt, day_times)

                            one_max_out_st = fields.Datetime.to_string(one_max_out_dt)
                            domain += [('action_date', 'in', (day_item, day_item + timedelta(1)))]
                            prv_day_times = self.get_day_timing(emp_calendar, prv_weekday, prv_day_item)[0]
                            prv_day_min_out = self.noke_time_2date('one_min_out', attendance_dt - timedelta(1),
                                                                   prv_day_times)
                            prv_day_max_out = self.noke_time_2date('one_max_out', attendance_dt - timedelta(1),
                                                                   prv_day_times)
                            nxt_day_times = self.get_day_timing(emp_calendar, nxt_weekday, nxt_day_item)[0]
                            nxt_day_min_in = self.convert_float_2time(nxt_day_times[0], attendance_dt + timedelta(1))
                            one_in_dom = domain.copy() + [('action', '=', 'sign_in'),
                                                          ('name', '<=', one_max_out_st),
                                                          ('name', '>', str(prv_day_max_out))]
                            out_dom = domain.copy() + [('action', '=', 'sign_out'),
                                                       ('name', '>', str(prv_day_max_out)),
                                                       ('name', '<', str(nxt_day_min_in))]
                        else:
                            one_max_in_dt = self.convert_float_2time(one_max_in, attendance_dt)
                            one_min_out_dt = self.convert_float_2time(one_min_out, attendance_dt)
                            one_max_out_dt = self.convert_float_2time(one_max_out, attendance_dt)
                            one_max_out_st = fields.Datetime.to_string(one_max_out_dt)
                            domain += [('action_date', '=', day_item)]
                            one_in_dom = domain.copy() + [('action', '=', 'sign_in'), ('name', '<=', one_max_out_st)]
                            out_dom = domain.copy() + [('action', '=', 'sign_out')]
                        signs_out = attendance_pool.search(out_dom, order="name asc")
                        one_signs_in = attendance_pool.search(one_in_dom, order="name asc")
                        base_dict = {'date': day_item, 'employee_id': employee.id, 'calendar_id': emp_calendar.id,
                                     'attending_type': 'in_cal'}
                        one_dict, shift_one_extra, force_create = {}, {}, True
                        exist_trans = transactions.search([('date', '=', day_item), ('employee_id', '=', employee.id)])
                        if not (not one_signs_in and not signs_out):
                            if one_signs_in:
                                signs_out = signs_out.filtered(
                                    lambda s: s.name >= one_signs_in[0].name) or attendance_pool
                            one_dict = self.prepare_shift(at_device, attendance_dt, one_signs_in, signs_out,
                                                          one_min_in_dt,
                                                          one_max_in_dt, one_min_out_dt, one_max_out_dt,
                                                          base_dict.copy())
                            if one_dict:
                                shift_one = one_dict['shift']
                                shift_one['sequence'] = 1
                                one_breaks = one_dict.get('break_periods', {})
                                one_in_dt, one_out_dt = shift_one['sign_in_dt'], shift_one['sign_out_dt']
                                del shift_one['sign_in_dt']
                                del shift_one['sign_out_dt']
                                if one_out_dt:
                                    signs_out = signs_out.filtered(lambda s: s.name > (one_out_dt))
                                force_create = False
                                if shift_one.get('attending_type') == 'out_cal':
                                    shift_one_extra = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'sequence': 1,
                                                       'employee_id': employee.id, 'calendar_id': emp_calendar.id,
                                                       'attending_type': 'in_cal'}
                        if force_create or not one_dict:
                            shift_one = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'employee_id': employee.id,
                                         'is_absent': True, 'calendar_id': emp_calendar.id, 'sequence': 1,
                                         'attending_type': 'in_cal'}
                            one_in_dt, one_out_dt = one_min_in_dt, one_max_out_dt
                            one_breaks = {}
                            force_create = True
                        one_exist_trans = exist_trans and exist_trans.filtered(
                            lambda t: t.sequence == 1 and t.attending_type == shift_one['attending_type']) or False
                        if one_exist_trans:
                            one_exist_trans.update(shift_one)
                            one_trans = one_exist_trans
                        else:
                            one_trans = transactions.create(shift_one)
                        if shift_one_extra:
                            onextra_exist_trans = exist_trans and \
                                                  exist_trans.filtered(
                                                      lambda
                                                          t: t.sequence == 1 and t.attending_type == 'out_cal') or False
                            if onextra_exist_trans:
                                onextra_exist_trans.update(shift_one_extra)
                            else:
                                transactions.create(shift_one_extra)
                        one_trans = exist_trans and \
                                    exist_trans.filtered(
                                        lambda t: t.sequence == 1 and t.attending_type == 'in_cal') or one_trans
                        one_extra_dlt = exist_trans and \
                                        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:
                                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 official_mission_module:
                            self.manage_mission(one_trans.id, one_min_in_dt, one_max_out_dt, one_in_dt, one_out_dt,
                                                one_breaks)
                    else:
                        day_times, planed_hours = self.get_day_timing(emp_calendar, weekday, day_item)
                        one_min_in, one_max_in = day_times[0], day_times[1]
                        one_min_out, one_max_out = day_times[2], day_times[3]
                        two_min_in, two_max_in = day_times[4], day_times[5]
                        two_min_out, two_max_out = day_times[6], day_times[7]
                        attendance_dt = datetime.combine(day_item, datetime.min.time())
                        one_min_in_dt = self.convert_float_2time(one_min_in, attendance_dt)
                        domain = [('employee_id', '=', employee.id)]
                        if emp_calendar.noke and not self.one_day_noke(emp_calendar):
                            one_max_in_dt = self.noke_time_2date('one_max_in', attendance_dt, day_times)
                            one_min_out_dt = self.noke_time_2date('one_min_out', attendance_dt, day_times)
                            one_max_out_dt = self.noke_time_2date('one_max_out', attendance_dt, day_times)
                            two_min_in_dt = self.noke_time_2date('two_min_in', attendance_dt, day_times)
                            two_max_in_dt = self.noke_time_2date('two_max_in', attendance_dt, day_times)
                            two_min_out_dt = self.noke_time_2date('two_min_out', attendance_dt, day_times)
                            two_max_out_dt = self.noke_time_2date('two_max_out', attendance_dt, day_times)
                            one_max_out_st = fields.Datetime.to_string(one_max_out_dt)
                            domain += [('action_date', 'in', (day_item, day_item + timedelta(1)))]
                            prv_day_times = self.get_day_timing(emp_calendar, prv_weekday, prv_day_item)[0]
                            prv_day_min_out = self.noke_time_2date('two_min_out', attendance_dt - timedelta(1),
                                                                   prv_day_times)
                            prv_day_max_out = self.noke_time_2date('two_max_out', attendance_dt - timedelta(1),
                                                                   prv_day_times)
                            nxt_day_times = self.get_day_timing(emp_calendar, nxt_weekday, nxt_day_item)[0]
                            nxt_day_min_in = self.convert_float_2time(nxt_day_times[0], attendance_dt + timedelta(1))
                            one_in_dom = domain.copy() + [('action', '=', 'sign_in'),
                                                          ('name', '<=', one_max_out_st),
                                                          ('name', '>', str(prv_day_min_out))]
                            two_in_dom = domain.copy() + [('action', '=', 'sign_in'),
                                                          ('name', '>', one_max_out_st),
                                                          ('name', '<=', str(two_min_out_dt))]
                            out_dom = domain.copy() + [('action', '=', 'sign_out'),
                                                       ('name', '>', str(prv_day_max_out)),
                                                       ('name', '<', str(nxt_day_min_in))]
                        else:
                            one_max_in_dt = self.convert_float_2time(one_max_in, attendance_dt)
                            one_min_out_dt = self.convert_float_2time(one_min_out, attendance_dt)
                            one_max_out_dt = self.convert_float_2time(one_max_out, attendance_dt)
                            two_min_in_dt = self.convert_float_2time(two_min_in, attendance_dt)
                            two_max_in_dt = self.convert_float_2time(two_max_in, attendance_dt)
                            two_min_out_dt = self.convert_float_2time(two_min_out, attendance_dt)
                            two_max_out_dt = self.convert_float_2time(two_max_out, attendance_dt)
                            one_max_out_st = fields.Datetime.to_string(one_max_out_dt)
                            domain += [('action_date', '=', day_item)]
                            one_in_dom = domain.copy() + [('action', '=', 'sign_in'), ('name', '<', one_max_out_st)]
                            two_in_dom = domain.copy() + [('action', '=', 'sign_in'), ('name', '>=', one_max_out_st)]
                            out_dom = domain.copy() + [('action', '=', 'sign_out')]

                        signs_out = attendance_pool.search(out_dom, order="name asc")
                        one_signs_in = attendance_pool.search(one_in_dom, order="name asc")
                        two_signs_in = attendance_pool.search(two_in_dom, order="name asc")
                        base_dict = {'date': day_item, 'employee_id': employee.id, 'calendar_id': emp_calendar.id,
                                     'attending_type': 'in_cal'}
                        one_dict, two_dict, shift_one_extra, shift_two_extra, force_create = {}, {}, {}, {}, True
                        exist_trans = transactions.search([('date', '=', day_item), ('employee_id', '=', employee.id)])
                        if not (not one_signs_in and not signs_out):
                            if one_signs_in:
                                signs_out = signs_out.filtered(
                                    lambda s: s.name > one_signs_in[0].name) or attendance_pool
                            one_dict = self.prepare_shift(at_device, attendance_dt, one_signs_in, signs_out,
                                                          one_min_in_dt,
                                                          one_max_in_dt, one_min_out_dt, one_max_out_dt,
                                                          base_dict.copy(),
                                                          two_min_in_dt)
                            if one_dict:
                                shift_one = one_dict['shift']
                                shift_one['sequence'] = 1
                                one_breaks = one_dict.get('break_periods', {})
                                one_in_dt, one_out_dt = shift_one['sign_in_dt'], shift_one['sign_out_dt']
                                del shift_one['sign_in_dt']
                                del shift_one['sign_out_dt']
                                if one_out_dt: signs_out = signs_out.filtered(lambda s: str(s.name) > str(one_out_dt))
                                force_create = False
                                if shift_one.get('attending_type') == 'out_cal':
                                    shift_one_extra = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'sequence': 1,
                                                       'employee_id': employee.id, 'calendar_id': emp_calendar.id,
                                                       'attending_type': 'in_cal'}
                        if force_create or not one_dict:
                            shift_one = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'employee_id': employee.id,
                                         'is_absent': True, 'calendar_id': emp_calendar.id, 'sequence': 1,
                                         'attending_type': 'in_cal'}
                            one_in_dt, one_out_dt = one_min_in_dt, one_max_out_dt
                            one_breaks = {}
                            force_create = True
                        if not (not two_signs_in and not signs_out):
                            two_dict = self.prepare_shift(at_device, attendance_dt, two_signs_in, signs_out,
                                                          two_min_in_dt,
                                                          two_max_in_dt, two_min_out_dt, two_max_out_dt, base_dict)
                            if two_signs_in:
                                signs_out = signs_out.filtered(
                                    lambda s: s.name > two_signs_in[0].name) or attendance_pool
                            if two_dict:
                                shift_two = two_dict['shift']
                                shift_two['sequence'] = 2
                                two_breaks = two_dict.get('break_periods', {})
                                two_in_dt, two_out_dt = shift_two['sign_in_dt'], shift_two['sign_out_dt']
                                del shift_two['sign_in_dt']
                                del shift_two['sign_out_dt']
                                force_create = False
                                if shift_two.get('attending_type') == 'out_cal':
                                    shift_two_extra = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'sequence': 2,
                                                       'employee_id': employee.id, 'calendar_id': emp_calendar.id,
                                                       'attending_type': 'in_cal'}
                        if force_create or not two_dict:
                            shift_two = {'sign_in': 0.0, 'sign_out': 0.0, 'date': day_item, 'employee_id': employee.id,
                                         'calendar_id': emp_calendar.id, 'sequence': 2, 'attending_type': 'in_cal'}
                            two_in_dt, two_out_dt = two_min_in_dt, two_max_out_dt
                            two_breaks = {}
                            force_create = True
                        if one_dict and one_dict['creep']:
                            # TODO: review cases not to be minus
                            shift_one['official_hours'] += one_dict['creep']
                            if two_dict and shift_two['temp_lateness'] and shift_two.get('attending_type') == 'in_cal':
                                shift_two['break_duration'] += shift_two['temp_lateness'] - one_dict['creep']
                                # shift_two['temp_lateness'] = 0.0
                                shift_two['carried_hours'] = one_dict['creep']
                        one_exist_trans = exist_trans and exist_trans.filtered(
                            lambda t: t.sequence == 1 and t.attending_type == shift_one['attending_type']) or False
                        if one_exist_trans:
                            one_exist_trans.update(shift_one)
                            one_trans = one_exist_trans
                        else:
                            one_trans = transactions.create(shift_one)
                        two_exist_trans = exist_trans and exist_trans.filtered(
                            lambda t: t.sequence == 2 and t.attending_type == shift_two['attending_type']) or False
                        if two_exist_trans:
                            two_exist_trans.update(shift_two)
                            two_trans = two_exist_trans
                        else:
                            two_trans = transactions.create(shift_two)
                        if shift_one_extra:
                            onextra_exist_trans = exist_trans and \
                                                  exist_trans.filtered(
                                                      lambda
                                                          t: t.sequence == 1 and t.attending_type == 'out_cal') or False
                            if onextra_exist_trans:
                                onextra_exist_trans.update(shift_one_extra)
                            else:
                                transactions.create(shift_one_extra)
                        if shift_two_extra:
                            twoxtra_exist_trans = exist_trans and \
                                                  exist_trans.filtered(
                                                      lambda
                                                          t: t.sequence == 2 and t.attending_type == 'in_cal') or False
                            if twoxtra_exist_trans:
                                twoxtra_exist_trans.update(shift_two_extra)
                            else:
                                transactions.create(shift_two_extra)
                        one_trans = exist_trans and \
                                    exist_trans.filtered(
                                        lambda t: t.sequence == 1 and t.attending_type == 'in_cal') or one_trans
                        one_extra_dlt = exist_trans and \
                                        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()
                        two_trans = exist_trans and \
                                    exist_trans.filtered(
                                        lambda t: t.sequence == 2 and t.attending_type == 'in_cal') or two_trans
                        two_extra_dlt = exist_trans and \
                                        exist_trans.filtered(
                                            lambda t: t.sequence == 2 and t.attending_type == 'out_cal') or False
                        if two_extra_dlt and not shift_two_extra: two_extra_dlt.unlink()
                        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:
                            self.manage_mission(one_trans.id, one_min_in_dt, one_max_out_dt, one_in_dt, one_out_dt,
                                                one_breaks)
                            self.manage_mission(two_trans.id, two_min_in_dt, two_max_out_dt, two_in_dt, two_out_dt,
                                                two_breaks)

                    off_list = []
                    day_trans = transactions.search([('date', '=', day_item), ('employee_id', '=', employee.id)])
                    weekend_days = emp_calendar.is_full_day and emp_calendar.full_day_off or emp_calendar.shift_day_off
                    if weekend_days:
                        for day in weekend_days:
                            create = (datetime.strptime(str(day.create_date.strftime(DATETIME_FORMAT)),
                                                        DATETIME_FORMAT) + timedelta(hours=3)).date()
                            off_list.append(day.name.lower())
                            if day.name.lower() == day_item.strftime('%A').lower():
                                for trans in day_trans:
                                    if emp_calendar.noke and create <= datetime.strptime(str(trans.date),
                                                                                         "%Y-%m-%d").date():
                                        trans.update({'public_holiday': True})
                                    else:
                                        if not emp_calendar.is_full_day:
                                            if day.shift == 'one' and trans.sequence == 1:
                                                trans.update({'public_holiday': True})
                                            elif day.shift == 'tow' and trans.sequence == 2:
                                                trans.update({'public_holiday': True})
                                            elif day.shift == 'both':
                                                trans.update({'public_holiday': True})
                                        else:
                                            trans.update({'public_holiday': True})

                            else:
                                for trans in day_trans:
                                    if trans.public_holiday and not trans.public_holiday_id:
                                        trans_wkd = datetime.strptime(str(trans.date), "%Y-%m-%d")
                                        if trans_wkd.strftime(
                                                '%A').lower() not in off_list and not trans.public_holiday_id:
                                            if emp_calendar.noke:
                                                pass
                                            else:
                                                trans.update({'public_holiday': False})
                    else:
                        for trans in day_trans:
                            if trans.public_holiday and not trans.public_holiday_id:
                                if emp_calendar.noke:
                                    pass
                                else:
                                    trans.update({'public_holiday': False})
                    if holidays_module:
                        ptrans = transactions.search([('date', '=', day_item), ('employee_id', '=', employee.id)])
                        public_trans = ptrans.filtered(lambda pk: pk.public_holiday_id)
                        if public:
                            for p in public:
                                for trans in ptrans:
                                    if p.date_from <= trans.date and p.date_to >= trans.date:
                                        trans.update({'public_holiday': True, 'public_holiday_id': p.id})
                        elif public_trans and not public:
                            for trans in public_trans:
                                if trans.public_holiday:
                                    trans.update({'public_holiday': False, 'public_holiday_id': False})
                        emp_stateless_leaves = holiday_pool.search([('employee_id', '=', employee.id),
                                                                    ('type', '!=', 'add'),
                                                                    ('date_from', '<=', str(day_item)),
                                                                    ('date_to', '>=', str(day_item))])
                        leaves = emp_stateless_leaves.filtered(lambda l: l.state == 'validate1')
                        cancelled_leaves = emp_stateless_leaves.filtered(lambda l: l.state != 'validate1')
                        if leaves:
                            for lev in leaves:
                                if lev.date_from and lev.date_to \
                                        and fields.Date.from_string(lev.date_from).strftime('%Y-%m-%d') \
                                        <= str(day_item) <= str(lev.date_to):
                                    for trans in ptrans:
                                        lv_hours = trans.calendar_id.is_full_day and trans.calendar_id.working_hours \
                                                   or trans.sequence == 1 and trans.calendar_id.shift_one_working_hours \
                                                   or trans.sequence == 2 and trans.calendar_id.shift_two_working_hours
                                        trans.update({
                                            'normal_leave': True,
                                            'leave_id': lev.id,
                                            'total_leave_hours': lv_hours,
                                            'break_duration': 0.0,
                                            'public_holiday': False,
                                            'is_absent': False,
                                            'total_absent_hours': 0.0})
                        elif cancelled_leaves:
                            for trans in ptrans:
                                for lev in cancelled_leaves:
                                    if lev.id == trans.leave_id.id:
                                        trans.update(
                                            {'normal_leave': False, 'leave_id': False, 'total_leave_hours': 0.0})
                    for tr in day_trans:
                        if tr.attending_type == 'in_cal':
                            if tr.sequence == 1:
                                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')
                    #     if len(day_trans.filtered(lambda t: t.employee_id == employee)) == 1:
                    #         if emp_tr.is_absent:
                    #             msg = "is absent"
                    #             body = """<div>
                    #             <p>Dear %s ,</p>
                    #             <p> Greetings, we kindly inform you that employee %s %s on %s
                    #             <br/>
                    #             <p>Best regards,</p>
                    #              """ % (employee.parent_id.name, employee.name, msg, emp_tr.date)
                    #             template.write({'body_html': body})
                    #             template.send_mail(emp_tr.id, force_send=True, raise_exception=False)
                    #         if emp_tr.approve_lateness:
                    #             msg = "is late"
                    #             body = """<div>
                    #             <p>Dear %s ,</p>
                    #             <p> Greetings, we kindly inform you that employee %s %s on %s
                    #             <br/>
                    #              <p>Best regards,</p>
                    #              """ % (employee.parent_id.name, employee.name, msg, emp_tr.date)
                    #             template.write({'body_html': body})
                    #             template.send_mail(emp_tr.id, force_send=True, raise_exception=False)
                    #         if emp_tr.approve_exit_out:
                    #             msg = "is exit out"
                    #             body = """<div>
                    #             <p>Dear %s ,</p>
                    #             <p> Greetings, we kindly inform you that employee %s %s on %s
                    #             <br/>
                    #             <p>Best regards,</p>
                    #              """ % (employee.parent_id.name, employee.name, msg, emp_tr.date)
                    #             template.write({'body_html': body})
                    #             template.send_mail(emp_tr.id, force_send=True, raise_exception=False)
                    #     else:
                    #         if all(tr.is_absent for tr in emp_tr):
                    #             if emp_tr and emp_tr[0]:
                    #                 msg = "is absent"
                    #                 body = """<div>
                    #                 <p>Dear %s ,</p>
                    #                 <p> Greetings, we kindly inform you that employee %s %s on %s
                    #                 <br/>
                    #                 <p>Best regards,</p>
                    #                 """ % (employee.parent_id.name, employee.name, msg, emp_tr[0].date)
                    #                 template.write({'body_html': body})
                    #                 template.send_mail(emp_tr[0].id, force_send=True, raise_exception=False)
                    #         if all(tr.approve_lateness for tr in emp_tr):
                    #             if emp_tr and emp_tr[0]:
                    #                 msg = "is later"
                    #                 body = """<div>
                    #                 <p>Dear %s ,</p>
                    #                 <p> Greetings, we kindly inform you that employee %s %s on %s
                    #                 <br/>
                    #                 <p>Best regards,</p>
                    #                 """ % (employee.parent_id.name, employee.name, msg, emp_tr[0].date)
                    #                 template.write({'body_html': body})
                    #                 template.send_mail(emp_tr[0].id, force_send=True, raise_exception=False)
                    #         if all(tr.approve_exit_out for tr in emp_tr):
                    #             if emp_tr and emp_tr[0]:
                    #                 msg = "is exit out"
                    #                 body = """<div>
                    #                 <p>Dear %s ,</p>
                    #                 <p> Greetings, we kindly inform you that employee %s %s on  %s
                    #                 <br/>
                    #                 <p>Best regards,</p>
                    #                 """ % (employee.parent_id.name, employee.name, msg, emp_tr[0].date)
                    #                 template.write({'body_html': body})
                    #                 template.send_mail(emp_tr[0].id, force_send=True, raise_exception=False)

    def get_user_id(self):
        employee_id = self.env['hr.employee'].search([('user_id', '=', self.env.uid)], limit=1)
        if employee_id:
            return employee_id.id
        else:
            return False

    @api.model
    def send_lateness_notifications(self):
        today = fields.Datetime.now().date()
        yesterday = today - timedelta(days=1)
        yesterday_records = self.env['hr.attendance.transaction'].search([
                                    ('date', '=', yesterday), ('employee_id.finger_print', '=', True)])
        
        email_values = {}
        yesterday_template_id = self.env.ref('attendances.attendance_notification_email_template').id
        for record in yesterday_records:
            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)):

                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.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 today_template_id:
                    template = item.env['mail.template'].browse(today_template_id)
                    template.send_mail(item.id, force_send=True)
    # @api.multi
    # def unlink(self):
    #     raise UserError(_('Sorry, you can not delete an attendance transaction manually.'))
    #     return super(HrAttendanceTransactions, self).unlink()
