Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 49 additions & 1 deletion pms/models/pms_availability.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,10 +85,20 @@ class PmsAvailability(models.Model):
)
]

# Note: a dependency on ``room_type_id.total_rooms_count`` is
# deliberately NOT declared here. That field is itself a stored
# compute on ``pms.room_type`` whose value changes whenever any of
# the room_type's ``room_ids`` toggles ``active`` or moves to a
# different ``room_type_id``. Declaring it as a dependency caused
# every such room change to trigger a recompute of EVERY
# ``pms.availability`` row tied to the affected room type(s) —
# historical rows included — which locked the table for minutes on
# a busy property. Instead, ``pms.room.write/create/unlink``
# triggers a SCOPED, future-only recompute via
# ``_recompute_real_avail_for_room_change`` below.
@api.depends(
"reservation_line_ids",
"reservation_line_ids.occupies_availability",
"room_type_id.total_rooms_count",
"reservation_line_ids.room_id",
"reservation_line_ids.date",
"parent_avail_id",
Expand Down Expand Up @@ -118,6 +128,44 @@ def _compute_real_avail(self):
)
record.real_avail = len(room_ids) - count_rooms_not_avail

@api.model
def _recompute_real_avail_for_room_change(
self, pms_property_ids, room_type_ids, date_from=None
):
"""Trigger a scoped recompute of ``real_avail`` after a
``pms.room`` change (active toggle, ``room_type_id`` move,
new/deleted room).

Limits the recompute to (property, room_type) pairs in the
given sets and to dates ``>= date_from`` (defaults to today).
Replaces the wider ``room_type_id.total_rooms_count`` automatic
dependency — see the comment on ``_compute_real_avail`` for the
rationale.

:param pms_property_ids: iterable of ``pms.property`` ids whose
rooms changed.
:param room_type_ids: iterable of ``pms.room.type`` ids whose
total room count is affected (use both OLD and NEW types
when handling a re-segmentation).
:param date_from: optional lower-bound date for the recompute
scope. Defaults to today.
"""
property_ids = [pid for pid in pms_property_ids if pid]
room_type_ids = [rid for rid in room_type_ids if rid]
if not property_ids or not room_type_ids:
return
if date_from is None:
date_from = fields.Date.today()
avails = self.search(
[
("pms_property_id", "in", property_ids),
("room_type_id", "in", room_type_ids),
("date", ">=", date_from),
]
)
if avails:
self.env.add_to_compute(self._fields["real_avail"], avails)

@api.depends("reservation_line_ids", "reservation_line_ids.room_id")
def _compute_parent_avail_id(self):
for record in self:
Expand Down
49 changes: 47 additions & 2 deletions pms/models/pms_room.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,23 @@ def _check_short_name(self):
_("The short name can't contain more than 4 characters")
)

# Fields whose change on a room shifts the room_type's effective
# room count for one or more (property, room_type) pairs and so
# invalidates ``pms.availability.real_avail`` for FUTURE dates.
_PMS_AVAIL_RELEVANT_FIELDS = {
"active",
"room_type_id",
"pms_property_id",
"parent_id",
}

def _trigger_avail_recompute(self, property_ids, room_type_ids):
if not property_ids or not room_type_ids:
return
self.env["pms.availability"]._recompute_real_avail_for_room_change(
property_ids, room_type_ids
)

@api.model_create_multi
def create(self, vals_list):
for vals in vals_list:
Expand All @@ -334,7 +351,12 @@ def create(self, vals_list):
vals.update({"short_name": short_name})
else:
vals.update({"short_name": vals["name"]})
return super().create(vals_list)
records = super().create(vals_list)
records._trigger_avail_recompute(
records.mapped("pms_property_id").ids,
records.mapped("room_type_id").ids,
)
return records

def write(self, vals):
if vals.get("name") and not vals.get("short_name"):
Expand All @@ -343,7 +365,30 @@ def write(self, vals):
vals.update({"short_name": short_name})
else:
vals.update({"short_name": vals["name"]})
return super().write(vals)
relevant_change = bool(set(vals) & self._PMS_AVAIL_RELEVANT_FIELDS)
old_property_ids = old_room_type_ids = []
if relevant_change:
old_property_ids = self.mapped("pms_property_id").ids
old_room_type_ids = self.mapped("room_type_id").ids
result = super().write(vals)
if relevant_change:
new_property_ids = self.mapped("pms_property_id").ids
new_room_type_ids = self.mapped("room_type_id").ids
# Union: a re-segmentation moves a room out of one type and
# into another, so BOTH the old and new (property, type)
# pairs need their future avail recomputed.
self._trigger_avail_recompute(
list(set(old_property_ids) | set(new_property_ids)),
list(set(old_room_type_ids) | set(new_room_type_ids)),
)
return result

def unlink(self):
property_ids = self.mapped("pms_property_id").ids
room_type_ids = self.mapped("room_type_id").ids
result = super().unlink()
self.env["pms.room"]._trigger_avail_recompute(property_ids, room_type_ids)
return result

def calculate_short_name(self, vals):
short_name = vals["name"][:2].upper()
Expand Down
Loading