mirror of
https://github.com/OCA/pms.git
synced 2025-01-29 00:17:45 +02:00
453 lines
18 KiB
Python
453 lines
18 KiB
Python
import base64
|
|
import datetime
|
|
import json
|
|
import logging
|
|
import re
|
|
import time
|
|
|
|
import requests
|
|
from bs4 import BeautifulSoup as bs
|
|
from dateutil.relativedelta import relativedelta
|
|
|
|
from odoo import _, api, fields, models
|
|
from odoo.exceptions import MissingError, ValidationError
|
|
from odoo.modules.module import get_module_resource
|
|
|
|
_logger = logging.getLogger(__name__)
|
|
|
|
|
|
class TravellerReport(models.TransientModel):
|
|
_name = "traveller.report.wizard"
|
|
_description = "Traveller Report"
|
|
|
|
txt_filename = fields.Text()
|
|
txt_binary = fields.Binary(string="File Download")
|
|
txt_message = fields.Char(string="File Preview")
|
|
date_target = fields.Date(
|
|
string="Date", required=True, default=lambda self: fields.Date.today()
|
|
)
|
|
pms_property_id = fields.Many2one(
|
|
comodel_name="pms.property",
|
|
string="Property",
|
|
required=True,
|
|
default=lambda self: self.env.user.get_active_property_ids()[0],
|
|
)
|
|
|
|
def generate_file_from_user_action(self):
|
|
pms_property = self.env["pms.property"].search(
|
|
[("id", "=", self.pms_property_id.id)]
|
|
)
|
|
# check if there's institution settings properly established
|
|
if (
|
|
not pms_property
|
|
or not pms_property.institution_property_id
|
|
or not pms_property.institution_user
|
|
or not pms_property.institution_password
|
|
):
|
|
raise ValidationError(
|
|
_("The guest information sending settings is not property updated.")
|
|
)
|
|
|
|
# build content
|
|
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"
|
|
self.txt_binary = base64.b64encode(str.encode(content))
|
|
self.txt_message = content
|
|
|
|
return {
|
|
"name": _("Traveller Report"),
|
|
"res_id": self.id,
|
|
"res_model": "traveller.report.wizard",
|
|
"target": "new",
|
|
"type": "ir.actions.act_window",
|
|
"view_id": self.env.ref("pms_l10n_es.traveller_report_wizard").id,
|
|
"view_mode": "form",
|
|
}
|
|
|
|
def generate_checkin_list(self, pms_property_id, date_target=False):
|
|
regex = re.compile("[^a-zA-Z0-9]")
|
|
|
|
# check if there's guests info pending to send
|
|
if not date_target:
|
|
date_target = fields.date.today()
|
|
domain = [
|
|
("state", "in", ["onboard", "done"]),
|
|
("arrival", ">=", str(date_target) + " 0:00:00"),
|
|
("arrival", "<=", str(date_target) + " 23:59:59"),
|
|
("pms_property_id", "=", pms_property_id),
|
|
]
|
|
pms_property = (
|
|
self.env["pms.property"]
|
|
.with_context(lang="es_ES")
|
|
.search([("id", "=", pms_property_id)])
|
|
)
|
|
# get checkin partners info to send
|
|
lines = self.env["pms.checkin.partner"].search(domain)
|
|
# build the property info record
|
|
# 1 | property id | property name | date | nº of checkin partners
|
|
content = (
|
|
"1|"
|
|
+ pms_property.institution_property_id.upper()
|
|
+ "|"
|
|
+ regex.sub(" ", pms_property.name.upper())
|
|
+ "|"
|
|
+ datetime.datetime.now().strftime("%Y%m%d|%H%M")
|
|
+ "|"
|
|
+ str(len(lines))
|
|
+ "\n"
|
|
)
|
|
# build each checkin partner line's record
|
|
# 2|DNI nº|Doc.number|doc.type|exp.date|lastname|lastname2|name|...
|
|
# ...gender|birthdate|nation.|checkin
|
|
lines = lines.with_context(lang="es_ES")
|
|
for line in lines:
|
|
content += "2"
|
|
# [P|N|..]
|
|
if line.document_type.code not in ["D", "C"]:
|
|
content += "||" + regex.sub("", line.document_number.upper()) + "|"
|
|
else:
|
|
content += "|" + regex.sub("", line.document_number.upper()) + "||"
|
|
content += line.document_type.code + "|"
|
|
content += line.document_expedition_date.strftime("%Y%m%d") + "|"
|
|
content += regex.sub(" ", line.lastname.upper()) + "|"
|
|
if line.lastname2:
|
|
content += regex.sub(" ", line.lastname2.upper())
|
|
content += "|" + regex.sub(" ", line.firstname.upper()) + "|"
|
|
if line.gender == "female":
|
|
content += "F|"
|
|
else:
|
|
content += "M|"
|
|
content += line.birthdate_date.strftime("%Y%m%d") + "|"
|
|
content += line.nationality_id.name.upper() + "|"
|
|
content += line.arrival.strftime("%Y%m%d") + "\n"
|
|
|
|
return content
|
|
|
|
def send_file_gc(self, file_content, called_from_user, pms_property):
|
|
try:
|
|
_logger.info(
|
|
"Sending file to Guardia Civil, Property %s, date: %s"
|
|
% (pms_property.name, self.date_target)
|
|
)
|
|
url = "https://hospederias.guardiacivil.es/"
|
|
login_route = "/hospederias/login.do"
|
|
upload_file_route = "/hospederias/cargaFichero.do"
|
|
logout_route = "/hospederias/logout.do"
|
|
target_date = self.date_target or fields.date.today()
|
|
if file_content:
|
|
headers = {
|
|
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
|
|
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
|
|
"Gecko) Chrome/90.0.4430.93 Mobile Safari/537.36",
|
|
}
|
|
session = requests.session()
|
|
login_payload = {
|
|
"usuario": pms_property.institution_user,
|
|
"pswd": pms_property.institution_password,
|
|
}
|
|
response_login = session.post(
|
|
url + login_route,
|
|
headers=headers,
|
|
data=login_payload,
|
|
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
|
|
)
|
|
time.sleep(0.4)
|
|
|
|
# check if authentication was successful / unsuccessful or the
|
|
# resource cannot be accessed
|
|
soup = bs(response_login.text, "html.parser")
|
|
errors_login = soup.select("#txterror > ul > li")
|
|
if errors_login:
|
|
raise ValidationError(errors_login[0].text)
|
|
else:
|
|
login_correct = soup.select(".cabecera2")
|
|
if not login_correct:
|
|
session.close()
|
|
raise ValidationError(_("Connection could not be established"))
|
|
|
|
# build the file to send
|
|
files = {
|
|
"fichero": (pms_property.institution_user + ".999", file_content)
|
|
}
|
|
|
|
# send file
|
|
response_file_sent = session.post(
|
|
url + upload_file_route,
|
|
data={"autoSeq": "on"},
|
|
files=files,
|
|
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
|
|
)
|
|
time.sleep(0.4)
|
|
|
|
# logout & close connection
|
|
session.get(
|
|
url + logout_route,
|
|
headers=headers,
|
|
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
|
|
)
|
|
session.close()
|
|
|
|
# check if the file send has been correct
|
|
soup = bs(response_file_sent.text, "html.parser")
|
|
errors = soup.select("#errores > tbody > tr")
|
|
if errors:
|
|
msg = "Errores en el fichero:\n"
|
|
for e in errors:
|
|
msg += "Error en línea " + e.select("a")[0].text + ": "
|
|
msg += e.select("a")[2].text + "\n"
|
|
return self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"error_sending_data": True,
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": target_date,
|
|
"txt_message": _("Error in file sended"),
|
|
"txt_incidencies_from_institution": msg,
|
|
}
|
|
)
|
|
else:
|
|
return self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"error_sending_data": False,
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": target_date,
|
|
"txt_message": _("Successful file sending"),
|
|
}
|
|
)
|
|
except Exception as e:
|
|
return self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"error_sending_data": True,
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": target_date,
|
|
"txt_message": str(e),
|
|
}
|
|
)
|
|
|
|
def send_file_pn(self, file_content, called_from_user, pms_property):
|
|
try:
|
|
base_url = "https://webpol.policia.es"
|
|
headers = {
|
|
"User-Agent": "Mozilla/5.0 (Linux; Android 6.0; Nexus 5 "
|
|
"Build/MRA58N) AppleWebKit/537.36 (KHTML, like "
|
|
"Gecko) Chrome/90.0.4430.93 Mobile Safari/537.36",
|
|
}
|
|
pre_login_route = "/e-hotel/login"
|
|
login_route = "/e-hotel/execute_login"
|
|
next_file_name_route = "/e-hotel/hospederia/ficheros/vista/envioFicheros"
|
|
upload_file_route = "/e-hotel/hospederia/ficheros/subirFichero"
|
|
|
|
pre_receipt_acknowledgment_route = (
|
|
"/e-hotel/hospederia/generarInformeFichero"
|
|
)
|
|
|
|
post_receipt_acknowledgment_route2 = "/e-hotel/hospederia/pdfInformeFichero"
|
|
logout_route = "/e-hotel/execute_logout"
|
|
target_date = self.date_target or fields.date.today()
|
|
session = requests.session()
|
|
# retrieve token
|
|
response_pre_login = session.post(
|
|
base_url + pre_login_route,
|
|
headers=headers,
|
|
verify=False,
|
|
)
|
|
time.sleep(0.3)
|
|
token = bs(response_pre_login.text, "html.parser").select(
|
|
"input[name='_csrf']"
|
|
)[0]["value"]
|
|
|
|
if not token:
|
|
raise MissingError(_("Could not get token login."))
|
|
|
|
# do login
|
|
session.post(
|
|
base_url + login_route,
|
|
headers=headers,
|
|
data={
|
|
"username": pms_property.institution_user,
|
|
"password": pms_property.institution_password,
|
|
"_csrf": token,
|
|
},
|
|
verify=False,
|
|
)
|
|
time.sleep(0.3)
|
|
headers["x-csrf-token"] = token
|
|
# retrieve file name to send
|
|
response_name_file_route = session.post(
|
|
base_url + next_file_name_route,
|
|
headers=headers,
|
|
verify=False,
|
|
)
|
|
time.sleep(0.2)
|
|
soup = bs(response_name_file_route.text, "html.parser")
|
|
file_name = soup.select("#msjNombreFichero > b > u")[0].text
|
|
|
|
if not file_name:
|
|
raise MissingError(_("Could not get next file name to send."))
|
|
|
|
# send file
|
|
upload_result = session.post(
|
|
base_url + upload_file_route,
|
|
headers=headers,
|
|
verify=False,
|
|
data={
|
|
"jsonHiddenComunes": "",
|
|
"ficheroJ": "",
|
|
"_csrf": token,
|
|
},
|
|
files={"fichero": (file_name, file_content)},
|
|
)
|
|
if upload_result.status_code != 200:
|
|
raise MissingError(_("Could not upload file."))
|
|
|
|
time.sleep(0.2)
|
|
# ask for receipt acknowledgment
|
|
session.post(
|
|
base_url + pre_receipt_acknowledgment_route,
|
|
headers=headers,
|
|
verify=False,
|
|
data={
|
|
"jsonHiddenComunes": "",
|
|
"ficheroJ": json.loads(upload_result.content.decode("latin-1"))[
|
|
"ficheroJ"
|
|
],
|
|
"_csrf": token,
|
|
},
|
|
)
|
|
# get receipt acknowledgment
|
|
response_post_ask_file2 = session.get(
|
|
base_url + post_receipt_acknowledgment_route2,
|
|
)
|
|
time.sleep(0.5)
|
|
log = self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": target_date,
|
|
"error_sending_data": False,
|
|
"file_incidencies_from_institution": base64.b64encode(
|
|
response_post_ask_file2.content
|
|
),
|
|
"txt_filename": file_name + ".pdf",
|
|
}
|
|
)
|
|
# do logout
|
|
session.post(
|
|
base_url + logout_route,
|
|
headers=headers,
|
|
verify=False,
|
|
data={"_csrf": token},
|
|
)
|
|
session.close()
|
|
return log
|
|
except Exception as e:
|
|
return self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"error_sending_data": True,
|
|
"txt_message": str(e),
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": target_date,
|
|
}
|
|
)
|
|
|
|
def send_file_institution(self, pms_property=False, offset=0, date_target=False):
|
|
try:
|
|
called_from_user = False
|
|
if not pms_property:
|
|
called_from_user = True
|
|
pms_property = self.env["pms.property"].search(
|
|
[("id", "=", self.pms_property_id.id)]
|
|
)
|
|
if (
|
|
not pms_property
|
|
or not pms_property.institution_property_id
|
|
or not pms_property.institution_user
|
|
or not pms_property.institution_password
|
|
):
|
|
raise ValidationError(
|
|
_("The guest information sending settings is not complete.")
|
|
)
|
|
date_target = self.date_target or False
|
|
if not date_target:
|
|
date_target = fields.Date.today()
|
|
date_target = date_target - relativedelta(days=offset)
|
|
file_content = self.generate_checkin_list(
|
|
pms_property_id=pms_property.id,
|
|
date_target=date_target,
|
|
)
|
|
if pms_property.institution == "policia_nacional":
|
|
log = self.send_file_pn(file_content, called_from_user, pms_property)
|
|
elif pms_property.institution == "guardia_civil":
|
|
log = self.send_file_gc(file_content, called_from_user, pms_property)
|
|
if log.error_sending_data:
|
|
_logger.warning(
|
|
"""
|
|
Error sending file, Property %s, date: %s, message: %s
|
|
"""
|
|
% (pms_property.name, self.date_target, log.txt_message)
|
|
)
|
|
raise ValidationError(log.txt_incidencies_from_institution)
|
|
_logger.info(
|
|
"""
|
|
Successful file sending, sending confirmation mail,
|
|
Property %s, date: %s" % (pms_property.name, self.date_target)
|
|
"""
|
|
)
|
|
if called_from_user:
|
|
message = {
|
|
"type": "ir.actions.client",
|
|
"tag": "display_notification",
|
|
"params": {
|
|
"title": _("Sent succesfully!"),
|
|
"message": _("Successful file sending"),
|
|
"sticky": False,
|
|
},
|
|
}
|
|
return message
|
|
else:
|
|
template = self.env.ref(
|
|
"pms_l10n_es.notification_send_success_travel_report_email"
|
|
)
|
|
template.send_mail(log.id, force_send=True)
|
|
except Exception as e:
|
|
_logger.warning(
|
|
"""
|
|
Error sending file, Property %s, date: %s, message: %s
|
|
"""
|
|
% (pms_property.name, self.date_target, str(e))
|
|
)
|
|
if called_from_user:
|
|
message = {
|
|
"type": "ir.actions.client",
|
|
"tag": "display_notification",
|
|
"params": {
|
|
"title": _("Error!"),
|
|
"message": _("Error sending file: %s" % e),
|
|
"sticky": False,
|
|
},
|
|
}
|
|
return message
|
|
else:
|
|
log = self.env["pms.log.institution.traveller.report"].create(
|
|
{
|
|
"error_sending_data": True,
|
|
"txt_incidencies_from_institution": str(e),
|
|
"pms_property_id": pms_property.id,
|
|
"target_date": date_target,
|
|
}
|
|
)
|
|
template = self.env.ref(
|
|
"pms_l10n_es.notification_send_error_travel_report_email"
|
|
)
|
|
template.send_mail(log.id, force_send=True)
|
|
|
|
@api.model
|
|
def send_file_institution_async(self, offset=0):
|
|
for prop in self.env["pms.property"].search([]):
|
|
if prop.institution:
|
|
self.send_file_institution(pms_property=prop, offset=offset)
|
|
time.sleep(0.5)
|