diff --git a/hr_holidays_leave_repeated/README.rst b/hr_holidays_leave_repeated/README.rst index 3d9ca68d..813d7a63 100644 --- a/hr_holidays_leave_repeated/README.rst +++ b/hr_holidays_leave_repeated/README.rst @@ -17,13 +17,13 @@ HR Holidays leave repeated :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html :alt: License: AGPL-3 .. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fhr--holidays-lightgray.png?logo=github - :target: https://github.com/OCA/hr-holidays/tree/14.0/hr_holidays_leave_repeated + :target: https://github.com/OCA/hr-holidays/tree/16.0/hr_holidays_leave_repeated :alt: OCA/hr-holidays .. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png - :target: https://translation.odoo-community.org/projects/hr-holidays-14-0/hr-holidays-14-0-hr_holidays_leave_repeated + :target: https://translation.odoo-community.org/projects/hr-holidays-16-0/hr-holidays-16-0-hr_holidays_leave_repeated :alt: Translate me on Weblate .. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png - :target: https://runboat.odoo-community.org/builds?repo=OCA/hr-holidays&target_branch=14.0 + :target: https://runboat.odoo-community.org/builds?repo=OCA/hr-holidays&target_branch=16.0 :alt: Try me on Runboat |badge1| |badge2| |badge3| |badge4| |badge5| @@ -48,13 +48,15 @@ Usage #. Alternatively set the 'Repeat Mode' field to 'End Date', then set 'Repeat Every' and 'Repeat End Date'. #. Create (save) the leave request. All the periodical leave requests are automatically created. +Note for HR Time Off responsibles : creating repeated leaves can only be used when selecting Mode = "By Employee" and that all selected employees should share the same resource calendar, otherwise an error is raised. + 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 to smash it by providing a detailed and welcomed -`feedback `_. +`feedback `_. Do not contact contributors directly about support or help with technical issues. @@ -86,6 +88,6 @@ 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. -This module is part of the `OCA/hr-holidays `_ project on GitHub. +This module is part of the `OCA/hr-holidays `_ project on GitHub. You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/hr_holidays_leave_repeated/__manifest__.py b/hr_holidays_leave_repeated/__manifest__.py index 55b06f2c..37c7e1eb 100644 --- a/hr_holidays_leave_repeated/__manifest__.py +++ b/hr_holidays_leave_repeated/__manifest__.py @@ -7,7 +7,7 @@ "author": "Onestein, Odoo Community Association (OCA)", "website": "https://github.com/OCA/hr-holidays", "category": "Human Resources", - "version": "14.0.1.1.0", + "version": "16.0.1.1.0", "license": "AGPL-3", "depends": ["hr_holidays"], "data": ["views/hr_leave_type.xml", "views/hr_leave.xml"], diff --git a/hr_holidays_leave_repeated/i18n/fr.po b/hr_holidays_leave_repeated/i18n/fr.po index 5c927b42..36b67f44 100644 --- a/hr_holidays_leave_repeated/i18n/fr.po +++ b/hr_holidays_leave_repeated/i18n/fr.po @@ -74,7 +74,7 @@ msgstr "Veuillez définir un nombre de répétitions positif." #: model:ir.model.fields,field_description:hr_holidays_leave_repeated.field_hr_leave__holiday_type_repeat #: model:ir.model.fields,field_description:hr_holidays_leave_repeated.field_hr_leave_type__repeat msgid "Repeat" -msgstr "Répeter" +msgstr "Répéter" #. module: hr_holidays_leave_repeated #: model:ir.model.fields,field_description:hr_holidays_leave_repeated.field_hr_leave__repeat_limit @@ -84,7 +84,7 @@ msgstr "Répéter # fois" #. module: hr_holidays_leave_repeated #: model:ir.model.fields,field_description:hr_holidays_leave_repeated.field_hr_leave__repeat_end_date msgid "Repeat End Date" -msgstr "Répéter la date de fin" +msgstr "Date de fin de répétition" #. module: hr_holidays_leave_repeated #: model:ir.model.fields,field_description:hr_holidays_leave_repeated.field_hr_leave__repeat_every @@ -145,15 +145,9 @@ msgstr "" #. module: hr_holidays_leave_repeated #: model:ir.model,name:hr_holidays_leave_repeated.model_hr_leave msgid "Time Off" -msgstr "" +msgstr "Congés" #. module: hr_holidays_leave_repeated #: model:ir.model,name:hr_holidays_leave_repeated.model_hr_leave_type msgid "Time Off Type" -msgstr "" - -#~ msgid "Leave" -#~ msgstr "Congé" - -#~ msgid "Leave Type" -#~ msgstr "Type de congé" +msgstr "Type de congés" diff --git a/hr_holidays_leave_repeated/i18n/nl.po b/hr_holidays_leave_repeated/i18n/nl.po index 313e204a..b95bf42c 100644 --- a/hr_holidays_leave_repeated/i18n/nl.po +++ b/hr_holidays_leave_repeated/i18n/nl.po @@ -146,15 +146,9 @@ msgstr "" #. module: hr_holidays_leave_repeated #: model:ir.model,name:hr_holidays_leave_repeated.model_hr_leave msgid "Time Off" -msgstr "" +msgstr "Verlof" #. module: hr_holidays_leave_repeated #: model:ir.model,name:hr_holidays_leave_repeated.model_hr_leave_type msgid "Time Off Type" -msgstr "" - -#~ msgid "Leave" -#~ msgstr "Verlof" - -#~ msgid "Leave Type" -#~ msgstr "Verlofsoort" +msgstr "Verlofsoort" diff --git a/hr_holidays_leave_repeated/models/hr_leave.py b/hr_holidays_leave_repeated/models/hr_leave.py index 4109c562..fe78f0ff 100644 --- a/hr_holidays_leave_repeated/models/hr_leave.py +++ b/hr_holidays_leave_repeated/models/hr_leave.py @@ -26,29 +26,30 @@ class HrLeave(models.Model): repeat_end_date = fields.Date(default=lambda self: fields.Date.today()) @api.model - def _update_repeated_workday_dates(self, employee, from_dt, to_dt, days): + def _update_repeated_workday_dates(self, resource_calendar, from_dt, to_dt, days): user = self.env.user - calendar = employee.resource_calendar_id orig_from_dt = fields.Datetime.context_timestamp(user, from_dt) orig_to_dt = fields.Datetime.context_timestamp(user, to_dt) - work_hours = calendar.get_work_hours_count(from_dt, to_dt, compute_leaves=False) + work_hours = resource_calendar.get_work_hours_count( + from_dt, to_dt, compute_leaves=False + ) while work_hours: from_dt = from_dt + relativedelta(days=days) to_dt = to_dt + relativedelta(days=days) - new_work_hours = calendar.get_work_hours_count( + user_from_dt = fields.Datetime.context_timestamp(user, from_dt) + user_to_dt = fields.Datetime.context_timestamp(user, to_dt) + from_dt = from_dt - user_from_dt.tzinfo._utcoffset + from_dt = from_dt + orig_from_dt.tzinfo._utcoffset + to_dt = to_dt - user_to_dt.tzinfo._utcoffset + to_dt = to_dt + orig_to_dt.tzinfo._utcoffset + + new_work_hours = resource_calendar.get_work_hours_count( from_dt, to_dt, compute_leaves=True ) if new_work_hours and work_hours <= new_work_hours: break - user_from_dt = fields.Datetime.context_timestamp(user, from_dt) - user_to_dt = fields.Datetime.context_timestamp(user, to_dt) - from_dt = from_dt - user_from_dt.tzinfo._utcoffset - from_dt = from_dt + orig_from_dt.tzinfo._utcoffset - to_dt = to_dt - user_to_dt.tzinfo._utcoffset - to_dt = to_dt + orig_to_dt.tzinfo._utcoffset - return from_dt, to_dt @api.model @@ -85,7 +86,7 @@ def _get_repeated_vals_dict(self): } @api.model - def _update_repeated_leave_vals(self, vals, employee): + def _update_repeated_leave_vals(self, vals, resource_calendar): vals_dict = self._get_repeated_vals_dict() param_dict = vals_dict[vals.get("repeat_every")] from_dt = fields.Datetime.from_string(vals.get("date_from")) @@ -96,7 +97,7 @@ def _update_repeated_leave_vals(self, vals, employee): raise UserError(param_dict["user_error_msg"]) from_dt, to_dt = self._update_repeated_workday_dates( - employee, from_dt, to_dt, param_dict["days"] + resource_calendar, from_dt, to_dt, param_dict["days"] ) vals["request_date_from"] = vals["date_from"] = from_dt @@ -105,7 +106,7 @@ def _update_repeated_leave_vals(self, vals, employee): return vals @api.model - def create_repeated_handler(self, vals, employee): + def create_repeated_handler(self, vals, resource_calendar): def _check_repeating(count, vals): repeat_mode = vals.get("repeat_mode", "times") if repeat_mode == "times" and count < vals.get("repeat_limit", 0): @@ -116,21 +117,47 @@ def _check_repeating(count, vals): return False count = 1 - vals = self._update_repeated_leave_vals(vals, employee) + vals = self._update_repeated_leave_vals(vals, resource_calendar) while _check_repeating(count, vals): self.with_context(skip_create_handler=True).create(vals) count += 1 - vals = self._update_repeated_leave_vals(vals, employee) + vals = self._update_repeated_leave_vals(vals, resource_calendar) - @api.model - def create(self, vals): - res = super().create(vals) + @api.model_create_multi + def create(self, vals_list): + res = super().create(vals_list) skip_create_handler = self.env.context.get("skip_create_handler") - all_vals_set = vals.get("repeat_every") and vals.get("repeat_mode") - if not skip_create_handler and all_vals_set: - employee = self.env["hr.employee"].browse(vals.get("employee_id")) - if employee.resource_calendar_id: - self.create_repeated_handler(vals, employee) + for vals in vals_list: + all_vals_set = ( + vals.get("repeat_every") + and vals.get("repeat_mode") + and vals.get("holiday_type") == "employee" + ) + if not skip_create_handler and all_vals_set: + employees = self.env["hr.employee"] + if vals.get("employee_id"): + employees = self.env["hr.employee"].browse(vals.get("employee_id")) + elif vals.get("employee_ids"): + employees = self.env["hr.employee"].browse( + vals.get("employee_ids")[0][2] + ) + resource_calendars = employees.mapped("resource_calendar_id") + if len(resource_calendars) == 1: + self.create_repeated_handler(vals, resource_calendars[0]) + elif len(resource_calendars) == 0: + raise ValidationError( + _( + "Please define resource calendar on employee(s) in order " + "to compute repeated leaves." + ) + ) + else: + raise ValidationError( + _( + "Creating leaves for multiple employees with different " + "resource calendar is not supported." + ) + ) return res @api.constrains("repeat_limit", "repeat_end_date") diff --git a/hr_holidays_leave_repeated/readme/USAGE.rst b/hr_holidays_leave_repeated/readme/USAGE.rst index 43511230..d047c649 100644 --- a/hr_holidays_leave_repeated/readme/USAGE.rst +++ b/hr_holidays_leave_repeated/readme/USAGE.rst @@ -2,3 +2,5 @@ #. Set the 'Repeat Mode' field to 'Number of Times'. Set the proper values for 'Repeat Every' and 'Repeat # times'. #. Alternatively set the 'Repeat Mode' field to 'End Date', then set 'Repeat Every' and 'Repeat End Date'. #. Create (save) the leave request. All the periodical leave requests are automatically created. + +Note for HR Time Off responsibles : creating repeated leaves can only be used when selecting Mode = "By Employee" and that all selected employees should share the same resource calendar, otherwise an error is raised. diff --git a/hr_holidays_leave_repeated/static/description/index.html b/hr_holidays_leave_repeated/static/description/index.html index 6d238c25..f4bdcdbc 100644 --- a/hr_holidays_leave_repeated/static/description/index.html +++ b/hr_holidays_leave_repeated/static/description/index.html @@ -8,10 +8,11 @@ /* :Author: David Goodger (goodger@python.org) -:Id: $Id: html4css1.css 8954 2022-01-20 10:10:25Z milde $ +:Id: $Id: html4css1.css 9511 2024-01-13 09:50:07Z milde $ :Copyright: This stylesheet has been placed in the public domain. Default cascading style sheet for the HTML output of Docutils. +Despite the name, some widely supported CSS2 features are used. See https://docutils.sourceforge.io/docs/howto/html-stylesheets.html for how to customize this style sheet. @@ -274,7 +275,7 @@ margin-left: 2em ; margin-right: 2em } -pre.code .ln { color: grey; } /* line numbers */ +pre.code .ln { color: gray; } /* line numbers */ pre.code, code { background-color: #eeeeee } pre.code .comment, code .comment { color: #5C6576 } pre.code .keyword, code .keyword { color: #3B0D06; font-weight: bold } @@ -300,7 +301,7 @@ span.pre { white-space: pre } -span.problematic { +span.problematic, pre.problematic { color: red } span.section-subtitle { @@ -368,7 +369,7 @@

HR Holidays leave repeated

!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !! source digest: sha256:162d88dbd2fd8cd12cd7f1c32c31e6e97c12d2bdd59120e9a955f2f49248efd1 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! --> -

Beta License: AGPL-3 OCA/hr-holidays Translate me on Weblate Try me on Runboat

+

Beta License: AGPL-3 OCA/hr-holidays Translate me on Weblate Try me on Runboat

This module allows to create periodical leaves.

Table of contents

@@ -398,13 +399,14 @@

Usage

  • Alternatively set the ‘Repeat Mode’ field to ‘End Date’, then set ‘Repeat Every’ and ‘Repeat End Date’.
  • Create (save) the leave request. All the periodical leave requests are automatically created.
  • +

    Note for HR Time Off responsibles : creating repeated leaves can only be used when selecting Mode = “By Employee” and that all selected employees should share the same resource calendar, otherwise an error is raised.

    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 to smash it by providing a detailed and welcomed -feedback.

    +feedback.

    Do not contact contributors directly about support or help with technical issues.

    @@ -426,11 +428,13 @@

    Contributors

    Maintainers

    This module is maintained by the OCA.

    -Odoo Community Association + +Odoo Community Association +

    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.

    -

    This module is part of the OCA/hr-holidays project on GitHub.

    +

    This module is part of the OCA/hr-holidays project on GitHub.

    You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

    diff --git a/hr_holidays_leave_repeated/tests/test_holidays_leave_repeated.py b/hr_holidays_leave_repeated/tests/test_holidays_leave_repeated.py index ded45e7e..91c28213 100644 --- a/hr_holidays_leave_repeated/tests/test_holidays_leave_repeated.py +++ b/hr_holidays_leave_repeated/tests/test_holidays_leave_repeated.py @@ -15,6 +15,7 @@ def setUp(self): self.date_end = datetime(2016, 12, 5, 18, 0, 0, 0) self.calendar = self.env["resource.calendar"].create({"name": "Calendar 1"}) + self.calendar2 = self.env["resource.calendar"].create({"name": "Calendar 2"}) for i in range(0, 7): self.env["resource.calendar.attendance"].create( @@ -42,9 +43,31 @@ def setUp(self): self.employee_5 = self.env["hr.employee"].create( {"name": "Failing Employee", "resource_calendar_id": self.calendar.id} ) + self.employee_6 = self.env["hr.employee"].create( + {"name": "Employee 6", "resource_calendar_id": self.calendar.id} + ) + self.employee_7 = self.env["hr.employee"].create( + {"name": "Employee 7", "resource_calendar_id": self.calendar.id} + ) + self.employee_8 = self.env["hr.employee"].create( + {"name": "Employee 8", "resource_calendar_id": self.calendar.id} + ) + self.employee_9 = self.env["hr.employee"].create( + { + "name": "Employee 9 - different calendar", + "resource_calendar_id": self.calendar2.id, + } + ) self.status_1 = self.env["hr.leave.type"].create( - {"name": "Repeating Status", "repeat": True, "validity_start": False} + {"name": "Repeating Status", "repeat": True} + ) + self.status_2 = self.env["hr.leave.type"].create( + { + "name": "Repeating Status - No Allocation", + "repeat": True, + "requires_allocation": "no", + } ) self.leave_1 = self.env["hr.leave"].create( @@ -95,6 +118,19 @@ def setUp(self): "employee_id": self.employee_4.id, } ) + self.leave_5 = self.env["hr.leave"].create( + { + "holiday_status_id": self.status_2.id, + "holiday_type": "employee", + "repeat_every": "workday", + "repeat_mode": "times", + "repeat_limit": 5, + "date_from": self.date_start, + "date_to": self.date_end, + "multi_employee": True, + "employee_ids": [(6, False, [self.employee_6.id, self.employee_7.id])], + } + ) def test_01_count_repetitions(self): @@ -122,11 +158,18 @@ def test_01_count_repetitions(self): ("employee_id", "=", self.employee_4.id), ] ) + leave_5_list = self.env["hr.leave"].search( + [ + ("holiday_status_id", "=", self.status_2.id), + ("employee_ids", "in", [self.employee_6.id, self.employee_7.id]), + ] + ) self.assertEqual(len(leave_1_list), 5) self.assertEqual(len(leave_2_list), 4) self.assertEqual(len(leave_3_list), 3) self.assertEqual(len(leave_4_list), 2) + self.assertEqual(len(leave_5_list), 5) def test_02_workdays(self): for i in range(0, 5): @@ -264,3 +307,21 @@ def test_09_check_repeat_end_date(self): "employee_id": self.employee_5.id, } ) + + def test_10_check_resource_calendar(self): + with self.assertRaises(ValidationError): + self.env["hr.leave"].create( + { + "holiday_status_id": self.status_2.id, + "holiday_type": "employee", + "repeat_every": "workday", + "repeat_mode": "times", + "repeat_limit": 5, + "date_from": self.date_start, + "date_to": self.date_end, + "multi_employee": True, + "employee_ids": [ + (6, False, [self.employee_8.id, self.employee_9.id]) + ], + } + ) diff --git a/hr_holidays_leave_repeated/views/hr_leave.xml b/hr_holidays_leave_repeated/views/hr_leave.xml index c6e3478f..ed286c15 100644 --- a/hr_holidays_leave_repeated/views/hr_leave.xml +++ b/hr_holidays_leave_repeated/views/hr_leave.xml @@ -4,77 +4,26 @@ hr.leave - - -
    + + + -
    -
    -
    -
    + + -
    -
    + attrs="{'invisible':['|', '|', '|', ('holiday_type', '!=', 'employee'), ('repeat_mode','=','date'), ('holiday_type_repeat','!=',True), ('repeat_every', '=', False)], 'readonly': [('id', '!=', False)]}" + /> +
    - - hr.leave - - - - -
    -
    -
    -
    -
    -
    -
    -
    -
    diff --git a/hr_holidays_leave_repeated/views/hr_leave_type.xml b/hr_holidays_leave_repeated/views/hr_leave_type.xml index 7bc12d0f..e1b0bc53 100644 --- a/hr_holidays_leave_repeated/views/hr_leave_type.xml +++ b/hr_holidays_leave_repeated/views/hr_leave_type.xml @@ -4,7 +4,7 @@ hr.leave.type - +