From a60d3304ef3a276cbd96ff62d48af9a19defffc3 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Thu, 19 Dec 2019 00:29:26 +0100 Subject: [PATCH 1/6] [ADD] connector_cbl: new module --- connector_cbl/README.rst | 29 +++ connector_cbl/__init__.py | 6 + connector_cbl/__manifest__.py | 30 +++ connector_cbl/controllers/__init__.py | 5 + connector_cbl/controllers/main.py | 35 ++++ connector_cbl/models/__init__.py | 6 + connector_cbl/models/backend.py | 66 +++++++ connector_cbl/models/cbl.py | 205 +++++++++++++++++++++ connector_cbl/security/ir.model.access.csv | 2 + connector_cbl/static/description/icon.png | Bin 0 -> 6342 bytes connector_cbl/templates/cbl_template.xml | 147 +++++++++++++++ connector_cbl/views/backend_views.xml | 60 ++++++ connector_cbl/views/menus.xml | 11 ++ 13 files changed, 602 insertions(+) create mode 100644 connector_cbl/README.rst create mode 100644 connector_cbl/__init__.py create mode 100644 connector_cbl/__manifest__.py create mode 100644 connector_cbl/controllers/__init__.py create mode 100644 connector_cbl/controllers/main.py create mode 100644 connector_cbl/models/__init__.py create mode 100644 connector_cbl/models/backend.py create mode 100644 connector_cbl/models/cbl.py create mode 100644 connector_cbl/security/ir.model.access.csv create mode 100644 connector_cbl/static/description/icon.png create mode 100644 connector_cbl/templates/cbl_template.xml create mode 100644 connector_cbl/views/backend_views.xml create mode 100644 connector_cbl/views/menus.xml diff --git a/connector_cbl/README.rst b/connector_cbl/README.rst new file mode 100644 index 000000000..f38073fc4 --- /dev/null +++ b/connector_cbl/README.rst @@ -0,0 +1,29 @@ +.. image:: https://img.shields.io/badge/licence-AGPL--3-blue.svg + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 + +============= +CBL Connector +============= + +* Connector to get tracking info from CBL + +Bug Tracker +=========== + +Bugs are tracked on `GitHub Issues +`_. In case of trouble, please +check there if your issue has already been reported. If you spotted it first, +help us smashing it by providing a detailed and welcomed feedback. + +Credits +======= + +Contributors +------------ + +* Eric Antones + + + + diff --git a/connector_cbl/__init__.py b/connector_cbl/__init__.py new file mode 100644 index 000000000..6036bad84 --- /dev/null +++ b/connector_cbl/__init__.py @@ -0,0 +1,6 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from . import controllers +from . import models diff --git a/connector_cbl/__manifest__.py b/connector_cbl/__manifest__.py new file mode 100644 index 000000000..d107dffd3 --- /dev/null +++ b/connector_cbl/__manifest__.py @@ -0,0 +1,30 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +{ + 'name': "CBL Connector", + 'description': "Connector to get tracking info from CBL", + 'version': '11.0.0.1.0', + 'author': 'NuoBiT Solutions, S.L., Eric Antones', + 'license': 'AGPL-3', + 'category': 'Connector', + 'website': 'https://github.com/nuobit', + 'external_dependencies': { + 'python': [ + 'requests', + 'lxml', + 'json', + ], + }, + 'depends': [ + 'connector', + ], + 'data': [ + 'views/backend_views.xml', + 'views/menus.xml', + 'templates/cbl_template.xml', + 'security/ir.model.access.csv', + ], + 'installable': True, +} diff --git a/connector_cbl/controllers/__init__.py b/connector_cbl/controllers/__init__.py new file mode 100644 index 000000000..39da29ee7 --- /dev/null +++ b/connector_cbl/controllers/__init__.py @@ -0,0 +1,5 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from . import main diff --git a/connector_cbl/controllers/main.py b/connector_cbl/controllers/main.py new file mode 100644 index 000000000..8db6a615c --- /dev/null +++ b/connector_cbl/controllers/main.py @@ -0,0 +1,35 @@ +from odoo import http + +from odoo.addons.connector_cbl.models.cbl import CBL +import werkzeug.exceptions + +import logging + +_logger = logging.getLogger(__name__) + + +class CBLController(http.Controller): + @http.route(['/tracking/cbl/', + ], type='http', auth="public") + def tracking_data(self, tracking_number=None): + remote_ip = http.request.httprequest.environ['REMOTE_ADDR'] + cbl_backend = http.request.env['cbl.backend'].sudo().search([ + ('active', '=', True), + ('state', '=', 'checked'), + ]).sorted(lambda x: x.sequence) + + if not cbl_backend: + raise werkzeug.exceptions.InternalServerError("No configuration found") + + er = CBL(username=cbl_backend.username, + password=cbl_backend.password) + + _logger.info("Asking %s CBL shipment... from %s" % (tracking_number, remote_ip)) + if not er.login(): + raise werkzeug.exceptions.Unauthorized() + data = er.filter_by_refcte(tracking_number) + # if not er.logout(): + # raise werkzeug.exceptions.InternalServerError("Logout not successful") + _logger.info("CBL shipment %s successfully retrieved from %s." % (tracking_number, remote_ip)) + + return http.request.render('connector_cbl.index', {'expeditions': data}) diff --git a/connector_cbl/models/__init__.py b/connector_cbl/models/__init__.py new file mode 100644 index 000000000..1935b26a3 --- /dev/null +++ b/connector_cbl/models/__init__.py @@ -0,0 +1,6 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from . import backend +from . import cbl diff --git a/connector_cbl/models/backend.py b/connector_cbl/models/backend.py new file mode 100644 index 000000000..ed7a72788 --- /dev/null +++ b/connector_cbl/models/backend.py @@ -0,0 +1,66 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +from odoo import models, fields, api, exceptions, _ + +from . import cbl + +import logging + +_logger = logging.getLogger(__name__) + + +class CBLBackend(models.Model): + _name = 'cbl.backend' + _inherit = 'connector.backend' + + _description = 'CBL Backend Configuration' + + @api.model + def _select_state(self): + return [('draft', 'Draft'), + ('checked', 'Checked'), + ('production', 'In Production')] + + name = fields.Char('Name', required=True) + + sequence = fields.Integer('Sequence', required=True, default=1) + + username = fields.Char('Username', required=True) + password = fields.Char('Password', required=True) + + output = fields.Text('Output', readonly=True) + + active = fields.Boolean( + string='Active', + default=True + ) + state = fields.Selection( + selection='_select_state', + string='State', + default='draft' + ) + + @api.multi + def button_reset_to_draft(self): + self.ensure_one() + self.write({'state': 'draft', 'output': None}) + + @api.multi + def _check_connection(self): + self.ensure_one() + er = cbl.CBL(username=self.username, password=self.password) + + if not er.login(): + raise exceptions.ValidationError("Error on logging in") + + # if not er.logout(): + # raise exceptions.ValidationError("Error on logging out") + + self.output = "OK" + + @api.multi + def button_check_connection(self): + self._check_connection() + self.write({'state': 'checked'}) diff --git a/connector_cbl/models/cbl.py b/connector_cbl/models/cbl.py new file mode 100644 index 000000000..5a65bdea2 --- /dev/null +++ b/connector_cbl/models/cbl.py @@ -0,0 +1,205 @@ +# Copyright NuoBiT Solutions, S.L. () +# Eric Antones +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) + +import requests + +import datetime +import re + +from lxml import etree +import json + +import logging + +_logger = logging.getLogger(__name__) + + +def xpath1(tree, xpath): + tag_l = tree.xpath(xpath) + if len(tag_l) != 1: + raise Exception("Expected 1 element") + + return tag_l[0] + + +class CBL: + _base_url = 'https://clientes.cbl-logistica.com' + + def __init__(self, username, password, debug=False): + self.session = requests.Session() + self.viewstate = None + self.username = username + self.password = password + self.headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', + } + self.debug = debug + + def login(self): + url = '%s/login.aspx' % self._base_url + + ## obtenim les dades del viewstate + res = self.session.get(url, headers=self.headers) + self._update_viewstate(res) + + ## fem el login + form_data = { + 'ScriptManager1': 'UpdatePanel1|Login1$LoginButton', + 'Login1$UserName': self.username, + 'Login1$Password': self.password, + 'Login1$LoginButton': 'ENTRAR', + } + form_data.update(self.viewstate) + res = self.session.post(url, data=form_data, headers=self.headers) + self._update_viewstate(res) + + ## comprovem que el login ha estat ok + return not self.is_login_page(res) + + def filter_by_refcte(self, refcte): + url = '%s/Consultas/envios.aspx' % self._base_url + + res = self.session.get(url, headers=self.headers) + self._update_viewstate(res) + + userid_tags = ['ctl00_TOPCONTENEDOR_WebCUI_def_user', + 'ctl00_TOPCONTENEDOR_WebCUI_loginid', + 'ctl00_TOPCONTENEDOR_WebCUI_recid'] + + tree = etree.HTML(res.text.encode()) + userid = None + for ut in userid_tags: + ut_tag = xpath1(tree, "//form[@id='aspnetForm']//input[@id='%s']" % ut) + if ut_tag is None: + raise Exception("Expected value") + if not userid: + userid = ut_tag.attrib['value'] + else: + if userid != ut_tag.attrib['value']: + raise Exception("Diferent userid values") + + if not userid: + raise Exception("userid not found") + + form_data = { + 'ctl00$AJAXScriptManager': 'ctl00$UpdatePanel1|ctl00$TOPCONTENEDOR$WebCUI_buscar', + + 'ctl00$TOPCONTENEDOR$WebCUI_del': '081', + 'ctl00$TOPCONTENEDOR$WebCUI_usuario': self.username, + + 'ctl00$TOPCONTENEDOR$WebCUI_def_user': userid, + 'ctl00$TOPCONTENEDOR$WebCUI_loginid': userid, + 'ctl00$TOPCONTENEDOR$WebCUI_recid': userid, + + 'ctl00$TOPCONTENEDOR$WebCUI_lista_f': 'SAL', + 'ctl00$TOPCONTENEDOR$WebCUI_lista_sit': 'TODO', + 'ctl00$TOPCONTENEDOR$WebCUI_ref': refcte, + 'ctl00$TOPCONTENEDOR$WebCUI_lista_tenv': 'TODO', + 'ctl00$TOPCONTENEDOR$WebCUI_consulta': '1', + 'ctl00$TOPCONTENEDOR$WebCUI_delDest': '000', + 'ctl00$TOPCONTENEDOR$WebCUI_lista_grp': 'TODOS', + 'ctl00$TOPCONTENEDOR$WebCUI_regxpag': '20', + 'ctl00$TOPCONTENEDOR$WebCUI_totalpag': '1', + 'ctl00$TOPCONTENEDOR$WebCUI_pers_cons': 'S', + 'ctl00$TOPCONTENEDOR$WebCUI_sit_inc': 'N', + 'ctl00$TOPCONTENEDOR$WebCUI_total_cons_gr1': '1', + 'ctl00$TOPCONTENEDOR$WebCUI_def_cons': '1', + 'ctl00$TOPCONTENEDOR$WebCUI_solocte': 'S', + 'ctl00$TOPCONTENEDOR$WebCUI_diascons': '62', + 'ctl00$TOPCONTENEDOR$WebCUI_buscar': 'Buscar', + } + form_data.update(self.viewstate) + res = self.session.post(url, data=form_data, headers=self.headers) + self._update_viewstate(res) + + # parse the page + tree = etree.HTML(res.text.encode()) + elem_table = xpath1(tree, "//div[@id='CONSENV_RES']//table") + + result_ld = [] + input_detail_l = elem_table.xpath("//tr/td/input[contains(@onclick, 'MuestraDetalleConsulta')]") + for input in input_detail_l: + m = re.match("^.+MuestraDetalleConsulta\('([^']+)'\)", input.attrib['onclick']) + if not m: + raise Exception("Unexpected content on MuestraDetalleConsulta") + nexpedicion = m.group(1) + + ### detalle expedicion + url = '%s/api/Comun/DetalleEnvio/QueryDatosExpedicion' % self._base_url + form_data = { + 'expedicion': nexpedicion, + 'propietario': 'null', + } + res = self.session.post(url, data=form_data, headers=self.headers) + data = res.json()['data'] + expedition_d = data + + ### tracking + url = '%s/api/Comun/DetalleEnvio/QueryTracking' % self._base_url + form_data = { + 'jtStartIndex': '1', + 'jtPageSize': '1', + 'jtSorting': None, + 'expedicion': nexpedicion, + 'propietario': 'null', + 'usuario': self.username, + } + res = self.session.post(url, data=form_data, headers=self.headers) + data = json.loads(res.json()['data'])['Table'] + for e in data: + e['FECHA'] = datetime.datetime.strptime(e['FECHA'], '%Y-%m-%dT%H:%M:%S') + expedition_d['Tracking'] = data + + result_ld.append(expedition_d) + + return result_ld + + def logout(self): + url = '%s/Default.aspx' % self._base_url + + res = self.session.get(url, headers=self.headers) + self._update_viewstate(res) + + form_data = { + '__EVENTTARGET': 'ctl00$LoginStatus1$ctl00', + '__EVENTARGUMENT': '', + 'ctl00$jquerylang': 'es', + 'ctl00$eschromeglobalactivo': 'S', + 'ctl00$eschromebrowser': 'S', + 'ctl00$chromeversion': '78', + 'ctl00$NoJavaPrint': 'N', + } + form_data.update(self.viewstate) + res = self.session.post(url, data=form_data, headers=self.headers) + + return self.is_login_page(res) + + def is_login_page(self, res): + tree = etree.HTML(res.text.encode()) + tag_form_login = tree.xpath("//form[@id='frmlogin']") + + return len(tag_form_login) != 0 + + def _update_viewstate(self, res): + self.viewstate = {} + tree = etree.HTML(res.text.encode()) + viewstate_fields = [ + '__VIEWSTATE', + '__VIEWSTATEGENERATOR', + '__VIEWSTATEENCRYPTED', + '__EVENTVALIDATION', + ] + for vsf in viewstate_fields: + tag_viewstate = tree.xpath("//*[@id='%s']" % vsf) + for t in tag_viewstate: + if 'value' not in t.attrib: + raise Exception("Unexpected, ViewState element iff found must have value attribute") + if vsf not in self.viewstate: + self.viewstate[vsf] = t.attrib['value'] or None + else: + if self.viewstate[vsf] != t.attrib['value']: + raise Exception("Unexpected! all the ViewState must have the same 'value'") + + if not self.viewstate: + raise Exception("ViewState not found") diff --git a/connector_cbl/security/ir.model.access.csv b/connector_cbl/security/ir.model.access.csv new file mode 100644 index 000000000..db7b1d844 --- /dev/null +++ b/connector_cbl/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 +"access_cbl_backend_manager","cbl_backend connector manager","model_cbl_backend","connector.group_connector_manager",1,1,1,1 diff --git a/connector_cbl/static/description/icon.png b/connector_cbl/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1cd641e792c30455187ca30940bc0f329ce8bbb0 GIT binary patch literal 6342 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 literal 0 HcmV?d00001 diff --git a/connector_cbl/templates/cbl_template.xml b/connector_cbl/templates/cbl_template.xml new file mode 100644 index 000000000..505fd0d81 --- /dev/null +++ b/connector_cbl/templates/cbl_template.xml @@ -0,0 +1,147 @@ + + + + + \ No newline at end of file diff --git a/connector_cbl/views/backend_views.xml b/connector_cbl/views/backend_views.xml new file mode 100644 index 000000000..add1756d4 --- /dev/null +++ b/connector_cbl/views/backend_views.xml @@ -0,0 +1,60 @@ + + + + + + cbl.backend.form + cbl.backend + +
+
+
+ +
+ +
+

+ +

+ + + + + + + +
+
+
+
+ + + cbl.backend.tree + cbl.backend + + + + + + + + + + + CBL Backends + cbl.backend + form + tree,form + +
diff --git a/connector_cbl/views/menus.xml b/connector_cbl/views/menus.xml new file mode 100644 index 000000000..5359ff132 --- /dev/null +++ b/connector_cbl/views/menus.xml @@ -0,0 +1,11 @@ + + + + + + + From a1f55a0c6f29d9f2e57b26af32fd5bf06920bd7c Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Thu, 19 Dec 2019 00:39:30 +0100 Subject: [PATCH 2/6] [IMP] connector_cbl: remove title --- connector_cbl/__manifest__.py | 2 +- connector_cbl/templates/cbl_template.xml | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/connector_cbl/__manifest__.py b/connector_cbl/__manifest__.py index d107dffd3..49c464095 100644 --- a/connector_cbl/__manifest__.py +++ b/connector_cbl/__manifest__.py @@ -5,7 +5,7 @@ { 'name': "CBL Connector", 'description': "Connector to get tracking info from CBL", - 'version': '11.0.0.1.0', + 'version': '11.0.0.1.1', 'author': 'NuoBiT Solutions, S.L., Eric Antones', 'license': 'AGPL-3', 'category': 'Connector', diff --git a/connector_cbl/templates/cbl_template.xml b/connector_cbl/templates/cbl_template.xml index 505fd0d81..d08e7c0d0 100644 --- a/connector_cbl/templates/cbl_template.xml +++ b/connector_cbl/templates/cbl_template.xml @@ -44,9 +44,6 @@

-

-

Detalles

-

From c438c2dfe6de206b4efc8a2863f4eb7bb91e29e6 Mon Sep 17 00:00:00 2001 From: Eric Antones Date: Thu, 19 Dec 2019 00:53:54 +0100 Subject: [PATCH 3/6] [FIX] connector_cbl: not found not dealt with --- connector_cbl/__manifest__.py | 2 +- connector_cbl/controllers/main.py | 10 +++++++--- connector_cbl/models/cbl.py | 7 ++++++- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/connector_cbl/__manifest__.py b/connector_cbl/__manifest__.py index 49c464095..c4d07c86a 100644 --- a/connector_cbl/__manifest__.py +++ b/connector_cbl/__manifest__.py @@ -5,7 +5,7 @@ { 'name': "CBL Connector", 'description': "Connector to get tracking info from CBL", - 'version': '11.0.0.1.1', + 'version': '11.0.0.1.2', 'author': 'NuoBiT Solutions, S.L., Eric Antones', 'license': 'AGPL-3', 'category': 'Connector', diff --git a/connector_cbl/controllers/main.py b/connector_cbl/controllers/main.py index 8db6a615c..8eb705192 100644 --- a/connector_cbl/controllers/main.py +++ b/connector_cbl/controllers/main.py @@ -19,17 +19,21 @@ def tracking_data(self, tracking_number=None): ]).sorted(lambda x: x.sequence) if not cbl_backend: - raise werkzeug.exceptions.InternalServerError("No configuration found") + return werkzeug.exceptions.InternalServerError("No configuration found") er = CBL(username=cbl_backend.username, password=cbl_backend.password) _logger.info("Asking %s CBL shipment... from %s" % (tracking_number, remote_ip)) if not er.login(): - raise werkzeug.exceptions.Unauthorized() + return werkzeug.exceptions.Unauthorized() + data = er.filter_by_refcte(tracking_number) + if not data: + return werkzeug.exceptions.NotFound("There's no data with tracking number '%s'" % tracking_number) + # if not er.logout(): - # raise werkzeug.exceptions.InternalServerError("Logout not successful") + # return werkzeug.exceptions.InternalServerError("Logout not successful") _logger.info("CBL shipment %s successfully retrieved from %s." % (tracking_number, remote_ip)) return http.request.render('connector_cbl.index', {'expeditions': data}) diff --git a/connector_cbl/models/cbl.py b/connector_cbl/models/cbl.py index 5a65bdea2..09484d336 100644 --- a/connector_cbl/models/cbl.py +++ b/connector_cbl/models/cbl.py @@ -115,7 +115,12 @@ def filter_by_refcte(self, refcte): # parse the page tree = etree.HTML(res.text.encode()) - elem_table = xpath1(tree, "//div[@id='CONSENV_RES']//table") + elem_table_l = tree.xpath("//div[@id='CONSENV_RES']//table") + if len(elem_table_l) == 0: + return [] + elif len(elem_table_l) > 1: + raise Exception("Unexpected content CONSENV_RES") + elem_table = elem_table_l[0] result_ld = [] input_detail_l = elem_table.xpath("//tr/td/input[contains(@onclick, 'MuestraDetalleConsulta')]") From da7ccece7b871724d46ec22a405fff38fcca6033 Mon Sep 17 00:00:00 2001 From: KNVx Date: Fri, 19 Jan 2024 10:11:42 +0100 Subject: [PATCH 4/6] [MIG] connector_cbl: pre-commit stuff --- connector_cbl/__manifest__.py | 40 ++--- connector_cbl/controllers/main.py | 47 ++++-- connector_cbl/models/backend.py | 45 +++-- connector_cbl/models/cbl.py | 157 +++++++++--------- connector_cbl/templates/cbl_template.xml | 139 +++++++++++----- connector_cbl/views/backend_views.xml | 61 +++++-- connector_cbl/views/menus.xml | 19 ++- setup/connector_cbl/odoo/addons/connector_cbl | 1 + setup/connector_cbl/setup.cfg | 2 + setup/connector_cbl/setup.py | 6 + 10 files changed, 317 insertions(+), 200 deletions(-) create mode 120000 setup/connector_cbl/odoo/addons/connector_cbl create mode 100644 setup/connector_cbl/setup.cfg create mode 100644 setup/connector_cbl/setup.py diff --git a/connector_cbl/__manifest__.py b/connector_cbl/__manifest__.py index c4d07c86a..c464fd83f 100644 --- a/connector_cbl/__manifest__.py +++ b/connector_cbl/__manifest__.py @@ -3,28 +3,28 @@ # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) { - 'name': "CBL Connector", - 'description': "Connector to get tracking info from CBL", - 'version': '11.0.0.1.2', - 'author': 'NuoBiT Solutions, S.L., Eric Antones', - 'license': 'AGPL-3', - 'category': 'Connector', - 'website': 'https://github.com/nuobit', - 'external_dependencies': { - 'python': [ - 'requests', - 'lxml', - 'json', + "name": "CBL Connector", + "description": "Connector to get tracking info from CBL", + "version": "11.0.0.1.2", + "author": "NuoBiT Solutions, S.L., Eric Antones", + "license": "AGPL-3", + "category": "Connector", + "website": "https://github.com/nuobit/odoo-addons", + "external_dependencies": { + "python": [ + "requests", + "lxml", + "json", ], }, - 'depends': [ - 'connector', + "depends": [ + "connector", ], - 'data': [ - 'views/backend_views.xml', - 'views/menus.xml', - 'templates/cbl_template.xml', - 'security/ir.model.access.csv', + "data": [ + "views/backend_views.xml", + "views/menus.xml", + "templates/cbl_template.xml", + "security/ir.model.access.csv", ], - 'installable': True, + "installable": True, } diff --git a/connector_cbl/controllers/main.py b/connector_cbl/controllers/main.py index 8eb705192..2ef8353b7 100644 --- a/connector_cbl/controllers/main.py +++ b/connector_cbl/controllers/main.py @@ -1,28 +1,40 @@ -from odoo import http +import logging -from odoo.addons.connector_cbl.models.cbl import CBL import werkzeug.exceptions -import logging +from odoo import http + +from odoo.addons.connector_cbl.models.cbl import CBL _logger = logging.getLogger(__name__) class CBLController(http.Controller): - @http.route(['/tracking/cbl/', - ], type='http', auth="public") + @http.route( + [ + "/tracking/cbl/", + ], + type="http", + auth="public", + ) def tracking_data(self, tracking_number=None): - remote_ip = http.request.httprequest.environ['REMOTE_ADDR'] - cbl_backend = http.request.env['cbl.backend'].sudo().search([ - ('active', '=', True), - ('state', '=', 'checked'), - ]).sorted(lambda x: x.sequence) + remote_ip = http.request.httprequest.environ["REMOTE_ADDR"] + cbl_backend = ( + http.request.env["cbl.backend"] + .sudo() + .search( + [ + ("active", "=", True), + ("state", "=", "checked"), + ] + ) + .sorted(lambda x: x.sequence) + ) if not cbl_backend: return werkzeug.exceptions.InternalServerError("No configuration found") - er = CBL(username=cbl_backend.username, - password=cbl_backend.password) + er = CBL(username=cbl_backend.username, password=cbl_backend.password) _logger.info("Asking %s CBL shipment... from %s" % (tracking_number, remote_ip)) if not er.login(): @@ -30,10 +42,15 @@ def tracking_data(self, tracking_number=None): data = er.filter_by_refcte(tracking_number) if not data: - return werkzeug.exceptions.NotFound("There's no data with tracking number '%s'" % tracking_number) + return werkzeug.exceptions.NotFound( + "There's no data with tracking number '%s'" % tracking_number + ) # if not er.logout(): # return werkzeug.exceptions.InternalServerError("Logout not successful") - _logger.info("CBL shipment %s successfully retrieved from %s." % (tracking_number, remote_ip)) + _logger.info( + "CBL shipment %s successfully retrieved from %s." + % (tracking_number, remote_ip) + ) - return http.request.render('connector_cbl.index', {'expeditions': data}) + return http.request.render("connector_cbl.index", {"expeditions": data}) diff --git a/connector_cbl/models/backend.py b/connector_cbl/models/backend.py index ed7a72788..9af12674d 100644 --- a/connector_cbl/models/backend.py +++ b/connector_cbl/models/backend.py @@ -2,50 +2,45 @@ # Eric Antones # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -from odoo import models, fields, api, exceptions, _ +import logging -from . import cbl +from odoo import api, exceptions, fields, models -import logging +from . import cbl _logger = logging.getLogger(__name__) class CBLBackend(models.Model): - _name = 'cbl.backend' - _inherit = 'connector.backend' + _name = "cbl.backend" + _inherit = "connector.backend" - _description = 'CBL Backend Configuration' + _description = "CBL Backend Configuration" @api.model def _select_state(self): - return [('draft', 'Draft'), - ('checked', 'Checked'), - ('production', 'In Production')] + return [ + ("draft", "Draft"), + ("checked", "Checked"), + ("production", "In Production"), + ] - name = fields.Char('Name', required=True) + name = fields.Char("Name", required=True) - sequence = fields.Integer('Sequence', required=True, default=1) + sequence = fields.Integer("Sequence", required=True, default=1) - username = fields.Char('Username', required=True) - password = fields.Char('Password', required=True) + username = fields.Char("Username", required=True) + password = fields.Char("Password", required=True) - output = fields.Text('Output', readonly=True) + output = fields.Text("Output", readonly=True) - active = fields.Boolean( - string='Active', - default=True - ) - state = fields.Selection( - selection='_select_state', - string='State', - default='draft' - ) + active = fields.Boolean(string="Active", default=True) + state = fields.Selection(selection="_select_state", string="State", default="draft") @api.multi def button_reset_to_draft(self): self.ensure_one() - self.write({'state': 'draft', 'output': None}) + self.write({"state": "draft", "output": None}) @api.multi def _check_connection(self): @@ -63,4 +58,4 @@ def _check_connection(self): @api.multi def button_check_connection(self): self._check_connection() - self.write({'state': 'checked'}) + self.write({"state": "checked"}) diff --git a/connector_cbl/models/cbl.py b/connector_cbl/models/cbl.py index 09484d336..616f1cd38 100644 --- a/connector_cbl/models/cbl.py +++ b/connector_cbl/models/cbl.py @@ -2,15 +2,13 @@ # Eric Antones # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) -import requests - import datetime +import json +import logging import re +import requests from lxml import etree -import json - -import logging _logger = logging.getLogger(__name__) @@ -24,7 +22,7 @@ def xpath1(tree, xpath): class CBL: - _base_url = 'https://clientes.cbl-logistica.com' + _base_url = "https://clientes.cbl-logistica.com" def __init__(self, username, password, debug=False): self.session = requests.Session() @@ -32,12 +30,12 @@ def __init__(self, username, password, debug=False): self.username = username self.password = password self.headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36', + "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/78.0.3904.108 Safari/537.36", } self.debug = debug def login(self): - url = '%s/login.aspx' % self._base_url + url = "%s/login.aspx" % self._base_url ## obtenim les dades del viewstate res = self.session.get(url, headers=self.headers) @@ -45,10 +43,10 @@ def login(self): ## fem el login form_data = { - 'ScriptManager1': 'UpdatePanel1|Login1$LoginButton', - 'Login1$UserName': self.username, - 'Login1$Password': self.password, - 'Login1$LoginButton': 'ENTRAR', + "ScriptManager1": "UpdatePanel1|Login1$LoginButton", + "Login1$UserName": self.username, + "Login1$Password": self.password, + "Login1$LoginButton": "ENTRAR", } form_data.update(self.viewstate) res = self.session.post(url, data=form_data, headers=self.headers) @@ -58,14 +56,16 @@ def login(self): return not self.is_login_page(res) def filter_by_refcte(self, refcte): - url = '%s/Consultas/envios.aspx' % self._base_url + url = "%s/Consultas/envios.aspx" % self._base_url res = self.session.get(url, headers=self.headers) self._update_viewstate(res) - userid_tags = ['ctl00_TOPCONTENEDOR_WebCUI_def_user', - 'ctl00_TOPCONTENEDOR_WebCUI_loginid', - 'ctl00_TOPCONTENEDOR_WebCUI_recid'] + userid_tags = [ + "ctl00_TOPCONTENEDOR_WebCUI_def_user", + "ctl00_TOPCONTENEDOR_WebCUI_loginid", + "ctl00_TOPCONTENEDOR_WebCUI_recid", + ] tree = etree.HTML(res.text.encode()) userid = None @@ -74,40 +74,37 @@ def filter_by_refcte(self, refcte): if ut_tag is None: raise Exception("Expected value") if not userid: - userid = ut_tag.attrib['value'] + userid = ut_tag.attrib["value"] else: - if userid != ut_tag.attrib['value']: + if userid != ut_tag.attrib["value"]: raise Exception("Diferent userid values") if not userid: raise Exception("userid not found") form_data = { - 'ctl00$AJAXScriptManager': 'ctl00$UpdatePanel1|ctl00$TOPCONTENEDOR$WebCUI_buscar', - - 'ctl00$TOPCONTENEDOR$WebCUI_del': '081', - 'ctl00$TOPCONTENEDOR$WebCUI_usuario': self.username, - - 'ctl00$TOPCONTENEDOR$WebCUI_def_user': userid, - 'ctl00$TOPCONTENEDOR$WebCUI_loginid': userid, - 'ctl00$TOPCONTENEDOR$WebCUI_recid': userid, - - 'ctl00$TOPCONTENEDOR$WebCUI_lista_f': 'SAL', - 'ctl00$TOPCONTENEDOR$WebCUI_lista_sit': 'TODO', - 'ctl00$TOPCONTENEDOR$WebCUI_ref': refcte, - 'ctl00$TOPCONTENEDOR$WebCUI_lista_tenv': 'TODO', - 'ctl00$TOPCONTENEDOR$WebCUI_consulta': '1', - 'ctl00$TOPCONTENEDOR$WebCUI_delDest': '000', - 'ctl00$TOPCONTENEDOR$WebCUI_lista_grp': 'TODOS', - 'ctl00$TOPCONTENEDOR$WebCUI_regxpag': '20', - 'ctl00$TOPCONTENEDOR$WebCUI_totalpag': '1', - 'ctl00$TOPCONTENEDOR$WebCUI_pers_cons': 'S', - 'ctl00$TOPCONTENEDOR$WebCUI_sit_inc': 'N', - 'ctl00$TOPCONTENEDOR$WebCUI_total_cons_gr1': '1', - 'ctl00$TOPCONTENEDOR$WebCUI_def_cons': '1', - 'ctl00$TOPCONTENEDOR$WebCUI_solocte': 'S', - 'ctl00$TOPCONTENEDOR$WebCUI_diascons': '62', - 'ctl00$TOPCONTENEDOR$WebCUI_buscar': 'Buscar', + "ctl00$AJAXScriptManager": "ctl00$UpdatePanel1|ctl00$TOPCONTENEDOR$WebCUI_buscar", + "ctl00$TOPCONTENEDOR$WebCUI_del": "081", + "ctl00$TOPCONTENEDOR$WebCUI_usuario": self.username, + "ctl00$TOPCONTENEDOR$WebCUI_def_user": userid, + "ctl00$TOPCONTENEDOR$WebCUI_loginid": userid, + "ctl00$TOPCONTENEDOR$WebCUI_recid": userid, + "ctl00$TOPCONTENEDOR$WebCUI_lista_f": "SAL", + "ctl00$TOPCONTENEDOR$WebCUI_lista_sit": "TODO", + "ctl00$TOPCONTENEDOR$WebCUI_ref": refcte, + "ctl00$TOPCONTENEDOR$WebCUI_lista_tenv": "TODO", + "ctl00$TOPCONTENEDOR$WebCUI_consulta": "1", + "ctl00$TOPCONTENEDOR$WebCUI_delDest": "000", + "ctl00$TOPCONTENEDOR$WebCUI_lista_grp": "TODOS", + "ctl00$TOPCONTENEDOR$WebCUI_regxpag": "20", + "ctl00$TOPCONTENEDOR$WebCUI_totalpag": "1", + "ctl00$TOPCONTENEDOR$WebCUI_pers_cons": "S", + "ctl00$TOPCONTENEDOR$WebCUI_sit_inc": "N", + "ctl00$TOPCONTENEDOR$WebCUI_total_cons_gr1": "1", + "ctl00$TOPCONTENEDOR$WebCUI_def_cons": "1", + "ctl00$TOPCONTENEDOR$WebCUI_solocte": "S", + "ctl00$TOPCONTENEDOR$WebCUI_diascons": "62", + "ctl00$TOPCONTENEDOR$WebCUI_buscar": "Buscar", } form_data.update(self.viewstate) res = self.session.post(url, data=form_data, headers=self.headers) @@ -123,57 +120,61 @@ def filter_by_refcte(self, refcte): elem_table = elem_table_l[0] result_ld = [] - input_detail_l = elem_table.xpath("//tr/td/input[contains(@onclick, 'MuestraDetalleConsulta')]") + input_detail_l = elem_table.xpath( + "//tr/td/input[contains(@onclick, 'MuestraDetalleConsulta')]" + ) for input in input_detail_l: - m = re.match("^.+MuestraDetalleConsulta\('([^']+)'\)", input.attrib['onclick']) + m = re.match( + r"^.+MuestraDetalleConsulta\('([^']+)'\)", input.attrib["onclick"] + ) if not m: raise Exception("Unexpected content on MuestraDetalleConsulta") nexpedicion = m.group(1) ### detalle expedicion - url = '%s/api/Comun/DetalleEnvio/QueryDatosExpedicion' % self._base_url + url = "%s/api/Comun/DetalleEnvio/QueryDatosExpedicion" % self._base_url form_data = { - 'expedicion': nexpedicion, - 'propietario': 'null', + "expedicion": nexpedicion, + "propietario": "null", } res = self.session.post(url, data=form_data, headers=self.headers) - data = res.json()['data'] + data = res.json()["data"] expedition_d = data ### tracking - url = '%s/api/Comun/DetalleEnvio/QueryTracking' % self._base_url + url = "%s/api/Comun/DetalleEnvio/QueryTracking" % self._base_url form_data = { - 'jtStartIndex': '1', - 'jtPageSize': '1', - 'jtSorting': None, - 'expedicion': nexpedicion, - 'propietario': 'null', - 'usuario': self.username, + "jtStartIndex": "1", + "jtPageSize": "1", + "jtSorting": None, + "expedicion": nexpedicion, + "propietario": "null", + "usuario": self.username, } res = self.session.post(url, data=form_data, headers=self.headers) - data = json.loads(res.json()['data'])['Table'] + data = json.loads(res.json()["data"])["Table"] for e in data: - e['FECHA'] = datetime.datetime.strptime(e['FECHA'], '%Y-%m-%dT%H:%M:%S') - expedition_d['Tracking'] = data + e["FECHA"] = datetime.datetime.strptime(e["FECHA"], "%Y-%m-%dT%H:%M:%S") + expedition_d["Tracking"] = data result_ld.append(expedition_d) return result_ld def logout(self): - url = '%s/Default.aspx' % self._base_url + url = "%s/Default.aspx" % self._base_url res = self.session.get(url, headers=self.headers) self._update_viewstate(res) form_data = { - '__EVENTTARGET': 'ctl00$LoginStatus1$ctl00', - '__EVENTARGUMENT': '', - 'ctl00$jquerylang': 'es', - 'ctl00$eschromeglobalactivo': 'S', - 'ctl00$eschromebrowser': 'S', - 'ctl00$chromeversion': '78', - 'ctl00$NoJavaPrint': 'N', + "__EVENTTARGET": "ctl00$LoginStatus1$ctl00", + "__EVENTARGUMENT": "", + "ctl00$jquerylang": "es", + "ctl00$eschromeglobalactivo": "S", + "ctl00$eschromebrowser": "S", + "ctl00$chromeversion": "78", + "ctl00$NoJavaPrint": "N", } form_data.update(self.viewstate) res = self.session.post(url, data=form_data, headers=self.headers) @@ -190,21 +191,25 @@ def _update_viewstate(self, res): self.viewstate = {} tree = etree.HTML(res.text.encode()) viewstate_fields = [ - '__VIEWSTATE', - '__VIEWSTATEGENERATOR', - '__VIEWSTATEENCRYPTED', - '__EVENTVALIDATION', + "__VIEWSTATE", + "__VIEWSTATEGENERATOR", + "__VIEWSTATEENCRYPTED", + "__EVENTVALIDATION", ] for vsf in viewstate_fields: tag_viewstate = tree.xpath("//*[@id='%s']" % vsf) for t in tag_viewstate: - if 'value' not in t.attrib: - raise Exception("Unexpected, ViewState element iff found must have value attribute") + if "value" not in t.attrib: + raise Exception( + "Unexpected, ViewState element iff found must have value attribute" + ) if vsf not in self.viewstate: - self.viewstate[vsf] = t.attrib['value'] or None + self.viewstate[vsf] = t.attrib["value"] or None else: - if self.viewstate[vsf] != t.attrib['value']: - raise Exception("Unexpected! all the ViewState must have the same 'value'") + if self.viewstate[vsf] != t.attrib["value"]: + raise Exception( + "Unexpected! all the ViewState must have the same 'value'" + ) if not self.viewstate: raise Exception("ViewState not found") diff --git a/connector_cbl/templates/cbl_template.xml b/connector_cbl/templates/cbl_template.xml index d08e7c0d0..34e9e2595 100644 --- a/connector_cbl/templates/cbl_template.xml +++ b/connector_cbl/templates/cbl_template.xml @@ -1,4 +1,4 @@ - + @@ -8,13 +8,19 @@ - - + + - + CBL - - - +