189 lines
6.8 KiB
Python
189 lines
6.8 KiB
Python
# -*- 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")
|
|
|