Files
enterprise-addons/l10n_sa_hr_payroll_account/tests/test_salary_rules.py
T

289 lines
12 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from datetime import date, datetime
from odoo.fields import Command
from odoo.tests.common import tagged
from odoo.addons.hr_payroll_account.tests.common import TestPayslipValidationCommon
@tagged('post_install', 'post_install_l10n', '-at_install', 'payslips_validation')
class TestPayslipValidation(TestPayslipValidationCommon):
@classmethod
@TestPayslipValidationCommon.setup_country('sa')
def setUpClass(cls):
super().setUpClass()
cls.env.user.group_ids |= cls.env.ref('hr_payroll.group_hr_payroll_manager')
cls._setup_common(
country=cls.env.ref('base.sa'),
structure=cls.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure'),
structure_type=cls.env.ref('l10n_sa_hr_payroll.ksa_employee_payroll_structure_type'),
contract_fields={
'wage': 10000.0,
'l10n_sa_housing_allowance': 400.0,
'l10n_sa_transportation_allowance': 200.0,
'l10n_sa_other_allowances': 150.0,
'l10n_sa_number_of_days': 20.0,
}
)
cls.env.user.group_ids |= cls.env.ref('hr_holidays.group_hr_holidays_manager')
cls.saudi_work_contact = cls.env['res.partner'].create({
'name': 'KSA Local Employee',
'company_id': cls.env.company.id,
})
cls.saudi_employee = cls.env['hr.employee'].create({
'name': 'KSA Local Employee',
'address_id': cls.saudi_work_contact.id,
'company_id': cls.env.company.id,
'country_id': cls.env.ref('base.sa').id,
'structure_type_id': cls.env.ref('l10n_sa_hr_payroll.ksa_employee_payroll_structure_type').id,
'date_version': date(2024, 1, 1),
'contract_date_start': date(2024, 1, 1),
'wage': 12000,
'l10n_sa_housing_allowance': 1000,
'l10n_sa_transportation_allowance': 200,
'l10n_sa_other_allowances': 500,
'l10n_sa_number_of_days': 21,
'l10n_sa_iqama_annual_amount': 6000.0,
'l10n_sa_medical_insurance_annual_amount': 4800.0,
'l10n_sa_work_permit_annual_amount': 3600.0,
})
cls.saudi_contract = cls.saudi_employee.version_id
cls.compensable_timeoff_type = cls.env['hr.leave.type'].create({
'name': "KSA Compensable Leaves",
'company_id': cls.env.company.id,
})
cls.env.company.write({
"l10n_sa_annual_leave_type_id": cls.compensable_timeoff_type.id,
})
cls.env['hr.leave.allocation'].create({
'employee_id': cls.saudi_employee.id,
'date_from': date(2024, 1, 1),
'holiday_status_id': cls.compensable_timeoff_type.id,
'number_of_days': 21,
'state': 'confirm',
}).action_approve()
def test_saudi_payslip(self):
payslip = self._generate_payslip(
date(2024, 1, 1), date(2024, 1, 31),
employee_id=self.saudi_employee.id,
version_id=self.saudi_employee.version_id.id,
struct_id=self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id)
payslip_results = {
'BASIC': 12000.0,
'GOSI_COMP': -1527.5,
'GOSI_EMP': -1267.5,
'HOUALLOW': 1000.0,
'OTALLOW': 500.0,
'TRAALLOW': 200.0,
'EOSP': 570.83,
'MEDICAL': 400.0,
'IQAMA': 500.0,
'WORKPER': 300.0,
'ANNUALP': 799.17,
'GROSS': 13700.0,
'NET': 12432.5,
'NETCOST': 14027.5,
}
self._validate_payslip(payslip, payslip_results)
def test_saudi_payslip_laid_off(self):
self.saudi_employee.write({
'active': False,
'departure_reason_id': self.env.ref('l10n_sa_hr_payroll.saudi_departure_clause_77').id,
'departure_date': date(2024, 3, 31),
})
self.saudi_contract.date_end = date(2024, 3, 31)
payslip = self._generate_payslip(
date(2024, 3, 1), date(2024, 3, 31),
employee_id=self.saudi_employee.id,
version_id=self.saudi_employee.version_id.id,
struct_id=self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id)
payslip_results = {
'BASIC': 12000.0,
'GOSI_COMP': -1527.5,
'GOSI_EMP': -1267.5,
'HOUALLOW': 1000.0,
'OTALLOW': 500.0,
'TRAALLOW': 200.0,
'EOSALLOW': 27400.0,
'EOSB': 13700.0,
'ANNUALCOMP': 2397.5,
'MEDICAL': 400.0,
'IQAMA': 500.0,
'WORKPER': 300.0,
'GROSS': 57197.5,
'NET': 55930.0,
'NETCOST': 57525.0,
}
self._validate_payslip(payslip, payslip_results)
def test_payslip_overtime_1(self):
payslip = self._generate_payslip(date(2024, 1, 1), date(2024, 1, 31))
work_entry = self.env['hr.work.entry'].create({
'name': 'OT',
'employee_id': payslip.employee_id.id,
'version_id': payslip.employee_id.version_id.id,
'date': date(2024, 1, 1),
'duration': 4,
'work_entry_type_id': self.env.ref('hr_work_entry.work_entry_type_overtime').id,
})
work_entry.action_validate()
payslip.compute_sheet()
payslip_results = {
'BASIC': 10000.0,
'GOSI_COMP': -1222.0,
'GOSI_EMP': -1014.0,
'HOUALLOW': 400.0,
'OTALLOW': 150.0,
'TRAALLOW': 200.0,
'EOSP': 895.83,
'ANNUALP': 597.22,
'GROSS': 10750.0,
'NET': 9736.0,
'NETCOST': 11972.0,
}
self._validate_payslip(payslip, payslip_results)
def test_salary_advance_payslip(self):
# Should not use `_generate_payslip` because we need to make sure that `ADV` input line is created with 0.0 amount
payslip = self.env['hr.payslip'].create([{
'name': "Test Payslip",
'employee_id': self.saudi_employee.id,
'version_id': self.saudi_employee.version_id.id,
'company_id': self.env.company.id,
'struct_id': self.env.ref('l10n_sa_hr_payroll.l10n_sa_salary_advance_and_loan').id,
'date_from': date(2024, 1, 1),
'date_to': date(2024, 1, 31),
}])
self.assertEqual(payslip.input_line_ids.filtered(lambda x: x.code == 'ADV').amount, 0.0)
payslip.input_line_ids.filtered(lambda x: x.code == 'ADV').amount = 2500.0
payslip.compute_sheet()
payslip_results = {'ADV': 2500.0, 'NET': 2500.0}
self._validate_payslip(payslip, payslip_results)
def test_saudi_payslip_after_salary_advance_payslip(self):
adv_payslip = self.env['hr.payslip'].create([{
'name': "Test Adv Payslip",
'employee_id': self.saudi_employee.id,
'version_id': self.saudi_employee.version_id.id,
'company_id': self.env.company.id,
'struct_id': self.env.ref('l10n_sa_hr_payroll.l10n_sa_salary_advance_and_loan').id,
'date_from': date(2024, 1, 1),
'date_to': date(2024, 1, 31),
}])
adv_payslip.input_line_ids.filtered(lambda x: x.code == 'ADV').amount = 2500.0
adv_payslip.compute_sheet()
adv_payslip.action_payslip_done()
payslip = self.env['hr.payslip'].create([{
'name': "Test Payslip",
'employee_id': self.saudi_employee.id,
'version_id': self.saudi_employee.version_id.id,
'company_id': self.env.company.id,
'struct_id': self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id,
'date_from': date(2024, 2, 1),
'date_to': date(2024, 2, 29),
}])
self.assertEqual(payslip.input_line_ids.filtered(lambda x: x.code == 'ADV').amount, 2500.0)
payslip.compute_sheet()
self.assertEqual(payslip._get_line_values(['ADVDED'])['ADVDED'][payslip.id]['total'], -2500.0)
def test_salary_loan_payslip(self):
loan_attachment = self.env['hr.salary.attachment'].create({
'employee_ids': [Command.link(self.saudi_employee.id)],
'description': 'Car Loan',
'other_input_type_id': self.env.ref('l10n_sa_hr_payroll.l10n_sa_input_loan_deduction').id,
'date_start': date(2024, 1, 1),
'monthly_amount': 200,
'total_amount': 600,
})
loan_action = loan_attachment.action_create_loan_payslip()
loan_payslip = self.env['hr.payslip'].browse(loan_action['res_id'])
self.assertEqual(loan_payslip.input_line_ids.filtered(lambda x: x.code == 'LOAN_DEDUCTION').amount, 600.0)
payslip = self.env['hr.payslip'].create([{
'name': "Test Payslip",
'employee_id': self.saudi_employee.id,
'version_id': self.saudi_employee.version_id.id,
'company_id': self.env.company.id,
'struct_id': self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id,
'date_from': date(2024, 1, 1),
'date_to': date(2024, 1, 31),
}])
self.assertEqual(payslip.input_line_ids.filtered(lambda x: x.code == 'LOAN_DEDUCTION').amount, 200.0)
payslip.compute_sheet()
self.assertEqual(payslip._get_line_values(['LOAN_DEDUCTION'])['LOAN_DEDUCTION'][payslip.id]['total'], -200.0)
def test_saudi_payslip_with_attendance(self):
if self.env["ir.module.module"]._get("hr_payroll_attendance").state != "installed":
self.skipTest(
"The test was skipped because the 'hr_payroll_attendance' module isn’t installed; therefore, attendance-based entries are unavailable."
)
self.saudi_employee.work_entry_source = "attendance"
payslip = self.env['hr.payslip'].create([{
'name': "Test Payslip",
'employee_id': self.saudi_employee.id,
'version_id': self.saudi_employee.version_id.id,
'company_id': self.env.company.id,
'struct_id': self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id,
'date_from': date(2026, 1, 1),
'date_to': date(2026, 1, 31),
}])
payslip.compute_sheet()
payslip_results = {
'BASIC': 0.0,
}
self._validate_payslip(payslip, payslip_results, skip_lines=True)
def test_saudi_payslip_gosi_contribution(self):
self.env['resource.calendar.leaves'].create([{
'name': "Absence",
'company_id': self.env.company.id,
'resource_id': self.saudi_employee.resource_id.id,
'date_from': datetime(2024, 1, 10, 6, 0, 0),
'date_to': datetime(2024, 1, 11, 22, 0, 0),
'time_type': "leave",
'work_entry_type_id': self.env.ref('hr_work_entry.work_entry_type_unpaid_leave').id
}])
payslip = self._generate_payslip(
date(2024, 1, 1), date(2024, 1, 31),
employee_id=self.saudi_employee.id,
version_id=self.saudi_employee.version_id.id,
struct_id=self.env.ref('l10n_sa_hr_payroll.ksa_saudi_employee_payroll_structure').id)
payslip_results = {
'BASIC': 12000.0,
'HOUALLOW': 1000.0,
'TRAALLOW': 200.0,
'OTALLOW': 500.0,
'GOSI_COMP': -1527.5,
'GOSI_EMP': -1267.5,
'UNPAID': -685.0,
'EOSP': 570.83,
'MEDICAL': 400.0,
'IQAMA': 500.0,
'WORKPER': 300.0,
'ANNUALP': 799.17,
'GROSS': 13700.0,
'NET': 11747.5,
'NETCOST': 13342.5,
}
self._validate_payslip(payslip, payslip_results)