229 lines
9.9 KiB
Python
229 lines
9.9 KiB
Python
# Part of Odoo. See LICENSE file for full copyright and licensing details.
|
|
|
|
import odoo.tests
|
|
|
|
from datetime import date, datetime
|
|
|
|
from odoo.addons.base.tests.common import BaseCommon
|
|
|
|
|
|
@odoo.tests.tagged('post_install', '-at_install')
|
|
class TestAnonymizedPayruns(BaseCommon):
|
|
|
|
@classmethod
|
|
def setUpClass(cls):
|
|
super().setUpClass()
|
|
|
|
cls.credit_account_full, cls.credit_account_half, cls.debit_account_full, cls.debit_account_half = cls.env['account.account'].create([
|
|
{
|
|
'name': 'Credit Account Full',
|
|
'account_type': 'asset_cash',
|
|
'code': 0
|
|
},
|
|
{
|
|
'name': 'Credit Account Half',
|
|
'account_type': 'asset_cash',
|
|
'code': 1
|
|
},
|
|
{
|
|
'name': 'Debit Account',
|
|
'account_type': 'asset_cash',
|
|
'code': 2
|
|
},
|
|
{
|
|
'name': 'Debit Account Half',
|
|
'account_type': 'asset_cash',
|
|
'code': 3
|
|
},
|
|
])
|
|
|
|
cls.test_journal = cls.env['account.journal'].create({
|
|
'name': 'Test Journal',
|
|
'type': 'credit',
|
|
'code': 'TEST',
|
|
'default_account_id': cls.credit_account_full.id,
|
|
'currency_id': cls.env.ref('base.USD').id,
|
|
'company_id': cls.company.id,
|
|
})
|
|
|
|
cls.test_structure_type_id = cls.env['hr.payroll.structure.type'].create({
|
|
'name': 'Nordic Warrior',
|
|
'country_id': False,
|
|
})
|
|
|
|
cls.plan = cls.env['account.analytic.plan'].create({'name': 'Plan'})
|
|
|
|
cls.aa_1, cls.aa_2 = cls.env['account.analytic.account'].create([
|
|
{
|
|
'name': 'Analytic Account 1',
|
|
'plan_id': cls.plan.id,
|
|
},
|
|
{
|
|
'name': 'Analytic Account 2',
|
|
'plan_id': cls.plan.id,
|
|
}
|
|
])
|
|
|
|
cls.nordic_warrior_structure = cls.env['hr.payroll.structure'].create({
|
|
'name': 'Salary Structure for Nordic Warriors',
|
|
'rule_ids': [
|
|
(0, 0, {
|
|
'name': 'Basic Salary',
|
|
'amount_select': 'percentage',
|
|
'amount_percentage': 100,
|
|
'amount_percentage_base': 'version.wage',
|
|
'code': 'BASIC',
|
|
'category_id': cls.env.ref('hr_payroll.BASIC').id,
|
|
'sequence': 1,
|
|
'account_credit': cls.credit_account_full.id,
|
|
'account_debit': cls.debit_account_full.id,
|
|
'analytic_distribution': {str(cls.aa_1.id) + ',' + str(cls.aa_2.id): 100}
|
|
}), (0, 0, {
|
|
'name': 'Additional Basic Salary',
|
|
'amount_select': 'percentage',
|
|
'amount_percentage': 50,
|
|
'amount_percentage_base': 'version.wage',
|
|
'code': 'BASICADD',
|
|
'category_id': cls.env.ref('hr_payroll.BASIC').id,
|
|
'sequence': 2,
|
|
'account_credit': cls.credit_account_half.id,
|
|
'account_debit': cls.debit_account_half.id
|
|
}),
|
|
],
|
|
'type_id': cls.test_structure_type_id.id,
|
|
'journal_id': cls.test_journal.id
|
|
})
|
|
|
|
cls.test_structure_type_id['default_struct_id'] = cls.nordic_warrior_structure.id
|
|
|
|
cls.emp_thorfinn, cls.emp_einar, cls.emp_canute = cls.env['hr.employee'].create([
|
|
{
|
|
'name': 'Thorfinn',
|
|
'company_id': cls.company.id,
|
|
'version_ids': [
|
|
(0, 0, {
|
|
'name': 'Thorfinn Version',
|
|
'company_id': cls.company.id,
|
|
'contract_date_start': datetime(2026, 3, 1),
|
|
'contract_date_end': datetime(2026, 3, 31),
|
|
'wage': 3000,
|
|
'date_version': datetime(2026, 3, 1).date(),
|
|
'structure_id': cls.nordic_warrior_structure.id,
|
|
'structure_type_id': cls.nordic_warrior_structure.type_id.id,
|
|
})]
|
|
},
|
|
{
|
|
'name': 'Einar',
|
|
'company_id': cls.company.id,
|
|
'version_ids': [
|
|
(0, 0, {
|
|
'name': 'Einar Version',
|
|
'company_id': cls.company.id,
|
|
'contract_date_start': datetime(2026, 3, 1),
|
|
'contract_date_end': datetime(2026, 3, 31),
|
|
'wage': 3000,
|
|
'date_version': datetime(2026, 3, 1).date(),
|
|
'structure_id': cls.nordic_warrior_structure.id,
|
|
'structure_type_id': cls.nordic_warrior_structure.type_id.id,
|
|
})]
|
|
},
|
|
{
|
|
'name': 'Canute',
|
|
'company_id': cls.company.id,
|
|
'version_ids': [
|
|
(0, 0, {
|
|
'name': 'Canute Version',
|
|
'company_id': cls.company.id,
|
|
'contract_date_start': datetime(2026, 3, 1),
|
|
'contract_date_end': datetime(2026, 3, 31),
|
|
'wage': 3000,
|
|
'date_version': datetime(2026, 3, 1).date(),
|
|
'structure_id': cls.nordic_warrior_structure.id,
|
|
'structure_type_id': cls.nordic_warrior_structure.type_id.id,
|
|
})]
|
|
},
|
|
])
|
|
|
|
for emp in [cls.emp_thorfinn, cls.emp_einar, cls.emp_canute]:
|
|
emp.version_ids = emp.version_ids.filtered('name') # Remove unnamed versions created by default
|
|
|
|
def test_check_correct_anonymization_with_analytic_distribution(self):
|
|
self.company.batch_payroll_move_lines = True
|
|
payrun = self.env['hr.payslip.run'].create({
|
|
'name': 'Test Payrun',
|
|
'date_start': date(2026, 3, 1),
|
|
'date_end': date(2026, 3, 31),
|
|
})
|
|
employees = [self.emp_thorfinn.id, self.emp_einar.id, self.emp_canute.id]
|
|
payrun.generate_payslips(employee_ids=employees)
|
|
payrun.action_validate()
|
|
|
|
expected_result = [
|
|
('Basic Salary', {str(self.aa_1.id) + ',' + str(self.aa_2.id): 100}, 0.0, 9000.0),
|
|
('Basic Salary', {str(self.aa_1.id) + ',' + str(self.aa_2.id): 100}, 9000.0, 0.0),
|
|
('Additional Basic Salary', False, 0.0, 4500.0),
|
|
('Additional Basic Salary', False, 4500.0, 0.0),
|
|
]
|
|
|
|
real_result = [(line.name, line.analytic_distribution, line.credit, line.debit) for line in payrun.move_id.line_ids]
|
|
|
|
self.assertEqual(real_result, expected_result)
|
|
|
|
def test_batch_move_lines_keeps_distinct_analytic_percentages(self):
|
|
""" With batch_payroll_move_lines, employees sharing the same analytic
|
|
account but with different percentages must not be merged into a single
|
|
move line — each distribution must keep its own line.
|
|
"""
|
|
self.company.batch_payroll_move_lines = True
|
|
self.nordic_warrior_structure.rule_ids.analytic_distribution = False
|
|
self.emp_thorfinn.version_id.analytic_distribution = {str(self.aa_1.id): 50}
|
|
self.emp_einar.version_id.analytic_distribution = {str(self.aa_1.id): 70}
|
|
self.emp_canute.version_id.analytic_distribution = {str(self.aa_1.id): 50}
|
|
|
|
payrun = self.env['hr.payslip.run'].create({
|
|
'name': 'Test Payrun Distinct Distributions',
|
|
'date_start': date(2026, 3, 1),
|
|
'date_end': date(2026, 3, 31),
|
|
})
|
|
payrun.generate_payslips(employee_ids=[self.emp_thorfinn.id, self.emp_einar.id, self.emp_canute.id])
|
|
payrun.action_validate()
|
|
|
|
expected_result = [
|
|
('Basic Salary', {str(self.aa_1.id): 50}, 0.0, 6000.0),
|
|
('Basic Salary', {str(self.aa_1.id): 50}, 6000.0, 0.0),
|
|
('Additional Basic Salary', {str(self.aa_1.id): 50}, 0.0, 3000.0),
|
|
('Additional Basic Salary', {str(self.aa_1.id): 50}, 3000.0, 0.0),
|
|
('Basic Salary', {str(self.aa_1.id): 70}, 0.0, 3000.0),
|
|
('Basic Salary', {str(self.aa_1.id): 70}, 3000.0, 0.0),
|
|
('Additional Basic Salary', {str(self.aa_1.id): 70}, 0.0, 1500.0),
|
|
('Additional Basic Salary', {str(self.aa_1.id): 70}, 1500.0, 0.0),
|
|
]
|
|
real_result = [(line.name, line.analytic_distribution, line.credit, line.debit) for line in payrun.move_id.line_ids]
|
|
self.assertEqual(real_result, expected_result)
|
|
|
|
def test_batch_move_lines_merges_identical_version_distributions(self):
|
|
""" With batch_payroll_move_lines, employees sharing the exact same
|
|
version-level analytic distribution must be merged into a single line.
|
|
"""
|
|
self.company.batch_payroll_move_lines = True
|
|
self.nordic_warrior_structure.rule_ids.analytic_distribution = False
|
|
distribution = {str(self.aa_1.id): 60}
|
|
(self.emp_thorfinn + self.emp_einar + self.emp_canute).version_id.analytic_distribution = distribution
|
|
|
|
payrun = self.env['hr.payslip.run'].create({
|
|
'name': 'Test Payrun Identical Distributions',
|
|
'date_start': date(2026, 3, 1),
|
|
'date_end': date(2026, 3, 31),
|
|
})
|
|
payrun.generate_payslips(employee_ids=[self.emp_thorfinn.id, self.emp_einar.id, self.emp_canute.id])
|
|
payrun.action_validate()
|
|
|
|
expected_result = [
|
|
('Basic Salary', distribution, 0.0, 9000.0),
|
|
('Basic Salary', distribution, 9000.0, 0.0),
|
|
('Additional Basic Salary', distribution, 0.0, 4500.0),
|
|
('Additional Basic Salary', distribution, 4500.0, 0.0),
|
|
]
|
|
real_result = [(line.name, line.analytic_distribution, line.credit, line.debit) for line in payrun.move_id.line_ids]
|
|
self.assertEqual(real_result, expected_result)
|