# -*- coding: utf-8 -*- from odoo.tests.common import TransactionCase from odoo.exceptions import UserError from odoo import fields from datetime import date, timedelta class TestOvertimeProcess(TransactionCase): def setUp(self): super(TestOvertimeProcess, self).setUp() self.account_type_expenses = self.env['account.account'].search([('account_type', '=', 'expense')], limit=1) if not self.account_type_expenses: user_type_expense = self.env.ref('account.data_account_type_expenses') self.account_type_expenses = self.env['account.account'].create({ 'name': 'Overtime Expense', 'code': 'X6000', 'account_type': 'expense', 'reconcile': True, }) self.overtime_account = self.account_type_expenses self.journal_account = self.env['account.account'].search([('account_type', '=', 'asset_cash')], limit=1) self.journal = self.env['account.journal'].create({ 'name': 'Overtime Journal', 'type': 'cash', 'code': 'OTJ', 'default_account_id': self.journal_account.id, }) self.working_hours = self.env['resource.calendar'].create({ 'name': 'Standard 40 Hours', 'hours_per_day': 8.0, # 'max_overtime_hour': 10.0, # 'overtime_factor_daily': 1.5, # 'overtime_factor_holiday': 2.0, # 'work_days': 20, # 'work_hour': 8, # 'journal_overtime_id': self.journal.id, # 'account_overtime_id': self.overtime_account.id, }) self.working_hours.write({ 'max_overtime_hour': 20.0, 'overtime_factor_daily': 1.5, 'overtime_factor_holiday': 2.0, 'work_days': 30, 'work_hour': 8, 'journal_overtime_id': self.journal.id, 'account_overtime_id': self.overtime_account.id, }) self.employee = self.env['hr.employee'].create({ 'name': 'Ahmed Tester', 'state': 'open', 'first_hiring_date': date.today() - timedelta(days=365), # موظف منذ سنة }) self.contract = self.env['hr.contract'].create({ 'name': 'Contract for Ahmed', 'employee_id': self.employee.id, 'state': 'open', 'wage': 5000, # الراتب الأساسي 'resource_calendar_id': self.working_hours.id, # 'total_allowance': 1000, # 'salary': 5000, }) self.contract.write({ 'total_allowance': 1000, 'salary': 5000, }) def test_overtime_period_constraint_cross_month(self): # Arrange date_from = date(2025, 1, 31) date_to = date(2025, 2, 1) # شهر مختلف # Act & Assert with self.assertRaises(UserError): request = self.env['employee.overtime.request'].create({ 'employee_id': self.employee.id, 'request_date': date_from, 'date_from': date_from, 'date_to': date_to, 'line_ids_over_time': [(0, 0, { 'employee_id': self.employee.id, 'over_time_workdays_hours': 5.0, })] }) request.line_ids_over_time.get_max_remain_hours() def test_max_hours_constraint_and_exception(self): # Arrange self.working_hours.write({'max_overtime_hour': 10.0}) date_from = date(2025, 3, 1) date_to = date(2025, 3, 5) with self.assertRaises(UserError): self.env['employee.overtime.request'].create({ 'employee_id': self.employee.id, 'date_from': date_from, 'date_to': date_to, 'exception': False, 'line_ids_over_time': [(0, 0, { 'employee_id': self.employee.id, 'over_time_workdays_hours': 15.0, })] }) request = self.env['employee.overtime.request'].create({ 'employee_id': self.employee.id, 'date_from': date_from, 'date_to': date_to, 'exception': True, 'line_ids_over_time': [(0, 0, { 'employee_id': self.employee.id, 'over_time_workdays_hours': 15.0, })] }) # Assert self.assertTrue(request.line_ids_over_time.exception, "Exception flag should be propagated to lines") def test_calculate_daily_rate(self): expected_hourly_rate = 8500.0 / 240.0 # Act request = self.env['employee.overtime.request'].create({ 'employee_id': self.employee.id, 'date_from': date(2025, 4, 1), 'date_to': date(2025, 4, 1), 'line_ids_over_time': [(0, 0, { 'employee_id': self.employee.id, 'over_time_workdays_hours': 2.0, })] }) line = request.line_ids_over_time[0] # Assert self.assertAlmostEqual(line.daily_hourly_rate, expected_hourly_rate, places=2, msg="Daily hourly rate calculation is incorrect") expected_total_price = expected_hourly_rate * 2.0 self.assertAlmostEqual(line.price_hour, expected_total_price, places=2, msg="Total price calculation is incorrect") def test_accounting_transfer_validation(self): # Arrange request = self.env['employee.overtime.request'].create({ 'employee_id': self.employee.id, 'date_from': date(2025, 5, 1), 'date_to': date(2025, 5, 2), 'transfer_type': 'accounting', 'line_ids_over_time': [(0, 0, { 'employee_id': self.employee.id, 'over_time_workdays_hours': 10.0, })] }) request.state = 'executive_office' # Act request.validated() # Assert self.assertEqual(request.state, 'validated', "State should be validated") line = request.line_ids_over_time[0] self.assertTrue(line.move_id, "Account Move should be created") self.assertEqual(line.move_id.state, 'draft', "Move should be in draft state initially") debit_line = line.move_id.line_ids.filtered(lambda l: l.debit > 0) credit_line = line.move_id.line_ids.filtered(lambda l: l.credit > 0) self.assertTrue(debit_line and credit_line, "Should have debit and credit lines") self.assertEqual(debit_line.account_id, self.overtime_account, "Debit account mismatch") self.assertEqual(credit_line.account_id, self.journal.default_account_id, "Credit account mismatch") self.assertAlmostEqual(debit_line.debit, line.price_hour, places=2, msg="Debit amount mismatch")