mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[RFC] Goodbye split reserves, Hi room by night!
This commit is contained in:
@@ -20,6 +20,7 @@ from . import account_move
|
|||||||
from . import product_template
|
from . import product_template
|
||||||
from . import res_company
|
from . import res_company
|
||||||
from . import account_payment
|
from . import account_payment
|
||||||
|
from . import pms_room_type_availability
|
||||||
from . import pms_room_type_restriction
|
from . import pms_room_type_restriction
|
||||||
from . import pms_room_type_restriction_item
|
from . import pms_room_type_restriction_item
|
||||||
from . import pms_reservation_line
|
from . import pms_reservation_line
|
||||||
|
|||||||
@@ -857,12 +857,12 @@ class PmsFolio(models.Model):
|
|||||||
and not rline.parent_reservation
|
and not rline.parent_reservation
|
||||||
and rline.state == state
|
and rline.state == state
|
||||||
):
|
):
|
||||||
dates = (rline.real_checkin, rline.real_checkout)
|
dates = (rline.checkin, rline.checkout)
|
||||||
vals = {
|
vals = {
|
||||||
"num": len(
|
"num": len(
|
||||||
self.reservation_ids.filtered(
|
self.reservation_ids.filtered(
|
||||||
lambda r: r.real_checkin == dates[0]
|
lambda r: r.checkin == dates[0]
|
||||||
and r.real_checkout == dates[1]
|
and r.checkout == dates[1]
|
||||||
and r.room_type_id.id == rline.room_type_id.id
|
and r.room_type_id.id == rline.room_type_id.id
|
||||||
and (r.to_send or import_all)
|
and (r.to_send or import_all)
|
||||||
and not r.parent_reservation
|
and not r.parent_reservation
|
||||||
|
|||||||
@@ -244,8 +244,6 @@ class PmsReservation(models.Model):
|
|||||||
out_service_description = fields.Text("Cause of out of service")
|
out_service_description = fields.Text("Cause of out of service")
|
||||||
checkin = fields.Date("Check In", required=True, default=_get_default_checkin)
|
checkin = fields.Date("Check In", required=True, default=_get_default_checkin)
|
||||||
checkout = fields.Date("Check Out", required=True, default=_get_default_checkout)
|
checkout = fields.Date("Check Out", required=True, default=_get_default_checkout)
|
||||||
real_checkin = fields.Date("From", compute="_compute_real_checkin", store=True,)
|
|
||||||
real_checkout = fields.Date("To", compute="_compute_real_checkout", store=True,)
|
|
||||||
arrival_hour = fields.Char(
|
arrival_hour = fields.Char(
|
||||||
"Arrival Hour",
|
"Arrival Hour",
|
||||||
default=_get_default_arrival_hour,
|
default=_get_default_arrival_hour,
|
||||||
@@ -460,12 +458,12 @@ class PmsReservation(models.Model):
|
|||||||
)
|
)
|
||||||
return
|
return
|
||||||
rooms_available = (
|
rooms_available = (
|
||||||
self.env["pms.room.type"].check_availability_room_type(
|
self.env["pms.room.type.availability"].rooms_available(
|
||||||
dfrom=reservation.checkin,
|
checkin=reservation.checkin,
|
||||||
dto=reservation.checkout,
|
checkout=reservation.checkout,
|
||||||
room_type_id=False, # Allow chosen any available room
|
room_type_id=False, # Allow chosen any available room
|
||||||
|
current_lines=reservation.reservation_line_ids.ids,
|
||||||
)
|
)
|
||||||
+ self.room_id
|
|
||||||
)
|
)
|
||||||
if (
|
if (
|
||||||
reservation.room_id
|
reservation.room_id
|
||||||
@@ -572,65 +570,6 @@ class PmsReservation(models.Model):
|
|||||||
else:
|
else:
|
||||||
reservation.adults = 0
|
reservation.adults = 0
|
||||||
|
|
||||||
@api.depends("checkin")
|
|
||||||
def _compute_real_checkin(self):
|
|
||||||
for reservation in self:
|
|
||||||
reservation.real_checkin = reservation.checkin
|
|
||||||
if reservation.splitted:
|
|
||||||
master_reservation = reservation.parent_reservation or reservation
|
|
||||||
reservation.real_checkin = master_reservation.checkin
|
|
||||||
splitted_reservations = self.env["pms.reservation"].search(
|
|
||||||
[
|
|
||||||
("splitted", "=", True),
|
|
||||||
("folio_id", "=", self.folio_id.id),
|
|
||||||
"|",
|
|
||||||
("parent_reservation", "=", master_reservation.id),
|
|
||||||
("id", "=", master_reservation.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for split in splitted_reservations:
|
|
||||||
if reservation.real_checkin > split.checkin:
|
|
||||||
reservation.real_checkin = split.checkin
|
|
||||||
|
|
||||||
@api.depends("checkout")
|
|
||||||
def _compute_real_checkout(self):
|
|
||||||
for reservation in self:
|
|
||||||
reservation.real_checkout = reservation.checkout
|
|
||||||
if reservation.splitted:
|
|
||||||
master_reservation = reservation.parent_reservation or reservation
|
|
||||||
reservation.real_checkout = master_reservation.checkout
|
|
||||||
splitted_reservations = self.env["pms.reservation"].search(
|
|
||||||
[
|
|
||||||
("splitted", "=", True),
|
|
||||||
("folio_id", "=", self.folio_id.id),
|
|
||||||
"|",
|
|
||||||
("parent_reservation", "=", master_reservation.id),
|
|
||||||
("id", "=", master_reservation.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for split in splitted_reservations:
|
|
||||||
if reservation.real_checkout < split.checkout:
|
|
||||||
reservation.real_checkout = split.checkout
|
|
||||||
|
|
||||||
@api.depends("splitted", "checkout")
|
|
||||||
def _compute_real_checkin(self):
|
|
||||||
for reservation in self:
|
|
||||||
if reservation.splitted:
|
|
||||||
master_reservation = reservation.parent_reservation or reservation
|
|
||||||
first_checkin = master_reservation.checkin
|
|
||||||
splitted_reservations = self.env["pms.reservation"].search(
|
|
||||||
[
|
|
||||||
("splitted", "=", True),
|
|
||||||
("folio_id", "=", self.folio_id.id),
|
|
||||||
"|",
|
|
||||||
("parent_reservation", "=", master_reservation.id),
|
|
||||||
("id", "=", master_reservation.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
for split in splitted_reservations:
|
|
||||||
if first_checkin > split.checkin:
|
|
||||||
reservation.real_checkin = split.checkin
|
|
||||||
|
|
||||||
@api.depends("checkin", "checkout", "state")
|
@api.depends("checkin", "checkout", "state")
|
||||||
def _compute_to_send(self):
|
def _compute_to_send(self):
|
||||||
for reservation in self:
|
for reservation in self:
|
||||||
@@ -987,9 +926,9 @@ class PmsReservation(models.Model):
|
|||||||
def _autoassign(self):
|
def _autoassign(self):
|
||||||
self.ensure_one()
|
self.ensure_one()
|
||||||
room_chosen = False
|
room_chosen = False
|
||||||
rooms_available = self.env["pms.room.type"].check_availability_room_type(
|
rooms_available = self.env["pms.room.type.availability"].rooms_available(
|
||||||
dfrom=self.checkin,
|
checkin=self.checkin,
|
||||||
dto=self.checkout,
|
checkout=self.checkout,
|
||||||
room_type_id=self.room_type_id.id or False,
|
room_type_id=self.room_type_id.id or False,
|
||||||
)
|
)
|
||||||
if rooms_available:
|
if rooms_available:
|
||||||
@@ -1035,8 +974,6 @@ class PmsReservation(models.Model):
|
|||||||
"splitted": self.splitted,
|
"splitted": self.splitted,
|
||||||
"room_type_id": self.room_type_id.id,
|
"room_type_id": self.room_type_id.id,
|
||||||
"room_id": self.room_id.id,
|
"room_id": self.room_id.id,
|
||||||
"real_checkin": self.real_checkin,
|
|
||||||
"real_checkout": self.real_checkout,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def confirm(self):
|
def confirm(self):
|
||||||
@@ -1119,7 +1056,7 @@ class PmsReservation(models.Model):
|
|||||||
tz_property = self.env.user.pms_property_id.tz
|
tz_property = self.env.user.pms_property_id.tz
|
||||||
today = fields.Date.context_today(self.with_context(tz=tz_property))
|
today = fields.Date.context_today(self.with_context(tz=tz_property))
|
||||||
days_diff = (
|
days_diff = (
|
||||||
fields.Date.from_string(self.real_checkin)
|
fields.Date.from_string(self.checkin)
|
||||||
- fields.Date.from_string(today)
|
- fields.Date.from_string(today)
|
||||||
).days
|
).days
|
||||||
if days_diff < 0:
|
if days_diff < 0:
|
||||||
@@ -1149,29 +1086,6 @@ class PmsReservation(models.Model):
|
|||||||
)
|
)
|
||||||
splitted_reservs.draft()
|
splitted_reservs.draft()
|
||||||
|
|
||||||
@api.model
|
|
||||||
def get_reservations(self, dfrom, dto):
|
|
||||||
"""
|
|
||||||
@param dfrom: range date from
|
|
||||||
@param dto: range date to (NO CHECKOUT, only night)
|
|
||||||
@return: array with the reservations _confirmed_ between both
|
|
||||||
dates `dfrom` and `dto`
|
|
||||||
"""
|
|
||||||
domain = self._get_domain_reservations_occupation(dfrom, dto)
|
|
||||||
# _logger.info(domain)
|
|
||||||
return self.env["pms.reservation"].search(domain)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def _get_domain_reservations_occupation(self, dfrom, dto):
|
|
||||||
domain = [
|
|
||||||
("reservation_line_ids.date", ">=", dfrom),
|
|
||||||
("reservation_line_ids.date", "<=", dto),
|
|
||||||
("state", "!=", "cancelled"),
|
|
||||||
("overbooking", "=", False),
|
|
||||||
("reselling", "=", False),
|
|
||||||
]
|
|
||||||
return domain
|
|
||||||
|
|
||||||
# INFO: This function is not in use and should include `dto` in the search
|
# INFO: This function is not in use and should include `dto` in the search
|
||||||
@api.model
|
@api.model
|
||||||
def get_reservations_dates(self, dfrom, dto, room_type=False):
|
def get_reservations_dates(self, dfrom, dto, room_type=False):
|
||||||
@@ -1252,183 +1166,10 @@ class PmsReservation(models.Model):
|
|||||||
action["target"] = "new"
|
action["target"] = "new"
|
||||||
return action
|
return action
|
||||||
|
|
||||||
def split(self, nights):
|
|
||||||
for record in self:
|
|
||||||
date_start_dt = fields.Date.from_string(record.checkin)
|
|
||||||
date_end_dt = fields.Date.from_string(record.checkout)
|
|
||||||
date_diff = abs((date_end_dt - date_start_dt).days)
|
|
||||||
new_start_date_dt = date_start_dt + timedelta(days=date_diff - nights)
|
|
||||||
if nights >= date_diff or nights < 1:
|
|
||||||
raise ValidationError(
|
|
||||||
_(
|
|
||||||
"Invalid Nights! Max is \
|
|
||||||
'%d'"
|
|
||||||
)
|
|
||||||
% (date_diff - 1)
|
|
||||||
)
|
|
||||||
|
|
||||||
vals = record.generate_copy_values(
|
|
||||||
new_start_date_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
|
||||||
date_end_dt.strftime(DEFAULT_SERVER_DATETIME_FORMAT),
|
|
||||||
)
|
|
||||||
# Days Price
|
|
||||||
reservation_lines = [[], []]
|
|
||||||
for rline in record.reservation_line_ids:
|
|
||||||
rline_dt = fields.Date.from_string(rline.date)
|
|
||||||
if rline_dt >= new_start_date_dt:
|
|
||||||
reservation_lines[1].append(
|
|
||||||
(
|
|
||||||
0,
|
|
||||||
False,
|
|
||||||
{
|
|
||||||
"date": rline.date,
|
|
||||||
"price": rline.price,
|
|
||||||
"cancel_discount": rline.cancel_discount,
|
|
||||||
"discount": rline.discount,
|
|
||||||
"move_line_ids": rline.move_line_ids,
|
|
||||||
"state": rline.state,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
reservation_lines[0].append((2, rline.id, False))
|
|
||||||
parent_res = record.parent_reservation or record
|
|
||||||
vals.update(
|
|
||||||
{
|
|
||||||
"splitted": True,
|
|
||||||
"parent_reservation": parent_res.id,
|
|
||||||
"room_type_id": parent_res.room_type_id.id,
|
|
||||||
"state": parent_res.state,
|
|
||||||
"reservation_line_ids": reservation_lines[1],
|
|
||||||
"preconfirm": False,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
reservation_copy = (
|
|
||||||
self.env["pms.reservation"]
|
|
||||||
.with_context({"ignore_avail_restrictions": True})
|
|
||||||
.create(vals)
|
|
||||||
)
|
|
||||||
if not reservation_copy:
|
|
||||||
raise ValidationError(
|
|
||||||
_(
|
|
||||||
"Unexpected error copying record. \
|
|
||||||
Can't split reservation!"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
record.write(
|
|
||||||
{
|
|
||||||
"checkout": new_start_date_dt.strftime(
|
|
||||||
DEFAULT_SERVER_DATETIME_FORMAT
|
|
||||||
),
|
|
||||||
"splitted": True,
|
|
||||||
"reservation_line_ids": reservation_lines[0],
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return True
|
|
||||||
|
|
||||||
def unify(self):
|
def unify(self):
|
||||||
self.ensure_one()
|
#TODO
|
||||||
if not self.splitted:
|
|
||||||
raise ValidationError(_("This reservation can't be unified"))
|
|
||||||
|
|
||||||
master_reservation = self.parent_reservation or self
|
|
||||||
|
|
||||||
splitted_reservs = self.env["pms.reservation"].search(
|
|
||||||
[
|
|
||||||
("splitted", "=", True),
|
|
||||||
("folio_id", "=", self.folio_id.id),
|
|
||||||
"|",
|
|
||||||
("parent_reservation", "=", master_reservation.id),
|
|
||||||
("id", "=", master_reservation.id),
|
|
||||||
]
|
|
||||||
)
|
|
||||||
self.unify_books(splitted_reservs)
|
|
||||||
|
|
||||||
self_is_master = master_reservation == self
|
|
||||||
if not self_is_master:
|
|
||||||
return {"type": "ir.actions.act_window_close"}
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def unify_ids(self, reserv_ids):
|
|
||||||
splitted_reservs = self.env[self._name].browse(reserv_ids)
|
|
||||||
self.unify_books(splitted_reservs)
|
|
||||||
|
|
||||||
@api.model
|
|
||||||
def unify_books(self, splitted_reservs):
|
|
||||||
parent_reservation = (
|
|
||||||
splitted_reservs[0].parent_reservation or splitted_reservs[0]
|
|
||||||
)
|
|
||||||
room_type_ids = splitted_reservs.mapped("room_type_id.id")
|
|
||||||
if len(room_type_ids) > 1 or (
|
|
||||||
len(room_type_ids) == 1
|
|
||||||
and parent_reservation.room_type_id.id != room_type_ids[0]
|
|
||||||
):
|
|
||||||
raise ValidationError(
|
|
||||||
_(
|
|
||||||
"This reservation can't be unified: They \
|
|
||||||
all need to be in the same nº room and room type"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Search checkout
|
|
||||||
last_checkout = splitted_reservs[0].checkout
|
|
||||||
first_checkin = splitted_reservs[0].checkin
|
|
||||||
master_reservation = splitted_reservs[0]
|
|
||||||
for reserv in splitted_reservs:
|
|
||||||
if last_checkout < reserv.checkout:
|
|
||||||
last_checkout = reserv.checkout
|
|
||||||
if first_checkin > reserv.checkin:
|
|
||||||
first_checkin = reserv.checkin
|
|
||||||
master_reservation = reserv
|
|
||||||
|
|
||||||
# Agrupate reservation lines
|
|
||||||
reservation_line_ids = splitted_reservs.mapped("reservation_line_ids")
|
|
||||||
reservation_line_ids.sorted(key=lambda r: r.date)
|
|
||||||
rlines = [(5, False, False)]
|
|
||||||
for rline in reservation_line_ids:
|
|
||||||
rlines.append(
|
|
||||||
(
|
|
||||||
0,
|
|
||||||
False,
|
|
||||||
{
|
|
||||||
"date": rline.date,
|
|
||||||
"price": rline.price,
|
|
||||||
"cancel_discount": rline.cancel_discount,
|
|
||||||
"discount": rline.discount,
|
|
||||||
"move_line_ids": rline.move_line_ids,
|
|
||||||
"state": rline.state,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
# Unify
|
|
||||||
osplitted_reservs = splitted_reservs - master_reservation
|
|
||||||
osplitted_reservs.sudo().unlink()
|
|
||||||
|
|
||||||
_logger.info("========== UNIFY")
|
|
||||||
_logger.info(master_reservation.real_checkin)
|
|
||||||
_logger.info(first_checkin)
|
|
||||||
_logger.info(master_reservation.real_checkout)
|
|
||||||
_logger.info(last_checkout)
|
|
||||||
|
|
||||||
master_reservation.write(
|
|
||||||
{
|
|
||||||
"checkout": last_checkout,
|
|
||||||
"splitted": master_reservation.real_checkin != first_checkin
|
|
||||||
or master_reservation.real_checkout != last_checkout,
|
|
||||||
"reservation_line_ids": rlines,
|
|
||||||
}
|
|
||||||
)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def open_master(self):
|
|
||||||
self.ensure_one()
|
|
||||||
if not self.parent_reservation:
|
|
||||||
raise ValidationError(_("This is the parent reservation"))
|
|
||||||
action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0]
|
|
||||||
action["views"] = [(self.env.ref("pms.pms_reservation_view_form").id, "form")]
|
|
||||||
action["res_id"] = self.parent_reservation.id
|
|
||||||
return action
|
|
||||||
|
|
||||||
def send_reservation_mail(self):
|
def send_reservation_mail(self):
|
||||||
return self.folio_id.send_reservation_mail()
|
return self.folio_id.send_reservation_mail()
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,15 @@ class PmsReservationLine(models.Model):
|
|||||||
required=True,
|
required=True,
|
||||||
copy=False,
|
copy=False,
|
||||||
)
|
)
|
||||||
|
room_id = fields.Many2one(
|
||||||
|
"pms.room",
|
||||||
|
string="Room",
|
||||||
|
ondelete="restrict",
|
||||||
|
compute="_compute_room_id",
|
||||||
|
store=True,
|
||||||
|
readonly=False,
|
||||||
|
domain="[('id', 'in', reservation_id.allowed_room_ids)]",
|
||||||
|
)
|
||||||
move_line_ids = fields.Many2many(
|
move_line_ids = fields.Many2many(
|
||||||
"account.move.line",
|
"account.move.line",
|
||||||
"reservation_line_move_rel",
|
"reservation_line_move_rel",
|
||||||
@@ -63,8 +72,27 @@ class PmsReservationLine(models.Model):
|
|||||||
readonly=False,
|
readonly=False,
|
||||||
)
|
)
|
||||||
discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0)
|
discount = fields.Float(string="Discount (%)", digits=("Discount"), default=0.0)
|
||||||
|
occupies_availability = fields.Boolean(
|
||||||
|
string = "Occupies",
|
||||||
|
compute="_compute_occupies_availability",
|
||||||
|
store=True,
|
||||||
|
help="This record is taken into account to calculate availability")
|
||||||
|
|
||||||
# Compute and Search methods
|
# Compute and Search methods
|
||||||
|
@api.depends(
|
||||||
|
"reservation_id.adults",
|
||||||
|
"reservation_id.room_type_id",
|
||||||
|
"reservation_id.room_id")
|
||||||
|
def _compute_room_id(self):
|
||||||
|
lines_no_room = self.filtered_domain([("room_id", "=", False)])
|
||||||
|
lines_with_room = self - lines_no_room
|
||||||
|
for line in lines_no_room:
|
||||||
|
if line.reservation_id.room_id:
|
||||||
|
line.room_id = line.reservation_id.room_id
|
||||||
|
# TODO: check_split reservation
|
||||||
|
# TODO: Allow with confirmation message to
|
||||||
|
# change de room if the user change the room_type?
|
||||||
|
|
||||||
@api.depends(
|
@api.depends(
|
||||||
"reservation_id",
|
"reservation_id",
|
||||||
"reservation_id.pricelist_id",
|
"reservation_id.pricelist_id",
|
||||||
@@ -99,6 +127,15 @@ class PmsReservationLine(models.Model):
|
|||||||
else:
|
else:
|
||||||
line.price = line._origin.price
|
line.price = line._origin.price
|
||||||
|
|
||||||
|
@api.depends("reservation_id.state", "reservation_id.overbooking")
|
||||||
|
def _compute_occupies_availability(self):
|
||||||
|
for line in self:
|
||||||
|
if line.reservation_id.state == "cancelled" or \
|
||||||
|
line.reservation_id.overbooking == True:
|
||||||
|
line.occupies_availability = False
|
||||||
|
else:
|
||||||
|
line.occupies_availability = True
|
||||||
|
|
||||||
def _recompute_price(self):
|
def _recompute_price(self):
|
||||||
#REVIEW: Conditional to avoid overriding already calculated prices,
|
#REVIEW: Conditional to avoid overriding already calculated prices,
|
||||||
# I'm not sure it's the best way
|
# I'm not sure it's the best way
|
||||||
@@ -131,10 +168,10 @@ class PmsReservationLine(models.Model):
|
|||||||
# and pricelist.cancelation_rule_id
|
# and pricelist.cancelation_rule_id
|
||||||
# ):
|
# ):
|
||||||
# date_start_dt = fields.Date.from_string(
|
# date_start_dt = fields.Date.from_string(
|
||||||
# reservation.real_checkin or reservation.checkin
|
# reservation.checkin
|
||||||
# )
|
# )
|
||||||
# date_end_dt = fields.Date.from_string(
|
# date_end_dt = fields.Date.from_string(
|
||||||
# reservation.real_checkout or reservation.checkout
|
# reservation.checkout
|
||||||
# )
|
# )
|
||||||
# days = abs((date_end_dt - date_start_dt).days)
|
# days = abs((date_end_dt - date_start_dt).days)
|
||||||
# rule = pricelist.cancelation_rule_id
|
# rule = pricelist.cancelation_rule_id
|
||||||
@@ -153,7 +190,7 @@ class PmsReservationLine(models.Model):
|
|||||||
# elif reservation.cancelled_reason == "intime":
|
# elif reservation.cancelled_reason == "intime":
|
||||||
# discount = 100
|
# discount = 100
|
||||||
|
|
||||||
# checkin = reservation.real_checkin or reservation.checkin
|
# checkin = reservation.checkin
|
||||||
# dates = []
|
# dates = []
|
||||||
# for i in range(0, days):
|
# for i in range(0, days):
|
||||||
# dates.append(
|
# dates.append(
|
||||||
|
|||||||
@@ -57,6 +57,14 @@ class PmsRoomType(models.Model):
|
|||||||
total_rooms_count = fields.Integer(compute="_compute_total_rooms", store=True)
|
total_rooms_count = fields.Integer(compute="_compute_total_rooms", store=True)
|
||||||
active = fields.Boolean("Active", default=True)
|
active = fields.Boolean("Active", default=True)
|
||||||
sequence = fields.Integer("Sequence", default=0)
|
sequence = fields.Integer("Sequence", default=0)
|
||||||
|
default_max_avail = fields.Integer("Max. Availability", default=-1,
|
||||||
|
help="Maximum simultaneous availability on own Booking Engine "
|
||||||
|
"given no availability rules. "
|
||||||
|
"Use `-1` for using maximum simultaneous availability.")
|
||||||
|
default_quota = fields.Integer("Default Quota", default=-1,
|
||||||
|
help="Quota assigned to the own Booking Engine given no availability rules. "
|
||||||
|
"Use `-1` for managing no quota.")
|
||||||
|
|
||||||
|
|
||||||
_sql_constraints = [
|
_sql_constraints = [
|
||||||
(
|
(
|
||||||
@@ -93,25 +101,6 @@ class PmsRoomType(models.Model):
|
|||||||
capacities = self.room_ids.mapped("capacity")
|
capacities = self.room_ids.mapped("capacity")
|
||||||
return min(capacities) if any(capacities) else 0
|
return min(capacities) if any(capacities) else 0
|
||||||
|
|
||||||
# TODO: Change name method by rooms_available()
|
|
||||||
@api.model
|
|
||||||
def check_availability_room_type(self, dfrom, dto, room_type_id=False, notthis=[]):
|
|
||||||
"""
|
|
||||||
Check the max availability for an specific
|
|
||||||
type of room in a range of dates
|
|
||||||
"""
|
|
||||||
reservations = self.env["pms.reservation"].get_reservations(dfrom, dto - timedelta(1))
|
|
||||||
reservations_rooms = reservations.mapped("room_id.id")
|
|
||||||
free_rooms = self.env["pms.room"].search(
|
|
||||||
[("id", "not in", reservations_rooms), ("id", "not in", notthis)]
|
|
||||||
) # TODO: Review if with the new caché V13 We need notthis []¿?
|
|
||||||
if room_type_id:
|
|
||||||
rooms_linked = (
|
|
||||||
self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids
|
|
||||||
)
|
|
||||||
free_rooms = free_rooms & rooms_linked
|
|
||||||
return free_rooms.sorted(key=lambda r: r.sequence)
|
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
def get_rate_room_types(self, **kwargs):
|
def get_rate_room_types(self, **kwargs):
|
||||||
"""
|
"""
|
||||||
|
|||||||
77
pms/models/pms_room_type_availability.py
Normal file
77
pms/models/pms_room_type_availability.py
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Copyright 2017 Alexandre Díaz
|
||||||
|
# Copyright 2017 Dario Lodeiros
|
||||||
|
# Copyright 2018 Pablo Quesada
|
||||||
|
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||||
|
from odoo import _, api, fields, models
|
||||||
|
from odoo.exceptions import ValidationError
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class PmsRoomTypeAvailability(models.Model):
|
||||||
|
_name = "pms.room.type.availability"
|
||||||
|
_description = "Availability"
|
||||||
|
_inherit = 'mail.thread'
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_max_avail(self):
|
||||||
|
return self.room_type_id.default_max_avail
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _default_quota(self):
|
||||||
|
return self.room_type_id.default_quota
|
||||||
|
|
||||||
|
# Fields declaration
|
||||||
|
room_type_id = fields.Many2one('hotel.room.type', 'Room Type',
|
||||||
|
required=True,
|
||||||
|
ondelete='cascade')
|
||||||
|
date = fields.Date('Date', required=True, track_visibility='always')
|
||||||
|
quota = fields.Integer("Quota", default=_default_quota,
|
||||||
|
track_visibility='always',
|
||||||
|
help="Generic Quota assigned.")
|
||||||
|
max_avail = fields.Integer("Max. Availability", default=-1, readonly=True,
|
||||||
|
track_visibility='always',
|
||||||
|
help="Maximum simultaneous availability on own Booking Engine.")
|
||||||
|
no_web = fields.Boolean('No Web', default=False,
|
||||||
|
track_visibility='onchange',
|
||||||
|
help="Set zero availability to the own Booking Engine "
|
||||||
|
"even when the availability is positive,")
|
||||||
|
|
||||||
|
_sql_constraints = [
|
||||||
|
(
|
||||||
|
"unique_availability_room_type_rule_date",
|
||||||
|
"unique(room_type_id, date)",
|
||||||
|
"The availability rule for this date in this room type already exists, "
|
||||||
|
"modify it instead of trying to create a new one",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Business Methods
|
||||||
|
@api.model
|
||||||
|
def rooms_available(self, checkin, checkout, room_type_id=False, current_lines=False):
|
||||||
|
domain = self._get_domain_reservations_occupation(
|
||||||
|
dfrom=checkin,
|
||||||
|
dto=checkout - timedelta(1),
|
||||||
|
current_lines=current_lines,
|
||||||
|
)
|
||||||
|
reservation_lines = self.env['pms.reservation.line'].search(domain)
|
||||||
|
reservations_rooms = reservation_lines.mapped("room_id.id")
|
||||||
|
free_rooms = self.env["pms.room"].search(
|
||||||
|
[("id", "not in", reservations_rooms)]
|
||||||
|
)
|
||||||
|
if room_type_id:
|
||||||
|
rooms_linked = (
|
||||||
|
self.env["pms.room.type"].search([("id", "=", room_type_id)]).room_ids
|
||||||
|
)
|
||||||
|
free_rooms = free_rooms & rooms_linked
|
||||||
|
return free_rooms.sorted(key=lambda r: r.sequence)
|
||||||
|
|
||||||
|
@api.model
|
||||||
|
def _get_domain_reservations_occupation(self, dfrom, dto, current_lines=False):
|
||||||
|
domain = [
|
||||||
|
("date", ">=", dfrom),
|
||||||
|
("date", "<=", dto),
|
||||||
|
("occupies_availability", "=", True),
|
||||||
|
("id","not in", current_lines),
|
||||||
|
]
|
||||||
|
return domain
|
||||||
@@ -22,8 +22,8 @@ user_access_pms_board_service_line,user_access_pms_board_service_line,model_pms_
|
|||||||
user_access_account_partial_reconcile,user_access_account_partial_reconcile,account.model_account_partial_reconcile,pms.group_pms_user,1,1,1,1
|
user_access_account_partial_reconcile,user_access_account_partial_reconcile,account.model_account_partial_reconcile,pms.group_pms_user,1,1,1,1
|
||||||
user_access_pms_cancelation_rule,user_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_user,1,0,0,0
|
user_access_pms_cancelation_rule,user_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_user,1,0,0,0
|
||||||
user_access_account_full_reconcile,user_access_account_full_reconcile,account.model_account_full_reconcile,pms.group_pms_user,1,1,1,1
|
user_access_account_full_reconcile,user_access_account_full_reconcile,account.model_account_full_reconcile,pms.group_pms_user,1,1,1,1
|
||||||
call_access_pms_cancelation_rule,call_access_pms_cancelation_rule,model_pms_cancelation_rule,base.group_user,1,0,0,0
|
|
||||||
user_access_property,user_access_property,model_pms_property,pms.group_pms_user,1,0,0,0
|
user_access_property,user_access_property,model_pms_property,pms.group_pms_user,1,0,0,0
|
||||||
|
user_access_availability,user_access_availability,model_pms_room_type_availability,pms.group_pms_user,1,0,0,0
|
||||||
manager_access_pms_floor,manager_access_pms_floor,model_pms_floor,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_floor,manager_access_pms_floor,model_pms_floor,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_amenity,manager_access_pms_amenity,model_pms_amenity,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_amenity,manager_access_pms_amenity,model_pms_amenity,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_amenity_type,manager_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_amenity_type,manager_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_manager,1,1,1,1
|
||||||
@@ -45,25 +45,5 @@ manager_access_pms_board_service_room_type,manager_access_pms_board_service_room
|
|||||||
manager_access_pms_board_service_room_type_line,manager_access_pms_board_service_room_type_line,model_pms_board_service_room_type_line,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_board_service_room_type_line,manager_access_pms_board_service_room_type_line,model_pms_board_service_room_type_line,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_board_service_line,manager_access_pms_board_service_line,model_pms_board_service_line,pms.group_pms_manager,1,1,1,1
|
manager_access_pms_board_service_line,manager_access_pms_board_service_line,model_pms_board_service_line,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_property,manager_access_property,model_pms_property,pms.group_pms_manager,1,1,1,1
|
manager_access_property,manager_access_property,model_pms_property,pms.group_pms_manager,1,1,1,1
|
||||||
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,base.group_user,1,1,1,1
|
manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1
|
||||||
call_access_pms_floor,call_access_pms_floor,model_pms_floor,pms.group_pms_call,1,0,0,0
|
user_access_availability,user_access_availability,model_pms_room_type_availability,pms.group_pms_manager,1,1,1,1
|
||||||
call_access_pms_amenity,call_access_pms_amenity,model_pms_amenity,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_amenity_type,call_access_pms_amenity_type,model_pms_amenity_type,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_service,call_access_pms_service,model_pms_service,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_pms_room_type_restriction,call_access_pms_room_type_restriction,model_pms_room_type_restriction,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_reservation_line,call_access_pms_reservation_line,model_pms_reservation_line,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_room_closure_reason,call_access_room_closure_reason,model_room_closure_reason,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_service_line,call_access_pms_service_line,model_pms_service_line,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_pms_board_service,call_access_pms_board_service,model_pms_board_service,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_checkin_partner,call_access_pms_checkin_partner,model_pms_checkin_partner,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_pms_room_type_class,call_access_pms_room_type_class,model_pms_room_type_class,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_room,call_access_pms_room,model_pms_room,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_shared_room,call_access_pms_shared_room,model_pms_shared_room,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_room_type_restriction_item,call_access_pms_room_type_restriction_item,model_pms_room_type_restriction_item,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_reservation,call_access_pms_reservation,model_pms_reservation,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_pms_folio,call_access_pms_folio,model_pms_folio,pms.group_pms_call,1,1,1,1
|
|
||||||
call_access_pms_room_type,call_access_pms_room_type,model_pms_room_type,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_board_service_room_type,call_access_pms_board_service_room_type,model_pms_board_service_room_type,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_board_service_room_type_line,call_access_pms_board_service_room_type_line,model_pms_board_service_room_type_line,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_pms_board_service_line,call_access_pms_board_service_line,model_pms_board_service_line,pms.group_pms_call,1,0,0,0
|
|
||||||
call_access_property,call_access_property,model_pms_property,pms.group_pms_call,1,0,0,0
|
|
||||||
|
|||||||
|
@@ -53,7 +53,7 @@
|
|||||||
<t t-esc="r.room_type_id.name" />
|
<t t-esc="r.room_type_id.name" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<t t-esc="r.real_checkin" />
|
<t t-esc="r.checkin" />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<t t-esc="r.nights" />
|
<t t-esc="r.nights" />
|
||||||
|
|||||||
@@ -380,7 +380,7 @@ class PmsRoomTypeWizards(models.TransientModel):
|
|||||||
[("room_type_id", "=", res.room_type_id.id)]
|
[("room_type_id", "=", res.room_type_id.id)]
|
||||||
)
|
)
|
||||||
real_max = len(
|
real_max = len(
|
||||||
res.room_type_id.check_availability_room_type(
|
self.env["pms.room.type.availability"].rooms_available(
|
||||||
res.checkin,
|
res.checkin,
|
||||||
(
|
(
|
||||||
fields.Date.from_string(res.checkout) - timedelta(days=1)
|
fields.Date.from_string(res.checkout) - timedelta(days=1)
|
||||||
|
|||||||
Reference in New Issue
Block a user