diff --git a/pms_l10n_es/data/cron_jobs.xml b/pms_l10n_es/data/cron_jobs.xml index f8e0d7934..ffcc62c4c 100644 --- a/pms_l10n_es/data/cron_jobs.xml +++ b/pms_l10n_es/data/cron_jobs.xml @@ -45,9 +45,9 @@ SES Automatic Sending Pending Traveller Reports Communications - 1 + 30 - days + minutes -1 code diff --git a/pms_l10n_es/models/pms_property.py b/pms_l10n_es/models/pms_property.py index 8df9882d4..f4fe7e026 100644 --- a/pms_l10n_es/models/pms_property.py +++ b/pms_l10n_es/models/pms_property.py @@ -42,7 +42,6 @@ class PmsProperty(models.Model): string="Institution lessor id", help="Id provided by institution to send data from lessor.", ) - ine_tourism_number = fields.Char( "Tourism number", help="Registration number in the Ministry of Tourism. Used for INE statistics.", diff --git a/pms_l10n_es/models/pms_reservation.py b/pms_l10n_es/models/pms_reservation.py index eeaaf5db9..eb6492f55 100644 --- a/pms_l10n_es/models/pms_reservation.py +++ b/pms_l10n_es/models/pms_reservation.py @@ -15,6 +15,38 @@ class PmsReservation(models.Model): string="Is SES", readonly=True, compute="_compute_is_ses", + store=True, + ) + ses_status_reservation = fields.Selection( + string="SES Status", + help="Status of the reservation in SES", + selection=[ + ("not_applicable", "Not Applicable"), + ("to_send", "Pending Notification"), + ("to_process", "Pending Processing"), + ("error_create", "Error Creating"), + ("error_sending", "Error Sending"), + ("error_processing", "Error Processing"), + ("processed", "Processed"), + ], + compute="_compute_ses_status_reservation", + store=True, + ) + ses_status_traveller_report = fields.Selection( + string="SES Status traveller", + help="Status of the traveller report in SES", + selection=[ + ("not_applicable", "Not Applicable"), + ("incomplete", "Incomplete checkin data"), + ("to_send", "Pending Notification"), + ("to_process", "Pending Processing"), + ("error_create", "Error Creating"), + ("error_sending", "Error Sending"), + ("error_processing", "Error Processing"), + ("processed", "Processed"), + ], + compute="_compute_ses_status_traveller_report", + store=True, ) @api.depends("pms_property_id") @@ -22,6 +54,38 @@ class PmsReservation(models.Model): for record in self: record.is_ses = record.pms_property_id.institution == "ses" + @api.depends("ses_communication_ids", "ses_communication_ids.state") + def _compute_ses_status_reservation(self): + for record in self: + if record.pms_property_id.institution != "ses": + record.ses_status_reservation = "not_applicable" + continue + communication = record.ses_communication_ids.filtered( + lambda x: x.entity == "RH" + ) + if len(communication) > 1: + # get the last communication + communication = communication.sorted(key=lambda x: x.create_date)[-1] + record.ses_status_reservation = ( + communication.state if communication else "error_create" + ) + + @api.depends("ses_communication_ids", "ses_communication_ids.state") + def _compute_ses_status_traveller_report(self): + for record in self: + if record.pms_property_id.institution != "ses": + record.ses_status_traveller_report = "not_applicable" + continue + communication = record.ses_communication_ids.filtered( + lambda x: x.entity == "PV" + ) + if len(communication) > 1: + # get the last communication + communication = communication.sorted(key=lambda x: x.create_date)[-1] + record.ses_status_traveller_report = ( + communication.state if communication else "error_create" + ) + @api.model def create_communication(self, reservation_id, operation, entity): self.env["pms.ses.communication"].create( @@ -100,6 +164,9 @@ class PmsReservation(models.Model): def write(self, vals): for record in self: - if record.pms_property_id.institution == "ses": + if ( + record.pms_property_id.institution == "ses" + and record.reservation_type != "out" + ): self.create_communication_after_update_reservation(record, vals) return super(PmsReservation, self).write(vals) diff --git a/pms_l10n_es/models/pms_ses_communication.py b/pms_l10n_es/models/pms_ses_communication.py index 6119cea67..a96ad5437 100644 --- a/pms_l10n_es/models/pms_ses_communication.py +++ b/pms_l10n_es/models/pms_ses_communication.py @@ -5,6 +5,8 @@ from odoo import fields, models class PmsSesCommunication(models.Model): _name = "pms.ses.communication" _description = "SES Communication" + _order = "create_date desc" + reservation_id = fields.Many2one( string="Reservation", help="Reservation related to this communication", @@ -12,6 +14,14 @@ class PmsSesCommunication(models.Model): required=True, comodel_name="pms.reservation", ) + pms_property_id = fields.Many2one( + comodel_name="pms.property", + string="Property", + help="Property related to this communication", + related="reservation_id.pms_property_id", + index=True, + store=True, + ) communication_id = fields.Char( string="Communication ID", help="ID of the communication", @@ -37,7 +47,6 @@ class PmsSesCommunication(models.Model): string="Query status time", help="Date and time of the last state query", ) - state = fields.Selection( string="State", help="State of the communication", @@ -52,7 +61,6 @@ class PmsSesCommunication(models.Model): ("processed", "Processed"), ], ) - sending_result = fields.Text( string="Sending Result", help="Notification sending result", @@ -86,3 +94,10 @@ class PmsSesCommunication(models.Model): string="SOAP Resp. Status", help="SOAP response status query", ) + + def force_send_communication(self): + for record in self: + self.env["traveller.report.wizard"].ses_send_communication( + entity=record.entity, + communication_id=record.communication_id, + ) diff --git a/pms_l10n_es/tests/test_pms_ses_communication.py b/pms_l10n_es/tests/test_pms_ses_communication.py index 7610a2b0f..676b28b30 100644 --- a/pms_l10n_es/tests/test_pms_ses_communication.py +++ b/pms_l10n_es/tests/test_pms_ses_communication.py @@ -221,65 +221,3 @@ class TestPmsSesCommunication(TestPms): reservation_communications, "Update adults should create 2 notifications with operations A and B", ) - - def test_create_notification_when_checkin_partner_on_board(self): - # ARRANGE - partner = self.env["res.partner"].create( - { - "name": "name test", - "firstname": "firstname test", - "lastname": "lastname test", - "lastname2": "lastname2 test", - "birthdate_date": "1995-12-10", - "gender": "male", - "nationality_id": self.env.ref("base.es").id, - "residence_street": "street test", - "residence_city": "city test", - "residence_zip": "zip test", - "residence_country_id": self.env.ref("base.us").id, - } - ) - reservation = self.env["pms.reservation"].create( - { - "pms_property_id": self.pms_property1.id, - "room_type_id": self.room_type.id, - "checkin": fields.date.today(), - "checkout": fields.date.today() + datetime.timedelta(days=13), - "adults": 1, - "children": 0, - "sale_channel_origin_id": self.sale_channel_direct1.id, - "partner_name": "Test reservation", - } - ) - document_type_dni = self.env["res.partner.id_category"].search( - [("code", "=", "D")], limit=1 - ) - checkin_partner = self.env["pms.checkin.partner"].create( - { - "reservation_id": reservation.id, - "partner_id": partner.id, - "document_number": "11111111H", - "document_type": document_type_dni.id, - "document_expedition_date": fields.date.today() - + datetime.timedelta(days=1), - "support_number": "123456", - } - ) - checkin_partner.action_on_board() - # ACT - self.env[ - "traveller.report.wizard" - ].create_pending_notifications_traveller_report() - # ASSERT - last_notification = self.env["pms.ses.communication"].search( - [ - ("reservation_id", "=", reservation.id), - ("operation", "=", "A"), - ("state", "=", "to_send"), - ("entity", "=", "PV"), - ], - ) - self.assertTrue( - last_notification, - "Notification should be created when checkin partner is on board", - ) diff --git a/pms_l10n_es/views/pms_ses_communication_views.xml b/pms_l10n_es/views/pms_ses_communication_views.xml index 3268b5e1b..440961e45 100644 --- a/pms_l10n_es/views/pms_ses_communication_views.xml +++ b/pms_l10n_es/views/pms_ses_communication_views.xml @@ -42,7 +42,15 @@ pms.ses.communication.tree pms.ses.communication - + + @@ -64,17 +72,84 @@ pms.ses.communication + - - - - - + + + + + + + + + + + + + + + + + + + Force Send Communication + + code + + records.force_send_communication() + + + form,list + + SES Communications pms.ses.communication diff --git a/pms_l10n_es/wizards/traveller_report.py b/pms_l10n_es/wizards/traveller_report.py index 66dca9a1b..2fec5fa5a 100644 --- a/pms_l10n_es/wizards/traveller_report.py +++ b/pms_l10n_es/wizards/traveller_report.py @@ -6,6 +6,7 @@ import json import logging import re import time +import traceback import xml.etree.cElementTree as ET import zipfile @@ -45,7 +46,7 @@ def replace_multiple_spaces(text: str) -> str: def clean_string_only_letters(string): clean_string = re.sub(r"[^a-zA-Z\s]", "", string).upper() clean_string = " ".join(clean_string.split()) - return + return clean_string def clean_string_only_numbers_and_letters(string): @@ -200,7 +201,7 @@ def _ses_xml_person_personal_info_elements(persona, checkin_partner): ) -def _ses_xml_municipality_code(residence_zip): +def _ses_xml_municipality_code(residence_zip, pms_property): with open( get_module_resource( "pms_l10n_es", "static/src/", "pms.ine.zip.municipality.ine.relation.csv" @@ -212,6 +213,11 @@ def _ses_xml_municipality_code(residence_zip): for fila in lector: if residence_zip in fila[0]: return fila[1][:5] + # REVIEW: If the zip code is not found, + # use provisory pms_property zip code + property_zip = pms_property.zip + if property_zip: + return property_zip[:5] raise ValidationError(_("The guest does not have a valid zip code.")) @@ -225,7 +231,10 @@ def _ses_xml_person_address_elements(persona, checkin_partner): ) if checkin_partner.residence_country_id.code == CODE_SPAIN: - municipio_code = _ses_xml_municipality_code(checkin_partner.residence_zip) + municipio_code = _ses_xml_municipality_code( + residence_zip=checkin_partner.residence_zip, + pms_property=checkin_partner.reservation_id.pms_property_id, + ) if municipio_code: ET.SubElement(direccion, "codigoMunicipio").text = municipio_code else: @@ -358,11 +367,15 @@ def _handle_request_exception(communication, e): ) else: if communication.state == "to_send": - communication.sending_result = f"Request error: {e}" + communication.sending_result = ( + f"Request error: {traceback.format_exc()}" + ) else: - communication.processing_result = f"Request error: {e}" + communication.processing_result = ( + f"Request error: {traceback.format_exc()}" + ) else: - communication.sending_result = f"Unexpected error: {e}" + communication.sending_result = f"Unexpected error: {traceback.format_exc()}" class TravellerReport(models.TransientModel): @@ -979,7 +992,7 @@ class TravellerReport(models.TransientModel): comunicacion = ET.SubElement(solicitud, "comunicacion") _ses_xml_contract_elements(comunicacion, reservation, people) for checkin_partner in reservation.checkin_partner_ids.filtered( - lambda x: x.state == "onboard" + lambda x: x.state in ["onboard", "done"] ): _ses_xml_person_elements(comunicacion, checkin_partner) @@ -998,7 +1011,7 @@ class TravellerReport(models.TransientModel): ): raise ValidationError(_("The reservations must be from the same property.")) elif all( - state != "onboard" + state not in ["onboard", "done"] for state in self.env["pms.reservation"] .browse(reservation_ids) .mapped("checkin_partner_ids") @@ -1006,7 +1019,7 @@ class TravellerReport(models.TransientModel): ): raise ValidationError(_("There are no guests onboard.")) elif not ignore_some_not_onboard and any( - state != "onboard" + state not in ["onboard", "done"] for state in self.env["pms.reservation"] .browse(reservation_ids) .mapped("checkin_partner_ids") @@ -1032,7 +1045,9 @@ class TravellerReport(models.TransientModel): num_people_on_board = len( self.env["pms.reservation"] .browse(reservation_id) - .checkin_partner_ids.filtered(lambda x: x.state == "onboard") + .checkin_partner_ids.filtered( + lambda x: x.state in ["onboard", "done"] + ) ) ET.SubElement( solicitud, @@ -1057,14 +1072,14 @@ class TravellerReport(models.TransientModel): return xml_str @api.model - def ses_send_communications(self, entity): - - for communication in self.env["pms.ses.communication"].search( - [ - ("state", "=", "to_send"), - ("entity", "=", entity), - ] - ): + def ses_send_communications(self, entity, communication_id=False): + domain = [ + ("state", "=", "to_send"), + ("entity", "=", entity), + ] + if communication_id: + domain.append(("id", "=", communication_id)) + for communication in self.env["pms.ses.communication"].search(domain): data = False try: if communication.operation == DELETE_OPERATION_CODE: @@ -1074,7 +1089,9 @@ class TravellerReport(models.TransientModel): ("state", "!=", "to_send"), ("entity", "=", communication.entity), ("operation", "=", CREATE_OPERATION_CODE), - ] + ], + order="id desc", + limit=1, ) data = ( "