diff --git a/l10n_de_holidays/README.rst b/l10n_de_holidays/README.rst new file mode 100644 index 00000000..669b9144 --- /dev/null +++ b/l10n_de_holidays/README.rst @@ -0,0 +1,73 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl + :alt: License: AGPL-3 + +=============================================== +Hr Holidays Public Generator DE +=============================================== + +This module extends the hr.holidays.public.generator model +and implements the functionality needed to generate and copy +the public holidays in Germany. + + +Usage +===== + +Go to "Leaves/Public Holidays/Generate Public Holidays" +* Choose "Year" for which the public holidays will be generated +* Choose "Germany" in the "Country" dropdown +* "State" field +** Choose "State" if you want the holidays only for specific state +** Leave "State" empty if you want all public holidays + +* "From Template" field + +Choose "From Template". This will be used as template and all +holidays with field "Date may change" = False (static holidays), +will be copied. All floating holidays ("Date may change" = True ), +will be calculated for the relevant "Year" + +* press "Generate" button + + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smash it by providing detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Yu Weng +* Nikolina Todorova + +Do not contact contributors directly about support or help with technical issues. + +Other credits +~~~~~~~~~~~~~ + +The development of this module has been financially supported by: + +- Agent ERP GmbH + +Maintainer +---------- + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +This module is maintained by the OCA. + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +To contribute to this module, please visit https://odoo-community.org. diff --git a/l10n_de_holidays/__init__.py b/l10n_de_holidays/__init__.py new file mode 100644 index 00000000..5cb1c491 --- /dev/null +++ b/l10n_de_holidays/__init__.py @@ -0,0 +1 @@ +from . import wizards diff --git a/l10n_de_holidays/__manifest__.py b/l10n_de_holidays/__manifest__.py new file mode 100644 index 00000000..f06e1b8b --- /dev/null +++ b/l10n_de_holidays/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +{ + "name": "Holidays for Germany", + "version": "16.0.1.0.0", + "license": "AGPL-3", + "category": "Human Resources", + "author": "Odoo Community Association (OCA)", + "website": "https://github.com/OCA/l10n-germany", + "depends": [ + "hr_holidays_public", + ], + "data": [ + "security/ir.model.access.csv", + "wizards/hr_holidays_public_generator_view.xml", + ], + "installable": True, + "auto_install": False, +} diff --git a/l10n_de_holidays/i18n/de.po b/l10n_de_holidays/i18n/de.po new file mode 100644 index 00000000..d097e54c --- /dev/null +++ b/l10n_de_holidays/i18n/de.po @@ -0,0 +1,236 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_de_holidays +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 11.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2018-09-07 09:48+0000\n" +"PO-Revision-Date: 2018-09-07 09:48+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "All Saints' Day" +msgstr "Allerheiligen" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Ascension Day" +msgstr "Christi Himmelfahrt" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Assumption Day" +msgstr "Mariä Himmelfahrt" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Boxing Day" +msgstr "2. Weihnachtsfeiertag" + +#. module: l10n_de_holidays +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Cancel" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Christmas Day" +msgstr "1. Weihnachtsfeiertag" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Corpus Christi" +msgstr "Fronleichnam" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__country_id +msgid "Country" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__create_uid +msgid "Created by" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__create_date +msgid "Created on" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Day of German Unity" +msgstr "Tag der Deutschen Einheit" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Day of Reformation" +msgstr "Reformationstag" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Easter Monday" +msgstr "Ostermontag" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Easter Sunday" +msgstr "Ostersonntag" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__template_id +msgid "From Template" +msgstr "" + +#. module: l10n_de_holidays +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Generate" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.actions.act_window,name:l10n_de_holidays.hr_holidays_public_generator_action +#: model:ir.ui.menu,name:l10n_de_holidays.hr_holidays_public_generator_menu +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Generate Public Holidays" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model,name:l10n_de_holidays.model_hr_holidays_public_generator +msgid "Generate public holidays - Calculations" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Good Friday" +msgstr "Karfreitag" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__id +msgid "ID" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "International Women's Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "International Workers' Day" +msgstr "Tag der Arbeit" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__write_date +msgid "Last Updated on" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "New Years's Day" +msgstr "Neujahr" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Repentance Day" +msgstr "Buß- und Bettag" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__state_id +msgid "State" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator.py:0 +#, python-format +msgid "" +"There is no copy function defined for this county or\n" +" the function name does not fit the requirement -\n" +" action_copy_{country_code}_holidays where " +"\"{country_code}\" id the\n" +" county code." +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator.py:0 +#, python-format +msgid "" +"There is no generate function defined for this county\n" +" or the function name does not fit the requirement -\n" +" action_generate_{country_code}_holidays where " +"\"{country_code}\" is\n" +" the county code." +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Three Kings Day" +msgstr "Heilige Drei Könige" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Whit Monday" +msgstr "Pfingstmontag" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "World Children's Day" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__year +msgid "Year" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "You cannot copy the holidays to the same year." +msgstr "Sie können die Feiertage nicht für dasselbe Jahr kopieren." + +#~ msgid "hr.holidays.public.generator" +#~ msgstr "hr.holidays.public.generator" + +#~ msgid "Warning" +#~ msgstr "Warnung" diff --git a/l10n_de_holidays/i18n/l10n_de_holidays.pot b/l10n_de_holidays/i18n/l10n_de_holidays.pot new file mode 100644 index 00000000..b824112f --- /dev/null +++ b/l10n_de_holidays/i18n/l10n_de_holidays.pot @@ -0,0 +1,225 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * l10n_de_holidays +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.0\n" +"Report-Msgid-Bugs-To: \n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "All Saints' Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Ascension Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Assumption Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Boxing Day" +msgstr "" + +#. module: l10n_de_holidays +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Cancel" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Christmas Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Corpus Christi" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__country_id +msgid "Country" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__create_uid +msgid "Created by" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__create_date +msgid "Created on" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Day of German Unity" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Day of Reformation" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__display_name +msgid "Display Name" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Easter Monday" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Easter Sunday" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__template_id +msgid "From Template" +msgstr "" + +#. module: l10n_de_holidays +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Generate" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.actions.act_window,name:l10n_de_holidays.hr_holidays_public_generator_action +#: model:ir.ui.menu,name:l10n_de_holidays.hr_holidays_public_generator_menu +#: model_terms:ir.ui.view,arch_db:l10n_de_holidays.hr_holidays_public_generator_view +msgid "Generate Public Holidays" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model,name:l10n_de_holidays.model_hr_holidays_public_generator +msgid "Generate public holidays - Calculations" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Good Friday" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__id +msgid "ID" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "International Women's Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "International Workers' Day" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator____last_update +msgid "Last Modified on" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__write_date +msgid "Last Updated on" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "New Years's Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Repentance Day" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__state_id +msgid "State" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator.py:0 +#, python-format +msgid "" +"There is no copy function defined for this county or\n" +" the function name does not fit the requirement -\n" +" action_copy_{country_code}_holidays where \"{country_code}\" id the\n" +" county code." +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator.py:0 +#, python-format +msgid "" +"There is no generate function defined for this county\n" +" or the function name does not fit the requirement -\n" +" action_generate_{country_code}_holidays where \"{country_code}\" is\n" +" the county code." +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Three Kings Day" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "Whit Monday" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "World Children's Day" +msgstr "" + +#. module: l10n_de_holidays +#: model:ir.model.fields,field_description:l10n_de_holidays.field_hr_holidays_public_generator__year +msgid "Year" +msgstr "" + +#. module: l10n_de_holidays +#: code:addons/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py:0 +#, python-format +msgid "You cannot copy the holidays to the same year." +msgstr "" diff --git a/l10n_de_holidays/security/ir.model.access.csv b/l10n_de_holidays/security/ir.model.access.csv new file mode 100644 index 00000000..8296126b --- /dev/null +++ b/l10n_de_holidays/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_hr_holidays_public_generator_user,access_hr_holidays_public_generator,model_hr_holidays_public_generator,base.group_user,1,1,1,1 diff --git a/l10n_de_holidays/static/description/icon.png b/l10n_de_holidays/static/description/icon.png new file mode 100644 index 00000000..3a0328b5 Binary files /dev/null and b/l10n_de_holidays/static/description/icon.png differ diff --git a/l10n_de_holidays/tests/__init__.py b/l10n_de_holidays/tests/__init__.py new file mode 100644 index 00000000..3576da4b --- /dev/null +++ b/l10n_de_holidays/tests/__init__.py @@ -0,0 +1,5 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from . import test_hr_holidays_public_generator diff --git a/l10n_de_holidays/tests/common.py b/l10n_de_holidays/tests/common.py new file mode 100644 index 00000000..aa691b6b --- /dev/null +++ b/l10n_de_holidays/tests/common.py @@ -0,0 +1,24 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo.addons.base.tests.common import BaseCommon + + +class TestHolidaysGenerator(BaseCommon): + @classmethod + def setUpClass(cls): + super(TestHolidaysGenerator, cls).setUpClass() + + # Usefull models + cls.HrHolidaysPublicGenerator = cls.env["hr.holidays.public.generator"] + cls.HrHolidaysPublicLine = cls.env["hr.holidays.public.line"] + cls.HrHolidaysPublic = cls.env["hr.holidays.public"] + cls.TestYear = 2018 + cls.CountryId = cls.env.ref("base.de").id + + # Test Create Public Holidays for 2018 + wizard_data = {"year": cls.TestYear, "country_id": cls.CountryId} + cls.hr_holidays_public_generator = cls.HrHolidaysPublicGenerator.create( + wizard_data + ) diff --git a/l10n_de_holidays/tests/test_hr_holidays_public_generator.py b/l10n_de_holidays/tests/test_hr_holidays_public_generator.py new file mode 100644 index 00000000..d3581815 --- /dev/null +++ b/l10n_de_holidays/tests/test_hr_holidays_public_generator.py @@ -0,0 +1,161 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from datetime import date + +from odoo.exceptions import UserError + +from . import common + + +class TestHrHolidaysPublicGenerator(common.TestHolidaysGenerator): + def test_action_generate_de_holidays(self): + self.hr_holidays_public_generator.action_run() + + hr_holiday_public = self.HrHolidaysPublic.search( + [("year", "=", self.TestYear), ("country_id", "=", self.CountryId)] + ) + if not hr_holiday_public: + hr_holiday_public = None + + self.assertIsNotNone(hr_holiday_public) + + if hr_holiday_public: + line_ids = hr_holiday_public.line_ids + if not line_ids: + line_ids = None + self.assertIsNotNone(line_ids) + + def test_calculate_fixed_holidays(self): + self.hr_holidays_public_generator.action_run() + + hr_holiday_public = self.HrHolidaysPublic.search( + [ + ("year", "=", self.TestYear), + ("country_id", "=", self.CountryId), + ] + ) + self.assertTrue(hr_holiday_public, "No public holiday created.") + + expected_dates = { + date(self.TestYear, 1, 1), + date(self.TestYear, 5, 1), + date(self.TestYear, 10, 3), + date(self.TestYear, 12, 25), + date(self.TestYear, 12, 26), + } + + fixed_dates = set(hr_holiday_public.line_ids.mapped("date")) + + self.assertTrue( + expected_dates.issubset(fixed_dates), + f"Expected holidays: {expected_dates}, found: {fixed_dates}", + ) + + for line in hr_holiday_public.line_ids: + if line.date in expected_dates: + self.assertFalse( + line.variable_date, f"{line.date} should not be variable date." + ) + + def test_action_copy_de_holidays(self): + self.hr_holidays_public_generator.action_generate_de_holidays() + template_id = self.HrHolidaysPublic.search( + [("year", "=", self.TestYear), ("country_id", "=", self.CountryId)] + )[0].id + + # Test Create Public Holidays for 2019 from 2019 + TestYear = 2019 + wizard_data = { + "year": TestYear, + "country_id": self.CountryId, + "template_id": template_id, + } + + hr_holidays_public_generator_copy = self.HrHolidaysPublicGenerator.create( + wizard_data + ) + + hr_holidays_public_generator_copy.action_run() + + hr_holiday_public = self.HrHolidaysPublic.search( + [("year", "=", TestYear), ("country_id", "=", self.CountryId)] + ) + if not hr_holiday_public: + hr_holiday_public = None + + self.assertIsNotNone(hr_holiday_public) + + if hr_holiday_public: + line_ids = hr_holiday_public.line_ids + if not line_ids: + line_ids = None + self.assertIsNotNone(line_ids) + + def test_copy_function_name_does_not_exists(self): + self.hr_holidays_public_generator.action_generate_de_holidays() + template_id = self.HrHolidaysPublic.search( + [("year", "=", self.TestYear), ("country_id", "=", self.CountryId)] + )[0].id + + # Test Create Public Holidays for 2019 from 2019 + # with not existing function for the CountryId + CountryId = self.ref("base.fr") + TestYear = 2019 + wizard_data = { + "year": TestYear, + "country_id": CountryId, + "template_id": template_id, + } + hr_holidays_public_generator_copy = self.HrHolidaysPublicGenerator.create( + wizard_data + ) + + with self.assertRaises(UserError): + hr_holidays_public_generator_copy.action_run() + + def test_generate_function_name_does_not_exists(self): + # Test Generate Public Holidays for 2018 + # with not existing function for the CountryId + CountryId = self.ref("base.fr") + wizard_data = {"year": self.TestYear, "country_id": CountryId} + hr_holidays_public_generator_generate = self.HrHolidaysPublicGenerator.create( + wizard_data + ) + + with self.assertRaises(UserError): + hr_holidays_public_generator_generate.action_run() + + def test_copy_to_same_year_error(self): + self.hr_holidays_public_generator.action_generate_de_holidays() + template_id = self.HrHolidaysPublic.search( + [("year", "=", self.TestYear), ("country_id", "=", self.CountryId)] + )[0].id + wizard_data = { + "year": self.TestYear, + "country_id": self.CountryId, + "template_id": template_id, + } + hr_holidays_public_generator_copy = self.HrHolidaysPublicGenerator.create( + wizard_data + ) + + with self.assertRaises(UserError): + hr_holidays_public_generator_copy.action_run() + + def test_calculate_repentance_day(self): + # Repentance Day 2023 should be on 22.11.2023. + result_2023 = self.hr_holidays_public_generator.calculate_repentance_day(2023) + self.assertTrue(result_2023, "No date calculated for 2023") + self.assertEqual(result_2023, "2023-11-22") + + # Repentance Day 2023 should be on 20.11.2024. + result_2024 = self.hr_holidays_public_generator.calculate_repentance_day(2024) + self.assertTrue(result_2024, "No date calculated for 2024") + self.assertEqual(result_2024, "2024-11-20") + + # Repentance Day 2023 should be on 19.11.2025. + result_2025 = self.hr_holidays_public_generator.calculate_repentance_day(2025) + self.assertTrue(result_2025, "No date calculated for 2025") + self.assertEqual(result_2025, "2025-11-19") diff --git a/l10n_de_holidays/wizards/__init__.py b/l10n_de_holidays/wizards/__init__.py new file mode 100644 index 00000000..7e4ec76c --- /dev/null +++ b/l10n_de_holidays/wizards/__init__.py @@ -0,0 +1,2 @@ +from . import hr_holidays_public_generator_de +from . import hr_holidays_public_generator diff --git a/l10n_de_holidays/wizards/hr_holidays_public_generator.py b/l10n_de_holidays/wizards/hr_holidays_public_generator.py new file mode 100644 index 00000000..2f67fa6f --- /dev/null +++ b/l10n_de_holidays/wizards/hr_holidays_public_generator.py @@ -0,0 +1,80 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + +COUNTRY_GENERATORS = [] + + +class HrHolidaysPublicGenerator(models.TransientModel): + """ + Usage: + * generate public holidays for specific country (if there + is no template set) + * copy public holidays for specific country + To extend the model one should: + * create new module with name "hr_holidays_public_generator_" + * create wizard that inherit "hr.holidays.public.generator" + * implement copy public holidays function with name action_copy_%s_holidays + where %s id the county code + * implement generate public holidays function with + name action_generate_%s_holidays where %s id the county code + """ + + _name = "hr.holidays.public.generator" + _description = "Generate public holidays" + + year = fields.Integer(required=True, default=(lambda self: datetime.today().year)) + country_id = fields.Many2one( + "res.country", + string="Country", + required=True, + domain=[("code", "in", COUNTRY_GENERATORS)], + ) + state_id = fields.Many2one("res.country.state", string="State") + template_id = fields.Many2one("hr.holidays.public", string="From Template") + + @api.onchange("template_id") + def onchange_template_id(self): + if self.template_id: + self.country_id = ( + self.template_id.country_id and self.template_id.country_id.id or False + ) + + def generate_function_copy_name(self): + function_name = "action_copy_%s_holidays" % self.country_id.code.lower() + return function_name + + def generate_function_generate_name(self): + function_name = "action_generate_%s_holidays" % self.country_id.code.lower() + return function_name + + def action_run(self): + self.ensure_one() + if self.template_id: + function_name = self.generate_function_copy_name() + if not hasattr(self, function_name): + raise UserError( + _( + """There is no copy function defined for this county or + the function name does not fit the requirement - + action_copy_{country_code}_holidays where "{country_code}" id the + county code.""" + ).format(country_code=self.country_id.code.lower()) + ) + getattr(self, function_name)() + else: + function_name = self.generate_function_generate_name() + if not hasattr(self, function_name): + raise UserError( + _( + """There is no generate function defined for this county + or the function name does not fit the requirement - + action_generate_{country_code}_holidays where "{country_code}" is + the county code.""" + ).format(country_code=self.country_id.code.lower()) + ) + getattr(self, function_name)() diff --git a/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py b/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py new file mode 100644 index 00000000..36571c20 --- /dev/null +++ b/l10n_de_holidays/wizards/hr_holidays_public_generator_de.py @@ -0,0 +1,380 @@ +# Copyright 2018 elego Software Solutions GmbH - Yu Weng +# Copyright 2018 initOS GmbH - Nikolina Todorova +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime, timedelta + +from dateutil import easter +from dateutil.relativedelta import SU, relativedelta + +from odoo import _, api, fields, models +from odoo.exceptions import UserError + +from . import hr_holidays_public_generator + +hr_holidays_public_generator.COUNTRY_GENERATORS.append("DE") + + +class HrHolidaysPublicGenerator(models.TransientModel): + _inherit = "hr.holidays.public.generator" + _description = "Generate public holidays - Calculations" + + @api.model + def calculate_easter_sunday(self, year): + return easter.easter(year) + + @api.model + def calculate_new_good_friday(self, easter): + good_friday = easter - timedelta(days=2) + return fields.Date.to_string(good_friday) + + @api.model + def calculate_easter_monday(self, easter): + easter_monday = easter + timedelta(days=1) + return fields.Date.to_string(easter_monday) + + @api.model + def calculate_ascension_day(self, easter): + ascension_day = easter + timedelta(days=39) + return fields.Date.to_string(ascension_day) + + @api.model + def calculate_whit_monday(self, easter): + whit_monday = easter + timedelta(days=50) + return fields.Date.to_string(whit_monday) + + @api.model + def calculate_corpus_christi(self, easter): + corpus_christi = easter + timedelta(days=60) + return fields.Date.to_string(corpus_christi) + + @api.model + def calculate_repentance_day(self, year): + # Repentance Day is always 11 days befor the first advent + first_advent = datetime(year, 12, 24) + relativedelta(weekday=SU(-4)) + repentance_day = first_advent + relativedelta(days=-11) + return fields.Date.to_string(repentance_day) + + @api.model + def calculate_floating_holidays(self, existing_holidays): + public_holiday_line_obj = self.env["hr.holidays.public.line"] + easter = self.calculate_easter_sunday(self.year) + + public_holiday_line_obj.create( + { + "name": _("Good Friday"), + "date": self.calculate_new_good_friday(easter), + "variable_date": True, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("Easter Sunday"), + "date": fields.Date.to_string(easter), + "variable_date": True, + "year_id": existing_holidays.id, + } + ) + + public_holiday_line_obj.create( + { + "name": _("Easter Monday"), + "date": self.calculate_easter_monday(easter), + "variable_date": True, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("Ascension Day"), + "date": self.calculate_ascension_day(easter), + "variable_date": True, + "year_id": existing_holidays.id, + } + ) + + public_holiday_line_obj.create( + { + "name": _("Whit Monday"), + "date": self.calculate_whit_monday(easter), + "variable_date": True, + "year_id": existing_holidays.id, + } + ) + + @api.model + def get_state_ids(self, codes=None): + """ + :codes: should be a list of states' codes + Returns states ids in :codes: + """ + if codes is None: + codes = [] + country = self.env.ref("base.de") + state_ids = self.env["res.country.state"].search( + [("code", "in", codes), ("country_id", "=", country.id)] + ) + return state_ids.ids + + @api.model + def calculate_state_floating_holidays(self, existing_holidays, state=None): + public_holiday_line_obj = self.env["hr.holidays.public.line"] + easter = self.calculate_easter_sunday(self.year) + # Baden-Württemberg, Bavaria, Hesse, North + # Rhine-Westphalia, Rhineland-Palatinate, Saarland + codes = ["DE-BW", "DE-BY", "DE-HE", "DE-NW", "DE-RP", "DE-SL"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("Corpus Christi"), + "date": self.calculate_corpus_christi(easter), + "variable_date": True, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + # Sachsen + codes = ["DE-SN"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("Repentance Day"), + "date": self.calculate_repentance_day(self.year), + "variable_date": True, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + + @api.model + def calculate_fixed_holidays(self, existing_holidays): + public_holiday_line_obj = self.env["hr.holidays.public.line"] + + public_holiday_line_obj.create( + { + "name": _("New Years's Day"), + "date": "%s-01-01" % existing_holidays.year, + "variable_date": False, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("International Workers' Day"), + "date": "%s-05-01" % existing_holidays.year, + "variable_date": False, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("Day of German Unity"), + "date": "%s-10-03" % existing_holidays.year, + "variable_date": False, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("Christmas Day"), + "date": "%s-12-25" % existing_holidays.year, + "variable_date": False, + "year_id": existing_holidays.id, + } + ) + public_holiday_line_obj.create( + { + "name": _("Boxing Day"), + "date": "%s-12-26" % existing_holidays.year, + "variable_date": False, + "year_id": existing_holidays.id, + } + ) + + @api.model + def calculate_state_fixed_holidays(self, existing_holidays, state=None): + public_holiday_line_obj = self.env["hr.holidays.public.line"] + # Baden-Württemberg, Bavaria, Saxony-Anhalt + codes = ["DE-BW", "DE-BY", "DE-ST"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("Three Kings Day"), + "date": "%s-01-06" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + # Thüringen + codes = ["DE-TH"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("World Children's Day"), + "date": "%s-09-20" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + + # Bavaria, Saarland + codes = ["DE-BY", "DE-SL"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("Assumption Day"), + "date": "%s-08-15" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + # BB, HB, HH, MVP, NDS, SH, SN, ST, TH + # (depends on + # https://www.timeanddate.com/holidays/germany/reformation-day) + codes = [ + "DE-BB", + "DE-HB", + "DE-HH", + "DE-MV", + "DE-NI", + "DE-SH", + "DE-SN", + "DE-ST", + "DE-TH", + ] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("Day of Reformation"), + "date": "%s-10-31" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + # Baden-Württemberg, Bavaria, + # North Rhine-Westphalia, Rhineland-Palatinate, Saarland + codes = ["DE-BW", "DE-BY", "DE-NW", "DE-RP", "DE-SL"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("All Saints' Day"), + "date": "%s-11-01" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + # Berlin, Mecklenburg-Vorpommern + codes = ["DE-BE", "DE-MV"] + state_ids = self.get_state_ids(codes) + + if not state or state.id in state_ids: + public_holiday_line_obj.create( + { + "name": _("International Women's Day"), + "date": "%s-03-08" % existing_holidays.year, + "variable_date": False, + "state_ids": [(6, 0, state_ids)], + "year_id": existing_holidays.id, + } + ) + + def action_delete_holidays(self, existing_holidays): + self.ensure_one() + if existing_holidays: + for holiday_line in existing_holidays.line_ids: + holiday_line.unlink() + return existing_holidays + + def action_generate_de_holidays(self): + public_holiday_obj = self.env["hr.holidays.public"] + + for wizard in self: + existing_holidays = public_holiday_obj.search( + [("year", "=", self.year), ("country_id", "=", wizard.country_id.id)] + ) + if not existing_holidays: + existing_holidays = public_holiday_obj.create( + {"year": wizard.year, "country_id": wizard.country_id.id} + ) + wizard.action_delete_holidays(existing_holidays) + wizard.calculate_floating_holidays(existing_holidays) + wizard.calculate_fixed_holidays(existing_holidays) + wizard.calculate_state_floating_holidays( + existing_holidays, state=wizard.state_id + ) + wizard.calculate_state_fixed_holidays( + existing_holidays, state=wizard.state_id + ) + + return { + "type": "ir.actions.act_window_close", + } + + def action_copy_de_holidays(self): + public_holiday_obj = self.env["hr.holidays.public"] + public_holiday_line_obj = self.env["hr.holidays.public.line"] + + for wizard in self: + if wizard.year == wizard.template_id.year: + raise UserError(_("You cannot copy the holidays to the same year.")) + + # unlink all currently existing holiday lines in target year + # before deleting target year + existing_holidays_year_to = public_holiday_obj.search( + [("year", "=", wizard.year), ("country_id", "=", wizard.country_id.id)] + ) + wizard.action_delete_holidays(existing_holidays_year_to) + + # create new year for holidays + if not existing_holidays_year_to: + new_holiday_year = public_holiday_obj.create( + {"year": wizard.year, "country_id": wizard.country_id.id} + ) + else: + new_holiday_year = existing_holidays_year_to[0] + # copy fixed holidays from source year replacing the year + for holiday in wizard.template_id.line_ids: + if holiday.variable_date: + continue + holiday_date = datetime.strptime(str(holiday.date), "%Y-%m-%d") + new_holiday_date = "%s-%s-%s" % ( + wizard.year, + holiday_date.month, + holiday_date.day, + ) + public_holiday_line_obj.create( + { + "name": holiday.name, + "date": new_holiday_date, + "variable_date": False, + "state_ids": [(6, 0, [s.id for s in holiday.state_ids])], + "year_id": new_holiday_year.id, + } + ) + wizard.calculate_floating_holidays(new_holiday_year) + wizard.calculate_state_floating_holidays( + new_holiday_year, state=wizard.state_id + ) + + return { + "type": "ir.actions.act_window_close", + } diff --git a/l10n_de_holidays/wizards/hr_holidays_public_generator_view.xml b/l10n_de_holidays/wizards/hr_holidays_public_generator_view.xml new file mode 100644 index 00000000..cab40749 --- /dev/null +++ b/l10n_de_holidays/wizards/hr_holidays_public_generator_view.xml @@ -0,0 +1,46 @@ + + + + hr.holidays.public.generator.view + hr.holidays.public.generator + form + +
+ + + + + + +
+
+
+
+
+ + + Generate Public Holidays + ir.actions.act_window + hr.holidays.public.generator + form + new + + + +
diff --git a/setup/l10n_de_holidays/odoo/addons/l10n_de_holidays b/setup/l10n_de_holidays/odoo/addons/l10n_de_holidays new file mode 120000 index 00000000..cce71da6 --- /dev/null +++ b/setup/l10n_de_holidays/odoo/addons/l10n_de_holidays @@ -0,0 +1 @@ +../../../../l10n_de_holidays \ No newline at end of file diff --git a/setup/l10n_de_holidays/setup.py b/setup/l10n_de_holidays/setup.py new file mode 100644 index 00000000..28c57bb6 --- /dev/null +++ b/setup/l10n_de_holidays/setup.py @@ -0,0 +1,6 @@ +import setuptools + +setuptools.setup( + setup_requires=['setuptools-odoo'], + odoo_addon=True, +)