from odoo.tests.common import TransactionCase from odoo.exceptions import ValidationError from datetime import date from unittest.mock import MagicMock class TestHrAttendanceReport(TransactionCase): def setUp(self): super(TestHrAttendanceReport, self).setUp() self.manager_user = self.env['hr.employee'].create({'name': 'Manager', 'finger_print': False}) self.dept = self.env['hr.department'].create({'name': 'IT', 'manager_id': self.manager_user.id}) self.calendar = self.env['resource.calendar'].create({ 'name': 'Standard 8h', 'is_full_day': True, 'full_min_sign_in': 8.0, 'full_max_sign_in': 9.0, 'full_min_sign_out': 16.0, 'full_max_sign_out': 17.0, 'working_hours': 8.0, 'working_days': 5, 'state': 'confirm', }) self.employee = self.env['hr.employee'].create({ 'name': 'Ahmed Ali', 'department_id': self.dept.id, 'finger_print': True, 'state': 'open', 'resource_calendar_id': self.calendar.id }) self.contract = self.env['hr.contract'].create({ 'name': 'Contract', 'employee_id': self.employee.id, 'total_allowance': 4000.0, 'state': 'program_directory', 'date_start': date(2023, 1, 1), 'resource_calendar_id': self.calendar.id, }) def test_01_generate_report_deduction_logic(self): common_vals = { 'employee_id': self.employee.id, 'calendar_id': self.calendar.id, 'attending_type': 'in_cal', 'sequence': 1, 'approve_lateness': True, 'approve_exit_out': True, } trans1 = self.env['hr.attendance.transaction'].create({ **common_vals, 'date': date(2023, 10, 1), 'plan_hours': 8.0, 'is_absent': True, }) trans2 = self.env['hr.attendance.transaction'].create({ **common_vals, 'date': date(2023, 10, 2), 'plan_hours': 8.0, 'official_hours': 6.0, 'lateness': 2.0, 'is_absent': False, }) type(trans1).public_holiday = property(lambda self: False) type(trans1).normal_leave = property(lambda self: False) # ------------------------------------------ # 5. توليد التقرير report = self.env['hr.attendance.report'].create({ 'name': 'Report', 'date_from': date(2023, 10, 1), 'date_to': date(2023, 10, 5), 'selected_employee_ids': [(4, self.employee.id)] }) report.generate_report() self.assertTrue(report.line_ids, "No record") line = report.line_ids[0] self.assertAlmostEqual(line.total_hours, 10.0, msg="fild hours") self.assertAlmostEqual(line.total_deduction, 1000.0, msg="mins") self.assertEqual(report.state, 'generated') from odoo.tests.common import TransactionCase from datetime import date from unittest.mock import MagicMock class TestFlexibleAttendanceReport(TransactionCase): def setUp(self): super(TestFlexibleAttendanceReport, self).setUp() self.manager = self.env['hr.employee'].create({'name': 'Manager'}) self.dept = self.env['hr.department'].create({'name': 'IT', 'manager_id': self.manager.id}) self.calendar_flex = self.env['resource.calendar'].create({ 'name': 'Flexible 40h', 'is_flexible': True, 'is_full_day': True, 'number_of_flexi_days': 5, 'working_hours': 8.0, 'full_min_sign_in': 8.0, 'full_max_sign_in': 9.0, 'full_min_sign_out': 16.0, 'full_max_sign_out': 17.0, 'state': 'confirm', }) self.employee = self.env['hr.employee'].create({ 'name': 'Flex Employee', 'department_id': self.dept.id, 'resource_calendar_id': self.calendar_flex.id, 'finger_print': True, 'state': 'open' }) def test_flexible_calculation_logic(self): def create_mock_transaction(plan, office, is_absent=False, sign_in=True, sign_out=True): mock = MagicMock() mock.plan_hours = plan mock.office_hours = office mock.is_absent = is_absent mock.public_holiday = False mock.normal_leave = False mock.personal_permission_id = False mock.official_id = False mock.approve_personal_permission = False mock.total_permission_hours = 0.0 mock.total_leave_hours = 0.0 mock.total_mission_hours = 0.0 mock.sign_in = sign_in mock.sign_out = sign_out mock.early_exit = 0.0 mock.lateness = 0.0 mock.break_duration = 0.0 mock.approve_exit_out = False mock.approve_lateness = False return mock all_trans_list = [ create_mock_transaction(8.0, 6.0), create_mock_transaction(8.0, 0.0, is_absent=True), create_mock_transaction(8.0, 4.0, sign_out=False) ] def create_mock_recordset(items): m = MagicMock() m.__iter__.return_value = iter(items) m.filtered.side_effect = lambda func: create_mock_recordset([i for i in items if func(i)]) m.mapped.side_effect = lambda name: [getattr(i, name) for i in items] m.__len__.return_value = len(items) return m transactions = create_mock_recordset(all_trans_list) report = self.env['hr.attendance.report'].new() result = report.calcualte_flexible_transaction(transactions) self.assertAlmostEqual(result['missed_hours'], 14.0, msg="Missed hours calculation failed") self.assertEqual(result['actual_absent_days'], 1, msg="Absent days count failed") self.assertAlmostEqual(result['missing_punch_hours'], 4.0, msg="Missing punch hours failed") class TestHrAttendanceReportApproved(TransactionCase): def setUp(self): super(TestHrAttendanceReportApproved, self).setUp() self.manager_user = self.env['hr.employee'].create({ 'name': 'Manager', 'finger_print': False }) self.dept = self.env['hr.department'].create({ 'name': 'IT', 'manager_id': self.manager_user.id }) self.calendar = self.env['resource.calendar'].create({ 'name': 'Test Calendar', 'is_full_day': True, 'full_min_sign_in': 8.0, 'full_max_sign_in': 9.0, 'full_min_sign_out': 16.0, 'full_max_sign_out': 17.0, 'working_hours': 8.0, 'working_days': 5, 'state': 'confirm', }) self.salary_category = self.env['hr.salary.rule.category'].create({ 'name': 'Test Deductions', 'code': 'TEST_DED_CAT' }) self.deduction_rule = self.env['hr.salary.rule'].create({ 'name': 'Test Deduction Rule', 'code': 'TEST_DED', 'category_id': self.salary_category.id, }) self.calendar.deduction_rule = self.deduction_rule self.employee = self.env['hr.employee'].create({ 'name': 'Ahmed Ali', 'department_id': self.dept.id, 'finger_print': True, 'state': 'open', 'resource_calendar_id': self.calendar.id, }) self.contract = self.env['hr.contract'].create({ 'name': 'Test Contract', 'employee_id': self.employee.id, 'total_allowance': 4000.0, 'state': 'program_directory', 'date_start': date(2023, 1, 1), 'resource_calendar_id': self.calendar.id, }) self.employee.contract_id = self.contract self.create_attendance_transactions() self.report = self.env['hr.attendance.report'].create({ 'name': 'Test Approved Report', 'date_from': date(2023, 10, 1), 'date_to': date(2023, 10, 5), 'deduct_date_from': date(2023, 10, 1), 'deduct_date_to': date(2023, 10, 31), 'selected_employee_ids': [(4, self.employee.id)] }) def create_attendance_transactions(self): self.env['hr.attendance.transaction'].create({ 'employee_id': self.employee.id, 'calendar_id': self.calendar.id, 'attending_type': 'in_cal', 'sequence': 1, 'date': date(2023, 10, 1), 'plan_hours': 8.0, 'is_absent': True }) def test_01_approved_creates_advantage_record(self): self.report.generate_report() line = self.report.line_ids[0] self.assertGreater(line.total_deduction, 0.0, "Must have deduction") self.assertFalse(line.advantage_id, "No advantage before approved") self.report.approved() self.assertEqual(self.report.state, 'approved', "State must be approved") self.assertTrue(line.advantage_id, "Advantage record must be created") advantage = line.advantage_id self.assertEqual(advantage.employee_id, self.employee) self.assertEqual(advantage.amount, line.total_deduction) self.assertEqual(advantage.benefits_discounts.id, self.deduction_rule.id) self.assertEqual(advantage.contract_advantage_id, self.contract) self.assertEqual(advantage.date_from, self.report.deduct_date_from) self.assertEqual(advantage.date_to, self.report.deduct_date_to) self.assertEqual(advantage.state, 'confirm') self.assertEqual(advantage.comments, 'Absence Deduction') self.assertTrue(advantage.out_rule) def test_02_approved_no_contract_no_advantage(self): employee_no_contract = self.env['hr.employee'].create({ 'name': 'No Contract Employee', 'finger_print': True, 'state': 'open', 'resource_calendar_id': self.calendar.id, }) self.env['hr.attendance.transaction'].create({ 'employee_id': employee_no_contract.id, 'calendar_id': self.calendar.id, 'attending_type': 'in_cal', 'sequence': 1, 'date': date(2023, 10, 1), 'plan_hours': 8.0, 'is_absent': True }) report_no_contract = self.env['hr.attendance.report'].create({ 'name': 'No Contract Report', 'date_from': date(2023, 10, 1), 'date_to': date(2023, 10, 5), 'deduct_date_from': date(2023, 10, 1), 'deduct_date_to': date(2023, 10, 31), 'selected_employee_ids': [(4, employee_no_contract.id)] }) report_no_contract.generate_report() line = report_no_contract.line_ids[0] report_no_contract.approved() self.assertFalse(line.advantage_id, "No advantage without contract") def test_03_approved_no_fingerprint_no_advantage(self): self.employee.finger_print = False self.report.generate_report() line = self.report.line_ids[0] self.report.approved() self.assertFalse(line.advantage_id, "No advantage without fingerprint") def test_04_multiple_lines_multiple_advantages(self): employee2 = self.env['hr.employee'].create({ 'name': 'Second Employee', 'finger_print': True, 'state': 'open', 'resource_calendar_id': self.calendar.id, 'department_id': self.dept.id }) contract2 = self.env['hr.contract'].create({ 'name': 'Second Contract', 'employee_id': employee2.id, 'total_allowance': 3000.0, 'state': 'program_directory', 'resource_calendar_id': self.calendar.id, }) employee2.contract_id = contract2 self.env['hr.attendance.transaction'].create({ 'employee_id': employee2.id, 'calendar_id': self.calendar.id, 'attending_type': 'in_cal', 'sequence': 1, 'date': date(2023, 10, 2), 'plan_hours': 8.0, 'is_absent': True }) self.report.write({'selected_employee_ids': [(4, employee2.id)]}) self.report.generate_report() self.assertFalse(self.report.line_ids[0].advantage_id) self.assertFalse(self.report.line_ids[1].advantage_id) self.report.approved() self.assertTrue(self.report.line_ids[0].advantage_id) self.assertTrue(self.report.line_ids[1].advantage_id) self.assertNotEqual( self.report.line_ids[0].advantage_id.id, self.report.line_ids[1].advantage_id.id )