From 1768a5f832085687cc964c4eed6274ba7bca6650 Mon Sep 17 00:00:00 2001
From: LauraCForgeFlow <laura.cazorla@forgeflow.com>
Date: Thu, 14 Nov 2024 08:41:33 +0100
Subject: [PATCH] [IMP] hr_holidays_natural_period: exclude public holidays in
 natural days computation

---
 hr_holidays_natural_period/README.rst         |   2 +
 hr_holidays_natural_period/__manifest__.py    |   3 +-
 .../demo/hr_leave_type_data.xml               |   1 +
 .../models/resource_calendar.py               |   8 +-
 hr_holidays_natural_period/readme/USAGE.rst   |   2 +
 .../static/description/index.html             |   2 +
 .../tests/test_hr_leave.py                    | 113 ++++++++++++++++--
 .../views/hr_leave_views.xml                  |  27 +++++
 8 files changed, 147 insertions(+), 11 deletions(-)
 create mode 100644 hr_holidays_natural_period/views/hr_leave_views.xml

diff --git a/hr_holidays_natural_period/README.rst b/hr_holidays_natural_period/README.rst
index 12a32afa..44787707 100644
--- a/hr_holidays_natural_period/README.rst
+++ b/hr_holidays_natural_period/README.rst
@@ -47,6 +47,8 @@ For using natural period on leaves:
 #. If no leave type is yet specified, then default configuration is to exclude
    public holidays.
 #. The number of days will be computed without employee calendar used.
+#. The computation can exclude public holidays or not, depending on the configuration
+   of the leave type.
 
 Bug Tracker
 ===========
diff --git a/hr_holidays_natural_period/__manifest__.py b/hr_holidays_natural_period/__manifest__.py
index 08ff5304..5d87f6e4 100644
--- a/hr_holidays_natural_period/__manifest__.py
+++ b/hr_holidays_natural_period/__manifest__.py
@@ -10,7 +10,8 @@
     "author": "Tecnativa, Odoo Community Association (OCA)",
     "license": "AGPL-3",
     "installable": True,
-    "depends": ["hr_holidays"],
+    "depends": ["hr_holidays_public"],
     "maintainers": ["victoralmau"],
     "demo": ["demo/hr_leave_type_data.xml"],
+    "data": ["views/hr_leave_views.xml"],
 }
diff --git a/hr_holidays_natural_period/demo/hr_leave_type_data.xml b/hr_holidays_natural_period/demo/hr_leave_type_data.xml
index a10abd02..7ee7e27b 100644
--- a/hr_holidays_natural_period/demo/hr_leave_type_data.xml
+++ b/hr_holidays_natural_period/demo/hr_leave_type_data.xml
@@ -5,5 +5,6 @@
         <field name="request_unit">natural_day</field>
         <field name="responsible_id" ref="base.user_admin" />
         <field name="employee_requests">yes</field>
+        <field name="exclude_public_holidays">False</field>
     </record>
 </odoo>
diff --git a/hr_holidays_natural_period/models/resource_calendar.py b/hr_holidays_natural_period/models/resource_calendar.py
index 9ae4784f..7fbbe2fb 100644
--- a/hr_holidays_natural_period/models/resource_calendar.py
+++ b/hr_holidays_natural_period/models/resource_calendar.py
@@ -47,7 +47,9 @@ def _attendance_intervals_batch(
             start_dt=start_dt, end_dt=end_dt, resources=resources, domain=domain, tz=tz
         )
         if self.env.context.get("natural_period"):
-            return self._natural_period_intervals_batch(
-                start_dt, end_dt, res, resources
-            )
+            res = self._natural_period_intervals_batch(start_dt, end_dt, res, resources)
+            if self.env.context.get("exclude_public_holidays") and resources:
+                return self._attendance_intervals_batch_exclude_public_holidays(
+                    start_dt, end_dt, res, resources, tz
+                )
         return res
diff --git a/hr_holidays_natural_period/readme/USAGE.rst b/hr_holidays_natural_period/readme/USAGE.rst
index a4425e87..7ef0e7c6 100644
--- a/hr_holidays_natural_period/readme/USAGE.rst
+++ b/hr_holidays_natural_period/readme/USAGE.rst
@@ -7,3 +7,5 @@ For using natural period on leaves:
 #. If no leave type is yet specified, then default configuration is to exclude
    public holidays.
 #. The number of days will be computed without employee calendar used.
+#. The computation can exclude public holidays or not, depending on the configuration
+   of the leave type.
diff --git a/hr_holidays_natural_period/static/description/index.html b/hr_holidays_natural_period/static/description/index.html
index 8cf807aa..a443cd47 100644
--- a/hr_holidays_natural_period/static/description/index.html
+++ b/hr_holidays_natural_period/static/description/index.html
@@ -394,6 +394,8 @@ <h1><a class="toc-backref" href="#toc-entry-1">Usage</a></h1>
 <li>If no leave type is yet specified, then default configuration is to exclude
 public holidays.</li>
 <li>The number of days will be computed without employee calendar used.</li>
+<li>The computation can exclude public holidays or not, depending on the configuration
+of the leave type.</li>
 </ol>
 </div>
 <div class="section" id="bug-tracker">
diff --git a/hr_holidays_natural_period/tests/test_hr_leave.py b/hr_holidays_natural_period/tests/test_hr_leave.py
index cd6be330..0e467cd8 100644
--- a/hr_holidays_natural_period/tests/test_hr_leave.py
+++ b/hr_holidays_natural_period/tests/test_hr_leave.py
@@ -56,6 +56,12 @@ def setUpClass(cls):
                 "user_id": cls.user.id,
             }
         )
+        cls.public_holiday = cls.env["hr.holidays.public"].create(
+            {
+                "year": 2023,
+                "country_id": False,
+            }
+        )
 
     def _create_leave_allocation(self, leave_type, days):
         leave_allocation_form = Form(
@@ -76,21 +82,114 @@ def _create_hr_leave(self, leave_type, date_from, date_to):
         leave_form.request_date_to = date_to
         return leave_form.save()
 
+    def _create_public_holiday_line(self, name, date, year):
+        public_holiday_line = Form(self.env["hr.holidays.public.line"].sudo())
+        public_holiday_line.name = name
+        public_holiday_line.date = date
+        public_holiday_line.year_id = year
+        return public_holiday_line.save()
+
     @users("test-user")
     def test_hr_leave_natural_day(self):
-        leave_allocation = self._create_leave_allocation(self.leave_type, 5)
+        leave_allocation = self._create_leave_allocation(self.leave_type, 10)
         leave_allocation.action_confirm()
         leave_allocation.sudo().action_validate()
         res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
-        self.assertEqual(res_leave_type["remaining_leaves"], "5")
-        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "5")
-        self.assertEqual(res_leave_type["max_leaves"], "5")
+        self.assertEqual(res_leave_type["remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["max_leaves"], "10")
+        self.assertEqual(res_leave_type["leaves_taken"], "0")
+        self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
+        self.assertEqual(res_leave_type["request_unit"], "natural_day")
+        leave = self._create_hr_leave(self.leave_type, "2023-01-08", "2023-01-15")
+        self.assertEqual(leave.number_of_days, 8)
+        self.assertEqual(leave.number_of_days_display, 8)
+
+    @users("test-user")
+    def test_hr_leave_natural_day_public_holiday_01(self):
+        leave_allocation = self._create_leave_allocation(self.leave_type, 10)
+        leave_allocation.action_confirm()
+        leave_allocation.sudo().action_validate()
+        self._create_public_holiday_line(
+            "Public holiday 1", "2023-01-09", self.public_holiday
+        )
+
+        res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
+        self.assertEqual(res_leave_type["remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["max_leaves"], "10")
+        self.assertEqual(res_leave_type["leaves_taken"], "0")
+        self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
+        self.assertEqual(res_leave_type["request_unit"], "natural_day")
+        self.assertEqual(self.leave_type.exclude_public_holidays, False)
+        leave = self._create_hr_leave(self.leave_type, "2023-01-08", "2023-01-15")
+        self.assertEqual(leave.number_of_days, 8)
+        self.assertEqual(leave.number_of_days_display, 8)
+
+    @users("test-user")
+    def test_hr_leave_natural_day_public_holiday_02(self):
+        leave_allocation = self._create_leave_allocation(self.leave_type, 10)
+        leave_allocation.action_confirm()
+        leave_allocation.sudo().action_validate()
+        self._create_public_holiday_line(
+            "Public holiday 1", "2023-01-09", self.public_holiday
+        )
+        self.leave_type.write({"exclude_public_holidays": True})
+
+        res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
+        self.assertEqual(res_leave_type["remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["max_leaves"], "10")
+        self.assertEqual(res_leave_type["leaves_taken"], "0")
+        self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
+        self.assertEqual(res_leave_type["request_unit"], "natural_day")
+        self.assertEqual(self.leave_type.exclude_public_holidays, True)
+        leave = self._create_hr_leave(self.leave_type, "2023-01-08", "2023-01-15")
+        self.assertEqual(leave.number_of_days, 7)
+        self.assertEqual(leave.number_of_days_display, 7)
+
+    @users("test-user")
+    def test_hr_leave_natural_day_public_holiday_weekend_01(self):
+        leave_allocation = self._create_leave_allocation(self.leave_type, 10)
+        leave_allocation.action_confirm()
+        leave_allocation.sudo().action_validate()
+        self._create_public_holiday_line(
+            "Public holiday 1", "2023-01-14", self.public_holiday
+        )
+
+        res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
+        self.assertEqual(res_leave_type["remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["max_leaves"], "10")
+        self.assertEqual(res_leave_type["leaves_taken"], "0")
+        self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
+        self.assertEqual(res_leave_type["request_unit"], "natural_day")
+        self.assertEqual(self.leave_type.exclude_public_holidays, False)
+        leave = self._create_hr_leave(self.leave_type, "2023-01-08", "2023-01-15")
+        self.assertEqual(leave.number_of_days, 8)
+        self.assertEqual(leave.number_of_days_display, 8)
+
+    @users("test-user")
+    def test_hr_leave_natural_day_public_holiday_weekend_02(self):
+        leave_allocation = self._create_leave_allocation(self.leave_type, 10)
+        leave_allocation.action_confirm()
+        leave_allocation.sudo().action_validate()
+        self._create_public_holiday_line(
+            "Public holiday 1", "2023-01-14", self.public_holiday
+        )
+        self.leave_type.write({"exclude_public_holidays": True})
+
+        res_leave_type = self.env["hr.leave.type"].get_days_all_request()[0][1]
+        self.assertEqual(res_leave_type["remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["virtual_remaining_leaves"], "10")
+        self.assertEqual(res_leave_type["max_leaves"], "10")
         self.assertEqual(res_leave_type["leaves_taken"], "0")
         self.assertEqual(res_leave_type["virtual_leaves_taken"], "0")
         self.assertEqual(res_leave_type["request_unit"], "natural_day")
-        leave = self._create_hr_leave(self.leave_type, "2023-01-02", "2023-01-05")
-        self.assertEqual(leave.number_of_days, 4.0)
-        self.assertEqual(leave.number_of_days_display, 4.0)
+        self.assertEqual(self.leave_type.exclude_public_holidays, True)
+        leave = self._create_hr_leave(self.leave_type, "2023-01-08", "2023-01-15")
+        self.assertEqual(leave.number_of_days, 7)
+        self.assertEqual(leave.number_of_days_display, 7)
 
     @users("test-user")
     def test_hr_leave_day(self):
diff --git a/hr_holidays_natural_period/views/hr_leave_views.xml b/hr_holidays_natural_period/views/hr_leave_views.xml
new file mode 100644
index 00000000..cc20860d
--- /dev/null
+++ b/hr_holidays_natural_period/views/hr_leave_views.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<odoo>
+
+    <record id="hr_leave_view_form" model="ir.ui.view">
+        <field name="name">hr.leave.view.form</field>
+        <field name="model">hr.leave</field>
+        <field name="inherit_id" ref="hr_holidays.hr_leave_view_form" />
+        <field name="arch" type="xml">
+            <xpath expr="//label[@for='request_unit_half']" position="attributes">
+                <attribute
+                    name="attrs"
+                >{'invisible': [('leave_type_request_unit', 'in', ['day', 'natural_day'])]}</attribute>
+            </xpath>
+            <xpath expr="//field[@name='request_unit_half']" position="attributes">
+                <attribute
+                    name="attrs"
+                >{'readonly': [('state', 'not in', ('draft', 'confirm'))], 'invisible': [('leave_type_request_unit', 'in', ['day', 'natural_day'])]}</attribute>
+            </xpath>
+            <xpath expr="//div[field[@name='request_unit_half']]" position="attributes">
+                <attribute
+                    name="attrs"
+                >{'invisible': [('leave_type_request_unit', 'in', ['day', 'natural_day'])]}</attribute>
+            </xpath>
+        </field>
+    </record>
+
+</odoo>