diff --git a/pms/__manifest__.py b/pms/__manifest__.py
index 4722890c1..e28274f0f 100644
--- a/pms/__manifest__.py
+++ b/pms/__manifest__.py
@@ -49,6 +49,7 @@
"wizards/pms_booking_engine_views.xml",
"wizards/wizard_folio_changes.xml",
"wizards/wizard_several_partners.xml",
+ "wizards/pms_booking_duplicate_views.xml",
"views/pms_amenity_views.xml",
"views/pms_amenity_type_views.xml",
"views/pms_board_service_views.xml",
diff --git a/pms/security/ir.model.access.csv b/pms/security/ir.model.access.csv
index 0cf1b3a73..3902f2d7f 100644
--- a/pms/security/ir.model.access.csv
+++ b/pms/security/ir.model.access.csv
@@ -64,3 +64,5 @@ user_access_pms_automated_mails,user_access_pms_automated_mails,model_pms_automa
access_pms_several_partners_wizard,access_pms_several_partners_wizard,model_pms_several_partners_wizard,base.group_user,1,1,1,1
user_access_res_partner_portal,user_access_res_partner_portal,model_res_partner,base.group_portal,1,1,1,1
user_access_pms_precheckin_portal,user_access_pms_precheckin_portal,model_pms_checkin_partner,base.group_portal,1,1,1,1
+user_access_pms_booking_duplicate,user_access_pms_booking_duplicate,model_pms_booking_duplicate,pms.group_pms_user,1,1,1,1
+user_access_pms_reservation_duplicate,user_access_pms_reservation_duplicate,model_pms_reservation_duplicate,pms.group_pms_user,1,1,1,1
diff --git a/pms/views/pms_folio_views.xml b/pms/views/pms_folio_views.xml
index cf6f4e163..36d35970a 100644
--- a/pms/views/pms_folio_views.xml
+++ b/pms/views/pms_folio_views.xml
@@ -176,6 +176,15 @@
>
New Booking Group
+
-
-
-
-
+
+
+
+
+
+
+
+
+
-
-
+
+
-
diff --git a/pms/wizards/__init__.py b/pms/wizards/__init__.py
index f6f5db47e..42e35d4e5 100644
--- a/pms/wizards/__init__.py
+++ b/pms/wizards/__init__.py
@@ -6,3 +6,4 @@ from . import folio_make_invoice_advance
from . import wizard_payment_folio
from . import wizard_folio_changes
from . import wizard_several_partners
+from . import pms_booking_duplicate
diff --git a/pms/wizards/pms_booking_duplicate.py b/pms/wizards/pms_booking_duplicate.py
new file mode 100644
index 000000000..0284a2ac4
--- /dev/null
+++ b/pms/wizards/pms_booking_duplicate.py
@@ -0,0 +1,535 @@
+import datetime
+
+from odoo import _, api, fields, models
+
+
+class BookingDuplicate(models.TransientModel):
+ _name = "pms.booking.duplicate"
+ _description = "Duplicate Booking"
+ _check_pms_properties_auto = True
+
+ reference_folio_id = fields.Many2one(
+ string="Folio Reference",
+ help="Folio to copy data",
+ comodel_name="pms.folio",
+ check_pms_properties=True,
+ )
+ start_date = fields.Date(
+ string="From:",
+ help="Date from first copy Checkin (reference min checkin folio reservation)",
+ required=True,
+ )
+
+ pricelist_id = fields.Many2one(
+ string="Pricelist",
+ help="Pricelist applied in folio",
+ readonly=False,
+ store=True,
+ comodel_name="product.pricelist",
+ compute="_compute_pricelist_id",
+ check_pms_properties=True,
+ )
+ pms_property_id = fields.Many2one(
+ related="reference_folio_id.pms_property_id",
+ string="Property",
+ help="Property to which the folio belongs",
+ comodel_name="pms.property",
+ check_pms_properties=True,
+ )
+ segmentation_ids = fields.Many2many(
+ string="Segmentation",
+ help="Partner Tags",
+ ondelete="restrict",
+ comodel_name="res.partner.category",
+ compute="_compute_segmentation_ids",
+ store=True,
+ readonly=False,
+ )
+ partner_name = fields.Char(
+ string="Partner name",
+ help="In whose name is the reservation",
+ compute="_compute_partner_name",
+ readonly=False,
+ store=True,
+ )
+ partner_id = fields.Many2one(
+ string="Partner",
+ help="Partner who made the reservation",
+ comodel_name="res.partner",
+ compute="_compute_partner_id",
+ readonly=False,
+ store=True,
+ check_pms_properties=True,
+ )
+
+ reservation_type = fields.Selection(
+ string="Type",
+ help="The type of the reservation. "
+ "Can be 'Normal', 'Staff' or 'Out of Service'",
+ selection=[("normal", "Normal"), ("staff", "Staff"), ("out", "Out of Service")],
+ compute="_compute_reservation_type",
+ readonly=False,
+ store=True,
+ )
+ agency_id = fields.Many2one(
+ string="Agency",
+ help="Agency that made the reservation",
+ comodel_name="res.partner",
+ domain=[("is_agency", "=", True)],
+ ondelete="restrict",
+ )
+ channel_type_id = fields.Many2one(
+ string="Direct Sale Channel",
+ help="Sales Channel through which the reservation was managed",
+ readonly=False,
+ store=True,
+ comodel_name="pms.sale.channel",
+ domain=[("channel_type", "=", "direct")],
+ ondelete="restrict",
+ compute="_compute_channel_type_id",
+ )
+ total_price_folio = fields.Float(
+ string="Total Price",
+ help="Total price of folio with taxes",
+ compute="_compute_total_price_folio",
+ )
+ discount = fields.Float(
+ string="Discount",
+ help="Discount that be applied in total price",
+ default=0,
+ )
+ internal_comment = fields.Text(
+ string="Internal Folio Notes",
+ help="Internal Folio notes for Staff",
+ )
+ created_folio_ids = fields.Many2many(
+ string="Folios",
+ help="Folios already created",
+ comodel_name="pms.folio",
+ )
+ rooms = fields.One2many(
+ string="Rooms",
+ help="Rooms to create",
+ readonly=False,
+ store=True,
+ comodel_name="pms.reservation.duplicate",
+ inverse_name="booking_duplicate_id",
+ compute="_compute_rooms",
+ check_pms_properties=True,
+ )
+ recompute_prices = fields.Boolean(
+ string="Recompute Price",
+ help="""Leave unchecked if you want to respect
+ the price of the original reservation regardless
+ of what is marked in the rate""",
+ default=False,
+ )
+
+ @api.depends("reference_folio_id")
+ def _compute_pricelist_id(self):
+ for record in self.filtered("reference_folio_id"):
+ if not record.pricelist_id:
+ record.pricelist_id = record.reference_folio_id.pricelist_id.id
+
+ @api.depends("reference_folio_id", "agency_id")
+ def _compute_channel_type_id(self):
+ for record in self.filtered("reference_folio_id"):
+ if record.reference_folio_id.agency_id == record.agency_id:
+ record.channel_type_id = record.reference_folio_id.channel_type_id
+ elif record.agency_id:
+ record.channel_type_id = record.agency_id.sale_channel_id.id
+
+ @api.depends("reference_folio_id")
+ def _compute_segmentation_ids(self):
+ for record in self:
+ record.segmentation_ids = record.reference_folio_id.segmentation_ids
+
+ @api.depends("agency_id", "reference_folio_id")
+ def _compute_partner_id(self):
+ for record in self:
+ if record.reference_folio_id.agency_id == record.agency_id:
+ record.partner_id = record.reference_folio_id.partner_id
+ elif record.agency_id and record.agency_id.invoice_to_agency:
+ record.partner_id = record.agency_id.id
+ elif not record.partner_id:
+ record.partner_id = False
+
+ @api.depends("reference_folio_id")
+ def _compute_reservation_type(self):
+ self.reservation_type = "normal"
+ for record in self:
+ record.reservation_type = record.reference_folio_id.reservation_type
+
+ @api.depends("partner_id")
+ def _compute_partner_name(self):
+ for record in self:
+ if record.reference_folio_id.partner_id == record.partner_id:
+ record.partner_name = record.reference_folio_id.partner_name
+ elif record.partner_id:
+ record.partner_name = record.partner_id.name
+ if (
+ record.agency_id
+ and not record.agency_id.invoice_to_agency
+ and not record.partner_name
+ ):
+ record.partner_name = _("Reservation from ") + record.agency_id.name
+ elif not record.partner_name:
+ record.partner_name = False
+
+ @api.depends("rooms.price_total")
+ def _compute_total_price_folio(self):
+ for record in self:
+ record.total_price_folio = 0
+ for line in record.rooms:
+ record.total_price_folio += line.price_total
+ record.total_price_folio = record.total_price_folio
+
+ @api.depends(
+ "reference_folio_id",
+ )
+ def _compute_rooms(self):
+ self.ensure_one()
+ reference_folio = self.reference_folio_id
+
+ if not reference_folio:
+ self.rooms = False
+ return
+
+ cmds = [(5, 0)]
+
+ for reservation in reference_folio.reservation_ids.filtered(
+ lambda r: r.state != "cancel"
+ ):
+ cmds.append(
+ (
+ 0,
+ 0,
+ {
+ "reference_reservation_id": reservation.id,
+ "booking_duplicate_id": self.id,
+ "checkin": False,
+ "checkout": False,
+ "preferred_room_id": reservation.preferred_room_id,
+ "room_type_id": reservation.room_type_id,
+ # "arrival_hour": reservation.arrival_hour,
+ # "departure_hour": reservation.departure_hour,
+ # "partner_internal_comment": reservation.partner_internal_comment,
+ "board_service_room_id": reservation.board_service_room_id.id,
+ "adults": reservation.adults,
+ },
+ )
+ )
+ self.rooms = cmds
+
+ def create_and_new(self):
+ self.create_folio()
+ return {
+ "name": _("Duplicate Folios"),
+ "res_model": "pms.booking.duplicate",
+ "type": "ir.actions.act_window",
+ "view_id": self.env.ref("pms.booking_duplicate").id,
+ "target": "new",
+ "view_mode": "form",
+ "context": {
+ "default_reference_folio_id": self.reference_folio_id.id,
+ "default_created_folio_ids": [(6, 0, self.created_folio_ids.ids)],
+ "default_start_date": self.start_date,
+ },
+ }
+
+ def create_and_close(self):
+ self.create_folio()
+ folio_ids = self.mapped("created_folio_ids.id")
+ action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0]
+ if len(folio_ids) > 1:
+ action["domain"] = [("id", "in", folio_ids)]
+ elif len(folio_ids) == 1:
+ form_view = [(self.env.ref("pms.pms_folio_view_form").id, "form")]
+ if "views" in action:
+ action["views"] = form_view + [
+ (state, view) for state, view in action["views"] if view != "form"
+ ]
+ else:
+ action["views"] = form_view
+ action["res_id"] = folio_ids[0]
+ else:
+ action = {"type": "ir.actions.act_window_close"}
+ return action
+
+ def view_folios(self):
+ folio_ids = self.mapped("created_folio_ids.id")
+ action = self.env.ref("pms.open_pms_folio1_form_tree_all").read()[0]
+ if len(folio_ids) > 1:
+ action["domain"] = [("id", "in", folio_ids)]
+ elif len(folio_ids) == 1:
+ form_view = [(self.env.ref("pms.pms_folio_view_form").id, "form")]
+ if "views" in action:
+ action["views"] = form_view + [
+ (state, view) for state, view in action["views"] if view != "form"
+ ]
+ else:
+ action["views"] = form_view
+ action["res_id"] = folio_ids[0]
+ else:
+ action = {"type": "ir.actions.act_window_close"}
+ return action
+
+ def create_folio(self):
+ folio = self.env["pms.folio"].create(
+ {
+ "reservation_type": self.reservation_type,
+ "pricelist_id": self.pricelist_id.id,
+ "partner_id": self.partner_id.id if self.partner_id else False,
+ "partner_name": self.partner_name,
+ "pms_property_id": self.pms_property_id.id,
+ "agency_id": self.agency_id.id,
+ "channel_type_id": self.channel_type_id.id,
+ "segmentation_ids": [(6, 0, self.segmentation_ids.ids)],
+ "internal_comment": self.internal_comment,
+ }
+ )
+ for res in self.rooms:
+ res_vals = {
+ "folio_id": folio.id,
+ "checkin": res.checkin,
+ "checkout": res.checkout,
+ "room_type_id": res.room_type_id.id,
+ "partner_id": self.partner_id.id if self.partner_id else False,
+ "partner_name": self.partner_name,
+ "pricelist_id": self.pricelist_id.id,
+ "pms_property_id": folio.pms_property_id.id,
+ "board_service_room_id": res.board_service_room_id.id,
+ "adults": res.adults,
+ }
+ ser_vals = [(5, 0)]
+ for service in res.reference_reservation_id.service_ids.filtered(
+ lambda s: not s.is_board_service
+ ):
+ ser_line_vals = [(5, 0)]
+ if service.product_id.id in res.service_ids.ids:
+ for ser_line in service.service_line_ids:
+ ser_line_vals.append(
+ (
+ 0,
+ 0,
+ {
+ "day_qty": ser_line.day_qty,
+ "price_unit": ser_line.price_unit,
+ "discount": ser_line.discount,
+ "date": ser_line.date
+ + datetime.timedelta(
+ days=(
+ res.reference_reservation_id.checkin
+ - self.start_date
+ ).days
+ )
+ if service.per_day
+ else fields.Date.today(),
+ },
+ )
+ )
+ ser_vals.append(
+ (
+ 0,
+ 0,
+ {
+ "product_id": service.product_id.id,
+ "is_board_service": service.is_board_service,
+ "service_line_ids": ser_line_vals,
+ },
+ )
+ )
+ res_vals["service_ids"] = ser_vals
+
+ if not self.recompute_prices:
+ line_vals = [(5, 0)]
+ for line in res.reference_reservation_id.reservation_line_ids:
+ line_vals.append(
+ (
+ 0,
+ 0,
+ {
+ "price": line.price,
+ "discount": line.discount,
+ "room_id": line.room_id.id,
+ "date": line.date
+ + datetime.timedelta(
+ days=(
+ res.reference_reservation_id.checkin
+ - self.start_date
+ ).days
+ ),
+ },
+ )
+ )
+ res_vals["reservation_line_ids"] = line_vals
+ new_reservation = self.env["pms.reservation"].create(res_vals)
+ # REVIEW: Board service overwrite prices
+ for service in new_reservation.service_ids.filtered("is_board_service"):
+ origin_services_board = (
+ res.reference_reservation_id.service_ids.filtered(
+ "is_board_service"
+ )
+ )
+ if origin_services_board:
+ service.service_line_ids.price = (
+ origin_services_board.service_line_ids[0].price
+ )
+ self.created_folio_ids = [(4, folio.id)]
+
+
+class PmsReservationDuplicate(models.TransientModel):
+ _name = "pms.reservation.duplicate"
+ _description = "Rooms in Duplicate Folio"
+ _check_pms_properties_auto = True
+
+ reference_reservation_id = fields.Many2one(
+ string="Reservation Reference",
+ help="Reservation to copy data",
+ comodel_name="pms.reservation",
+ check_pms_properties=True,
+ )
+ adults = fields.Integer(string="Adults")
+ booking_duplicate_id = fields.Many2one(
+ string="Folio Wizard ID",
+ comodel_name="pms.booking.duplicate",
+ )
+ checkin = fields.Date(
+ string="From:", help="Date Reservation starts ", compute="_compute_checkin"
+ )
+ checkout = fields.Date(
+ string="To:",
+ help="Date Reservation ends",
+ compute="_compute_checkout",
+ )
+ room_type_id = fields.Many2one(
+ string="Room Type",
+ help="Room Type reserved",
+ comodel_name="pms.room.type",
+ check_pms_properties=True,
+ )
+ preferred_room_id = fields.Many2one(
+ string="Room",
+ help="Room reserved",
+ comodel_name="pms.room",
+ check_pms_properties=True,
+ )
+ allowed_room_ids = fields.Many2many(
+ string="Allowed Rooms",
+ help="It contains all available rooms for this reservation",
+ comodel_name="pms.room",
+ compute="_compute_allowed_room_ids",
+ )
+ available = fields.Boolean(
+ string="Available room",
+ store="true",
+ compute="_compute_available",
+ )
+ price_total = fields.Float(
+ string="Total price",
+ help="The total price in the folio",
+ compute="_compute_price_total",
+ )
+ pms_property_id = fields.Many2one(
+ string="Property",
+ help="Propertiy with access to the element;",
+ related="booking_duplicate_id.pms_property_id",
+ )
+ board_service_room_id = fields.Many2one(
+ string="Board Service",
+ help="Board Service included in the room",
+ comodel_name="pms.board.service.room.type",
+ domain="[('pms_room_type_id','=',room_type_id)]",
+ check_pms_properties=True,
+ )
+ service_ids = fields.Many2many(
+ string="Services",
+ comodel_name="product.product",
+ relation="reservation_duplicate_product_rel",
+ column1="reservation_duplicate_id",
+ column2="product_id",
+ compute="_compute_service_ids",
+ readonly=False,
+ store=True,
+ )
+
+ @api.depends("booking_duplicate_id.start_date")
+ def _compute_checkin(self):
+ self.checkin = False
+ start_date = self.booking_duplicate_id.start_date
+ if start_date:
+ checkin_ref = min(
+ self.booking_duplicate_id.mapped(
+ "reference_folio_id.reservation_ids.checkin"
+ )
+ )
+ for record in self:
+ if record.reference_reservation_id.checkin == checkin_ref:
+ record.checkin = start_date
+ else:
+ dif_days = (
+ record.reference_reservation_id.checkin - checkin_ref
+ ).nights
+ record.checkin = start_date + datetime.timedelta(days=dif_days)
+
+ @api.depends("checkin")
+ def _compute_checkout(self):
+ self.checkout = False
+ for record in self.filtered("checkin"):
+ res_days = record.reference_reservation_id.nights
+ record.checkout = record.checkin + datetime.timedelta(days=res_days)
+
+ @api.depends("preferred_room_id", "checkin", "checkout")
+ def _compute_available(self):
+ self.available = True
+ for record in self:
+ lines = self.env["pms.reservation.line"].search(
+ [
+ ("date", ">=", record.checkin),
+ ("date", "<", record.checkout),
+ ("occupies_availability", "=", True),
+ ]
+ )
+ if lines:
+ record.available = False
+
+ @api.depends(
+ "checkin",
+ "checkout",
+ "preferred_room_id",
+ "pms_property_id",
+ )
+ def _compute_allowed_room_ids(self):
+ self.allowed_room_ids = False
+ for reservation in self.filtered(lambda r: r.checkin and r.checkout):
+ pms_property = reservation.pms_property_id
+ pms_property = pms_property.with_context(
+ checkin=reservation.checkin,
+ checkout=reservation.checkout,
+ room_type_id=False, # Allows to choose any available room
+ pricelist_id=reservation.pricelist_id.id,
+ class_id=reservation.room_type_id.class_id.id
+ if reservation.room_type_id
+ else False,
+ real_avail=True,
+ )
+ allowed_room_ids = (
+ pms_property.free_room_ids.ids
+ - reservation.booking_duplicate_id.room_ids.mapped(
+ "preferred_room_id.id"
+ )
+ )
+ reservation.allowed_room_ids = self.env["room.id"].browse(allowed_room_ids)
+
+ @api.depends("room_type_id", "board_service_room_id", "checkin", "checkout")
+ def _compute_price_total(self):
+ self.price_total = 0
+ for record in self.filtered("checkout"):
+ record.price_total = record.reference_reservation_id.price_room_services_set
+
+ @api.depends("reference_reservation_id")
+ def _compute_service_ids(self):
+ for record in self:
+ record.service_ids = list(
+ set(record.reference_reservation_id.service_ids.mapped("product_id.id"))
+ )
diff --git a/pms/wizards/pms_booking_duplicate_views.xml b/pms/wizards/pms_booking_duplicate_views.xml
new file mode 100644
index 000000000..aaf2821bf
--- /dev/null
+++ b/pms/wizards/pms_booking_duplicate_views.xml
@@ -0,0 +1,181 @@
+
+
+
+ Duplicate Folio
+ pms.booking.duplicate
+
+
+
+
+
+
+ Folio creation
+ ir.actions.act_window
+ pms.booking.duplicate
+
+ form
+ new
+
+
+
diff --git a/pms/wizards/wizard_folio_changes.py b/pms/wizards/wizard_folio_changes.py
index 06a7c54c8..1136fe7ea 100644
--- a/pms/wizards/wizard_folio_changes.py
+++ b/pms/wizards/wizard_folio_changes.py
@@ -1,4 +1,6 @@
-from odoo import _, api, fields, models
+from datetime import timedelta
+
+from odoo import api, fields, models
class WizardFolioChanges(models.TransientModel):
@@ -11,6 +13,40 @@ class WizardFolioChanges(models.TransientModel):
default=lambda self: self._default_folio_id(),
comodel_name="pms.folio",
)
+ modification_type = fields.Selection(
+ string="Modification Type",
+ selection=[
+ ("reservations", "Reservations"),
+ ("dates", "Dates"),
+ ("services", "Services Prices"),
+ ],
+ default="reservations",
+ )
+ room_type_filter_ids = fields.Many2many(
+ string="Room types",
+ default=lambda self: self._default_room_type_filter_ids(),
+ comodel_name="pms.room.type",
+ relation="folio_changes_room_type_rel",
+ column1="folio_changes_id",
+ column2="room_type_ids",
+ domain="[('id', 'in', allowed_room_type_ids)]",
+ )
+ allowed_room_type_ids = fields.Many2many(
+ string="Allowed Room Types",
+ comodel_name="pms.room.type",
+ relation="folio_changes_allowed_room_type_rel",
+ column1="folio_changes_id",
+ column2="allowed_room_type_ids",
+ compute="_compute_allowed_room_type_ids",
+ )
+ change_from_date = fields.Date(
+ string="Apply From",
+ default=lambda self: self.default_change_from_date(),
+ )
+ change_to_date = fields.Date(
+ string="Apply To",
+ default=lambda self: self.default_change_to_date(),
+ )
reservation_ids = fields.Many2many(
string="Reservations",
default=lambda self: self._default_reservation_ids(),
@@ -26,18 +62,96 @@ class WizardFolioChanges(models.TransientModel):
relation="folio_changes_allowed_reservation_rel",
column1="folio_changes_id",
column2="allowed_reservation_ids",
- compute="_compute_allowed_reservations",
+ compute="_compute_allowed_reservation_ids",
+ )
+ service_ids = fields.Many2many(
+ string="Services",
+ default=lambda self: self._default_service_ids(),
+ comodel_name="pms.service",
+ relation="folio_changes_service_rel",
+ column1="folio_changes_id",
+ column2="service_ids",
+ domain="[('id', 'in', allowed_service_ids)]",
+ )
+ allowed_service_ids = fields.Many2many(
+ string="Allowed Services",
+ comodel_name="pms.service",
+ relation="folio_changes_allowed_service_rel",
+ column1="folio_changes_id",
+ column2="allowed_service_ids",
+ compute="_compute_allowed_service_ids",
+ )
+ apply_new_checkin = fields.Boolean(
+ string="Apply Checkin Update",
+ default=False,
+ )
+ new_checkin = fields.Date(
+ string="New Checkin",
+ default=lambda self: self.default_change_new_checkin(),
+ )
+
+ apply_new_checkout = fields.Boolean(
+ string="Apply Checkout Update",
+ default=False,
+ )
+ new_checkout = fields.Date(
+ string="New Checkout",
+ default=lambda self: self.default_change_new_checkout(),
+ )
+ nights = fields.Integer(
+ string="Nights",
+ compute="_compute_nights",
+ )
+ dates_incongruence = fields.Boolean(
+ string="Dates incrongruence",
+ help="Indicates that there are reservations with different checkin and/or checkout",
+ compute="_compute_dates_incongruence",
+ store=True,
+ )
+ apply_price = fields.Boolean(
+ string="Apply Price update",
+ default=False,
)
new_price = fields.Float(
string="New Price",
)
+
+ apply_discount = fields.Boolean(
+ string="Apply Discount update",
+ default=False,
+ )
new_discount = fields.Float(
string="New Discount %",
)
+
+ apply_board_service = fields.Boolean(
+ string="Add Board Service to reservations",
+ default=False,
+ )
new_board_service_id = fields.Many2one(
string="New Board Service",
comodel_name="pms.board.service",
)
+
+ apply_service = fields.Boolean(
+ string="Add Service to reservations",
+ default=False,
+ )
+ new_service_id = fields.Many2one(
+ string="New Service",
+ comodel_name="product.product",
+ domain="[('sale_ok','=',True)]",
+ )
+
+ apply_day_qty = fields.Boolean(
+ string="Add Service to reservations",
+ help="If not set, it will use the default product day qty",
+ default=False,
+ )
+ day_qty = fields.Integer(
+ string="Quantity per day",
+ )
+
apply_on_monday = fields.Boolean(
string="Apply Availability Rule on mondays",
default=False,
@@ -81,13 +195,86 @@ class WizardFolioChanges(models.TransientModel):
folio = self.env["pms.folio"].browse(folio_id)
return folio.reservation_ids
+ def _default_room_type_filter_ids(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return self.env["pms.room.type"].browse(
+ folio.mapped("reservation_ids.room_type_id.id")
+ )
+
+ def default_change_new_checkin(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return min(folio.reservation_ids.mapped("checkin"), default=False)
+
+ def default_change_new_checkout(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return max(folio.reservation_ids.mapped("checkout"), default=False)
+
+ def _default_service_ids(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return folio.service_ids
+
+ def default_change_from_date(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return min(folio.reservation_ids.mapped("checkin"), default=False)
+
+ def default_change_to_date(self):
+ folio_id = self._context.get("active_id")
+ folio = self.env["pms.folio"].browse(folio_id)
+ return max(folio.reservation_ids.mapped("checkout"), default=False)
+
+ @api.depends("new_checkin", "new_checkout")
+ def _compute_nights(self):
+ for record in self:
+ record.nights = (record.new_checkout - record.new_checkin).days
+
+ @api.depends("reservation_ids")
+ def _compute_dates_incongruence(self):
+ self.dates_incongruence = False
+ for record in self:
+ if (
+ len(set(record.reservation_ids.mapped("checkin"))) > 1
+ or len(set(record.reservation_ids.mapped("checkout"))) > 1
+ ):
+ record.dates_incongruence = True
+
@api.depends("folio_id")
- def _compute_allowed_reservations(self):
+ def _compute_allowed_reservation_ids(self):
self.ensure_one()
self.allowed_reservation_ids = self.folio_id.reservation_ids
+ @api.depends("folio_id")
+ def _compute_allowed_service_ids(self):
+ self.ensure_one()
+ self.allowed_service_ids = self.folio_id.service_ids
+
+ @api.depends("folio_id")
+ def _compute_allowed_room_type_ids(self):
+ self.ensure_one()
+ self.allowed_room_type_ids = self.env["pms.room.type"].browse(
+ self.folio_id.mapped("reservation_ids.room_type_id.id")
+ )
+
+ @api.onchange("room_type_filter_ids")
+ def _onchange_room_type_filter_ids(self):
+ self.service_ids = self.folio_id.service_ids.filtered(
+ lambda s: s.reservation_id
+ and s.reservation_id.room_type_id.id in self.room_type_filter_ids.ids
+ )
+ self.reservation_ids = self.folio_id.reservation_ids.filtered(
+ lambda r: r.room_type_id.id in self.room_type_filter_ids.ids
+ )
+
+ @api.onchange("reservation_ids")
+ def _onchange_reservations_ids(self):
+ self.new_checkin = min(self.reservation_ids.mapped("checkin"), default=False)
+ self.new_checkout = max(self.reservation_ids.mapped("checkout"), default=False)
+
def button_change(self):
- vals = {}
week_days_to_apply = (
self.apply_on_monday,
self.apply_on_tuesday,
@@ -97,52 +284,117 @@ class WizardFolioChanges(models.TransientModel):
self.apply_on_saturday,
self.apply_on_sunday,
)
- reservation_lines = self.reservation_ids.reservation_line_ids
- if not self.apply_on_all_week:
- reservation_lines = reservation_lines.filtered(
- lambda x: week_days_to_apply[x.date.timetuple()[6]]
+ if self.modification_type == "dates":
+ self._update_dates(
+ reservations=self.reservation_ids,
+ new_checkin=self.new_checkin,
+ new_checkout=self.new_checkout,
)
- if self.new_price or self.new_discount:
- if self.new_price:
- vals["price"] = self.new_price
- if self.new_discount:
- vals["discount"] = self.new_discount
-
- reservation_lines.write(vals)
-
- self.folio_id.message_post(
- body=_(
- "Prices/Discounts have been changed from folio",
- )
- )
- reservations = self.env["pms.reservation"].browse(
- reservation_lines.mapped("reservation_id.id")
- )
- for reservation in reservations:
- reservation.message_post(
- body=_(
- "Prices/Discounts have been changed from folio",
+ else:
+ dates = [
+ self.change_from_date + timedelta(days=d)
+ for d in range((self.change_to_date - self.change_from_date).days + 1)
+ ]
+ if self.modification_type == "reservations":
+ reservation_lines = self.reservation_ids.reservation_line_ids
+ if not self.apply_on_all_week:
+ reservation_lines = reservation_lines.filtered(
+ lambda x: week_days_to_apply[x.date.timetuple()[6]]
+ and x.date in dates
)
+ if self.apply_discount or self.apply_price:
+ self._update_reservations(
+ reservation_lines=reservation_lines,
+ new_price=self.apply_price and self.new_price,
+ new_discount=self.apply_discount and self.new_discount,
+ )
+ if self.apply_board_service and self.new_board_service_id:
+ self._add_board_service(
+ reservations=self.reservation_ids,
+ new_board_service_id=self.new_board_service_id.id,
+ )
+ if self.apply_service and self.new_service_id:
+ self._add_service(
+ reservations=self.reservation_ids,
+ new_service_id=self.new_service_id.id,
+ day_qty=self.day_qty if self.apply_day_qty else -1,
+ )
+ elif self.modification_type == "services":
+ service_lines = self.service_ids.service_line_ids
+ if not self.apply_on_all_week:
+ reservation_lines = service_lines.filtered(
+ lambda x: week_days_to_apply[x.date.timetuple()[6]]
+ and x.date in dates
+ )
+ self._update_services(
+ service_lines=service_lines,
+ new_price=self.apply_price and self.new_price,
+ new_discount=self.apply_discount and self.new_discount,
)
- if self.new_board_service_id:
- for reservation in self.reservation_ids:
- if (
- self.new_board_service_id.id
- in reservation.room_type_id.board_service_room_type_ids.ids
- ):
- reservation.board_service_room_id = (
- reservation.room_type_id.board_service_room_type_ids.filtered(
- lambda x: x.pms_board_service_id.id
- == self.new_board_service_id.id
- and (
- self.folio_id.pms_property_id.id
- in x.pms_property_ids.ids
- or not x.pms_property_ids
- )
+
+ def _update_dates(self, reservations, new_checkin, new_checkout):
+ for res in reservations:
+ if new_checkin:
+ res.checkin = new_checkin
+ if new_checkout:
+ res.checkout = new_checkout
+
+ def _update_reservations(
+ self, reservation_lines, new_price=False, new_discount=False
+ ):
+ line_vals = {}
+ if new_price:
+ line_vals["price"] = new_price
+ if new_discount:
+ line_vals["discount"] = new_discount
+ if line_vals:
+ reservation_lines.write(line_vals)
+
+ def _add_board_service(self, reservations, new_board_service_id):
+ for reservation in reservations:
+ if new_board_service_id in reservation.room_type_id.mapped(
+ "board_service_room_type_ids.pms_board_service_id.id"
+ ):
+ reservation.board_service_room_id = (
+ reservation.room_type_id.board_service_room_type_ids.filtered(
+ lambda x: x.pms_board_service_id.id == new_board_service_id
+ and (
+ reservation.folio_id.pms_property_id.id
+ in x.pms_property_ids.ids
+ or not x.pms_property_ids
)
)
- reservation.message_post(
- body=_(
- "Board service has been changed from folio",
- )
+ )
+
+ def _add_service(self, reservations, new_service_id, day_qty):
+ old_services = reservations.service_ids
+ reservations.write(
+ {
+ "service_ids": [
+ (
+ 0,
+ 0,
+ {
+ "product_id": new_service_id,
+ },
)
+ ]
+ }
+ )
+ new_services = reservations.service_ids - old_services
+ # Use -1 to set default qty qty per day
+ if day_qty > -1:
+ new_services.day_qty = day_qty
+
+ def _update_services(
+ self, service_lines, new_price=False, new_discount=False, new_day_qty=False
+ ):
+ line_vals = {}
+ if new_price:
+ line_vals["price_unit"] = new_price
+ if new_discount:
+ line_vals["discount"] = new_discount
+ if new_day_qty:
+ line_vals["day_qty"] = new_day_qty
+ if line_vals:
+ service_lines.write(line_vals)
diff --git a/pms/wizards/wizard_folio_changes.xml b/pms/wizards/wizard_folio_changes.xml
index 6cde01821..03b90c80f 100644
--- a/pms/wizards/wizard_folio_changes.xml
+++ b/pms/wizards/wizard_folio_changes.xml
@@ -4,105 +4,305 @@
wizard.folio.changes.view.form
wizard.folio.changes
-