# -*- coding: utf-8 -*- # Part of Odoo. See LICENSE file for full copyright and licensing details. import base64 from freezegun import freeze_time from datetime import date from dateutil.relativedelta import relativedelta from odoo.tests import HttpCase, tagged, TransactionCase from odoo.tools import file_open from odoo.addons.mail.tests.common import mail_new_test_user @tagged('-at_install', 'post_install', 'payroll_dashboard_ui') class TestDashboardUi(HttpCase): def test_dashboard_ui(self): # This test is meant to test the basic features of the dashboard company = self.env['res.company'].create({ 'name': 'Payroll Dashboard Company', }) user = mail_new_test_user( self.env, name="Laurie Poiret", login="dashboarder", groups="base.group_user,hr_payroll.group_hr_payroll_manager", company_id=company.id) if self.env.ref('sign.group_sign_manager', raise_if_not_found=False): user.groups_id += self.env.ref('sign.group_sign_manager', raise_if_not_found=False) department = self.env['hr.department'].create({ 'name': 'Payroll', 'company_id': company.id, }) # this will make one of the action box non empty self.env['hr.employee'].create({ 'user_id': user.id, 'company_id': company.id, 'department_id': department.id, 'resource_calendar_id': company.resource_calendar_id.id, }) # The test will break if sign is installed if self.env['ir.module.module'].search([('name', '=', 'sign'), ('state', '=', 'installed')]): user.groups_id += self.env.ref('sign.group_sign_manager', raise_if_not_found=False) with file_open('sign/static/demo/employment.pdf', "rb") as f: pdf_content = base64.b64encode(f.read()) attachment = self.env['ir.attachment'].with_user(user).create({ 'type': 'binary', 'datas': pdf_content, 'name': 'Employment Contract.pdf', }) self.env['sign.template'].with_user(user).create({ 'attachment_id': attachment.id, 'sign_item_ids': [(6, 0, [])], }) self.start_tour("/", "payroll_dashboard_ui_tour", login='dashboarder', timeout=300) @tagged('-at_install', 'post_install', 'payroll_dashboard') class TestDashboard(TransactionCase): @classmethod def setUpClass(cls): super().setUpClass() cls.company = cls.env['res.company'].create({ 'name': 'Dashboard Company', }) cls.user = mail_new_test_user( cls.env, name="Laurie Poiret", login="dashboarder", groups="base.group_user,hr_payroll.group_hr_payroll_manager", company_id=cls.company.id, ) def test_multi_sections(self): # Test that the dashboard function returns a dict with all keys, regardless of emptyness Payslip = self.env['hr.payslip'] all_sections = Payslip._get_dashboard_default_sections() dashboard = Payslip.get_payroll_dashboard_data(all_sections) self.assertTrue(all(section in dashboard for section in all_sections)) sub_sections = all_sections[1:3] dashboard = Payslip.get_payroll_dashboard_data(sub_sections) self.assertEqual(len(sub_sections), len(dashboard)) self.assertTrue(all(section in dashboard for section in sub_sections)) def test_dashboard_batches(self): # Tests the dates for batches, by default it should return # the batches for the last 3 'active' months ignoring # any month that has been skipped payslip_runs = self.env['hr.payslip.run'].create([ { 'name': 'Batch 1 - Oct', 'date_start': '2021-10-01', 'date_end': '2021-10-31', 'company_id': self.company.id, }, { 'name': 'Batch 1 - Sept', 'date_start': '2021-09-01', 'date_end': '2021-09-30', 'company_id': self.company.id, }, { 'name': 'Batch 2 - Sept', 'date_start': '2021-09-01', 'date_end': '2021-09-30', 'company_id': self.company.id, }, { 'name': 'Batch 1 - June', 'date_start': '2021-06-01', 'date_end': '2021-06-30', 'company_id': self.company.id, }, { 'name': 'Batch 1 - May', 'date_start': '2021-05-01', 'date_end': '2021-05-31', 'company_id': self.company.id, }, ]) with freeze_time(date(2021, 12, 31)): batches_to_return = payslip_runs.filtered(lambda r: r.date_start.month >= 6) dashboard = self.env['hr.payslip'].with_user(self.user).get_payroll_dashboard_data(sections=['batches']) self.assertTrue('batches' in dashboard) self.assertEqual(len(dashboard['batches']), len(batches_to_return)) self.assertEqual(set(read['id'] for read in dashboard['batches']), set(batches_to_return.ids)) def test_dashboard_empty_stats(self): # Tests that when stats are empty they are tagged as sample dashboard = self.env['hr.payslip'].with_user(self.user).get_payroll_dashboard_data(sections=['stats']) self.assertTrue(all(stats['is_sample'] is True for stats in dashboard['stats'])) def _test_dashboard_stats(self): # Tests that the result inside of the stats dashboard is somewhat coherent emp_1, emp_2, emp_3 = self.env['hr.employee'].create([ {'name': 'Employee 1', 'company_id': self.company.id}, {'name': 'Employee 2', 'company_id': self.company.id}, {'name': 'Employee 3', 'company_id': self.company.id} ]) today = date.today() self.env['hr.contract'].create([ { 'name': 'Contract 1', 'employee_id': emp_1.id, 'date_start': today - relativedelta(months=1, day=1), 'state': 'open', 'wage': 1000, 'company_id': self.company.id, }, { 'name': 'Contract 2', 'employee_id': emp_2.id, 'date_start': today - relativedelta(day=1), 'state': 'open', 'wage': 2000, 'company_id': self.company.id, }, { 'name': 'Contract 3', 'employee_id': emp_3.id, 'date_start': today + relativedelta(months=1, day=1), 'state': 'open', 'wage': 4500, 'company_id': self.company.id, } ]) self.env['hr.employee'].flush_model() self.env['hr.contract'].flush_model() dashboard = self.env['hr.payslip'].with_user(self.user).get_payroll_dashboard_data(sections=['stats']) # Identify the different sections employer_cost = employees = None for section in dashboard['stats']: if section['id'] == 'employer_cost': employer_cost = section elif section['id'] == 'employees': employees = section self.assertTrue(all([employer_cost, employees])) # Check employees monthly # Employees contains the number of unique employees that worked on that period employees_stat = employees['data']['monthly'] self.assertEqual(employees_stat[0]['value'], 1) self.assertEqual(employees_stat[1]['value'], 2) self.assertEqual(employees_stat[2]['value'], 3) # Check yearly employees outside the function as the assertions are dependent on the freeze time date return employees['data']['yearly'] def test_dashboard_stat_end_of_year(self): # Tests the dashboard at the end of a year with freeze_time(date(2021, 12, 1)): employees_stat = self._test_dashboard_stats() self.assertEqual(employees_stat[0]['value'], 0) self.assertEqual(employees_stat[1]['value'], 2) self.assertEqual(employees_stat[2]['value'], 3) def test_dashboard_stat_start_of_year(self): # Tests the dashboard again but at the start of a year with freeze_time(date(2021, 1, 1)): employees_stat = self._test_dashboard_stats() self.assertEqual(employees_stat[0]['value'], 1) self.assertEqual(employees_stat[1]['value'], 3) self.assertEqual(employees_stat[2]['value'], 3) def test_dashboard_stat_middle_of_year(self): # Tests the dashboard again but at the middle of the year with freeze_time(date(2021, 6, 1)): employees_stat = self._test_dashboard_stats() self.assertEqual(employees_stat[0]['value'], 0) self.assertEqual(employees_stat[1]['value'], 3) self.assertEqual(employees_stat[2]['value'], 3) def test_dashboard_no_english_language_access(self): # Tests that we can access the dashboard when we don't have english language active Payslip = self.env['hr.payslip'] # We remove english from every model of the app that needed it to get french as main and unique language self.env['res.lang']._activate_lang('fr_FR') fr_lang = self.env['res.lang'].search([['code', '=', 'fr_FR']]) if 'website' in self.env: self.env['website'].search([]).write({'language_ids': fr_lang.ids, 'default_lang_id': fr_lang.id}) self.env['res.users'].with_context(active_test=False).search([]).write({'lang' : 'fr_FR'}) self.env['res.partner'].search([]).write({'lang' : 'fr_FR'}) self.env['res.lang'].search([['code', '=', 'en_US']]).active = False # We only test that we won't receive a traceback when we don't have access to the english language # The normal flow of the functions are tested above Payslip.get_payroll_dashboard_data() Payslip.get_dashboard_warnings()