From 503f0d5459f38540af36aa82b30d2fa0011135e8 Mon Sep 17 00:00:00 2001
From: jb
Date: Mon, 24 Oct 2022 15:47:54 +0200
Subject: [PATCH 01/25] [ADD] hr_contract_employee_calendar_planning
---
.../README.rst | 100 ++++
.../__init__.py | 3 +
.../__manifest__.py | 14 +
.../hooks.py | 63 +++
.../models/__init__.py | 1 +
.../models/contract.py | 23 +
.../readme/CONTRIBUTORS.rst | 1 +
.../readme/DESCRIPTION.rst | 28 ++
.../readme/USAGE.rst | 0
.../static/description/index.html | 443 ++++++++++++++++++
.../tests/__init__.py | 1 +
..._hr_contract_employee_calendar_planning.py | 79 ++++
.../views/contract.xml | 14 +
13 files changed, 770 insertions(+)
create mode 100644 hr_contract_employee_calendar_planning/README.rst
create mode 100644 hr_contract_employee_calendar_planning/__init__.py
create mode 100644 hr_contract_employee_calendar_planning/__manifest__.py
create mode 100644 hr_contract_employee_calendar_planning/hooks.py
create mode 100644 hr_contract_employee_calendar_planning/models/__init__.py
create mode 100644 hr_contract_employee_calendar_planning/models/contract.py
create mode 100644 hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.rst
create mode 100644 hr_contract_employee_calendar_planning/readme/DESCRIPTION.rst
create mode 100644 hr_contract_employee_calendar_planning/readme/USAGE.rst
create mode 100644 hr_contract_employee_calendar_planning/static/description/index.html
create mode 100644 hr_contract_employee_calendar_planning/tests/__init__.py
create mode 100644 hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
create mode 100644 hr_contract_employee_calendar_planning/views/contract.xml
diff --git a/hr_contract_employee_calendar_planning/README.rst b/hr_contract_employee_calendar_planning/README.rst
new file mode 100644
index 00000000000..25be4039702
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/README.rst
@@ -0,0 +1,100 @@
+======================================
+Hr Contract Employee Calendar Planning
+======================================
+
+.. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ !! This file is generated by oca-gen-addon-readme !!
+ !! changes will be overwritten. !!
+ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png
+ :target: https://odoo-community.org/page/development-status
+ :alt: Beta
+.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png
+ :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-lightgray.png?logo=github
+ :target: https://github.com/OCA/hr/tree/14.0/hr_contract_employee_calendar_planning
+ :alt: OCA/hr
+.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
+ :target: https://translation.odoo-community.org/projects/hr-14-0/hr-14-0-hr_contract_employee_calendar_planning
+ :alt: Translate me on Weblate
+.. |badge5| image:: https://img.shields.io/badge/runbot-Try%20me-875A7B.png
+ :target: https://runbot.odoo-community.org/runbot/116/14.0
+ :alt: Try me on Runbot
+
+|badge1| |badge2| |badge3| |badge4| |badge5|
+
+This module ensures a consistent working times history when using the
+**hr_contract** and **hr_employee_calendar_planning** modules together.
+
+There are 3 different data models which can relate to working time
+(resource.calendar) records:
+
+* Employees (hr.employee)
+* Contracts (hr.contract)
+* Resources (resource.resource) -> related to hr.employee through resource.mixin
+
+The **hr_employee_calendar_planning** module adds the calendar_ids field
+to employees, which allows a more flexible working times configuration:
+Instead of selecting a single resource.calendar, multiple calendars can be
+combined into one auto-generated calendar.
+
+However, contracts are not considered when creating auto-generated calendars.
+This can lead to unexpected behaviour, because the active contract calendar
+and the employee calendar can diverge (calendar mismatch). Additionally, when
+configuring a new contract, or changing the existing contract calendar,
+the employee calendar will be overwritten by the contract calendar.
+
+To resolve this issue, this module migrates current and previous contract
+calendars into the calendar_ids and thus the auto-generated calendar.
+Additionally, changes to the employee calendar by contracts are prevented.
+The resource_calendar_id field is hidden in contract views, since all
+working times should be managed in the calendar_ids field for each employee.
+However, it is still possible to keep a meaningful contract history,
+e.g. for salary information.
+
+**Table of contents**
+
+.. contents::
+ :local:
+
+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 smashing it by providing a detailed and welcomed
+`feedback `_.
+
+Do not contact contributors directly about support or help with technical issues.
+
+Credits
+=======
+
+Authors
+~~~~~~~
+
+* cibex
+
+Contributors
+~~~~~~~~~~~~
+
+* Jonas Buchholz
+
+Maintainers
+~~~~~~~~~~~
+
+This module is maintained by the OCA.
+
+.. image:: https://odoo-community.org/logo.png
+ :alt: Odoo Community Association
+ :target: https://odoo-community.org
+
+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 `_ project on GitHub.
+
+You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/hr_contract_employee_calendar_planning/__init__.py b/hr_contract_employee_calendar_planning/__init__.py
new file mode 100644
index 00000000000..6f4a716678c
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/__init__.py
@@ -0,0 +1,3 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+from . import models
+from .hooks import post_init_hook
diff --git a/hr_contract_employee_calendar_planning/__manifest__.py b/hr_contract_employee_calendar_planning/__manifest__.py
new file mode 100644
index 00000000000..7429d278f4b
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/__manifest__.py
@@ -0,0 +1,14 @@
+# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
+{
+ "name": "Hr Contract Employee Calendar Planning",
+ "version": "14.0.1.0.0",
+ "category": "Human Resources",
+ "website": "https://github.com/OCA/hr",
+ "author": "cibex,Odoo Community Association (OCA)",
+ "license": "AGPL-3",
+ "application": False,
+ "auto_install": True,
+ "depends": ["hr_contract", "hr_employee_calendar_planning"],
+ "data": ["views/contract.xml"],
+ "post_init_hook": "post_init_hook",
+}
diff --git a/hr_contract_employee_calendar_planning/hooks.py b/hr_contract_employee_calendar_planning/hooks.py
new file mode 100644
index 00000000000..19835c6e98e
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/hooks.py
@@ -0,0 +1,63 @@
+import logging
+
+from odoo import SUPERUSER_ID, api
+
+_logger = logging.getLogger(__name__)
+
+
+def post_init_hook(cr, registry, employees=None):
+ """Migrate calendars from contracts to calendar_ids
+ to have consistent work schedule history"""
+ with api.Environment.manage():
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ if not employees:
+ employees = env["hr.employee"].with_context(active_test=False).search([])
+
+ for employee in employees.filtered("contract_ids"):
+ contract_calendar_lines = []
+ for contract in employee.contract_ids:
+ date_start = contract.date_start
+ date_end = contract.date_end
+ # filter calendar_ids to check for overlaps with contracts
+ # with the same work schedule
+ cal_ids = employee.calendar_ids.filtered(
+ lambda x: x.calendar_id == contract.resource_calendar_id
+ and (not x.date_start or not date_end or x.date_start < date_end)
+ and (not x.date_end or not date_start or x.date_end > date_start)
+ )
+ if cal_ids:
+ _logger.info(f"{contract} is overlapping with {cal_ids}")
+ for calendar in cal_ids:
+ if date_start and calendar.date_start != date_start:
+ _logger.info(
+ f"changing date_start of {calendar} "
+ f"from {calendar.date_start} to {date_start}"
+ )
+ calendar.date_start = date_start
+ if date_end and calendar.date_end != date_end:
+ _logger.info(
+ f"changing date_end of {calendar} "
+ f"from {calendar.date_end} to {date_end}"
+ )
+ calendar.date_end = date_end
+ else:
+ _logger.info(
+ f"adding new calendar_id for {contract.employee_id.name}: "
+ f"{contract.resource_calendar_id.name} from {date_start} to {date_end}"
+ )
+ contract_calendar_lines.append(
+ (
+ 0,
+ 0,
+ {
+ "date_start": date_start,
+ "date_end": date_end,
+ "calendar_id": contract.resource_calendar_id.id,
+ },
+ )
+ )
+
+ employee.calendar_ids = contract_calendar_lines
+
+ # set correct calendar in contract
+ employee.contract_id.resource_calendar_id = employee.resource_calendar_id
diff --git a/hr_contract_employee_calendar_planning/models/__init__.py b/hr_contract_employee_calendar_planning/models/__init__.py
new file mode 100644
index 00000000000..99a5468ac8a
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/models/__init__.py
@@ -0,0 +1 @@
+from . import contract
diff --git a/hr_contract_employee_calendar_planning/models/contract.py b/hr_contract_employee_calendar_planning/models/contract.py
new file mode 100644
index 00000000000..eceafa6ca57
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/models/contract.py
@@ -0,0 +1,23 @@
+from odoo import models
+
+
+class HrContract(models.Model):
+ _inherit = "hr.contract"
+
+ def write(self, vals):
+ if (
+ vals.get("resource_calendar_id")
+ and self.employee_id
+ and vals.get("resource_calendar_id")
+ != self.employee_id.resource_calendar_id.id
+ ):
+ # in the write method of contracts, when writing the resource_calendar_id
+ # the employee resource_calendar_id is set to the same id
+ # this interferes with the logic of hr_employee_calendar_planning
+ # which assumes that calendar times are managed by resource.calendar.attendances
+ # in auto-generated calendars based on the employee's calendar_ids
+ # since the default calendar for new contracts is the employee calendar
+ # and we set the correct calendar for the existing contract in the post_init_hook
+ # we resolve this conflict by not allowing calendar changes in contracts
+ vals.pop("resource_calendar_id")
+ return super().write(vals)
diff --git a/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.rst b/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.rst
new file mode 100644
index 00000000000..a310dc5d781
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.rst
@@ -0,0 +1 @@
+* Jonas Buchholz
diff --git a/hr_contract_employee_calendar_planning/readme/DESCRIPTION.rst b/hr_contract_employee_calendar_planning/readme/DESCRIPTION.rst
new file mode 100644
index 00000000000..33f40ed3aeb
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/readme/DESCRIPTION.rst
@@ -0,0 +1,28 @@
+This module ensures a consistent working times history when using the
+**hr_contract** and **hr_employee_calendar_planning** modules together.
+
+There are 3 different data models which can relate to working time
+(resource.calendar) records:
+
+* Employees (hr.employee)
+* Contracts (hr.contract)
+* Resources (resource.resource) -> related to hr.employee through resource.mixin
+
+The **hr_employee_calendar_planning** module adds the calendar_ids field
+to employees, which allows a more flexible working times configuration:
+Instead of selecting a single resource.calendar, multiple calendars can be
+combined into one auto-generated calendar.
+
+However, contracts are not considered when creating auto-generated calendars.
+This can lead to unexpected behaviour, because the active contract calendar
+and the employee calendar can diverge (calendar mismatch). Additionally, when
+configuring a new contract, or changing the existing contract calendar,
+the employee calendar will be overwritten by the contract calendar.
+
+To resolve this issue, this module migrates current and previous contract
+calendars into the calendar_ids and thus the auto-generated calendar.
+Additionally, changes to the employee calendar by contracts are prevented.
+The resource_calendar_id field is hidden in contract views, since all
+working times should be managed in the calendar_ids field for each employee.
+However, it is still possible to keep a meaningful contract history,
+e.g. for salary information.
diff --git a/hr_contract_employee_calendar_planning/readme/USAGE.rst b/hr_contract_employee_calendar_planning/readme/USAGE.rst
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/hr_contract_employee_calendar_planning/static/description/index.html b/hr_contract_employee_calendar_planning/static/description/index.html
new file mode 100644
index 00000000000..ebdbb804b08
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/static/description/index.html
@@ -0,0 +1,443 @@
+
+
+
+
+
+
+Hr Contract Employee Calendar Planning
+
+
+
+
+
Hr Contract Employee Calendar Planning
+
+
+

+
This module ensures a consistent working times history when using the
+hr_contract and hr_employee_calendar_planning modules together.
+
There are 3 different data models which can relate to working time
+(resource.calendar) records:
+
+- Employees (hr.employee)
+- Contracts (hr.contract)
+- Resources (resource.resource) -> related to hr.employee through resource.mixin
+
+
The hr_employee_calendar_planning module adds the calendar_ids field
+to employees, which allows a more flexible working times configuration:
+Instead of selecting a single resource.calendar, multiple calendars can be
+combined into one auto-generated calendar.
+
However, contracts are not considered when creating auto-generated calendars.
+This can lead to unexpected behaviour, because the active contract calendar
+and the employee calendar can diverge (calendar mismatch). Additionally, when
+configuring a new contract, or changing the existing contract calendar,
+the employee calendar will be overwritten by the contract calendar.
+
To resolve this issue, this module migrates current and previous contract
+calendars into the calendar_ids and thus the auto-generated calendar.
+Additionally, changes to the employee calendar by contracts are prevented.
+The resource_calendar_id field is hidden in contract views, since all
+working times should be managed in the calendar_ids field for each employee.
+However, it is still possible to keep a meaningful contract history,
+e.g. for salary information.
+
Table of contents
+
+
+
+
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 smashing it by providing a detailed and welcomed
+feedback.
+
Do not contact contributors directly about support or help with technical issues.
+
+
+
+
+
+
+
+
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.
+
This module is part of the OCA/hr project on GitHub.
+
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
+
+
+
+
+
diff --git a/hr_contract_employee_calendar_planning/tests/__init__.py b/hr_contract_employee_calendar_planning/tests/__init__.py
new file mode 100644
index 00000000000..4c053338648
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/tests/__init__.py
@@ -0,0 +1 @@
+from . import test_hr_contract_employee_calendar_planning
diff --git a/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py b/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
new file mode 100644
index 00000000000..cac8c27972f
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
@@ -0,0 +1,79 @@
+from datetime import datetime
+
+from odoo.addons.hr_contract.tests.common import TestContractCommon
+
+from ..hooks import post_init_hook
+
+
+class TestHrContractEmployeeCalendarPlanning(TestContractCommon):
+ def setUp(self):
+ super().setUp()
+ calendar_ids = [
+ (
+ 0,
+ 0,
+ {
+ "date_start": False,
+ "date_end": datetime.strptime("2020-11-30", "%Y-%m-%d").date(),
+ "calendar_id": self.env["resource.calendar"].browse([2]).id,
+ },
+ ),
+ (
+ 0,
+ 0,
+ {
+ "date_start": datetime.strptime("2020-12-01", "%Y-%m-%d").date(),
+ "date_end": False,
+ "calendar_id": self.env["resource.calendar"].browse([1]).id,
+ },
+ ),
+ ]
+ self.employee.calendar_ids = calendar_ids
+
+ def test_calendar_migration_from_contracts(self):
+ self.contract1 = self.env["hr.contract"].create(
+ {
+ "name": "contract1",
+ "employee_id": self.employee.id,
+ "wage": 1,
+ "state": "close",
+ "kanban_state": "normal",
+ "resource_calendar_id": self.env["resource.calendar"].browse([1]).id,
+ "date_start": datetime.strptime("2018-11-30", "%Y-%m-%d").date(),
+ "date_end": datetime.strptime("2019-11-30", "%Y-%m-%d").date(),
+ }
+ )
+ self.contract2 = self.env["hr.contract"].create(
+ {
+ "name": "contract2",
+ "employee_id": self.employee.id,
+ "wage": 1,
+ "state": "open",
+ "kanban_state": "normal",
+ "resource_calendar_id": self.env["resource.calendar"].browse([2]).id,
+ "date_start": datetime.strptime("2019-12-01", "%Y-%m-%d").date(),
+ "date_end": datetime.strptime("2020-11-30", "%Y-%m-%d").date(),
+ }
+ )
+ original_cal_ids = self.employee.calendar_ids.ids
+ start_dt = datetime(2019, 1, 1, 0, 0, 0)
+ end_dt = datetime(2019, 1, 2, 0, 0, 0)
+ self.assertEqual(
+ 7.0,
+ self.employee.resource_calendar_id.get_work_hours_count(
+ start_dt=start_dt,
+ end_dt=end_dt,
+ ),
+ )
+ # calendar migration from contracts
+ post_init_hook(self.env.cr, self.env.registry, self.employee)
+ self.assertEqual(
+ 8.0,
+ self.employee.resource_calendar_id.get_work_hours_count(
+ start_dt=start_dt,
+ end_dt=end_dt,
+ ),
+ )
+ self.assertTrue(
+ all(ids in self.employee.calendar_ids.ids for ids in original_cal_ids)
+ )
diff --git a/hr_contract_employee_calendar_planning/views/contract.xml b/hr_contract_employee_calendar_planning/views/contract.xml
new file mode 100644
index 00000000000..b34104e9690
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/views/contract.xml
@@ -0,0 +1,14 @@
+
+
+ hr.contract.employee.calendar.planning.form
+ hr.contract
+
+ 999
+
+
+
+
+
+
+
+
From 7a6b179cd585bcdaf478535279cae1e8ea8e2b1f Mon Sep 17 00:00:00 2001
From: jb
Date: Mon, 23 Jan 2023 12:20:26 +0100
Subject: [PATCH 02/25] [MIG] hr_contract_employee_calendar_planning: Migration
to 15.0
---
.../__manifest__.py | 2 +-
.../hooks.py | 95 +++++++++----------
2 files changed, 48 insertions(+), 49 deletions(-)
diff --git a/hr_contract_employee_calendar_planning/__manifest__.py b/hr_contract_employee_calendar_planning/__manifest__.py
index 7429d278f4b..b2aad417a8b 100644
--- a/hr_contract_employee_calendar_planning/__manifest__.py
+++ b/hr_contract_employee_calendar_planning/__manifest__.py
@@ -1,7 +1,7 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Hr Contract Employee Calendar Planning",
- "version": "14.0.1.0.0",
+ "version": "15.0.1.0.0",
"category": "Human Resources",
"website": "https://github.com/OCA/hr",
"author": "cibex,Odoo Community Association (OCA)",
diff --git a/hr_contract_employee_calendar_planning/hooks.py b/hr_contract_employee_calendar_planning/hooks.py
index 19835c6e98e..051f6408bf4 100644
--- a/hr_contract_employee_calendar_planning/hooks.py
+++ b/hr_contract_employee_calendar_planning/hooks.py
@@ -8,56 +8,55 @@
def post_init_hook(cr, registry, employees=None):
"""Migrate calendars from contracts to calendar_ids
to have consistent work schedule history"""
- with api.Environment.manage():
- env = api.Environment(cr, SUPERUSER_ID, {})
- if not employees:
- employees = env["hr.employee"].with_context(active_test=False).search([])
+ env = api.Environment(cr, SUPERUSER_ID, {})
+ if not employees:
+ employees = env["hr.employee"].with_context(active_test=False).search([])
- for employee in employees.filtered("contract_ids"):
- contract_calendar_lines = []
- for contract in employee.contract_ids:
- date_start = contract.date_start
- date_end = contract.date_end
- # filter calendar_ids to check for overlaps with contracts
- # with the same work schedule
- cal_ids = employee.calendar_ids.filtered(
- lambda x: x.calendar_id == contract.resource_calendar_id
- and (not x.date_start or not date_end or x.date_start < date_end)
- and (not x.date_end or not date_start or x.date_end > date_start)
- )
- if cal_ids:
- _logger.info(f"{contract} is overlapping with {cal_ids}")
- for calendar in cal_ids:
- if date_start and calendar.date_start != date_start:
- _logger.info(
- f"changing date_start of {calendar} "
- f"from {calendar.date_start} to {date_start}"
- )
- calendar.date_start = date_start
- if date_end and calendar.date_end != date_end:
- _logger.info(
- f"changing date_end of {calendar} "
- f"from {calendar.date_end} to {date_end}"
- )
- calendar.date_end = date_end
- else:
- _logger.info(
- f"adding new calendar_id for {contract.employee_id.name}: "
- f"{contract.resource_calendar_id.name} from {date_start} to {date_end}"
- )
- contract_calendar_lines.append(
- (
- 0,
- 0,
- {
- "date_start": date_start,
- "date_end": date_end,
- "calendar_id": contract.resource_calendar_id.id,
- },
+ for employee in employees.filtered("contract_ids"):
+ contract_calendar_lines = []
+ for contract in employee.contract_ids:
+ date_start = contract.date_start
+ date_end = contract.date_end
+ # filter calendar_ids to check for overlaps with contracts
+ # with the same work schedule
+ cal_ids = employee.calendar_ids.filtered(
+ lambda x: x.calendar_id == contract.resource_calendar_id
+ and (not x.date_start or not date_end or x.date_start < date_end)
+ and (not x.date_end or not date_start or x.date_end > date_start)
+ )
+ if cal_ids:
+ _logger.info(f"{contract} is overlapping with {cal_ids}")
+ for calendar in cal_ids:
+ if date_start and calendar.date_start != date_start:
+ _logger.info(
+ f"changing date_start of {calendar} "
+ f"from {calendar.date_start} to {date_start}"
+ )
+ calendar.date_start = date_start
+ if date_end and calendar.date_end != date_end:
+ _logger.info(
+ f"changing date_end of {calendar} "
+ f"from {calendar.date_end} to {date_end}"
)
+ calendar.date_end = date_end
+ else:
+ _logger.info(
+ f"adding new calendar_id for {contract.employee_id.name}: "
+ f"{contract.resource_calendar_id.name} from {date_start} to {date_end}"
+ )
+ contract_calendar_lines.append(
+ (
+ 0,
+ 0,
+ {
+ "date_start": date_start,
+ "date_end": date_end,
+ "calendar_id": contract.resource_calendar_id.id,
+ },
)
+ )
- employee.calendar_ids = contract_calendar_lines
+ employee.calendar_ids = contract_calendar_lines
- # set correct calendar in contract
- employee.contract_id.resource_calendar_id = employee.resource_calendar_id
+ # set correct calendar in contract
+ employee.contract_id.resource_calendar_id = employee.resource_calendar_id
From 40fa309eb697f00348099aff55b9f7a5e03503f0 Mon Sep 17 00:00:00 2001
From: oca-ci
Date: Sat, 17 Dec 2022 20:13:26 +0000
Subject: [PATCH 03/25] [UPD] Update hr_contract_employee_calendar_planning.pot
---
...hr_contract_employee_calendar_planning.pot | 34 +++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 hr_contract_employee_calendar_planning/i18n/hr_contract_employee_calendar_planning.pot
diff --git a/hr_contract_employee_calendar_planning/i18n/hr_contract_employee_calendar_planning.pot b/hr_contract_employee_calendar_planning/i18n/hr_contract_employee_calendar_planning.pot
new file mode 100644
index 00000000000..8627ce35d4c
--- /dev/null
+++ b/hr_contract_employee_calendar_planning/i18n/hr_contract_employee_calendar_planning.pot
@@ -0,0 +1,34 @@
+# Translation of Odoo Server.
+# This file contains the translation of the following modules:
+# * hr_contract_employee_calendar_planning
+#
+msgid ""
+msgstr ""
+"Project-Id-Version: Odoo Server 14.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: hr_contract_employee_calendar_planning
+#: model:ir.model.fields,field_description:hr_contract_employee_calendar_planning.field_hr_contract__display_name
+msgid "Display Name"
+msgstr ""
+
+#. module: hr_contract_employee_calendar_planning
+#: model:ir.model,name:hr_contract_employee_calendar_planning.model_hr_contract
+msgid "Employee Contract"
+msgstr ""
+
+#. module: hr_contract_employee_calendar_planning
+#: model:ir.model.fields,field_description:hr_contract_employee_calendar_planning.field_hr_contract__id
+msgid "ID"
+msgstr ""
+
+#. module: hr_contract_employee_calendar_planning
+#: model:ir.model.fields,field_description:hr_contract_employee_calendar_planning.field_hr_contract____last_update
+msgid "Last Modified on"
+msgstr ""
From 46ac0a97305fe0bcf087ab6da2a1f9f568c57204 Mon Sep 17 00:00:00 2001
From: OCA-git-bot
Date: Sat, 17 Dec 2022 20:18:18 +0000
Subject: [PATCH 04/25] [UPD] README.rst
---
.../static/description/index.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/hr_contract_employee_calendar_planning/static/description/index.html b/hr_contract_employee_calendar_planning/static/description/index.html
index ebdbb804b08..83eca9df847 100644
--- a/hr_contract_employee_calendar_planning/static/description/index.html
+++ b/hr_contract_employee_calendar_planning/static/description/index.html
@@ -3,7 +3,7 @@
-
+
Hr Contract Employee Calendar Planning
-
-
Hr Contract Employee Calendar Planning
+
+
+
+
+
+
+
Hr Contract Employee Calendar Planning
-

+

This module ensures a consistent working times history when using the
hr_contract and hr_employee_calendar_planning modules together.
There are 3 different data models which can relate to working time
@@ -411,10 +416,10 @@
Hr Contract Employee Calendar Planning
-
+
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
@@ -422,21 +427,21 @@
Do not contact contributors directly about support or help with technical issues.
+
From 2f764d28f4c6a4164f961dde121d5b388c3ec20f Mon Sep 17 00:00:00 2001
From: FernandoRomera
Date: Sun, 25 Jan 2026 13:47:59 +0100
Subject: [PATCH 24/25] [18.0][FIX] hr_contract_employee_calendar_planning:
avoid errors with hr_leave
---
hr_contract_employee_calendar_planning/hooks.py | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/hr_contract_employee_calendar_planning/hooks.py b/hr_contract_employee_calendar_planning/hooks.py
index 0d641f4b4ac..3372246a1f1 100644
--- a/hr_contract_employee_calendar_planning/hooks.py
+++ b/hr_contract_employee_calendar_planning/hooks.py
@@ -67,4 +67,7 @@ def post_init_hook(env, employees=None):
employee.calendar_ids = contract_calendar_lines
# set correct calendar in contract
- employee.contract_id.resource_calendar_id = employee.resource_calendar_id
+ # Prevent the resource calendar of leaves to be updated by a write
+ employee.contract_id.with_context(
+ no_leave_resource_calendar_update=True
+ ).update({"resource_calendar_id": employee.resource_calendar_id.id})
From 788712dac0f65c9fe529d711616a3aba6b706104 Mon Sep 17 00:00:00 2001
From: Vicent-S73
Date: Tue, 24 Feb 2026 09:30:57 +0100
Subject: [PATCH 25/25] [MIG] hr_contract_employee_calendar_planning: Migration
to 19.0
---
.../README.rst | 22 +++---
.../__manifest__.py | 4 +-
.../hooks.py | 58 +++++++-------
.../models/__init__.py | 2 +-
.../models/{contract.py => hr_version.py} | 29 ++++---
.../readme/CONTRIBUTORS.md | 2 +
.../static/description/index.html | 8 +-
..._hr_contract_employee_calendar_planning.py | 78 ++++++++++++-------
.../views/contract.xml | 21 +++--
9 files changed, 129 insertions(+), 95 deletions(-)
rename hr_contract_employee_calendar_planning/models/{contract.py => hr_version.py} (55%)
diff --git a/hr_contract_employee_calendar_planning/README.rst b/hr_contract_employee_calendar_planning/README.rst
index 4e34898e6dd..aed44ae9b23 100644
--- a/hr_contract_employee_calendar_planning/README.rst
+++ b/hr_contract_employee_calendar_planning/README.rst
@@ -21,13 +21,13 @@ Hr Contract Employee Calendar Planning
: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-lightgray.png?logo=github
- :target: https://github.com/OCA/hr/tree/18.0/hr_contract_employee_calendar_planning
+ :target: https://github.com/OCA/hr/tree/19.0/hr_contract_employee_calendar_planning
:alt: OCA/hr
.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png
- :target: https://translation.odoo-community.org/projects/hr-18-0/hr-18-0-hr_contract_employee_calendar_planning
+ :target: https://translation.odoo-community.org/projects/hr-19-0/hr-19-0-hr_contract_employee_calendar_planning
: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&target_branch=18.0
+ :target: https://runboat.odoo-community.org/builds?repo=OCA/hr&target_branch=19.0
:alt: Try me on Runboat
|badge1| |badge2| |badge3| |badge4| |badge5|
@@ -38,10 +38,10 @@ This module ensures a consistent working times history when using the
There are 3 different data models which can relate to working time
(resource.calendar) records:
-- Employees (hr.employee)
-- Contracts (hr.contract)
-- Resources (resource.resource) -> related to hr.employee through
- resource.mixin
+- Employees (hr.employee)
+- Contracts (hr.contract)
+- Resources (resource.resource) -> related to hr.employee through
+ resource.mixin
The **hr_employee_calendar_planning** module adds the calendar_ids field
to employees, which allows a more flexible working times configuration:
@@ -79,7 +79,7 @@ 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.
@@ -94,7 +94,9 @@ Authors
Contributors
------------
-- Jonas Buchholz
+- Jonas Buchholz
+- `Studio73 `__:
+- Vicent Castells
Maintainers
-----------
@@ -109,6 +111,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 `_ project on GitHub.
+This module is part of the `OCA/hr `_ project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/hr_contract_employee_calendar_planning/__manifest__.py b/hr_contract_employee_calendar_planning/__manifest__.py
index f020c8702af..5fc6ff33dc1 100644
--- a/hr_contract_employee_calendar_planning/__manifest__.py
+++ b/hr_contract_employee_calendar_planning/__manifest__.py
@@ -1,14 +1,14 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).
{
"name": "Hr Contract Employee Calendar Planning",
- "version": "18.0.1.0.0",
+ "version": "19.0.1.0.0",
"category": "Human Resources",
"website": "https://github.com/OCA/hr",
"author": "cibex,Odoo Community Association (OCA)",
"license": "AGPL-3",
"application": False,
"auto_install": True,
- "depends": ["hr_contract", "hr_employee_calendar_planning"],
+ "depends": ["hr", "hr_employee_calendar_planning", "resource"],
"data": ["views/contract.xml"],
"post_init_hook": "post_init_hook",
}
diff --git a/hr_contract_employee_calendar_planning/hooks.py b/hr_contract_employee_calendar_planning/hooks.py
index 3372246a1f1..c016de1db03 100644
--- a/hr_contract_employee_calendar_planning/hooks.py
+++ b/hr_contract_employee_calendar_planning/hooks.py
@@ -1,37 +1,43 @@
import logging
+from odoo.fields import Command
+
_logger = logging.getLogger(__name__)
def post_init_hook(env, employees=None):
- """Migrate calendars from contracts to calendar_ids
+ """Migrate calendars from versions to calendar_ids
to have consistent work schedule history"""
if not employees:
employees = env["hr.employee"].with_context(active_test=False).search([])
- for employee in employees.filtered("contract_ids"):
- contract_calendar_lines = []
- for contract in employee.contract_ids.sorted("date_start"):
- date_start = contract.date_start
- date_end = contract.date_end
- # filter calendar_ids to check for overlaps with contracts
+ for employee in employees.filtered(
+ lambda e: e.version_ids.filtered("contract_date_start")
+ ):
+ version_calendar_lines = []
+ versions = employee.version_ids.filtered("contract_date_start").sorted(
+ "date_start"
+ )
+ for version in versions:
+ date_start = version.date_start
+ date_end = version.date_end
+ # filter calendar_ids to check for overlaps with versions
# with the same work schedule
cal_ids = employee.calendar_ids.filtered(
- lambda x, contract=contract: x.calendar_id
- == contract.resource_calendar_id
+ lambda x, version=version: x.calendar_id == version.resource_calendar_id
and (
not x.date_start
- or not contract.date_end
- or x.date_start < contract.date_end
+ or not version.date_end
+ or x.date_start < version.date_end
)
and (
not x.date_end
- or not contract.date_start
- or x.date_end > contract.date_start
+ or not version.date_start
+ or x.date_end > version.date_start
)
)
if cal_ids:
- _logger.info(f"{contract} is overlapping with {cal_ids}")
+ _logger.info(f"{version} is overlapping with {cal_ids}")
for calendar in cal_ids.sorted("date_start"):
if date_start and calendar.date_start != date_start:
_logger.info(
@@ -48,26 +54,24 @@ def post_init_hook(env, employees=None):
break
else:
_logger.info(
- f"adding new calendar_id for {contract.employee_id.name}: "
- f"{contract.resource_calendar_id.name} "
+ f"adding new calendar_id for {version.employee_id.name}: "
+ f"{version.resource_calendar_id.name} "
f"from {date_start} to {date_end}"
)
- contract_calendar_lines.append(
- (
- 0,
- 0,
+ version_calendar_lines.append(
+ Command.create(
{
"date_start": date_start,
"date_end": date_end,
- "calendar_id": contract.resource_calendar_id.id,
- },
+ "calendar_id": version.resource_calendar_id.id,
+ }
)
)
- employee.calendar_ids = contract_calendar_lines
+ employee.calendar_ids = version_calendar_lines
- # set correct calendar in contract
+ # set correct calendar in current version
# Prevent the resource calendar of leaves to be updated by a write
- employee.contract_id.with_context(
- no_leave_resource_calendar_update=True
- ).update({"resource_calendar_id": employee.resource_calendar_id.id})
+ employee.version_id.with_context(no_leave_resource_calendar_update=True).update(
+ {"resource_calendar_id": employee.resource_calendar_id.id}
+ )
diff --git a/hr_contract_employee_calendar_planning/models/__init__.py b/hr_contract_employee_calendar_planning/models/__init__.py
index 99a5468ac8a..c942b570e64 100644
--- a/hr_contract_employee_calendar_planning/models/__init__.py
+++ b/hr_contract_employee_calendar_planning/models/__init__.py
@@ -1 +1 @@
-from . import contract
+from . import hr_version
diff --git a/hr_contract_employee_calendar_planning/models/contract.py b/hr_contract_employee_calendar_planning/models/hr_version.py
similarity index 55%
rename from hr_contract_employee_calendar_planning/models/contract.py
rename to hr_contract_employee_calendar_planning/models/hr_version.py
index e12f3fbd959..431f6975763 100644
--- a/hr_contract_employee_calendar_planning/models/contract.py
+++ b/hr_contract_employee_calendar_planning/models/hr_version.py
@@ -1,41 +1,40 @@
from odoo import api, models
-class HrContract(models.Model):
- _inherit = "hr.contract"
+class HrVersion(models.Model):
+ _inherit = "hr.version"
def write(self, vals):
- if (
- vals.get("resource_calendar_id")
- and self.employee_id
+ if vals.get("resource_calendar_id") and self.filtered(
+ lambda version: version.employee_id
and vals.get("resource_calendar_id")
- != self.employee_id.resource_calendar_id.id
+ != version.employee_id.resource_calendar_id.id
):
- # in the write method of contracts, when writing the resource_calendar_id
+ # in the write method of versions, when writing the resource_calendar_id
# the employee resource_calendar_id is set to the same id
# this interferes with the logic of hr_employee_calendar_planning,
# which assumes that calendar times are managed by
# resource.calendar.attendances in auto-generated calendars
# based on the employee's calendar_ids.
- # since the default calendar for new contracts is the employee calendar,
- # and we set the correct calendar for the existing contract
+ # since the default calendar for new versions is the employee calendar,
+ # and we set the correct calendar for the existing version
# in the post_init_hook, we resolve this conflict by not allowing
- # calendar changes in contracts.
+ # calendar changes in versions.
vals.pop("resource_calendar_id")
return super().write(vals)
@api.model_create_multi
def create(self, vals_list):
- # the create method of contracts syncs contract
+ # the create method of versions syncs version
# calendars with employee calendars
# in order to not overwrite the employee calendar
- # we set the contract calendar to match the employee calendar
+ # we set the version calendar to match the employee calendar
for vals in vals_list:
- employee_contract = (
+ employee_calendar = (
self.env["hr.employee"]
.browse([vals.get("employee_id")])
.resource_calendar_id
)
- if employee_contract:
- vals.update({"resource_calendar_id": employee_contract.id})
+ if employee_calendar:
+ vals.update({"resource_calendar_id": employee_calendar.id})
return super().create(vals_list)
diff --git a/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.md b/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.md
index 847f336582a..29d9e540700 100644
--- a/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.md
+++ b/hr_contract_employee_calendar_planning/readme/CONTRIBUTORS.md
@@ -1 +1,3 @@
- Jonas Buchholz \
+- [Studio73](https://www.studio73.es/):
+- Vicent Castells \<\>
diff --git a/hr_contract_employee_calendar_planning/static/description/index.html b/hr_contract_employee_calendar_planning/static/description/index.html
index e1ab2c2bccd..20f1fac8145 100644
--- a/hr_contract_employee_calendar_planning/static/description/index.html
+++ b/hr_contract_employee_calendar_planning/static/description/index.html
@@ -374,7 +374,7 @@ Hr Contract Employee Calendar Planning
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
!! source digest: sha256:18ed8fc47869734b2baa5068f1346d1bbc662d2b834e9a2efa4ce502117ea7fa
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->
-

+

This module ensures a consistent working times history when using the
hr_contract and hr_employee_calendar_planning modules together.
There are 3 different data models which can relate to working time
@@ -423,7 +423,7 @@
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.
@@ -449,7 +451,7 @@
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 project on GitHub.
+
This module is part of the OCA/hr project on GitHub.
You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.
diff --git a/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py b/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
index bd3aada17cb..a6209894d96 100644
--- a/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
+++ b/hr_contract_employee_calendar_planning/tests/test_hr_contract_employee_calendar_planning.py
@@ -1,12 +1,38 @@
from datetime import datetime
-from odoo.addons.hr_contract.tests.common import TestContractCommon
+from odoo.addons.hr.tests.common import TestHrCommon
from ..hooks import post_init_hook
-class TestHrContractEmployeeCalendarPlanning(TestContractCommon):
+class TestHrContractEmployeeCalendarPlanning(TestHrCommon):
def test_calendar_migration_from_contracts(self):
+ calendar_40h = self.env["resource.calendar"].browse(1)
+ calendar_35h = calendar_40h.copy(
+ {
+ "name": "Test 35h calendar",
+ "attendance_ids": [(5, 0, 0)],
+ }
+ )
+ calendar_35h.write(
+ {
+ "attendance_ids": [
+ (
+ 0,
+ 0,
+ {
+ "name": "Monday",
+ "dayofweek": "0",
+ "hour_from": 8,
+ "hour_to": 15,
+ },
+ ),
+ ],
+ }
+ )
+
+ self.assertTrue(calendar_40h.exists())
+ self.assertTrue(calendar_35h.exists())
self.env = self.env(
context=dict(
self.env.context,
@@ -19,30 +45,28 @@ def test_calendar_migration_from_contracts(self):
)
# newly created contracts get the same calendar as the employee
- self.employee.resource_calendar_id = self.env.ref(
- "resource.resource_calendar_std"
- )
- self.env["hr.contract"].create(
+ self.employee.resource_calendar_id = calendar_40h
+ self.employee.create_version(
{
"name": "contract1",
"wage": 1,
- "state": "close",
- "employee_id": self.employee.id,
- "date_start": datetime.strptime("2018-11-30", "%Y-%m-%d").date(),
- "date_end": datetime.strptime("2019-11-30", "%Y-%m-%d").date(),
+ "date_version": datetime.strptime("2018-11-30", "%Y-%m-%d").date(),
+ "contract_date_start": datetime.strptime(
+ "2018-11-30", "%Y-%m-%d"
+ ).date(),
+ "contract_date_end": datetime.strptime("2019-11-30", "%Y-%m-%d").date(),
}
)
- self.employee.resource_calendar_id = self.env.ref(
- "resource.resource_calendar_std_35h"
- )
- self.env["hr.contract"].create(
+ self.employee.resource_calendar_id = calendar_35h
+ self.employee.create_version(
{
"name": "contract2",
"wage": 1,
- "state": "open",
- "employee_id": self.employee.id,
- "date_start": datetime.strptime("2019-12-01", "%Y-%m-%d").date(),
- "date_end": datetime.strptime("2020-11-30", "%Y-%m-%d").date(),
+ "date_version": datetime.strptime("2019-12-01", "%Y-%m-%d").date(),
+ "contract_date_start": datetime.strptime(
+ "2019-12-01", "%Y-%m-%d"
+ ).date(),
+ "contract_date_end": datetime.strptime("2020-11-30", "%Y-%m-%d").date(),
}
)
self.employee.calendar_ids.unlink()
@@ -51,13 +75,13 @@ def test_calendar_migration_from_contracts(self):
{
"date_start": False,
"date_end": datetime.strptime("2020-11-30", "%Y-%m-%d").date(),
- "calendar_id": 2,
+ "calendar_id": calendar_35h.id,
"employee_id": self.employee.id,
},
{
"date_start": datetime.strptime("2020-12-01", "%Y-%m-%d").date(),
"date_end": False,
- "calendar_id": 1,
+ "calendar_id": calendar_40h.id,
"employee_id": self.employee.id,
},
]
@@ -84,19 +108,21 @@ def test_calendar_migration_from_contracts(self):
def test_contract_create_write(self):
self.employee.resource_calendar_id = self.env["resource.calendar"].browse([1])
- contract = self.env["hr.contract"].create(
+ version = self.env["hr.version"].create(
{
"name": "contract1",
"wage": 1,
- "state": "close",
"employee_id": self.employee.id,
- "date_start": datetime.strptime("2018-11-30", "%Y-%m-%d").date(),
- "date_end": datetime.strptime("2019-11-30", "%Y-%m-%d").date(),
+ "date_version": datetime.strptime("2018-11-30", "%Y-%m-%d").date(),
+ "contract_date_start": datetime.strptime(
+ "2018-11-30", "%Y-%m-%d"
+ ).date(),
+ "contract_date_end": datetime.strptime("2019-11-30", "%Y-%m-%d").date(),
"resource_calendar_id": self.env["resource.calendar"].browse([2]).id,
}
)
self.assertEqual(self.employee.resource_calendar_id.id, 1)
- contract.write(
+ version.write(
{
"resource_calendar_id": self.env["resource.calendar"].browse([2]).id,
}
@@ -105,7 +131,7 @@ def test_contract_create_write(self):
def test_contract_write(self):
self.employee.resource_calendar_id = self.env["resource.calendar"].browse([1])
- self.env["hr.contract"].write(
+ self.env["hr.version"].write(
{
"resource_calendar_id": 2,
}
diff --git a/hr_contract_employee_calendar_planning/views/contract.xml b/hr_contract_employee_calendar_planning/views/contract.xml
index 7be868e4476..512d54d1d9b 100644
--- a/hr_contract_employee_calendar_planning/views/contract.xml
+++ b/hr_contract_employee_calendar_planning/views/contract.xml
@@ -1,26 +1,25 @@
- hr.contract.employee.calendar.planning.form
- hr.contract
-
+ hr.employee.calendar.planning.form
+ hr.employee
+
999
-
-
+
- hr.contract.employee.calendar.planning.history.form
- hr.contract.history
-
+ hr.version.employee.calendar.planning.history.form
+ hr.version
+
999
-
-
-