diff --git a/pms-l10n-es/models/pms_checkin_partner.py b/pms-l10n-es/models/pms_checkin_partner.py index 0f6a906a1..2d257790e 100644 --- a/pms-l10n-es/models/pms_checkin_partner.py +++ b/pms-l10n-es/models/pms_checkin_partner.py @@ -4,18 +4,90 @@ from odoo import api, fields, models class PmsCheckinPartner(models.Model): _inherit = "pms.checkin.partner" - lastname2 = fields.Char("Last Name", related="partner_id.lastname2") - birthdate_date = fields.Date("Birthdate", related="partner_id.birthdate_date") + lastname2 = fields.Char( + "Last Name", + compute="_compute_lastname2", + store=True, + readonly=False, + ) + birthdate_date = fields.Date( + "Birthdate", + compute="_compute_birth_date", + store=True, + readonly=False, + ) document_number = fields.Char( - "Document Number", related="partner_id.document_number" + "Document Number", + compute="_compute_document_number", + store=True, + readonly=False, ) document_type = fields.Selection( - "Document Type", related="partner_id.document_type" + [ + ("D", "DNI"), + ("P", "Passport"), + ("C", "Driving License"), + ("I", "Identification Document"), + ("N", "Spanish residence permit"), + ("X", "European residence permit"), + ], + string="Document Type", + help="Select a valid document type", + compute="_compute_document_type", + store=True, + readonly=False, ) document_expedition_date = fields.Date( - "Expedition Date", related="partner_id.document_expedition_date" + "Expedition Date", + compute="_compute_document_expedition_date", + store=True, + readonly=False, ) - gender = fields.Selection("Gender", related="partner_id.gender") + gender = fields.Selection( + [("male", "Male"), ("female", "Female"), ("other", "Other")], + string="Gender", + compute="_compute_gender", + store=True, + readonly=False, + ) + + @api.depends("partner_id", "partner_id.lastname2") + def _compute_lastname2(self): + for record in self: + if not record.lastname2: + record.lastname2 = record.partner_id.lastname2 + + @api.depends("partner_id", "partner_id.birthdate_date") + def _compute_birth_date(self): + for record in self: + if not record.birthdate_date: + record.birthdate_date = record.partner_id.birthdate_date + + @api.depends("partner_id", "partner_id.document_number") + def _compute_document_number(self): + for record in self: + if not record.document_number: + record.document_number = record.partner_id.document_number + + @api.depends("partner_id", "partner_id.document_type") + def _compute_document_type(self): + for record in self: + if not record.document_type: + record.document_type = record.partner_id.document_type + + @api.depends("partner_id", "partner_id.document_expedition_date") + def _compute_document_expedition_date(self): + for record in self: + if not record.document_expedition_date: + record.document_expedition_date = ( + record.partner_id.document_expedition_date + ) + + @api.depends("partner_id", "partner_id.gender") + def _compute_gender(self): + for record in self: + if not record.gender: + record.gender = record.partner_id.gender @api.model def _checkin_mandatory_fields(self, depends=False): diff --git a/pms-l10n-es/models/res_partner.py b/pms-l10n-es/models/res_partner.py index 1f82504f7..4dfe8fad0 100644 --- a/pms-l10n-es/models/res_partner.py +++ b/pms-l10n-es/models/res_partner.py @@ -1,4 +1,4 @@ -from odoo import fields, models +from odoo import _, api, fields, models class ResPartner(models.Model): @@ -20,3 +20,30 @@ class ResPartner(models.Model): string="Document number", ) document_expedition_date = fields.Date(string="Document expedition date") + + @api.constrains("document_number", "document_type") + def _check_document(self): + for record in self.filtered("document_number"): + if not record.document_type: + raise models.ValidationError(_("Document Type field are mandatory")) + partner = self.search( + [ + ("document_number", "=", record.document_number), + ("document_type", "=", record.document_type), + ("id", "!=", record.id), + ] + ) + if partner: + raise models.ValidationError( + _( + "Document Number Partner %s already exist (%s)", + record.document_number, + partner.name, + ) + ) + + @api.model + def _get_key_fields(self): + key_fields = super(ResPartner, self)._get_key_fields() + key_fields.extend(["document_number"]) + return key_fields diff --git a/pms-l10n-es/views/pms_checkin_partner_views.xml b/pms-l10n-es/views/pms_checkin_partner_views.xml index 989371703..d7a3dc3d1 100644 --- a/pms-l10n-es/views/pms_checkin_partner_views.xml +++ b/pms-l10n-es/views/pms_checkin_partner_views.xml @@ -36,6 +36,24 @@ + + Checkin partner view reservation tree Spain + pms.checkin.partner + + + + + + + + + + + + + + + Checkin partner view tree Spain pms.checkin.partner diff --git a/pms/data/pms_data.xml b/pms/data/pms_data.xml index 16f222d59..b7b08f8e8 100644 --- a/pms/data/pms_data.xml +++ b/pms/data/pms_data.xml @@ -24,6 +24,7 @@ commitsun@hootel.com https://www.commitsun.com + diff --git a/pms/data/pms_sequence.xml b/pms/data/pms_sequence.xml index 952b46c00..f1a56bbe2 100644 --- a/pms/data/pms_sequence.xml +++ b/pms/data/pms_sequence.xml @@ -1,12 +1,18 @@ - PMS Folio pms.folio F/ 5 + + + PMS Checkin + pms.checkin.partner + C/ + 5 + diff --git a/pms/models/pms_checkin_partner.py b/pms/models/pms_checkin_partner.py index c364c8e2e..5ae1fa44c 100644 --- a/pms/models/pms_checkin_partner.py +++ b/pms/models/pms_checkin_partner.py @@ -2,6 +2,8 @@ # Copyright 2018 Alexandre Diaz # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import json + from odoo import _, api, fields, models from odoo.exceptions import ValidationError @@ -17,10 +19,7 @@ class PmsCheckinPartner(models.Model): # Fields declaration identifier = fields.Char( - "Identifier", - compute="_compute_identifier", - readonly=False, - store=True, + "Identifier", readonly=True, index=True, default=lambda self: _("New") ) partner_id = fields.Many2one( "res.partner", @@ -35,10 +34,29 @@ class PmsCheckinPartner(models.Model): pms_property_id = fields.Many2one( "pms.property", default=_get_default_pms_property, required=True ) - name = fields.Char("Name", related="partner_id.name") - email = fields.Char("E-mail", related="partner_id.email") - mobile = fields.Char("Mobile", related="partner_id.mobile") - image_128 = fields.Image(related="partner_id.image_128") + name = fields.Char( + "Name", + compute="_compute_name", + store=True, + readonly=False, + ) + email = fields.Char( + "E-mail", + compute="_compute_email", + store=True, + readonly=False, + ) + mobile = fields.Char( + "Mobile", + compute="_compute_mobile", + store=True, + readonly=False, + ) + image_128 = fields.Image( + compute="_compute_image_128", + store=True, + readonly=False, + ) segmentation_ids = fields.Many2many( related="reservation_id.segmentation_ids", readonly=True, @@ -98,6 +116,29 @@ class PmsCheckinPartner(models.Model): else: record.state = "precheckin" + @api.depends("partner_id", "partner_id.name") + def _compute_name(self): + for record in self: + if not record.name: + record.name = record.partner_id.name + + @api.depends("partner_id", "partner_id.email") + def _compute_email(self): + for record in self: + if not record.email: + record.email = record.partner_id.email + + @api.depends("partner_id", "partner_id.mobile") + def _compute_mobile(self): + for record in self: + if not record.mobile: + record.mobile = record.partner_id.mobile + + @api.depends("partner_id", "partner_id.image_128") + def _compute_image_128(self): + for record in self: + record.image_128 = record.partner_id.image_128 + @api.model def _checkin_mandatory_fields(self, depends=False): # api.depends need "reservation_id.state" in de lambda function @@ -105,6 +146,13 @@ class PmsCheckinPartner(models.Model): return ["reservation_id.state", "name"] return ["name"] + @api.model + def _checkin_partner_fields(self): + # api.depends need "reservation_id.state" in de lambda function + checkin_fields = self._checkin_mandatory_fields() + checkin_fields.extend(["mobile", "email"]) + return checkin_fields + # Constraints and onchanges @api.constrains("departure", "arrival") @@ -143,9 +191,51 @@ class PmsCheckinPartner(models.Model): ) if len(draft_checkins) > 0 and vals.get("partner_id"): draft_checkins[0].sudo().unlink() + if vals.get("identifier", _("New")) == _("New") or "identifier" not in vals: + pms_property_id = ( + self.env.user.get_active_property_ids()[0] + if "pms_property_id" not in vals + else vals["pms_property_id"] + ) + vals["identifier"] = self.env["ir.sequence"].search( + [("pms_property_id", "=", pms_property_id)] + ).next_by_code("pms.checkin.partner") or _("New") return super(PmsCheckinPartner, self).create(vals) - # Action methods + def write(self, vals): + res = super(PmsCheckinPartner, self).write(vals) + ResPartner = self.env["res.partner"] + if any(field in vals for field in ResPartner._get_key_fields()): + # Create Partner if get key field in the checkin + for record in self: + key = False + partner = False + if not record.partner_id: + partner_vals = {} + for field in self._checkin_partner_fields(): + if getattr(record, field): + partner_vals[field] = getattr(record, field) + if field in ResPartner._get_key_fields() and partner_vals.get( + field + ): + key = True + partner = ResPartner.search( + [(field, "=", getattr(record, field))] + ) + if key: + partner = ResPartner.create(partner_vals) + record.partner_id = partner + + if any(field in vals for field in self._checkin_partner_fields()): + # Update partner when the checkin partner field is not set on the partner + for record in self: + if record.partner_id: + partner_vals = {} + for field in self._checkin_partner_fields(): + if not getattr(record.partner_id, field): + partner_vals[field] = getattr(record, field) + record.partner_id.write(partner_vals) + return res def action_on_board(self): for record in self: @@ -169,3 +259,24 @@ class PmsCheckinPartner(models.Model): } record.update(vals) return True + + @api.model + def import_room_list_json(self, roomlist_json): + roomlist_json = json.loads(roomlist_json) + for checkin_dict in roomlist_json: + identifier = checkin_dict["identifier"] + reservation_id = checkin_dict["reservation_id"] + checkin = self.env["pms.checkin.partner"].search( + [("identifier", "=", identifier)] + ) + reservation = self.env["pms.reservation"].browse(reservation_id) + if not checkin: + raise ValidationError( + _("%s not found in checkins (%s)"), identifier, reservation.name + ) + checkin_vals = {} + for key, value in checkin_dict.items(): + if key in ("reservation_id", "folio_id", "identifier"): + continue + checkin_vals[key] = value + checkin.write(checkin_vals) diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index 0943b5f4e..834f9237f 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -150,6 +150,24 @@ class PmsFolio(models.Model): help="Pricelist for current folio.", ) checkin_partner_ids = fields.One2many("pms.checkin.partner", "folio_id") + count_rooms_pending_arrival = fields.Integer( + "Pending Arrival", + compute="_compute_count_rooms_pending_arrival", + store=True, + ) + checkins_ratio = fields.Integer( + string="Pending Arrival Ratio", + compute="_compute_checkins_ratio", + ) + pending_checkin_data = fields.Integer( + "Checkin Data", + compute="_compute_pending_checkin_data", + store=True, + ) + ratio_checkin_data = fields.Integer( + string="Pending Checkin Data", + compute="_compute_ratio_checkin_data", + ) move_ids = fields.Many2many( "account.move", string="Invoices", @@ -466,13 +484,33 @@ class PmsFolio(models.Model): ) @api.depends("reservation_ids", "reservation_ids.state") - def _compute_reservations_pending_arrival(self): - for record in self: - record.reservation_pending_arrival_ids = record.reservation_ids.filtered( - lambda r: r.state in ("draft", "precheckin") + def _compute_count_rooms_pending_arrival(self): + self.count_rooms_pending_arrival = 0 + for folio in self.filtered("reservation_ids"): + folio.count_rooms_pending_arrival = len( + folio.reservation_ids.filtered( + lambda c: c.state in ("draf", "confirm", "no_show") + ) ) - record.reservations_pending_count = len( - record.reservations_pending_arrival_ids + + @api.depends("checkin_partner_ids", "checkin_partner_ids.state") + def _compute_pending_checkin_data(self): + for folio in self: + folio.pending_checkin_data = len( + folio.checkin_partner_ids.filtered(lambda c: c.state == "draft") + ) + + @api.depends("pending_checkin_data") + def _compute_ratio_checkin_data(self): + self.ratio_checkin_data = 0 + for folio in self: + folio.ratio_checkin_data = ( + ( + sum(folio.reservation_ids.mapped("adults")) + - folio.pending_checkin_data + ) + * 100 + / sum(folio.reservation_ids.mapped("adults")) ) # TODO: Add return_ids to depends @@ -583,9 +621,22 @@ class PmsFolio(models.Model): "res_model": "pms.checkin.partner", "type": "ir.actions.act_window", "domain": [("reservation_id", "in", rooms)], + "search_view_id": [ + self.env.ref("pms.pms_checkin_partner_view_folio_search").id, + "search", + ], "target": "new", } + def action_to_arrive(self): + self.ensure_one() + reservations = self.reservation_ids.filtered( + lambda c: c.state in ("draf", "confirm", "no_show") + ) + action = self.env.ref("pms.open_pms_reservation_form_tree_all").read()[0] + action["domain"] = [("id", "in", reservations.ids)] + return action + # ORM Overrides @api.model def create(self, vals): @@ -639,23 +690,6 @@ class PmsFolio(models.Model): # self._create_analytic_account() return True - # CHECKIN/OUT PROCESS - - def _compute_checkin_partner_count(self): - for record in self: - if record.reservation_type == "normal" and record.reservation_ids: - filtered_reservs = record.reservation_ids.filtered( - lambda x: x.state != "cancelled" - ) - mapped_checkin_partner = filtered_reservs.mapped( - "checkin_partner_ids.id" - ) - record.checkin_partner_count = len(mapped_checkin_partner) - mapped_checkin_partner_count = filtered_reservs.mapped( - lambda x: (x.adults + x.children) - len(x.checkin_partner_ids) - ) - record.checkin_partner_pending_count = sum(mapped_checkin_partner_count) - def _get_tax_amount_by_group(self): self.ensure_one() res = {} diff --git a/pms/models/pms_property.py b/pms/models/pms_property.py index 2fd0e3e76..38b8ff313 100644 --- a/pms/models/pms_property.py +++ b/pms/models/pms_property.py @@ -58,6 +58,9 @@ class PmsProperty(models.Model): folio_sequence_id = fields.Many2one( "ir.sequence", "Folio Sequence", check_company=True, copy=False ) + checkin_sequence_id = fields.Many2one( + "ir.sequence", "Checkin Sequence", check_company=True, copy=False + ) tz = fields.Selection( _tz_get, string="Timezone", diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 4f539aa35..f54bfaf06 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -1330,6 +1330,8 @@ class PmsReservation(models.Model): def action_reservation_checkout(self): for record in self: + if record.state not in ("onboard", "no_checkout"): + raise UserError(_("This reservation cannot be check out")) record.state = "done" if record.checkin_partner_ids: record.checkin_partner_ids.filtered( @@ -1351,6 +1353,10 @@ class PmsReservation(models.Model): "popup": True, }, "domain": [("reservation_id", "=", self.id), ("state", "=", "draft")], + "search_view_id": [ + self.env.ref("pms.pms_checkin_partner_view_folio_search").id, + "search", + ], "target": "new", } @@ -1367,6 +1373,10 @@ class PmsReservation(models.Model): "edit": True, "popup": True, }, + "search_view_id": [ + self.env.ref("pms.pms_checkin_partner_view_folio_search").id, + "search", + ], "domain": [("reservation_id", "=", self.id)], "target": "new", } diff --git a/pms/models/res_partner.py b/pms/models/res_partner.py index d715688e0..317c489a9 100644 --- a/pms/models/res_partner.py +++ b/pms/models/res_partner.py @@ -93,3 +93,33 @@ class ResPartner(models.Model): raise models.ValidationError(_("Sale Channel must be entered")) if not record.is_agency and record.sale_channel_id: record.sale_channel_id = None + + @api.constrains("mobile", "email") + def _check_duplicated(self): + for record in self: + partner, field = record._search_duplicated() + if partner: + raise models.ValidationError( + _( + "Partner %s found with same %s (%s)", + partner.name, + partner._fields[field].string, + getattr(record, field), + ) + ) + + def _search_duplicated(self): + self.ensure_one() + partner = False + for field in self._get_key_fields(): + if getattr(self, field): + partner = self.search( + [(field, "=", getattr(self, field)), ("id", "!=", self.id)] + ) + if partner: + field = field + return partner, field + + @api.model + def _get_key_fields(self): + return ["mobile", "email"] diff --git a/pms/views/pms_checkin_partner_views.xml b/pms/views/pms_checkin_partner_views.xml index 4b6b37790..0109eb883 100644 --- a/pms/views/pms_checkin_partner_views.xml +++ b/pms/views/pms_checkin_partner_views.xml @@ -30,6 +30,7 @@ required="True" domain="[('is_company','=', False)]" /> + @@ -65,7 +66,8 @@ attrs="{'invisible': [('state','!=','precheckin')]}" /> - + + @@ -77,6 +79,40 @@ + + pms.checkin.partner.folio.view.tree + pms.checkin.partner + 20 + + + + +

@@ -70,11 +98,17 @@ attrs="{'invisible':[('reservation_type','not in',('out'))]}" />

- - + + + - + --> - - - - - - - + - -