mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
@@ -367,6 +367,9 @@ class PmsReservation(models.Model):
|
|||||||
help="Field indicating type of cancellation. "
|
help="Field indicating type of cancellation. "
|
||||||
"It can be 'late', 'intime' or 'noshow'",
|
"It can be 'late', 'intime' or 'noshow'",
|
||||||
copy=False,
|
copy=False,
|
||||||
|
compute="_compute_cancelled_reason",
|
||||||
|
readonly=False,
|
||||||
|
store=True,
|
||||||
selection=[("late", "Late"), ("intime", "In time"), ("noshow", "No Show")],
|
selection=[("late", "Late"), ("intime", "In time"), ("noshow", "No Show")],
|
||||||
tracking=True,
|
tracking=True,
|
||||||
)
|
)
|
||||||
@@ -582,6 +585,16 @@ class PmsReservation(models.Model):
|
|||||||
compute="_compute_discount",
|
compute="_compute_discount",
|
||||||
tracking=True,
|
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(
|
date_order = fields.Date(
|
||||||
string="Date Order",
|
string="Date Order",
|
||||||
help="Order date of reservation",
|
help="Order date of reservation",
|
||||||
@@ -1146,10 +1159,12 @@ class PmsReservation(models.Model):
|
|||||||
for res in self:
|
for res in self:
|
||||||
res.nights = len(res.reservation_line_ids)
|
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):
|
def _compute_price_services(self):
|
||||||
for record in 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")
|
@api.depends("price_services", "price_total")
|
||||||
def _compute_price_room_services_set(self):
|
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
|
record.price_room_services_set = record.price_services + record.price_total
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"reservation_line_ids.discount", "reservation_line_ids.cancel_discount"
|
"reservation_line_ids.discount",
|
||||||
|
"reservation_line_ids.cancel_discount",
|
||||||
)
|
)
|
||||||
def _compute_discount(self):
|
def _compute_discount(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
@@ -1167,8 +1183,17 @@ class PmsReservation(models.Model):
|
|||||||
price = line.price - first_discount
|
price = line.price - first_discount
|
||||||
cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01)
|
cancel_discount = price * ((line.cancel_discount or 0.0) * 0.01)
|
||||||
discount += first_discount + cancel_discount
|
discount += first_discount + cancel_discount
|
||||||
|
|
||||||
record.discount = 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")
|
@api.depends("reservation_line_ids.price", "discount", "tax_ids")
|
||||||
def _compute_amount_reservation(self):
|
def _compute_amount_reservation(self):
|
||||||
"""
|
"""
|
||||||
@@ -1660,37 +1685,39 @@ class PmsReservation(models.Model):
|
|||||||
if not record.allowed_cancel:
|
if not record.allowed_cancel:
|
||||||
raise UserError(_("This reservation cannot be cancelled"))
|
raise UserError(_("This reservation cannot be cancelled"))
|
||||||
else:
|
else:
|
||||||
cancel_reason = (
|
record.state = "cancel"
|
||||||
"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.folio_id._compute_amount()
|
record.folio_id._compute_amount()
|
||||||
|
|
||||||
def action_assign(self):
|
def action_assign(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
record.to_assign = False
|
record.to_assign = False
|
||||||
|
|
||||||
def compute_cancelation_reason(self):
|
@api.depends("state")
|
||||||
self.ensure_one()
|
def _compute_cancelled_reason(self):
|
||||||
pricelist = self.pricelist_id
|
for record in self:
|
||||||
if pricelist and pricelist.cancelation_rule_id:
|
# self.ensure_one()
|
||||||
tz_property = self.pms_property_id.tz
|
if record.state == "cancel":
|
||||||
today = fields.Date.context_today(self.with_context(tz=tz_property))
|
pricelist = record.pricelist_id
|
||||||
days_diff = (
|
if record._context.get("no_penalty", False):
|
||||||
fields.Date.from_string(self.checkin) - fields.Date.from_string(today)
|
record.cancelled_reason = "intime"
|
||||||
).days
|
_logger.info("Modified Reservation - No Penalty")
|
||||||
if days_diff < 0:
|
elif pricelist and pricelist.cancelation_rule_id:
|
||||||
return "noshow"
|
tz_property = record.pms_property_id.tz
|
||||||
elif days_diff < pricelist.cancelation_rule_id.days_intime:
|
today = fields.Date.context_today(
|
||||||
return "late"
|
record.with_context(tz=tz_property)
|
||||||
else:
|
)
|
||||||
return "intime"
|
days_diff = (
|
||||||
return False
|
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):
|
def action_reservation_checkout(self):
|
||||||
for record in self:
|
for record in self:
|
||||||
|
|||||||
@@ -388,56 +388,52 @@ class PmsReservationLine(models.Model):
|
|||||||
def _compute_cancel_discount(self):
|
def _compute_cancel_discount(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
line.cancel_discount = 0
|
line.cancel_discount = 0
|
||||||
# TODO: Review cancel logic
|
reservation = line.reservation_id
|
||||||
# reservation = line.reservation_id
|
pricelist = reservation.pricelist_id
|
||||||
# pricelist = reservation.pricelist_id
|
if reservation.state == "cancel":
|
||||||
# if reservation.state == "cancel":
|
if (
|
||||||
# if (
|
reservation.cancelled_reason
|
||||||
# reservation.cancelled_reason
|
and pricelist
|
||||||
# and pricelist
|
and pricelist.cancelation_rule_id
|
||||||
# and pricelist.cancelation_rule_id
|
):
|
||||||
# ):
|
checkin = fields.Date.from_string(reservation.checkin)
|
||||||
# date_start_dt = fields.Date.from_string(
|
checkout = fields.Date.from_string(reservation.checkout)
|
||||||
# reservation.checkin
|
days = abs((checkin - checkout).days)
|
||||||
# )
|
rule = pricelist.cancelation_rule_id
|
||||||
# date_end_dt = fields.Date.from_string(
|
discount = 0
|
||||||
# reservation.checkout
|
if reservation.cancelled_reason == "late":
|
||||||
# )
|
discount = 100 - rule.penalty_late
|
||||||
# days = abs((date_end_dt - date_start_dt).days)
|
if rule.apply_on_late == "first":
|
||||||
# rule = pricelist.cancelation_rule_id
|
days = 1
|
||||||
# if reservation.cancelled_reason == "late":
|
elif rule.apply_on_late == "days":
|
||||||
# discount = 100 - rule.penalty_late
|
days = rule.days_late
|
||||||
# if rule.apply_on_late == "first":
|
elif reservation.cancelled_reason == "noshow":
|
||||||
# days = 1
|
discount = 100 - rule.penalty_noshow
|
||||||
# elif rule.apply_on_late == "days":
|
if rule.apply_on_noshow == "first":
|
||||||
# days = rule.days_late
|
days = 1
|
||||||
# elif reservation.cancelled_reason == "noshow":
|
elif rule.apply_on_noshow == "days":
|
||||||
# discount = 100 - rule.penalty_noshow
|
days = rule.days_late - 1
|
||||||
# if rule.apply_on_noshow == "first":
|
elif reservation.cancelled_reason == "intime":
|
||||||
# days = 1
|
discount = 100
|
||||||
# elif rule.apply_on_noshow == "days":
|
|
||||||
# days = rule.days_late - 1
|
|
||||||
# elif reservation.cancelled_reason == "intime":
|
|
||||||
# discount = 100
|
|
||||||
|
|
||||||
# checkin = reservation.checkin
|
dates = []
|
||||||
# dates = []
|
for i in range(0, days):
|
||||||
# for i in range(0, days):
|
dates.append(
|
||||||
# dates.append(
|
fields.Date.from_string(
|
||||||
# (
|
fields.Date.from_string(checkin)
|
||||||
# fields.Date.from_string(checkin) + timedelta(days=i)
|
+ datetime.timedelta(days=i)
|
||||||
# ).strftime(DEFAULT_SERVER_DATE_FORMAT)
|
)
|
||||||
# )
|
)
|
||||||
# reservation.reservation_line_ids.filtered(
|
reservation.reservation_line_ids.filtered(
|
||||||
# lambda r: r.date in dates
|
lambda r: r.date in dates
|
||||||
# ).update({"cancel_discount": discount})
|
).update({"cancel_discount": discount})
|
||||||
# reservation.reservation_line_ids.filtered(
|
reservation.reservation_line_ids.filtered(
|
||||||
# lambda r: r.date not in dates
|
lambda r: r.date not in dates
|
||||||
# ).update({"cancel_discount": 100})
|
).update({"cancel_discount": 100})
|
||||||
# else:
|
else:
|
||||||
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
reservation.reservation_line_ids.update({"cancel_discount": 0})
|
||||||
# else:
|
else:
|
||||||
# reservation.reservation_line_ids.update({"cancel_discount": 0})
|
reservation.reservation_line_ids.update({"cancel_discount": 0})
|
||||||
|
|
||||||
@api.depends("room_id", "pms_property_id", "date", "occupies_availability")
|
@api.depends("room_id", "pms_property_id", "date", "occupies_availability")
|
||||||
def _compute_avail_id(self):
|
def _compute_avail_id(self):
|
||||||
@@ -473,17 +469,6 @@ class PmsReservationLine(models.Model):
|
|||||||
if duplicated:
|
if duplicated:
|
||||||
raise ValidationError(_("Duplicated reservation line date"))
|
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")
|
@api.constrains("room_id")
|
||||||
def _check_adults(self):
|
def _check_adults(self):
|
||||||
for record in self.filtered("room_id"):
|
for record in self.filtered("room_id"):
|
||||||
|
|||||||
@@ -168,7 +168,7 @@ class PmsService(models.Model):
|
|||||||
)
|
)
|
||||||
price_total = fields.Monetary(
|
price_total = fields.Monetary(
|
||||||
string="Total",
|
string="Total",
|
||||||
help="Total price without taxes",
|
help="Total price with taxes",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
compute="_compute_amount_service",
|
compute="_compute_amount_service",
|
||||||
@@ -181,6 +181,15 @@ class PmsService(models.Model):
|
|||||||
compute="_compute_amount_service",
|
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
|
# Compute and Search methods
|
||||||
@api.depends("product_id")
|
@api.depends("product_id")
|
||||||
def _compute_tax_ids(self):
|
def _compute_tax_ids(self):
|
||||||
@@ -380,7 +389,16 @@ class PmsService(models.Model):
|
|||||||
else:
|
else:
|
||||||
service.service_line_ids = False
|
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):
|
def name_get(self):
|
||||||
result = []
|
result = []
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
# Copyright 2017-2018 Alexandre Díaz
|
# Copyright 2017-2018 Alexandre Díaz
|
||||||
# Copyright 2017 Dario Lodeiros
|
# Copyright 2017 Dario Lodeiros
|
||||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
import datetime
|
||||||
|
|
||||||
from odoo import _, api, fields, models
|
from odoo import _, api, fields, models
|
||||||
from odoo.exceptions import ValidationError
|
from odoo.exceptions import ValidationError
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ class PmsServiceLine(models.Model):
|
|||||||
)
|
)
|
||||||
price_day_total = fields.Monetary(
|
price_day_total = fields.Monetary(
|
||||||
string="Total",
|
string="Total",
|
||||||
help="Total price without taxes",
|
help="Total price with taxes",
|
||||||
readonly=True,
|
readonly=True,
|
||||||
store=True,
|
store=True,
|
||||||
compute="_compute_day_amount_service",
|
compute="_compute_day_amount_service",
|
||||||
@@ -169,60 +171,35 @@ class PmsServiceLine(models.Model):
|
|||||||
record.discount = 0
|
record.discount = 0
|
||||||
|
|
||||||
# TODO: Refact method and allowed cancelled single days
|
# 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):
|
def _compute_cancel_discount(self):
|
||||||
for line in self:
|
for line in self:
|
||||||
line.cancel_discount = 0
|
line.cancel_discount = 0
|
||||||
# TODO: Review cancel logic
|
reservation = line.reservation_id
|
||||||
# reservation = line.reservation_id.reservation_id
|
if reservation.state == "cancel":
|
||||||
# pricelist = reservation.pricelist_id
|
if (
|
||||||
# if reservation.state == "cancel":
|
reservation.cancelled_reason
|
||||||
# if (
|
and reservation.pricelist_id
|
||||||
# reservation.cancel_reason
|
and reservation.pricelist_id.cancelation_rule_id
|
||||||
# and pricelist
|
and reservation.reservation_line_ids.mapped("cancel_discount")
|
||||||
# and pricelist.cancelation_rule_id
|
):
|
||||||
# ):
|
if line.is_board_service:
|
||||||
# date_start_dt = fields.Date.from_string(
|
consumed_date = (
|
||||||
# reservation.checkin
|
line.date
|
||||||
# )
|
if line.product_id.consumed_on == "before"
|
||||||
# date_end_dt = fields.Date.from_string(
|
else line.date + datetime.timedelta(days=-1)
|
||||||
# reservation.checkout
|
)
|
||||||
# )
|
line.cancel_discount = (
|
||||||
# days = abs((date_end_dt - date_start_dt).days)
|
reservation.reservation_line_ids.filtered(
|
||||||
# rule = pricelist.cancelation_rule_id
|
lambda l: l.date == consumed_date
|
||||||
# if reservation.cancelled_reason == "late":
|
).cancel_discount
|
||||||
# discount = 100 - rule.penalty_late
|
)
|
||||||
# if rule.apply_on_late == "first":
|
else:
|
||||||
# days = 1
|
line.cancel_discount = 100
|
||||||
# elif rule.apply_on_late == "days":
|
else:
|
||||||
# days = rule.days_late
|
line.cancel_discount = 0
|
||||||
# elif reservation.cancelled_reason == "noshow":
|
else:
|
||||||
# discount = 100 - rule.penalty_noshow
|
line.cancel_discount = 0
|
||||||
# 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})
|
|
||||||
|
|
||||||
@api.depends("day_qty")
|
@api.depends("day_qty")
|
||||||
def _compute_auto_qty(self):
|
def _compute_auto_qty(self):
|
||||||
|
|||||||
@@ -2569,3 +2569,425 @@ class TestPmsReservations(TestPms):
|
|||||||
"Reservation from " + agency.name,
|
"Reservation from " + agency.name,
|
||||||
"Partner name doesn't match with to the expected",
|
"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",
|
||||||
|
)
|
||||||
|
|||||||
@@ -405,6 +405,12 @@
|
|||||||
widget="monetary"
|
widget="monetary"
|
||||||
options="{'currency_field': 'currency_id'}"
|
options="{'currency_field': 'currency_id'}"
|
||||||
/>
|
/>
|
||||||
|
<field
|
||||||
|
name="services_discount"
|
||||||
|
string="Discount Services"
|
||||||
|
widget="monetary"
|
||||||
|
options="{'currency_field': 'currency_id'}"
|
||||||
|
/>
|
||||||
<field name="price_subtotal" invisible="1" />
|
<field name="price_subtotal" invisible="1" />
|
||||||
<div
|
<div
|
||||||
class="oe_subtotal_footer_separator oe_inline o_td_label"
|
class="oe_subtotal_footer_separator oe_inline o_td_label"
|
||||||
@@ -542,6 +548,7 @@
|
|||||||
<field name="tax_ids" widget="many2many_tags" />
|
<field name="tax_ids" widget="many2many_tags" />
|
||||||
<field name="price_subtotal" />
|
<field name="price_subtotal" />
|
||||||
<field name="price_tax" />
|
<field name="price_tax" />
|
||||||
|
<field name="discount" />
|
||||||
<field name="price_total" />
|
<field name="price_total" />
|
||||||
<field name="service_line_ids" invisible="1">
|
<field name="service_line_ids" invisible="1">
|
||||||
<tree string="Days">
|
<tree string="Days">
|
||||||
|
|||||||
Reference in New Issue
Block a user