From 1f96a908ac2858873f6c0ac90b6ed4c1283fa1b9 Mon Sep 17 00:00:00 2001 From: OscarIndias Date: Mon, 5 Jun 2023 17:53:36 +0200 Subject: [PATCH] [ADD] delivery_deliverea: This module allows integration with deliverea --- delivery_deliverea/README.rst | 151 ++ delivery_deliverea/__init__.py | 2 + delivery_deliverea/__manifest__.py | 31 + delivery_deliverea/controllers/__init__.py | 2 + delivery_deliverea/controllers/http.py | 27 + delivery_deliverea/controllers/main.py | 32 + .../data/deliverea_states_data.xml | 198 ++ delivery_deliverea/data/ir_cron.xml | 17 + .../data/product_packaging_data.xml | 12 + delivery_deliverea/data/queue_job_data.xml | 18 + delivery_deliverea/i18n/es.po | 623 +++++ delivery_deliverea/models/__init__.py | 9 + .../models/carrier_deliverea.py | 19 + .../models/carrier_deliverea_parameter.py | 18 + .../models/carrier_deliverea_service.py | 20 + .../models/deliverea_distribution_center.py | 21 + .../models/deliverea_request.py | 188 ++ delivery_deliverea/models/deliverea_state.py | 21 + delivery_deliverea/models/delivery_carrier.py | 554 +++++ .../models/stock_package_type.py | 9 + delivery_deliverea/models/stock_picking.py | 58 + delivery_deliverea/readme/CONFIGURE.rst | 20 + delivery_deliverea/readme/CONTRIBUTORS.rst | 6 + delivery_deliverea/readme/CREDITS.rst | 0 delivery_deliverea/readme/DESCRIPTION.rst | 1 + delivery_deliverea/readme/HISTORY.rst | 4 + delivery_deliverea/readme/INSTALL.rst | 8 + delivery_deliverea/readme/ROADMAP.rst | 1 + delivery_deliverea/readme/USAGE.rst | 17 + .../security/ir.model.access.csv | 11 + .../static/description/icon.png | Bin 0 -> 10390 bytes .../static/description/index.html | 507 +++++ delivery_deliverea/tests/__init__.py | 1 + .../tests/carrier_labels_data.py | 19 + .../tests/carrier_services_data.py | 2008 +++++++++++++++++ .../tests/test_delivery_deliverea.py | 430 ++++ delivery_deliverea/tests/test_webhook.py | 50 + delivery_deliverea/tests/tools.py | 103 + .../deliverea_distribution_center_views.xml | 40 + .../views/deliverea_state_mapping.xml | 29 + .../views/delivery_carrier_views.xml | 121 + .../views/stock_picking_views.xml | 32 + requirements.txt | 1 + .../odoo/addons/delivery_deliverea | 1 + setup/delivery_deliverea/setup.py | 6 + 45 files changed, 5446 insertions(+) create mode 100644 delivery_deliverea/README.rst create mode 100644 delivery_deliverea/__init__.py create mode 100644 delivery_deliverea/__manifest__.py create mode 100644 delivery_deliverea/controllers/__init__.py create mode 100644 delivery_deliverea/controllers/http.py create mode 100644 delivery_deliverea/controllers/main.py create mode 100644 delivery_deliverea/data/deliverea_states_data.xml create mode 100644 delivery_deliverea/data/ir_cron.xml create mode 100644 delivery_deliverea/data/product_packaging_data.xml create mode 100644 delivery_deliverea/data/queue_job_data.xml create mode 100644 delivery_deliverea/i18n/es.po create mode 100644 delivery_deliverea/models/__init__.py create mode 100644 delivery_deliverea/models/carrier_deliverea.py create mode 100644 delivery_deliverea/models/carrier_deliverea_parameter.py create mode 100644 delivery_deliverea/models/carrier_deliverea_service.py create mode 100644 delivery_deliverea/models/deliverea_distribution_center.py create mode 100644 delivery_deliverea/models/deliverea_request.py create mode 100644 delivery_deliverea/models/deliverea_state.py create mode 100644 delivery_deliverea/models/delivery_carrier.py create mode 100644 delivery_deliverea/models/stock_package_type.py create mode 100644 delivery_deliverea/models/stock_picking.py create mode 100644 delivery_deliverea/readme/CONFIGURE.rst create mode 100644 delivery_deliverea/readme/CONTRIBUTORS.rst create mode 100644 delivery_deliverea/readme/CREDITS.rst create mode 100644 delivery_deliverea/readme/DESCRIPTION.rst create mode 100644 delivery_deliverea/readme/HISTORY.rst create mode 100644 delivery_deliverea/readme/INSTALL.rst create mode 100644 delivery_deliverea/readme/ROADMAP.rst create mode 100644 delivery_deliverea/readme/USAGE.rst create mode 100644 delivery_deliverea/security/ir.model.access.csv create mode 100644 delivery_deliverea/static/description/icon.png create mode 100644 delivery_deliverea/static/description/index.html create mode 100644 delivery_deliverea/tests/__init__.py create mode 100644 delivery_deliverea/tests/carrier_labels_data.py create mode 100644 delivery_deliverea/tests/carrier_services_data.py create mode 100644 delivery_deliverea/tests/test_delivery_deliverea.py create mode 100644 delivery_deliverea/tests/test_webhook.py create mode 100644 delivery_deliverea/tests/tools.py create mode 100644 delivery_deliverea/views/deliverea_distribution_center_views.xml create mode 100644 delivery_deliverea/views/deliverea_state_mapping.xml create mode 100644 delivery_deliverea/views/delivery_carrier_views.xml create mode 100644 delivery_deliverea/views/stock_picking_views.xml create mode 120000 setup/delivery_deliverea/odoo/addons/delivery_deliverea create mode 100644 setup/delivery_deliverea/setup.py diff --git a/delivery_deliverea/README.rst b/delivery_deliverea/README.rst new file mode 100644 index 0000000000..fa44a2cea4 --- /dev/null +++ b/delivery_deliverea/README.rst @@ -0,0 +1,151 @@ +================== +Delivery Deliverea +================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:c394bcf2eb7b913e035898f5095ee5a394ebb9ca965716169fe1aa691f69cd26 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |badge1| image:: https://img.shields.io/badge/maturity-Beta-yellow.png + :target: https://odoo-community.org/page/development-status + :alt: Beta +.. |badge2| image:: https://img.shields.io/badge/licence-AGPL--3-blue.png + :target: http://www.gnu.org/licenses/agpl-3.0-standalone.html + :alt: License: AGPL-3 +.. |badge3| image:: https://img.shields.io/badge/github-OCA%2Fdelivery--carrier-lightgray.png?logo=github + :target: https://github.com/OCA/delivery-carrier/tree/16.0/delivery_deliverea + :alt: OCA/delivery-carrier +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/delivery-carrier-16-0/delivery-carrier-16-0-delivery_deliverea + :alt: Translate me on Weblate +.. |badge5| image:: https://img.shields.io/badge/runboat-Try%20me-875A7B.png + :target: https://runboat.odoo-community.org/builds?repo=OCA/delivery-carrier&target_branch=16.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Deliverea API intration with Odoo. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +This module needs the `unidecode` python library. + +It depends on the modules: + +* `base_delivery_carrier_label `_ repositorio OCA/delivery-carrier +* `delivery_package_number `_ repositorio OCA/delivery-carrier +* `delivery_state `_ repositorio OCA/delivery-carrier +* `queue_job `_ repositorio OCA/queue + +Configuration +============= + +To configure your Deliverea services, go to: + +- *Inventory > Configuration > Delivery methods* and create a new one. +- Choose *Deliverea* as provider. +- Configure your Deliverea credentials: user and password. +- You can configure the next fields: + * Deliverea production environment URL + * Deliverea test environment URL + * Deliverea tracking URL + * Deliverea Distribution Center + * Deliverea carrier + * Deliverea carrier service + * Deliverea articles description + * Select note field + * Notify by sms + * Notify by email + * Hide sender + * Return label + * Return proof delivery + * Saturday delivery + +Usage +===== + +Shipping codes are generated when a stock pick is validated whose carrier is configured +for Deliverea as a supplier is validated. +for Deliverea as a supplier is validated. The label is generated and attached to the +the document according to the previously configured label generation parameters. + +In case you accidentally delete the attached label, you can generate it again by +clicking on the *Deliverea button. On the *Deliverea Label* button at the top of the +picking form. + +In order to select the Deliverea distribution center you have to click on the Import +distribution centers button, which will import all available distribution centers. + +In order to select the type of carrier and the type of service to be used for this +shipping method, click on the Import carriers and services button. + +Once the distribution centers and services are imported, we can select them in the +fields. + +Known issues / Roadmap +====================== + +* No specified roadmap and no known problems in operation. + +Changelog +========= + +16.0.1.0.0 (2023-06-07) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history + +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 to smash it by providing a detailed and welcomed +`feedback `_. + +Do not contact contributors directly about support or help with technical issues. + +Credits +======= + +Authors +~~~~~~~ + +* FactorLibre + +Contributors +~~~~~~~~~~~~ + +* `FactorLibre `_: + + * Jorge Martínez + * Zahra Velasco + * Nacho Morales + * Oscar Indias + +Maintainers +~~~~~~~~~~~ + +This module is maintained by the OCA. + +.. image:: https://odoo-community.org/logo.png + :alt: Odoo Community Association + :target: https://odoo-community.org + +OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use. + +This module is part of the `OCA/delivery-carrier `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/delivery_deliverea/__init__.py b/delivery_deliverea/__init__.py new file mode 100644 index 0000000000..91c5580fed --- /dev/null +++ b/delivery_deliverea/__init__.py @@ -0,0 +1,2 @@ +from . import controllers +from . import models diff --git a/delivery_deliverea/__manifest__.py b/delivery_deliverea/__manifest__.py new file mode 100644 index 0000000000..ba80a8a967 --- /dev/null +++ b/delivery_deliverea/__manifest__.py @@ -0,0 +1,31 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl) +{ + "name": "Delivery Deliverea", + "summary": "Delivery Carrier implementation for Deliverea using their API", + "version": "16.0.1.0.0", + "category": "Stock", + "website": "https://github.com/OCA/delivery-carrier", + "author": "FactorLibre, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": [ + "base_delivery_carrier_label", + "delivery_state", + "queue_job", + "delivery_package_number", + ], + "external_dependencies": {"python": ["unidecode"]}, + "data": [ + "data/deliverea_states_data.xml", + "data/product_packaging_data.xml", + "data/ir_cron.xml", + "data/queue_job_data.xml", + "security/ir.model.access.csv", + "views/deliverea_distribution_center_views.xml", + "views/deliverea_state_mapping.xml", + "views/delivery_carrier_views.xml", + "views/stock_picking_views.xml", + ], +} diff --git a/delivery_deliverea/controllers/__init__.py b/delivery_deliverea/controllers/__init__.py new file mode 100644 index 0000000000..e081123377 --- /dev/null +++ b/delivery_deliverea/controllers/__init__.py @@ -0,0 +1,2 @@ +from . import main +from . import http diff --git a/delivery_deliverea/controllers/http.py b/delivery_deliverea/controllers/http.py new file mode 100644 index 0000000000..0071a432c7 --- /dev/null +++ b/delivery_deliverea/controllers/http.py @@ -0,0 +1,27 @@ +# © 2022 FactorLibre - Asier Neira +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html) +import json + +from odoo.http import JsonRPCDispatcher + +# Add @http.route where data parsing is required +ROUTES = ["/deliverea-tracking-webhook"] + + +__original___init__ = JsonRPCDispatcher.__init__ + + +def __init__(self, request): + if request and request.httprequest.path in ROUTES: + data = { + "data": json.loads( + request.httprequest.data.decode(request.httprequest.charset or "utf-8") + ) + } + request.httprequest.data = request.httprequest._cached_data = json.dumps( + data + ).encode(request.httprequest.charset or "utf-8") + __original___init__(self, request=request) + + +JsonRPCDispatcher.__init__ = __init__ diff --git a/delivery_deliverea/controllers/main.py b/delivery_deliverea/controllers/main.py new file mode 100644 index 0000000000..7e0c46da93 --- /dev/null +++ b/delivery_deliverea/controllers/main.py @@ -0,0 +1,32 @@ +# © 2022 - FactorLibre - Jore Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import json + +# from odoo.addons.queue_job.job import job +from odoo import http +from odoo.http import request + + +class DelivereaWebhook(http.Controller): + @http.route( + ["/deliverea-tracking-webhook"], + type="json", + auth="public", + methods=["POST"], + csrf=False, + no_jsonrpc=True, + ) + def order_import_webhook(self, **post): + SudoStockPicking = request.env["stock.picking"].sudo() + data = json.loads(request.httprequest.data) + for line in data.get("data"): + SudoStockPicking.with_delay().deliverea_update_tracking_state(line) + return self.return_response("Tracking OK") + + @staticmethod + def return_response(msg, code=200): + return http.Response( + json.dumps({"message": msg, "status": code}), + code, + headers=[("Content-Type", "application/json")], + ) diff --git a/delivery_deliverea/data/deliverea_states_data.xml b/delivery_deliverea/data/deliverea_states_data.xml new file mode 100644 index 0000000000..2321d12057 --- /dev/null +++ b/delivery_deliverea/data/deliverea_states_data.xml @@ -0,0 +1,198 @@ + + + + 01 + Preparing shipment + shipping_recorded_in_carrier + + + 02 + Pickup assigned + shipping_recorded_in_carrier + + + 03 + Pending pickup + in_transit + + + 04 + Documentation errors + incidence + + + 05 + Picked up + warehouse_delivered + + + 06 + Package leaving origin + in_transit + + + 07 + Administrative formalities/custom clearance + in_transit + + + 08 + Package on way to destination + in_transit + + + 09 + Administrative documentation received + in_transit + + + 10 + Ready to deliver + in_transit + + + 11 + Delivered + customer_delivered + + + 12 + Delivery failed + incidence + + + 13 + Errors in address + incidence + + + 14 + Refused package + incidence + + + 15 + Waiting sender action + incidence + + + 16 + Closed for vacations + incidence + + + 17 + Package returned + incidence + + + 18 + Waiting recipient action + incidence + + + 19 + Package withheld + incidence + + + 20 + Recipient changed delivery date + incidence + + + 21 + Recipient changed delivery address + incidence + + + 22 + Incorrect route + incidence + + + 23 + Reassigned pickup address + incidence + + + 24 + Pending for recipient pickup + in_transit + + + 25 + Damaged/Lost/Partial + incidence + + + 26 + Processing solution + incidence + + + 27 + Tracking unavailable + incidence + + + 28 + Expired + incidence + + + 29 + Collection failed + incidence + + + 30 + Deposited in drop point + in_transit + + + 31 + Pickup cancelled + canceled_shipment + + + 32 + Route cancelled for force majeure + incidence + + + 35 + Stolen package + incidence + + + 36 + Without space + incidence + + + 37 + Delivery cancelled + canceled_shipment + + + 38 + Without tie to delivery + incidence + + + 39 + Absent + incidence + + + 40 + Wrong address + incidence + + + 99 + Incidence not mapped + incidence + + diff --git a/delivery_deliverea/data/ir_cron.xml b/delivery_deliverea/data/ir_cron.xml new file mode 100644 index 0000000000..e8aac068b5 --- /dev/null +++ b/delivery_deliverea/data/ir_cron.xml @@ -0,0 +1,17 @@ + + + + + Delivera: Get Services + + + 1 + days + -1 + + + code + model.deliverea_get_services_cron(extra_domain=[]) + + diff --git a/delivery_deliverea/data/product_packaging_data.xml b/delivery_deliverea/data/product_packaging_data.xml new file mode 100644 index 0000000000..633c082dae --- /dev/null +++ b/delivery_deliverea/data/product_packaging_data.xml @@ -0,0 +1,12 @@ + + + + default + Deliverea Default Package + deliverea + 15 + 10 + 20 + 2 + + diff --git a/delivery_deliverea/data/queue_job_data.xml b/delivery_deliverea/data/queue_job_data.xml new file mode 100644 index 0000000000..84d0db405b --- /dev/null +++ b/delivery_deliverea/data/queue_job_data.xml @@ -0,0 +1,18 @@ + + + + + deliverea + + + + + + deliverea_update_tracking_state + + + diff --git a/delivery_deliverea/i18n/es.po b/delivery_deliverea/i18n/es.po new file mode 100644 index 0000000000..d20430d7f3 --- /dev/null +++ b/delivery_deliverea/i18n/es.po @@ -0,0 +1,623 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * delivery_deliverea +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 16.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2023-06-07 14:38+0000\n" +"PO-Revision-Date: 2023-06-07 14:38+0000\n" +"Last-Translator: \n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "API Configuration" +msgstr "Configuración de la API" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__active +msgid "Active" +msgstr "Activo" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__active +msgid "Active?" +msgstr "Activo?" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "" +"Additional service attributes. Availability depends on the carrier and " +"service." +msgstr "Atributos adicionales del servicio. La disponibilidad depende del transportista y del servicio." + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__address +msgid "Address" +msgstr "Dirección" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "" +"Attention! Failure to set these fields will not allow you to ship " +"successfully." +msgstr "Atención! Si no se establecen estos campos no se podrán realizar envíos de forma satisfactoria." + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__billing_account +msgid "Billing Account Id" +msgstr "Billing Account Id" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__canceled_shipment +msgid "Canceled shipment" +msgstr "Envio cancelado" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_stock_package_type__package_carrier_type +msgid "Carrier" +msgstr "Transportista" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__code +msgid "Carrier Code" +msgstr "Código de transportista" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "Carrier Configuration" +msgstr "Configuración del transportista" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/deliverea_request.py:0 +#, python-format +msgid "Carrier Error" +msgstr "Error del transportista" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__name +msgid "Carrier Name" +msgstr "Transportista" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__delivery_state +msgid "Carrier State" +msgstr "Estado del transportista" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__city +msgid "City" +msgstr "Cuidad" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__carrier_deliverea_parameter__type__conditional +msgid "Conditional" +msgstr "Condicional" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__country_id +msgid "Country" +msgstr "Pais" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__create_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__create_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__create_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__create_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__create_uid +msgid "Created by" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__create_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__create_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__create_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__create_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__create_date +msgid "Created on" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__customer_delivered +msgid "Customer delivered" +msgstr "Entregado al cliente" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_default_packaging_id +msgid "Default Packaging Type" +msgstr "Tipo de embalaje por defecto" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_default_packaging_id +msgid "Default weight, height, width and length for packages" +msgstr "Peso, altura, anchura y longitud por defecto de los paquetes" + +#. module: delivery_deliverea +#: model:ir.actions.server,name:delivery_deliverea.ir_cron_get_service_delivera_ir_actions_server +#: model:ir.cron,cron_name:delivery_deliverea.ir_cron_get_service_delivera +msgid "Delivera: Get Services" +msgstr "Deliverea: Obtener servicios" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__delivery_carrier__delivery_type__deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__stock_package_type__package_carrier_type__deliverea +msgid "Deliverea" +msgstr "Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_description +msgid "Deliverea Articles Description" +msgstr "Descripción de los articulos" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_carrier_id +msgid "Deliverea Carrier" +msgstr "Deliverea Carrier" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_carrier_service +msgid "Deliverea Carrier Service" +msgstr "Servicios del transportista" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "Deliverea Configuration" +msgstr "Configuración deliverea" + +#. module: delivery_deliverea +#: model:ir.actions.act_window,name:delivery_deliverea.delivery_deliverea_distribution_center_action +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__deliverea_distribution_center_id +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_distribution_center_id +msgid "Deliverea Distribution Center" +msgstr "Centro de distribución Deliverea" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/deliverea_request.py:0 +#, python-format +msgid "Deliverea Error" +msgstr "Error de Deliverea" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/deliverea_request.py:0 +#, python-format +msgid "Deliverea Error. Uncontrolled error, it is necessary to check the log" +msgstr "" +"Error de Deliverea. Error no controlado, es necesario comprobar el log" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_stock_picking__deliverea_parcel_client_code +msgid "Deliverea Parcel Client Code" +msgstr "Código de cliente de paquete de Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_password +msgid "Deliverea Password" +msgstr "Contraseña Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_stock_picking__deliverea_reference +msgid "Deliverea Reference" +msgstr "Referencia Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__carrier_deliverea_service +msgid "Deliverea Service" +msgstr "Servicio Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__state +msgid "Deliverea State" +msgstr "Estado Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__code +msgid "Deliverea State Code" +msgstr "Código de estado Deliverea" + +#. module: delivery_deliverea +#: model:ir.actions.act_window,name:delivery_deliverea.delivery_deliverea_state_mapping_action +#: model:ir.ui.menu,name:delivery_deliverea.delivery_deliverea_state_mapping_menu +msgid "Deliverea State Mapping" +msgstr "Mapeo de estados deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_username +msgid "Deliverea Username" +msgstr "Nombre usuario Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__deliverea_parameters +msgid "Deliverea parameters" +msgstr "Parametros Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_url_prod +msgid "Deliverea production environment URL" +msgstr "URL entorno de producción Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__carrier_id +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__code +msgid "Deliverea service code" +msgstr "Código de servicio de deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_url_test +msgid "Deliverea test environment URL" +msgstr "URL entorno de test Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_url_tracking +msgid "Deliverea tracking URL" +msgstr "URL de seguimiento Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__description +msgid "Description" +msgstr "Descripción" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_description +msgid "" +"Description of the expedition articles (required for crossborder " +"international expeditions if no description by parcel[items] is present)" +msgstr "Descripción de los artículos de la expedición (necesaria para las expediciones internacionales transfronterizas si no hay descripción por paquete[artículos])" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__display_name +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__display_name +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__display_name +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__display_name +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__name +msgid "Distribution Center" +msgstr "Centro de distribución" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__email +msgid "Email" +msgstr "Email" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_picking_withcarrier_out_form +msgid "Generate Pickup" +msgstr "Generar recogida deliverea" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_picking_withcarrier_out_form +msgid "Get Deliverea Label" +msgstr "Obtener etiqueta Deliverea" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_hide_sender +msgid "Hide Sender" +msgstr "Ocultar el remitente" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__id +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__id +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__id +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__id +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__id +msgid "ID" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__carrier_deliverea_parameter__type__ignored +msgid "Ignored" +msgstr "Ignorado" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "Import Carriers and Services" +msgstr "Importar transportistas y servicios" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "Import Distribution Centers" +msgstr "Importar centros de distribución" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__in_transit +msgid "In transit" +msgstr "En tránsito" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__incidence +msgid "Incidence" +msgstr "Incidencia" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_note_selection_id +msgid "" +"It allows to choose the field that we want to show within the carrier " +"observations" +msgstr "Permite elegir el campo que queremos mostrar dentro de las observaciones del transportista" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea____last_update +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter____last_update +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service____last_update +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center____last_update +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state____last_update +msgid "Last Modified on" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__write_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__write_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__write_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__write_uid +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea__write_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__write_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__write_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__write_date +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_state__write_date +msgid "Last Updated on" +msgstr "" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__latitude +msgid "Latitude" +msgstr "Latitud" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__longitude +msgid "Longitude" +msgstr "Longitud" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__name +msgid "Name" +msgstr "Nombre" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_notifications_email +msgid "Notify by email" +msgstr "Notificar por email" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_notifications_sms +msgid "Notify by sms" +msgstr "Notificar por sms" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__observations +msgid "Observations" +msgstr "Observaciones" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__carrier_deliverea_parameter__type__optional +msgid "Optional" +msgstr "Opcional" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__name +msgid "Parameter Name" +msgstr "Nombre del parametro" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__phone +msgid "Phone" +msgstr "Telefono" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__delivery_type +msgid "Provider" +msgstr "Proveedor" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__carrier_deliverea_parameter__type__required +msgid "Required" +msgstr "Requerido" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_return_label +msgid "Return Label" +msgstr "Etiqueta de devolución" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_return_proof_delivery +msgid "Return Proof Delivery" +msgstr "Entrega de la prueba de devolución" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_saturday_delivery +msgid "Saturday Delivery" +msgstr "Entrega el sábado" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_delivery_carrier__deliverea_note_selection_id +msgid "Select Note Field" +msgstr "Seleccionar campo para notas" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "Service Attributes" +msgstr "Atributos del servicio" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_delivery_carrier +msgid "Shipping Methods" +msgstr "Forma de envío" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__shipping_recorded_in_carrier +msgid "Shipping recorded in carrier" +msgstr "Envío registrado en el transportista" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.delivery_deliverea_state_mapping_tree_view +msgid "States" +msgstr "Estados" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_stock_package_type +msgid "Stock package type" +msgstr "Tipo de paquete de existencias" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_notifications_email +msgid "The carrier will send info email(s) to the final customer" +msgstr "El transportista enviará el(los) correo(s) informativo(s) al cliente final" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_notifications_sms +msgid "The carrier will send info sms(s) to the final customer" +msgstr "El transportista enviará el(los) sms informativo(s) al cliente final" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/delivery_carrier.py:0 +#, python-format +msgid "The field %s is required" +msgstr "El campo %s es obligatorio" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/delivery_carrier.py:0 +#, python-format +msgid "" +"The max_weight, height, width and length values must be defined in the " +"package associated with the carrier." +msgstr "" +"Los valores de peso máximo, altura, anchura y longitud deben definirse en el" +" paquete asociado al soporte." + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/delivery_carrier.py:0 +#, python-format +msgid "The value for %s field/s is mandatory in %s" +msgstr "El valor de los campos %s es obligatorio en %s" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/deliverea_request.py:0 +#, python-format +msgid "Timeout: the server did not reply within 60s" +msgstr "Tiempo de espera: el servidor no ha respondido en 60s" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_stock_picking +msgid "Transfer" +msgstr "Albarán" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_parameter__type +msgid "Type" +msgstr "Tipo" + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__uuid +msgid "UUID" +msgstr "UUID" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__carrier_deliverea_parameter__type__unsupported +msgid "Unsupported" +msgstr "No soportado" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/deliverea_request.py:0 +#, python-format +msgid "Unsupported request type, use 'GET', 'POST', 'UPDATE', 'DELETE'" +msgstr "" +"Tipo de solicitud no admitido, utilice 'GET', 'POST', 'UPDATE', 'DELETE'" + +#. module: delivery_deliverea +#: model:ir.model.fields.selection,name:delivery_deliverea.selection__deliverea_state__delivery_state__warehouse_delivered +msgid "Warehouse delivered" +msgstr "Entregado por almacén" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_saturday_delivery +msgid "" +"Whether or not the expedition should be delivered on Saturday or wait until " +"next Monday (availability depends on carrier and service)" +msgstr "Si la expedición debe entregarse el sábado o esperar hasta el próximo lunes (la disponibilidad depende del transportista y del servicio)" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_return_proof_delivery +msgid "" +"Whether or not to a proof of delivery should be returned back to origin " +"(availability depends on carrier and service)" +msgstr "Si se debe devolver o no un comprobante de entrega a origen (la disponibilidad depende del transportista y del servicio)" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_hide_sender +msgid "" +"Whether or not to hide sender information in the printed label (availability" +" depends on carrier and service)" +msgstr "Ocultar o no la información del remitente en la etiqueta impresa (la disponibilidad depende del transportista y del servicio)" + +#. module: delivery_deliverea +#: model:ir.model.fields,help:delivery_deliverea.field_delivery_carrier__deliverea_return_label +msgid "" +"Whether or not to include dormant return label in case the final customer " +"wants to return the expedition (availability depends on carrier and service)" +msgstr "Incluir o no la etiqueta de devolución inactiva en caso de que el cliente final quiera devolver la expedición (la disponibilidad depende del transportista y del servicio)" + +#. module: delivery_deliverea +#. odoo-python +#: code:addons/delivery_deliverea/models/delivery_carrier.py:0 +#, python-format +msgid "You must select a distribution center first" +msgstr "Primero tienes que seleccionar un centro de distribución" + +#. module: delivery_deliverea +#: model_terms:ir.ui.view,arch_db:delivery_deliverea.view_delivery_carrier_form +msgid "" +"You will need to import the first time you use Deliverea or when you want to" +" update these fields." +msgstr "Será necesario realizar una importación la primera vez que se utilice Deliverea o cuando se deseen actualizar esos campos." + +#. module: delivery_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_deliverea_distribution_center__zip +msgid "Zip" +msgstr "C.P" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_carrier_deliverea +#: model:ir.model.fields,field_description:delivery_deliverea.field_carrier_deliverea_service__carrier_deliverea_id +msgid "carrier.deliverea" +msgstr "carrier.deliverea" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_carrier_deliverea_parameter +msgid "carrier.deliverea.parameter" +msgstr "carrier.deliverea.parameter" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_carrier_deliverea_service +msgid "carrier.deliverea.service" +msgstr "carrier.deliverea.service" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_deliverea_distribution_center +msgid "deliverea.distribution.center" +msgstr "deliverea.distribution.center" + +#. module: delivery_deliverea +#: model:ir.model,name:delivery_deliverea.model_deliverea_state +msgid "deliverea.state" +msgstr "deliverea.state" diff --git a/delivery_deliverea/models/__init__.py b/delivery_deliverea/models/__init__.py new file mode 100644 index 0000000000..7185b8fbbf --- /dev/null +++ b/delivery_deliverea/models/__init__.py @@ -0,0 +1,9 @@ +from . import carrier_deliverea +from . import deliverea_distribution_center +from . import deliverea_request +from . import deliverea_state +from . import carrier_deliverea_parameter +from . import carrier_deliverea_service +from . import delivery_carrier +from . import stock_package_type +from . import stock_picking diff --git a/delivery_deliverea/models/carrier_deliverea.py b/delivery_deliverea/models/carrier_deliverea.py new file mode 100644 index 0000000000..0ce9e34031 --- /dev/null +++ b/delivery_deliverea/models/carrier_deliverea.py @@ -0,0 +1,19 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class CarrierDeliverea(models.Model): + _name = "carrier.deliverea" + + name = fields.Char(string="Carrier Name") + code = fields.Char(string="Carrier Code") + deliverea_distribution_center_id = fields.Many2one( + comodel_name="deliverea.distribution.center", + string="Deliverea Distribution Center", + ) + carrier_deliverea_service = fields.One2many( + string="Deliverea Service", + comodel_name="carrier.deliverea.service", + inverse_name="carrier_deliverea_id", + ) diff --git a/delivery_deliverea/models/carrier_deliverea_parameter.py b/delivery_deliverea/models/carrier_deliverea_parameter.py new file mode 100644 index 0000000000..e779e7a408 --- /dev/null +++ b/delivery_deliverea/models/carrier_deliverea_parameter.py @@ -0,0 +1,18 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class CarrierDeliveaParameter(models.Model): + _name = "carrier.deliverea.parameter" + + name = fields.Char(string="Parameter Name") + type = fields.Selection( + selection=[ + ("conditional", "Conditional"), + ("ignored", "Ignored"), + ("optional", "Optional"), + ("required", "Required"), + ("unsupported", "Unsupported"), + ], + ) diff --git a/delivery_deliverea/models/carrier_deliverea_service.py b/delivery_deliverea/models/carrier_deliverea_service.py new file mode 100644 index 0000000000..2ffeda6745 --- /dev/null +++ b/delivery_deliverea/models/carrier_deliverea_service.py @@ -0,0 +1,20 @@ +# © 2023 - FactorLibre - Oscar Indias +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +from odoo import fields, models + + +class CarrierDelivereaService(models.Model): + _name = "carrier.deliverea.service" + + name = fields.Char() + description = fields.Char() + deliverea_parameters = fields.Many2many(comodel_name="carrier.deliverea.parameter") + carrier_deliverea_id = fields.Many2one( + string="carrier deliverea", comodel_name="carrier.deliverea" + ) + code = fields.Char(string="Deliverea service code", required=True) + carrier_id = fields.One2many( + comodel_name="delivery.carrier", + inverse_name="deliverea_carrier_service", + ) + active = fields.Boolean(default=True) diff --git a/delivery_deliverea/models/deliverea_distribution_center.py b/delivery_deliverea/models/deliverea_distribution_center.py new file mode 100644 index 0000000000..7763a990f0 --- /dev/null +++ b/delivery_deliverea/models/deliverea_distribution_center.py @@ -0,0 +1,21 @@ +# Copyright 2023 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class DelivereaDistributionCenter(models.Model): + _name = "deliverea.distribution.center" + + uuid = fields.Char() + active = fields.Boolean() + name = fields.Char(string="Distribution Center") + address = fields.Char() + city = fields.Char() + zip = fields.Char() + country_id = fields.Many2one("res.country", string="Country") + observations = fields.Char() + phone = fields.Char() + email = fields.Char() + latitude = fields.Char() + longitude = fields.Char() + billing_account = fields.Char(string="Billing Account Id") diff --git a/delivery_deliverea/models/deliverea_request.py b/delivery_deliverea/models/deliverea_request.py new file mode 100644 index 0000000000..d78eb8c3c8 --- /dev/null +++ b/delivery_deliverea/models/deliverea_request.py @@ -0,0 +1,188 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# Copyright 2022 FactorLibre - Zahra Velasco +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import requests + +from odoo import _ +from odoo.exceptions import UserError + + +class DelivereaRequest(object): + def __init__(self, carrier): + self.carrier_id = carrier + path = ( + self.carrier_id.deliverea_url_prod + if self.carrier_id.prod_environment + else self.carrier_id.deliverea_url_test + ) + if path[-1] != "/": + path += "/" + self.urls = { + "distribution_centers": path + "distribution-centers", + "carriers": path + "distribution-centers/{distributionCenterId}/carriers", + "carrier_services": path + "distribution-centers/{distributionCenterId}" + "/carriers/{carrierCode}/cost-centers/{costCenter}", + "carrier_services_integrations": path + + "carriers/{carrierCode}/integrations/{integration_code}", + "create_shipment": path + "shipments", + "delete_shipment": path + "shipments/{delivereaReference}", + "get_shipment_label": path + "shipments/{delivereaReference}/label", + "create_return": path + "returns", + "get_shipment_tracking": path + "shipments/{delivereaReference}/trackings", + "get_return_tracking": path + "returns/{delivereaReference}/trackings", + } + + def _send_api_request(self, request_type, url, data=None, skip_auth=False): + if data is None: + data = {} + try: + auth = tuple() + if not skip_auth: + auth = tuple( + [ + self.carrier_id.deliverea_username, + self.carrier_id.deliverea_password, + ] + ) + if request_type == "GET": + res = requests.get(url=url, auth=auth, timeout=60) + elif request_type == "POST": + res = requests.post(url=url, auth=auth, json=data, timeout=60) + elif request_type == "PUT": + res = requests.put(url=url, auth=auth, json=data, timeout=60) + elif request_type == "DELETE": + res = requests.delete(url=url, auth=auth, json=data, timeout=60) + else: + raise UserError( + _("Unsupported request type, use 'GET', 'POST', 'UPDATE', 'DELETE'") + ) + except requests.exceptions.Timeout as exc_timeout: + raise UserError( + _("Timeout: the server did not reply within 60s") + ) from exc_timeout + if res.status_code == 202: + return True + if res.status_code not in range(200, 299): + self._check_error(res.json()) + return res + + def _check_error(self, res): + error = res.get("error", False) or res.get("errors", False) + if error: + if isinstance(error, list): + error = error[0] + return_code = error.get("code") + message = error.get("message") + detail = error.get("detail", "") + if return_code: + raise UserError( + _("%(name)s: %(rcode)s %(message)s %(detail)s %(ccode)s") + % { + "name": _("Deliverea Error"), + "rcode": return_code, + "message": message, + "detail": " ".join( + ["%s: %s" % (key, value) for key, value in detail.items()] + ) + if detail and not isinstance(detail, str) + else detail or "", + "ccode": "\n{}: {} {}".format( + _("Carrier Error"), + error.get("carrierCode"), + error.get("carrierMessage"), + ) + if error.get("carrierCode") + else "", + } + ) + else: + raise UserError( + _( + "Deliverea Error. " + "Uncontrolled error, it is necessary to check the log" + ) + ) + + def get_distribution_centers(self): + res = self._send_api_request( + request_type="GET", url=self.urls["distribution_centers"] + ) + return res.json() + + def get_carrier_list(self, distribution_center_id): + res = self._send_api_request( + request_type="GET", + url=self.urls["carriers"].format( + distributionCenterId=distribution_center_id + ), + ) + return res.json() + + def get_carrier_detail(self, distribution_center_id, carrier_code, cost_center): + res = self._send_api_request( + request_type="GET", + url=self.urls["carrier_services"].format( + carrierCode=carrier_code, + distributionCenterId=distribution_center_id, + costCenter=cost_center, + ), + ) + return res.json() + + def get_carrier_services_integrations(self, carrier_code, integration_code): + res = self._send_api_request( + request_type="GET", + url=self.urls["carrier_services_integrations"].format( + carrierCode=carrier_code, + integration_code=integration_code, + ), + ) + return res.json() + + def create_shipment(self, vals): + res = self._send_api_request( + request_type="POST", url=self.urls["create_shipment"], data=vals + ) + return res.json() + + def delete_shipment(self, deliverea_reference): + self._send_api_request( + request_type="DELETE", + url=self.urls["delete_shipment"].format( + delivereaReference=deliverea_reference + ), + ) + return True + + def get_shipment_label(self, deliverea_reference): + res = self._send_api_request( + request_type="GET", + url=self.urls["get_shipment_label"].format( + delivereaReference=deliverea_reference, + ), + ) + return res.json() + + def create_return(self, vals): + res = self._send_api_request( + request_type="POST", url=self.urls["create_return"], data=vals + ) + return res.json() + + def get_shipment_tracking(self, deliverea_reference): + res = self._send_api_request( + request_type="GET", + url=self.urls["get_shipment_tracking"].format( + delivereaReference=deliverea_reference + ), + ) + return res.json() + + def get_return_tracking(self, deliverea_reference): + res = self._send_api_request( + request_type="GET", + url=self.urls["get_return_tracking"].format( + delivereaReference=deliverea_reference + ), + ) + return res.json() diff --git a/delivery_deliverea/models/deliverea_state.py b/delivery_deliverea/models/deliverea_state.py new file mode 100644 index 0000000000..84860df6a8 --- /dev/null +++ b/delivery_deliverea/models/deliverea_state.py @@ -0,0 +1,21 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class DelivereaState(models.Model): + _name = "deliverea.state" + + code = fields.Char("Deliverea State Code") + state = fields.Char("Deliverea State") + delivery_state = fields.Selection( + selection=[ + ("shipping_recorded_in_carrier", "Shipping recorded in carrier"), + ("in_transit", "In transit"), + ("canceled_shipment", "Canceled shipment"), + ("incidence", "Incidence"), + ("customer_delivered", "Customer delivered"), + ("warehouse_delivered", "Warehouse delivered"), + ], + string="Carrier State", + ) diff --git a/delivery_deliverea/models/delivery_carrier.py b/delivery_deliverea/models/delivery_carrier.py new file mode 100644 index 0000000000..f6ab013977 --- /dev/null +++ b/delivery_deliverea/models/delivery_carrier.py @@ -0,0 +1,554 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# Copyright 2022 FactorLibre - Zahra Velasco +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import logging +from datetime import datetime + +from odoo import _, api, fields, models +from odoo.exceptions import UserError +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT, config, ustr + +from .deliverea_request import DelivereaRequest + +_logger = logging.getLogger(__name__) + + +MANDATORY_SENDER_FIELDS = ( + "name", + "address", + "city", + "zipCode", + "countryCode", + "phone", + "email", +) +MANDATORY_PAYLOAD_FIELDS = ( + "carrierCode", + "serviceCode", + "distributionCenterId", + "clientReference", +) + + +class DeliveryCarrier(models.Model): + _inherit = "delivery.carrier" + + @api.model + def _get_default_field(self): + default_field = self.env["ir.model.fields"].search( + [ + ("model_id.name", "=", "stock.picking"), + ("name", "=", "note"), + ] + ) + return default_field.id + + delivery_type = fields.Selection( + selection_add=[("deliverea", "Deliverea")], + ondelete={"deliverea": "set default"}, + ) + deliverea_username = fields.Char() + deliverea_password = fields.Char() + deliverea_distribution_center_id = fields.Many2one( + comodel_name="deliverea.distribution.center", + string="Deliverea Distribution Center", + ) + deliverea_url_prod = fields.Char(string="Deliverea production environment URL") + deliverea_url_test = fields.Char(string="Deliverea test environment URL") + deliverea_url_tracking = fields.Char(string="Deliverea tracking URL") + deliverea_carrier_id = fields.Many2one( + comodel_name="carrier.deliverea", string="Deliverea Carrier" + ) + deliverea_description = fields.Char( + string="Deliverea Articles Description", + help="Description of the expedition articles (required for crossborder" + " international expeditions if no description by parcel[items] is present)", + ) + deliverea_note_selection_id = fields.Many2one( + comodel_name="ir.model.fields", + string="Select Note Field", + domain="[('model_id', '=', 'stock.picking')]", + default=_get_default_field, + help="It allows to choose the field that we want " + "to show within the carrier observations", + ) + deliverea_default_packaging_id = fields.Many2one( + comodel_name="stock.package.type", + string="Default Packaging Type", + domain=[("package_carrier_type", "=", "deliverea")], + help="Default weight, height, width and length for packages", + ) + deliverea_notifications_sms = fields.Boolean( + string="Notify by sms", + help="The carrier will send info sms(s) to the final customer", + ) + deliverea_notifications_email = fields.Boolean( + string="Notify by email", + help="The carrier will send info email(s) to the final customer", + ) + deliverea_saturday_delivery = fields.Boolean( + string="Saturday Delivery", + help="Whether or not the expedition should be delivered on Saturday" + " or wait until next Monday (availability depends on carrier and service)", + ) + deliverea_return_label = fields.Boolean( + string="Return Label", + help="Whether or not to include dormant return label in case the final customer" + " wants to return the expedition (availability depends on carrier and service)", + ) + deliverea_return_proof_delivery = fields.Boolean( + string="Return Proof Delivery", + help="Whether or not to a proof of delivery should be returned" + " back to origin (availability depends on carrier and service)", + ) + deliverea_hide_sender = fields.Boolean( + string="Hide Sender", + help="Whether or not to hide sender information in the printed label" + " (availability depends on carrier and service)", + ) + deliverea_carrier_service = fields.Many2one( + comodel_name="carrier.deliverea.service", + domain="[('carrier_deliverea_id', '=', deliverea_carrier_id)]", + ) + + def deliverea_get_distribution_centers(self): + deliverea_request = DelivereaRequest(self) + deliverea_distribution_center_env = self.env["deliverea.distribution.center"] + distribution_centers = deliverea_request.get_distribution_centers() + for distribution_center in distribution_centers.get("data"): + center = deliverea_distribution_center_env.search( + [("uuid", "=", distribution_center.get("id"))] + ) + vals = { + "active": distribution_center.get("active"), + "name": distribution_center.get("name"), + "address": distribution_center.get("address"), + "city": distribution_center.get("city"), + "zip": distribution_center.get("zipCode"), + "observations": distribution_center.get("observations"), + "phone": distribution_center.get("phone"), + "email": distribution_center.get("email"), + "latitude": distribution_center.get("latitude"), + "longitude": distribution_center.get("longitude"), + "billing_account": distribution_center.get("billingAccountId"), + } + if not center: + country_id = self.env["res.country"].search( + [("code", "=", distribution_center.get("countryCode"))] + ) + deliverea_distribution_center_env.create( + { + "uuid": distribution_center.get("id"), + "country_id": country_id.id if country_id else False, + **vals, + } + ) + else: + center.write(vals) + + def deliverea_get_services_cron(self, extra_domain=None): + domain = [ + ("delivery_type", "=", "deliverea"), + ("deliverea_distribution_center_id", "!=", False), + ] + if extra_domain: + domain.extend(extra_domain) + delivery_carrier_ids = self.env["delivery.carrier"].search(domain) + for delivery_carrier in delivery_carrier_ids: + try: + delivery_carrier.deliverea_get_services() + self.env.cr.savepoint() + if not config["test_enable"]: + _logger.info( + 'The services of the delivery carrier "%s" has been updated' + % delivery_carrier.name + ) + except Exception as e: + _logger.warning(ustr(e)) + self.env.cr.rollback() + return True + + def deliverea_get_services(self): + deliverea_request = DelivereaRequest(self) + if not self.deliverea_distribution_center_id: + raise UserError(_("You must select a distribution center first")) + carriers = deliverea_request.get_carrier_list( + self.deliverea_distribution_center_id.uuid + ) + for carrier in carriers.get("data"): + deliverea_carrier = self.env["carrier.deliverea"].search( + [ + ("code", "=", carrier.get("code")), + ( + "deliverea_distribution_center_id", + "=", + self.deliverea_distribution_center_id.id, + ), + ] + ) + if not deliverea_carrier: + deliverea_carrier = self.env["carrier.deliverea"].create( + { + "name": carrier.get("code"), + "code": carrier.get("code"), + "deliverea_distribution_center_id": ( + self.deliverea_distribution_center_id.id + ), + } + ) + services = deliverea_request.get_carrier_detail( + distribution_center_id=self.deliverea_distribution_center_id.uuid, + carrier_code=carrier.get("code"), + cost_center=carrier.get("costCenters")[0].get("code"), + ) + for service in services.get("services"): + service_id = self.env["carrier.deliverea.service"].search( + [ + ("carrier_deliverea_id", "=", deliverea_carrier.id), + ("code", "=", service.get("code")), + ("active", "in", [True, False]), + ] + ) + active_service = service.get("active", False) + # Si un servicio no esta creado y viene desactivada, no la creamos + if service_id and service_id.active != active_service: + service_id.write({"active": active_service}) + if not active_service and not service_id: + continue + services_parameter = ( + deliverea_request.get_carrier_services_integrations( + carrier.get("code"), services.get("integrationCode") + ) + ) + service_parameter = next( + ( + item + for item in services_parameter.get("services") + if item.get("code") == service.get("code") + ), + {}, + ) + if not service_id: + service_id = self.env["carrier.deliverea.service"].create( + { + "name": service_parameter.get("name") + or service.get("code"), + "code": service.get("code"), + "description": service_parameter.get("description") or "", + "carrier_deliverea_id": deliverea_carrier.id, + "active": active_service, + } + ) + for parameter in service_parameter.get("parameters", []): + parameter_id = self.env["carrier.deliverea.parameter"].search( + [ + ("name", "=", parameter.get("name")), + ("type", "=", parameter.get("necessity").get("type")), + ] + ) + if not parameter_id: + parameter_id = self.env["carrier.deliverea.parameter"].create( + { + "name": parameter.get("name"), + "type": parameter.get("necessity").get("type"), + } + ) + if parameter_id.id not in service_id.deliverea_parameters.ids: + service_id.deliverea_parameters = [ + ( + 4, + parameter_id.id, + ) + ] + + def _delete_empty_values(self, values): + delete = [] + for key, value in values.items(): + if values[key].__class__ is dict: + self._delete_empty_values(values[key]) + if value == "": + delete.append(key) + for key in delete: + del values[key] + + def _check_mandatory_fields(self, values, mandatory_list, object_id): + errors = [] + for key, value in values.items(): + if key in mandatory_list and not value: + errors.append(key) + if errors: + raise UserError( + _("The value for %(field)s field/s is mandatory in %(object_id)s") + % {"field": ", ".join(errors), "object_id": object_id.name} + ) + + def _get_field_from_partner_or_parent_id(self, partner, field): + return partner[field] or partner.parent_id and partner.parent_id[field] or "" + + def _get_deliverea_sender_info(self, partner, request_type): + country_id = self._get_field_from_partner_or_parent_id(partner, "country_id") + state_id = self._get_field_from_partner_or_parent_id(partner, "state_id") + values = { + "name": self._get_field_from_partner_or_parent_id(partner, "name"), + "address": " ".join( + [ + self._get_field_from_partner_or_parent_id(partner, "street"), + self._get_field_from_partner_or_parent_id(partner, "street2"), + ] + ).strip(), + "city": self._get_field_from_partner_or_parent_id(partner, "city"), + "zipCode": self._get_field_from_partner_or_parent_id(partner, "zip"), + "countryCode": country_id.code if country_id else "", + "idNumber": self._get_field_from_partner_or_parent_id(partner, "vat"), + "stateCode": state_id.code if state_id else "", + "observations": "", + "phone": self._get_field_from_partner_or_parent_id(partner, "phone") + or self._get_field_from_partner_or_parent_id(partner, "mobile"), + "email": self._get_field_from_partner_or_parent_id(partner, "email"), + } + self._check_mandatory_fields( + values, + MANDATORY_SENDER_FIELDS, + partner, + ) + return values + + def _get_deliverea_parcel_items(self, picking): + items = [] + for move_line in picking.move_line_ids: + items.append( + { + "sku": move_line.product_id.barcode, + "description": move_line.product_id.name, + "amount": abs(move_line.move_id.sale_line_id.price_total), + "units": int(move_line.reserved_uom_qty), + } + ) + return items + + def _get_deliverea_parcel_info(self, carrier, picking): + parcels = [] + num_packages = picking.number_of_packages or 1 + default_package = carrier.deliverea_default_packaging_id + if ( + not default_package.max_weight + or not default_package.height + or not default_package.width + or not default_package.packaging_length + ): + raise UserError( + _( + "The max_weight, height, width and length values must be defined" + " in the package associated with the carrier." + ) + ) + for i in range(num_packages): + parcels.append( + { + "weight": default_package.max_weight, + "height": default_package.height, + "width": default_package.width, + "length": default_package.packaging_length, + } + ) + if ( + i == 0 + and picking.picking_type_id.warehouse_id.partner_id.country_id.code + != picking.partner_id.country_id.code + ): + parcels[0].update({"items": self._get_deliverea_parcel_items(picking)}) + return parcels + + def _get_service_attributes(self, carrier, service): + values = { + "cashOnDelivery": "0.0 EUR", + "carrierNotifications": { + "sms": carrier.deliverea_notifications_sms, + "email": carrier.deliverea_notifications_email, + }, + "saturdayDelivery": carrier.deliverea_saturday_delivery, + "includeReturnLabel": carrier.deliverea_return_label, + "returnProofOfDelivery": carrier.deliverea_return_label, + "hideSender": carrier.deliverea_hide_sender, + "insuranceValue": "0.0 EUR", + } + for parameter in service.deliverea_parameters: + if parameter.name in values.keys(): + if parameter.type in ("ignored", "unsupported"): + del values[parameter.name] + elif parameter.type == "required": + raise UserError( + _("The field %(parameter)s is required") + % {"parameter": parameter.name} + ) + else: + # These keys have a different value in the dictionary of the API call + # with respect to information received as a parameter + if parameter.name == "notificationViaSMS" and parameter.type in ( + "ignored", + "unsupported", + ): + del values["carrierNotifications"]["sms"] + elif parameter.name == "notificationViaEmail" and parameter.type in ( + "ignored", + "unsupported", + ): + del values["carrierNotifications"]["email"] + if ( + "carrierNotifications" in values.keys() + and len(values["carrierNotifications"]) == 0 + ): + del values["carrierNotifications"] + return values + + def _prepare_deliverea_order(self, picking): + carrier = picking.carrier_id + service = carrier.deliverea_carrier_service + request_type = "to" if picking.picking_type_code == "outgoing" else "from" + payload = { + request_type: self._get_deliverea_sender_info( + picking.partner_id, request_type + ), + "carrierCode": carrier.deliverea_carrier_id.code, + "serviceCode": service.code, + "costCenterCode": "DEFAULT", + "distributionCenterId": carrier.deliverea_distribution_center_id.uuid, + "clientReference": picking.name, + "shippingDate": datetime.now().strftime("%Y-%m-%dT%H:%M:%S") + "+00:00", + "description": carrier.deliverea_description, + "totalAmount": "{} {}".format( + abs(picking.sale_id.amount_total) if picking.sale_id else 0, + picking.sale_id.currency_id.name or self.company_id.currency_id.name, + ), + "parcels": self._get_deliverea_parcel_info(carrier, picking), + "serviceAttributes": self._get_service_attributes(carrier, service), + "clientAdditionalInfo": { + "freeLabelText": hasattr(self, carrier.deliverea_note_selection_id.name) + and getattr(self, carrier.deliverea_note_selection_id.name) + or "", + }, + } + self._delete_empty_values(payload) + self._check_mandatory_fields(payload, MANDATORY_SENDER_FIELDS, carrier) + return payload + + def deliverea_send_shipping(self, pickings): + res = [] + deliverea_request = DelivereaRequest(self) + for picking in pickings: + if picking.picking_type_code == "outgoing": + vals = self._prepare_deliverea_order(picking) + response = deliverea_request.create_shipment(vals) + picking.write( + { + "deliverea_reference": response.get("delivereaReference", ""), + "carrier_tracking_ref": response.get("carrierReference", ""), + } + ) + res.append( + { + "exact_price": 0, + "tracking_number": response.get("carrierReference", ""), + } + ) + self.deliverea_get_label(picking) + else: + # incoming moves are generated from the "generate pickup" button + res.append( + { + "exact_price": 0, + "tracking_number": picking.carrier_tracking_ref, + } + ) + return res + + def deliverea_get_return_label(self, pickings): + self.deliverea_return_shipping(pickings) + + def deliverea_return_shipping(self, pickings): + res = [] + deliverea_request = DelivereaRequest(self) + for picking in pickings: + vals = self._prepare_deliverea_order(picking) + response = deliverea_request.create_return(vals) + picking.write( + { + "carrier_tracking_ref": response.get("carrierReference", ""), + "deliverea_reference": response.get("delivereaReference", ""), + } + ) + return res + + def deliverea_cancel_shipment(self, pickings): + deliverea_request = DelivereaRequest(self) + for picking in pickings: + deliverea_request.delete_shipment(picking.deliverea_reference) + picking.write({"deliverea_reference": False}) + + def deliverea_get_label(self, pickings): + deliverea_request = DelivereaRequest(self) + for picking in pickings: + if not picking.deliverea_reference: + # Recreate shipping and get label + self.deliverea_send_shipping(picking) + else: + response = deliverea_request.get_shipment_label( + picking.deliverea_reference + ) + if response: + file_type = response.get("type") + self.env["shipping.label"].create( + { + "name": "%s.%s" % (picking.name, file_type), + "type": "binary", + "datas": response.get("content"), + "res_model": picking._name, + "res_id": picking.id, + } + ) + self.deliverea_tracking_state_update(picking) + return True + + def deliverea_get_tracking_link(self, picking): + domain = ( + self.deliverea_url_tracking + if self.deliverea_url_tracking[-1] == "/" + else self.deliverea_url_tracking + "/" + ) + return "{}?q={}".format(domain, picking.deliverea_reference) + + def deliverea_tracking_state_update(self, picking): + self.ensure_one() + if not picking.deliverea_reference: + return + deliverea_request = DelivereaRequest(self) + if picking.picking_type_code == "outgoing": + tracking_events = deliverea_request.get_shipment_tracking( + picking.deliverea_reference + ) + else: + tracking_events = deliverea_request.get_return_tracking( + picking.deliverea_reference + ) + if not tracking_events: + return + picking.tracking_state_history = "\n".join( + [ + "- {}: [{}] {}".format( + event.get("occurredAt"), event.get("code"), event.get("message") + ) + for event in tracking_events + ] + ) + tracking = tracking_events.pop() + picking.tracking_state = "[{}] {}".format( + tracking.get("code"), tracking.get("message") + ) + deliverea_state = self.env["deliverea.state"].search( + [("code", "=", tracking.get("code"))] + ) + picking.delivery_state = deliverea_state.delivery_state + if picking.delivery_state == "customer_delivered": + picking.date_delivered = datetime.strftime( + datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ) diff --git a/delivery_deliverea/models/stock_package_type.py b/delivery_deliverea/models/stock_package_type.py new file mode 100644 index 0000000000..2dc7741d4d --- /dev/null +++ b/delivery_deliverea/models/stock_package_type.py @@ -0,0 +1,9 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from odoo import fields, models + + +class ProductPackaging(models.Model): + _inherit = "stock.package.type" + + package_carrier_type = fields.Selection(selection_add=[("deliverea", "Deliverea")]) diff --git a/delivery_deliverea/models/stock_picking.py b/delivery_deliverea/models/stock_picking.py new file mode 100644 index 0000000000..f962e210a8 --- /dev/null +++ b/delivery_deliverea/models/stock_picking.py @@ -0,0 +1,58 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# Copyright 2022 FactorLibre - Zahra Velasco +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +from datetime import datetime + +from odoo import api, fields, models +from odoo.tools import DEFAULT_SERVER_DATETIME_FORMAT + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + deliverea_reference = fields.Char() + deliverea_parcel_client_code = fields.Char() + + def deliverea_get_label(self): + self.ensure_one() + if self.delivery_type != "deliverea": + return + return self.carrier_id.deliverea_get_label(self) + + @api.model + def deliverea_update_tracking_state(self, data): + if data.get("delivereaReference"): + picking_id = ( + self.env["stock.picking"] + .sudo() + .search([("deliverea_reference", "=", data.get("delivereaReference"))]) + ) + if picking_id: + deliverea_state = self.env["deliverea.state"].search( + [("code", "=", data.get("trackingCode"))] + ) + picking_id.write( + { + "delivery_state": deliverea_state.delivery_state + if deliverea_state + else False, + "date_delivered": datetime.strftime( + datetime.now(), DEFAULT_SERVER_DATETIME_FORMAT + ) + if deliverea_state + and deliverea_state.delivery_state == "customer_delivered" + else False, + "carrier_tracking_url": data.get("advancedTrackingUrl"), + "tracking_state": "[{}] {}".format( + data.get("trackingCode"), data.get("trackingDetails") + ), + "tracking_state_history": ( + picking_id.tracking_state_history or "" + ) + + "- {}: [{}] {} \n".format( + data.get("date"), + data.get("trackingCode"), + data.get("trackingDetails"), + ), + } + ) diff --git a/delivery_deliverea/readme/CONFIGURE.rst b/delivery_deliverea/readme/CONFIGURE.rst new file mode 100644 index 0000000000..b11a5f9817 --- /dev/null +++ b/delivery_deliverea/readme/CONFIGURE.rst @@ -0,0 +1,20 @@ +To configure your Deliverea services, go to: + +- *Inventory > Configuration > Delivery methods* and create a new one. +- Choose *Deliverea* as provider. +- Configure your Deliverea credentials: user and password. +- You can configure the next fields: + * Deliverea production environment URL + * Deliverea test environment URL + * Deliverea tracking URL + * Deliverea Distribution Center + * Deliverea carrier + * Deliverea carrier service + * Deliverea articles description + * Select note field + * Notify by sms + * Notify by email + * Hide sender + * Return label + * Return proof delivery + * Saturday delivery diff --git a/delivery_deliverea/readme/CONTRIBUTORS.rst b/delivery_deliverea/readme/CONTRIBUTORS.rst new file mode 100644 index 0000000000..c15af61d9a --- /dev/null +++ b/delivery_deliverea/readme/CONTRIBUTORS.rst @@ -0,0 +1,6 @@ +* `FactorLibre `_: + + * Jorge Martínez + * Zahra Velasco + * Nacho Morales + * Oscar Indias diff --git a/delivery_deliverea/readme/CREDITS.rst b/delivery_deliverea/readme/CREDITS.rst new file mode 100644 index 0000000000..e69de29bb2 diff --git a/delivery_deliverea/readme/DESCRIPTION.rst b/delivery_deliverea/readme/DESCRIPTION.rst new file mode 100644 index 0000000000..e58647a323 --- /dev/null +++ b/delivery_deliverea/readme/DESCRIPTION.rst @@ -0,0 +1 @@ +Deliverea API intration with Odoo. diff --git a/delivery_deliverea/readme/HISTORY.rst b/delivery_deliverea/readme/HISTORY.rst new file mode 100644 index 0000000000..800d0ac660 --- /dev/null +++ b/delivery_deliverea/readme/HISTORY.rst @@ -0,0 +1,4 @@ +16.0.1.0.0 (2023-06-07) +~~~~~~~~~~~~~~~~~~~~~~~ + +* Start of the history diff --git a/delivery_deliverea/readme/INSTALL.rst b/delivery_deliverea/readme/INSTALL.rst new file mode 100644 index 0000000000..a8cdd61216 --- /dev/null +++ b/delivery_deliverea/readme/INSTALL.rst @@ -0,0 +1,8 @@ +This module needs the `unidecode` python library. + +It depends on the modules: + +* `base_delivery_carrier_label `_ repositorio OCA/delivery-carrier +* `delivery_package_number `_ repositorio OCA/delivery-carrier +* `delivery_state `_ repositorio OCA/delivery-carrier +* `queue_job `_ repositorio OCA/queue diff --git a/delivery_deliverea/readme/ROADMAP.rst b/delivery_deliverea/readme/ROADMAP.rst new file mode 100644 index 0000000000..b83542627d --- /dev/null +++ b/delivery_deliverea/readme/ROADMAP.rst @@ -0,0 +1 @@ +* No specified roadmap and no known problems in operation. diff --git a/delivery_deliverea/readme/USAGE.rst b/delivery_deliverea/readme/USAGE.rst new file mode 100644 index 0000000000..3d311fdcfa --- /dev/null +++ b/delivery_deliverea/readme/USAGE.rst @@ -0,0 +1,17 @@ +Shipping codes are generated when a stock pick is validated whose carrier is configured +for Deliverea as a supplier is validated. +for Deliverea as a supplier is validated. The label is generated and attached to the +the document according to the previously configured label generation parameters. + +In case you accidentally delete the attached label, you can generate it again by +clicking on the *Deliverea button. On the *Deliverea Label* button at the top of the +picking form. + +In order to select the Deliverea distribution center you have to click on the Import +distribution centers button, which will import all available distribution centers. + +In order to select the type of carrier and the type of service to be used for this +shipping method, click on the Import carriers and services button. + +Once the distribution centers and services are imported, we can select them in the +fields. diff --git a/delivery_deliverea/security/ir.model.access.csv b/delivery_deliverea/security/ir.model.access.csv new file mode 100644 index 0000000000..cfbe92eb0d --- /dev/null +++ b/delivery_deliverea/security/ir.model.access.csv @@ -0,0 +1,11 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_carrier_deliverea_stock_user,carrier.deliverea stock_user,model_carrier_deliverea,stock.group_stock_user,1,0,0,0 +access_carrier_deliverea_stock_manager,carrier.deliverea stock_manager,model_carrier_deliverea,stock.group_stock_manager,1,1,1,1 +access_deliverea_distribution_center_stock_user,deliverea.distribution.center stock_user,model_deliverea_distribution_center,stock.group_stock_user,1,0,0,0 +access_deliverea_distribution_center_stock_manager,deliverea.distribution.center stock_manager,model_deliverea_distribution_center,stock.group_stock_manager,1,1,1,1 +access_deliverea_state_stock_user,deliverea.state stock_user,model_deliverea_state,stock.group_stock_user,1,0,0,0 +access_deliverea_state_stock_manager,deliverea.state stock_manager,model_deliverea_state,stock.group_stock_manager,1,1,1,1 +access_carrier_deliverea_parameter_user,carrier.deliverea.parameter stock_user,model_carrier_deliverea_parameter,stock.group_stock_user,1,0,0,0 +access_carrier_deliverea_parameter_manager,carrier.deliverea.parameter stock_manager,model_carrier_deliverea_parameter,stock.group_stock_manager,1,1,1,1 +access_carrier_deliverea_service_user,carrier.deliverea.service stock_user,model_carrier_deliverea_service,stock.group_stock_user,1,0,0,0 +access_carrier_deliverea_service_manager,carrier.deliverea.service stock_manager,model_carrier_deliverea_service,stock.group_stock_manager,1,1,1,1 diff --git a/delivery_deliverea/static/description/icon.png b/delivery_deliverea/static/description/icon.png new file mode 100644 index 0000000000000000000000000000000000000000..2dcfd060ceb3fae1d5c9c4080a4f3a9d8740ff64 GIT binary patch literal 10390 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 literal 0 HcmV?d00001 diff --git a/delivery_deliverea/static/description/index.html b/delivery_deliverea/static/description/index.html new file mode 100644 index 0000000000..bacc212d48 --- /dev/null +++ b/delivery_deliverea/static/description/index.html @@ -0,0 +1,507 @@ + + + + + + +Delivery Deliverea + + + +
+

Delivery Deliverea

+ + +

Beta License: AGPL-3 OCA/delivery-carrier Translate me on Weblate Try me on Runboat

+

Deliverea API intration with Odoo.

+

Table of contents

+ +
+

Installation

+

This module needs the unidecode python library.

+

It depends on the modules:

+ +
+
+

Configuration

+

To configure your Deliverea services, go to:

+
    +
  • Inventory > Configuration > Delivery methods and create a new one.
  • +
  • Choose Deliverea as provider.
  • +
  • Configure your Deliverea credentials: user and password.
  • +
  • +
    You can configure the next fields:
    +
      +
    • Deliverea production environment URL
    • +
    • Deliverea test environment URL
    • +
    • Deliverea tracking URL
    • +
    • Deliverea Distribution Center
    • +
    • Deliverea carrier
    • +
    • Deliverea carrier service
    • +
    • Deliverea articles description
    • +
    • Select note field
    • +
    • Notify by sms
    • +
    • Notify by email
    • +
    • Hide sender
    • +
    • Return label
    • +
    • Return proof delivery
    • +
    • Saturday delivery
    • +
    +
    +
    +
  • +
+
+
+

Usage

+

Shipping codes are generated when a stock pick is validated whose carrier is configured +for Deliverea as a supplier is validated. +for Deliverea as a supplier is validated. The label is generated and attached to the +the document according to the previously configured label generation parameters.

+

In case you accidentally delete the attached label, you can generate it again by +clicking on the Deliverea button. On the *Deliverea Label button at the top of the +picking form.

+

In order to select the Deliverea distribution center you have to click on the Import +distribution centers button, which will import all available distribution centers.

+

In order to select the type of carrier and the type of service to be used for this +shipping method, click on the Import carriers and services button.

+

Once the distribution centers and services are imported, we can select them in the +fields.

+
+
+

Known issues / Roadmap

+
    +
  • No specified roadmap and no known problems in operation.
  • +
+
+
+

Changelog

+
+

16.0.1.0.0 (2023-06-07)

+
    +
  • Start of the history
  • +
+
+
+
+

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 to smash it by providing a detailed and welcomed +feedback.

+

Do not contact contributors directly about support or help with technical issues.

+
+
+

Credits

+
+

Authors

+
    +
  • FactorLibre
  • +
+
+ +
+

Maintainers

+

This module is maintained by the OCA.

+Odoo Community Association +

OCA, or the Odoo Community Association, is a nonprofit organization whose +mission is to support the collaborative development of Odoo features and +promote its widespread use.

+

This module is part of the OCA/delivery-carrier project on GitHub.

+

You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute.

+
+
+
+ + diff --git a/delivery_deliverea/tests/__init__.py b/delivery_deliverea/tests/__init__.py new file mode 100644 index 0000000000..9d58def635 --- /dev/null +++ b/delivery_deliverea/tests/__init__.py @@ -0,0 +1 @@ +from . import test_delivery_deliverea, test_webhook diff --git a/delivery_deliverea/tests/carrier_labels_data.py b/delivery_deliverea/tests/carrier_labels_data.py new file mode 100644 index 0000000000..cdd965869d --- /dev/null +++ b/delivery_deliverea/tests/carrier_labels_data.py @@ -0,0 +1,19 @@ +# fmt: off +LABEL_1_1 = { + "type": "zpl", + "content": "XlhBCl5DSTI4Cl5GTzQwLDQ4N15HQjczMiwzLDNeRlMKXkZPNDAsODY3XkdCNzMyLDMsM15GUwpeRk80MCw0Nl5BMCwyOF5GQjczMiwxLDAsQ15GRGR1bW15XCZeRlMKXkZPNjI1LDMzXkEwLDQ1XkZCOTk5OSwxLDAsTF5GRDI4MDM1XCZeRlMKXkZPNDAsMTEyXkdCNzMyLDMsM15GUwpeRk80MCwxODdeR0IzNjYsMywzXkZTCl5GTzQwNiwxMTJeR0IzLDI3NSwzXkZTCl5GTzQwLDM4N15HQjczMiwzLDNeRlMKXkZPNDAsOTJeQTAsMTdeRkI5OTk5LDEsMCxMXkZERGF0b3MgZGUgRW52w61vXCZeRlMKXkZPNDAsMTIyXkEwLDE3XkZCOTk5OSwxLDAsTF5GREFwZWxsaWRvcyB5IE5vbWJyZVwmXkZTCl5GTzQwLDE0Ml5BMCwyMF5GQjQwNiwyLDAsTF5GREpvcmdlXCZeRlMKXkZPNDAsMzQyXkEwLDE3XkZCOTk5OSwxLDAsTF5GRFRlbMOpZm9ub3M6XCZeRlMKXkZPNDAsMzYyXkEwLDIwXkZCOTk5OSwxLDAsTF5GRDEyMzQ1Njc4OVwmXkZTCl5GTzQwLDE5N15BMCwxN15GQjk5OTksMSwwLExeRkREb21pY2lsaW86XCZeRlMKXkZPNDAsMjE3XkEwLDIwXkZCNDA2LDUsMCxMXkZEU3RyZWV0IDFcJjI4MDM1IE1hZHJpZFwmTWFkcmlkXCZeRlMKXkZPNDI2LDEyMl5BMCwxN15GQjk5OTksMSwwLExeRkRGcmFuamEgaG9yYXJpYTpcJl5GUwpeRk80MjYsMTQyXkEwLDIwXkZCOTk5OSwxLDAsTF5GRCAgICAgICAtXCZeRlMKXkZPNDI2LDE3Ml5BMCwxN15GQjk5OTksMSwwLExeRkRSZWVtYm9sc286XCZeRlMKXkZPNDI2LDE5Ml5BMCw0MF5GQjM0NiwxLDAsQ15GRFNJXCZeRlMKXkZPNDI2LDIzMl5BMCwxN15GQjk5OTksMSwwLExeRkRQZWRpZG86XCZeRlMKXkZPNDI2LDI1Ml5BMCwyMF5GQjk5OTksMSwwLExeRkRXSC9PVVQvMDAwMDhcJl5GUwpeRk8yMTAsNTgwXkdGQSw1MjU2LDUyNTYsNTAsOlo2NDplSnp0eTdFTmdDQVlCV0VTQ2pwY3dJUTE3UDZWbklCSVhJeFJTRndBT3d2aWt6RXNycmpta2svU1dMTWJNYlFjZkRkZm5uVE9TaDFMDQpuZXZxbHU0M3VOMWEzSktrUXdnRUFvRkFJQkFJQkFLQlFDQVFDQVFDOFNQeEFiY3QzQlU9OmI0MDBeRlMKXkZPNDAsNzUwXkEwLDM1XkZCNzMyLDEsMCxDXkZEMDAwMDAwMDAwMDAwMDZcJl5GUwpeRk8xNDQsNTAwXkEwLDM1XkZCNTIwLDEsMCxDXkZEVEhJUyBJUyBBIERVTU1ZIExBQkVMIVwmXkZTCl5GTzI5MSwxMDYzXkdGQSwyODYyLDI4NjIsMjksOlo2NDplSno3Ly8vL0Q1azZoajk4N0EvcTdPVWIvOHQvcUxObi9zZnd1UEUvVU9ML3FPU281S2prcU9TbzVLamtxT1NvNUtqa3FPU29KQ0ZKDQpBSDJscFNNPTozZTkwXkZTCl5GTzQwLDExODBeQTAsMzVeRkI3MzIsMSwwLENeRkQwMDAzXCZeRlMKXlhaCg==", # noqa + "delivereaReference": "Secb189a5d61d32", + "carrierReference": "success:e92c6550-795d-4aac-8952-668daa5370e0", + "clientReference": "WH\\/OUT\\/00008", + "carrierCode": "dummy", +} + +LABEL_1_2 = { + "type": "zpl", + "content": "XlhBCl5DSTI4Cl5GTzQwLDQ4N15HQjczMiwzLDNeRlMKXkZPNDAsODY3XkdCNzMyLDMsM15GUwpeRk80MCw0Nl5BMCwyOF5GQjczMiwxLDAsQ15GRGR1bW15XCZeRlMKXkZPNjI1LDMzXkEwLDQ1XkZCOTk5OSwxLDAsTF5GRDI4MDM1XCZeRlMKXkZPNDAsMTEyXkdCNzMyLDMsM15GUwpeRk80MCwxODdeR0IzNjYsMywzXkZTCl5GTzQwNiwxMTJeR0IzLDI3NSwzXkZTCl5GTzQwLDM4N15HQjczMiwzLDNeRlMKXkZPNDAsOTJeQTAsMTdeRkI5OTk5LDEsMCxMXkZERGF0b3MgZGUgRW52w61vXCZeRlMKXkZPNDAsMTIyXkEwLDE3XkZCOTk5OSwxLDAsTF5GREFwZWxsaWRvcyB5IE5vbWJyZVwmXkZTCl5GTzQwLDE0Ml5BMCwyMF5GQjQwNiwyLDAsTF5GREpvcmdlXCZeRlMKXkZPNDAsMzQyXkEwLDE3XkZCOTk5OSwxLDAsTF5GRFRlbMOpZm9ub3M6XCZeRlMKXkZPNDAsMzYyXkEwLDIwXkZCOTk5OSwxLDAsTF5GRDEyMzQ1Njc4OVwmXkZTCl5GTzQwLDE5N15BMCwxN15GQjk5OTksMSwwLExeRkREb21pY2lsaW86XCZeRlMKXkZPNDAsMjE3XkEwLDIwXkZCNDA2LDUsMCxMXkZEU3RyZWV0IDFcJjI4MDM1IE1hZHJpZFwmTWFkcmlkXCZeRlMKXkZPNDI2LDEyMl5BMCwxN15GQjk5OTksMSwwLExeRkRGcmFuamEgaG9yYXJpYTpcJl5GUwpeRk80MjYsMTQyXkEwLDIwXkZCOTk5OSwxLDAsTF5GRCAgICAgICAtXCZeRlMKXkZPNDI2LDE3Ml5BMCwxN15GQjk5OTksMSwwLExeRkRSZWVtYm9sc286XCZeRlMKXkZPNDI2LDE5Ml5BMCw0MF5GQjM0NiwxLDAsQ15GRFNJXCZeRlMKXkZPNDI2LDIzMl5BMCwxN15GQjk5OTksMSwwLExeRkRQZWRpZG86XCZeRlMKXkZPNDI2LDI1Ml5BMCwyMF5GQjk5OTksMSwwLExeRkRXSC9PVVQvMDAwMDhcJl5GUwpeRk8yMTAsNTgwXkdGQSw1MjU2LDUyNTYsNTAsOlo2NDplSnp0eTdFSndDQVVCdUdBaFoxWklPQWE2ZDVLbVVBaVdjeFJCQmQ0WlFyaGoyT2t1T0thZzAvU1BNbzJVK3dsQnJkUTMveXNhcHQ3DQpXMnU0RGI5aWR1dnB6Skp1SVJBSUJBS0JRQ0FRQ0FRQ2dVQWdFQWpFajhRSGxQbzB3QT09OmUxNDleRlMKXkZPNDAsNzUwXkEwLDM1XkZCNzMyLDEsMCxDXkZEMDAwMDAwMDAwMDAwMDdcJl5GUwpeRk8xNDQsNTAwXkEwLDM1XkZCNTIwLDEsMCxDXkZEVEhJUyBJUyBBIERVTU1ZIExBQkVMIVwmXkZTCl5GTzI5MSwxMDYzXkdGQSwyODYyLDI4NjIsMjksOlo2NDplSno3Ly8vL0Q1azZoajk4N0EvcTdPVS8yRE4vL0dmUC9JL2hjZU4vb01UL1VjbFJ5VkhKVWNsUnlWSEpVY2xSeVZISlVjbFJTVUtTDQpBRnYxclBNPTo0MGRhXkZTCl5GTzQwLDExODBeQTAsMzVeRkI3MzIsMSwwLENeRkQwMDA0XCZeRlMKXlhaCg==", # noqa + "delivereaReference": "Secb189a5d61d32", + "carrierReference": "success:e92c6550-795d-4aac-8952-668daa5370e0", + "clientReference": "WH\\/OUT\\/00008", + "carrierCode": "dummy", +} +# fmt: on diff --git a/delivery_deliverea/tests/carrier_services_data.py b/delivery_deliverea/tests/carrier_services_data.py new file mode 100644 index 0000000000..b1255e9ddf --- /dev/null +++ b/delivery_deliverea/tests/carrier_services_data.py @@ -0,0 +1,2008 @@ +CARRIER_LIST = { + "data": [ + { + "distributionCenterId": "d19f3f7b-6c53-4a0d-940f-0003b8bea864", + "code": "dhl", + "costCenters": [ + { + "code": "DEFAULT", + "active": True, + "services": [ + {"code": "day-definite-eu", "active": True}, + {"code": "day-definite-intl", "active": True}, + {"code": "time-definite-canarias", "active": True}, + {"code": "time-definite-eu", "active": True}, + {"code": "time-definite-intl", "active": True}, + {"code": "time-definite-national", "active": False}, + ], + } + ], + }, + { + "distributionCenterId": "d19f3f7b-6c53-4a0d-940f-0003b8bea864", + "code": "gls", + "costCenters": [ + { + "code": "DEFAULT", + "active": True, + "services": [ + {"code": "gls-830", "active": True}, + {"code": "gls-courier-10", "active": True}, + {"code": "gls-courier-14", "active": True}, + {"code": "gls-courier-24", "active": True}, + {"code": "gls-economy", "active": True}, + {"code": "gls-euro-business-parcel", "active": True}, + {"code": "gls-maritimo", "active": False}, + ], + } + ], + }, + { + "distributionCenterId": "d19f3f7b-6c53-4a0d-940f-0003b8bea864", + "code": "tipsa", + "costCenters": [ + { + "code": "DEFAULT", + "active": True, + "services": [ + {"code": "tipsa-10-horas", "active": True}, + {"code": "tipsa-14-horas", "active": True}, + {"code": "tipsa-baleares", "active": True}, + {"code": "tipsa-canarias", "active": True}, + {"code": "tipsa-economy", "active": True}, + {"code": "tipsa-masivo", "active": True}, + ], + } + ], + }, + ], +} + +DHL_DETAILS = { + "active": True, + "code": "DEFAULT", + "contact": "", + "credentials": { + "account_number": "*****", + "code_client": "DELIVEREA", + "import_account_number": "*****", + "password": "*****", + }, + "integrationCode": "any-v1", + "labelRangeId": "", + "labelSource": "carrier", + "labelType": "zpl", + "needsPreDraft": True, + "services": [ + { + "active": True, + "code": "day-definite-eu", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "day-definite-intl", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "time-definite-canarias", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "time-definite-eu", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "time-definite-intl", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": False, + "code": "time-definite-national", + "expirationTime": {"time": 15, "unit": "days"}, + }, + ], +} + + +DHL_SERVICES = { + "code": "any-v1", + "carrierCode": "dhl", + "supportedCountries": ["ANY"], + "services": [ + { + "code": "time-definite-national", + "name": "Time Definite - Nacional", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "time-definite-eu", + "name": "Time Definite - Union Europea", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "time-definite-intl", + "name": "Time Definite - Internacional", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "day-definite-eu", + "name": "Day Definite - Union Europea", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "day-definite-intl", + "name": "Day Definite - Internacional", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "time-definite-canarias", + "name": "Time Definite - Canarias", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + ], + "supportedFeatures": ["documentExpedition", "getProofOfDelivery", "getLabel"], + "supportedLabels": [{"source": "carrier", "types": ["pdf"]}], + "credentialsFields": [ + {"key": "code_client", "label": "Id Cliente"}, + {"key": "password", "label": "Contraseña"}, + {"key": "account_number", "label": "Código de cuenta para exportaciones"}, + { + "key": "import_account_number", + "label": "Código de cuenta para importaciones", + }, + ], +} + +GLS_DETAILS = { + "active": True, + "code": "DEFAULT", + "contact": "", + "credentials": {"UIDClient": "6BAB7A53-3B6D-4D5A-9450-702D2FAC0B11"}, + "integrationCode": "es-v1", + "labelRangeId": "", + "labelSource": "carrier", + "labelType": "zpl", + "needsPreDraft": True, + "services": [ + { + "active": True, + "code": "gls-830", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "gls-courier-10", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "gls-courier-14", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "gls-courier-24", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "gls-economy", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "gls-euro-business-parcel", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": False, + "code": "gls-maritimo", + "expirationTime": {"time": 15, "unit": "days"}, + }, + ], +} + +GLS_SERVICES = { + "code": "es-v1", + "carrierCode": "gls", + "supportedCountries": ["ES"], + "services": [ + { + "code": "gls-courier-10", + "name": "COURIER entrega antes de las 10:00 ", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-courier-14", + "name": "COURIER entrega antes de las 14:00", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-courier-24", + "name": "COURIER entrega antes de las 20:00", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-economy", + "name": "ECONOMY", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-830", + "name": "Entrega a primera hora de la mañana", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-euro-business-parcel", + "name": "EUROBUSINESS SMALL PARCEL", + "description": "Este servicio solo acepta envíos monobulto", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "gls-maritimo", + "name": "MARÍTIMO", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + ], + "supportedFeatures": ["documentExpedition", "getLabel"], + "supportedLabels": [ + {"source": "carrier", "types": ["pdf"]}, + {"source": "label-engine", "types": ["pdf"]}, + ], + "credentialsFields": [{"key": "UIDClient", "label": "Id de cliente"}], +} + +TIPSA_DETAILS = { + "active": True, + "code": "DEFAULT", + "contact": "", + "credentials": { + "code_agency": "000000", + "code_client": "33333", + "pwd": "PR%20%18%", + }, + "integrationCode": "es-v1", + "labelRangeId": "", + "labelSource": "carrier", + "labelType": "zpl", + "needsPreDraft": True, + "services": [ + { + "active": True, + "code": "tipsa-10-horas", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "tipsa-14-horas", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "tipsa-baleares", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "tipsa-canarias", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "tipsa-economy", + "expirationTime": {"time": 15, "unit": "days"}, + }, + { + "active": True, + "code": "tipsa-masivo", + "expirationTime": {"time": 15, "unit": "days"}, + }, + ], +} + +TIPSA_SERVICES = { + "code": "es-v1", + "carrierCode": "tipsa", + "supportedCountries": ["ES"], + "services": [ + { + "code": "tipsa-10-horas", + "name": "Urgente 10 horas", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "tipsa-14-horas", + "name": "Urgente 14 Horas", + "description": "", + "type": "24", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "tipsa-economy", + "name": "Economy", + "description": "", + "type": "48", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "tipsa-masivo", + "name": "Masivo 48/72 Horas", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "tipsa-baleares", + "name": "Baleares Area/Maritima", + "description": "", + "type": "48", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + { + "code": "tipsa-canarias", + "name": "Canarias Maritima", + "description": "", + "type": "72", + "parameters": [ + { + "name": "senderGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientGeolocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "cashOnDelivery", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "dropPointKey", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "pickupTime", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "deliveryTime", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "notificationViaSMS", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "notificationViaEmail", + "necessity": {"type": "optional", "condition": None}, + }, + { + "name": "saturdayDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "includeReturnLabel", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "insuranceValue", + "necessity": {"type": "unsupported", "condition": None}, + }, + { + "name": "maxShippingDays", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "returnProofOfDelivery", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "hideSender", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "containerNumber", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermLocation", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "incotermZipCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "senderStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "recipientStateCode", + "necessity": {"type": "ignored", "condition": None}, + }, + { + "name": "parcelLabelBarcode", + "necessity": {"type": "ignored", "condition": None}, + }, + ], + }, + ], + "supportedFeatures": [ + "documentExpedition", + "cancelExpedition", + "getLabel", + "getProofOfDelivery", + "labellessPickup", + ], + "supportedLabels": [{"source": "carrier", "types": ["pdf"]}], + "credentialsFields": [ + {"key": "code_agency", "label": "Código de agencia"}, + {"key": "code_client", "label": "Id de cliente"}, + {"key": "pwd", "label": "Contraseña"}, + ], +} diff --git a/delivery_deliverea/tests/test_delivery_deliverea.py b/delivery_deliverea/tests/test_delivery_deliverea.py new file mode 100644 index 0000000000..b963329789 --- /dev/null +++ b/delivery_deliverea/tests/test_delivery_deliverea.py @@ -0,0 +1,430 @@ +# Copyright 2022 FactorLibre - Jorge Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import json +import urllib +from unittest import mock + +import requests + +from odoo.exceptions import UserError +from odoo.tests import common + +from ..models.deliverea_request import DelivereaRequest +from .carrier_labels_data import LABEL_1_1, LABEL_1_2 +from .carrier_services_data import ( + CARRIER_LIST, + DHL_DETAILS, + DHL_SERVICES, + GLS_DETAILS, + GLS_SERVICES, + TIPSA_DETAILS, + TIPSA_SERVICES, +) + +request_model = ( + "odoo.addons.delivery_deliverea.models.deliverea_request.DelivereaRequest" +) + + +class MockResponse(object): + def __init__(self, resp_data, code=200, msg="OK", **kwargs): + self.resp_data = resp_data + self.content = resp_data + self.status_code = code + self.msg = msg + self.ok = kwargs.get("ok", True) + self.headers = kwargs.get("headers", {}) + + def raise_for_status(self): + if self.status_code != 200: + raise urllib.error.HTTPError( + "", self.status_code, str(self.status_code), None, None + ) + + def read(self): + # pylint: disable=method-required-super + return self.resp_data + + def getcode(self): + return self.code + + def json(self): + return self.resp_data + + @property + def text(self): + return json.dumps(self.resp_data) + + +class TestDeliverea(common.TransactionCase): + def setUp(self): + super().setUp() + self.shipping_product = self.env.ref("delivery.product_product_local_delivery") + self.carrier_deliverea = self.env["carrier.deliverea"].create( + {"name": "Dummy", "code": "dummy"} + ) + distribution_center = self.env["deliverea.distribution.center"].create( + { + "name": "Default Distribution Center", + "uuid": "123", + } + ) + self.carrier = self.env["delivery.carrier"].create( + { + "name": "Deliverea", + "delivery_type": "deliverea", + "product_id": self.shipping_product.id, + "deliverea_carrier_id": self.carrier_deliverea.id, + "deliverea_username": "Test", + "deliverea_password": "Test", + "deliverea_distribution_center_id": distribution_center.id, + "deliverea_url_prod": "https://preapi.deliverea.com/v3/", + "deliverea_url_test": "https://preapi.deliverea.com/v3/", + "deliverea_default_packaging_id": self.env.ref( + "delivery_deliverea.stock_package_deliverea_default" + ).id, + "deliverea_description": "Description", + } + ) + self.carrier.deliverea_note_selection_id = self.env["ir.model.fields"].search( + [("model", "=", "stock.picking"), ("name", "=", "note")] + ) + self.service = self.env["carrier.deliverea.service"].create( + { + "name": "Standard", + "code": "standard", + "description": "Standard", + "carrier_id": [(4, self.carrier.id)], + } + ) + self.product = self.env["product.product"].create( + {"name": "Test Product", "type": "product"} + ) + self.partner = self.env["res.partner"].create( + { + "name": "Test Partner", + "street": "Street 1", + "street2": "Street 2", + "zip": "28035", + "city": "Madrid", + "country_id": self.env.ref("base.es").id, + "state_id": self.env.ref("base.state_es_m").id, + "phone": "123456789", + "email": "test@test.com", + } + ) + self.stock_location = self.env.ref("stock.stock_location_stock") + self.customer_location = self.env.ref("stock.stock_location_customers") + self.picking = self.env["stock.picking"].create( + { + "name": "Test Picking", + "partner_id": self.partner.id, + "move_ids": [ + ( + 0, + 0, + { + "product_id": self.product.id, + "product_uom_qty": 1, + "product_uom": self.product.uom_id.id, + "name": self.product.name, + "location_id": self.stock_location.id, + "location_dest_id": self.customer_location.id, + }, + ) + ], + "location_id": self.stock_location.id, + "location_dest_id": self.customer_location.id, + "picking_type_id": self.env.ref("stock.picking_type_out").id, + "carrier_id": self.carrier.id, + "number_of_packages": 2, + } + ) + self.env["stock.quant"].create( + { + "product_id": self.product.id, + "location_id": self.stock_location.id, + "quantity": 1, + } + ) + self.picking.action_assign() + + def test_00_get_services(self): + def _call_side_effect(*args, **kwargs): + if "integrations" in kwargs.get("url"): + if "dhl" in kwargs.get("url"): + return MockResponse(DHL_SERVICES, code=200) + if "gls" in kwargs.get("url"): + return MockResponse(GLS_SERVICES, code=200) + if "tipsa" in kwargs.get("url"): + return MockResponse(TIPSA_SERVICES, code=200) + elif "cost-centers" in kwargs.get("url"): + if "dhl" in kwargs.get("url"): + return MockResponse(DHL_DETAILS, code=200) + if "gls" in kwargs.get("url"): + return MockResponse(GLS_DETAILS, code=200) + if "tipsa" in kwargs.get("url"): + return MockResponse(TIPSA_DETAILS, code=200) + elif "carriers" in kwargs.get("url"): + return MockResponse(CARRIER_LIST, code=200) + + mock_call = mock.MagicMock(side_effect=_call_side_effect) + with mock.patch.object(requests, "get", new=mock_call): + self.carrier.deliverea_get_services() + self.assertEqual(self.env["carrier.deliverea"].search([], count=True), 4) + self.assertEqual( + self.env["carrier.deliverea.service"].search( + [("carrier_deliverea_id", "!=", False)], count=True + ), + 17, + ) + self.assertEqual( + self.env["carrier.deliverea.service"].search( + [("carrier_deliverea_id", "!=", False), ("active", "=", False)], + count=True, + ), + 0, + ) + self.assertNotEqual( + self.env["carrier.deliverea.service"] + .search( + [("carrier_deliverea_id", "!=", False), ("active", "=", False)], + limit=1, + ) + .code, + "time-definite-national", + ) + + def test_01_create_order(self): + create_shipment_value = { + "delivereaReference": "Sbe2fd53e94869d", + "carrierCode": "dummy", + "serviceCode": "standard", + "costCenterCode": "DEFAULT", + } + get_shipment_tracking_value = [ + { + "code": "01", + "message": "Pedido en preparación", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-02T13:00:54+0100", + "receivedAt": "2021-01-02T13:00:54+0100", + }, + { + "code": "11", + "message": "Recogido", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-03T13:00:54+0100", + "receivedAt": "2021-01-03T13:00:54+0100", + }, + ] + + def _call_side_effect(*args, **kwargs): + if "label" in kwargs.get("url"): + return MockResponse(LABEL_1_1, code=200) + elif "trackings" in kwargs.get("url"): + return MockResponse(get_shipment_tracking_value, code=200) + + mock_call = mock.MagicMock(side_effect=_call_side_effect) + requests_create_shipment = mock.MagicMock( + return_value=MockResponse(create_shipment_value, code=200) + ) + with mock.patch.object(requests, "post", new=requests_create_shipment): + with mock.patch.object(requests, "get", new=mock_call): + self.carrier.deliverea_send_shipping(self.picking) + self.assertEqual( + self.picking.deliverea_reference, + "Sbe2fd53e94869d", + ) + + def test_02_create_order_mandatory_field(self): + self.partner.zip = False + with self.assertRaises(UserError) as e: + self.carrier.deliverea_send_shipping(self.picking) + self.assertEqual( + "The value for zipCode field/s is mandatory in Test Partner", + e.exception.args[0], + ) + + def test_03_get_label(self): + get_shipment_tracking_value = [ + { + "code": "01", + "message": "Pedido en preparación", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-02T13:00:54+0100", + "receivedAt": "2021-01-02T13:00:54+0100", + }, + { + "code": "11", + "message": "Recogido", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-03T13:00:54+0100", + "receivedAt": "2021-01-03T13:00:54+0100", + }, + ] + + def _call_side_effect(*args, **kwargs): + if "label" in kwargs.get("url"): + return MockResponse(LABEL_1_2, code=200) + elif "trackings" in kwargs.get("url"): + return MockResponse(get_shipment_tracking_value, code=200) + + mock_call = mock.MagicMock(side_effect=_call_side_effect) + + with mock.patch.object(requests, "get", new=mock_call): + self.picking.deliverea_reference = "Secb189a5d61d32" + self.picking.deliverea_parcel_client_code = "0001,0002" + self.carrier.deliverea_get_label(self.picking) + attachment = self.env["ir.attachment"].search( + [ + ("res_model", "=", "stock.picking"), + ("res_id", "=", self.picking.id), + ] + ) + self.assertTrue(attachment) + + def test_04_create_return_and_delete(self): + return_value = { + "delivereaReference": "Sbe2fd53e94869d", + "carrierCode": "dummy", + "serviceCode": "standard", + "costCenterCode": "DEFAULT", + } + requests_mock_call = mock.MagicMock( + return_value=MockResponse(return_value, code=200) + ) + with mock.patch.object(requests, "post", new=requests_mock_call): + self.carrier.deliverea_return_shipping(self.picking) + self.assertEqual( + self.picking.deliverea_reference, + "Sbe2fd53e94869d", + ) + requests_mock_call = mock.MagicMock( + return_value=MockResponse("DELETE", code=200) + ) + with mock.patch.object(requests, "delete", new=requests_mock_call): + self.carrier.deliverea_cancel_shipment(self.picking) + requests_mock_call.assert_called() + + def test_05_get_tracking_shipment(self): + return_value = [ + { + "code": "01", + "message": "Pedido en preparación", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-02T13:00:54+0100", + "receivedAt": "2021-01-02T13:00:54+0100", + }, + { + "code": "11", + "message": "Recogido", + "carrierCode": None, + "category": "documented", + "carrierMessage": None, + "occurredAt": "2021-01-03T13:00:54+0100", + "receivedAt": "2021-01-03T13:00:54+0100", + }, + ] + requests_mock_call = mock.MagicMock( + return_value=MockResponse(return_value, code=200) + ) + with mock.patch.object(requests, "get", new=requests_mock_call): + self.carrier.deliverea_tracking_state_update(self.picking) + self.assertEqual(self.picking.tracking_state, False) + self.assertEqual(self.picking.tracking_state_history, False) + self.picking.deliverea_reference = "Sbe2fd53e94869d" + self.carrier.deliverea_tracking_state_update(self.picking) + self.assertEqual(self.picking.tracking_state, "[11] Recogido") + self.assertEqual( + self.picking.tracking_state_history, + "- 2021-01-02T13:00:54+0100: [01] Pedido en preparación\n" + "- 2021-01-03T13:00:54+0100: [11] Recogido", + ) + self.assertEqual(self.picking.delivery_state, "customer_delivered") + self.assertTrue(self.picking.date_delivered) + + def test_06_create_return(self): + return_value = { + "delivereaReference": "Sbe2fd53e94869d", + "carrierCode": "dummy", + "serviceCode": "standard", + "costCenterCode": "DEFAULT", + } + requests_mock_call = mock.MagicMock( + return_value=MockResponse(return_value, code=200) + ) + with mock.patch.object(requests, "post", new=requests_mock_call): + self.carrier.deliverea_return_shipping(self.picking) + self.assertEqual( + self.picking.deliverea_reference, + "Sbe2fd53e94869d", + ) + requests_mock_call = mock.MagicMock( + return_value=MockResponse("DELETE", code=200) + ) + with mock.patch.object(requests, "delete", new=requests_mock_call): + self.carrier.deliverea_cancel_shipment(self.picking) + requests_mock_call.assert_called() + + def test_07_get_exception(self): + with self.assertRaises(UserError): + DelivereaRequest._send_api_request( + DelivereaRequest, request_type="TEST", url="TEST", skip_auth=True + ) + + def test_08_get_exception(self): + requests_mock_call = mock.MagicMock(return_value=MockResponse("Test", code=202)) + with mock.patch.object(requests, "get", new=requests_mock_call): + res = DelivereaRequest._send_api_request( + DelivereaRequest, request_type="GET", url="TEST", skip_auth=True + ) + self.assertTrue(res) + + def test_9_get_exception(self): + requests_mock_call = mock.MagicMock( + return_value=MockResponse( + { + "error": { + "code": "error_code", + "message": "code_message", + "detail": "error_detail", + } + }, + code=300, + ) + ) + with mock.patch.object(requests, "get", new=requests_mock_call): + with mock.patch.object( + DelivereaRequest, "_check_error", return_value=True + ) as mock_error: + DelivereaRequest._send_api_request( + DelivereaRequest, request_type="GET", url="TEST", skip_auth=True + ) + mock_error.assert_called() + + def test_10_get_exception(self): + return_value = { + "error": { + "code": "error_code", + "message": "code_message", + "detail": "error_detail", + } + } + with self.assertRaises(UserError): + DelivereaRequest._check_error(DelivereaRequest, return_value) + + def test_11_get_exception(self): + return_value = {"Error test": "Error"} + with self.assertRaises(UserError): + DelivereaRequest._check_error(DelivereaRequest, return_value) diff --git a/delivery_deliverea/tests/test_webhook.py b/delivery_deliverea/tests/test_webhook.py new file mode 100644 index 0000000000..8e7b9deb52 --- /dev/null +++ b/delivery_deliverea/tests/test_webhook.py @@ -0,0 +1,50 @@ +# © 2023 - FactorLibre - Oscar Indias +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). +import json + +from odoo.tests import common + +from ..controllers.http import JsonRPCDispatcher +from ..controllers.main import DelivereaWebhook +from .tools import MockRequest + + +class TestWebhook(common.TransactionCase): + def setUp(self): + super().setUp() + self.full_url = "/deliverea-tracking-webhook" + self.headers = { + "Content-Type": "application/json", + "environ": { + "REQUEST_METHOD": "POST", + }, + } + self.params = { + "test": "test", + } + + def test_01(self): + request = MockRequest( + self.env, headers=self.headers, data=self.params, path=self.full_url + ) + dispatcher = JsonRPCDispatcher(request.request) + data = dispatcher.request.httprequest.data + if isinstance(data, bytes): + data = data.decode() + self.assertTrue("data" in data) + data = json.loads(data) + self.assertNotEqual(data, self.params) + self.assertEqual(data.get("data"), self.params) + + def test_02(self): + request = MockRequest( + self.env, headers=self.headers, data=self.params, path=self.full_url + ) + dispatcher = JsonRPCDispatcher(request.request) + req = MockRequest(self.env) + req.request = dispatcher.request + jobs = self.env["queue.job"].search_count([]) + with req: + DelivereaWebhook.order_import_webhook(DelivereaWebhook) + jobs_after_execute = self.env["queue.job"].search_count([]) + self.assertTrue(jobs_after_execute > jobs) diff --git a/delivery_deliverea/tests/tools.py b/delivery_deliverea/tests/tools.py new file mode 100644 index 0000000000..4f17141b39 --- /dev/null +++ b/delivery_deliverea/tests/tools.py @@ -0,0 +1,103 @@ +# Part of Odoo. See LICENSE file for full copyright and licensing details. +import json + +import werkzeug + +import odoo + + +class DotDict(dict): + """Helper for dot.notation access to dictionary attributes + E.g. + foo = DotDict({'bar': False}) + return foo.bar + """ + + def __getattr__(self, attrib): + val = self.get(attrib) + return DotDict(val) if type(val) is dict else val + + # pylint: disable=missing-return + def __setattr__(self, key, value): + self.__setitem__(key, value) + + # pylint: disable=missing-return + def __setitem__(self, key, value): + super(DotDict, self).__setitem__(key, value) + self.__dict__.update({key: value}) + + +class MockObject(object): + _log_call = [] + + def __init__(self, *args, **kwargs): + self.__dict__ = kwargs + + +class MockRequest(object): + """Class with context manager mocking odoo.http.request for tests""" + + def __init__(self, env, **kw): + app = MockObject( + routing={ + "type": "http", + "website": True, + "multilang": kw.get("multilang", True), + } + ) + app.get_db_router = app.bind = app.match = app + + lang = kw.get("lang") + if not lang: + lang_code = kw.get("context", {}).get( + "lang", env.context.get("lang", "en_US") + ) + lang = env["res.lang"]._lang_get(lang_code) + + context = kw.get("context", {}) + context.setdefault("lang", lang_code) + + httprequest_data = b"" + if kw.get("data", {}): + httprequest_data = json.dumps(kw.get("data", {})).encode() + self.request = DotDict( + { + "context": context, + "db": None, + "env": env, + "jsonrequest": kw.get("data", {}), + "httprequest": DotDict( + { + "path": kw.get("path", "/hello/"), + "app": app, + "environ": { + "REMOTE_ADDR": "127.0.0.1", + }, + "cookies": kw.get("cookies", {}), + "user_agent": DotDict({"browser": ""}), + "headers": kw.get("headers", {}), + "data": httprequest_data, + "charset": "utf-8", + } + ), + "lang": lang, + "redirect": werkzeug.utils.redirect, + "session": { + "geoip": { + "country_code": kw.get("country_code"), + }, + "debug": False, + "sale_order_id": kw.get("sale_order_id"), + }, + "website": kw.get("website"), + # partial(HttpRequest.render, HttpRequest), + "render": lambda *args, **kwargs: b"", + } + ) + + def __enter__(self): + odoo.http._request_stack.push(self.request) + return self.request + + def __exit__(self, exc_type, exc_value, traceback): + odoo.http._request_stack.pop() diff --git a/delivery_deliverea/views/deliverea_distribution_center_views.xml b/delivery_deliverea/views/deliverea_distribution_center_views.xml new file mode 100644 index 0000000000..2ed0aa47c3 --- /dev/null +++ b/delivery_deliverea/views/deliverea_distribution_center_views.xml @@ -0,0 +1,40 @@ + + + + delivery.deliverea.distribution.center.view_form + deliverea.distribution.center + +
+ + + + + + + + + + + + + + + + + + +
+
+
+ + + Deliverea Distribution Center + ir.actions.act_window + deliverea.distribution.center + tree,form + + +
diff --git a/delivery_deliverea/views/deliverea_state_mapping.xml b/delivery_deliverea/views/deliverea_state_mapping.xml new file mode 100644 index 0000000000..210e02b1cf --- /dev/null +++ b/delivery_deliverea/views/deliverea_state_mapping.xml @@ -0,0 +1,29 @@ + + + + delivery.deliverea.state.mapping.tree + deliverea.state + + + + + + + + + + + Deliverea State Mapping + ir.actions.act_window + deliverea.state + tree,form + + + + diff --git a/delivery_deliverea/views/delivery_carrier_views.xml b/delivery_deliverea/views/delivery_carrier_views.xml new file mode 100644 index 0000000000..c25018907a --- /dev/null +++ b/delivery_deliverea/views/delivery_carrier_views.xml @@ -0,0 +1,121 @@ + + + + delivery.carrier + delivery_base.delivery.carrier.view_form + + + + + + + + + + + + + + + + + +
+

You will need to import the first time you use Deliverea or when you want to update these fields.

+

Attention! Failure to set these fields will not allow you to ship successfully.

+
+
+ +