import datetime from odoo import _, api, fields, models class BookingEngine(models.TransientModel): _name = "pms.booking.engine" _description = "Booking engine" _check_pms_properties_auto = True start_date = fields.Date( string="From:", help="Start date for creation of reservations and folios", required=True, ) end_date = fields.Date( string="To:", help="End date for creation of reservations and folios", 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, domain="[('is_pms_available', '=', True)]", ) pms_property_id = fields.Many2one( string="Property", help="Property to which the folio belongs", default=lambda self: self._default_pms_property_id(), comodel_name="pms.property", check_pms_properties=True, ) segmentation_ids = fields.Many2many( string="Segmentation", help="Partner Tags", ondelete="restrict", comodel_name="res.partner.category", domain="[('is_used_in_checkin', '=', True)]", ) 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, ) folio_id = fields.Many2one( string="Folio", help="Folio in which are included new reservations", comodel_name="pms.folio", check_pms_properties=True, ) availability_results = fields.One2many( string="Availability Results", help="Availability Results", readonly=False, store=True, comodel_name="pms.folio.availability.wizard", inverse_name="booking_engine_id", compute="_compute_availability_results", check_pms_properties=True, ) reservation_type = fields.Selection( string="Type", help="The type of the reservation. " "Can be 'Normal', 'Staff' or 'Out of Service'", default=lambda *a: "normal", selection=[("normal", "Normal"), ("staff", "Staff"), ("out", "Out of Service")], ) 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", required=True, ) 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, ) can_create_folio = fields.Boolean( string="Can create folio", compute="_compute_can_create_folio" ) internal_comment = fields.Text( string="Internal Folio Notes", help="Internal Folio notes for Staff", ) def _default_pms_property_id(self): if self._context.get("default_folio_id"): folio = self.env["pms.folio"].browse(self._context.get("default_folio_id")) return folio.pms_property_id.id else: return self.env.user.get_active_property_ids()[0] @api.depends("availability_results.value_num_rooms_selected") def _compute_can_create_folio(self): for record in self: record.can_create_folio = any( record.availability_results.mapped("value_num_rooms_selected") ) @api.depends("partner_id") def _compute_pricelist_id(self): for record in self: record.pricelist_id = ( record.partner_id.property_product_pricelist.id if record.partner_id.property_product_pricelist.is_pms_available else self.pms_property_id.default_pricelist_id.id ) @api.depends("agency_id") def _compute_channel_type_id(self): for record in self: if record.agency_id: record.channel_type_id = record.agency_id.sale_channel_id.id @api.depends("availability_results.price_total", "discount") def _compute_total_price_folio(self): for record in self: record.total_price_folio = 0 for line in record.availability_results: record.total_price_folio += line.price_total record.total_price_folio = record.total_price_folio * (1 - record.discount) @api.depends("agency_id") def _compute_partner_id(self): for record in self: if record.agency_id and record.agency_id.invoice_to_agency == "always": record.partner_id = record.agency_id.id elif not record.partner_id: record.partner_id = False @api.depends("partner_id", "agency_id") def _compute_partner_name(self): for record in self: if record.partner_id: record.partner_name = record.partner_id.name if ( record.agency_id and not record.agency_id.invoice_to_agency == "always" 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( "start_date", "end_date", "pricelist_id", ) def _compute_availability_results(self): for record in self: record.availability_results = False if record.start_date and record.end_date: if record.end_date == record.start_date: record.end_date = record.end_date + datetime.timedelta(days=1) cmds = [(5, 0, 0)] for room_type_iterator in self.env["pms.room.type"].search( [ "|", ("pms_property_ids", "=", False), ("pms_property_ids", "in", record.pms_property_id.id), ] ): pms_property = record.pms_property_id pms_property = pms_property.with_context( checkin=record.start_date, checkout=record.end_date, room_type_id=room_type_iterator.id, pricelist_id=record.pricelist_id.id, ) num_rooms_available = pms_property.availability cmds.append( ( 0, 0, { "booking_engine_id": record.id, "checkin": record.start_date, "checkout": record.end_date, "room_type_id": room_type_iterator.id, "num_rooms_available": num_rooms_available, }, ) ) # remove old items old_lines = record.availability_results.mapped("id") for old_line in old_lines: cmds.append((2, old_line)) record.availability_results = cmds record.availability_results = record.availability_results.sorted( key=lambda s: s.num_rooms_available, reverse=True ) def create_folio(self): for record in self: if not record.folio_id: folio = self.env["pms.folio"].create( { "reservation_type": record.reservation_type, "pricelist_id": record.pricelist_id.id, "partner_id": record.partner_id.id if record.partner_id else False, "partner_name": record.partner_name, "pms_property_id": record.pms_property_id.id, "agency_id": record.agency_id.id, "sale_channel_origin_id": record.channel_type_id.id, "segmentation_ids": [(6, 0, record.segmentation_ids.ids)], "internal_comment": record.internal_comment, } ) else: folio = record.folio_id reservation_values = [] for line in record.availability_results: for _reservations_to_create in range(0, line.value_num_rooms_selected): res_dict = { "folio_id": folio.id, "checkin": line.checkin, "checkout": line.checkout, "room_type_id": line.room_type_id.id, "partner_id": record.partner_id.id if record.partner_id else False, "partner_name": record.partner_name, "pricelist_id": record.pricelist_id.id, "pms_property_id": folio.pms_property_id.id, "board_service_room_id": line.board_service_room_id.id, } reservation_values.append((0, 0, res_dict)) folio.write( { "reservation_ids": reservation_values, } ) if record.discount: # TODO: Refact compute discount in reservation and service lines folio.reservation_ids.reservation_line_ids.discount = ( record.discount * 100 ) action = self.sudo().env.ref("pms.open_pms_folio1_form_tree_all").read()[0] action["views"] = [ (self.sudo().env.ref("pms.pms_folio_view_form").id, "form") ] action["res_id"] = folio.id return action class NumRoomsSelectionModel(models.TransientModel): _name = "pms.num.rooms.selection" _description = "Dinamic Selection based on avails room" _rec_name = "value" value = fields.Integer() room_type_id = fields.Char() booking_engine_id = fields.One2many( comodel_name="pms.folio.availability.wizard", inverse_name="num_rooms_selected", ) class AvailabilityWizard(models.TransientModel): _name = "pms.folio.availability.wizard" _description = "Room type line in Booking Engine" _check_pms_properties_auto = True booking_engine_id = fields.Many2one( string="Folio Wizard ID", comodel_name="pms.booking.engine", ) checkin = fields.Date( string="From:", help="Date Reservation starts ", required=True, ) checkout = fields.Date( string="To:", help="Date Reservation ends", required=True, ) room_type_id = fields.Many2one( string="Room Type", help="Room Type reserved", comodel_name="pms.room.type", check_pms_properties=True, ) num_rooms_available = fields.Integer( string="Available rooms", help="Number of rooms that are available", store=True, compute="_compute_num_rooms_available", ) num_rooms_selected = fields.Many2one( string="Selected rooms", readonly=False, store=True, comodel_name="pms.num.rooms.selection", domain="[('value', '<=', num_rooms_available), " "('room_type_id', '=', room_type_id)]", compute="_compute_num_rooms_selected", ) value_num_rooms_selected = fields.Integer( string="Number of Rooms Selected", readonly=False, store=True, compute="_compute_value_num_rooms_selected", ) price_per_room = fields.Float( string="Price per room", help="Price per room in folio", compute="_compute_price_per_room", ) 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_engine_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), ('pms_property_id','=',pms_property_id) ] """, check_pms_properties=True, ) @api.depends("room_type_id", "checkin", "checkout") def _compute_num_rooms_available(self): for record in self: pms_property = record.booking_engine_id.pms_property_id pms_property = pms_property.with_context( checkin=record.checkin, checkout=record.checkout, room_type_id=record.room_type_id.id, pricelist_id=record.booking_engine_id.pricelist_id.id, ) record.num_rooms_available = pms_property.availability @api.depends("num_rooms_available") def _compute_num_rooms_selected(self): for record in self: for elem_to_insert in range(0, record.num_rooms_available + 1): if ( self.env["pms.num.rooms.selection"].search_count( [ ("value", "=", elem_to_insert), ("room_type_id", "=", record.room_type_id.id), ] ) == 0 ): self.env["pms.num.rooms.selection"].create( { "value": elem_to_insert, "room_type_id": record.room_type_id.id, } ) record.num_rooms_selected = self.env["pms.num.rooms.selection"].search( [("value", "=", 0), ("room_type_id", "=", record.room_type_id.id)] ) @api.depends("num_rooms_selected") def _compute_value_num_rooms_selected(self): for record in self: if record.num_rooms_selected: record.value_num_rooms_selected = record.num_rooms_selected.value elif not record.value_num_rooms_selected: record.value_num_rooms_selected = 0 @api.depends("room_type_id", "board_service_room_id", "checkin", "checkout") def _compute_price_per_room(self): for record in self: record.price_per_room = self._get_price_by_room_type( room_type_id=record.room_type_id.id, board_service_room_id=record.board_service_room_id.id, checkin=record.checkin, checkout=record.checkout, pricelist_id=record.booking_engine_id.pricelist_id.id, pms_property_id=record.booking_engine_id.pms_property_id.id, ) @api.depends("price_per_room", "value_num_rooms_selected") def _compute_price_total(self): for record in self: record.price_total = record.price_per_room * record.value_num_rooms_selected @api.model def _get_price_by_room_type( self, room_type_id, checkin, checkout, board_service_room_id, pricelist_id, pms_property_id, adults=False, ): room_type_total_price_per_room = 0 room_type = self.env["pms.room.type"].browse(room_type_id) pms_property = self.env["pms.property"].browse(pms_property_id) for date_iterator in [ checkin + datetime.timedelta(days=x) for x in range(0, (checkout - checkin).days) ]: product = room_type.product_id product = product.with_company(pms_property.company_id).with_context( quantity=1, date=fields.Date.today(), consumption_date=date_iterator, pricelist=pricelist_id, uom=product.uom_id.id, property=pms_property_id, ) room_type_total_price_per_room += product.price if board_service_room_id: board_service_room = self.env["pms.board.service.room.type"].browse( board_service_room_id ) nights = (checkout - checkin).days adults = adults or room_type.get_room_type_capacity(pms_property_id) room_type_total_price_per_room += ( board_service_room.amount * nights * adults ) return room_type_total_price_per_room