diff --git a/pms/models/__init__.py b/pms/models/__init__.py index cad3603fa..8bab3ddac 100644 --- a/pms/models/__init__.py +++ b/pms/models/__init__.py @@ -21,6 +21,7 @@ from . import pms_room_type from . import pms_service from . import account_move from . import product_template +from . import product_product from . import res_company from . import account_payment from . import pms_room_type_availability_plan diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 007e11a2d..d3e8dee5b 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -468,8 +468,18 @@ class PmsReservation(models.Model): readonly=False, store=True, ) + date_order = fields.Date( + compute="_compute_pms_creation_date", + store=True, + readonly=False, + ) # Compute and Search methods + + def _compute_pms_creation_date(self): + for record in self: + record.date_order = datetime.datetime.today() + @api.depends("checkin", "checkout", "room_type_id") def _compute_name(self): for reservation in self: diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py index 2dcb772af..5a95fb349 100644 --- a/pms/models/pms_reservation_line.py +++ b/pms/models/pms_reservation_line.py @@ -275,7 +275,8 @@ class PmsReservationLine(models.Model): lang=partner.lang, partner=partner.id, quantity=1, - date=line.date, + date=line.reservation_id.date_order, + date_overnight=line.date, pricelist=reservation.pricelist_id.id, uom=product.uom_id.id, property=reservation.pms_property_id.id, diff --git a/pms/models/product_pricelist.py b/pms/models/product_pricelist.py index 3fbbf7799..213f77d23 100644 --- a/pms/models/product_pricelist.py +++ b/pms/models/product_pricelist.py @@ -65,27 +65,67 @@ class ProductPricelist(models.Model): def _compute_price_rule_get_items( self, products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids ): - items = super(ProductPricelist, self)._compute_price_rule_get_items( - products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids - ) - # Discard the rules with defined properties other than the context, - # and we reorder the rules to return the most concrete property rule first - if "property" in self._context: - items_filtered = items.filtered( - lambda i: not i.pms_property_ids - or self._context["property"] in i.pms_property_ids.ids + + if "property" in self._context and self._context["property"]: + self.env["product.pricelist.item"].flush( + ["price", "currency_id", "company_id"] ) - reverse_id = items_filtered.sorted(id, reverse=True) - return items_filtered.sorted( - key=lambda s: ( - s.applied_on, - True if (not s.date_end or not s.date_start) else False, - True - if (not s.date_end or not s.date_start) - else (s.date_end - s.date_start).days, - ((not s.pms_property_ids, s), len(s.pms_property_ids)), - reverse_id, - ) + self.env.cr.execute( + """ + SELECT item.id + FROM product_pricelist_item item + LEFT JOIN product_category categ + ON item.categ_id = categ.id + LEFT JOIN pms_property_product_pricelist_rel cab + ON item.pricelist_id = cab.product_pricelist_id + LEFT JOIN pms_property_product_pricelist_item_rel lin + ON item.id = lin.product_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 + OR item.product_tmpl_id = ANY(%s)) + AND (item.product_id IS NULL OR item.product_id = ANY(%s)) + AND (item.categ_id IS NULL OR item.categ_id = ANY(%s)) + AND (item.pricelist_id = %s) + AND (item.date_start IS NULL OR item.date_start <=%s) + AND (item.date_end IS NULL OR item.date_end >=%s) + AND (item.date_start_overnight IS NULL + OR item.date_start_overnight <=%s) + AND (item.date_end_overnight IS NULL + OR item.date_end_overnight >=%s) + GROUP BY item.id + ORDER BY item.applied_on, + /* REVIEW: priotrity date sale / date overnight */ + item.date_end - item.date_start ASC, + item.date_end_overnight - item.date_start_overnight ASC, + NULLIF((SELECT COUNT(1) + FROM pms_property_product_pricelist_item_rel l + WHERE item.id = l.product_pricelist_item_id) + + (SELECT COUNT(1) + FROM pms_property_product_pricelist_rel c + WHERE item.pricelist_id = c.product_pricelist_id),0) + NULLS LAST, + item.id DESC; + """, + ( + self._context["property"], + self._context["property"], + prod_tmpl_ids, + prod_ids, + categ_ids, + self.id, + date, + date, + self._context["date_overnight"], + self._context["date_overnight"], + ), + ) + + item_ids = [x[0] for x in self.env.cr.fetchall()] + items = self.env["product.pricelist.item"].browse(item_ids) + else: + items = super(ProductPricelist, self)._compute_price_rule_get_items( + products_qty_partner, date, uom_id, prod_tmpl_ids, prod_ids, categ_ids ) return items diff --git a/pms/models/product_pricelist_item.py b/pms/models/product_pricelist_item.py index 0fcb9099c..47249481c 100644 --- a/pms/models/product_pricelist_item.py +++ b/pms/models/product_pricelist_item.py @@ -9,3 +9,11 @@ class ProductPricelistItem(models.Model): pms_property_ids = fields.Many2many( "pms.property", string="Properties", required=False, ondelete="restrict" ) + date_start_overnight = fields.Date( + string="Start Date Overnight", + help="Start date to apply daily pricelist items", + ) + date_end_overnight = fields.Date( + string="End Date Overnight", + help="End date to apply daily pricelist items", + ) diff --git a/pms/models/product_product.py b/pms/models/product_product.py new file mode 100644 index 000000000..c87902e0b --- /dev/null +++ b/pms/models/product_product.py @@ -0,0 +1,17 @@ +from odoo import api, models + + +class ProductProduct(models.Model): + _inherit = "product.product" + + @api.depends_context( + "pricelist", + "partner", + "quantity", + "uom", + "date", + "date_overnight", + "no_variant_attributes_price_extra", + ) + def _compute_product_price(self): + super(ProductProduct, self)._compute_product_price() diff --git a/pms/tests/test_pms_pricelist_priority.py b/pms/tests/test_pms_pricelist_priority.py index df1b227a6..81ada1b5c 100644 --- a/pms/tests/test_pms_pricelist_priority.py +++ b/pms/tests/test_pms_pricelist_priority.py @@ -2,10 +2,9 @@ import datetime from freezegun import freeze_time -from odoo.tests import common, tagged +from odoo.tests import common -@tagged("standard", "nice") class TestPmsPricelistRules(common.TransactionCase): def create_common_scenario(self): self.product_template = self.env["product.template"].create( @@ -76,482 +75,9 @@ class TestPmsPricelistRules(common.TransactionCase): } ) - @freeze_time("2000-01-01 00:00:00") - def test_items_sort_applied_on(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "categ_id": self.product_category.id, - "applied_on": "2_product_category", - "date_start": datetime.datetime.now(), - "date_end": datetime.datetime.now() + datetime.timedelta(days=3), - "pms_property_ids": [self.property1.id], - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.now(), - "date_end": datetime.datetime.now() + datetime.timedelta(days=3), - "pms_property_ids": [self.property1.id], - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item2.fixed_price * n_days - - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-16 00:00:00") - def test_items_sort_date(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() + datetime.timedelta(days=3), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=10, hours=23, minutes=59), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(days=3, hours=12, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=5, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item1.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-05 00:00:00") - def test_items_sort_property(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 50.0, - "pms_property_ids": [self.property1.id], - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(days=2, hours=12, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=5, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item2.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-20 00:00:00") - def test_three_items_sort_applied_on(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=1, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "product_id": self.room_type.product_id.id, - "product_tmpl_id": self.product_template.id, - "applied_on": "1_product", - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=1, hours=23, minutes=59), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item3 = self.env["product.pricelist.item"].create( - { - "name": "item_3", - "categ_id": self.product_category.id, - "applied_on": "2_product_category", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=1, hours=23, minutes=59), - "fixed_price": 60.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(hours=12, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=1, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - expected_price = self.item1.fixed_price - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-25 00:00:00") - def test_three_items_sort_date(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=6, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() - + datetime.timedelta(days=1, hours=00, minutes=00), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item3 = self.env["product.pricelist.item"].create( - { - "name": "item_3", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() - + datetime.timedelta(days=2, hours=00, minutes=00), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=4, hours=23, minutes=59), - "fixed_price": 60.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(days=3, hours=10, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=4, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - expected_price = self.item3.fixed_price - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-02-01 00:00:00") - def test_three_items_sort_property(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=3, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=3, hours=23, minutes=59), - "fixed_price": 50.0, - "pms_property_ids": [self.property1.id], - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item3 = self.env["product.pricelist.item"].create( - { - "name": "item_3", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=3, hours=23, minutes=59), - "fixed_price": 60.0, - "pms_property_ids": [self.property1.id, self.property2.id], - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=2, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item3.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-02-01 00:00:00") - def test_sort_applied_on_before_date(self): - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=8, hours=23, minutes=59), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "product_id": self.room_type.product_id.id, - "product_tmpl_id": self.product_template.id, - "applied_on": "1_product", - "date_start": datetime.datetime.today() - + datetime.timedelta(days=2, hours=00, minutes=00), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 50.0, - "pms_property_ids": [self.property1.id], - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(days=2, hours=12, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=4, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item1.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-02-10 00:00:00") - def test_sort_date_before_property(self): - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=10, hours=23, minutes=59), - "fixed_price": 40.0, - "pms_property_ids": [self.property1.id], - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( - { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() - + datetime.timedelta(days=2, hours=00, minutes=00), - "date_end": datetime.datetime.today() - + datetime.timedelta(days=5, hours=23, minutes=59), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today() - + datetime.timedelta(days=2, hours=12, minutes=00), - "checkout": datetime.datetime.today() - + datetime.timedelta(days=4, hours=12, minutes=00), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - # ACT - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item2.fixed_price * n_days - - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") + @freeze_time("2000-01-01") def test_simple_price_without_items(self): + # TEST CASE : no items applied # ARRANGE self.create_common_scenario() @@ -574,286 +100,387 @@ class TestPmsPricelistRules(common.TransactionCase): expected_price, reservation.price_subtotal, "The price is not as expected" ) - @freeze_time("2000-01-01 00:00:00") - def test_item_without_date_end(self): - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item1.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") - def test_item_without_date_start(self): - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( - { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_end": datetime.datetime.today() + datetime.timedelta(days=60), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( - { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item1.fixed_price * n_days - - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") - def test_items_sorted_whitout_date_end(self): + @freeze_time("2022-01-01") + def test_items_sort(self): # ARRANGE self.create_common_scenario() - self.item1 = self.env["product.pricelist.item"].create( + # - test cases to verify the order for each field considered individually + # - test cases to prioritize fields over other fields: + # 1. applied_on + # 2. date + # 3. date overnight + # 4. num. properties + # 5. id + # - tie + # - no [date_start|date_end|date_start_overnight|date_end_overnight] + + test_cases = [ { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() + datetime.timedelta(days=60), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( + "name": "sorting applied_on", + "expected_price": 50 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "2_product_category", + "categ_id": self.product_category.id, + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": 50.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "1_product", + "product_id": self.room_type.product_id.id, + "product_tmpl_id": self.product_template.id, + "fixed_price": 40.0, + }, + ], + }, { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today() + datetime.timedelta(days=7), - "date_end": datetime.datetime.today() + datetime.timedelta(days=14), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( + "name": "sorting SALE date min range", + "expected_price": 50.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=2), + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=1), + "fixed_price": 50.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=3), + "fixed_price": 40.0, + }, + ], + }, { - "checkin": datetime.datetime.today() + datetime.timedelta(days=7), - "checkout": datetime.datetime.today() + datetime.timedelta(days=10), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item2.fixed_price * n_days - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") - def test_items_sorted_whitout_date_start(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( + "name": "sorting OVERNIGHT date min range", + "expected_price": 40.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=6), + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=10), + "fixed_price": 50.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "fixed_price": 40.0, + }, + ], + }, { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( + "name": "sorting num. properties", + "expected_price": 50.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "pms_property_ids": [self.property1.id], + "fixed_price": 50.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "pms_property_ids": [self.property1.id, self.property2.id], + "fixed_price": 40.0, + }, + ], + }, { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_end": datetime.datetime.today() + datetime.timedelta(days=60), - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( + "name": "sorting by item id", + "expected_price": 40.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": 50.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "fixed_price": 40.0, + }, + ], + }, { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item1.fixed_price * n_days - - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") - def test_items_sorted_whith_tie(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( + "name": "prioritize applied_on over SALE date", + "expected_price": 60.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=2), + "fixed_price": 60.0, + }, + { + "pricelist_id": self.pricelist.id, + "product_id": self.room_type.product_id.id, + "product_tmpl_id": self.product_template.id, + "applied_on": "1_product", + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=1), + "fixed_price": 50.0, + }, + ], + }, { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "pms_property_ids": [self.property1.id], - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( + "name": "prioritize SALE date over OVERNIGHT date", + "expected_price": 120.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=10), + "fixed_price": 120.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "fixed_price": 50.0, + }, + ], + }, { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "pms_property_ids": [self.property1.id], - "fixed_price": 60.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( + "name": "prioritize OVERNIGHT date over min. num. properties", + "expected_price": 50.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "fixed_price": 120.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "pms_property_ids": [self.property1.id, self.property2.id], + "fixed_price": 50.0, + }, + ], + }, { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item2.fixed_price * n_days - - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) - - @freeze_time("2000-01-01 00:00:00") - def test_three_items_sorted_whith_tie(self): - - # ARRANGE - self.create_common_scenario() - - self.item1 = self.env["product.pricelist.item"].create( + "name": "prioritize min. num. properties over item id", + "expected_price": 50.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "fixed_price": 120.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "pms_property_ids": [self.property1.id, self.property2.id], + "fixed_price": 50.0, + }, + ], + }, { - "name": "item_1", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "pms_property_ids": [self.property1.id], - "fixed_price": 40.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item2 = self.env["product.pricelist.item"].create( + "name": "tie => order by item id", + "expected_price": 50.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=3), + "pms_property_ids": [self.property1.id, self.property2.id], + "fixed_price": 120.0, + }, + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now() + + datetime.timedelta(days=3), + "date_start": datetime.datetime.now(), + "date_end": datetime.datetime.now() + + datetime.timedelta(days=3), + "pms_property_ids": [self.property1.id, self.property2.id], + "fixed_price": 50.0, + }, + ], + }, { - "name": "item_2", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "pms_property_ids": [self.property1.id], - "fixed_price": 60.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - self.item3 = self.env["product.pricelist.item"].create( + "name": "no SALE DATE START", + "expected_price": 40.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_end": datetime.datetime.now() + + datetime.timedelta(days=1), + "fixed_price": 40.0, + }, + ], + }, { - "name": "item_3", - "applied_on": "0_product_variant", - "product_id": self.room_type.product_id.id, - "date_start": datetime.datetime.today(), - "date_end": datetime.datetime.today() + datetime.timedelta(days=7), - "pms_property_ids": [self.property1.id], - "fixed_price": 50.0, - "pricelist_id": self.pricelist.id, - "compute_price": "fixed", - } - ) - - # ACT - reservation = self.env["pms.reservation"].create( + "name": "no SALE DATE END", + "expected_price": 40.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start": datetime.datetime.now(), + "fixed_price": 40.0, + }, + ], + }, { - "checkin": datetime.datetime.today(), - "checkout": datetime.datetime.today() + datetime.timedelta(days=3), - "preferred_room_id": self.room.id, - "pms_property_id": self.property1.id, - "pricelist_id": self.pricelist.id, - } - ) - n_days = (reservation.checkout - reservation.checkin).days - expected_price = self.item3.fixed_price * n_days + "name": "no overnight DATE START", + "expected_price": 40.0 + self.room_type.list_price * 2, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_end_overnight": datetime.datetime.now(), + "fixed_price": 40.0, + }, + ], + }, + { + "name": "no overnight DATE END", + "expected_price": 40.0 * 3, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "fixed_price": 40.0, + }, + ], + }, + { + "name": "only applied overnight in one night", + "expected_price": 40.0 + self.room_type.list_price * 2, + "items": [ + { + "pricelist_id": self.pricelist.id, + "applied_on": "0_product_variant", + "product_id": self.room_type.product_id.id, + "date_start_overnight": datetime.datetime.now(), + "date_end_overnight": datetime.datetime.now(), + "fixed_price": 40.0, + }, + ], + }, + ] - # ASSERT - self.assertEqual( - expected_price, reservation.price_subtotal, "The price is not as expected" - ) + for tc in test_cases: + with self.subTest(k=tc): + + # ARRANGE + items = [] + for item in tc["items"]: + item = self.env["product.pricelist.item"].create(item) + items.append(item.id) + + # ACT + reservation = self.env["pms.reservation"].create( + { + "checkin": datetime.datetime.now(), + "checkout": datetime.datetime.now() + + datetime.timedelta(days=3), + "preferred_room_id": self.room.id, + "pms_property_id": self.property1.id, + "pricelist_id": self.pricelist.id, + } + ) + reservation_price = reservation.price_subtotal + self.env["pms.reservation"].browse(reservation.id).unlink() + self.env["product.pricelist.item"].browse(items).unlink() + + # ASSERT + self.assertEqual(tc["expected_price"], reservation_price, tc["name"]) diff --git a/pms/tests/test_pms_wizard_folio.py b/pms/tests/test_pms_wizard_folio.py index d331503a3..b2896e68e 100644 --- a/pms/tests/test_pms_wizard_folio.py +++ b/pms/tests/test_pms_wizard_folio.py @@ -1,6 +1,3 @@ -# import datetime -# from freezegun import freeze_time -# import datetime from freezegun import freeze_time @@ -246,16 +243,13 @@ class TestPmsWizardMassiveChanges(TestHotel): # expected price expected_price_total = days * price_today * num_double_rooms - # convert dates to datetimes - dates = self.env["pms.folio.wizard"].get_datetime_from_start_end(checkin) - # set pricelist item for current day product_tmpl_id = self.test_room_type_double.product_id.product_tmpl_id.id pricelist_item = self.env["product.pricelist.item"].create( { "pricelist_id": self.test_pricelist.id, - "date_start": dates[0], - "date_end": dates[1], + "date_start_overnight": checkin, + "date_end_overnight": checkin, "compute_price": "fixed", "applied_on": "1_product", "product_tmpl_id": product_tmpl_id, @@ -316,16 +310,13 @@ class TestPmsWizardMassiveChanges(TestHotel): checkout = fields.date.today() + datetime.timedelta(days=1) days = (checkout - checkin).days - # convert dates to datetimes - dates = self.env["pms.folio.wizard"].get_datetime_from_start_end(checkin) - # set pricelist item for current day product_tmpl_id = self.test_room_type_double.product_id.product_tmpl_id.id pricelist_item = self.env["product.pricelist.item"].create( { "pricelist_id": self.test_pricelist.id, - "date_start": dates[0], - "date_end": dates[1], + "date_start_overnight": checkin, + "date_end_overnight": checkin, "compute_price": "fixed", "applied_on": "1_product", "product_tmpl_id": product_tmpl_id, diff --git a/pms/tests/test_pms_wizard_massive_changes.py b/pms/tests/test_pms_wizard_massive_changes.py index 80eb43f0d..efbb094bb 100644 --- a/pms/tests/test_pms_wizard_massive_changes.py +++ b/pms/tests/test_pms_wizard_massive_changes.py @@ -1,6 +1,5 @@ import datetime -import pytz from freezegun import freeze_time from odoo import fields @@ -301,16 +300,11 @@ class TestPmsWizardMassiveChanges(TestHotel): price = 20 min_quantity = 3 + vals = { "pricelist_id": self.test_pricelist, - "date_start": datetime.datetime.combine( - date_from, - datetime.time.min, - ), - "date_end": datetime.datetime.combine( - date_to, - datetime.time.max, - ), + "date_start": date_from, + "date_end": date_to, "compute_price": "fixed", "applied_on": "1_product", "product_tmpl_id": self.test_room_type_double.product_id.product_tmpl_id, @@ -330,26 +324,20 @@ class TestPmsWizardMassiveChanges(TestHotel): "min_quantity": min_quantity, } ).apply_massive_changes() - vals["date_start"] = pytz.timezone("Europe/Madrid").localize(vals["date_start"]) - vals["date_end"] = pytz.timezone("Europe/Madrid").localize(vals["date_end"]) + vals["date_start_overnight"] = date_from + vals["date_end_overnight"] = date_to + + del vals["date_start"] + del vals["date_end"] + # ASSERT for key in vals: with self.subTest(k=key): - if key == "date_start" or key == "date_end": - self.assertEqual( - fields.Datetime.context_timestamp( - self.test_pricelist.item_ids[0], - self.test_pricelist.item_ids[0][key], - ), - vals[key], - "The value of " + key + " is not correctly established", - ) - else: - self.assertEqual( - self.test_pricelist.item_ids[0][key], - vals[key], - "The value of " + key + " is not correctly established", - ) + self.assertEqual( + self.test_pricelist.item_ids[0][key], + vals[key], + "The value of " + key + " is not correctly established", + ) @freeze_time("1980-12-01") def test_day_of_week_pricelist_items_create(self): @@ -398,17 +386,12 @@ class TestPmsWizardMassiveChanges(TestHotel): # ASSERT pricelist_items = self.test_pricelist.item_ids.sorted( - key=lambda s: s.date_start + key=lambda s: s.date_start_overnight ) # ASSERT self.assertTrue( - ( - fields.Datetime.context_timestamp( - pricelist_items[index], pricelist_items[index].date_start - ) - ).timetuple()[6] - == index + pricelist_items[index].date_start_overnight.timetuple()[6] == index and test_case[index], "Rule not created on correct day of week", ) diff --git a/pms/views/product_pricelist_item_views.xml b/pms/views/product_pricelist_item_views.xml index e41566525..480926ad1 100644 --- a/pms/views/product_pricelist_item_views.xml +++ b/pms/views/product_pricelist_item_views.xml @@ -11,6 +11,10 @@ options="{'no_create': True,'no_open': True}" /> + + + + diff --git a/pms/views/product_pricelist_views.xml b/pms/views/product_pricelist_views.xml index 79c86cd4f..deac8fbc3 100644 --- a/pms/views/product_pricelist_views.xml +++ b/pms/views/product_pricelist_views.xml @@ -18,13 +18,16 @@ expr="//field[@name='item_ids']/tree/field[@name='base']" position="after" > + + - + +
@@ -199,21 +200,28 @@ > + pricelist items will be overwritten: - - - + > + + + + + + + +