diff --git a/pms/__manifest__.py b/pms/__manifest__.py index 894b682d3..9963101f8 100644 --- a/pms/__manifest__.py +++ b/pms/__manifest__.py @@ -36,6 +36,7 @@ "data/pms_confirmed_reservation_email_template.xml", "data/pms_modified_reservation_email_template.xml", "data/pms_cancelled_reservation_email_template.xml", + "data/pms_precheckin_invitation_email_template.xml", "data/pms_data.xml", "data/traveller_report_paperformat.xml", "report/pms_folio.xml", @@ -81,8 +82,10 @@ "views/reservation_portal_templates.xml", "views/res_company_views.xml", "views/traveller_report_template.xml", + "views/assets.xml", "wizards/wizard_split_join_swap_reservation.xml", "views/pms_automated_mails_views.xml", + "views/precheckin_portal_templates.xml", "wizards/wizard_massive_changes.xml", "wizards/wizard_advanced_filters.xml", ], diff --git a/pms/controllers/pms_portal.py b/pms/controllers/pms_portal.py index 3eeffcd74..0ecf0e989 100644 --- a/pms/controllers/pms_portal.py +++ b/pms/controllers/pms_portal.py @@ -1,4 +1,6 @@ -from odoo import _, http +import re + +from odoo import _, fields, http, tools from odoo.exceptions import AccessError, MissingError from odoo.http import request @@ -109,6 +111,35 @@ class PortalFolio(CustomerPortal): values = self._folio_get_page_view_values(folio_sudo, access_token, **kw) return request.render("pms.folio_portal_template", values) + @http.route( + ["/my/folios//precheckin"], type="http", auth="user", website=True + ) + def portal_my_folio_precheckin( + self, folio_id, access_token=None, report_type=None, download=False, **kw + ): + values = self._prepare_portal_layout_values() + try: + folio_sudo = self._document_check_access( + "pms.folio", + folio_id, + access_token=access_token, + ) + except (AccessError, MissingError): + return request.redirect("/my") + values.update(self._folio_get_page_view_values(folio_sudo, access_token, **kw)) + values.update({"no_breadcrumbs": True, "error": {}}) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + doc_type_ids = request.env["res.partner.id_category"].sudo().search([]) + values.update( + { + "country_ids": country_ids, + "state_ids": state_ids, + "doc_type_ids": doc_type_ids, + } + ) + return request.render("pms.portal_my_folio_precheckin", values) + class PortalReservation(CustomerPortal): def _prepare_home_portal_values(self, counters): @@ -144,9 +175,7 @@ class PortalReservation(CustomerPortal): auth="user", website=True, ) - def portal_my_reservations( - self, page=1, date_begin=None, date_end=None, sortby=None, filterby=None, **kw - ): + def portal_my_reservations(self, page=1, date_begin=None, date_end=None): partner = request.env.user.partner_id values = self._prepare_portal_layout_values() Reservation = request.env["pms.reservation"] @@ -208,9 +237,403 @@ class PortalReservation(CustomerPortal): ) except (AccessError, MissingError): return request.redirect("/my") - # for attachment in reservation_sudo.attachment_ids: - # attachment.generate_access_token() values = self._reservation_get_page_view_values( reservation_sudo, access_token, **kw ) return request.render("pms.portal_my_reservation_detail", values) + + @http.route( + ["/my/reservations//precheckin"], + type="http", + auth="user", + website=True, + ) + def portal_my_reservation_precheckin(self, reservation_id, access_token=None, **kw): + try: + reservation_sudo = self._document_check_access( + "pms.reservation", + reservation_id, + access_token=access_token, + ) + except (AccessError, MissingError): + return request.redirect("/my") + values = self._reservation_get_page_view_values( + reservation_sudo, access_token, **kw + ) + values.update({"no_breadcrumbs": True, "error": {}}) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + doc_type_ids = request.env["res.partner.id_category"].sudo().search([]) + values.update( + { + "country_ids": country_ids, + "state_ids": state_ids, + "doc_type_ids": doc_type_ids, + } + ) + return request.render("pms.portal_my_reservation_precheckin", values) + + +class PortalPrecheckin(CustomerPortal): + def _precheckin_get_page_view_values(self, checkin_partner, access_token, **kwargs): + values = {"checkin_partner": checkin_partner, "token": access_token} + return self._get_page_view_values( + checkin_partner, + access_token, + values, + "my_precheckins_history", + False, + **kwargs + ) + + @http.route( + ["/my/precheckin/"], + type="http", + auth="user", + website=True, + ) + def portal_my_precheckin_detail(self, checkin_partner_id, access_token=None, **kw): + try: + checkin_sudo = self._document_check_access( + "pms.checkin.partner", + checkin_partner_id, + access_token=access_token, + ) + except (AccessError, MissingError): + return request.redirect("/my") + values = self._precheckin_get_page_view_values(checkin_sudo, access_token, **kw) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + doc_type_ids = request.env["res.partner.id_category"].sudo().search([]) + values.update( + { + "doc_type_ids": doc_type_ids, + "country_ids": country_ids, + "state_ids": state_ids, + "no_breadcrumbs": True, + "error": {}, + } + ) + return request.render("pms.portal_my_precheckin_detail", values) + + @http.route(["/my/precheckin"], type="http", auth="user", website=True, csrf=False) + def portal_precheckin_submit(self, **kw): + values = dict() + checkin_partner = request.env["pms.checkin.partner"].browse(int(kw.get("id"))) + values.update( + { + "checkin_partner": checkin_partner, + "error": {}, + "error_message": {}, + } + ) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + doc_type_ids = request.env["res.partner.id_category"].sudo().search([]) + if kw: + error, error_message = self.form_validate(kw, None) + values.update( + { + "no_breadcrumbs": True, + "error": error, + "error_message": error_message, + "country_ids": country_ids, + "state_ids": state_ids, + "doc_type_ids": doc_type_ids, + } + ) + if error: + return request.render("pms.portal_my_precheckin_detail", values) + else: + try: + values = kw + if values.get("document_type"): + doc_type = ( + request.env["res.partner.id_category"] + .sudo() + .search([("name", "=", values.get("document_type"))]) + ) + values.update( + { + "document_type": doc_type.id, + } + ) + request.env["pms.checkin.partner"].sudo()._save_data_from_portal( + values + ) + doc_type_ids = ( + request.env["res.partner.id_category"].sudo().search([]) + ) + values.update( + { + "doc_type_ids": doc_type_ids, + } + ) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + values.update( + { + "country_ids": country_ids, + "state_ids": state_ids, + } + ) + values.update( + { + "success": True, + "checkin_partner": checkin_partner, + "no_breadcrumbs": True, + "error": {}, + } + ) + return request.render("pms.portal_my_precheckin_detail", values) + except (AccessError, MissingError): + return request.redirect("/my") + + @http.route( + ["/my/precheckin/folio_reservation"], + type="http", + auth="user", + website=True, + csrf=False, + ) + def portal_precheckin_folio_submit(self, **kw): + errors = {} + e_messages = {} + counter = 1 + has_error = False + checkin_partners = False + if kw.get("folio_id"): + folio = request.env["pms.folio"].browse(int(kw.get("folio_id"))) + checkin_partners = folio.checkin_partner_ids + elif kw.get("reservation_id"): + reservation = request.env["pms.reservation"].browse( + int(kw.get("reservation_id")) + ) + checkin_partners = reservation.checkin_partner_ids + for checkin in checkin_partners: + values = { + "id": kw.get("id-" + str(counter)), + "firstname": kw.get("firstname-" + str(counter)), + "lastname": kw.get("lastname-" + str(counter)), + "lastname2": kw.get("lastname2-" + str(counter)), + "gender": kw.get("gender-" + str(counter)), + "birthdate_date": kw.get("birthdate_date-" + str(counter)) + if kw.get("birthdate_date-" + str(counter)) + else False, + "document_type": kw.get("document_type-" + str(counter)), + "document_number": kw.get("document_number-" + str(counter)), + "document_expedition_date": kw.get( + "document_expedition_date-" + str(counter) + ) + if kw.get("document_expedition_date-" + str(counter)) + else False, + "mobile": kw.get("mobile-" + str(counter)), + "email": kw.get("email-" + str(counter)), + "nationality_id": kw.get("nationality_id-" + str(counter)), + "state": kw.get("state-" + str(counter)), + } + + if values.get("document_type"): + doc_type_code = values.get("document_type") + doc_type = ( + request.env["res.partner.id_category"] + .sudo() + .search([("name", "=", doc_type_code)]) + ) + values.update( + { + "document_type": doc_type.id, + } + ) + error, error_message = self.form_validate(kw, counter) + errors.update(error) + e_messages.update(error_message) + if error_message: + has_error = True + else: + checkin.sudo()._save_data_from_portal(values) + counter = counter + 1 + values = {"no_breadcrumbs": True} + doc_type_ids = request.env["res.partner.id_category"].sudo().search([]) + country_ids = request.env["res.country"].search([]) + state_ids = request.env["res.country.state"].search([]) + values.update( + { + "doc_type_ids": doc_type_ids, + "country_ids": country_ids, + "state_ids": state_ids, + } + ) + if has_error: + filtered_dict_error = {k: v for k, v in errors.items() if v} + filtered_dict_error_messages = {k: v for k, v in e_messages.items() if v} + values.update( + { + "error": filtered_dict_error, + "error_message": filtered_dict_error_messages, + } + ) + else: + values.update({"success": True, "error": {}}) + if kw.get("folio_id"): + folio = request.env["pms.folio"].browse(int(kw.get("folio_id"))) + values.update( + { + "folio": folio, + } + ) + return request.render("pms.portal_my_folio_precheckin", values) + elif kw.get("reservation_id"): + reservation = request.env["pms.reservation"].browse( + int(kw.get("reservation_id")) + ) + values.update( + { + "reservation": reservation, + } + ) + return request.render("pms.portal_my_reservation_precheckin", values) + + def form_validate(self, data, counter): + error, error_message = self.form_document_validate(data, counter) + keys = data.keys() + mobile = "mobile" if "mobile" in keys else "mobile-" + str(counter) + if data[mobile]: + if not re.match( + r"^(\d{3}[\-\s]?\d{2}[\-\s]?\d{2}[\-\s]?\d{2}[\-\s]?|" + r"\d{3}[\-\s]?\d{3}[\-\s]?\d{3})$", + data[mobile], + ): + error[mobile] = "error" + error_message[mobile] = "Invalid phone" + birthdate_date = ( + "birthdate_date" + if "birthdate_date" in keys + else "birthdate_date-" + str(counter) + ) + if data[birthdate_date] and data[birthdate_date] > str(fields.Datetime.today()): + error[birthdate_date] = "error" + error_message[birthdate_date] = "Birthdate must be less than today" + email = "email" if "email" in keys else "email-" + str(counter) + if data[email] and not tools.single_email_re.match(data[email]): + error[email] = "error" + error_message[email] = "Email format is wrong" + firstname = "firstname" if "firstname" in keys else "firstname-" + str(counter) + lastname = "lastname" if "lastname" in keys else "lastname-" + str(counter) + lastname2 = "lastname2" if "lastname2" in keys else "lastname2-" + str(counter) + if not data[firstname] and not data[lastname] and not data[lastname2]: + error[firstname] = "error" + error_message[firstname] = "Firstname or any lastname are not included" + return error, error_message + + def form_document_validate(self, data, counter): + error = dict() + error_message = {} + keys = data.keys() + document_number = ( + "document_number" + if "document_number" in keys + else "document_number-" + str(counter) + ) + document_type = ( + "document_type" + if "document_type" in keys + else "document_type-" + str(counter) + ) + document_expedition_date = ( + "document_expedition_date" + if "document_expedition_date" in keys + else "document_expedition_date-" + str(counter) + ) + if data[document_expedition_date] and not data[document_number]: + error[document_expedition_date] = "error" + error_message[ + document_expedition_date + ] = "Document Number not entered and Document Type is not selected" + if data[document_number]: + if not data[document_type]: + error[document_type] = "error" + error_message[document_type] = "Document Type is not selected" + if data[document_type] == "D": + if len(data[document_number]) == 9 or len(data[document_number]) == 10: + if not re.match(r"^\d{8}[ -]?[a-zA-Z]$", data[document_number]): + error[document_number] = "error" + error_message[document_number] = "The DNI format is wrong" + letters = { + 0: "T", + 1: "R", + 2: "W", + 3: "A", + 4: "G", + 5: "M", + 6: "Y", + 7: "F", + 8: "P", + 9: "D", + 10: "X", + 11: "B", + 12: "N", + 13: "J", + 14: "Z", + 15: "S", + 16: "Q", + 17: "V", + 18: "H", + 19: "L", + 20: "C", + 21: "K", + 22: "E", + } + dni_number = data[document_number][0:8] + dni_letter = data[document_number][ + len(data[document_number]) - 1 : len(data[document_number]) + ] + if letters.get(int(dni_number) % 23) != dni_letter.upper(): + error[document_number] = "error" + error_message[document_number] = "DNI format is invalid" + else: + error[document_number] = "error" + error_message[document_number] = "DNI is invalid" + if data[document_type] == "C" and not re.match( + r"^\d{8}[ -]?[a-zA-Z]$", data[document_number] + ): + error[document_number] = "error" + error_message[document_number] = "The Driving License format is wrong" + if data[document_type] == "N" and not re.match( + r"^[X|Y]{1}[ -]?\d{7,8}[ -]?[a-zA-Z]$", data[document_number] + ): + error[document_number] = "error" + error_message[ + document_number + ] = "The Spanish Residence Permit format is wrong" + if data[document_type] == "X" and not re.match( + r"^[X|Y]{1}[ -]?\d{7,8}[ -]?[a-zA-Z]$", data[document_number] + ): + error[document_number] = "error" + error_message[ + document_number + ] = "The European Residence Permit format is wrong" + elif data[document_type]: + error[document_number] = "error" + error_message[document_number] = "Document Number not entered" + return error, error_message + + @http.route( + ["/my/precheckin/send_invitation"], + auth="user", + type="json", + website=True, + csrf=False, + ) + def portal_precheckin_folio_send_invitation(self, **kw): + if kw.get("folio_id"): + folio = request.env["pms.folio"].browse(int(kw.get("folio_id"))) + kw.update({"folio": folio}) + checkin_partner = request.env["pms.checkin.partner"].browse( + int(kw["checkin_partner_id"]) + ) + firstname = kw["firstname"] + email = kw["email"] + checkin_partner.write({"firstname": firstname, "email": email}) + checkin_partner.send_portal_invitation_email(firstname, email) + # request.portal_my_folio_precheckin(kw) diff --git a/pms/data/pms_precheckin_invitation_email_template.xml b/pms/data/pms_precheckin_invitation_email_template.xml new file mode 100644 index 000000000..a9f1fc444 --- /dev/null +++ b/pms/data/pms_precheckin_invitation_email_template.xml @@ -0,0 +1,57 @@ + + + + + Precheckin Invitation + + Hi ${object.firstname}, do your check-in now in ${object.pms_property_id.name} + ${object.pms_property_id.partner_id.email | safe} + ${(object.email and '"%s" <%s>' % (object.name, object.email) or object.partner_id.email_formatted or '') | safe} + +
+ Do your check-in now and save time. +
+ Access our quick registration system. In a few steps you will be able to register your data in an agile, simple and secure way, avoiding queues at reception. + If you register your data in our system, your passage through reception will be much faster, being able to enjoy the comfort of your room right away. + + + + +
+ + + + +
+ +
Check-in +
+
Hacer check-in
+
+
+
+
+
+
+
diff --git a/pms/models/pms_checkin_partner.py b/pms/models/pms_checkin_partner.py index 1621615ea..a44a21b1b 100644 --- a/pms/models/pms_checkin_partner.py +++ b/pms/models/pms_checkin_partner.py @@ -3,15 +3,20 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). import json +from datetime import datetime + +from dateutil.relativedelta import relativedelta from odoo import _, api, fields, models from odoo.exceptions import UserError, ValidationError +from odoo.tools import DEFAULT_SERVER_DATE_FORMAT from odoo.tools.safe_eval import safe_eval class PmsCheckinPartner(models.Model): _name = "pms.checkin.partner" _description = "Partner Checkins" + _inherit = ["portal.mixin"] _rec_name = "identifier" _check_pms_properties_auto = True @@ -459,6 +464,13 @@ class PmsCheckinPartner(models.Model): else: record.partner_incongruences = False + def _compute_access_url(self): + super(PmsCheckinPartner, self)._compute_access_url() + for checkin in self: + checkin.access_url = "/my/precheckin/%s" % (checkin.id) + + # Constraints and onchanges + @api.constrains("departure", "arrival") def _check_departure(self): for record in self: @@ -648,6 +660,30 @@ class PmsCheckinPartner(models.Model): checkin_vals[key] = value checkin.write(checkin_vals) + @api.model + def calculate_doc_type_expedition_date_from_validity_date( + self, doc_type, doc_date, birthdate + ): + today = fields.datetime.today() + datetime_doc_date = datetime.strptime(doc_date, DEFAULT_SERVER_DATE_FORMAT) + if datetime_doc_date < today: + return datetime_doc_date + datetime_birthdate = datetime.strptime(birthdate, DEFAULT_SERVER_DATE_FORMAT) + age = today.year - datetime_birthdate.year + document_type = self.env["res.partner.id_category"].search( + [("id", "=", doc_type)] + ) + document_expedition_date = False + if document_type.code == "D" or document_type.code == "P": + if age < 30: + document_expedition_date = datetime_doc_date - relativedelta(years=5) + else: + document_expedition_date = datetime_doc_date - relativedelta(years=10) + if document_type.code == "C": + if age < 70: + document_expedition_date = datetime_doc_date - relativedelta(years=10) + return document_expedition_date + def action_on_board(self): for record in self: if record.reservation_id.checkin > fields.Date.today(): @@ -703,3 +739,57 @@ class PmsCheckinPartner(models.Model): "type": "ir.actions.act_window", "context": ctx, } + + def _save_data_from_portal(self, values): + checkin_partner = self.env["pms.checkin.partner"].browse(int(values.get("id"))) + if values.get("nationality_id"): + nationality_id = self.env["res.country"].search( + [("id", "=", values.get("nationality_id"))] + ) + values.update({"nationality_id": nationality_id.id}) + else: + values.update({"nationality_id": False}) + if not values.get("document_type"): + values.update({"document_type": False}) + if values.get("state"): + state_id = self.env["res.country.state"].search( + [("id", "=", values.get("state"))] + ) + values.update({"state_id": state_id}) + values.pop("state") + if values.get("document_expedition_date"): + doc_type = values.get("document_type") + doc_date = values.get("document_expedition_date") + birthdate = values.get("birthdate_date") + document_expedition_date = ( + self.calculate_doc_type_expedition_date_from_validity_date( + doc_type, doc_date, birthdate + ) + ) + values.update({"document_expedition_date": document_expedition_date}) + checkin_partner.sudo().write(values) + + def send_portal_invitation_email(self, invitation_firstname=None, email=None): + template = self.sudo().env.ref( + "pms.precheckin_invitation_email", raise_if_not_found=False + ) + subject = template._render_field( + "subject", [6, 0, self.id], compute_lang=True, post_process=True + )[self.id] + body = template._render_field( + "body_html", [6, 0, self.id], compute_lang=True, post_process=True + )[self.id] + invitation_mail = ( + self.env["mail.mail"] + .sudo() + .create( + { + "subject": subject, + "body_html": body, + "email_from": self.pms_property_id.partner_id.email, + "email_to": email, + } + ) + ) + + invitation_mail.send() diff --git a/pms/models/pms_folio.py b/pms/models/pms_folio.py index b49ecaab5..0094dcb2b 100644 --- a/pms/models/pms_folio.py +++ b/pms/models/pms_folio.py @@ -770,6 +770,8 @@ class PmsFolio(models.Model): # else: # raise UserError(_("Some reservations have different currency")) + # is_checkin = fields.Boolean() + def _compute_access_url(self): super(PmsFolio, self)._compute_access_url() for folio in self: diff --git a/pms/models/pms_reservation.py b/pms/models/pms_reservation.py index 8b7b15701..afeefcf4e 100644 --- a/pms/models/pms_reservation.py +++ b/pms/models/pms_reservation.py @@ -1135,6 +1135,11 @@ class PmsReservation(models.Model): # date checking record.check_in_out_dates() + def _compute_precheckin_url(self): + super(PmsReservation, self)._compute_access_url() + for reservation in self: + reservation.access_url = "/my/reservations/precheckin/%s" % (reservation.id) + @api.depends("pms_property_id", "folio_id") def _compute_arrival_hour(self): for record in self: diff --git a/pms/security/ir.model.access.csv b/pms/security/ir.model.access.csv index 4897a7e30..0cf1b3a73 100644 --- a/pms/security/ir.model.access.csv +++ b/pms/security/ir.model.access.csv @@ -62,3 +62,5 @@ user_access_pms_folio_portal,user_access_pms_folio_portal,model_pms_folio,base.g user_access_pms_reservation_portal,user_access_pms_reservation_portal,model_pms_reservation,base.group_portal,1,0,0,0 user_access_pms_automated_mails,user_access_pms_automated_mails,model_pms_automated_mails,pms.group_pms_user,1,1,1,1 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 diff --git a/pms/security/pms_security.xml b/pms/security/pms_security.xml index c16ee839f..7ab889e8c 100644 --- a/pms/security/pms_security.xml +++ b/pms/security/pms_security.xml @@ -237,5 +237,26 @@ + + Portal Personal Pre-checkin + + [] + + + + + Res Partner Rule + + [] + + + + + Portal Checkin Partner Rule + + [] + + + diff --git a/pms/static/src/js/send_invitation_data.js b/pms/static/src/js/send_invitation_data.js new file mode 100644 index 000000000..5e5c01471 --- /dev/null +++ b/pms/static/src/js/send_invitation_data.js @@ -0,0 +1,41 @@ +odoo.define("pms.SendInvitationData", function (require) { + "use strict"; + + require("web.dom_ready"); + var publicWidget = require("web.public.widget"); + + publicWidget.registry.SendInvitationData = publicWidget.Widget.extend({ + selector: ".o_send_invitation_js", + events: { + click: "_onReminderToggleClick", + }, + + _onReminderToggleClick: function (ev) { + ev.preventDefault(); + var checkinPartnerId = $(ev.currentTarget) + .parent() + .parent() + .find("input[name=checkin_partner_id]") + .val(); + var firstname = $(ev.currentTarget) + .parent() + .parent() + .find("input[name=invitation_firstname]") + .val(); + var email = $(ev.currentTarget) + .parent() + .parent() + .find("input[name=invitation_email]") + .val(); + this._rpc({ + route: "/my/precheckin/send_invitation", + params: { + checkin_partner_id: checkinPartnerId, + firstname: firstname, + email: email, + }, + }); + }, + }); + return publicWidget.registry.SendInvitationData; +}); diff --git a/pms/tests/test_pms_checkin_partner.py b/pms/tests/test_pms_checkin_partner.py index 77de1fb3a..29cd15214 100644 --- a/pms/tests/test_pms_checkin_partner.py +++ b/pms/tests/test_pms_checkin_partner.py @@ -1189,3 +1189,289 @@ class TestPmsCheckinPartner(TestPms): msg="A partner can be added to the checkin partner", ): several_partners_wizard.add_partner() + + def test_calculate_dni_expedition_date_from_validity_date_age_lt_30(self): + """ + Check that the calculate_doc_type_expedition_date_from_validity_date() + method calculates correctly the expedition_date of an id category DNI + when the age is less than 30. + ------------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id DNI, birthdate calculated so that the age + is = 20 years old and document_date = today + 1 year. The expected + expedition date has to be doc_date - 5 years + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "D")]).id + ) + doc_date = fields.date.today() + datetime.timedelta(days=366) + doc_date_str = str(doc_date) + + # age=20 years old + birthdate = fields.date.today() - datetime.timedelta(days=7305) + birthdate_str = str(birthdate) + + # expected_expedition_date = doc_date - 5 years + expected_exp_date = doc_date - datetime.timedelta(days=1826.25) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + expected_exp_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_calculate_dni_expedition_date_from_validity_date_age_gt_30(self): + """ + Check that the calculate_doc_type_expedition_date_from_validity_date() + method calculates correctly the expedition_date of an id category DNI + when the age is greater than 30. + ------------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id DNI, birthdate calculated so that the age + is = 40 years old and document_date = today + 1 year. The expected + expedition date has to be doc_date - 10 years + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "D")]).id + ) + doc_date = fields.date.today() + datetime.timedelta(days=366) + doc_date_str = str(doc_date) + + # age=40 years old + birthdate = fields.date.today() - datetime.timedelta(days=14610) + birthdate_str = str(birthdate) + + # expected_expedition_date = doc_date - 10 years + expected_exp_date = doc_date - datetime.timedelta(days=3652.5) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + expected_exp_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_calculate_passport_expedition_date_from_validity_date_age_lt_30(self): + """ + Check that the calculate_doc_type_expedition_date_from_validity_date() + method calculates correctly the expedition_date of an id category Passport + when the age is less than 30. + ------------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id Passport, birthdate calculated so that the age + is = 20 years old and document_date = today + 1 year. The expected + expedition date has to be doc_date - 5 years + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "P")]).id + ) + doc_date = fields.date.today() + datetime.timedelta(days=366) + doc_date_str = str(doc_date) + + # age=20 years old + birthdate = fields.date.today() - datetime.timedelta(days=7305) + birthdate_str = str(birthdate) + + # expected_expedition_date = doc_date - 5 years + expected_exp_date = doc_date - datetime.timedelta(days=1826.25) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + expected_exp_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_calculate_passport_expedition_date_from_validity_date_age_gt_30(self): + """ + Check that the calculate_doc_type_expedition_date_from_validity_date() + method calculates correctly the expedition_date of an id category Passport + when the age is greater than 30. + ------------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id Passport, birthdate calculated so that the age + is = 40 years old and document_date = today + 1 year. The expected + expedition date has to be doc_date - 10 years + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "P")]).id + ) + doc_date = fields.date.today() + datetime.timedelta(days=366) + doc_date_str = str(doc_date) + + # age=40 years old + birthdate = fields.date.today() - datetime.timedelta(days=14610) + birthdate_str = str(birthdate) + + # expected_expedition_date = doc_date - 10 years + expected_exp_date = doc_date - datetime.timedelta(days=3652.5) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + expected_exp_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_calculate_drive_license_expedition_date_from_validity_date_age_lt_70(self): + """ + Check that the calculate_doc_type_expedition_date_from_validity_date() + method calculates correctly the expedition_date of an id category Driving + License when the age is lesser than 70. + ------------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id DNI, birthdate calculated so that the age + is = 40 years old and document_date = today + 1 year. The expected + expedition date has to be doc_date - 10 years + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "C")]).id + ) + doc_date = fields.date.today() + datetime.timedelta(days=366) + doc_date_str = str(doc_date) + + # age=40 years old + birthdate = fields.date.today() - datetime.timedelta(days=14610) + birthdate_str = str(birthdate) + + # expected_expedition_date = doc_date - 10 years + expected_exp_date = doc_date - datetime.timedelta(days=3652.5) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + expected_exp_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_calculate_expedition_date(self): + """ + Check that if the value of the doc_date is less than today, + the method calculate_doc_type_expedition_date_from_validity_date + returns the value of the doc_date as expedition_date. + ----------- + We launch the method calculate_doc_type_expedition_date_from_validity_date + with the parameters doc_type_id DNI, birthdate calculated so that the age + is = 20 years old and document_date = today - 1 year. The expected + expedition date has to be the value of doc_date. + """ + doc_type_id = ( + self.env["res.partner.id_category"].search([("code", "=", "D")]).id + ) + doc_date = fields.date.today() - datetime.timedelta(days=366) + doc_date_str = str(doc_date) + birthdate = fields.date.today() - datetime.timedelta(days=7305) + birthdate_str = str(birthdate) + expedition_date = ( + self.checkin1.calculate_doc_type_expedition_date_from_validity_date( + doc_type_id, doc_date_str, birthdate_str + ) + ) + date_expedition_date = datetime.date( + year=expedition_date.year, + month=expedition_date.month, + day=expedition_date.day, + ) + self.assertEqual( + date_expedition_date, + doc_date, + "Expedition date doesn't correspond with expected expedition date", + ) + + def test_save_checkin_from_portal(self): + """ + Check by subtesting that a checkin partner is saved correctly + with the _save_data_from_portal() method. + --------- + A reservation is created with an adult, and it will create a checkin partner. + A dictionary is created with the values to be saved and with the key 'id' + equal to the id of the checkin_partner created when the reservation was + created. We launch the _save_data_from_portal() method, passing the created + dictionary as a parameter. Then it is verified that the value of each key + in the dictionary corresponds to the fields of the saved checkin_partner. + """ + self.reservation = self.env["pms.reservation"].create( + { + "checkin": datetime.date.today() + datetime.timedelta(days=10), + "checkout": datetime.date.today() + datetime.timedelta(days=13), + "room_type_id": self.room_type1.id, + "partner_id": self.host1.id, + "adults": 1, + "pms_property_id": self.pms_property1.id, + } + ) + checkin_partner_id = self.reservation.checkin_partner_ids[0] + checkin_partner_vals = { + "id": checkin_partner_id.id, + "firstname": "SerafĂ­n", + "lastname": "Rivas", + "lastname2": "Gonzalez", + "document_type": self.id_category, + "document_number": "18038946T", + "document_expedition_date": "2015-10-07", + "birthdate_date": "1983-10-05", + "mobile": "60595595", + "email": "serafin@example.com", + "gender": "male", + "nationality_id": "1", + "state": "1", + } + checkin_partner_id._save_data_from_portal(checkin_partner_vals) + nationality_id = self.env["res.country"].browse( + checkin_partner_vals["nationality_id"] + ) + checkin_partner_vals.update( + { + "birthdate_date": datetime.date(1983, 10, 5), + "document_expedition_date": datetime.date(2015, 10, 7), + "nationality_id": nationality_id, + } + ) + for key in checkin_partner_vals: + with self.subTest(k=key): + self.assertEqual( + self.reservation.checkin_partner_ids[0][key], + checkin_partner_vals[key], + "The value of " + key + " is not correctly established", + ) diff --git a/pms/views/assets.xml b/pms/views/assets.xml new file mode 100644 index 000000000..0ff4d6887 --- /dev/null +++ b/pms/views/assets.xml @@ -0,0 +1,13 @@ + + + + + + +