Files
enterprise-addons/hr_payroll/tests/test_payslip_flow.py
T

678 lines
30 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import datetime
from dateutil.relativedelta import relativedelta
from odoo import Command
from odoo.tests import HttpCase, tagged
from odoo.addons.hr_payroll.tests.common import TestPayslipBase
from odoo.addons.mail.tests.common import mail_new_test_user
from odoo.exceptions import UserError
from odoo.tests import Form
class TestPayslipFlow(TestPayslipBase):
def test_00_payslip_flow(self):
""" Testing payslip flow and report printing """
# I create an employee Payslip
richard_payslip = self.env['hr.payslip'].create({
'name': 'Payslip of Richard',
'employee_id': self.richard_emp.id,
})
payslip_input = self.env['hr.payslip.input'].search([('payslip_id', '=', richard_payslip.id)])
# I assign the amount to Input data
payslip_input.write({'amount': 5.0})
# I verify the payslip is in draft state
self.assertEqual(richard_payslip.state, 'draft', 'State not changed!')
richard_payslip.compute_sheet()
# Then I click on the 'Confirm' button on payslip
richard_payslip.action_payslip_done()
# I verify that the payslip is in validated state
self.assertEqual(richard_payslip.state, 'validated', 'State not changed!')
# Then I click on the 'Mark as paid' button on payslip
richard_payslip.action_payslip_paid()
# I verify that the payslip is in paid state
self.assertEqual(richard_payslip.state, 'paid', 'State not changed!')
# I want to check refund payslip so I click on refund button.
richard_payslip.refund_sheet()
# I check on new payslip Credit Note is checked or not.
payslip_refund = self.env['hr.payslip'].search([('name', 'like', 'Refund: '+ richard_payslip.name), ('credit_note', '=', True)])
self.assertTrue(bool(payslip_refund), "Payslip not refunded!")
# I want to generate a payslip from Payslip run.
payslip_run = self.env['hr.payslip.run'].create({
'date_end': '2011-09-30',
'date_start': '2011-09-01',
'name': 'Payslip for Employee'
})
# I create record for generating the payslip for this Payslip run.
# The contract of Richard starts in 2018, the payrun is for 2011, no valid versions can be found.
# So the generate_payslip without versions must raise an error
with self.assertRaises(UserError):
payslip_run.generate_payslips(employee_ids=[self.richard_emp.id])
def test_01_batch_with_specific_structure(self):
""" Generate payslips for the employee whose running contract is based on the same Salary Structure Type"""
specific_structure_type = self.env['hr.payroll.structure.type'].create({
'name': 'Structure Type Test'
})
specific_structure = self.env['hr.payroll.structure'].create({
'name': 'End of the Year Bonus - Test',
'type_id': specific_structure_type.id,
})
# 13th month pay
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'structure_id': specific_structure.id,
'name': 'End of the year bonus',
})
with self.assertRaises(UserError):
payslip_run.generate_payslips(payslip_run._get_valid_version_ids())
# Update the structure type and generate payslips again
specific_structure_type.default_struct_id = specific_structure.id
self.richard_emp.version_ids[0].structure_type_id = specific_structure_type.id
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'structure_id': specific_structure.id,
'name': 'Batch for Structure',
})
payslip_run.generate_payslips(payslip_run._get_valid_version_ids())
self.richard_emp.structure_type_id = specific_structure_type.id
self.assertTrue(payslip_run.slip_ids)
self.assertTrue(self.richard_emp.id in payslip_run.slip_ids.employee_id.ids)
self.assertEqual(len(payslip_run.slip_ids), 1)
self.assertEqual(payslip_run.slip_ids.struct_id.id, specific_structure.id)
def test_03_payslip_batch_with_payment_process(self):
'''
Test to check if some payslips in the batch are already paid,
the batch status can be updated to 'paid' without affecting
those already paid payslips.
'''
self.contract_jules = self.jules_emp.version_id.write({
'date_version': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'name': 'Contract for Jules',
'wage': 5000.33,
'employee_id': self.jules_emp.id,
'structure_type_id': self.structure_type.id,
})
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'name': 'Payment Test',
})
payslip_run.generate_payslips(employee_ids=[self.richard_emp.id, self.jules_emp.id])
payslip_run.action_validate()
self.assertEqual(len(payslip_run.slip_ids), 2)
self.assertTrue(all(payslip.state == 'validated' for payslip in payslip_run.slip_ids), 'State not changed!')
# Mark the first payslip as paid and store the paid date
payslip_run.slip_ids[0].action_payslip_paid()
paid_date = payslip_run.slip_ids[0].paid_date
self.assertEqual(payslip_run.slip_ids[0].state, 'paid', 'State not changed!')
self.assertEqual(payslip_run.slip_ids[1].state, 'validated', 'State not changed!')
payslip_run.action_paid()
self.assertEqual(payslip_run.state, '03_paid', 'State not changed!')
self.assertTrue(all(payslip.state == 'paid' for payslip in payslip_run.slip_ids), 'State not changed!')
self.assertEqual(payslip_run.slip_ids[0].paid_date, paid_date, 'payslip paid date should not be changed')
def test_04_payslip_batch_wizard_for_employee_selection_mode(self):
'''
Test employee_ids selection in batch payslip generating wizard
based on employee selection mode fields
'''
self.richard_emp.version_ids[0].contract_date_end = False
self.contract_jules = self.jules_emp.version_id.write({
'date_version': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'name': 'Contract for Jules',
'wage': 5000.33,
'employee_id': self.jules_emp.id,
})
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'name': 'Payment Test',
})
payslip_run.generate_payslips(employee_ids=[self.richard_emp.id, self.jules_emp.id])
self.assertEqual(len(payslip_run.slip_ids.employee_id.ids), 2)
self.assertTrue(all(employee in [self.richard_emp.id, self.jules_emp.id] for employee in payslip_run.slip_ids.employee_id.ids))
def test_05_payslip_batch_wizard_for_department_selection_mode(self):
'''
Test employee_ids selection in batch payslip generating wizard
based on department selection mode fields
'''
self.richard_emp.version_ids[0].date_end = False
self.contract_jules = self.jules_emp.version_id.write({
'date_version': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'name': 'Contract for Jules',
'wage': 5000.33,
'employee_id': self.jules_emp.id,
})
self.richard_emp.department_id = False
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'name': 'Payment Test',
})
employees = self.env["hr.employee"].search([('department_id', 'in', [self.dep_rd.id])])
payslip_run.generate_payslips(employee_ids=employees.ids)
self.assertEqual(len(employees.ids), 1)
self.assertEqual(employees.ids[0], self.jules_emp.id)
def test_06_payslip_batch_wizard_for_job_selection_mode(self):
'''
Test employee_ids selection in batch payslip generating wizard
based on job selection mode fields
'''
self.richard_emp.version_ids[0].date_end = False
job_developer = self.env['hr.job'].create({
'name': 'Experienced Developer',
'department_id': self.dep_rd.id,
'no_of_recruitment': 5,
})
self.richard_emp.job_id = job_developer.id
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'name': 'Payment Test',
})
employees = self.env["hr.employee"].search([('job_id', 'in', [job_developer.id])])
payslip_run.generate_payslips(employee_ids=employees.ids)
self.assertEqual(len(employees.ids), 1)
self.assertTrue(self.richard_emp in employees)
def test_07_payslip_batch_wizard_for_category_selection_mode(self):
'''
Test employee_ids selection in batch payslip generating wizard
based on category selection mode fields
'''
self.richard_emp.version_ids[0].date_end = False
self.contract_jules = self.jules_emp.version_id.write({
'date_version': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'name': 'Contract for Jules',
'wage': 5000.33,
'employee_id': self.jules_emp.id,
})
category_tag = self.env['hr.employee.category'].create({'name': 'Test category'})
self.jules_emp.category_ids = [category_tag.id]
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date.today() + relativedelta(years=-1, month=8, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=8, day=31),
'name': 'Payment Test',
})
employees = self.env["hr.employee"].search([('category_ids', 'in', [category_tag.id])])
payslip_run.generate_payslips(employee_ids=employees.ids)
self.assertEqual(len(employees.ids), 1)
self.assertEqual(employees.ids[0], self.jules_emp.id)
def test_08_payslip_batch_wizard_for_structure_type_selection_mode_with_multiple_contract(self):
structure_typeA, structure_typeB = self.env['hr.payroll.structure.type'].create([
{'name': 'Test A'},
{'name': 'Test B'},
])
structureA, structureB = self.env['hr.payroll.structure'].create([
{
'name': 'Test structure A',
'type_id': structure_typeA.id,
},
{
'name': 'Test structure B',
'type_id': structure_typeB.id,
},
])
payslip_runA, payslip_runB, payslip_runC = self.env['hr.payslip.run'].create([
{
'date_start': datetime.date.today() + relativedelta(years=-1, month=2, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=2, day=31),
'structure_id': structureA.id,
'name': 'Payslip RUN A'
},
{
'date_start': datetime.date.today() + relativedelta(years=-1, month=4, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=4, day=31),
'structure_id': structureB.id,
'name': 'Payslip RUN B'
},
{
'date_start': datetime.date.today() + relativedelta(years=-1, month=3, day=1),
'date_end': datetime.date.today() + relativedelta(years=-1, month=3, day=31),
'structure_id': structureB.id,
'name': 'Payslip RUN C'
},
])
employee_timmy, employee_gerard, employee_michel = self.env['hr.employee'].create([
{
'name': 'Timmy',
'sex': 'male',
'birthday': '1984-05-01',
'date_version': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'contract_date_end': datetime.date.today() + relativedelta(years=-1, month=3, day=15),
'wage': 5000.33,
'structure_type_id': structure_typeA.id,
},
{
'name': 'Gerard',
'sex': 'male',
'birthday': '1964-01-23',
'date_version': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'wage': 5000.33,
'structure_type_id': structure_typeA.id,
},
{
'name': 'Michel',
'sex': 'male',
'birthday': '1975-10-06',
'date_version': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=1, day=1),
'wage': 7000.33,
'structure_type_id': structure_typeB.id,
},
])
employee_timmy.create_version({
'date_version': datetime.date.today() + relativedelta(years=-1, month=3, day=16),
'contract_date_start': datetime.date.today() + relativedelta(years=-1, month=3, day=16),
'contract_date_end': datetime.date.today() + relativedelta(years=1, month=3, day=31),
'wage': 7000.33,
'employee_id': employee_timmy.id,
'structure_type_id': structure_typeB.id,
})
# Batch A for only structure A
# For february YEAR-1
# Expected employees/contracts
# Timmy contract A (YEAR-1/01/01 -> YEAR-1/03/15)
# Gerard contract A (YEAR-1/01/01 -> no end date )
payslip_runA.generate_payslips(payslip_runA._get_valid_version_ids())
self.assertEqual(len(payslip_runA.slip_ids.employee_id.ids), 2)
self.assertEqual(len(payslip_runA.slip_ids.ids), 2)
self.assertTrue(all(
payslip.version_id.structure_type_id == payslip.struct_id.type_id and
payslip.struct_id.type_id == payslip_runA.structure_id.type_id
for payslip in payslip_runA.slip_ids))
# Batch B for only structure B
# For april YEAR-1
# Expected employees/contracts
# Timmy contract B (YEAR-1/03/16 -> no end date)
# Michel contract B (YEAR-1/01/01 -> no end date)
payslip_runB.generate_payslips(payslip_runB._get_valid_version_ids())
self.assertEqual(len(payslip_runB.slip_ids.employee_id.ids), 2)
self.assertEqual(len(payslip_runB.slip_ids.ids), 2)
self.assertTrue(all(
payslip.version_id.structure_type_id == payslip.struct_id.type_id and
payslip.struct_id.type_id == payslip_runB.structure_id.type_id
for payslip in payslip_runB.slip_ids))
# Batch C for two structures
# For march YEAR-1
# Expected employees/contracts
# Timmy| contract A (YEAR-1/01/01 -> YEAR-1/03/15)
# | contract B (YEAR-1/03/16 -> no end date )
# Gerard contract A (YEAR-1/01/01 -> no end date )
# Michel contract B (YEAR-1/01/01 -> no end date )
payslip_runC.generate_payslips(payslip_runC._get_valid_version_ids())
self.assertEqual(len(payslip_runC.slip_ids.employee_id.ids), 2)
self.assertEqual(len(payslip_runC.slip_ids.ids), 2)
self.assertTrue(all(
payslip.version_id.structure_type_id == payslip.struct_id.type_id and
payslip.struct_id.type_id == payslip_runC.structure_id.type_id
for payslip in payslip_runC.slip_ids))
def test_09_payslip_creation_with_employee_without_contract(self):
employee = self.env['hr.employee'].create({
'name': 'Johnny',
})
payslip_form = Form(self.env['hr.payslip'])
payslip_form.employee_id = employee
payslip_form.save()
self.assertTrue(payslip_form)
def test_10_related_payslip_not_flagged_as_duplicate(self):
"""Refund/Correction payslips must NOT be treated as duplicates."""
original = self.env['hr.payslip'].create({
'name': 'Original Payslip',
'employee_id': self.richard_emp.id,
'struct_id': self.richard_emp.structure_type_id.default_struct_id.id,
'date_from': datetime.date(2025, 12, 1),
'date_to': datetime.date(2025, 12, 31),
})
original.compute_sheet()
original.action_payslip_done()
original.correct_sheet()
related = original.related_payslip_ids
refund = related.filtered(lambda p: p.is_refund_payslip)
correction = related - refund
warnings = refund._get_warnings_by_slip()[refund]
self.assertFalse(
any("Similar payslips found" in (w.get('message') or "") for w in warnings),
"Refund payslip incorrectly flagged as duplicate."
)
correction_warnings = correction._get_warnings_by_slip()[correction]
self.assertFalse(
any("Similar payslips found" in (w.get('message') or "") for w in correction_warnings),
"Correction payslip incorrectly flagged as duplicate."
)
duplicate = self.env['hr.payslip'].create({
'name': 'Actual Duplicate Payslip',
'employee_id': self.richard_emp.id,
'struct_id': original.struct_id.id,
'date_from': original.date_from,
'date_to': original.date_to,
})
duplicate_warnings = duplicate._get_warnings_by_slip()[duplicate]
self.assertTrue(
any("Similar payslips found" in (w.get('message') or "") for w in duplicate_warnings),
"Unrelated duplicate payslip should still trigger the warning."
)
def test_04_cancel_a_done_payslip_with_payroll_admin(self):
"""Cancel a validated payslip using a new user with Payroll Admin access."""
test_user = mail_new_test_user(
self.env, name="Test user", login="test_user",
groups="hr_payroll.group_hr_payroll_manager"
)
richard_payslip = self.env['hr.payslip'].create({
'name': 'Payslip of Richard',
'employee_id': self.richard_emp.id,
})
richard_payslip.action_payslip_done()
self.assertEqual(richard_payslip.state, 'validated')
richard_payslip.with_user(test_user).action_payslip_cancel()
self.assertEqual(richard_payslip.state, 'cancel')
def test_hr_payslip_without_date_to(self):
"""Ensure payslip creation fails if date_to is missing."""
employee = self.env['hr.employee'].create({
'name': 'Jethalal Gada',
'date_version': '2025-10-01',
'contract_date_start': '2025-10-01',
})
payslip_form = Form(self.env['hr.payslip'])
payslip_form.employee_id = employee
payslip_form.date_to = False
with self.assertRaises(AssertionError):
payslip_form.save()
self.assertTrue(payslip_form)
def test_05_fully_flexible_contracts_payslip(self):
""" Test payslip generation for fully flexible contracts (no working schedule) with attendance-based work entries """
if not self.env["ir.module.module"].search([("name", "=", "hr_work_entry_attendance"), ("state", "=", "installed")]):
self.skipTest("Module 'hr_work_entry_attendance' is not installed!")
attendance_work_entry_type = self.env.ref('hr_work_entry.work_entry_type_attendance')
# Case 1: contract with no calendar
date_from = datetime.date.today()
date_to = date_from + relativedelta(months=1)
employee_with_calendar = self.env['hr.employee'].create({
'name': 'employee 1',
'resource_calendar_id': self.env.ref('resource.resource_calendar_std').id,
'work_entry_source': 'attendance',
'wage': 5000,
'structure_type_id': self.structure_type.id,
'date_version': date_from - relativedelta(months=2),
'contract_date_start': date_from - relativedelta(months=2),
})
flexible_contract_1 = employee_with_calendar.version_id
flexible_contract_1.resource_calendar_id = False
for day in range(7):
work_date = date_from + relativedelta(days=day)
if work_date.weekday() < 5:
self.env['hr.work.entry'].create({
'name': f'attendance {day + 1}',
'employee_id': employee_with_calendar.id,
'version_id': flexible_contract_1.id,
'work_entry_type_id': attendance_work_entry_type.id,
'date': work_date,
'duration': 8,
})
payslip_1 = self.env['hr.payslip'].create({
'name': "payslip of employee 1",
'employee_id': employee_with_calendar.id,
'date_from': date_from,
'date_to': date_to,
})
payslip_1.compute_sheet()
self.assertTrue(payslip_1.worked_days_line_ids, 'worked days should be generated for fully flexible contract')
attendance_line_1 = payslip_1.worked_days_line_ids.filtered(lambda l: l.work_entry_type_id == attendance_work_entry_type)
self.assertTrue(attendance_line_1, 'attendance worked days should be present')
self.assertEqual(attendance_line_1.number_of_days, 5, 'payslip should record 5 worked days')
self.assertEqual(attendance_line_1.number_of_hours, 40, 'payslip should record 40 hours')
# Case 2: employee with no calendar, contract with no calendar (full flexibility in both)
employee_no_calendar = self.env['hr.employee'].create({
'name': 'employee 2',
'resource_calendar_id': False,
'work_entry_source': 'attendance',
'wage': 6000,
'structure_type_id': self.structure_type.id,
'date_version': date_from - relativedelta(months=2),
'contract_date_start': date_from - relativedelta(months=2),
})
flexible_contract_2 = employee_no_calendar.version_id
for day in range(7):
work_date = date_from + relativedelta(days=day + 10)
if work_date.weekday() < 5:
self.env['hr.work.entry'].create({
'name': f'Attendance {day + 1}',
'employee_id': employee_no_calendar.id,
'version_id': flexible_contract_2.id,
'work_entry_type_id': attendance_work_entry_type.id,
'date': work_date,
'duration': 8,
})
payslip_2 = self.env['hr.payslip'].create({
'name': 'payslip 2',
'employee_id': employee_no_calendar.id,
'date_from': date_from,
'date_to': date_to,
})
payslip_2.compute_sheet()
self.assertTrue(payslip_2.worked_days_line_ids, 'worked days should be generated for fully flexible contract')
attendance_line_2 = payslip_2.worked_days_line_ids.filtered(lambda l: l.work_entry_type_id == attendance_work_entry_type)
self.assertTrue(attendance_line_2, 'attendance worked days should be present')
self.assertGreater(attendance_line_2.number_of_hours, 0, 'payslip record attendance hours')
def test_06_pay_run_payslip_name(self):
"""
This test checks that the name of the payslip contains the name and the period for which the pay run is
being run.
"""
payslip_run = self.env['hr.payslip.run'].create({
'date_end': '2025-11-30',
'date_start': '2025-11-01',
'name': 'Payslip for Employee'
})
payslip_run.generate_payslips(employee_ids=[self.richard_emp.id])
self.assertEqual(payslip_run.slip_ids.name, 'Salary Slip - Richard - November 2025')
def test_10_translation_title_multiple_employee_payslip(self):
self.env['res.lang']._activate_lang('it_IT')
self.richard_emp.write({
'contract_date_start': datetime.date(2025, 1, 1),
'contract_date_end': datetime.date.today() + relativedelta(years=2),
'wage': 6000.33,
'lang': 'en_US',
})
self.jules_emp.write({
'contract_date_start': datetime.date(2025, 1, 1),
'contract_date_end': datetime.date.today() + relativedelta(years=2),
'wage': 6000.33,
'lang': 'it_IT',
})
payslip_run = self.env['hr.payslip.run'].create({
'date_start': datetime.date(2026, 8, 1),
'date_end': datetime.date(2026, 8, 31),
'name': 'Payment Test',
})
payslip_run.generate_payslips(employee_ids=[self.richard_emp.id, self.jules_emp.id])
richard_slip = payslip_run.slip_ids.filtered(lambda s: s.employee_id == self.richard_emp)
jules_slip = payslip_run.slip_ids.filtered(lambda s: s.employee_id == self.jules_emp)
richard_slip.ensure_one()
jules_slip.ensure_one()
self.assertEqual(richard_slip.name, 'Salary Slip - Richard - August 2026')
self.assertEqual(jules_slip.name, 'Cedolino retribuzione - Jules - agosto 2026')
def test_payslip_duplicate_warning_ignores_different_contract(self):
"""
Test that the duplicate payslip warning does not appear when the existing payslips for the period
are linked to a different contract than the one on the payslip being computed.
"""
structure_type = self.env['hr.payroll.structure.type'].create({
'name': 'Test Type',
})
employee = self.env['hr.employee'].create({
'name': 'Test Employee',
})
contract1 = self.env['hr.version'].create({
'employee_id': employee.id,
'structure_type_id': structure_type.id,
'date_version': datetime.date(2025, 3, 1),
'contract_date_start': datetime.date(2025, 3, 1),
'contract_date_end': datetime.date(2025, 3, 15),
'wage': 1000.0,
})
contract2 = self.env['hr.version'].create({
'employee_id': employee.id,
'structure_type_id': structure_type.id,
'date_version': datetime.date(2025, 3, 16),
'contract_date_start': datetime.date(2025, 3, 16),
'wage': 1000.0,
})
payslip_with_contract_1 = self.env['hr.payslip'].create({
'name': 'Payslip March Contract 1',
'employee_id': employee.id,
'struct_id': self.structure_type.default_struct_id.id,
'date_from': datetime.date(2025, 3, 1),
'date_to': datetime.date(2025, 3, 31),
'version_id': contract1.id,
})
payslip_with_contract_1.compute_sheet()
payslip_with_contract_1.action_payslip_done()
payslip_with_contract_2 = self.env['hr.payslip'].create({
'name': 'Payslip March Contract 2',
'employee_id': employee.id,
'struct_id': self.structure_type.default_struct_id.id,
'date_from': datetime.date(2025, 3, 1),
'date_to': datetime.date(2025, 3, 31),
'version_id': contract2.id,
})
payslip_with_contract_2.compute_sheet()
warning_messages = [
warning.get('message') or ''
for warning in payslip_with_contract_2._get_warnings_by_slip()[payslip_with_contract_2]
]
self.assertFalse(
any("Similar payslips found" in msg for msg in warning_messages),
f"Warning incorrectly appeared. Found messages: {warning_messages}"
)
payslip_with_contract_2.action_payslip_done()
# Payslips linked to the same contract should still trigger the warning
payslip2_with_contract2 = self.env['hr.payslip'].create({
'name': 'Payslip 2 March Contract 2',
'employee_id': employee.id,
'struct_id': self.structure_type.default_struct_id.id,
'date_from': datetime.date(2025, 3, 1),
'date_to': datetime.date(2025, 3, 31),
'version_id': contract2.id,
})
payslip2_with_contract2.compute_sheet()
warning_messages = [
warning.get('message') or ''
for warning in payslip2_with_contract2._get_warnings_by_slip()[payslip2_with_contract2]
]
self.assertTrue(
any("Similar payslips found" in msg for msg in warning_messages),
f"Warning did NOT appear. Found messages: {warning_messages}"
)
@tagged('-at_install', 'post_install')
class TestPayslipUi(HttpCase):
def test_tour_date_input(self):
"""Test payslip form date input."""
self.start_tour("/odoo", 'hr_payroll_form_view_date_input_tour', login='admin')