diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py
index 91a882b1b..2bc22d09f 100644
--- a/pms/models/pms_reservation.py
+++ b/pms/models/pms_reservation.py
@@ -367,6 +367,9 @@ class PmsReservation(models.Model):
help="Field indicating type of cancellation. "
"It can be 'late', 'intime' or 'noshow'",
copy=False,
+ compute="_compute_cancelled_reason",
+ readonly=False,
+ store=True,
selection=[("late", "Late"), ("intime", "In time"), ("noshow", "No Show")],
tracking=True,
)
@@ -582,6 +585,16 @@ class PmsReservation(models.Model):
compute="_compute_discount",
tracking=True,
)
+
+ services_discount = fields.Float(
+ string="Services discount (€)",
+ help="Services discount",
+ readonly=False,
+ store=True,
+ digits=("Discount"),
+ compute="_compute_services_discount",
+ tracking=True,
+ )
date_order = fields.Date(
string="Date Order",
help="Order date of reservation",
@@ -1146,10 +1159,12 @@ class PmsReservation(models.Model):
for res in self:
res.nights = len(res.reservation_line_ids)
- @api.depends("service_ids.price_total")
+ @api.depends("service_ids.price_total", "services_discount")
def _compute_price_services(self):
for record in self:
- record.price_services = sum(record.mapped("service_ids.price_total"))
+ record.price_services = (
+ sum(record.mapped("service_ids.price_total")) - record.services_discount
+ )
@api.depends("price_services", "price_total")
def _compute_price_room_services_set(self):
@@ -1157,7 +1172,8 @@ class PmsReservation(models.Model):
record.price_room_services_set = record.price_services + record.price_total
@api.depends(
- "reservation_line_ids.discount", "reservation_line_ids.cancel_discount"
+ "reservation_line_ids.discount",
+ "reservation_line_ids.cancel_discount",
)
def _compute_discount(self):
for record in self:
@@ -1167,8 +1183,17 @@ class PmsReservation(models.Model):
price = line.price - first_discount
cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01)
discount += first_discount + cancel_discount
+
record.discount = discount
+ @api.depends("service_ids.discount")
+ def _compute_services_discount(self):
+ for record in self:
+ services_discount = 0
+ for service in record.service_ids:
+ services_discount += service.discount
+ record.services_discount = services_discount
+
@api.depends("reservation_line_ids.price", "discount", "tax_ids")
def _compute_amount_reservation(self):
"""
@@ -1660,37 +1685,39 @@ class PmsReservation(models.Model):
if not record.allowed_cancel:
raise UserError(_("This reservation cannot be cancelled"))
else:
- cancel_reason = (
- "intime"
- if self._context.get("no_penalty", False)
- else record.compute_cancelation_reason()
- )
- if self._context.get("no_penalty", False):
- _logger.info("Modified Reservation - No Penalty")
- record.write({"state": "cancel", "cancelled_reason": cancel_reason})
- # record._compute_cancel_discount()
+ record.state = "cancel"
record.folio_id._compute_amount()
def action_assign(self):
for record in self:
record.to_assign = False
- def compute_cancelation_reason(self):
- self.ensure_one()
- pricelist = self.pricelist_id
- if pricelist and pricelist.cancelation_rule_id:
- tz_property = self.pms_property_id.tz
- today = fields.Date.context_today(self.with_context(tz=tz_property))
- days_diff = (
- fields.Date.from_string(self.checkin) - fields.Date.from_string(today)
- ).days
- if days_diff < 0:
- return "noshow"
- elif days_diff < pricelist.cancelation_rule_id.days_intime:
- return "late"
- else:
- return "intime"
- return False
+ @api.depends("state")
+ def _compute_cancelled_reason(self):
+ for record in self:
+ # self.ensure_one()
+ if record.state == "cancel":
+ pricelist = record.pricelist_id
+ if record._context.get("no_penalty", False):
+ record.cancelled_reason = "intime"
+ _logger.info("Modified Reservation - No Penalty")
+ elif pricelist and pricelist.cancelation_rule_id:
+ tz_property = record.pms_property_id.tz
+ today = fields.Date.context_today(
+ record.with_context(tz=tz_property)
+ )
+ days_diff = (
+ fields.Date.from_string(record.checkin)
+ - fields.Date.from_string(today)
+ ).days
+ if days_diff < 0:
+ record.cancelled_reason = "noshow"
+ elif days_diff < pricelist.cancelation_rule_id.days_intime:
+ record.cancelled_reason = "late"
+ else:
+ record.cancelled_reason = "intime"
+ else:
+ record.cancelled_reason = False
def action_reservation_checkout(self):
for record in self:
diff --git a/pms/models/pms_reservation_line.py b/pms/models/pms_reservation_line.py
index df504aa32..d0533224d 100644
--- a/pms/models/pms_reservation_line.py
+++ b/pms/models/pms_reservation_line.py
@@ -388,56 +388,52 @@ class PmsReservationLine(models.Model):
def _compute_cancel_discount(self):
for line in self:
line.cancel_discount = 0
- # TODO: Review cancel logic
- # reservation = line.reservation_id
- # pricelist = reservation.pricelist_id
- # if reservation.state == "cancel":
- # if (
- # reservation.cancelled_reason
- # and pricelist
- # and pricelist.cancelation_rule_id
- # ):
- # date_start_dt = fields.Date.from_string(
- # reservation.checkin
- # )
- # date_end_dt = fields.Date.from_string(
- # reservation.checkout
- # )
- # days = abs((date_end_dt - date_start_dt).days)
- # rule = pricelist.cancelation_rule_id
- # if reservation.cancelled_reason == "late":
- # discount = 100 - rule.penalty_late
- # if rule.apply_on_late == "first":
- # days = 1
- # elif rule.apply_on_late == "days":
- # days = rule.days_late
- # elif reservation.cancelled_reason == "noshow":
- # discount = 100 - rule.penalty_noshow
- # if rule.apply_on_noshow == "first":
- # days = 1
- # elif rule.apply_on_noshow == "days":
- # days = rule.days_late - 1
- # elif reservation.cancelled_reason == "intime":
- # discount = 100
+ reservation = line.reservation_id
+ pricelist = reservation.pricelist_id
+ if reservation.state == "cancel":
+ if (
+ reservation.cancelled_reason
+ and pricelist
+ and pricelist.cancelation_rule_id
+ ):
+ checkin = fields.Date.from_string(reservation.checkin)
+ checkout = fields.Date.from_string(reservation.checkout)
+ days = abs((checkin - checkout).days)
+ rule = pricelist.cancelation_rule_id
+ discount = 0
+ if reservation.cancelled_reason == "late":
+ discount = 100 - rule.penalty_late
+ if rule.apply_on_late == "first":
+ days = 1
+ elif rule.apply_on_late == "days":
+ days = rule.days_late
+ elif reservation.cancelled_reason == "noshow":
+ discount = 100 - rule.penalty_noshow
+ if rule.apply_on_noshow == "first":
+ days = 1
+ elif rule.apply_on_noshow == "days":
+ days = rule.days_late - 1
+ elif reservation.cancelled_reason == "intime":
+ discount = 100
- # checkin = reservation.checkin
- # dates = []
- # for i in range(0, days):
- # dates.append(
- # (
- # fields.Date.from_string(checkin) + timedelta(days=i)
- # ).strftime(DEFAULT_SERVER_DATE_FORMAT)
- # )
- # reservation.reservation_line_ids.filtered(
- # lambda r: r.date in dates
- # ).update({"cancel_discount": discount})
- # reservation.reservation_line_ids.filtered(
- # lambda r: r.date not in dates
- # ).update({"cancel_discount": 100})
- # else:
- # reservation.reservation_line_ids.update({"cancel_discount": 0})
- # else:
- # reservation.reservation_line_ids.update({"cancel_discount": 0})
+ dates = []
+ for i in range(0, days):
+ dates.append(
+ fields.Date.from_string(
+ fields.Date.from_string(checkin)
+ + datetime.timedelta(days=i)
+ )
+ )
+ reservation.reservation_line_ids.filtered(
+ lambda r: r.date in dates
+ ).update({"cancel_discount": discount})
+ reservation.reservation_line_ids.filtered(
+ lambda r: r.date not in dates
+ ).update({"cancel_discount": 100})
+ else:
+ reservation.reservation_line_ids.update({"cancel_discount": 0})
+ else:
+ reservation.reservation_line_ids.update({"cancel_discount": 0})
@api.depends("room_id", "pms_property_id", "date", "occupies_availability")
def _compute_avail_id(self):
@@ -473,17 +469,6 @@ class PmsReservationLine(models.Model):
if duplicated:
raise ValidationError(_("Duplicated reservation line date"))
- @api.constrains("state")
- def constrains_service_cancel(self):
- for record in self:
- if record.state == "cancel":
- room_services = record.reservation_id.service_ids
- for service in room_services:
- cancel_lines = service.service_line_ids.filtered(
- lambda r: r.date == record.date
- )
- cancel_lines.day_qty = 0
-
@api.constrains("room_id")
def _check_adults(self):
for record in self.filtered("room_id"):
diff --git a/pms/models/pms_service.py b/pms/models/pms_service.py
index fe0f1fba9..858c0cfa7 100644
--- a/pms/models/pms_service.py
+++ b/pms/models/pms_service.py
@@ -168,7 +168,7 @@ class PmsService(models.Model):
)
price_total = fields.Monetary(
string="Total",
- help="Total price without taxes",
+ help="Total price with taxes",
readonly=True,
store=True,
compute="_compute_amount_service",
@@ -181,6 +181,15 @@ class PmsService(models.Model):
compute="_compute_amount_service",
)
+ discount = fields.Float(
+ string="Discount (€)",
+ help="Discount of total price",
+ readonly=False,
+ store=True,
+ digits=("Discount"),
+ compute="_compute_discount",
+ )
+
# Compute and Search methods
@api.depends("product_id")
def _compute_tax_ids(self):
@@ -380,7 +389,16 @@ class PmsService(models.Model):
else:
service.service_line_ids = False
- # Default methods
+ @api.depends("service_line_ids.cancel_discount")
+ def _compute_discount(self):
+ for record in self:
+ discount = 0
+ for line in record.service_line_ids:
+ first_discount = line.price_day_total * ((line.discount or 0.0) * 0.01)
+ price = line.price_day_total - first_discount
+ cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01)
+ discount += first_discount + cancel_discount
+ record.discount = discount
def name_get(self):
result = []
diff --git a/pms/models/pms_service_line.py b/pms/models/pms_service_line.py
index 5ba12f371..7e316d4ae 100644
--- a/pms/models/pms_service_line.py
+++ b/pms/models/pms_service_line.py
@@ -1,6 +1,8 @@
# Copyright 2017-2018 Alexandre Díaz
# Copyright 2017 Dario Lodeiros
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
+import datetime
+
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
@@ -71,7 +73,7 @@ class PmsServiceLine(models.Model):
)
price_day_total = fields.Monetary(
string="Total",
- help="Total price without taxes",
+ help="Total price with taxes",
readonly=True,
store=True,
compute="_compute_day_amount_service",
@@ -169,60 +171,35 @@ class PmsServiceLine(models.Model):
record.discount = 0
# TODO: Refact method and allowed cancelled single days
- @api.depends("service_id.reservation_id.cancelled_reason")
+ @api.depends("service_id.reservation_id.reservation_line_ids.cancel_discount")
def _compute_cancel_discount(self):
for line in self:
line.cancel_discount = 0
- # TODO: Review cancel logic
- # reservation = line.reservation_id.reservation_id
- # pricelist = reservation.pricelist_id
- # if reservation.state == "cancel":
- # if (
- # reservation.cancel_reason
- # and pricelist
- # and pricelist.cancelation_rule_id
- # ):
- # date_start_dt = fields.Date.from_string(
- # reservation.checkin
- # )
- # date_end_dt = fields.Date.from_string(
- # reservation.checkout
- # )
- # days = abs((date_end_dt - date_start_dt).days)
- # rule = pricelist.cancelation_rule_id
- # if reservation.cancelled_reason == "late":
- # discount = 100 - rule.penalty_late
- # if rule.apply_on_late == "first":
- # days = 1
- # elif rule.apply_on_late == "days":
- # days = rule.days_late
- # elif reservation.cancelled_reason == "noshow":
- # discount = 100 - rule.penalty_noshow
- # if rule.apply_on_noshow == "first":
- # days = 1
- # elif rule.apply_on_noshow == "days":
- # days = rule.days_late - 1
- # elif reservation.cancelled_reason == "intime":
- # discount = 100
-
- # checkin = reservation.checkin
- # dates = []
- # for i in range(0, days):
- # dates.append(
- # (
- # fields.Date.from_string(checkin) + timedelta(days=i)
- # ).strftime(DEFAULT_SERVER_DATE_FORMAT)
- # )
- # reservation.reservation_line_ids.filtered(
- # lambda r: r.date in dates
- # ).update({"cancel_discount": discount})
- # reservation.reservation_line_ids.filtered(
- # lambda r: r.date not in dates
- # ).update({"cancel_discount": 100})
- # else:
- # reservation.reservation_line_ids.update({"cancel_discount": 0})
- # else:
- # reservation.reservation_line_ids.update({"cancel_discount": 0})
+ reservation = line.reservation_id
+ if reservation.state == "cancel":
+ if (
+ reservation.cancelled_reason
+ and reservation.pricelist_id
+ and reservation.pricelist_id.cancelation_rule_id
+ and reservation.reservation_line_ids.mapped("cancel_discount")
+ ):
+ if line.is_board_service:
+ consumed_date = (
+ line.date
+ if line.product_id.consumed_on == "before"
+ else line.date + datetime.timedelta(days=-1)
+ )
+ line.cancel_discount = (
+ reservation.reservation_line_ids.filtered(
+ lambda l: l.date == consumed_date
+ ).cancel_discount
+ )
+ else:
+ line.cancel_discount = 100
+ else:
+ line.cancel_discount = 0
+ else:
+ line.cancel_discount = 0
@api.depends("day_qty")
def _compute_auto_qty(self):
diff --git a/pms/tests/test_pms_reservation.py b/pms/tests/test_pms_reservation.py
index e65e6ba47..179534974 100644
--- a/pms/tests/test_pms_reservation.py
+++ b/pms/tests/test_pms_reservation.py
@@ -2569,3 +2569,425 @@ class TestPmsReservations(TestPms):
"Reservation from " + agency.name,
"Partner name doesn't match with to the expected",
)
+
+ @freeze_time("2010-11-10")
+ def test_cancel_discount_board_service(self):
+ """
+ When a reservation is cancelled, service discount in case of board_services
+ must be equal to the discounts of each reservation_line.
+
+ """
+
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.product = self.env["product.product"].create(
+ {
+ "name": "Product test",
+ "per_day": True,
+ "consumed_on": "after",
+ }
+ )
+ self.board_service = self.env["pms.service"].create(
+ {
+ "is_board_service": True,
+ "product_id": self.product.id,
+ }
+ )
+
+ self.room_type_double.list_price = 25
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": fields.date.today() + datetime.timedelta(days=-3),
+ "checkout": fields.date.today() + datetime.timedelta(days=3),
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ "service_ids": [self.board_service.id],
+ }
+ )
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ # ASSERT
+ self.assertEqual(
+ set(reservation.reservation_line_ids.mapped("cancel_discount")),
+ set(reservation.service_ids.service_line_ids.mapped("cancel_discount")),
+ "Cancel discount of reservation service lines must be the same "
+ "that reservation board services",
+ )
+
+ @freeze_time("2011-10-10")
+ def test_cancel_discount_reservation_line(self):
+ """
+ When a reservation is cancelled, cancellation discount is given
+ by the cancellation rule associated with the reservation pricelist.
+ Each reservation_line calculates depending on the cancellation
+ reason which is the correspondig discount. In this case the
+ cancellation reason is'noshow' and the rule specifies that 50% must
+ be reducted every day, that is, on each of reseravtion_lines
+ """
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.room_type_double.list_price = 50
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": fields.date.today() + datetime.timedelta(days=-3),
+ "checkout": fields.date.today() + datetime.timedelta(days=3),
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ }
+ )
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ # ASSERT
+ self.assertEqual(
+ set(reservation.reservation_line_ids.mapped("cancel_discount")),
+ {self.cancelation_rule.penalty_noshow},
+ "Cancel discount of reservation_lines must be equal than cancellation rule penalty",
+ )
+
+ @freeze_time("2011-11-11")
+ def test_cancel_discount_service(self):
+ """
+ When a reservation is cancelled, service discount in
+ services that are not board_services ALWAYS have to be 100%,
+ refardless of the cancellation rule associated with the pricelist
+ """
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.product = self.env["product.product"].create(
+ {
+ "name": "Product test",
+ "per_day": True,
+ "consumed_on": "after",
+ "is_extra_bed": True,
+ }
+ )
+ self.service = self.env["pms.service"].create(
+ {
+ "is_board_service": False,
+ "product_id": self.product.id,
+ }
+ )
+
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": fields.date.today() + datetime.timedelta(days=-3),
+ "checkout": fields.date.today() + datetime.timedelta(days=3),
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ "service_ids": [self.service.id],
+ }
+ )
+
+ expected_cancel_discount = 100
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ # ASSERT
+ self.assertEqual(
+ {expected_cancel_discount},
+ set(reservation.service_ids.service_line_ids.mapped("cancel_discount")),
+ "Cancel discount of services must be 100%",
+ )
+
+ @freeze_time("2011-06-06")
+ def test_discount_in_service(self):
+ """
+ Discount in pms.service is calculated from the
+ discounts that each if its service lines has,
+ in this case when reservation is cancelled a
+ 50% cancellation discount is applied and
+ there aren't other different discounts
+ """
+
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.product = self.env["product.product"].create(
+ {
+ "name": "Product test",
+ "per_day": True,
+ "consumed_on": "after",
+ }
+ )
+ self.board_service = self.env["pms.service"].create(
+ {
+ "is_board_service": True,
+ "product_id": self.product.id,
+ }
+ )
+
+ self.room_type_double.list_price = 25
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": fields.date.today() + datetime.timedelta(days=-3),
+ "checkout": fields.date.today() + datetime.timedelta(days=3),
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ "service_ids": [self.board_service.id],
+ }
+ )
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ expected_discount = sum(
+ sl.price_day_total * sl.cancel_discount / 100
+ for sl in self.board_service.service_line_ids
+ )
+ # ASSERT
+ self.assertEqual(
+ expected_discount,
+ self.board_service.discount,
+ "Service discount must be the sum of its services_lines discount",
+ )
+
+ @freeze_time("2011-11-11")
+ def test_services_discount_in_reservation(self):
+ """
+ Services discount in reservation is equal to the sum of the discounts of all
+ its services, whether they are board_services or not
+ """
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.product1 = self.env["product.product"].create(
+ {
+ "name": "Product test1",
+ "per_day": True,
+ "consumed_on": "after",
+ "is_extra_bed": True,
+ }
+ )
+ self.service = self.env["pms.service"].create(
+ {
+ "is_board_service": False,
+ "product_id": self.product1.id,
+ }
+ )
+ self.service.flush()
+ self.product2 = self.env["product.product"].create(
+ {
+ "name": "Product test 2",
+ "per_person": True,
+ "consumed_on": "after",
+ }
+ )
+ self.board_service = self.env["pms.service"].create(
+ {
+ "is_board_service": True,
+ "product_id": self.product2.id,
+ }
+ )
+
+ self.room_type_double.list_price = 25
+ checkin = fields.date.today() + datetime.timedelta(days=-3)
+ checkout = fields.date.today() + datetime.timedelta(days=3)
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": checkin,
+ "checkout": checkout,
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ "service_ids": [self.service.id, self.board_service.id],
+ }
+ )
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ expected_discount = sum(s.discount for s in reservation.service_ids)
+
+ # ASSERT
+ self.assertEqual(
+ expected_discount,
+ reservation.services_discount,
+ "Services discount isn't the expected",
+ )
+
+ @freeze_time("2011-12-12")
+ def test_price_services_in_reservation(self):
+ """
+ Service price total in a reservation corresponds to the sum of prices
+ of all its services less the total discount of that services
+ """
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.product1 = self.env["product.product"].create(
+ {
+ "name": "Product test1",
+ "per_day": True,
+ "consumed_on": "after",
+ "is_extra_bed": True,
+ }
+ )
+ self.service = self.env["pms.service"].create(
+ {
+ "is_board_service": False,
+ "product_id": self.product1.id,
+ }
+ )
+ self.service.flush()
+ self.product2 = self.env["product.product"].create(
+ {
+ "name": "Product test 2",
+ "per_person": True,
+ "consumed_on": "after",
+ }
+ )
+ self.board_service = self.env["pms.service"].create(
+ {
+ "is_board_service": True,
+ "product_id": self.product2.id,
+ }
+ )
+
+ self.room_type_double.list_price = 25
+ checkin = fields.date.today() + datetime.timedelta(days=-3)
+ checkout = fields.date.today() + datetime.timedelta(days=3)
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": checkin,
+ "checkout": checkout,
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ "service_ids": [self.service.id, self.board_service.id],
+ }
+ )
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+ expected_price = (
+ self.service.price_total
+ + self.board_service.price_total * reservation.adults
+ ) - reservation.services_discount
+
+ # ASSERT
+ self.assertEqual(
+ expected_price,
+ reservation.price_services,
+ "Services price isn't the expected",
+ )
+
+ @freeze_time("2011-08-08")
+ def test_room_discount_in_reservation(self):
+ """
+ Discount in pms.reservation is calculated from the
+ discounts that each if its reservation lines has,
+ in this case when reservation is cancelled a 50%
+ cancellation discount is applied and
+ there aren't other different discounts
+ """
+ # ARRANGE
+ self.cancelation_rule = self.env["pms.cancelation.rule"].create(
+ {
+ "name": "Cancelation Rule Test",
+ "penalty_noshow": 50,
+ "apply_on_noshow": "all",
+ }
+ )
+
+ self.pricelist1.cancelation_rule_id = self.cancelation_rule.id
+
+ self.room_type_double.list_price = 30
+ checkin = fields.date.today() + datetime.timedelta(days=-3)
+ checkout = fields.date.today() + datetime.timedelta(days=3)
+ reservation = self.env["pms.reservation"].create(
+ {
+ "checkin": checkin,
+ "checkout": checkout,
+ "room_type_id": self.room_type_double.id,
+ "partner_id": self.partner1.id,
+ "pms_property_id": self.pms_property1.id,
+ "pricelist_id": self.pricelist1.id,
+ }
+ )
+
+ # ACTION
+ reservation.action_cancel()
+ reservation.flush()
+
+ expected_discount = sum(
+ rl.price * rl.cancel_discount / 100
+ for rl in reservation.reservation_line_ids
+ )
+
+ # ASSERT
+ self.assertEqual(
+ expected_discount,
+ reservation.discount,
+ "Room discount isn't the expected",
+ )
diff --git a/pms/views/pms_reservation_views.xml b/pms/views/pms_reservation_views.xml
index 5b83161da..af3fd4b9a 100644
--- a/pms/views/pms_reservation_views.xml
+++ b/pms/views/pms_reservation_views.xml
@@ -405,6 +405,12 @@
widget="monetary"
options="{'currency_field': 'currency_id'}"
/>
+