mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
[IMP] pms_l10n_es: new mandatory institution to send reservations & guest info communications
This commit is contained in:
@@ -438,7 +438,7 @@ class PmsCheckinPartner(models.Model):
|
||||
elif not record.residence_state_id:
|
||||
record.residence_state_id = False
|
||||
|
||||
@api.depends(lambda self: self._checkin_manual_fields(depends=True))
|
||||
@api.depends(lambda self: self._get_depends_state_fields())
|
||||
def _compute_state(self):
|
||||
for record in self:
|
||||
if not record.state:
|
||||
@@ -454,7 +454,8 @@ class PmsCheckinPartner(models.Model):
|
||||
elif any(
|
||||
not getattr(record, field)
|
||||
for field in record._checkin_mandatory_fields(
|
||||
country=record.document_country_id
|
||||
residence_country=record.residence_country_id,
|
||||
document_type=record.document_type,
|
||||
)
|
||||
):
|
||||
record.state = "draft"
|
||||
@@ -783,7 +784,7 @@ class PmsCheckinPartner(models.Model):
|
||||
return res
|
||||
|
||||
@api.model
|
||||
def _checkin_manual_fields(self, country=False, depends=False):
|
||||
def _checkin_manual_fields(self, country=False):
|
||||
manual_fields = [
|
||||
"name",
|
||||
"partner_id",
|
||||
@@ -805,20 +806,19 @@ class PmsCheckinPartner(models.Model):
|
||||
"residence_country_id",
|
||||
"residence_state_id",
|
||||
]
|
||||
# api.depends need "reservation_id.state" in the lambda function
|
||||
if depends:
|
||||
manual_fields.append("reservation_id.state")
|
||||
return manual_fields
|
||||
|
||||
@api.model
|
||||
def _checkin_mandatory_fields(self, country=False, depends=False):
|
||||
def _get_depends_state_fields(self):
|
||||
manual_fields = self._checkin_manual_fields()
|
||||
manual_fields.append("reservation_id.state")
|
||||
return manual_fields
|
||||
|
||||
@api.model
|
||||
def _checkin_mandatory_fields(self, residence_country=False, document_type=False):
|
||||
mandatory_fields = [
|
||||
"name",
|
||||
]
|
||||
# api.depends need "reservation_id.state" in the lambda function
|
||||
if depends:
|
||||
mandatory_fields.extend(["reservation_id.state", "name"])
|
||||
|
||||
return mandatory_fields
|
||||
|
||||
@api.model
|
||||
@@ -888,7 +888,11 @@ class PmsCheckinPartner(models.Model):
|
||||
raise ValidationError(_("Its too late to checkin"))
|
||||
|
||||
if any(
|
||||
not getattr(record, field) for field in self._checkin_mandatory_fields()
|
||||
not getattr(record, field)
|
||||
for field in self._checkin_mandatory_fields(
|
||||
residence_country=record.residence_country_id,
|
||||
document_type=record.document_type,
|
||||
)
|
||||
):
|
||||
raise ValidationError(_("Personal data is missing for check-in"))
|
||||
vals = {
|
||||
|
||||
@@ -2117,7 +2117,8 @@ class PmsReservation(models.Model):
|
||||
else:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The Property and Sale Channel Origin are mandatory in the reservation"
|
||||
"The Property, Sale Channel Origin "
|
||||
"and name / partner name / agency are mandatory in the reservation"
|
||||
)
|
||||
)
|
||||
if vals.get("name", _("New")) == _("New") or "name" not in vals:
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"data": [
|
||||
"data/res.country.state.csv",
|
||||
"data/pms.ine.tourism.type.category.csv",
|
||||
# "data/pms.ine.zip.municipality.ine.relation.csv",
|
||||
"data/cron_jobs.xml",
|
||||
"data/pms_data.xml",
|
||||
"data/queue_data.xml",
|
||||
@@ -39,9 +40,11 @@
|
||||
"views/pms_property_views.xml",
|
||||
"views/pms_room_views.xml",
|
||||
"views/pms_log_institution_traveller_report_views.xml",
|
||||
"views/pms_ses_communication_views.xml",
|
||||
"views/pms_ine_tourism_type_category.xml",
|
||||
"views/res_partner_id_number_view.xml",
|
||||
"views/pms_checkin_partner_views.xml",
|
||||
"views/pms_reservation_views.xml",
|
||||
"wizards/traveller_report.xml",
|
||||
"wizards/wizard_ine.xml",
|
||||
"reports/invoice.xml",
|
||||
|
||||
@@ -16,4 +16,81 @@
|
||||
/>
|
||||
<field name="code">model.send_file_institution_async()</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="auto_creation_traveller_communications">
|
||||
<field name="name">Automatic Creation Traveller Communications</field>
|
||||
<field name="active" eval="False" />
|
||||
<field name="interval_number">3</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_traveller_report_wizard" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 16:57:00')"
|
||||
/>
|
||||
<field
|
||||
name="code"
|
||||
>model.create_pending_notifications_traveller_report()</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="auto_sending_pending_reservation_communications">
|
||||
<field
|
||||
name="name"
|
||||
>Automatic Sending Pending Reservation Communications</field>
|
||||
<field name="active" eval="False" />
|
||||
<field name="interval_number">3</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_traveller_report_wizard" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 16:57:00')"
|
||||
/>
|
||||
<field name="code">model.ses_send_communications('RH')</field>
|
||||
</record>
|
||||
|
||||
<record
|
||||
model="ir.cron"
|
||||
id="auto_sending_pending_traveller_reports_communications"
|
||||
>
|
||||
<field
|
||||
name="name"
|
||||
>Automatic Sending Pending Traveller Reports Communications</field>
|
||||
<field name="active" eval="False" />
|
||||
<field name="interval_number">3</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_traveller_report_wizard" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 16:57:00')"
|
||||
/>
|
||||
<field name="code">model.ses_send_communications('PV')</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.cron" id="auto_processing_sent_communications">
|
||||
<field name="name">Automatic Process Sent Communications</field>
|
||||
<field name="active" eval="False" />
|
||||
<field name="interval_number">3</field>
|
||||
<field name="user_id" ref="base.user_root" />
|
||||
<field name="interval_type">minutes</field>
|
||||
<field name="numbercall">-1</field>
|
||||
<field name="doall" eval="False" />
|
||||
<field name="state">code</field>
|
||||
<field name="model_id" ref="model_traveller_report_wizard" />
|
||||
<field
|
||||
name="nextcall"
|
||||
eval="datetime.now(pytz.timezone('UTC')).strftime('%Y-%m-%d 16:57:00')"
|
||||
/>
|
||||
<field name="code">model.ses_process_communications()</field>
|
||||
</record>
|
||||
</odoo>
|
||||
|
||||
@@ -8,3 +8,5 @@ from . import pms_room
|
||||
from . import res_partner
|
||||
from . import pms_checkin_partner
|
||||
from . import res_partner_id_number
|
||||
from . import pms_ses_communication
|
||||
from . import pms_reservation
|
||||
|
||||
@@ -3,6 +3,8 @@ import logging
|
||||
from odoo import api, fields, models
|
||||
|
||||
CODE_SPAIN = "ES"
|
||||
CODE_NIF = "D"
|
||||
CODE_NIE = "N"
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -34,9 +36,9 @@ class PmsCheckinPartner(models.Model):
|
||||
record.support_number = False
|
||||
|
||||
@api.model
|
||||
def _checkin_mandatory_fields(self, country=False, depends=False):
|
||||
def _checkin_mandatory_fields(self, residence_country=False, document_type=False):
|
||||
mandatory_fields = super(PmsCheckinPartner, self)._checkin_mandatory_fields(
|
||||
depends
|
||||
residence_country, document_type
|
||||
)
|
||||
mandatory_fields.extend(
|
||||
[
|
||||
@@ -46,20 +48,35 @@ class PmsCheckinPartner(models.Model):
|
||||
"document_type",
|
||||
"document_expedition_date",
|
||||
"nationality_id",
|
||||
"residence_street",
|
||||
"residence_city",
|
||||
"residence_country_id",
|
||||
"residence_zip",
|
||||
]
|
||||
)
|
||||
if depends or (country and country.code == CODE_SPAIN):
|
||||
|
||||
if residence_country and residence_country.code == CODE_SPAIN:
|
||||
mandatory_fields.extend(
|
||||
[
|
||||
"residence_state_id",
|
||||
"residence_street",
|
||||
"residence_city",
|
||||
]
|
||||
)
|
||||
if document_type.code and document_type.code == CODE_NIF:
|
||||
mandatory_fields.extend(
|
||||
[
|
||||
"lastname2",
|
||||
]
|
||||
)
|
||||
if document_type and document_type.code in [CODE_NIF, CODE_NIE]:
|
||||
mandatory_fields.extend(
|
||||
[
|
||||
"support_number",
|
||||
]
|
||||
)
|
||||
return mandatory_fields
|
||||
|
||||
@api.model
|
||||
def _checkin_manual_fields(self, country=False, depends=False):
|
||||
manual_fields = super(PmsCheckinPartner, self)._checkin_manual_fields(depends)
|
||||
def _checkin_manual_fields(self, country=False):
|
||||
manual_fields = super(PmsCheckinPartner, self)._checkin_manual_fields()
|
||||
manual_fields.extend(["support_number"])
|
||||
return manual_fields
|
||||
|
||||
@@ -17,6 +17,7 @@ class PmsProperty(models.Model):
|
||||
("policia_nacional", "Policía Nacional"),
|
||||
("ertxaintxa", "Ertxaintxa (soon)"),
|
||||
("mossos", "Mossos_d'esquadra (soon)"),
|
||||
("ses", "SES"),
|
||||
],
|
||||
string="Institution",
|
||||
help="Institution to send daily guest data.",
|
||||
@@ -26,6 +27,10 @@ class PmsProperty(models.Model):
|
||||
string="Institution property id",
|
||||
help="Id provided by institution to send data from property.",
|
||||
)
|
||||
ses_url = fields.Char(
|
||||
string="SES URL",
|
||||
help="URL to send the data to SES",
|
||||
)
|
||||
institution_user = fields.Char(
|
||||
string="Institution user", help="User provided by institution to send the data."
|
||||
)
|
||||
@@ -33,6 +38,11 @@ class PmsProperty(models.Model):
|
||||
string="Institution password",
|
||||
help="Password provided by institution to send the data.",
|
||||
)
|
||||
institution_lessor_id = fields.Char(
|
||||
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.",
|
||||
|
||||
88
pms_l10n_es/models/pms_reservation.py
Normal file
88
pms_l10n_es/models/pms_reservation.py
Normal file
@@ -0,0 +1,88 @@
|
||||
from odoo import api, fields, models
|
||||
|
||||
|
||||
class PmsReservation(models.Model):
|
||||
_inherit = "pms.reservation"
|
||||
ses_communication_ids = fields.One2many(
|
||||
string="SES Communications",
|
||||
help="Communications related to this reservation",
|
||||
comodel_name="pms.ses.communication",
|
||||
inverse_name="reservation_id",
|
||||
)
|
||||
is_ses = fields.Boolean(
|
||||
string="Is SES",
|
||||
readonly=True,
|
||||
compute="_compute_is_ses",
|
||||
)
|
||||
|
||||
@api.depends("pms_property_id")
|
||||
def _compute_is_ses(self):
|
||||
for record in self:
|
||||
record.is_ses = record.pms_property_id.institution == "ses"
|
||||
|
||||
@api.model
|
||||
def create_communication(self, reservation_id, operation, entity):
|
||||
self.env["pms.ses.communication"].create(
|
||||
{
|
||||
"reservation_id": reservation_id,
|
||||
"operation": operation,
|
||||
"entity": entity,
|
||||
}
|
||||
)
|
||||
|
||||
@api.model
|
||||
def create(self, vals):
|
||||
reservation = super(PmsReservation, self).create(vals)
|
||||
if reservation.pms_property_id.institution == "ses":
|
||||
self.create_communication(reservation.id, "A", "RH")
|
||||
return reservation
|
||||
|
||||
@api.model
|
||||
def create_communication_after_update_reservation(self, reservation, vals):
|
||||
state_changed = "state" in vals and (
|
||||
(vals["state"] != "cancel" and reservation.state == "cancel")
|
||||
or (vals["state"] == "cancel" and reservation.state != "cancel")
|
||||
)
|
||||
check_changed = (
|
||||
any(
|
||||
key in vals and vals[key] != getattr(reservation, key)
|
||||
for key in ["adults", "checkin", "checkout"]
|
||||
)
|
||||
and reservation.state != "cancel"
|
||||
)
|
||||
|
||||
if state_changed or check_changed:
|
||||
# delete all pending notifications
|
||||
self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
("state", "=", "to_send"),
|
||||
("entity", "=", "RH"),
|
||||
]
|
||||
).unlink()
|
||||
|
||||
# last communication
|
||||
last_communication = self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
("entity", "=", "RH"),
|
||||
],
|
||||
order="id desc",
|
||||
limit=1,
|
||||
)
|
||||
|
||||
if state_changed:
|
||||
if vals["state"] == "cancel" and last_communication.operation == "A":
|
||||
self.create_communication(reservation.id, "B", "RH")
|
||||
elif vals["state"] != "cancel" and last_communication.operation == "B":
|
||||
self.create_communication(reservation.id, "A", "RH")
|
||||
elif check_changed:
|
||||
if last_communication.operation == "A":
|
||||
self.create_communication(reservation.id, "B", "RH")
|
||||
self.create_communication(reservation.id, "A", "RH")
|
||||
|
||||
def write(self, vals):
|
||||
for record in self:
|
||||
if record.pms_property_id.institution == "ses":
|
||||
self.create_communication_after_update_reservation(record, vals)
|
||||
return super(PmsReservation, self).write(vals)
|
||||
86
pms_l10n_es/models/pms_ses_communication.py
Normal file
86
pms_l10n_es/models/pms_ses_communication.py
Normal file
@@ -0,0 +1,86 @@
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).
|
||||
from odoo import fields, models
|
||||
|
||||
|
||||
class PmsSesCommunication(models.Model):
|
||||
_name = "pms.ses.communication"
|
||||
_description = "SES Communication"
|
||||
reservation_id = fields.Many2one(
|
||||
string="Reservation",
|
||||
help="Reservation related to this communication",
|
||||
index=True,
|
||||
required=True,
|
||||
comodel_name="pms.reservation",
|
||||
)
|
||||
communication_id = fields.Char(
|
||||
string="Communication ID",
|
||||
help="ID of the communication",
|
||||
default=False,
|
||||
)
|
||||
operation = fields.Selection(
|
||||
string="Operation",
|
||||
help="Operation of the communication",
|
||||
selection=[("A", "New communication"), ("B", "Delete communication")],
|
||||
required=True,
|
||||
)
|
||||
entity = fields.Selection(
|
||||
string="Entity",
|
||||
help="Entity of the communication",
|
||||
selection=[("RH", "Reservation"), ("PV", "Traveller report")],
|
||||
required=True,
|
||||
)
|
||||
communication_time = fields.Datetime(
|
||||
string="Communication time",
|
||||
help="Date and time of the communication",
|
||||
)
|
||||
query_status_time = fields.Datetime(
|
||||
string="Query status time",
|
||||
help="Date and time of the last state query",
|
||||
)
|
||||
|
||||
state = fields.Selection(
|
||||
string="State",
|
||||
help="State of the communication",
|
||||
default="to_send",
|
||||
required=True,
|
||||
selection=[
|
||||
("to_send", "Pending Notification"),
|
||||
("to_process", "Pending Processing"),
|
||||
("error_sending", "Error Sending"),
|
||||
("error_processing", "Error Processing"),
|
||||
("processed", "Processed"),
|
||||
],
|
||||
)
|
||||
sending_result = fields.Text(
|
||||
string="Sending Result",
|
||||
help="Notification sending result",
|
||||
)
|
||||
processing_result = fields.Text(
|
||||
string="Processing Result",
|
||||
help="Notification processing result",
|
||||
)
|
||||
communication_xml = fields.Text(
|
||||
string="XML Com.",
|
||||
help="XML content communication",
|
||||
)
|
||||
communication_soap = fields.Text(
|
||||
string="SOAP Com.",
|
||||
help="SOAP content communication",
|
||||
)
|
||||
response_communication_soap = fields.Text(
|
||||
string="SOAP Resp. Com.",
|
||||
help="SOAP response communication",
|
||||
)
|
||||
|
||||
query_status_xml = fields.Text(
|
||||
string="XML Query Status",
|
||||
help="XML query status content communication",
|
||||
)
|
||||
query_status_soap = fields.Text(
|
||||
string="SOAP Query Status",
|
||||
help="SOAP query status content communication",
|
||||
)
|
||||
response_query_status_soap = fields.Text(
|
||||
string="SOAP Resp. Status",
|
||||
help="SOAP response status query",
|
||||
)
|
||||
@@ -3,3 +3,4 @@ user_access_traveller_report_wizard,user_access_traveller_report_wizard,model_tr
|
||||
user_access_traveller_report_logs,user_access_traveller_report_logs,model_pms_log_institution_traveller_report,pms.group_pms_user,1,1,1,1
|
||||
user_access_pms_ine_tourism_type_category,user_access_pms_ine_tourism_type_category,model_pms_ine_tourism_type_category,pms.group_pms_user,1,1,1,1
|
||||
user_access_pms_ine_wizard,user_access_pms_ine_wizard,model_pms_ine_wizard,pms.group_pms_user,1,1,1,1
|
||||
user_access_pms_ses_communication,user_access_pms_ses_communication,model_pms_ses_communication,pms.group_pms_user,1,1,1,1
|
||||
|
||||
|
14609
pms_l10n_es/static/src/pms.ine.zip.municipality.ine.relation.csv
Normal file
14609
pms_l10n_es/static/src/pms.ine.zip.municipality.ine.relation.csv
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,3 +1,4 @@
|
||||
from . import test_wizard_ine
|
||||
from . import test_res_partner
|
||||
from . import test_wizard_traveller_report
|
||||
from . import test_pms_ses_communication
|
||||
|
||||
285
pms_l10n_es/tests/test_pms_ses_communication.py
Normal file
285
pms_l10n_es/tests/test_pms_ses_communication.py
Normal file
@@ -0,0 +1,285 @@
|
||||
from odoo import fields
|
||||
from odoo.tools.safe_eval import datetime
|
||||
|
||||
from .common import TestPms
|
||||
|
||||
|
||||
class TestPmsSesCommunication(TestPms):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.sale_channel_direct1 = self.env["pms.sale.channel"].create(
|
||||
{
|
||||
"name": "Door",
|
||||
"channel_type": "direct",
|
||||
}
|
||||
)
|
||||
# create room type
|
||||
self.room_type = self.env["pms.room.type"].create(
|
||||
{
|
||||
"name": "Room type test",
|
||||
"default_code": "DBL_Test",
|
||||
"class_id": self.room_type_class1.id,
|
||||
}
|
||||
)
|
||||
# room
|
||||
self.room_double_1 = self.env["pms.room"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"name": "Room test 1",
|
||||
"room_type_id": self.room_type.id,
|
||||
"capacity": 2,
|
||||
}
|
||||
)
|
||||
self.pms_property1.institution = "ses"
|
||||
|
||||
def test_create_notification_when_create_reservation(self):
|
||||
# ARRANGE/ACT
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"room_type_id": self.room_type.id,
|
||||
"checkin": "2021-01-01",
|
||||
"checkout": "2021-01-02",
|
||||
"adults": 2,
|
||||
"children": 0,
|
||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||
"partner_name": "Test reservation",
|
||||
}
|
||||
)
|
||||
# ASSERT
|
||||
last_notification = self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
]
|
||||
)
|
||||
self.assertEqual(
|
||||
last_notification.operation,
|
||||
"A",
|
||||
"Creating a reservation should create a notification with operation A",
|
||||
)
|
||||
|
||||
def test_not_create_notification_when_cancel_reservation_and_not_sent(self):
|
||||
# ARRANGE
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"room_type_id": self.room_type.id,
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=2),
|
||||
"adults": 2,
|
||||
"children": 0,
|
||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||
"partner_name": "Test reservation",
|
||||
}
|
||||
)
|
||||
# ACT
|
||||
reservation.action_cancel()
|
||||
# ASSERT
|
||||
last_notifications = self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
],
|
||||
order="id",
|
||||
)
|
||||
self.assertFalse(
|
||||
last_notifications,
|
||||
"Cancelling a reservation not sent should not create a notification",
|
||||
)
|
||||
|
||||
def test_create_notification_when_cancel_reservation_and_is_sent(self):
|
||||
# ARRANGE
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"room_type_id": self.room_type.id,
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=2),
|
||||
"adults": 2,
|
||||
"children": 0,
|
||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||
"partner_name": "Test reservation",
|
||||
}
|
||||
)
|
||||
notification_after_create_reservation = self.env[
|
||||
"pms.ses.communication"
|
||||
].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
("operation", "=", "A"),
|
||||
]
|
||||
)
|
||||
notification_after_create_reservation.state = "to_process"
|
||||
# ACT
|
||||
reservation.action_cancel()
|
||||
# ASSERT
|
||||
last_notifications = (
|
||||
self.env["pms.ses.communication"]
|
||||
.search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
],
|
||||
order="id",
|
||||
)
|
||||
.mapped("operation")
|
||||
)
|
||||
self.assertEqual(
|
||||
last_notifications,
|
||||
["A", "B"],
|
||||
"Canceling a reservation should create a notification with operation B",
|
||||
)
|
||||
|
||||
def test_create_notification_when_modify_reservation_and_not_sent(self):
|
||||
# ARRANGE
|
||||
update_operations = [
|
||||
{
|
||||
"adults": 1,
|
||||
},
|
||||
{
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=10),
|
||||
},
|
||||
{
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=12),
|
||||
},
|
||||
]
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"room_type_id": self.room_type.id,
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=13),
|
||||
"adults": 2,
|
||||
"children": 0,
|
||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||
"partner_name": "Test reservation",
|
||||
}
|
||||
)
|
||||
# ACT & ASSERT
|
||||
for _index, update_operation in enumerate(update_operations):
|
||||
with self.subTest(k=update_operation):
|
||||
reservation.write(update_operation)
|
||||
last_notification_operations = (
|
||||
self.env["pms.ses.communication"]
|
||||
.search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
],
|
||||
order="id",
|
||||
)
|
||||
.mapped("operation")
|
||||
)
|
||||
self.assertEqual(
|
||||
["A"],
|
||||
last_notification_operations,
|
||||
"Update adults should create 2 notifications with operations A and B",
|
||||
)
|
||||
|
||||
def test_create_notification_when_modify_reservation_and_is_sent(self):
|
||||
# ARRANGE
|
||||
update_operations = [
|
||||
{
|
||||
"adults": 1,
|
||||
},
|
||||
{
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=10),
|
||||
},
|
||||
{
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=12),
|
||||
},
|
||||
]
|
||||
reservation = self.env["pms.reservation"].create(
|
||||
{
|
||||
"pms_property_id": self.pms_property1.id,
|
||||
"room_type_id": self.room_type.id,
|
||||
"checkin": fields.date.today() + datetime.timedelta(days=1),
|
||||
"checkout": fields.date.today() + datetime.timedelta(days=13),
|
||||
"adults": 2,
|
||||
"children": 0,
|
||||
"sale_channel_origin_id": self.sale_channel_direct1.id,
|
||||
"partner_name": "Test reservation",
|
||||
}
|
||||
)
|
||||
reservation_communications = self.env["pms.ses.communication"].search(
|
||||
[("reservation_id", "=", reservation.id)]
|
||||
)
|
||||
reservation_communications.state = "to_process"
|
||||
# ACT & ASSERT
|
||||
for _index, update_operation in enumerate(update_operations):
|
||||
with self.subTest(k=update_operation):
|
||||
reservation.write(update_operation)
|
||||
reservation_communications = (
|
||||
self.env["pms.ses.communication"]
|
||||
.search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
],
|
||||
order="id",
|
||||
)
|
||||
.mapped("operation")
|
||||
)
|
||||
self.assertEqual(
|
||||
["A", "B", "A"],
|
||||
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",
|
||||
)
|
||||
@@ -250,6 +250,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_1.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -257,6 +260,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_2.id,
|
||||
"reservation_id": self.reservation_1.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create reservation 2
|
||||
@@ -275,12 +281,18 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_3.id,
|
||||
"reservation_id": self.reservation_2.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
self.checkin4 = self.env["pms.checkin.partner"].create(
|
||||
{
|
||||
"partner_id": self.partner_4.id,
|
||||
"reservation_id": self.reservation_2.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create reservation 3
|
||||
@@ -299,6 +311,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_5.id,
|
||||
"reservation_id": self.reservation_3.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create reservation property 2
|
||||
@@ -317,6 +332,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_5.id,
|
||||
"reservation_id": self.reservation_property_2.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -350,6 +368,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_6.id,
|
||||
"reservation_id": self.reservation_4.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
|
||||
@@ -357,6 +378,9 @@ class TestWizardINE(TestPms):
|
||||
{
|
||||
"partner_id": self.partner_7.id,
|
||||
"reservation_id": self.reservation_4.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# checkin partners on board
|
||||
@@ -387,6 +411,9 @@ class TestWizardINE(TestPms):
|
||||
"residence_country_id": self.country_russia.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
self.partner_russia_2 = self.env["res.partner"].create(
|
||||
@@ -397,6 +424,9 @@ class TestWizardINE(TestPms):
|
||||
"residence_country_id": self.country_russia.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
self.partner_russia_3 = self.env["res.partner"].create(
|
||||
@@ -407,6 +437,9 @@ class TestWizardINE(TestPms):
|
||||
"residence_country_id": self.country_russia.id,
|
||||
"birthdate_date": "2000-06-25",
|
||||
"gender": "male",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create document for 3 checkin partners (russia)
|
||||
@@ -670,38 +703,38 @@ class TestWizardINE(TestPms):
|
||||
[("name", "=", "Pontevedra")]
|
||||
)
|
||||
|
||||
self.checkin1.nationality_id = country_spain
|
||||
self.partner_1.nationality_id = country_spain
|
||||
self.checkin1.residence_country_id = country_spain
|
||||
self.partner_1.residence_country_id = country_spain
|
||||
self.checkin1.residence_state_id = state_ourense
|
||||
self.partner_1.residence_state_id = state_ourense
|
||||
|
||||
self.checkin2.nationality_id = country_spain
|
||||
self.partner_2.nationality_id = country_spain
|
||||
self.checkin2.residence_country_id = country_spain
|
||||
self.partner_2.residence_country_id = country_spain
|
||||
self.checkin2.residence_state_id = state_pontevedra
|
||||
self.partner_2.residence_state_id = state_pontevedra
|
||||
|
||||
self.checkin3.nationality_id = country_spain
|
||||
self.partner_3.nationality_id = country_spain
|
||||
self.checkin3.residence_country_id = country_spain
|
||||
self.partner_3.residence_country_id = country_spain
|
||||
self.checkin3.residence_state_id = state_ourense
|
||||
self.partner_3.residence_state_id = state_ourense
|
||||
|
||||
self.checkin4.nationality_id = country_spain
|
||||
self.partner_4.nationality_id = country_spain
|
||||
self.checkin4.residence_country_id = country_spain
|
||||
self.partner_4.residence_country_id = country_spain
|
||||
self.checkin4.residence_state_id = state_ourense
|
||||
self.partner_4.residence_state_id = state_ourense
|
||||
|
||||
self.checkin5.nationality_id = country_spain
|
||||
self.partner_5.nationality_id = country_spain
|
||||
self.checkin5.residence_country_id = country_spain
|
||||
self.partner_5.residence_country_id = country_spain
|
||||
self.checkin5.residence_state_id = state_madrid
|
||||
self.partner_5.residence_state_id = state_madrid
|
||||
|
||||
self.checkin6.nationality_id = country_spain
|
||||
self.partner_6.nationality_id = country_spain
|
||||
self.checkin6.residence_country_id = country_spain
|
||||
self.partner_6.residence_country_id = country_spain
|
||||
self.checkin6.residence_state_id = state_madrid
|
||||
self.partner_6.residence_state_id = state_madrid
|
||||
|
||||
self.checkin7.nationality_id = country_spain
|
||||
self.partner_7.nationality_id = country_spain
|
||||
self.checkin7.residence_country_id = country_spain
|
||||
self.partner_7.residence_country_id = country_spain
|
||||
self.checkin7.residence_state_id = state_madrid
|
||||
self.partner_7.residence_state_id = state_madrid
|
||||
|
||||
@@ -914,7 +947,7 @@ class TestWizardINE(TestPms):
|
||||
"""
|
||||
# ARRANGE
|
||||
self.ideal_scenario()
|
||||
self.reservation_1.checkin_partner_ids[1].nationality_id = False
|
||||
self.reservation_1.checkin_partner_ids[1].residence_country_id = False
|
||||
start_date = datetime.date(2021, 2, 1)
|
||||
end_date = datetime.date(2021, 2, 4)
|
||||
|
||||
@@ -969,38 +1002,38 @@ class TestWizardINE(TestPms):
|
||||
[("name", "=", "Ourense (Orense)")]
|
||||
)
|
||||
|
||||
self.checkin1.nationality_id = country_spain
|
||||
self.partner_1.nationality_id = country_spain
|
||||
self.checkin1.residence_country_id = country_spain
|
||||
self.partner_1.residence_country_id = country_spain
|
||||
self.checkin1.residence_state_id = state_ourense
|
||||
self.partner_1.residence_state_id = state_ourense
|
||||
|
||||
self.checkin2.nationality_id = country_spain
|
||||
self.partner_2.nationality_id = country_spain
|
||||
self.checkin2.residence_country_id = country_spain
|
||||
self.partner_2.residence_country_id = country_spain
|
||||
self.checkin2.residence_state_id = False
|
||||
self.partner_2.residence_state_id = False
|
||||
|
||||
self.checkin3.nationality_id = country_spain
|
||||
self.partner_3.nationality_id = country_spain
|
||||
self.checkin3.residence_country_id = country_spain
|
||||
self.partner_3.residence_country_id = country_spain
|
||||
self.checkin3.residence_state_id = state_ourense
|
||||
self.partner_3.residence_state_id = state_ourense
|
||||
|
||||
self.checkin4.nationality_id = country_spain
|
||||
self.partner_4.nationality_id = country_spain
|
||||
self.checkin4.residence_country_id = country_spain
|
||||
self.partner_4.residence_country_id = country_spain
|
||||
self.checkin4.residence_state_id = state_ourense
|
||||
self.partner_4.residence_state_id = state_ourense
|
||||
|
||||
self.checkin5.nationality_id = country_spain
|
||||
self.partner_5.nationality_id = country_spain
|
||||
self.checkin5.residence_country_id = country_spain
|
||||
self.partner_5.residence_country_id = country_spain
|
||||
self.checkin5.residence_state_id = state_madrid
|
||||
self.partner_5.residence_state_id = state_madrid
|
||||
|
||||
self.checkin6.nationality_id = country_spain
|
||||
self.partner_6.nationality_id = country_spain
|
||||
self.checkin6.residence_country_id = country_spain
|
||||
self.partner_6.residence_country_id = country_spain
|
||||
self.checkin6.residence_state_id = state_madrid
|
||||
self.partner_6.residence_state_id = state_madrid
|
||||
|
||||
self.checkin7.nationality_id = country_spain
|
||||
self.partner_7.nationality_id = country_spain
|
||||
self.checkin7.residence_country_id = country_spain
|
||||
self.partner_7.residence_country_id = country_spain
|
||||
self.checkin7.residence_state_id = state_madrid
|
||||
self.partner_7.residence_state_id = state_madrid
|
||||
|
||||
|
||||
@@ -124,6 +124,9 @@ class TestWizardTravellerReport(TestPms):
|
||||
"reservation_id": self.reservation_1.id,
|
||||
"firstname": "John",
|
||||
"lastname": "Doe",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create reservation 2
|
||||
@@ -144,6 +147,9 @@ class TestWizardTravellerReport(TestPms):
|
||||
"reservation_id": self.reservation_2.id,
|
||||
"firstname": "Martha",
|
||||
"lastname": "Stewart",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# checkin partners on board
|
||||
@@ -188,6 +194,9 @@ class TestWizardTravellerReport(TestPms):
|
||||
"firstname": "John",
|
||||
"lastname": "Doe",
|
||||
"nationality_id": self.country_italy.id,
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# Create reservation 2
|
||||
@@ -208,6 +217,9 @@ class TestWizardTravellerReport(TestPms):
|
||||
"reservation_id": self.reservation_2.id,
|
||||
"firstname": "Martha",
|
||||
"lastname": "Stewart",
|
||||
"residence_street": "Test street 1",
|
||||
"residence_city": "Test city",
|
||||
"residence_zip": "08001",
|
||||
}
|
||||
)
|
||||
# checkin partners on board
|
||||
|
||||
@@ -18,7 +18,12 @@
|
||||
<div class="col-6">
|
||||
<group name="property_data">
|
||||
<field name="institution" />
|
||||
<field name="institution_lessor_id" />
|
||||
<field name="institution_property_id" />
|
||||
<field
|
||||
name="ses_url"
|
||||
attrs="{'invisible': [('institution','!=','ses')]}"
|
||||
/>
|
||||
</group>
|
||||
</div>
|
||||
<div class="col-6 px-0">
|
||||
@@ -31,6 +36,7 @@
|
||||
class="btn btn-primary btn-sm"
|
||||
type="object"
|
||||
string="Test user/password"
|
||||
attrs="{'invisible': [('institution','=','ses')]}"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
34
pms_l10n_es/views/pms_reservation_views.xml
Normal file
34
pms_l10n_es/views/pms_reservation_views.xml
Normal file
@@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record id="view_pms_reservation_from_pms_l110n_es" model="ir.ui.view">
|
||||
<field name="name">Rerservation Form l10n_es</field>
|
||||
<field name="model">pms.reservation</field>
|
||||
<field name="inherit_id" ref="pms.pms_reservation_view_form" />
|
||||
<field name="arch" type="xml">
|
||||
<xpath expr="//page[@name='others']" position="before">
|
||||
<page
|
||||
string="SES Communications"
|
||||
name="property_general"
|
||||
attrs="{'invisible': [('is_ses','!=',True),'|',('is_ses','=',True),('ses_communication_ids','!=',False)]}"
|
||||
>
|
||||
<field name="is_ses" invisible="1" />
|
||||
<field name="ses_communication_ids">
|
||||
<tree string="Comunicaciones">
|
||||
<field name="reservation_id" />
|
||||
<field name="communication_id" />
|
||||
<field name="operation" />
|
||||
<field name="entity" />
|
||||
<!-- times -->
|
||||
<field name="create_date" />
|
||||
<field name="communication_time" />
|
||||
<field name="query_status_time" />
|
||||
<field name="state" />
|
||||
<field name="sending_result" />
|
||||
<field name="processing_result" />
|
||||
</tree>
|
||||
</field>
|
||||
</page>
|
||||
</xpath>
|
||||
</field>
|
||||
</record>
|
||||
</odoo>
|
||||
90
pms_l10n_es/views/pms_ses_communication_views.xml
Normal file
90
pms_l10n_es/views/pms_ses_communication_views.xml
Normal file
@@ -0,0 +1,90 @@
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<odoo>
|
||||
<record model="ir.ui.view" id="pms_ses_communication_view_form">
|
||||
<field name="name">pms.ses.communication.form</field>
|
||||
<field name="model">pms.ses.communication</field>
|
||||
<field name="arch" type="xml">
|
||||
<form string="Log institution traveller report detail">
|
||||
<sheet>
|
||||
<group>
|
||||
<field name="reservation_id" />
|
||||
<field name="communication_id" />
|
||||
<field name="operation" />
|
||||
<field name="entity" />
|
||||
<!-- times -->
|
||||
<field name="create_date" />
|
||||
<field name="communication_time" />
|
||||
<field name="query_status_time" />
|
||||
<!-- results -->
|
||||
<field name="state" />
|
||||
<field name="sending_result" />
|
||||
<field name="processing_result" />
|
||||
<!-- communication soap, xml (request) & soap (response) -->
|
||||
<field name="communication_xml" widget="CopyClipboardChar" />
|
||||
<field name="communication_soap" widget="CopyClipboardChar" />
|
||||
<field
|
||||
name="response_communication_soap"
|
||||
widget="CopyClipboardChar"
|
||||
/>
|
||||
<!-- processing soap, xml (request) & soap (response) -->
|
||||
<field name="query_status_xml" widget="CopyClipboardChar" />
|
||||
<field name="query_status_soap" widget="CopyClipboardChar" />
|
||||
<field
|
||||
name="response_query_status_soap"
|
||||
widget="CopyClipboardChar"
|
||||
/>
|
||||
</group>
|
||||
</sheet>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
<record model="ir.ui.view" id="pms_ses_communication_view_tree">
|
||||
<field name="name">pms.ses.communication.tree</field>
|
||||
<field name="model">pms.ses.communication</field>
|
||||
<field name="arch" type="xml">
|
||||
<tree name="Property Ubications" create="false">
|
||||
<field name="reservation_id" />
|
||||
<field name="communication_id" />
|
||||
<field name="operation" />
|
||||
<field name="entity" />
|
||||
<!-- times -->
|
||||
<field name="create_date" />
|
||||
<field name="communication_time" />
|
||||
<field name="query_status_time" />
|
||||
<!-- results -->
|
||||
<field name="state" />
|
||||
<field name="sending_result" />
|
||||
<field name="processing_result" />
|
||||
</tree>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.ui.view" id="pms_ses_communication_view_search">
|
||||
<field name="name">pms.ses.communication.search</field>
|
||||
<field name="model">pms.ses.communication</field>
|
||||
<field name="arch" type="xml">
|
||||
<search string="Log SES Communications">
|
||||
<field name="communication_id" />
|
||||
<field name="reservation_id" />
|
||||
<!-- times -->
|
||||
<field name="create_date" />
|
||||
<field name="communication_time" />
|
||||
<field name="query_status_time" />
|
||||
<field name="state" />
|
||||
</search>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
<record model="ir.actions.act_window" id="open_pms_ses_communication_form_tree">
|
||||
<field name="name">SES Communications</field>
|
||||
<field name="res_model">pms.ses.communication</field>
|
||||
<field name="view_mode">tree,form</field>
|
||||
</record>
|
||||
<menuitem
|
||||
name="SES Communications"
|
||||
id="menu_open_pms_ses_communication_form_tree"
|
||||
action="open_pms_ses_communication_form_tree"
|
||||
parent="pms.menu_reservations"
|
||||
sequence="29"
|
||||
/>
|
||||
</odoo>
|
||||
@@ -1,13 +1,18 @@
|
||||
import base64
|
||||
import csv
|
||||
import datetime
|
||||
import io
|
||||
import json
|
||||
import logging
|
||||
import re
|
||||
import time
|
||||
import xml.etree.cElementTree as ET
|
||||
import zipfile
|
||||
|
||||
import requests
|
||||
from bs4 import BeautifulSoup as bs
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from requests.packages.urllib3.exceptions import InsecureRequestWarning
|
||||
|
||||
from odoo import _, api, fields, models
|
||||
from odoo.exceptions import MissingError, ValidationError
|
||||
@@ -15,6 +20,305 @@ from odoo.modules.module import get_module_resource
|
||||
|
||||
_logger = logging.getLogger(__name__)
|
||||
|
||||
CODE_SPAIN = "ES"
|
||||
CODE_PASSPORT = "P"
|
||||
CODE_DNI = "D"
|
||||
CODE_NIE = "N"
|
||||
|
||||
REQUEST_CODE_OK = "0"
|
||||
XML_OK = "1"
|
||||
XML_PROCESSING = "4"
|
||||
XML_PENDING = "5"
|
||||
|
||||
DELETE_OPERATION_CODE = "B"
|
||||
|
||||
# Disable insecure request warnings
|
||||
requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
|
||||
|
||||
|
||||
def _string_to_zip_to_base64(string_data):
|
||||
zip_buffer = io.BytesIO()
|
||||
with zipfile.ZipFile(zip_buffer, "w", zipfile.ZIP_DEFLATED) as zip_file:
|
||||
zip_file.writestr("data.xml", string_data.encode("utf-8"))
|
||||
zip_buffer.seek(0)
|
||||
zip_data = zip_buffer.read()
|
||||
zip_base64 = base64.b64encode(zip_data)
|
||||
return zip_base64.decode()
|
||||
|
||||
|
||||
def _ses_xml_payment_elements(contrato, reservation):
|
||||
pago = ET.SubElement(contrato, "pago")
|
||||
payments = reservation.folio_id.payment_ids.filtered(lambda x: x.state == "posted")
|
||||
tipo_pago = "DESTI"
|
||||
if payments:
|
||||
payment = payments[0]
|
||||
tipo_pago = "EFECT" if payment.journal_id.type == "cash" else "PLATF"
|
||||
ET.SubElement(pago, "tipoPago").text = tipo_pago
|
||||
|
||||
|
||||
def _ses_xml_contract_elements(comunicacion, reservation):
|
||||
contrato = ET.SubElement(comunicacion, "contrato")
|
||||
ET.SubElement(contrato, "referencia").text = reservation.name
|
||||
ET.SubElement(contrato, "fechaContrato").text = str(reservation.date_order)[:10]
|
||||
ET.SubElement(
|
||||
contrato, "fechaEntrada"
|
||||
).text = f"{str(reservation.checkin)[:10]}T00:00:00"
|
||||
ET.SubElement(
|
||||
contrato, "fechaSalida"
|
||||
).text = f"{str(reservation.checkout)[:10]}T00:00:00"
|
||||
ET.SubElement(contrato, "numPersonas").text = str(reservation.adults)
|
||||
_ses_xml_payment_elements(contrato, reservation)
|
||||
|
||||
|
||||
def _ses_xml_text_element_and_validate(parent, tag, text, error_message):
|
||||
if text:
|
||||
ET.SubElement(parent, tag).text = text
|
||||
else:
|
||||
raise ValidationError(error_message)
|
||||
|
||||
|
||||
def _ses_xml_map_document_type(code):
|
||||
if code == CODE_DNI:
|
||||
return "NIF"
|
||||
elif code == CODE_NIE:
|
||||
return "NIE"
|
||||
elif code == CODE_PASSPORT:
|
||||
return "PAS"
|
||||
else:
|
||||
return "OTRO"
|
||||
|
||||
|
||||
def _ses_xml_person_names_elements(persona, reservation, checkin_partner):
|
||||
if reservation:
|
||||
name = False
|
||||
if reservation.partner_id.firstname:
|
||||
name = reservation.partner_id.firstname
|
||||
elif reservation.partner_name:
|
||||
name = reservation.partner_name.split(" ")[0]
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"nombre",
|
||||
name,
|
||||
_("The reservation does not have a name."),
|
||||
)
|
||||
|
||||
if reservation.partner_id.lastname:
|
||||
firstname = reservation.partner_id.lastname
|
||||
elif reservation.partner_name and len(reservation.partner_name.split(" ")) > 1:
|
||||
firstname = reservation.partner_name.split(" ")[1]
|
||||
else:
|
||||
firstname = "No aplica"
|
||||
ET.SubElement(persona, "apellido1").text = firstname
|
||||
|
||||
elif checkin_partner:
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"nombre",
|
||||
checkin_partner.firstname,
|
||||
_("The guest does not have a name."),
|
||||
)
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"apellido1",
|
||||
checkin_partner.lastname,
|
||||
_("The guest does not have a lastname."),
|
||||
)
|
||||
|
||||
if checkin_partner.document_type.code == CODE_DNI:
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"apellido2",
|
||||
checkin_partner.partner_id.lastname2,
|
||||
_("The guest does not have a second lastname."),
|
||||
)
|
||||
|
||||
|
||||
def _ses_xml_person_personal_info_elements(persona, checkin_partner):
|
||||
ET.SubElement(persona, "rol").text = "VI"
|
||||
|
||||
_ses_xml_person_names_elements(
|
||||
persona, reservation=False, checkin_partner=checkin_partner
|
||||
)
|
||||
|
||||
if checkin_partner.document_type.code:
|
||||
document_type = _ses_xml_map_document_type(checkin_partner.document_type.code)
|
||||
ET.SubElement(persona, "tipoDocumento").text = document_type
|
||||
else:
|
||||
raise ValidationError(_("The guest does not have a document type."))
|
||||
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"numeroDocumento",
|
||||
checkin_partner.document_number,
|
||||
_("The guest does not have a document number."),
|
||||
)
|
||||
|
||||
if checkin_partner.document_type.code in [CODE_DNI, CODE_NIE]:
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"soporteDocumento",
|
||||
checkin_partner.support_number,
|
||||
_("The guest does not have a support number."),
|
||||
)
|
||||
_ses_xml_text_element_and_validate(
|
||||
persona,
|
||||
"fechaNacimiento",
|
||||
str(checkin_partner.birthdate_date)[:10],
|
||||
_("The guest does not have a birthdate."),
|
||||
)
|
||||
|
||||
|
||||
def _ses_xml_municipality_code(residence_zip):
|
||||
with open(
|
||||
get_module_resource(
|
||||
"pms_l10n_es", "static/src/", "pms.ine.zip.municipality.ine.relation.csv"
|
||||
),
|
||||
"r",
|
||||
newline="",
|
||||
) as f:
|
||||
lector = csv.reader(f)
|
||||
for fila in lector:
|
||||
if residence_zip in fila[0]:
|
||||
return fila[1][:5]
|
||||
raise ValidationError(_("The guest does not have a valid zip code."))
|
||||
|
||||
|
||||
def _ses_xml_person_address_elements(persona, checkin_partner):
|
||||
direccion = ET.SubElement(persona, "direccion")
|
||||
_ses_xml_text_element_and_validate(
|
||||
direccion,
|
||||
"direccion",
|
||||
checkin_partner.residence_street,
|
||||
_("The guest does not have a street."),
|
||||
)
|
||||
|
||||
if checkin_partner.residence_country_id.code == CODE_SPAIN:
|
||||
municipio_code = _ses_xml_municipality_code(checkin_partner.residence_zip)
|
||||
if municipio_code:
|
||||
ET.SubElement(direccion, "codigoMunicipio").text = municipio_code
|
||||
else:
|
||||
_ses_xml_text_element_and_validate(
|
||||
direccion,
|
||||
"nombreMunicipio",
|
||||
checkin_partner.residence_city,
|
||||
_("The guest does not have a city."),
|
||||
)
|
||||
|
||||
_ses_xml_text_element_and_validate(
|
||||
direccion,
|
||||
"codigoPostal",
|
||||
checkin_partner.residence_zip,
|
||||
_("The guest does not have a zip code."),
|
||||
)
|
||||
_ses_xml_text_element_and_validate(
|
||||
direccion,
|
||||
"pais",
|
||||
checkin_partner.residence_country_id.code_alpha3,
|
||||
_("The guest does not have a country."),
|
||||
)
|
||||
|
||||
|
||||
def _ses_xml_person_contact_elements(persona, reservation, checkin_partner=False):
|
||||
partner = reservation.partner_id
|
||||
contact_methods = []
|
||||
if checkin_partner:
|
||||
contact_methods.extend(
|
||||
[
|
||||
checkin_partner.mobile,
|
||||
checkin_partner.phone,
|
||||
checkin_partner.email,
|
||||
]
|
||||
)
|
||||
contact_methods.extend(
|
||||
[
|
||||
partner.mobile,
|
||||
partner.phone,
|
||||
partner.email,
|
||||
reservation.email,
|
||||
reservation.pms_property_id.partner_id.email,
|
||||
reservation.pms_property_id.partner_id.phone,
|
||||
]
|
||||
)
|
||||
|
||||
for contact in contact_methods:
|
||||
if contact:
|
||||
tag = "telefono" if "@" not in contact else "correo"
|
||||
ET.SubElement(persona, tag).text = contact
|
||||
break
|
||||
else:
|
||||
raise ValidationError(
|
||||
_(
|
||||
"The guest/reservation partner and property does not "
|
||||
"have a contact method (mail or phone)"
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def _ses_xml_person_elements(comunicacion, checkin_partner):
|
||||
persona = ET.SubElement(comunicacion, "persona")
|
||||
_ses_xml_person_personal_info_elements(persona, checkin_partner)
|
||||
_ses_xml_person_address_elements(persona, checkin_partner)
|
||||
_ses_xml_person_contact_elements(
|
||||
persona, checkin_partner.reservation_id, checkin_partner
|
||||
)
|
||||
|
||||
|
||||
def _get_auth_headers(communication):
|
||||
user = communication.reservation_id.pms_property_id.institution_user
|
||||
password = communication.reservation_id.pms_property_id.institution_password
|
||||
|
||||
user_and_password_base64 = "Basic " + base64.b64encode(
|
||||
bytes(user + ":" + password, "utf-8")
|
||||
).decode("utf-8")
|
||||
|
||||
return {
|
||||
"Authorization": user_and_password_base64,
|
||||
"Content-Type": "text/xml; charset=utf-8",
|
||||
}
|
||||
|
||||
|
||||
def _generate_payload(lessor_id, operation, entity, data):
|
||||
return f"""
|
||||
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
|
||||
xmlns:com="http://www.soap.servicios.hospedajes.mir.es/comunicacion">
|
||||
<soapenv:Header/>
|
||||
<soapenv:Body>
|
||||
<com:comunicacionRequest>
|
||||
<peticion>
|
||||
<cabecera>
|
||||
<codigoArrendador>{lessor_id}</codigoArrendador>
|
||||
<aplicacion>Roomdoo</aplicacion>
|
||||
<tipoOperacion>{operation}</tipoOperacion>
|
||||
<tipoComunicacion>{entity}</tipoComunicacion>
|
||||
</cabecera>
|
||||
<solicitud>{data}</solicitud>
|
||||
</peticion>
|
||||
</com:comunicacionRequest>
|
||||
</soapenv:Body>
|
||||
</soapenv:Envelope>
|
||||
"""
|
||||
|
||||
|
||||
def _handle_request_exception(communication, e):
|
||||
if isinstance(e, requests.exceptions.RequestException):
|
||||
if isinstance(e, requests.exceptions.ConnectionError):
|
||||
if communication.state == "to_send":
|
||||
communication.sending_result = "Cannot establish the connection."
|
||||
else:
|
||||
communication.processing_result = "Cannot establish the connection."
|
||||
elif isinstance(e, requests.exceptions.Timeout):
|
||||
if communication.state == "to_send":
|
||||
communication.sending_result = "The request took too long to complete."
|
||||
else:
|
||||
communication.processing_result = (
|
||||
"The request took too long to complete."
|
||||
)
|
||||
else:
|
||||
if communication.state == "to_send":
|
||||
communication.sending_result = f"Request error: {e}"
|
||||
else:
|
||||
communication.processing_result = f"Request error: {e}"
|
||||
|
||||
|
||||
class TravellerReport(models.TransientModel):
|
||||
_name = "traveller.report.wizard"
|
||||
@@ -22,10 +326,26 @@ class TravellerReport(models.TransientModel):
|
||||
|
||||
txt_filename = fields.Text()
|
||||
txt_binary = fields.Binary(string="File Download")
|
||||
txt_message = fields.Char(string="File Preview")
|
||||
txt_message = fields.Char(
|
||||
string="File Preview",
|
||||
readonly=True,
|
||||
store=True,
|
||||
compute="_compute_txt_message",
|
||||
)
|
||||
date_target = fields.Date(
|
||||
string="Date", required=True, default=lambda self: fields.Date.today()
|
||||
)
|
||||
date_from = fields.Date(
|
||||
string="From",
|
||||
required=True,
|
||||
default=lambda self: fields.Date.today(),
|
||||
)
|
||||
date_to = fields.Date(
|
||||
string="To",
|
||||
required=True,
|
||||
default=lambda self: fields.Date.today() + relativedelta(days=1),
|
||||
)
|
||||
|
||||
pms_property_id = fields.Many2one(
|
||||
comodel_name="pms.property",
|
||||
string="Property",
|
||||
@@ -33,6 +353,35 @@ class TravellerReport(models.TransientModel):
|
||||
default=lambda self: self.env.user.get_active_property_ids()[0],
|
||||
)
|
||||
|
||||
is_ses = fields.Boolean(
|
||||
string="Is SES",
|
||||
readonly=True,
|
||||
compute="_compute_is_ses",
|
||||
)
|
||||
|
||||
report_type = fields.Selection(
|
||||
string="Report Type",
|
||||
required=True,
|
||||
default="reservations",
|
||||
help="Report type (reservation/traveller report)",
|
||||
selection=[
|
||||
("reservations", "Reservations Report"),
|
||||
("travellers", "Travellers Report"),
|
||||
],
|
||||
)
|
||||
|
||||
@api.depends(
|
||||
"pms_property_id", "date_target", "date_from", "date_to", "report_type"
|
||||
)
|
||||
def _compute_txt_message(self):
|
||||
for record in self:
|
||||
record.txt_message = False
|
||||
|
||||
@api.depends("pms_property_id.institution")
|
||||
def _compute_is_ses(self):
|
||||
for record in self:
|
||||
record.is_ses = record.pms_property_id.institution == "ses"
|
||||
|
||||
def generate_file_from_user_action(self):
|
||||
pms_property = self.env["pms.property"].search(
|
||||
[("id", "=", self.pms_property_id.id)]
|
||||
@@ -45,22 +394,64 @@ class TravellerReport(models.TransientModel):
|
||||
or not pms_property.institution_password
|
||||
):
|
||||
raise ValidationError(
|
||||
_("The guest information sending settings is not property updated.")
|
||||
_("The guest information sending settings is not property set up.")
|
||||
)
|
||||
|
||||
content = False
|
||||
# build content
|
||||
content = self.generate_checkin_list(
|
||||
pms_property_id=pms_property.id,
|
||||
date_target=self.date_target,
|
||||
)
|
||||
if self.is_ses:
|
||||
if self.report_type == "travellers":
|
||||
content = self.generate_ses_travellers_list(
|
||||
pms_property_id=pms_property.id,
|
||||
date_target=self.date_target,
|
||||
)
|
||||
elif self.report_type == "reservations":
|
||||
content = self.generate_ses_reservation_list(
|
||||
pms_property_id=pms_property.id,
|
||||
date_from=self.date_from,
|
||||
date_to=self.date_to,
|
||||
)
|
||||
else:
|
||||
content = self.generate_checkin_list(
|
||||
pms_property_id=pms_property.id,
|
||||
date_target=self.date_target,
|
||||
)
|
||||
|
||||
if content:
|
||||
self.txt_filename = pms_property.institution_property_id + ".999"
|
||||
if self.is_ses:
|
||||
if self.report_type == "travellers":
|
||||
self.txt_filename = (
|
||||
pms_property.institution_property_id
|
||||
+ "-"
|
||||
+ self.date_target.strftime("%Y%m%d")
|
||||
)
|
||||
else:
|
||||
self.txt_filename = (
|
||||
pms_property.institution_property_id
|
||||
+ "-"
|
||||
+ self.date_from.strftime("%Y%m%d")
|
||||
+ "-"
|
||||
+ self.date_to.strftime("%Y%m%d")
|
||||
)
|
||||
self.txt_filename = self.txt_filename + ".xml"
|
||||
|
||||
else:
|
||||
self.txt_filename = (
|
||||
pms_property.institution_property_id
|
||||
+ "-"
|
||||
+ self.date_target.strftime("%Y%m%d")
|
||||
+ ".999"
|
||||
)
|
||||
|
||||
self.txt_binary = base64.b64encode(str.encode(content))
|
||||
self.txt_message = content
|
||||
|
||||
return {
|
||||
"name": _("Traveller Report"),
|
||||
"name": _(
|
||||
"Travellers Report"
|
||||
if self.report_type == "travellers" or not self.is_ses
|
||||
else "Reservations Report"
|
||||
),
|
||||
"res_id": self.id,
|
||||
"res_model": "traveller.report.wizard",
|
||||
"target": "new",
|
||||
@@ -450,3 +841,290 @@ class TravellerReport(models.TransientModel):
|
||||
if prop.institution:
|
||||
self.send_file_institution(pms_property=prop, offset=offset)
|
||||
time.sleep(0.5)
|
||||
|
||||
# SES RESERVATIONS
|
||||
def generate_ses_reservation_list(self, pms_property_id, date_from, date_to):
|
||||
reservation_ids = (
|
||||
self.env["pms.reservation"]
|
||||
.search(
|
||||
[
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("state", "!=", "cancel"),
|
||||
("reservation_type", "!=", "out"),
|
||||
"|",
|
||||
("date_order", ">=", date_from),
|
||||
("date_order", "<=", date_to),
|
||||
]
|
||||
)
|
||||
.mapped("id")
|
||||
)
|
||||
return self.generate_xml_reservations(reservation_ids)
|
||||
|
||||
def generate_xml_reservation(self, solicitud, reservation_id):
|
||||
reservation = self.env["pms.reservation"].browse(reservation_id)
|
||||
|
||||
if not reservation.pms_property_id.institution_property_id:
|
||||
raise ValidationError(
|
||||
_("The property does not have an institution property id.")
|
||||
)
|
||||
|
||||
# SOLICITUD > COMUNICACION
|
||||
comunicacion = ET.SubElement(solicitud, "comunicacion")
|
||||
|
||||
# SOLICITUD > COMUNICACION > ESTABLECIMIENTO
|
||||
establecimiento = ET.SubElement(comunicacion, "establecimiento")
|
||||
|
||||
# SOLICITUD > COMUNICACION > ESTABLECIMIENTO > CODIGO
|
||||
ET.SubElement(
|
||||
establecimiento, "codigo"
|
||||
).text = reservation.pms_property_id.institution_property_id
|
||||
|
||||
# SOLICITUD > COMUNICACION > CONTRATO
|
||||
_ses_xml_contract_elements(comunicacion, reservation)
|
||||
|
||||
# SOLICITUD > COMUNICACION > PERSONA
|
||||
persona = ET.SubElement(comunicacion, "persona")
|
||||
|
||||
# SOLICITUD > COMUNICACION > PERSONA > ROL
|
||||
ET.SubElement(persona, "rol").text = "TI"
|
||||
|
||||
# SOLICITUD > COMUNICACION > PERSONA > NOMBRE
|
||||
_ses_xml_person_names_elements(persona, reservation, checkin_partner=None)
|
||||
_ses_xml_person_contact_elements(persona, reservation)
|
||||
|
||||
def generate_xml_reservations(self, reservation_ids):
|
||||
if not reservation_ids:
|
||||
raise ValidationError(_("Theres's no reservation to generate the XML"))
|
||||
|
||||
# SOLICITUD
|
||||
solicitud = ET.Element("solicitud")
|
||||
for reservation_id in reservation_ids:
|
||||
ET.SubElement(
|
||||
solicitud,
|
||||
self.generate_xml_reservation(solicitud, reservation_id),
|
||||
)
|
||||
xml_str = ET.tostring(solicitud, encoding="unicode")
|
||||
|
||||
xml_str = (
|
||||
'<ns2:peticion xmlns:ns2="http://www.neg.hospedajes.mir.es/altaReservaHospedaje">'
|
||||
+ xml_str
|
||||
+ "</ns2:peticion>"
|
||||
)
|
||||
return xml_str
|
||||
|
||||
# SES RESERVATIONS TRAVELLER REPORT
|
||||
def generate_ses_travellers_list(self, pms_property_id, date_target):
|
||||
reservation_ids = (
|
||||
self.env["pms.reservation"]
|
||||
.search(
|
||||
[
|
||||
("pms_property_id", "=", pms_property_id),
|
||||
("checkin", "=", date_target),
|
||||
]
|
||||
)
|
||||
.mapped("id")
|
||||
)
|
||||
return self.generate_xml_reservations_travellers_report(reservation_ids)
|
||||
|
||||
def generate_xml_reservation_travellers_report(self, solicitud, reservation_id):
|
||||
reservation = self.env["pms.reservation"].browse(reservation_id)
|
||||
comunicacion = ET.SubElement(solicitud, "comunicacion")
|
||||
_ses_xml_contract_elements(comunicacion, reservation)
|
||||
|
||||
for checkin_partner in reservation.checkin_partner_ids.filtered(
|
||||
lambda x: x.state == "onboard"
|
||||
):
|
||||
_ses_xml_person_elements(comunicacion, checkin_partner)
|
||||
|
||||
def generate_xml_reservations_travellers_report(self, reservation_ids):
|
||||
if not reservation_ids:
|
||||
raise ValidationError(_("Theres's no reservation to generate the XML"))
|
||||
|
||||
if (
|
||||
len(
|
||||
self.env["pms.reservation"]
|
||||
.browse(reservation_ids)
|
||||
.mapped("pms_property_id")
|
||||
)
|
||||
> 1
|
||||
):
|
||||
raise ValidationError(_("The reservations must be from the same property."))
|
||||
if not any(
|
||||
state == "onboard"
|
||||
for state in self.env["pms.reservation"]
|
||||
.browse(reservation_ids)
|
||||
.mapped("checkin_partner_ids")
|
||||
.mapped("state")
|
||||
):
|
||||
raise ValidationError(
|
||||
_("There are no guests to generate the travellers report.")
|
||||
)
|
||||
|
||||
# SOLICITUD
|
||||
solicitud = ET.Element("solicitud")
|
||||
|
||||
pms_property = (
|
||||
self.env["pms.reservation"].browse(reservation_ids[0]).pms_property_id
|
||||
)
|
||||
|
||||
if not pms_property.institution_property_id:
|
||||
raise ValidationError(
|
||||
_("The property does not have an institution property id.")
|
||||
)
|
||||
|
||||
# SOLICITUD -> CODIGO ESTABLECIMIENTO
|
||||
ET.SubElement(
|
||||
solicitud, "codigoEstablecimiento"
|
||||
).text = pms_property.institution_property_id
|
||||
|
||||
for reservation_id in reservation_ids:
|
||||
ET.SubElement(
|
||||
solicitud,
|
||||
self.generate_xml_reservation_travellers_report(
|
||||
solicitud, reservation_id
|
||||
),
|
||||
)
|
||||
xml_str = ET.tostring(solicitud, encoding="unicode")
|
||||
|
||||
xml_str = (
|
||||
'<ns2:peticion xmlns:ns2="http://www.neg.hospedajes.mir.es/altaParteHospedaje">'
|
||||
+ xml_str
|
||||
+ "</ns2:peticion>"
|
||||
)
|
||||
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),
|
||||
]
|
||||
):
|
||||
data = False
|
||||
if communication.entity == "RH":
|
||||
data = self.generate_xml_reservations([communication.reservation_id.id])
|
||||
elif communication.entity == "PV":
|
||||
data = self.generate_xml_reservations_travellers_report(
|
||||
[communication.reservation_id.id]
|
||||
)
|
||||
communication.communication_xml = data
|
||||
data = _string_to_zip_to_base64(data)
|
||||
payload = _generate_payload(
|
||||
communication.reservation_id.pms_property_id.institution_lessor_id,
|
||||
communication.operation,
|
||||
communication.entity,
|
||||
data,
|
||||
)
|
||||
communication.communication_soap = payload
|
||||
communication.communication_time = fields.Datetime.now()
|
||||
try:
|
||||
soap_response = requests.request(
|
||||
"POST",
|
||||
communication.reservation_id.pms_property_id.ses_url,
|
||||
headers=_get_auth_headers(communication),
|
||||
data=payload,
|
||||
verify=False,
|
||||
)
|
||||
root = ET.fromstring(soap_response.text)
|
||||
communication.sending_result = root.find(".//descripcion").text
|
||||
communication.response_communication_soap = soap_response.text
|
||||
result_code = root.find(".//codigo").text
|
||||
if result_code == REQUEST_CODE_OK:
|
||||
communication.communication_id = root.find(".//lote").text
|
||||
if communication.operation == "A":
|
||||
communication.state = "to_process"
|
||||
else:
|
||||
communication.state = "processed"
|
||||
else:
|
||||
communication.state = "error_sending"
|
||||
|
||||
except requests.exceptions.RequestException as e:
|
||||
_handle_request_exception(communication, e)
|
||||
|
||||
@api.model
|
||||
def ses_process_communications(self):
|
||||
for communication in self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("state", "=", "to_process"),
|
||||
("operation", "!=", DELETE_OPERATION_CODE),
|
||||
]
|
||||
):
|
||||
var_xml_get_batch = f"""
|
||||
<con:lotes
|
||||
xmlns:con="http://www.neg.hospedajes.mir.es/consultarComunicacion">
|
||||
<con:lote>{communication.communication_id}</con:lote>
|
||||
</con:lotes>
|
||||
"""
|
||||
communication.query_status_xml = var_xml_get_batch
|
||||
data = _string_to_zip_to_base64(var_xml_get_batch)
|
||||
payload = _generate_payload(
|
||||
communication.reservation_id.pms_property_id.institution_lessor_id,
|
||||
"C",
|
||||
"",
|
||||
data,
|
||||
)
|
||||
communication.query_status_soap = payload
|
||||
communication.query_status_time = fields.Datetime.now()
|
||||
try:
|
||||
soap_response = requests.request(
|
||||
"POST",
|
||||
communication.reservation_id.pms_property_id.ses_url,
|
||||
headers=_get_auth_headers(communication),
|
||||
data=payload,
|
||||
verify=False,
|
||||
)
|
||||
root = ET.fromstring(soap_response.text)
|
||||
communication.response_communication_soap = soap_response.text
|
||||
result_code = root.find(".//codigo").text
|
||||
communication.response_query_status_soap = soap_response.text
|
||||
if result_code == REQUEST_CODE_OK:
|
||||
result_status = root.find(".//codigoEstado").text
|
||||
if result_status == XML_OK:
|
||||
communication.state = "processed"
|
||||
communication.processing_result = root.find(
|
||||
".//descripcion"
|
||||
).text
|
||||
elif result_status in [XML_PROCESSING, XML_PENDING]:
|
||||
communication.state = "to_process"
|
||||
communication.processing_result = "Not processed yet"
|
||||
else:
|
||||
communication.state = "error_processing"
|
||||
communication.processing_result = root.find(".//error").text
|
||||
# request errors
|
||||
else:
|
||||
communication.state = "error_processing"
|
||||
communication.processing_result = root.find(".//descripcion").text
|
||||
except requests.exceptions.RequestException as e:
|
||||
_handle_request_exception(communication, e)
|
||||
|
||||
@api.model
|
||||
def create_pending_notifications_traveller_report(self):
|
||||
|
||||
domain = [
|
||||
("state", "=", "onboard"),
|
||||
("checkin", "=", fields.Datetime.today().date()),
|
||||
("pms_property_id.institution", "=", "ses"),
|
||||
]
|
||||
for reservation in (
|
||||
self.env["pms.reservation"]
|
||||
.search(domain)
|
||||
.filtered(
|
||||
lambda x: any(
|
||||
state == "onboard"
|
||||
for state in x.checkin_partner_ids.mapped("state")
|
||||
)
|
||||
)
|
||||
):
|
||||
self.env["pms.ses.communication"].search(
|
||||
[
|
||||
("reservation_id", "=", reservation.id),
|
||||
("entity", "=", "PV"),
|
||||
("state", "=", "to_send"),
|
||||
]
|
||||
).unlink()
|
||||
self.env["pms.reservation"].create_communication(
|
||||
reservation.id,
|
||||
"A",
|
||||
"PV",
|
||||
)
|
||||
|
||||
@@ -9,10 +9,40 @@
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<group>
|
||||
<field name="is_ses" invisible="1" />
|
||||
<field name="pms_property_id" />
|
||||
<field name="date_target" />
|
||||
<field
|
||||
name="report_type"
|
||||
attrs="{'invisible': [('is_ses', '=', False)]}"
|
||||
/>
|
||||
<field
|
||||
name="date_target"
|
||||
attrs="{'invisible': [('is_ses', '=', True), ('report_type', '=', 'reservations')]}"
|
||||
/>
|
||||
<field
|
||||
name="date_from"
|
||||
attrs="{'invisible': [
|
||||
'|',
|
||||
'&',
|
||||
('is_ses', '=', True),
|
||||
('report_type', '=', 'travellers'),
|
||||
('is_ses', '=', False),
|
||||
]}"
|
||||
/>
|
||||
<field
|
||||
name="date_to"
|
||||
attrs="{'invisible': [
|
||||
'|',
|
||||
'&',
|
||||
('is_ses', '=', True),
|
||||
('report_type', '=', 'travellers'),
|
||||
('is_ses', '=', False),
|
||||
]}"
|
||||
/>
|
||||
</group>
|
||||
<group attrs="{'invisible': [('txt_message','=',False)]}">
|
||||
<group
|
||||
attrs="{'invisible': ['|', ('txt_message','=',False), ('is_ses', '=', True)]}"
|
||||
>
|
||||
<field name="txt_message" readonly="1" />
|
||||
</group>
|
||||
<group attrs="{'invisible': [('txt_message','=',False)]}">
|
||||
@@ -25,26 +55,30 @@
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="row ">
|
||||
<div class="col-3">
|
||||
<button
|
||||
<div class="row ">
|
||||
<div class="col-3">
|
||||
<button
|
||||
name="generate_file_from_user_action"
|
||||
class="btn btn-primary btn-sm"
|
||||
type="object"
|
||||
string="Preview file"
|
||||
string="Generate file"
|
||||
/>
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button
|
||||
</div>
|
||||
<div class="col-3">
|
||||
<button
|
||||
name="send_file_institution"
|
||||
class="btn btn-primary btn-sm"
|
||||
type="object"
|
||||
string="Send file"
|
||||
attrs="{'invisible': [('txt_message','=',False)]}"
|
||||
attrs="{'invisible': [
|
||||
'|',
|
||||
('txt_message','=',False),
|
||||
('is_ses', '=', True),
|
||||
]}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<footer />
|
||||
</div>
|
||||
</form>
|
||||
</field>
|
||||
</record>
|
||||
|
||||
@@ -238,7 +238,7 @@ class WizardIne(models.TransientModel):
|
||||
"""
|
||||
|
||||
for entry in read_group_result:
|
||||
if not entry["nationality_id"]:
|
||||
if not entry["residence_country_id"]:
|
||||
guests_with_no_nationality = self.env["pms.checkin.partner"].search(
|
||||
entry["__domain"]
|
||||
)
|
||||
@@ -253,24 +253,24 @@ class WizardIne(models.TransientModel):
|
||||
guests_with_no_nationality,
|
||||
)
|
||||
)
|
||||
# get nationality_id from group set read_group results
|
||||
nationality_id_code = (
|
||||
# get residence_country_id from group set read_group results
|
||||
residence_country_id_code = (
|
||||
self.env["res.country"]
|
||||
.search([("id", "=", entry["nationality_id"][0])])
|
||||
.search([("id", "=", entry["residence_country_id"][0])])
|
||||
.code
|
||||
)
|
||||
# all countries except Spain
|
||||
if nationality_id_code != CODE_SPAIN:
|
||||
if residence_country_id_code != CODE_SPAIN:
|
||||
|
||||
# get count of each result
|
||||
num = entry["__count"]
|
||||
|
||||
# update/create dicts for countries & dates and set num. arrivals
|
||||
if not nationalities.get(nationality_id_code):
|
||||
nationalities[nationality_id_code] = dict()
|
||||
if not nationalities[nationality_id_code].get(date):
|
||||
nationalities[nationality_id_code][date] = dict()
|
||||
nationalities[nationality_id_code][date][type_of_entry] = num
|
||||
if not nationalities.get(residence_country_id_code):
|
||||
nationalities[residence_country_id_code] = dict()
|
||||
if not nationalities[residence_country_id_code].get(date):
|
||||
nationalities[residence_country_id_code][date] = dict()
|
||||
nationalities[residence_country_id_code][date][type_of_entry] = num
|
||||
else:
|
||||
# arrivals grouped by state_id (Spain "provincias")
|
||||
read_by_arrivals_spain = self.env["pms.checkin.partner"].read_group(
|
||||
@@ -351,36 +351,36 @@ class WizardIne(models.TransientModel):
|
||||
# arrivals
|
||||
arrivals = hosts.filtered(lambda x: x.reservation_id.checkin == p_date)
|
||||
|
||||
# arrivals grouped by nationality_id
|
||||
# arrivals grouped by residence_country_id
|
||||
read_by_arrivals = self.env["pms.checkin.partner"].read_group(
|
||||
[("id", "in", arrivals.ids)],
|
||||
["nationality_id"],
|
||||
["nationality_id"],
|
||||
orderby="nationality_id",
|
||||
["residence_country_id"],
|
||||
["residence_country_id"],
|
||||
orderby="residence_country_id",
|
||||
lazy=False,
|
||||
)
|
||||
|
||||
# departures
|
||||
departures = hosts.filtered(lambda x: x.reservation_id.checkout == p_date)
|
||||
|
||||
# departures grouped by nationality_id
|
||||
# departures grouped by residence_country_id
|
||||
read_by_departures = self.env["pms.checkin.partner"].read_group(
|
||||
[("id", "in", departures.ids)],
|
||||
["nationality_id"],
|
||||
["nationality_id"],
|
||||
orderby="nationality_id",
|
||||
["residence_country_id"],
|
||||
["residence_country_id"],
|
||||
orderby="residence_country_id",
|
||||
lazy=False,
|
||||
)
|
||||
|
||||
# pernoctations
|
||||
pernoctations = hosts - departures
|
||||
|
||||
# pernoctations grouped by nationality_id
|
||||
# pernoctations grouped by residence_country_id
|
||||
read_by_pernoctations = self.env["pms.checkin.partner"].read_group(
|
||||
[("id", "in", pernoctations.ids)],
|
||||
["nationality_id"],
|
||||
["nationality_id"],
|
||||
orderby="nationality_id",
|
||||
["residence_country_id"],
|
||||
["residence_country_id"],
|
||||
orderby="residence_country_id",
|
||||
lazy=False,
|
||||
)
|
||||
ine_add_arrivals_departures_pernoctations(
|
||||
|
||||
Reference in New Issue
Block a user