From e8bb9fd0d058ae5d9ea1193e9cb7077ddc90bf2d Mon Sep 17 00:00:00 2001 From: miguelpadin Date: Thu, 3 Jun 2021 00:21:27 +0200 Subject: [PATCH] [IMP] pms: manage pricelist of board services --- pms/models/pms_board_service_room_type.py | 9 + pms/models/pms_service.py | 4 +- pms/models/product_pricelist.py | 12 +- pms/models/product_pricelist_item.py | 81 ++- pms/tests/test_pms_pricelist.py | 589 ++++++++++++++++ pms/tests/test_pms_wizard_massive_changes.py | 405 ++++++++++- pms/views/product_pricelist_item_views.xml | 29 +- pms/views/product_pricelist_views.xml | 12 + pms/wizards/wizard_massive_changes.py | 674 +++++++++++++++---- pms/wizards/wizard_massive_changes.xml | 40 +- 10 files changed, 1688 insertions(+), 167 deletions(-) diff --git a/pms/models/pms_board_service_room_type.py b/pms/models/pms_board_service_room_type.py index 881042cb5..8beefc95e 100644 --- a/pms/models/pms_board_service_room_type.py +++ b/pms/models/pms_board_service_room_type.py @@ -109,6 +109,15 @@ class PmsBoardServiceRoomType(models.Model): total += service.amount record.update({"amount": total}) + def name_get(self): + res = [] + for record in self: + name = "{} - {}".format( + record.pms_board_service_id.name, record.pms_room_type_id.name + ) + res.append((record.id, name)) + return res + @api.constrains("by_default") def constrains_duplicated_board_defaul(self): for record in self: diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py index 61b6835e7..6fbd0c2de 100644 --- a/pms/models/pms_service.py +++ b/pms/models/pms_service.py @@ -544,10 +544,10 @@ class PmsService(models.Model): board_service=board_room_type.id if board_room_type else False, uom=self.product_id.uom_id.id, fiscal_position=False, - property=self.pms_property_id.id, + property=self.reservation_id.pms_property_id.id, ) if date: - product_context["date_overnight"] = date + product_context["consumption_date"] = date if reservation and self.is_board_service: product_context["board_service"] = reservation.board_service_room_id.id product = self.product_id.with_context(product_context) diff --git a/pms/models/product_pricelist.py b/pms/models/product_pricelist.py index b76571c4c..cff0c5a07 100644 --- a/pms/models/product_pricelist.py +++ b/pms/models/product_pricelist.py @@ -69,6 +69,7 @@ class ProductPricelist(models.Model): def _compute_price_rule_get_items( self, products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids ): + board_service = True if self._context.get("board_service") else False if ( "property" in self._context and self._context["property"] @@ -84,8 +85,6 @@ class ProductPricelist(models.Model): ON item.pricelist_id = cab.product_pricelist_id LEFT JOIN product_pricelist_item_pms_property_rel lin ON item.id = lin.product_pricelist_item_id - LEFT JOIN board_service_pricelist_item_rel board - ON item.id = board.pricelist_item_id WHERE (lin.pms_property_id = %s OR lin.pms_property_id IS NULL) AND (cab.pms_property_id = %s OR cab.pms_property_id IS NULL) AND (item.product_tmpl_id IS NULL @@ -119,8 +118,6 @@ class ProductPricelist(models.Model): prod_tmpl_ids, prod_ids, categ_ids, - # on_board_service_bool, - # board_service_id, self.id, date, date, @@ -131,6 +128,13 @@ class ProductPricelist(models.Model): item_ids = [x[0] for x in self.env.cr.fetchall()] items = self.env["product.pricelist.item"].browse(item_ids) + if board_service: + items = items.filtered( + lambda x: x.board_service_room_type_id.id + == self._context.get("board_service") + ) + else: + items = items.filtered(lambda x: not x.board_service_room_type_id.id) else: items = super(ProductPricelist, self)._compute_price_rule_get_items( products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids diff --git a/pms/models/product_pricelist_item.py b/pms/models/product_pricelist_item.py index 267f145a1..454e14d28 100644 --- a/pms/models/product_pricelist_item.py +++ b/pms/models/product_pricelist_item.py @@ -1,6 +1,6 @@ # Copyright 2017 Alexandre Díaz, Pablo Quesada, Darío Lodeiros # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). -from odoo import fields, models +from odoo import api, fields, models class ProductPricelistItem(models.Model): @@ -26,18 +26,10 @@ class ProductPricelistItem(models.Model): string="End Date Overnight", help="End date to apply daily pricelist items", ) - on_board_service = fields.Boolean( - string="On Board Service", - help="Those included in Board Services", - ) - board_service_room_type_ids = fields.Many2many( - string="Board Services", - help="""Specify a Board services on Room Types.""", + board_service_room_type_id = fields.Many2one( + string="Board Service", + help="Specify a Board services on Room Types.", comodel_name="pms.board.service.room.type", - relation="board_service_pricelist_item_rel", - column1="pricelist_item_id", - column2="board_service_id", - ondelete="cascade", check_pms_properties=True, ) pricelist_id = fields.Many2one( @@ -55,3 +47,68 @@ class ProductPricelistItem(models.Model): help="Product template associated with the item", check_pms_properties=True, ) + allowed_board_service_product_ids = fields.Many2many( + string="Allowed board service products", + comodel_name="product.product", + store=True, + readonly=False, + compute="_compute_allowed_board_service_product_ids", + ) + + allowed_board_service_room_type_ids = fields.Many2many( + string="Allowed board service room types", + comodel_name="pms.board.service.room.type", + store=True, + readonly=False, + compute="_compute_allowed_board_service_room_type_ids", + ) + + @api.depends("board_service_room_type_id") + def _compute_allowed_board_service_product_ids(self): + for record in self: + domain = [] + if record.board_service_room_type_id: + domain.append( + ( + "id", + "in", + record.board_service_room_type_id.board_service_line_ids.mapped( + "product_id" + ).ids, + ) + ) + allowed_board_service_product_ids = self.env["product.product"].search( + domain + ) + record.allowed_board_service_product_ids = allowed_board_service_product_ids + + @api.depends("product_id") + def _compute_allowed_board_service_room_type_ids(self): + for record in self: + allowed_board_service_room_type_ids = [] + all_board_service_room_type_ids = self.env[ + "pms.board.service.room.type" + ].search([]) + if record.product_id: + for board_service_room_type_id in all_board_service_room_type_ids: + if ( + record.product_id + in board_service_room_type_id.board_service_line_ids.mapped( + "product_id" + ) + ): + allowed_board_service_room_type_ids.append( + board_service_room_type_id.id + ) + else: + allowed_board_service_room_type_ids = ( + all_board_service_room_type_ids.ids + ) + domain = [] + if allowed_board_service_room_type_ids: + domain.append(("id", "in", allowed_board_service_room_type_ids)) + record.allowed_board_service_room_type_ids = ( + self.env["pms.board.service.room.type"].search(domain) + if domain + else False + ) diff --git a/pms/tests/test_pms_pricelist.py b/pms/tests/test_pms_pricelist.py index 23fcd0218..853733852 100644 --- a/pms/tests/test_pms_pricelist.py +++ b/pms/tests/test_pms_pricelist.py @@ -2,6 +2,7 @@ import datetime from freezegun import freeze_time +from odoo import fields from odoo.exceptions import UserError, ValidationError from odoo.tests import common, tagged @@ -81,12 +82,51 @@ class TestPmsPricelist(common.SavepointCase): } ) + # pms.room + self.room1 = self.env["pms.room"].create( + { + "pms_property_id": self.property1.id, + "name": "Single 101", + "room_type_id": self.room_type.id, + "capacity": 2, + } + ) + self.pricelist = self.env["product.pricelist"].create( { "name": "pricelist_1", "pms_property_ids": [self.property1.id, self.property2.id], } ) + # product.product 1 + self.test_service_breakfast = self.env["product.product"].create( + {"name": "Test Breakfast"} + ) + + # pms.board.service + self.test_board_service_only_breakfast = self.env["pms.board.service"].create( + { + "name": "Test Only Breakfast", + "default_code": "CB1", + } + ) + # pms.board.service.line + self.board_service_line_single_1 = self.env["pms.board.service.line"].create( + { + "product_id": self.test_service_breakfast.id, + "pms_board_service_id": self.test_board_service_only_breakfast.id, + } + ) + + # pms.board.service.room.type + self.test_board_service_single = self.env["pms.board.service.room.type"].create( + { + "pms_room_type_id": self.room_type.id, + "pms_board_service_id": self.test_board_service_only_breakfast.id, + } + ) + + self.partner1 = self.env["res.partner"].create({"name": "Carles"}) def test_advanced_pricelist_exists(self): @@ -200,6 +240,555 @@ class TestPmsPricelist(common.SavepointCase): } ) + # board services pricelist items + def test_board_service_pricelist_item_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on board services at SALE date. + # The reservation created take into account the board service + # pricelist item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "board_service_room_type_id": self.test_board_service_single.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "board_service_room_id": self.test_board_service_single.id, + } + ) + # ASSERT + self.assertEqual( + reservation_created.service_ids.price_subtotal, + expected_price, + "The reservation created should take into account the board service" + " pricelist item created previously according to the SALE date.", + ) + + def test_board_service_pricelist_item_not_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on board services at SALE date. + # The reservation created DONT take into account the board service pricelist + # item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "board_service_room_type_id": self.test_board_service_single.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "board_service_room_id": self.test_board_service_single.id, + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.service_ids.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the board service pricelist" + " item created previously according to the SALE date.", + ) + + def test_board_service_pricelist_item_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on board services + # at CONSUMPTION date. + # The reservation created take into account the board service + # pricelist item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start_overnight": date_from, + "date_end_overnight": date_to, + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "board_service_room_type_id": self.test_board_service_single.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now() + datetime.timedelta(days=1), + "checkout": datetime.datetime.now() + datetime.timedelta(days=2), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "board_service_room_id": self.test_board_service_single.id, + } + ) + # ASSERT + self.assertEqual( + reservation_created.service_ids.price_subtotal, + expected_price, + "The reservation created should take into account the board service" + " pricelist item created previously according to the CONSUMPTION date.", + ) + + def test_board_service_pricelist_item_not_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on board services + # at CONSUMPTION date. + # The reservation created DONT take into account the board service + # pricelist item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=2) + date_to = fields.date.today() + datetime.timedelta(days=2) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "board_service_room_type_id": self.test_board_service_single.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "board_service_room_id": self.test_board_service_single.id, + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.service_ids.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the board service" + " pricelist item created previously according to the CONSUMPTION date.", + ) + + # room types pricelist items + def test_room_type_pricelist_item_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on room types + # at SALE date. + # The reservation created take into account the room type + # pricelist item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + } + ) + # ASSERT + self.assertEqual( + reservation_created.price_subtotal, + expected_price, + "The reservation created should take into account the room type" + " pricelist item created previously according to the SALE date.", + ) + + def test_room_type_pricelist_item_not_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on room types + # at SALE date. + # The reservation created DONT take into account the room type + # pricelist item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the room type" + " pricelist item created previously according to the SALE date.", + ) + + def test_room_type_pricelist_item_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on room types + # at CONSUMPTION date. + # The reservation created take into account the room type + # pricelist item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start_overnight": date_from, + "date_end_overnight": date_to, + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now() + datetime.timedelta(days=1), + "checkout": datetime.datetime.now() + datetime.timedelta(days=2), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + } + ) + # ASSERT + self.assertEqual( + reservation_created.price_subtotal, + expected_price, + "The reservation created should take into account the room type" + " pricelist item created previously according to the CONSUMPTION date.", + ) + + def test_room_type_pricelist_item_not_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on room types + # at CONSUMPTION date. + # The reservation created DONT take into account the room type + # pricelist item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=2) + date_to = fields.date.today() + datetime.timedelta(days=2) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the room type" + " pricelist item created previously according to the CONSUMPTION date.", + ) + + # services pricelist items + def test_service_pricelist_item_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on services at SALE date. + # The reservation created take into account the service + # pricelist item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "service_ids": [(0, 0, {"product_id": self.test_service_breakfast.id})], + } + ) + # ASSERT + self.assertEqual( + reservation_created.service_ids.price_subtotal, + expected_price, + "The reservation created should take into account the service" + " pricelist item created previously according to the SALE date.", + ) + + def test_service_pricelist_item_not_apply_sale_dates(self): + # TEST CASE + # Pricelist item is created to apply on services at SALE date. + # The reservation created DONT take into account the service pricelist + # item created previously according to the SALE date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "service_ids": [(0, 0, {"product_id": self.test_service_breakfast.id})], + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.service_ids.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the service pricelist" + " item created previously according to the SALE date.", + ) + + def test_service_pricelist_item_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on services at CONSUMPTION date. + # The reservation created take into account the service + # pricelist item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=1) + date_to = fields.date.today() + datetime.timedelta(days=1) + expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start_overnight": date_from, + "date_end_overnight": date_to, + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "fixed_price": expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now() + datetime.timedelta(days=1), + "checkout": datetime.datetime.now() + datetime.timedelta(days=2), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "service_ids": [(0, 0, {"product_id": self.test_service_breakfast.id})], + } + ) + # ASSERT + self.assertEqual( + reservation_created.service_ids.price_subtotal, + expected_price, + "The reservation created should take into account the service" + " pricelist item created previously according to the CONSUMPTION date.", + ) + + def test_service_pricelist_item_not_apply_overnight_dates(self): + # TEST CASE + # Pricelist item is created to apply on services at CONSUMPTION date. + # The reservation created DONT take into account the service pricelist + # item created previously according to the CONSUMPTION date. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + datetime.timedelta(days=2) + date_to = fields.date.today() + datetime.timedelta(days=2) + not_expected_price = 1000.0 + vals = { + "pricelist_id": self.pricelist.id, + "date_start": datetime.datetime.combine( + date_from, datetime.datetime.min.time() + ), + "date_end": datetime.datetime.combine( + date_to, datetime.datetime.max.time() + ), + "compute_price": "fixed", + "applied_on": "0_product_variant", + "product_id": self.test_service_breakfast.id, + "fixed_price": not_expected_price, + "pms_property_ids": [self.property1.id], + } + self.env["product.pricelist.item"].create(vals) + # ACT + reservation_created = self.env["pms.reservation"].create( + { + "partner_id": self.partner1.id, + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + datetime.timedelta(days=1), + "preferred_room_id": self.room1.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + "service_ids": [(0, 0, {"product_id": self.test_service_breakfast.id})], + } + ) + # ASSERT + self.assertNotEqual( + reservation_created.service_ids.price_subtotal, + not_expected_price, + "The reservation created shouldnt take into account the service pricelist " + "item created previously according to the CONSUMPTION date.", + ) + @freeze_time("2000-01-01") def test_pricelist_daily_failed(self): self.create_common_scenario() diff --git a/pms/tests/test_pms_wizard_massive_changes.py b/pms/tests/test_pms_wizard_massive_changes.py index 6cab0afca..c07afacb5 100644 --- a/pms/tests/test_pms_wizard_massive_changes.py +++ b/pms/tests/test_pms_wizard_massive_changes.py @@ -61,7 +61,6 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): self.test_room_type_class = self.env["pms.room.type.class"].create( {"name": "Room", "default_code": "ROOM"} ) - # pms.room.type self.test_room_type_single = self.env["pms.room.type"].create( { @@ -71,7 +70,6 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): "class_id": self.test_room_type_class.id, } ) - # pms.room.type self.test_room_type_double = self.env["pms.room.type"].create( { "pms_property_ids": [self.test_property.id], @@ -81,6 +79,62 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): } ) + # pms.board.service + self.test_board_service_only_breakfast = self.env["pms.board.service"].create( + { + "name": "Test Only Breakfast", + "default_code": "CB1", + } + ) + self.test_board_service_half_board = self.env["pms.board.service"].create( + { + "name": "Test Half Board", + "default_code": "CB2", + } + ) + # product.product 1 + self.test_service_breakfast = self.env["product.product"].create( + {"name": "Test Breakfast"} + ) + self.test_service_dinner = self.env["product.product"].create( + {"name": "Test Dinner"} + ) + self.test_service_spa = self.env["product.product"].create({"name": "Test Spa"}) + # pms.board.service.room.type + self.test_board_service_single = self.env["pms.board.service.room.type"].create( + { + "pms_room_type_id": self.test_room_type_single.id, + "pms_board_service_id": self.test_board_service_only_breakfast.id, + } + ) + self.test_board_service_double = self.env["pms.board.service.room.type"].create( + { + "pms_room_type_id": self.test_room_type_double.id, + "pms_board_service_id": self.test_board_service_half_board.id, + } + ) + # pms.board.service.line + self.board_service_line_single_1 = self.env["pms.board.service.line"].create( + { + "product_id": self.test_service_breakfast.id, + "pms_board_service_id": self.test_board_service_only_breakfast.id, + } + ) + self.board_service_line_double_1 = self.env["pms.board.service.line"].create( + { + "product_id": self.test_service_breakfast.id, + "pms_board_service_id": self.test_board_service_half_board.id, + } + ) + self.board_service_line_double_2 = self.board_service_line = self.env[ + "pms.board.service.line" + ].create( + { + "product_id": self.test_service_dinner.id, + "pms_board_service_id": self.test_board_service_half_board.id, + } + ) + # MASSIVE CHANGE WIZARD TESTS ON AVAILABILITY RULES @freeze_time("1980-12-01") @@ -498,7 +552,7 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): } ) - wizard = self.env["pms.massive.changes.wizard"].create( + wizard_result = self.env["pms.massive.changes.wizard"].create( { "massive_changes_on": "pricelist", "availability_plan_ids": [ @@ -522,7 +576,7 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): self.test_availability_plan.id, self.test_availability_plan_2.id, ] - for avail_plan_id in wizard["availability_plan_ids"].ids: + for avail_plan_id in wizard_result["availability_plan_ids"].ids: with self.subTest(k=avail_plan_id): self.assertIn( avail_plan_id, @@ -541,7 +595,7 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): } ) - wizard = self.env["pms.massive.changes.wizard"].create( + wizard_result = self.env["pms.massive.changes.wizard"].create( { "massive_changes_on": "pricelist", "pricelist_ids": [ @@ -555,18 +609,18 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): ) pricelist_ids = [self.test_pricelist.id, self.test_pricelist_2.id] - for pricelist_id in wizard["pricelist_ids"].ids: + for pricelist_id in wizard_result["pricelist_ids"].ids: with self.subTest(k=pricelist_id): self.assertIn( pricelist_id, pricelist_ids, "Pricelist has not been write " ) @freeze_time("2025-02-01") - def test_several_room_types(self): + def test_several_room_types_pricelist(self): self.create_common_scenario() date_from = fields.date.today() date_to = date_from + datetime.timedelta(days=6) - wizard = self.env["pms.massive.changes.wizard"].create( + wizard_result = self.env["pms.massive.changes.wizard"].create( { "massive_changes_on": "pricelist", "pricelist_ids": [(6, 0, [self.test_pricelist.id])], @@ -584,8 +638,341 @@ class TestPmsWizardMassiveChanges(common.SavepointCase): ) room_type_ids = [self.test_room_type_double.id, self.test_room_type_double.id] - for room_type_id in wizard["room_type_ids"].ids: + for room_type_id in wizard_result["room_type_ids"].ids: with self.subTest(k=room_type_id): self.assertIn( room_type_id, room_type_ids, "Room type has not been write " ) + + @freeze_time("2025-02-01") + def test_several_room_types_availability_plan(self): + self.create_common_scenario() + date_from = fields.date.today() + date_to = date_from + datetime.timedelta(days=6) + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "availability_plan", + "availability_plan_ids": [(6, 0, [self.test_availability_plan.id])], + "room_type_ids": [ + ( + 6, + 0, + [self.test_room_type_double.id, self.test_room_type_double.id], + ) + ], + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + } + ) + + room_type_ids = [self.test_room_type_double.id, self.test_room_type_double.id] + for room_type_id in wizard_result["room_type_ids"].ids: + with self.subTest(k=room_type_id): + self.assertIn( + room_type_id, room_type_ids, "Room type has not been write " + ) + + @freeze_time("2025-02-01") + def test_one_board_service_room_type_no_board_service(self): + # TEST CASE + # Call to wizard with one board service room type and no + # board service. + # The wizard must create as many pricelist items as there + # are services on the given board service. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "board_services", + "board_service_room_type_ids": [ + ( + 6, + 0, + [self.test_board_service_single.id], + ) + ], + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "consumption_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "=", self.board_service_line_single_1.product_id.id), + ] + ) + # ASSERT + self.assertIn( + self.test_service_breakfast, + items_created.mapped("product_id"), + "The wizard must create as many pricelist items as there " + "are services on the given board service.", + ) + + @freeze_time("2025-02-01") + def test_one_board_service_room_type_with_board_service(self): + # TEST CASE + # Call to wizard with one board service room type and + # board service. + # The wizard must create one pricelist items with + # the board service given. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "board_services", + "board_service_room_type_ids": [ + ( + 6, + 0, + [self.test_board_service_single.id], + ) + ], + "board_service": self.board_service_line_single_1.product_id.id, + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "consumption_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "=", self.board_service_line_single_1.product_id.id), + ] + ) + # ASSERT + self.assertIn( + self.test_service_breakfast, + items_created.mapped("product_id"), + "The wizard must create one pricelist items with " + " the board service given.", + ) + + @freeze_time("2025-02-01") + def test_several_board_service_room_type_no_board_service(self): + # TEST CASE + # Call to wizard with several board service room type and no + # board service. + # The wizard must create as many pricelist items as there + # are services on the given board services. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + product_ids_expected = ( + self.test_board_service_double.pms_board_service_id.board_service_line_ids.mapped( + "product_id" + ).ids + + self.test_board_service_single.pms_board_service_id.board_service_line_ids.mapped( + "product_id" + ).ids + ) + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "board_services", + "board_service_room_type_ids": [ + ( + 6, + 0, + [ + self.test_board_service_single.id, + self.test_board_service_double.id, + ], + ) + ], + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "consumption_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "in", product_ids_expected), + ] + ) + # ASSERT + self.assertEqual( + set(product_ids_expected), + set(items_created.mapped("product_id").ids), + "The wizard should create as many pricelist items as there" + " are services on the given board services.", + ) + + @freeze_time("2025-02-01") + def test_several_board_service_room_type_with_board_service(self): + # TEST CASE + # Call to wizard with several board service room types and + # board service. + # The wizard must create as many pricelist items as there + # are services on the given board services. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + board_service_id_double = self.test_board_service_double.pms_board_service_id + board_service_id_single = self.test_board_service_single.pms_board_service_id + product_ids_expected = list( + set(board_service_id_double.board_service_line_ids.mapped("product_id").ids) + & set( + board_service_id_single.board_service_line_ids.mapped("product_id").ids + ) + ) + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "board_services", + "board_service_room_type_ids": [ + ( + 6, + 0, + [ + self.test_board_service_single.id, + self.test_board_service_double.id, + ], + ) + ], + "board_service": self.test_service_breakfast.id, + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "consumption_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "in", product_ids_expected), + ] + ) + # ASSERT + self.assertEqual( + set(product_ids_expected), + set(items_created.mapped("product_id").ids), + "The wizard should create as many pricelist items as there" + " are services on the given board services.", + ) + + @freeze_time("2025-02-01") + def test_service(self): + # TEST CASE + # Call to wizard with one service (product_id) + # The wizard must create one pricelist items with + # the given service (product_id). + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "service", + "service": self.test_service_spa.id, + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "consumption_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "=", self.test_service_spa.id), + ] + ) + # ASSERT + self.assertIn( + self.test_service_spa.id, + items_created.mapped("product_id").ids, + "The wizard should create one pricelist items with" + " the given service (product_id).", + ) + + @freeze_time("2025-02-01") + def test_sale_dates(self): + # TEST CASE + # Call to wizard with one service (product_id) + # and dates of SALE + # The wizard must create one pricelist items with + # the given service (product_id) and dates of SALE. + + # ARRANGE + self.create_common_scenario() + date_from = fields.date.today() + date_to = fields.date.today() + wizard_result = self.env["pms.massive.changes.wizard"].create( + { + "massive_changes_on": "pricelist", + "pricelist_ids": [(6, 0, [self.test_pricelist.id])], + "apply_pricelists_on": "service", + "service": self.test_service_spa.id, + "pms_property_ids": [self.test_property.id], + "start_date": date_from, + "end_date": date_to, + "date_types": "sale_dates", + } + ) + # ACT + wizard_result.apply_massive_changes() + + items_created = self.env["product.pricelist.item"].search( + [ + ("pricelist_id", "=", self.test_pricelist.id), + ("pms_property_ids", "=", self.test_property.id), + ("product_id", "=", self.test_service_spa.id), + ] + ) + + expected_dates = [ + datetime.datetime.combine(date_from, datetime.datetime.min.time()), + datetime.datetime.combine(date_to, datetime.datetime.max.time()), + ] + # ASSERT + self.assertEqual( + expected_dates, + items_created.mapped("date_start") + items_created.mapped("date_end"), + "The wizard should create one pricelist items with" + " the given service (product_id) and dates of sale.", + ) diff --git a/pms/views/product_pricelist_item_views.xml b/pms/views/product_pricelist_item_views.xml index 0226574f2..6f65b1e20 100644 --- a/pms/views/product_pricelist_item_views.xml +++ b/pms/views/product_pricelist_item_views.xml @@ -5,13 +5,15 @@ - + + + + + + [('id', 'in',allowed_board_service_product_ids)] + product.pricelist.item - - - - - + + + + + + + + + diff --git a/pms/views/product_pricelist_views.xml b/pms/views/product_pricelist_views.xml index 3883064bd..3d3f19aa2 100644 --- a/pms/views/product_pricelist_views.xml +++ b/pms/views/product_pricelist_views.xml @@ -29,6 +29,18 @@ + + + + + +