From 30f0748cb89fcbe2f697fb08ba77c3303c1c318a Mon Sep 17 00:00:00 2001 From: miguelpadin Date: Tue, 20 Feb 2024 17:46:48 +0000 Subject: [PATCH] =?UTF-8?q?[WIP]=20pms-housekeeping:=20wip=20method=20to?= =?UTF-8?q?=20create=20automated=20tasks=C3=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pms_housekeeping/README.rst | 2 +- pms_housekeeping/__manifest__.py | 1 + pms_housekeeping/data/cron_jobs.xml | 16 + pms_housekeeping/models/hr_employee.py | 41 +- .../models/pms_housekeeping_task.py | 149 ++++++ .../models/pms_housekeeping_task_type.py | 63 ++- .../static/description/index.html | 2 +- pms_housekeeping/tests/__init__.py | 2 + pms_housekeeping/tests/common.py | 63 +++ .../tests/test_pms_housekeeping_task.py | 466 ++++++++++++++++++ .../tests/test_pms_hr_employee.py | 92 ++++ pms_housekeeping/views/hr_employee_views.xml | 2 + .../pms_housekeeping_task_type_views.xml | 8 +- 13 files changed, 901 insertions(+), 6 deletions(-) create mode 100644 pms_housekeeping/data/cron_jobs.xml create mode 100644 pms_housekeeping/tests/__init__.py create mode 100644 pms_housekeeping/tests/common.py create mode 100644 pms_housekeeping/tests/test_pms_housekeeping_task.py create mode 100644 pms_housekeeping/tests/test_pms_hr_employee.py diff --git a/pms_housekeeping/README.rst b/pms_housekeeping/README.rst index cd50d442e..c91c03231 100644 --- a/pms_housekeeping/README.rst +++ b/pms_housekeeping/README.rst @@ -7,7 +7,7 @@ Housekeeping !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! - !! source digest: sha256:4807e347dd2088e7cfafc8201f1105b6162c3f0793e9b87f570b6ba38c1756e7 + !! source digest: sha256:ad6a062413114aacf03fe2fbca31a04c8247dd80589f13cc1f21d890a478f2eb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! .. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png diff --git a/pms_housekeeping/__manifest__.py b/pms_housekeeping/__manifest__.py index 92c7d9212..638a1512b 100644 --- a/pms_housekeeping/__manifest__.py +++ b/pms_housekeeping/__manifest__.py @@ -17,6 +17,7 @@ "data": [ "security/ir.model.access.csv", "data/pms_housekeeping_data.xml", + "data/cron_jobs.xml", "views/hr_employee_views.xml", "views/pms_housekeeping_task_type_views.xml", "views/pms_housekeeping_views.xml", diff --git a/pms_housekeeping/data/cron_jobs.xml b/pms_housekeeping/data/cron_jobs.xml new file mode 100644 index 000000000..2abe2f446 --- /dev/null +++ b/pms_housekeeping/data/cron_jobs.xml @@ -0,0 +1,16 @@ + + + + + Generate Housekeeping Tasks + 1 + + days + -1 + + code + + + model.generate_task_properties() + + diff --git a/pms_housekeeping/models/hr_employee.py b/pms_housekeeping/models/hr_employee.py index 323d57f88..1fc7606a1 100644 --- a/pms_housekeeping/models/hr_employee.py +++ b/pms_housekeeping/models/hr_employee.py @@ -1,7 +1,8 @@ # Copyright 2020 Jose Luis Algara (Alda Hotels ) # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import api, fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class HrEmployee(models.Model): @@ -13,9 +14,47 @@ class HrEmployee(models.Model): help="Rooms pre assigned to this employee", ) + allowed_pre_assigned_room_ids = fields.Many2many( + comodel_name="pms.room", + string="Allowed Pre Assigned Rooms", + help="Rooms allowed to be pre assigned to this employee", + compute="_compute_allowed_pre_assigned_room_ids", + ) + job_name = fields.Char(string="Job Name", compute="_compute_job_name") + @api.constrains("pre_assigned_room_ids") + def _check_pre_assigned_room_ids(self): + for record in self: + if record.pre_assigned_room_ids: + for room in record.pre_assigned_room_ids: + if room not in record.allowed_pre_assigned_room_ids: + raise ValidationError( + _("The room should belong to the employee's property.") + ) + + @api.constrains("pre_assigned_room_ids") + def _check_job_id(self): + for record in self: + if ( + record.job_id + and record.job_id + != self.env.ref("pms_housekeeping.housekeeping_job_id") + and record.pre_assigned_room_ids + ): + raise ValidationError(_("The job position should be Housekeeper.")) + @api.depends("job_id") def _compute_job_name(self): for record in self: record.job_name = record.job_id.name + + @api.depends("property_ids") + def _compute_allowed_pre_assigned_room_ids(self): + for record in self: + domain = [] + if record.property_ids: + domain.append(("pms_property_id", "in", record.property_ids.ids)) + record.allowed_pre_assigned_room_ids = ( + self.env["pms.room"].search(domain).ids + ) diff --git a/pms_housekeeping/models/pms_housekeeping_task.py b/pms_housekeeping/models/pms_housekeeping_task.py index 7d435cb4b..54cd0602c 100644 --- a/pms_housekeeping/models/pms_housekeeping_task.py +++ b/pms_housekeeping/models/pms_housekeeping_task.py @@ -1,3 +1,5 @@ +from datetime import timedelta + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -103,6 +105,12 @@ class PmsHouseKeepingTask(models.Model): _("Task Date must be greater than or equal to today") ) + @api.constrains("parent_id") + def _check_parent_id(self): + for rec in self: + if rec.parent_id.parent_id: + raise ValidationError(_("Parent task cannot have a parent task")) + def action_cancel(self): for rec in self: rec.state = "cancel" @@ -235,3 +243,144 @@ class PmsHouseKeepingTask(models.Model): ) return super(PmsHouseKeepingTask, self).create(vals) + + @api.model + def generate_tasks(self, pms_property_id): + for room in self.env["pms.room"].search( + [("pms_property_id", "=", pms_property_id.id)] + ): + for task_type in self.env["pms.housekeeping.task.type"].search( + [ + "|", + ("pms_property_ids", "in", [pms_property_id.id]), + ("pms_property_ids", "=", False), + ], + order="priority asc", + ): + if task_type.is_checkout: + reservations_with_checkout_today = self.env[ + "pms.reservation" + ].search( + [ + ("checkout", "=", fields.Date.today()), + ] + ) + reservation_line_with_checkout_today = self.env[ + "pms.reservation.line" + ].search( + [ + ( + "reservation_id", + "in", + reservations_with_checkout_today.ids, + ), + ("room_id", "=", room.id), + ] + ) + if reservation_line_with_checkout_today: + self.create_housekeeping_tasks(room, task_type) + break + + if task_type.is_overnight: + reservation_line_today = self.env["pms.reservation.line"].search( + [ + ("room_id", "=", room.id), + ("date", "=", fields.Date.today() + timedelta(days=-1)), + ("occupies_availability", "=", True), + ] + ) + if reservation_line_today and len(reservation_line_today) == 1: + reservation_checkin = ( + self.env["pms.reservation"] + .browse(reservation_line_today.reservation_id.id) + .checkin + ) + + days_between_checkin_and_today = ( + fields.Date.today() + ) - reservation_checkin + if ( + days_between_checkin_and_today.days + % task_type.days_after_clean_overnight + == 0 + ): + self.create_housekeeping_tasks(room, task_type) + break + if task_type.is_checkin: + reservations_with_checkin_today = self.env[ + "pms.reservation" + ].search( + [ + ("checkin", "=", fields.Date.today()), + ] + ) + reservation_line_with_checkout_today = self.env[ + "pms.reservation.line" + ].search( + [ + ( + "reservation_id", + "in", + reservations_with_checkin_today.ids, + ), + ("room_id", "=", room.id), + ] + ) + if reservation_line_with_checkout_today: + self.create_housekeeping_tasks(room, task_type) + break + if task_type.is_empty: + previous_reservations = self.env["pms.reservation"].search( + [ + ("checkout", "<", fields.Date.today()), + ("pms_property_id", "=", pms_property_id.id), + ] + ) + checkouts = ( + self.env["pms.reservation.line"] + .search( + [ + ("reservation_id", "in", previous_reservations.ids), + ("room_id", "=", room.id), + ], + ) + .mapped("date") + ) + + if checkouts: + last_checkout = max(checkouts) + days_between_last_checkout_and_today = (fields.Date.today()) - ( + last_checkout + timedelta(days=1) + ) + if ( + days_between_last_checkout_and_today.days + % task_type.days_after_clean_empty + == 0 + ): + self.create_housekeeping_tasks(room, task_type) + break + + def create_housekeeping_tasks(self, room, task_type): + task = self.env["pms.housekeeping.task"].create( + { + "name": task_type.name + " " + room.name, + "room_id": room.id, + "task_type_id": task_type.id, + "task_date": fields.Date.today(), + } + ) + + for task_type_child in task_type.child_ids: + self.env["pms.housekeeping.task"].create( + { + "name": task_type_child.name + " " + room.name, + "task_type_id": task_type_child.id, + "room_id": room.id, + "task_date": fields.Date.today(), + "parent_id": task.id, + } + ) + + def generate_task_properties(self): + for pms_property in self.env["pms.property"].search([]): + self.generate_tasks(pms_property) diff --git a/pms_housekeeping/models/pms_housekeeping_task_type.py b/pms_housekeeping/models/pms_housekeeping_task_type.py index ff7986847..056cc90df 100644 --- a/pms_housekeeping/models/pms_housekeeping_task_type.py +++ b/pms_housekeeping/models/pms_housekeeping_task_type.py @@ -1,4 +1,5 @@ -from odoo import fields, models +from odoo import _, api, fields, models +from odoo.exceptions import ValidationError class PmsHouseKeepingTaskType(models.Model): @@ -24,7 +25,6 @@ class PmsHouseKeepingTaskType(models.Model): column1="task_type_id", column2="employee_id", string="Housekeepers", - domain="[('job_id.name', '=', 'Housekeeper')]", ) parent_id = fields.Many2one( string="Parent Task Type", @@ -32,4 +32,63 @@ class PmsHouseKeepingTaskType(models.Model): comodel_name="pms.housekeeping.task.type", domain="[('id', '!=', id)]", ) + child_ids = fields.One2many( + string="Child Task Types", + comodel_name="pms.housekeeping.task.type", + inverse_name="parent_id", + ) is_inspection = fields.Boolean(string="Inspection") + + pms_property_ids = fields.Many2many( + comodel_name="pms.property", + relation="pms_housekeeping_task_type_pms_property_rel", + column1="task_type_id", + column2="property_id", + string="Properties", + ) + + allowed_housekeeper_ids = fields.Many2many( + comodel_name="hr.employee", + relation="pms_housekeeping_task_type_allowed_hr_employee_rel", + column1="task_type_id", + column2="employee_id", + string="Allowed Employees", + compute="_compute_allowed_housekeeper_ids", + ) + + @api.constrains("is_overnight", "days_after_clean_overnight") + def _check_days_after_clean_overnight(self): + for record in self: + if record.is_overnight and record.days_after_clean_overnight <= 0: + raise ValidationError( + _("Days After Clean Overnight should be greater than 0") + ) + + @api.constrains("is_empty", "days_after_clean_empty") + def _check_days_after_clean_empty(self): + for record in self: + if record.is_empty and record.days_after_clean_empty <= 0: + raise ValidationError( + _("Days After Clean Empty should be greater than 0") + ) + + @api.constrains("parent_id") + def _check_parent_id(self): + for rec in self: + if rec.parent_id.parent_id: + raise ValidationError( + _("Parent task type cannot have a parent task type") + ) + + @api.depends("pms_property_ids") + def _compute_allowed_housekeeper_ids(self): + for record in self: + domain = [] + if record.pms_property_ids: + domain = [ + "|", + ("property_ids", "in", record.pms_property_ids.ids), + ("property_ids", "in", False), + ] + domain.append(("job_id.name", "=", "Housekeeper")) + record.allowed_housekeeper_ids = self.env["hr.employee"].search(domain).ids diff --git a/pms_housekeeping/static/description/index.html b/pms_housekeeping/static/description/index.html index 807ad9226..8bad6dcfd 100644 --- a/pms_housekeeping/static/description/index.html +++ b/pms_housekeeping/static/description/index.html @@ -367,7 +367,7 @@ ul.auto-toc { !! This file is generated by oca-gen-addon-readme !! !! changes will be overwritten. !! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -!! source digest: sha256:4807e347dd2088e7cfafc8201f1105b6162c3f0793e9b87f570b6ba38c1756e7 +!! source digest: sha256:ad6a062413114aacf03fe2fbca31a04c8247dd80589f13cc1f21d890a478f2eb !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! -->

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

This module adds housekeeping feature to property management system (PMS).

diff --git a/pms_housekeeping/tests/__init__.py b/pms_housekeeping/tests/__init__.py new file mode 100644 index 000000000..b90277bc3 --- /dev/null +++ b/pms_housekeeping/tests/__init__.py @@ -0,0 +1,2 @@ +from . import test_pms_housekeeping_task +from . import test_pms_hr_employee diff --git a/pms_housekeeping/tests/common.py b/pms_housekeeping/tests/common.py new file mode 100644 index 000000000..2563d9d14 --- /dev/null +++ b/pms_housekeeping/tests/common.py @@ -0,0 +1,63 @@ +from odoo.tests import common + + +class TestPms(common.SavepointCase): + def setUp(self): + super().setUp() + # delete all previous pms.housekeeping.task.type records (only for the test purpose) + self.env["pms.housekeeping.task.type"].search([]).unlink() + # create a sale channel + self.sale_channel1 = self.env["pms.sale.channel"].create( + { + "name": "Door", + "channel_type": "direct", + } + ) + self.availability_plan1 = self.env["pms.availability.plan"].create( + { + "name": "Availability Plan 1", + } + ) + self.pricelist1 = self.env["product.pricelist"].create( + { + "name": "Pricelist 1", + "availability_plan_id": self.availability_plan1.id, + "is_pms_available": True, + } + ) + self.company1 = self.env["res.company"].create( + { + "name": "Company 1", + } + ) + self.pms_property1 = self.env["pms.property"].create( + { + "name": "Property 1", + "company_id": self.company1.id, + "default_pricelist_id": self.pricelist1.id, + } + ) + self.room_type_class1 = self.env["pms.room.type.class"].create( + { + "name": "Room Type Class 1", + "default_code": "RTC1", + } + ) + self.room_type1 = self.env["pms.room.type"].create( + { + "name": "Room type 1", + "default_code": "c1", + "company_id": self.company1.id, + "class_id": self.room_type_class1.id, + } + ) + + self.room1 = self.env["pms.room"].create( + { + "name": "Room 101", + "pms_property_id": self.pms_property1.id, + "room_type_id": self.room_type1.id, + } + ) + # create partner + self.partner1 = self.env["res.partner"].create({"name": "Ana"}) diff --git a/pms_housekeeping/tests/test_pms_housekeeping_task.py b/pms_housekeeping/tests/test_pms_housekeeping_task.py new file mode 100644 index 000000000..089ad86f8 --- /dev/null +++ b/pms_housekeeping/tests/test_pms_housekeeping_task.py @@ -0,0 +1,466 @@ +from datetime import datetime, timedelta + +from freezegun import freeze_time + +from odoo.exceptions import ValidationError + +from .common import TestPms + + +class TestPmsHousekeepingTask(TestPms): + def setUp(self): + super().setUp() + + @freeze_time("2000-01-04") + def test_no_create_overnight_task_when_it_shouldnt_when_no_overnight(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_overnight": True, + "days_after_clean_overnight": 2, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertFalse(housekeeping_task, "Housekeeping task shouldn't be created") + + @freeze_time("2000-01-10") + def test_create_overnight_task_when_it_should_be_created_with_different_dates(self): + # ARRANGE + # create reservation with checkin today + self.env["pms.reservation"].create( + { + "checkin": datetime.today(), + "checkout": datetime.today() + timedelta(days=7), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_overnight": True, + "days_after_clean_overnight": 2, + } + ) + # Define a list of dates to iterate over + test_dates = [ + "2000-01-12", + "2000-01-14", + "2000-01-16", + ] + for test_date in test_dates: + with self.subTest(test_date=test_date): + # Freeze time to the current test date + with freeze_time(test_date): + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id), ("task_date", "=", test_date)] + ) + # Verify that the housekeeping task is created + self.assertTrue( + housekeeping_task, "Housekeeping task should be created" + ) + + @freeze_time("2000-01-10") + def test_create_overnight_task_when_it_shouldnt_be_created_with_different_dates( + self, + ): + # ARRANGE + # create reservation with checkin today + self.env["pms.reservation"].create( + { + "checkin": datetime.today(), + "checkout": datetime.today() + timedelta(days=7), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_overnight": True, + "days_after_clean_overnight": 2, + } + ) + # Define a list of dates to iterate over + test_dates = [ + "2000-01-11", + "2000-01-13", + "2000-01-15", + ] + for test_date in test_dates: + with self.subTest(test_date=test_date): + # Freeze time to the current test date + with freeze_time(test_date): + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id), ("task_date", "=", test_date)] + ) + # Verify that the housekeeping task is created + self.assertFalse( + housekeeping_task, "Housekeeping task shouldn't be created" + ) + + ################### + @freeze_time("2000-01-04") + def test_no_create_empty_task_when_it_shouldnt_when_no_empty(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_empty": True, + "days_after_clean_empty": 2, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertFalse(housekeeping_task, "Housekeeping task shouldn't be created") + + @freeze_time("2000-02-11") + def test_create_empty_task_when_it_should_be_created_with_different_dates(self): + # ARRANGE + # create reservation with checkout today - 10 days + self.env["pms.reservation"].create( + { + "checkin": datetime.today() + timedelta(days=-20), + "checkout": datetime.today() + timedelta(days=-10), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + # create task type + self.env["pms.housekeeping.task.type"].create( + {"name": "Task Type 1", "is_empty": True, "days_after_clean_empty": 2} + ) + # Define a list of dates to iterate over + test_dates = [ + "2000-02-03", + "2000-02-05", + "2000-02-07", + ] + for test_date in test_dates: + with self.subTest(test_date=test_date): + # Freeze time to the current test date + with freeze_time(test_date): + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id), ("task_date", "=", test_date)] + ) + # Verify that the housekeeping task is created + self.assertTrue( + housekeeping_task, "Housekeeping task should be created" + ) + + @freeze_time("2000-02-11") + def test_create_empty_task_when_it_shouldnt_be_created_with_different_dates(self): + # ARRANGE + # create reservation with checkout today - 10 days + self.env["pms.reservation"].create( + { + "checkin": datetime.today() + timedelta(days=-20), + "checkout": datetime.today() + timedelta(days=-10), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + # create task type + self.env["pms.housekeeping.task.type"].create( + {"name": "Task Type 1", "is_empty": True, "days_after_clean_empty": 2} + ) + # Define a list of dates to iterate over + test_dates = [ + "2000-02-02", + "2000-02-04", + "2000-02-06", + ] + for test_date in test_dates: + with self.subTest(test_date=test_date): + # Freeze time to the current test date + with freeze_time(test_date): + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id), ("task_date", "=", test_date)] + ) + # Verify that the housekeeping task is created + self.assertFalse( + housekeeping_task, "Housekeeping task should be created" + ) + + @freeze_time("2000-01-04") + def test_create_checkin_task_when_it_should_when_checkin(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_checkin": True, + } + ) + # create reservation with checkin today + self.env["pms.reservation"].create( + { + "checkin": datetime.today(), + "checkout": datetime.today() + timedelta(days=3), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertTrue(housekeeping_task, "Housekeeping task should be created") + + @freeze_time("2000-01-04") + def test_no_create_checkin_task_when_it_shouldnt_when_no_checkin(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_checkin": True, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertFalse(housekeeping_task, "Housekeeping task shouldn't be created") + + @freeze_time("2000-01-04") + def test_create_checkout_task_when_it_should_when_checkout(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_checkout": True, + } + ) + # create reservation with checkout today + self.env["pms.reservation"].create( + { + "checkin": datetime.today() + timedelta(days=-3), + "checkout": datetime.today(), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertTrue(housekeeping_task, "Housekeeping task should be created") + + @freeze_time("2000-01-04") + def test_no_create_checkout_task_when_it_shouldnt_when_no_checkout(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_checkout": True, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task is not created + self.assertFalse(housekeeping_task, "Housekeeping task shouldn't be created") + + @freeze_time("2000-01-04") + def test_create_task_type_childs(self): + # ARRANGE + # create task type + parent_task_type = self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type Parent", + "is_checkout": True, + } + ) + child_task_type = self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type Child", + "is_checkout": True, + "parent_id": parent_task_type.id, + } + ) + # create reservation with checkout today + self.env["pms.reservation"].create( + { + "checkin": datetime.today() + timedelta(days=-3), + "checkout": datetime.today(), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id), ("task_type_id", "=", child_task_type.id)] + ) + # Verify that the housekeeping task is not created + self.assertTrue(housekeeping_task, "Child housekeeping task should be created") + + def test_no_create_task_type_childs(self): + # ARRANGE + # create task type + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type Parent", + "is_checkout": True, + } + ) + + # create reservation with checkout today + self.env["pms.reservation"].create( + { + "checkin": datetime.today() + timedelta(days=-3), + "checkout": datetime.today(), + "room_type_id": self.room_type1.id, + "partner_id": self.partner1.id, + "pms_property_id": self.pms_property1.id, + "pricelist_id": self.pricelist1.id, + "sale_channel_origin_id": self.sale_channel1.id, + } + ) + + # ACT + # call method to create task + self.env["pms.housekeeping.task"].generate_tasks(self.pms_property1) + + # ASSERT + # search for the task + housekeeping_task = self.env["pms.housekeeping.task"].search( + [("room_id", "=", self.room1.id)] + ) + # Verify that the housekeeping task childs is not created + self.assertFalse( + housekeeping_task.child_ids, "Child housekeeping task shouldn´t be created" + ) + + def test_days_after_clean_overnight_constraint(self): + # ARRANGE, ACT & ASSERT + # create task type and verify that the constraint is raised + with self.assertRaises( + ValidationError, msg="Days After Clean Overnight should be greater than 0" + ): + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_overnight": True, + "days_after_clean_overnight": 0, + } + ) + + def test_days_after_clean_empty_constraint(self): + # ARRANGE, ACT & ASSERT + # create task type and verify that the constraint is raised + with self.assertRaises( + ValidationError, msg="Days After Clean Overnight should be greater than 0" + ): + self.env["pms.housekeeping.task.type"].create( + { + "name": "Task Type 1", + "is_empty": True, + "days_after_clean_empty": 0, + } + ) diff --git a/pms_housekeeping/tests/test_pms_hr_employee.py b/pms_housekeeping/tests/test_pms_hr_employee.py new file mode 100644 index 000000000..7440779aa --- /dev/null +++ b/pms_housekeeping/tests/test_pms_hr_employee.py @@ -0,0 +1,92 @@ +from odoo.exceptions import ValidationError +from .common import TestPms + + +class TestPmsHrEmployee(TestPms): + def setUp(self): + super().setUp() + + def test_employee_pre_assigned_room_inconsistent(self): + # ARRANGE + + self.pms_property2 = self.env["pms.property"].create( + { + "name": "Property 2", + "company_id": self.company1.id, + "default_pricelist_id": self.pricelist1.id, + } + ) + self.room2 = self.env["pms.room"].create( + { + "name": "Room 202", + "pms_property_id": self.pms_property2.id, + "room_type_id": self.room_type1.id, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The room should belong to the employee's property." + ): + self.hr_employee = self.env["hr.employee"].create( + { + "name": "Test Employee", + "company_id": self.company1.id, + "job_id": self.env.ref("pms_housekeeping.housekeeping_job_id").id, + "property_ids": [(6, 0, [self.pms_property1.id])], + "pre_assigned_room_ids": [(6, 0, [self.room2.id])], + } + ) + + def test_employee_pre_assigned_room_consistent_with_property(self): + # ARRANGE + self.hr_employee = self.env["hr.employee"].create( + { + "name": "Test Employee", + "company_id": self.company1.id, + "job_id": self.env.ref("pms_housekeeping.housekeeping_job_id").id, + "property_ids": [(6, 0, [self.pms_property1.id])], + } + ) + + # ACT + self.hr_employee.pre_assigned_room_ids = [(6, 0, [self.room1.id])] + + # ASSERT + self.assertTrue( + self.hr_employee.pre_assigned_room_ids, "Pre assigned room is not consistent with property" + ) + + def test_employee_pre_assigned_room_consistent_without_properties(self): + # ARRANGE + self.hr_employee = self.env["hr.employee"].create( + { + "name": "Test Employee", + "company_id": self.company1.id, + "job_id": self.env.ref("pms_housekeeping.housekeeping_job_id").id, + } + ) + + # ACT + self.hr_employee.pre_assigned_room_ids = [(6, 0, [self.room1.id])] + + # ASSERT + self.assertTrue( + self.hr_employee.pre_assigned_room_ids, "Pre assigned room is not consistent without properties" + ) + + def test_not_pre_assigned_room_no_housekeeper_employee(self): + # ARRANGE + self.hr_employee = self.env["hr.employee"].create( + { + "name": "Test Employee", + "company_id": self.company1.id, + "job_id": self.env.ref("hr.job_trainee").id, + } + ) + + # ACT & ASSERT + with self.assertRaises( + ValidationError, msg="The job position should be Housekeeper." + ): + self.hr_employee.pre_assigned_room_ids = [(6, 0, [self.room1.id])] diff --git a/pms_housekeeping/views/hr_employee_views.xml b/pms_housekeeping/views/hr_employee_views.xml index d3159d825..ad0c7e79f 100644 --- a/pms_housekeeping/views/hr_employee_views.xml +++ b/pms_housekeeping/views/hr_employee_views.xml @@ -9,8 +9,10 @@ name="pre_assigned_room_ids" widget="many2many_tags" attrs="{'invisible': [('job_name', '!=', 'Housekeeper')]}" + domain="[('id', 'in', allowed_pre_assigned_room_ids)]" /> + diff --git a/pms_housekeeping/views/pms_housekeeping_task_type_views.xml b/pms_housekeeping/views/pms_housekeeping_task_type_views.xml index 497b938b5..97148c78f 100644 --- a/pms_housekeeping/views/pms_housekeeping_task_type_views.xml +++ b/pms_housekeeping/views/pms_housekeeping_task_type_views.xml @@ -54,7 +54,13 @@ - + + +