# -*- coding: utf-8 -*- from odoo.tests.common import TransactionCase, Form from odoo.exceptions import UserError from odoo.tests import tagged from datetime import date, datetime, timedelta from dateutil.relativedelta import relativedelta class TestPayrollAdvanceFlow(TransactionCase): def setUp(cls): super(TestPayrollAdvanceFlow, cls).setUp() cls.company = cls.env.company cls.account_salary = cls.env['account.account'].create({ 'name': 'Basic Salary Account', 'code': '600001', 'account_type': 'expense', 'reconcile': True, }) cls.account_payable = cls.env['account.account'].create({ 'name': 'Salaries Payable', 'code': '200001', 'account_type': 'liability_payable', 'reconcile': True, }) cls.journal = cls.env['account.journal'].create({ 'name': 'Salary Journal', 'type': 'general', 'code': 'SAL', 'default_account_id': cls.account_payable.id, }) cls.rule_basic = cls.env['hr.salary.rule'].create({ 'name': 'Basic Salary', 'sequence': 1, 'code': 'BASIC', 'category_id': cls.env.ref('exp_hr_payroll.ALW').id, 'condition_select': 'none', 'amount_select': 'code', 'amount_python_compute': 'result = contract.wage', 'rule_debit_account_id': cls.account_salary.id, }) cls.rule_net = cls.env['hr.salary.rule'].create({ 'name': 'Net Salary', 'sequence': 100, 'code': 'NET', 'category_id': cls.env.ref('exp_hr_payroll.DED').id, 'condition_select': 'none', 'amount_select': 'code', 'amount_python_compute': 'result = categories.BASIC + categories.ALW + categories.DED', 'rule_credit_account_id': cls.account_payable.id, }) cls.structure = cls.env['hr.payroll.structure'].create({ 'name': 'Standard Structure', 'type': 'scale', 'code': 'STRUCT_001', 'rule_ids': [(4, cls.rule_basic.id), (4, cls.rule_net.id)], 'transfer_type': 'one_by_one', }) cls.employee = cls.env['hr.employee'].create({ 'name': 'Test Employee Payroll', 'first_hiring_date': date.today() - relativedelta(years=1), 'state': 'open', }) cls.contract = cls.env['hr.contract'].create({ 'name': 'Contract For Test', 'employee_id': cls.employee.id, 'state': 'program_directory', 'wage': 5000.0, 'salary_scale': cls.structure.id, 'journal_id': cls.journal.id, 'date_start': date.today() - relativedelta(years=1), }) def test_01_payslip_compute_and_transfer(self): date_from = date.today().replace(day=1) date_to = date.today() + relativedelta(months=+1, day=1, days=-1) payslip = self.env['hr.payslip'].create({ 'name': 'Test Payslip', 'employee_id': self.employee.id, 'date_from': date_from, 'date_to': date_to, 'contract_id': self.contract.id, 'struct_id': self.structure.id }) payslip.compute_sheet() self.assertEqual(payslip.state, 'computed', "State should be 'computed' after computing sheet") basic_line = payslip.line_ids.filtered(lambda l: l.code == 'BASIC') self.assertEqual(basic_line.total, 5000.0, "Basic salary should be 5000") payslip.compute_totals() self.assertEqual(payslip.total_allowances, 5000.0, "Total allowances should be calculated correctly") payslip.confirm() self.assertEqual(payslip.state, 'confirmed') payslip.transfer() self.assertEqual(payslip.state, 'transfered') self.assertTrue(payslip.move_id, "Journal Entry should be created") self.assertEqual(payslip.move_id.state, 'draft', "Move should be created in draft state initially") def test_02_payslip_loans_integration(self): payslip = self.env['hr.payslip'].create({ 'name': 'Loan Payslip', 'employee_id': self.employee.id, 'date_from': date.today().replace(day=1), 'date_to': date.today() + relativedelta(months=+1, day=1, days=-1), }) self.env['payslip.loans'].create({ 'payslip_loan': payslip.id, 'name': 'Car Loan', 'code': 'LOAN01', 'amount': 500.0, 'date': date.today(), 'account_id': self.account_payable.id, }) payslip.compute_totals() self.assertEqual(payslip.total_loans, 500.0, "Total loans field should calculate sum of loan lines") self.assertEqual(payslip.total_sum, 500.0, "Total sum logic check (depends on allowances setup)") def test_03_payslip_run_batch_process(self): date_start = date.today().replace(day=1) date_end = date.today() + relativedelta(months=+1, day=1, days=-1) payslip_run = self.env['hr.payslip.run'].create({ 'name': 'Monthly Run', 'date_start': date_start, 'date_end': date_end, 'salary_scale': self.structure.id, }) payslip_run.check_date_start() self.assertEqual(payslip_run.date_end, date_end) payslip_run.compute_sheet() self.assertTrue(payslip_run.slip_ids, "Payslips should be generated for eligible employees") generated_slip = payslip_run.slip_ids[0] self.assertEqual(generated_slip.employee_id, self.employee) self.assertEqual(generated_slip.state, 'computed') payslip_run.confirm() self.assertEqual(generated_slip.state, 'confirmed') payslip_run.transfer() self.assertEqual(payslip_run.state, 'transfered') self.assertTrue(payslip_run.move_id or generated_slip.move_id, "Accounting move should be generated") def test_04_payslip_withdraw_and_reset(self): payslip = self.env['hr.payslip'].create({ 'name': 'Withdraw Test', 'employee_id': self.employee.id, 'date_from': date.today(), 'date_to': date.today(), }) payslip.compute_sheet() payslip.confirm() payslip.withdraw() self.assertEqual(payslip.state, 'draft', "State should return to draft after withdraw") self.assertFalse(payslip.move_id, "Account move should be unlinked/deleted")