224 lines
8.2 KiB
Python
224 lines
8.2 KiB
Python
# -*- coding: utf-8 -*-
|
|
from odoo.tests.common import TransactionCase
|
|
from odoo.exceptions import UserError, ValidationError
|
|
from odoo import fields
|
|
from datetime import date, datetime, timedelta
|
|
|
|
|
|
class TestPayrollRules(TransactionCase):
|
|
|
|
def setUp(self):
|
|
super(TestPayrollRules, self).setUp()
|
|
|
|
self.env.user.tz = 'UTC'
|
|
self.company = self.env.ref('base.main_company')
|
|
|
|
self.category_basic = self.env['hr.salary.rule.category'].create({
|
|
'name': 'Basic',
|
|
'code': 'BASIC',
|
|
})
|
|
self.structure = self.env['hr.payroll.structure'].create({
|
|
'name': 'Test Structure',
|
|
'code': 'TEST_STRUCT',
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
attendances = []
|
|
for day in range(5): # 0=Monday to 4=Friday
|
|
attendances.append((0, 0, {
|
|
'name': 'Morning',
|
|
'dayofweek': str(day),
|
|
'hour_from': 8,
|
|
'hour_to': 12,
|
|
'day_period': 'morning',
|
|
}))
|
|
attendances.append((0, 0, {
|
|
'name': 'Afternoon',
|
|
'dayofweek': str(day),
|
|
'hour_from': 13,
|
|
'hour_to': 17,
|
|
'day_period': 'afternoon',
|
|
}))
|
|
|
|
self.calendar = self.env['resource.calendar'].create({
|
|
'name': 'Standard 40 Hours UTC',
|
|
'tz': 'UTC',
|
|
'hours_per_day': 8.0,
|
|
'attendance_ids': attendances,
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
self.employee = self.env['hr.employee'].create({
|
|
'name': 'Test Employee Payroll',
|
|
'company_id': self.company.id,
|
|
'resource_calendar_id': self.calendar.id,
|
|
})
|
|
|
|
if self.employee.resource_id:
|
|
self.employee.resource_id.write({
|
|
'calendar_id': self.calendar.id,
|
|
'tz': 'UTC',
|
|
})
|
|
|
|
self.contract = self.env['hr.contract'].create({
|
|
'name': 'Contract for Test',
|
|
'employee_id': self.employee.id,
|
|
'struct_id': self.structure.id,
|
|
'wage': 5000.0,
|
|
'state': 'open',
|
|
'date_start': date.today() - timedelta(days=100),
|
|
'resource_calendar_id': self.calendar.id,
|
|
'schedule_pay': 'monthly',
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
self.payslip = self.env['hr.payslip'].create({
|
|
'employee_id': self.employee.id,
|
|
'contract_id': self.contract.id,
|
|
'struct_id': self.structure.id,
|
|
'date_from': date.today().replace(day=1),
|
|
'date_to': (date.today().replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1),
|
|
'company_id': self.company.id,
|
|
})
|
|
|
|
def test_satisfy_condition_python(self):
|
|
rule = self.env['hr.salary.rule'].create({
|
|
'name': 'Python Condition Rule',
|
|
'sequence': 10,
|
|
'code': 'PY_COND',
|
|
'category_id': self.category_basic.id,
|
|
'condition_select': 'python',
|
|
'condition_python': 'result = contract.wage > 3000',
|
|
'amount_select': 'fix',
|
|
'amount_fix': 100.0,
|
|
})
|
|
localdict = {'contract': self.contract, 'employee': self.employee}
|
|
self.assertTrue(rule._satisfy_condition(localdict))
|
|
|
|
rule.condition_python = 'result = contract.wage > 6000'
|
|
self.assertFalse(rule._satisfy_condition(localdict))
|
|
|
|
def test_satisfy_condition_range(self):
|
|
rule = self.env['hr.salary.rule'].create({
|
|
'name': 'Range Condition Rule',
|
|
'sequence': 10,
|
|
'code': 'RANGE_COND',
|
|
'category_id': self.category_basic.id,
|
|
'condition_select': 'range',
|
|
'condition_range': 'contract.wage',
|
|
'condition_range_min': 1000,
|
|
'condition_range_max': 6000,
|
|
'amount_select': 'fix',
|
|
'amount_fix': 100.0,
|
|
})
|
|
localdict = {'contract': self.contract}
|
|
self.assertTrue(rule._satisfy_condition(localdict))
|
|
|
|
self.contract.wage = 8000
|
|
self.assertFalse(rule._satisfy_condition(localdict))
|
|
|
|
def test_compute_rule_percentage(self):
|
|
rule = self.env['hr.salary.rule'].create({
|
|
'name': 'Percentage Rule',
|
|
'sequence': 10,
|
|
'code': 'PERCENT',
|
|
'category_id': self.category_basic.id,
|
|
'amount_select': 'percentage',
|
|
'amount_percentage_base': 'contract.wage',
|
|
'amount_percentage': 10.0,
|
|
'quantity': '1.0',
|
|
})
|
|
localdict = {'contract': self.contract}
|
|
amount, qty, rate = rule._compute_rule(localdict)
|
|
self.assertEqual(amount, 5000.0)
|
|
self.assertEqual(amount * qty * rate / 100.0, 500.0)
|
|
|
|
def test_compute_rule_python_code(self):
|
|
rule = self.env['hr.salary.rule'].create({
|
|
'name': 'Python Code Rule',
|
|
'sequence': 10,
|
|
'code': 'PY_CODE',
|
|
'category_id': self.category_basic.id,
|
|
'amount_select': 'code',
|
|
'amount_python_compute': 'result = contract.wage + 500',
|
|
})
|
|
localdict = {'contract': self.contract}
|
|
amount, qty, rate = rule._compute_rule(localdict)
|
|
self.assertEqual(amount, 5500.0)
|
|
|
|
def test_get_contract(self):
|
|
old_contract = self.env['hr.contract'].create({
|
|
'name': 'Old Contract',
|
|
'employee_id': self.employee.id,
|
|
'wage': 4000,
|
|
'state': 'close',
|
|
'date_start': date.today() - timedelta(days=400),
|
|
'date_end': date.today() - timedelta(days=200),
|
|
'resource_calendar_id': self.calendar.id,
|
|
'struct_id': self.structure.id,
|
|
})
|
|
|
|
date_from = date.today().replace(day=1)
|
|
date_to = (date.today().replace(day=1) + timedelta(days=32)).replace(day=1) - timedelta(days=1)
|
|
contract_ids = self.env['hr.payslip'].get_contract(self.employee, date_from, date_to)
|
|
|
|
self.assertIn(self.contract.id, contract_ids)
|
|
self.assertNotIn(old_contract.id, contract_ids)
|
|
|
|
def test_get_worked_day_lines(self):
|
|
today = date.today()
|
|
days_ahead = 0 - today.weekday()
|
|
if days_ahead <= 0:
|
|
days_ahead += 7
|
|
|
|
next_monday = today + timedelta(days=days_ahead)
|
|
date_from = next_monday
|
|
date_to = next_monday + timedelta(days=4)
|
|
try:
|
|
worked_days = self.env['hr.payslip'].get_worked_day_lines(self.contract, date_from, date_to)
|
|
|
|
work_entry = next((item for item in worked_days if item['code'] == 'WORK100'), None)
|
|
|
|
self.assertIsNotNone(work_entry, "WORK100 entry not found in result")
|
|
|
|
except AttributeError as e:
|
|
print(f"Skipping test_get_worked_day_lines due to missing dependency: {e}")
|
|
|
|
def test_payslip_line_compute_total(self):
|
|
line = self.env['hr.payslip.line'].create({
|
|
'slip_id': self.payslip.id,
|
|
'name': 'Test Line',
|
|
'code': 'TEST',
|
|
'contract_id': self.contract.id,
|
|
'salary_rule_id': self.env['hr.salary.rule'].search([], limit=1).id,
|
|
'employee_id': self.employee.id,
|
|
'quantity': 2.0,
|
|
'amount': 500.0,
|
|
'rate': 50.0,
|
|
'category_id': self.category_basic.id,
|
|
})
|
|
self.assertEqual(line.total, 500.0)
|
|
|
|
def test_get_inputs(self):
|
|
rule_with_input = self.env['hr.salary.rule'].create({
|
|
'name': 'Rule with Input',
|
|
'code': 'INPUT_RULE',
|
|
'category_id': self.category_basic.id,
|
|
'amount_select': 'fix',
|
|
'amount_fix': 0.0,
|
|
'sequence': 50,
|
|
})
|
|
self.env['hr.rule.input'].create({
|
|
'name': 'Commission Input',
|
|
'code': 'COMMISSION',
|
|
'input_id': rule_with_input.id,
|
|
})
|
|
self.structure.write({'rule_ids': [(4, rule_with_input.id)]})
|
|
|
|
date_from = date.today()
|
|
date_to = date.today()
|
|
inputs = self.env['hr.payslip'].get_inputs(self.contract, date_from, date_to)
|
|
|
|
found_input = next((i for i in inputs if i['code'] == 'COMMISSION'), None)
|
|
self.assertIsNotNone(found_input)
|
|
self.assertEqual(found_input['contract_id'], self.contract.id) |