Merge PR #3 into 14.0

Signed-off-by DarioLodeiros
This commit is contained in:
OCA-git-bot
2021-05-29 11:07:04 +00:00
17 changed files with 627 additions and 10 deletions

View File

@@ -1,2 +1,3 @@
partner-contact partner-contact
reporting-engine reporting-engine
queue

View File

@@ -1 +1,2 @@
from . import models from . import models
from . import wizards

View File

@@ -15,10 +15,23 @@
"partner_second_lastname", "partner_second_lastname",
"partner_contact_gender", "partner_contact_gender",
"partner_contact_birthdate", "partner_contact_birthdate",
"partner_contact_nationality",
"queue_job",
], ],
"external_dependencies": {
"python": [
"bs4",
],
},
"data": [ "data": [
"data/cron_jobs.xml",
"data/queue_data.xml",
"data/queue_job_function_data.xml",
"security/ir.model.access.csv",
"views/pms_checkin_partner_views.xml", "views/pms_checkin_partner_views.xml",
"views/pms_property_views.xml",
"views/res_partner_views.xml", "views/res_partner_views.xml",
"wizards/traveller_report.xml",
], ],
"installable": True, "installable": True,
} }

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="0">
<record model="ir.cron" id="autosend_traveller_report">
<field name="name">Automatic Send Traveller Report</field>
<field name="active" eval="True" />
<field name="interval_number">1</field>
<field name="user_id" ref="base.user_root" />
<field name="interval_type">days</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 03:00:00')"
/>
<field name="code">model.send_file_gc_async()</field>
</record>
</data>
</odoo>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<data noupdate="1">
<record id="channel_gc_file_send" model="queue.job.channel">
<field name="name">gc_file_send</field>
<field name="parent_id" ref="queue_job.channel_root" />
</record>
</data>
</odoo>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo noupdate="1">
<record
id="traveller_report_send_file_async_job_function"
model="queue.job.function"
>
<field name="model_id" ref="pms_l10n_es.model_traveller_report_wizard" />
<field name="method">send_file_async</field>
<field name="channel_id" ref="pms_l10n_es.channel_gc_file_send" />
<field name="retry_pattern" eval="{1: 10, 5: 30, 10: 60, 15: 300}" />
</record>
</odoo>

View File

@@ -1,2 +1,3 @@
from . import res_partner from . import res_partner
from . import pms_checkin_partner from . import pms_checkin_partner
from . import pms_property

View File

@@ -62,12 +62,13 @@ class PmsCheckinPartner(models.Model):
store=True, store=True,
readonly=False, readonly=False,
) )
nationality_id = fields.Many2one(
@api.depends("partner_id", "partner_id.lastname") string="Nationality ID",
def _compute_lastname(self): compute="_compute_nationality",
for record in self: comodel_name="res.country",
if not record.lastname: store=True,
record.lastname = record.partner_id.lastname readonly=False,
)
@api.depends("partner_id", "partner_id.firstname") @api.depends("partner_id", "partner_id.firstname")
def _compute_firstname(self): def _compute_firstname(self):
@@ -75,6 +76,12 @@ class PmsCheckinPartner(models.Model):
if not record.firstname: if not record.firstname:
record.firstname = record.partner_id.firstname record.firstname = record.partner_id.firstname
@api.depends("partner_id", "partner_id.lastname")
def _compute_lastname(self):
for record in self:
if not record.lastname:
record.lastname = record.partner_id.lastname
@api.depends("partner_id", "partner_id.lastname2") @api.depends("partner_id", "partner_id.lastname2")
def _compute_lastname2(self): def _compute_lastname2(self):
for record in self: for record in self:
@@ -113,6 +120,12 @@ class PmsCheckinPartner(models.Model):
if not record.gender: if not record.gender:
record.gender = record.partner_id.gender record.gender = record.partner_id.gender
@api.depends("partner_id", "partner_id.lastname")
def _compute_nationality(self):
for record in self:
if not record.nationality_id:
record.nationality_id = record.partner_id.nationality_id
@api.model @api.model
def _checkin_mandatory_fields(self, depends=False): def _checkin_mandatory_fields(self, depends=False):
mandatory_fields = super(PmsCheckinPartner, self)._checkin_mandatory_fields( mandatory_fields = super(PmsCheckinPartner, self)._checkin_mandatory_fields(
@@ -120,7 +133,6 @@ class PmsCheckinPartner(models.Model):
) )
mandatory_fields.extend( mandatory_fields.extend(
[ [
"lastname2",
"birthdate_date", "birthdate_date",
"document_number", "document_number",
"document_type", "document_type",

View File

@@ -0,0 +1,98 @@
import time
import requests
from bs4 import BeautifulSoup as bs
from odoo import _, fields, models
from odoo.exceptions import ValidationError
from odoo.modules import get_module_resource
class PmsProperty(models.Model):
_inherit = "pms.property"
institution = fields.Selection(
[
("guardia_civil", "Guardia Civil"),
("policia_nacional", "Policía Nacional (soon)"),
("ertxaintxa", "Ertxaintxa (soon)"),
("mossos", "Mossos_d'esquadra (soon)"),
],
string="Institution",
default="guardia_civil",
help="Institution to send daily guest data.",
)
institution_property_id = fields.Char(
string="Institution property id",
size=10,
help="Id provided by institution to send data from property.",
)
institution_user = fields.Char(
string="Institution user", help="User provided by institution to send the data."
)
institution_password = fields.Char(
string="Institution password",
help="Password provided by institution to send the data.",
)
def test_gc_connection(self):
for pms_property in self:
if (
pms_property.institution == "guardia_civil"
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
):
url = "https://hospederias.guardiacivil.es/"
login_route = "/hospederias/login.do"
logout_route = "/hospederias/logout.do"
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,
}
# login
response_login = session.post(
url + login_route,
headers=headers,
data=login_payload,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
time.sleep(1)
# logout
session.get(
url + logout_route,
headers=headers,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
session.close()
# check if authentication was successful / unsuccessful or the
# resource cannot be accessed
soup = bs(response_login.text, "html.parser")
errors = soup.select("#txterror > ul > li")
if errors:
raise ValidationError(errors[0].text)
else:
login_correct = soup.select(".cabecera2")
if login_correct:
message = {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Connection Established!"),
"message": _("Connection established succesfully"),
"sticky": False,
},
}
return message
else:
raise ValidationError(_("Connection could not be established"))

View File

@@ -0,0 +1,2 @@
id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink
user_access_traveller_report_wizard,user_access_traveller_report_wizard,model_traveller_report_wizard,pms.group_pms_user,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
2 user_access_traveller_report_wizard user_access_traveller_report_wizard model_traveller_report_wizard pms.group_pms_user 1 1 1 1

104
pms_l10n_es/static/cert.pem Normal file
View File

@@ -0,0 +1,104 @@
-----BEGIN CERTIFICATE-----
MIIIwDCCB6igAwIBAgIQGBMhPyx6Rolez7EMhDFA1jANBgkqhkiG9w0BAQsFADBHMQswCQYDVQQG
EwJFUzERMA8GA1UECgwIRk5NVC1SQ00xJTAjBgNVBAsMHEFDIENvbXBvbmVudGVzIEluZm9ybcOh
dGljb3MwHhcNMjAwNTI4MTIzOTQwWhcNMjIwNTI4MTIzOTM5WjCBmDELMAkGA1UEBhMCRVMxDzAN
BgNVBAcMBk1BRFJJRDEuMCwGA1UECgwlRElSRUNDSU9OIEdFTkVSQUwgREUgTEEgR1VBUkRJQSBD
SVZJTDESMBAGA1UEBRMJUzI4MTYwMDNEMRgwFgYDVQRhDA9WQVRFUy1TMjgxNjAwM0QxGjAYBgNV
BAMMESouZ3VhcmRpYWNpdmlsLmVzMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEApA++
/lNaimrEGvflr1eNHDDLmEZF/FKIA9Bd9YJnieciGg+3ZJ9/Kd99eV1V9zIgfdSLLDucMeflp6s6
8v563u4+zX7AgREB539rKAaCUwtqVcutZROL+NIjkoHHtMHgMlCbbQaKC32n8awaB0YRNGI+eLMJ
4u7X2iBR1OohEjGFa1xfDS2UHOeWsxW5eDPGhK3iaDSHmcy1jtIGVmas4YtcNFRf/8y2v2I2QqHY
2l7IbiDMAdSW2nPme/5HZXvIlkDdD4SG17xhIAe1uPEBKd0g6lt89OLCFetNwcRaqjFtA5DrpmiQ
0hqHQTk8AST6ibpRvAZCKvln+uXNWcDDywIDAQABo4IFVDCCBVAwDAYDVR0TAQH/BAIwADCBgQYI
KwYBBQUHAQEEdTBzMDsGCCsGAQUFBzABhi9odHRwOi8vb2NzcGNvbXAuY2VydC5mbm10LmVzL29j
c3AvT2NzcFJlc3BvbmRlcjA0BggrBgEFBQcwAoYoaHR0cDovL3d3dy5jZXJ0LmZubXQuZXMvY2Vy
dHMvQUNDT01QLmNydDCCASgGA1UdIASCAR8wggEbMIIBDQYKKwYBBAGsZgMJETCB/jApBggrBgEF
BQcCARYdaHR0cDovL3d3dy5jZXJ0LmZubXQuZXMvZHBjcy8wgdAGCCsGAQUFBwICMIHDDIHAQ2Vy
dGlmaWNhZG8gd2lsZGNhcmQgZGUgYXV0ZW50aWNhY2nDs24gZGUgc2l0aW8gd2ViIHNlZ8O6biBy
ZWdsYW1lbnRvIGV1cm9wZW8gZUlEQVMuIFN1amV0byBhIGNvbmRpY2lvbmVzIGRlIHVzbyBzZWfD
um4gRFBDIGRlIEZOTVQtUkNNLCBOSUY6IFEyODI2MDA0LUogKEMvSm9yZ2UgSnVhbiAxMDYtMjgw
MDktTWFkcmlkLUVzcGHDsWEpMAgGBgQAj3oBBzAcBgNVHREEFTATghEqLmd1YXJkaWFjaXZpbC5l
czATBgNVHSUEDDAKBggrBgEFBQcDATAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0OBBYEFEMrHDu3J/7S
OVend+G0LsfbflPRMIGmBggrBgEFBQcBAwSBmTCBljALBgYEAI5GAQMCAQ8wEwYGBACORgEGMAkG
BwQAjkYBBgMwcgYGBACORgEFMGgwMhYsaHR0cHM6Ly93d3cuY2VydC5mbm10LmVzL3Bkcy9QRFNf
Q09NUF9lcy5wZGYTAmVzMDIWLGh0dHBzOi8vd3d3LmNlcnQuZm5tdC5lcy9wZHMvUERTX0NPTVBf
ZW4ucGRmEwJlbjAfBgNVHSMEGDAWgBQZ+FgvFNamzJsEmAgNTNerAKeDZTCB4AYDVR0fBIHYMIHV
MIHSoIHPoIHMhoGebGRhcDovL2xkYXBjb21wLmNlcnQuZm5tdC5lcy9DTj1DUkwxLE9VPUFDJTIw
Q29tcG9uZW50ZXMlMjBJbmZvcm1hdGljb3MsTz1GTk1ULVJDTSxDPUVTP2NlcnRpZmljYXRlUmV2
b2NhdGlvbkxpc3Q7YmluYXJ5P2Jhc2U/b2JqZWN0Y2xhc3M9Y1JMRGlzdHJpYnV0aW9uUG9pbnSG
KWh0dHA6Ly93d3cuY2VydC5mbm10LmVzL2NybHNjb21wL0NSTDEuY3JsMIIBfwYKKwYBBAHWeQIE
AgSCAW8EggFrAWkAdwApeb7wnjk5IfBWc59jpXflvld9nGAK+PlNXSZcJV3HhAAAAXJbS5kkAAAE
AwBIMEYCIQDxUFJkb+yYGmvDckxbKLZvcycZmEvAX6g8GH3jqr7RjAIhAOshzUdC84VntNxq56Dx
160eCj65UVL9A64z7hnXnUKBAHYARqVV63X6kSAwtaKJafTzfREsQXS+/Um4havy/HD+bUcAAAFy
W0ua9gAABAMARzBFAiEAj3S5/JDapowZIsHUOFXQuxxb26oIhOy/YfNLZI01pQMCIEN8b4AxHp9Z
mj8StFvP4/YLqvYgQbhgewCvFExlsRtVAHYAQcjKsd8iRkoQxqE6CUKHXk4xixsD6+tLx2jwkGKW
BvYAAAFyW0ucLAAABAMARzBFAiEAt+XKebp1KrEamXlKU2VkKt6lsRiylbNFTCTpGm5GAP8CIEUT
WXZee7o83zyXQ9nBy0t8TcRtI70vuheLQ909va6dMA0GCSqGSIb3DQEBCwUAA4IBAQBJAplPXRRv
b4nLZFqQvQjHlr0TSKTiC21vAzXe/1iiuPOPZF3AE1ZcOMEqiPWTN6GOR3UYksZE2Cfm0xqNOytr
bj/INmu5tX5U7yvGQxiB8EdHaTdQQknAi/IBEx0KJE+EAT+upOPYbne5km1lffN19WwQ2f3ybAbj
l5ZpWR6lSuV3JStUllHAS/0HjwBbqHhxJZ2BTG4Ca5SkHnbK/K1mfwLe2VGVYp7WWUzKkSQQ8Hs0
RuY9EZ0sovRH7lL/EIf7uV/5d1V4kW1h7F38jEPDH4UVTHDMMaR9NV/4pES0zQnI6y5NJCiRP39k
GFyKTE/4x7pFeIFnXXIwswUWFA9h
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIG1jCCBL6gAwIBAgIQNMarBE42mRJRyCULbJTWwDANBgkqhkiG9w0BAQsFADA7MQswCQYDVQQG
EwJFUzERMA8GA1UECgwIRk5NVC1SQ00xGTAXBgNVBAsMEEFDIFJBSVogRk5NVC1SQ00wHhcNMTMw
NjI0MTA1MjU5WhcNMjgwNjI0MTA1MjU5WjBHMQswCQYDVQQGEwJFUzERMA8GA1UECgwIRk5NVC1S
Q00xJTAjBgNVBAsMHEFDIENvbXBvbmVudGVzIEluZm9ybcOhdGljb3MwggEiMA0GCSqGSIb3DQEB
AQUAA4IBDwAwggEKAoIBAQCXVx8rdbF7/xY44CaSqzzGo5BhvzA8knxC/3KJYVzTf+CkOvMxMUDu
b8b0h38MDujm/RKZhBNOWbKhxF3U61ZVhcR9xOCciuS/soT80m3BByxAKcZsNka0jCA4XRkglDaA
FxCHEZ06MOnvXsSOZDfPYahbQ3VFCVycJuhlHdAwSpmceQwcRYkR6YgXwTiyzCNGivMKAmRS3dIt
qDOmDW/nxiDFq/Jd8VWY7GFkwbbAeqYId8FjN8zfvafunsB9SLFkUjPPMeqfmC7Bdh7HMxLpaOXR
OwH201cmlebiPkn0xSFxXFqwhhr6yN8UQYZ3O/+xdHLrS6DS9+CJUF6d09ijAgMBAAGjggLIMIIC
xDASBgNVHRMBAf8ECDAGAQH/AgEAMA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUGfhYLxTWpsyb
BJgIDUzXqwCng2UwgZgGCCsGAQUFBwEBBIGLMIGIMEkGCCsGAQUFBzABhj1odHRwOi8vb2NzcGZu
bXRyY21jYS5jZXJ0LmZubXQuZXMvb2NzcGZubXRyY21jYS9PY3NwUmVzcG9uZGVyMDsGCCsGAQUF
BzAChi9odHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9jZXJ0cy9BQ1JBSVpGTk1UUkNNLmNydDAfBgNV
HSMEGDAWgBT3fcX9xOiaG3dkp/UdoMy/h2CabTCB6wYDVR0gBIHjMIHgMIHdBgRVHSAAMIHUMCkG
CCsGAQUFBwIBFh1odHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzCBpgYIKwYBBQUHAgIwgZkM
gZZTdWpldG8gYSBsYXMgY29uZGljaW9uZXMgZGUgdXNvIGV4cHVlc3RhcyBlbiBsYSBEZWNsYXJh
Y2nDs24gZGUgUHLDoWN0aWNhcyBkZSBDZXJ0aWZpY2FjacOzbiBkZSBsYSBGTk1ULVJDTSAoIEMv
IEpvcmdlIEp1YW4sIDEwNi0yODAwOS1NYWRyaWQtRXNwYcOxYSkwgdQGA1UdHwSBzDCByTCBxqCB
w6CBwIaBkGxkYXA6Ly9sZGFwZm5tdC5jZXJ0LmZubXQuZXMvQ049Q1JMLE9VPUFDJTIwUkFJWiUy
MEZOTVQtUkNNLE89Rk5NVC1SQ00sQz1FUz9hdXRob3JpdHlSZXZvY2F0aW9uTGlzdDtiaW5hcnk/
YmFzZT9vYmplY3RjbGFzcz1jUkxEaXN0cmlidXRpb25Qb2ludIYraHR0cDovL3d3dy5jZXJ0LmZu
bXQuZXMvY3Jscy9BUkxGTk1UUkNNLmNybDANBgkqhkiG9w0BAQsFAAOCAgEAo2bsQ2xLDcyodieq
jd+uy/lfxDw/MbrAq/ZaNFkIlcypUYamOM4vrm5rz8oLjPCoLkJ48P+nP08Gkcl5Q6q6VFcZLia+
U3gfHXrkyqToQlrtViGCGH3xA4u56XtMHGXSdk9vQ0yDnW5f7bUEkp+uvcKewrOvNcpbIAgD4eU7
gdOS0w7BagcFRBgTKBw2s3z73fRZtouJg/atmWYtXbBsfNjph+pCh+h5sbSyZUVzO5AemyjpYYYN
MWDQrTXq+7O8zIPuPaNESjEexuzn+VjHG90RlUK1LygARi+Ir0opD2w6erb/hK8Eea7MFdKQ2ASq
NBGJggNo5vfPVvjHiL+Antmh7mQSKL+4YwFU64d4KK9k0C1mbJethDQFKcjTK1vMvnXFiupsIuyT
qwKauo7u2zMKzY4r3VYOW9TpMyLPFIY8pII5GyNzXlL0F4nscOvduTEPEYqxeNJfpDDPY/DO8Wfx
gdRTy2W3D/UoAulb+Y+nuzGGCtFQrsSMQX487R+aY0nWot/hajef6BcPuxhDfQrg5IafrISVmcJA
plb3tXhh0sz7RbYz6jf1bke4eU5fnrTMtGlVteUL2vjrfUPHW07kBJuaQ7sxORNV3bpHisOnHj+A
riQzCn5vINpSHW6hTm7IfRkbltu/aQrsMuUhP7HE/v+uXe5CuboV5ubZhHU=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFgzCCA2ugAwIBAgIPXZONMGc2yAYdGsdUhGkHMA0GCSqGSIb3DQEBCwUAMDsxCzAJBgNVBAYT
AkVTMREwDwYDVQQKDAhGTk1ULVJDTTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTAeFw0wODEw
MjkxNTU5NTZaFw0zMDAxMDEwMDAwMDBaMDsxCzAJBgNVBAYTAkVTMREwDwYDVQQKDAhGTk1ULVJD
TTEZMBcGA1UECwwQQUMgUkFJWiBGTk1ULVJDTTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoC
ggIBALpxgHpMhm5/yBNtwMZ9HACXjywMI7sQmkCpGreHiPibVmr75nuOi5KOpyVdWRHbNi63URcf
qQgfBBckWKo3Shjf5TnUV/3XwSyRAZHiItQDwFj8d0fsjz50Q7qsNI1NOHZnjrDIbzAzWHFctPVr
btQBULgTfmxKo0nRIBnuvMApGGWn3v7v3QqQIecaZ5JCEJhfTzC8PhxFtBDXaEAUwED653cXeuYL
j2VbPNmaUtu1vZ5Gzz3rkQUCwJaydkxNEJY7kvqcfw+Z374jNUUeAlz+taibmSXaXvMiwzn15Cou
08YfxGyqxRxqAQVKL9LFwag0Jl1mpdICIfkYtwb1TplvqKtMUejPUBjFd8g5CSxJkjKZqLsXF3mw
WsXmo8RZZUc1g16p6DULmbvkzSDGm0oGObVo/CK67lWMK07q87Hj/LaZmtVC+nFNCM+HHmpxffnT
tOmlcYF7wk5HlqX2doWjKI/pgG6BU6VtX7hI+cL5NqYuSf+4lsKMB7ObiFj86xsc3i1w4peSMKGJ
47xVqCfWS+2QrYv6YyVZLag13cqXM7zlzced0ezvXg5KkAYmY6252TUtB7p2ZSysV4999AeU14EC
ll2jB0nVetBX+RvnU0Z1qrB5QstocQjpYL05ac70r8NWQMetUqIJ5G+GR4of6ygnXYMgrwTJbFaa
i0b1AgMBAAGjgYMwgYAwDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYE
FPd9xf3E6Jobd2Sn9R2gzL+HYJptMD4GA1UdIAQ3MDUwMwYEVR0gADArMCkGCCsGAQUFBwIBFh1o
dHRwOi8vd3d3LmNlcnQuZm5tdC5lcy9kcGNzLzANBgkqhkiG9w0BAQsFAAOCAgEAB5BK3/MjTvDD
nFFlm5wioooMhfNzKWtN/gHiqQxjAb8EZ6WdmF/9ARP67Jpi6Yb+tmLSbkyU+8B1RXxlDPiyN8+s
D8+Nb/kZ94/sHvJwnvDKuO+3/3Y3dlv2bojzr2IyIpMNOmqOFGYMLVN0V2Ue1bLdI4E7pWYjJ2cJ
j+F3qkPNZVEI7VFY/uY5+ctHhKQV8Xa7pO6kO8Rf77IzlhEYt8llvhjho6Tc+hj507wTmzl6NLrT
Qfv6MooqtyuGC2mDOL7Nii4LcK2NJpLuHvUBKwrZ1pebbuCoGRw6IYsMHkCtA+fdZn71uSANA+iW
+YJF1DngoABd15jmfZ5nc8OaKveri6E6FO80vFIOiZiaBECEHX5FaZNXzuvO+FB8TxxuBEOb+dY7
Ixjp6o7RTUaN8Tvkasq6+yO3m/qZASlaWFot4/nUbQ4mrcFuNLwy+AwF+mWj2zs3gyLp1txyM/1d
8iC9djwj2ij3+RvrWWTV3F9yfiD8zYm1kGdNYno/Tq0dwzn+evQoFt9B9kiABdcPUXmsEKvU7ANm
5mqwujGSQkBqvjrTcuFqN1W8rB2Vt2lh8kORdOag0wokRqEIr9baRRmW1FMdW4R58MD3R++Lj8UG
rp1MYp3/RgT408m2ECVAdf4WqslKYIYvuu8wd+RU4riEmViAqhOLUTpPSPaLtrM=
-----END CERTIFICATE-----

View File

@@ -7,12 +7,14 @@
<field name="arch" type="xml"> <field name="arch" type="xml">
<data> <data>
<xpath expr="//field[@name='partner_id']" position="after"> <xpath expr="//field[@name='partner_id']" position="after">
<field name="lastname" />
<field name="lastname2" /> <field name="lastname2" />
<field name="gender" /> <field name="gender" />
<field name="birthdate_date" /> <field name="birthdate_date" />
<field name="document_type" /> <field name="document_type" />
<field name="document_number" /> <field name="document_number" />
<field name="document_expedition_date" /> <field name="document_expedition_date" />
<field name="nationality_id" />
</xpath> </xpath>
</data> </data>
</field> </field>
@@ -32,6 +34,7 @@
<field name="document_type" /> <field name="document_type" />
<field name="document_number" /> <field name="document_number" />
<field name="document_expedition_date" /> <field name="document_expedition_date" />
<field name="nationality_id" />
</xpath> </xpath>
</data> </data>
</field> </field>
@@ -51,6 +54,7 @@
<field name="document_type" /> <field name="document_type" />
<field name="document_number" /> <field name="document_number" />
<field name="document_expedition_date" /> <field name="document_expedition_date" />
<field name="nationality_id" />
</xpath> </xpath>
</data> </data>
</field> </field>
@@ -70,11 +74,11 @@
<field name="document_type" /> <field name="document_type" />
<field name="document_number" /> <field name="document_number" />
<field name="document_expedition_date" /> <field name="document_expedition_date" />
<field name="nationality_id" />
</xpath> </xpath>
</data> </data>
</field> </field>
</record> </record>
<record id="pms_checkin_partner_view_search" model="ir.ui.view"> <record id="pms_checkin_partner_view_search" model="ir.ui.view">
<field name="name">Checkin partner view tree Spain</field> <field name="name">Checkin partner view tree Spain</field>
<field name="model">pms.checkin.partner</field> <field name="model">pms.checkin.partner</field>
@@ -93,6 +97,4 @@
</data> </data>
</field> </field>
</record> </record>
</odoo> </odoo>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8" ?>
<odoo>
<!-- Hotel Settings -->
<record id="view_property_form_pms_l10n_es" model="ir.ui.view">
<field name="name">Property Form l10n_es</field>
<field name="model">pms.property</field>
<field name="inherit_id" ref="pms.pms_property_views_form" />
<field name="arch" type="xml">
<xpath expr="//page[@name='property_settings']" position="inside">
<div class="row">
<div class="col-12">
<div class="o_horizontal_separator">
Guest information sending settings
</div>
</div>
<div class="col-6">
<group name="property_data">
<field name="institution" />
<field name="institution_property_id" />
</group>
</div>
<div class="col-6 px-0">
<group name="property_data">
<field name="institution_user" />
<field name="institution_password" password="True" />
</group>
<button
name="test_gc_connection"
class="btn btn-primary btn-sm"
type="object"
string="Test user/password"
/>
</div>
</div>
</xpath>
</field>
</record>
</odoo>

View File

@@ -0,0 +1 @@
from . import traveller_report

View File

@@ -0,0 +1,237 @@
import base64
import datetime
import time
from datetime import date
import requests
from bs4 import BeautifulSoup as bs
from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
from odoo.modules.module import get_module_resource
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")
def generate_file(self):
# get the active property
pms_property = self.env["pms.property"].search(
[("id", "=", self.env.user.get_active_property_ids()[0])]
)
# build content
content = self.generate_checkin_list(pms_property.id)
if not pms_property.institution_property_id:
raise ValidationError(
_("The guest information sending settings is not property updated.")
)
elif not content:
raise ValidationError(_("There is no guest information to send."))
else:
# file creation
txt_binary = self.env["traveller.report.wizard"].create(
{
"txt_filename": pms_property.institution_property_id + ".999",
"txt_binary": base64.b64encode(str.encode(content)),
"txt_message": content,
}
)
return {
"name": _("Traveller Report"),
"res_id": txt_binary.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",
"view_type": "form",
}
def generate_checkin_list(self, property_id):
# check if there's guests info pending to send
if self.env["pms.checkin.partner"].search_count(
[
("state", "=", "onboard"),
("arrival", ">=", str(date.today()) + " 0:00:00"),
("arrival", "<=", str(date.today()) + " 23:59:59"),
]
):
# get the active property
pms_property = self.env["pms.property"].search([("id", "=", property_id)])
# check if the GC configuration info is properly set
if not (
pms_property.name
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
):
raise ValidationError(
_("Check the GC configuration to send the guests info")
)
else:
# get checkin partners info to send
lines = self.env["pms.checkin.partner"].search(
[
("state", "=", "onboard"),
("arrival", ">=", str(date.today()) + " 0:00:00"),
("arrival", "<=", str(date.today()) + " 23:59:59"),
]
)
# build the property info record
# 1 | property id | property name | date | nº of checkin partners
content = (
"1|"
+ pms_property.institution_property_id.upper()
+ "|"
+ 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
for line in lines:
content += "2"
# [P|N|..]
if line.document_type != "D":
content += "||" + line.document_number.upper() + "|"
else:
content += "|" + line.document_number.upper() + "||"
content += line.document_type + "|"
content += line.document_expedition_date.strftime("%Y%m%d") + "|"
content += line.lastname.upper() + "|"
if line.lastname2:
content += line.lastname2.upper()
content += "|" + 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, pms_property=False):
url = "https://hospederias.guardiacivil.es/"
login_route = "/hospederias/login.do"
upload_file_route = "/hospederias/cargaFichero.do"
logout_route = "/hospederias/logout.do"
called_from_user = False
if not pms_property:
called_from_user = True
# get the active property
pms_property = self.env["pms.property"].search(
[("id", "=", self.env.user.get_active_property_ids()[0])]
)
if not (
pms_property
and pms_property.institution_property_id
and pms_property.institution_user
and pms_property.institution_password
):
raise ValidationError(
_("The guest information sending settings is not complete.")
)
content = self.generate_checkin_list(pms_property.id)
if 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,
}
# login
response_login = session.post(
url + login_route,
headers=headers,
data=login_payload,
verify=get_module_resource("pms_l10n_es", "static", "cert.pem"),
)
# 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", content)}
time.sleep(1)
# 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(1)
# 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"
raise ValidationError(msg)
else:
if called_from_user:
message = {
"type": "ir.actions.client",
"tag": "display_notification",
"params": {
"title": _("Sent succesfully!"),
"message": _("Successful file sending"),
"sticky": False,
},
}
return message
@api.model
def send_file_gc_async(self):
for prop in self.env["pms.property"].search([]):
self.with_delay().send_file_gc(prop)

View File

@@ -0,0 +1,62 @@
<?xml version="1.0" ?>
<odoo>
<record id="traveller_report_wizard" model="ir.ui.view">
<field name="name">Traveller Report</field>
<field name="model">traveller.report.wizard</field>
<field name="arch" type="xml">
<form>
<field name="txt_filename" invisible="1" />
<div class="row">
<div class="col-12">
<group attrs="{'invisible': [('txt_message','=',False)]}">
<field name="txt_message" readonly="1" />
</group>
<group attrs="{'invisible': [('txt_message','=',False)]}">
<field
name="txt_binary"
filename="txt_filename"
readonly="1"
/>
</group>
</div>
</div>
<div class="row ">
<div class="col-3">
<button
name="generate_file"
class="btn btn-primary btn-sm"
type="object"
string="Preview file"
/>
</div>
<div class="col-3">
<button
name="send_file_gc"
class="btn btn-primary btn-sm"
type="object"
string="Send file"
attrs="{'invisible': [('txt_message','=',False)]}"
/>
</div>
</div>
<footer />
</form>
</field>
</record>
<record id="action_traveller_report" model="ir.actions.act_window">
<field name="name">Traveller Report</field>
<field name="type">ir.actions.act_window</field>
<field name="res_model">traveller.report.wizard</field>
<field name="view_id" ref="traveller_report_wizard" />
<field name="view_mode">form</field>
<field name="target">new</field>
</record>
<menuitem
id="menu_traveller_report"
name="Traveller Report"
sequence="30"
parent="pms.pms_configuration_menu"
action="action_traveller_report"
/>
</odoo>

View File

@@ -1,2 +1,3 @@
# generated from manifests external_dependencies # generated from manifests external_dependencies
bs4
xlrd xlrd