diff --git a/oca_dependencies.txt b/oca_dependencies.txt index f28a53f0f..eb1f856db 100644 --- a/oca_dependencies.txt +++ b/oca_dependencies.txt @@ -1,2 +1,3 @@ partner-contact reporting-engine +queue diff --git a/pms_l10n_es/__init__.py b/pms_l10n_es/__init__.py index 0650744f6..aee8895e7 100644 --- a/pms_l10n_es/__init__.py +++ b/pms_l10n_es/__init__.py @@ -1 +1,2 @@ from . import models +from . import wizards diff --git a/pms_l10n_es/__manifest__.py b/pms_l10n_es/__manifest__.py index 912abc329..48304201c 100644 --- a/pms_l10n_es/__manifest__.py +++ b/pms_l10n_es/__manifest__.py @@ -15,10 +15,23 @@ "partner_second_lastname", "partner_contact_gender", "partner_contact_birthdate", + "partner_contact_nationality", + "queue_job", ], + "external_dependencies": { + "python": [ + "bs4", + ], + }, "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_property_views.xml", "views/res_partner_views.xml", + "wizards/traveller_report.xml", ], "installable": True, } diff --git a/pms_l10n_es/data/cron_jobs.xml b/pms_l10n_es/data/cron_jobs.xml new file mode 100644 index 000000000..f99af664a --- /dev/null +++ b/pms_l10n_es/data/cron_jobs.xml @@ -0,0 +1,21 @@ + + + + + Automatic Send Traveller Report + + 1 + + days + -1 + + code + + + model.send_file_gc_async() + + + diff --git a/pms_l10n_es/data/queue_data.xml b/pms_l10n_es/data/queue_data.xml new file mode 100644 index 000000000..42bfe7236 --- /dev/null +++ b/pms_l10n_es/data/queue_data.xml @@ -0,0 +1,9 @@ + + + + + gc_file_send + + + + diff --git a/pms_l10n_es/data/queue_job_function_data.xml b/pms_l10n_es/data/queue_job_function_data.xml new file mode 100644 index 000000000..c59467665 --- /dev/null +++ b/pms_l10n_es/data/queue_job_function_data.xml @@ -0,0 +1,12 @@ + + + + + send_file_async + + + + diff --git a/pms_l10n_es/models/__init__.py b/pms_l10n_es/models/__init__.py index 4354d9499..ebd8614e3 100644 --- a/pms_l10n_es/models/__init__.py +++ b/pms_l10n_es/models/__init__.py @@ -1,2 +1,3 @@ from . import res_partner from . import pms_checkin_partner +from . import pms_property diff --git a/pms_l10n_es/models/pms_checkin_partner.py b/pms_l10n_es/models/pms_checkin_partner.py index 81fd7be26..1daf65134 100644 --- a/pms_l10n_es/models/pms_checkin_partner.py +++ b/pms_l10n_es/models/pms_checkin_partner.py @@ -62,12 +62,13 @@ class PmsCheckinPartner(models.Model): store=True, readonly=False, ) - - @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 + nationality_id = fields.Many2one( + string="Nationality ID", + compute="_compute_nationality", + comodel_name="res.country", + store=True, + readonly=False, + ) @api.depends("partner_id", "partner_id.firstname") def _compute_firstname(self): @@ -75,6 +76,12 @@ class PmsCheckinPartner(models.Model): if not record.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") def _compute_lastname2(self): for record in self: @@ -113,6 +120,12 @@ class PmsCheckinPartner(models.Model): if not record.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 def _checkin_mandatory_fields(self, depends=False): mandatory_fields = super(PmsCheckinPartner, self)._checkin_mandatory_fields( @@ -120,7 +133,6 @@ class PmsCheckinPartner(models.Model): ) mandatory_fields.extend( [ - "lastname2", "birthdate_date", "document_number", "document_type", diff --git a/pms_l10n_es/models/pms_property.py b/pms_l10n_es/models/pms_property.py new file mode 100644 index 000000000..9a0fba218 --- /dev/null +++ b/pms_l10n_es/models/pms_property.py @@ -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")) diff --git a/pms_l10n_es/security/ir.model.access.csv b/pms_l10n_es/security/ir.model.access.csv new file mode 100644 index 000000000..d8d1060d6 --- /dev/null +++ b/pms_l10n_es/security/ir.model.access.csv @@ -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 diff --git a/pms_l10n_es/static/cert.pem b/pms_l10n_es/static/cert.pem new file mode 100644 index 000000000..7011ea9ea --- /dev/null +++ b/pms_l10n_es/static/cert.pem @@ -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----- diff --git a/pms_l10n_es/views/pms_checkin_partner_views.xml b/pms_l10n_es/views/pms_checkin_partner_views.xml index 8eefdeef2..96a1f4de6 100644 --- a/pms_l10n_es/views/pms_checkin_partner_views.xml +++ b/pms_l10n_es/views/pms_checkin_partner_views.xml @@ -7,12 +7,14 @@ + + @@ -32,6 +34,7 @@ + @@ -51,6 +54,7 @@ + @@ -70,11 +74,11 @@ + - Checkin partner view tree Spain pms.checkin.partner @@ -93,6 +97,4 @@ - - diff --git a/pms_l10n_es/views/pms_property_views.xml b/pms_l10n_es/views/pms_property_views.xml new file mode 100644 index 000000000..53a3f0e34 --- /dev/null +++ b/pms_l10n_es/views/pms_property_views.xml @@ -0,0 +1,40 @@ + + + + + + Property Form l10n_es + pms.property + + + + +
+
+
+ Guest information sending settings +
+
+
+ + + + +
+
+ + + + +
+
+
+
+
+
diff --git a/pms_l10n_es/wizards/__init__.py b/pms_l10n_es/wizards/__init__.py new file mode 100644 index 000000000..1a18223f3 --- /dev/null +++ b/pms_l10n_es/wizards/__init__.py @@ -0,0 +1 @@ +from . import traveller_report diff --git a/pms_l10n_es/wizards/traveller_report.py b/pms_l10n_es/wizards/traveller_report.py new file mode 100644 index 000000000..d260f3dee --- /dev/null +++ b/pms_l10n_es/wizards/traveller_report.py @@ -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) diff --git a/pms_l10n_es/wizards/traveller_report.xml b/pms_l10n_es/wizards/traveller_report.xml new file mode 100644 index 000000000..54ce3dbf7 --- /dev/null +++ b/pms_l10n_es/wizards/traveller_report.xml @@ -0,0 +1,62 @@ + + + + Traveller Report + traveller.report.wizard + +
+ +
+
+ + + + + + +
+ +
+
+
+
+
+
+
+