# Part of Odoo. See LICENSE file for full copyright and licensing details. import logging import pdb from dateutil.relativedelta import relativedelta from odoo.tests.common import tagged, TransactionCase from odoo.tools import file_open from odoo import Command from odoo.tests import HttpCase, tagged, TransactionCase import json from datetime import datetime, date from freezegun import freeze_time from collections import defaultdict from unittest.mock import patch _logger = logging.getLogger(__name__) @tagged('post_install_l10n', 'post_install', '-at_install', 'swissdec_payroll') class TestSwissdecCommon(TransactionCase): @classmethod def _l10n_ch_generate_swissdec_demo_payslip(cls, contract, date_from, date_to, company, after_payment=False): batch = cls.env['hr.payslip.run'].search( [('date_start', '=', date_from), ('company_id', '=', company)]) vals = { 'name': f"Monthly Pay Batch - {date_from.year}-{date_from.month}", 'employee_id': contract.employee_id.id, 'version_id': contract.id, 'company_id': company, 'struct_id': cls.env.ref('l10n_ch_hr_payroll.hr_payroll_structure_ch_elm').id, 'date_from': date_from, 'date_to': date_to, 'l10n_ch_after_departure_payment': after_payment, 'payslip_run_id': batch.id, } payslip = cls.env['hr.payslip'].with_context(tracking_disable=True).create(vals) cls.env.flush_all() payslip.compute_sheet() payslip.action_payslip_done() return payslip @classmethod def _l10n_ch_compute_swissdec_demo_paylips(cls, company, date_from): _logger.info('Created payslips for period %s-%s', date_from.year, date_from.month) batch = cls._l10n_ch_create_batch(company, date_from.month, pay_13th=date_from.month == 12) _logger.info('Computed payslips for period %s-%s', date_from.year, date_from.month) return batch @classmethod def _l10n_ch_create_batch(cls, company, month, pay_13th=False): cls.env.flush_all() first_of_month = date.today().replace(day=1) end_of_month = first_of_month + relativedelta(months=1, days=-1) batch = cls.env['hr.payslip.run'].create({ "company_id": company.id, "structure_id": cls.env.ref('l10n_ch_hr_payroll.hr_payroll_structure_ch_elm').id, "l10n_ch_pay_13th_month": pay_13th, "date_start": str(first_of_month), "date_end": str(end_of_month) }) batch.sudo().generate_payslips(batch.sudo()._get_valid_version_ids()) batch.sudo().action_validate() cls.env.flush_all() return batch def _normalize_data(self, data): """ Recursively transform data so that the order of lists no longer matters. """ if isinstance(data, dict): return {k: self._normalize_data(v) for k, v in sorted(data.items(), key=lambda d: str(d))} elif isinstance(data, list): return sorted([self._normalize_data(item) for item in data], key=lambda d: str(d)) else: return data def _get_truth_base_path(self): return "" def _compare_with_truth_base(self, declaration_type, identifier, generated_dict): truth_base = json.load(file_open(f'{self._get_truth_base_path()}/{declaration_type}.json')), truth_dict = truth_base[0].get(identifier) json_formated = json.loads(json.dumps(generated_dict)) self.assertDictEqual( self._normalize_data(json_formated), self._normalize_data(truth_dict), f"Mismatch in declaration '{identifier}'." ) @classmethod def setUpClass(cls): super().setUpClass() cls.maxDiff = None cls.env['res.company'].search([('name', '=', 'Muster AG')]).write({'name': 'Muster AG (Old)'}) cls.muster_ag_company = cls.env['res.company'].create({ 'name': 'Muster AG', 'street': 'Bahnhofstrasse 1', 'zip': '6003', 'city': 'Luzern', 'country_id': cls.env.ref('base.ch').id, 'l10n_ch_uid': 'CHE-999.999.996', 'phone': '0412186532', 'l10n_ch_30_day_method': True, 'currency_id': cls.env.ref("base.CHF").id }) cls.env.user.company_ids |= cls.muster_ag_company cls.env = cls.env(context=dict(cls.env.context, allowed_company_ids=cls.muster_ag_company.ids)) # Load IS Rates rates_to_load = [ ('LU_N_A_0', 'LU_A0N.json'), ('LU_N_N_0', 'LU_N0N.json'), ('BE_N_A_0', 'BE_A0N.json'), ('BE_N_A_1', 'BE_A1N.json'), ('BE_N_B_0', 'BE_B0N.json'), ('BE_N_C_0', 'BE_C0N.json'), ('BE_N_B_1', 'BE_B1N.json'), ('BE_N_H_1', 'BE_H1N.json'), ('BE_N_L_0', 'BE_L0N.json'), ('BE_HE_N', 'BE_HEN.json'), ('BE_ME_N', 'BE_MEN.json'), ('LU_NO_N', 'LU_NON.json'), ('BE_SF_N', 'BE_SFN.json'), ('TI_N_A_0', 'TI_A0N.json'), ('TI_N_B_0', 'TI_B0N.json'), ('TI_N_B_1', 'TI_B1N.json'), ('TI_N_C_0', 'TI_C0N.json'), ('TI_N_F_0', 'TI_F0N.json'), ('TI_N_F_1', 'TI_F1N.json'), ('TI_N_R_0', 'TI_R0N.json'), ('TI_N_T_0', 'TI_T0N.json'), ('VD_N_A_0', 'VD_A0N.json'), ('VD_N_A_1', 'VD_A1N.json'), ('VD_N_A_2', 'VD_A2N.json'), ('VD_N_B_0', 'VD_B0N.json'), ] rates_to_unlink = cls.env['hr.rule.parameter'] for xml_id, file_name in rates_to_load: rates_to_unlink += cls.env['hr.rule.parameter'].search([('code', '=', f'l10n_ch_withholding_tax_rates_{xml_id}')]) if rates_to_unlink: rates_to_unlink.unlink() for xml_id, file_name in rates_to_load: cls.env['hr.rule.parameter'].search([('code', '=', f'l10n_ch_withholding_tax_rates_{xml_id}')]).unlink() rule_parameter = cls.env['hr.rule.parameter'].create({ 'name': f'CH Withholding Tax: {xml_id}', 'code': f'l10n_ch_withholding_tax_rates_{xml_id}', 'country_id': cls.env.ref('base.ch').id, }) cls.env['hr.rule.parameter.value'].create({ 'parameter_value': json.load(file_open(f'l10n_ch_hr_payroll/tests/data/is_rates/{file_name}')), 'rule_parameter_id': rule_parameter.id, 'date_from': date(2021, 1, 1), })