diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 7a265938b..e72da8a31 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -93,7 +93,6 @@ "views/res_partner_id_category.xml", "views/payment_transaction_views.xml", "views/account_move_line_views.xml", - "views/pms_team_views.xml", ], "demo": [ "demo/pms_master_data.xml", diff --git a/pms/data/cron_jobs.xml b/pms/data/cron_jobs.xml index 4266372ba..fe5e404f4 100644 --- a/pms/data/cron_jobs.xml +++ b/pms/data/cron_jobs.xml @@ -47,7 +47,7 @@ model.update_daily_priority_reservation() - + Auto Invoicing Folios 5 diff --git a/pms/models/__init__.py b/pms/models/__init__.py index 343813cc0..cb2c8eb37 100644 --- a/pms/models/__init__.py +++ b/pms/models/__init__.py @@ -47,4 +47,4 @@ from . import res_partner_id_number from . import pms_automated_mails from . import payment_transaction from . import res_partner_id_category -from . import pms_team +from . import pms_team_member diff --git a/pms/models/pms_availability.py b/pms/models/pms_availability.py index 3e35770ee..e837c06d9 100644 --- a/pms/models/pms_availability.py +++ b/pms/models/pms_availability.py @@ -186,7 +186,6 @@ class PmsAvailability(models.Model): room=room.parent_id, checkin=checkin, checkout=checkout, - pms_property_id=room.pms_property_id.id, ): occupied_room_ids.append(room.id) for room in rooms.filtered("child_ids"): @@ -194,7 +193,6 @@ class PmsAvailability(models.Model): rooms=room.child_ids, checkin=checkin, checkout=checkout, - pms_property_id=room.pms_property_id.id, ): occupied_room_ids.append(room.id) occupied_room_ids.extend( @@ -212,7 +210,7 @@ class PmsAvailability(models.Model): return occupied_room_ids @api.model - def get_occupied_parent_rooms(self, room, checkin, checkout, pms_property_id): + def get_occupied_parent_rooms(self, room, checkin, checkout): RoomLines = self.env["pms.reservation.line"] if ( RoomLines.search_count( @@ -220,7 +218,6 @@ class PmsAvailability(models.Model): ("date", ">=", checkin), ("date", "<=", checkout - datetime.timedelta(1)), ("room_id", "=", room.id), - ("pms_property_id", "=", pms_property_id), ("occupies_availability", "=", True), ] ) @@ -229,22 +226,23 @@ class PmsAvailability(models.Model): return True if room.parent_id: return self.get_occupied_parent_rooms( - room=room.parent_room_id, - checkin=checkin, - checkout=checkout, + room=room.parent_id, checkin=checkin, checkout=checkout ) return False @api.model - def get_occupied_child_rooms(self, rooms, checkin, checkout, pms_property_id): + def get_occupied_child_rooms(self, rooms, checkin, checkout): RoomLines = self.env["pms.reservation.line"] + mapped_properties = list(set(rooms.mapped("pms_property_id.id"))) + if len(mapped_properties) > 1: + raise ValidationError(_("Rooms shared between different properties")) + if ( RoomLines.search_count( [ ("date", ">=", checkin), ("date", "<=", checkout - datetime.timedelta(1)), ("room_id", "in", rooms.ids), - ("pms_property_id", "=", pms_property_id), ("occupies_availability", "=", True), ] ) diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 0c0fb785e..b3abfac61 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -153,8 +153,8 @@ class PmsFolio(models.Model): compute="_compute_commission", ) user_id = fields.Many2one( - string="Salesperson", - help="The user who created the folio", + string="Reception Manager", + help="The reception manager in the folio", readonly=False, index=True, store=True, @@ -163,6 +163,39 @@ class PmsFolio(models.Model): compute="_compute_user_id", tracking=True, ) + revenue_user_id = fields.Many2one( + string="Revenue Manager", + help="The revenue manager in the folio", + readonly=False, + index=True, + store=True, + comodel_name="res.users", + ondelete="restrict", + compute="_compute_revenue_user_id", + tracking=True, + ) + administrative_user_id = fields.Many2one( + string="Administrative Manager", + help="The administrative manager in the folio", + readonly=False, + index=True, + store=True, + comodel_name="res.users", + ondelete="restrict", + compute="_compute_administrative_user_id", + tracking=True, + ) + manager_user_id = fields.Many2one( + string="Main Manager", + help="The main manager in the folio", + readonly=False, + index=True, + store=True, + comodel_name="res.users", + ondelete="restrict", + compute="_compute_manager_user_id", + tracking=True, + ) agency_id = fields.Many2one( string="Agency", help="Only allowed if the field of partner is_agency is True", @@ -912,11 +945,54 @@ class PmsFolio(models.Model): elif not folio.partner_id: folio.partner_id = False - @api.depends("partner_id") + @api.depends("pms_property_id") def _compute_user_id(self): + active_user_id = self.env.uid for folio in self: if not folio.user_id: - folio.user_id = (folio.partner_id.user_id.id or self.env.uid,) + property_users = folio.pms_property_id.member_ids.filtered( + lambda u: u.pms_role == "reception" + ).mapped("user_id") + if property_users: + if active_user_id in property_users.ids: + folio.user_id = active_user_id + elif property_users: + folio.user_id = property_users[0] + else: + folio.user_id = active_user_id or folio.pms_property_id.user_id + + @api.depends("pms_property_id") + def _compute_revenue_user_id(self): + for folio in self: + revenue_users = folio.pms_property_id.member_ids.filtered( + lambda u: u.pms_role == "revenue" + ).mapped("user_id") + if revenue_users: + folio.revenue_user_id = revenue_users[0] + else: + folio.revenue_user_id = False + + @api.depends("pms_property_id") + def _compute_administrative_user_id(self): + for folio in self: + administrative_users = folio.pms_property_id.member_ids.filtered( + lambda u: u.pms_role == "administrative" + ).mapped("user_id") + if administrative_users: + folio.administrative_user_id = administrative_users[0] + else: + folio.administrative_user_id = False + + @api.depends("pms_property_id") + def _compute_manager_user_id(self): + for folio in self: + manager_users = folio.pms_property_id.member_ids.filtered( + lambda u: u.pms_role == "manager" + ).mapped("user_id") + if manager_users: + folio.manager_user_id = manager_users[0] + else: + folio.manager_user_id = False @api.depends( "partner_id", diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 340095a0b..8f77bdd06 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -190,6 +190,19 @@ class PmsProperty(models.Model): help="Maximum amount to create the simplified invoice", default=400.0, ) + user_id = fields.Many2one( + string="Team Leader", + copy=False, + comodel_name="res.users", + ondelete="restrict", + tracking=True, + ) + member_ids = fields.One2many( + string="Team Members", + comodel_name="pms.team.member", + inverse_name="pms_property_id", + copy=False, + ) @api.depends_context( "checkin", diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index b3a38e226..eeb7f8691 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -191,13 +191,15 @@ class PmsReservation(models.Model): check_pms_properties=True, ) user_id = fields.Many2one( - string="Salesperson", - help="User who manages the reservation", + string="Reception Manager", + help="The reception manager in the folio", readonly=False, + index=True, store=True, - related="folio_id.user_id", - depends=["folio_id.user_id"], - default=lambda self: self.env.user.id, + comodel_name="res.users", + ondelete="restrict", + compute="_compute_user_id", + tracking=True, ) show_update_pricelist = fields.Boolean( string="Has Pricelist Changed", @@ -988,6 +990,22 @@ class PmsReservation(models.Model): reservation.pms_property_id.default_pricelist_id ) + @api.depends("folio_id", "pms_property_id") + def _compute_user_id(self): + active_user_id = self.env.uid + for res in self: + if not res.user_id and not res.folio_id: + property_users = res.pms_property_id.member_ids.filtered( + lambda u: u.pms_role == "reception" + ).mapped("user_id") + if property_users: + if active_user_id in property_users.ids: + res.user_id = active_user_id + elif property_users: + res.user_id = property_users[0] + else: + res.user_id = active_user_id or res.pms_property_id.user_id + @api.depends("pricelist_id", "room_type_id") def _compute_show_update_pricelist(self): for reservation in self: diff --git a/pms/models/pms_team.py b/pms/models/pms_team.py deleted file mode 100644 index 6221d5bb2..000000000 --- a/pms/models/pms_team.py +++ /dev/null @@ -1,15 +0,0 @@ -from odoo import fields, models - - -class PmsTeam(models.Model): - _name = "pms.team" - _inherit = ["mail.thread"] - _description = "PMS Team" - _check_pms_properties_auto = True - - name = fields.Char("PMS Team", required=True) - sequence = fields.Integer("Sequence", default=10) - active = fields.Boolean(default=True) - pms_property_id = fields.Many2one("pms.property", string="Property") - user_id = fields.Many2one("res.users", string="Team Leader") - member_ids = fields.One2many("res.users", "pms_team_id", string="Channel Members") diff --git a/pms/models/pms_team_member.py b/pms/models/pms_team_member.py new file mode 100644 index 000000000..0ad7b1754 --- /dev/null +++ b/pms/models/pms_team_member.py @@ -0,0 +1,45 @@ +from odoo import fields, models + + +class PmsTeamMember(models.Model): + _name = "pms.team.member" + _description = "PMS Team Member" + + name = fields.Char( + string="Name", + store=True, + related="user_id.name", + ) + active = fields.Boolean( + string="Active", + default=True, + ) + sequence = fields.Integer( + string="Sequence", + default=10, + ) + pms_property_id = fields.Many2one( + string="Property", + comodel_name="pms.property", + store=True, + ondelete="restrict", + ) + user_id = fields.Many2one( + string="User Member", + copy=False, + comodel_name="res.users", + ondelete="restrict", + ) + pms_role = fields.Selection( + string="PMS Role", + help="The member role in the organization" + "It can be 'Reception', 'Revenue', 'Administrative', or 'Manager'", + copy=False, + selection=[ + ("reception", "Reception"), + ("revenue", "Revenue"), + ("administrative", "Administrative"), + ("manager", "Operational Manager"), + ], + required=True, + ) diff --git a/pms/models/res_partner.py b/pms/models/res_partner.py index fc375513d..df0de86e9 100644 --- a/pms/models/res_partner.py +++ b/pms/models/res_partner.py @@ -276,8 +276,6 @@ class ResPartner(models.Model): vat_document_types.append((doc_type.name, doc_type.name)) return vat_document_types - team_id = fields.Many2one("pms.team", "PMS Team") - @api.depends("pms_checkin_partner_ids", "pms_checkin_partner_ids.gender") def _compute_gender(self): if hasattr(super(), "_compute_gender"): diff --git a/pms/models/res_users.py b/pms/models/res_users.py index aea99349f..a7becc480 100644 --- a/pms/models/res_users.py +++ b/pms/models/res_users.py @@ -25,8 +25,6 @@ class ResUsers(models.Model): domain="[('company_id','in',company_ids)]", ) - pms_team_id = fields.Many2one("pms.team", "User's PMS Team") - @api.model def get_active_property_ids(self): # TODO: Require performance test and security (dont allow any property id) diff --git a/pms/security/ir.model.access.csv b/pms/security/ir.model.access.csv index 585fe914c..878e87523 100644 --- a/pms/security/ir.model.access.csv +++ b/pms/security/ir.model.access.csv @@ -24,6 +24,7 @@ user_access_account_full_reconcile,user_access_account_full_reconcile,account.mo 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_availability_plan,pms.group_pms_user,1,0,0,0 user_access_pms_sale_channel,user_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_user,1,0,0,0 +user_access_pms_team_member,user_access_pms_team_member,model_pms_team_member,pms.group_pms_user,1,0,0,0 manager_access_pms_ubication,manager_access_pms_ubication,model_pms_ubication,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 @@ -47,6 +48,7 @@ manager_access_property,manager_access_property,model_pms_property,pms.group_pms manager_access_pms_cancelation_rule,manager_access_pms_cancelation_rule,model_pms_cancelation_rule,pms.group_pms_manager,1,1,1,1 manager_access_availability,manager_access_availability,model_pms_availability_plan,pms.group_pms_manager,1,1,1,1 manager_access_pms_sale_channel,manager_access_pms_sale_channel,model_pms_sale_channel,pms.group_pms_manager,1,1,1,1 +manager_access_pms_team_member,manager_access_pms_team_member,model_pms_team_member,pms.group_pms_manager,1,1,1,1 user_access_pms_reservation_split_join_swap_wizard,user_access_pms_reservation_split_join_swap_wizard,model_pms_reservation_split_join_swap_wizard,pms.group_pms_user,1,1,1,1 user_access_pms_wizard_reservation_lines_split,user_access_pms_wizard_reservation_lines_split,model_pms_wizard_reservation_lines_split,pms.group_pms_user,1,1,1,1 user_access_pms_massive_changes_wizard,user_access_pms_massive_changes_wizard,model_pms_massive_changes_wizard,pms.group_pms_user,1,1,1,1 @@ -66,4 +68,3 @@ user_access_res_partner_portal,user_access_res_partner_portal,model_res_partner, 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 -user_access_pms_team,user_access_pms_team,model_pms_team,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 41254ca2e..77a3de91e 100644 --- a/pms/views/pms_folio_views.xml +++ b/pms/views/pms_folio_views.xml @@ -586,6 +586,11 @@ + + + + + diff --git a/pms/views/pms_property_views.xml b/pms/views/pms_property_views.xml index 4b6fd7745..2e25bfff8 100644 --- a/pms/views/pms_property_views.xml +++ b/pms/views/pms_property_views.xml @@ -130,6 +130,10 @@ string="Cancellation Email" name="property_canceled_template" /> +
@@ -162,6 +166,75 @@ + +