diff --git a/delivery_dhl_parcel/README.rst b/delivery_dhl_parcel/README.rst new file mode 100644 index 00000000000..4a2b99a2d26 --- /dev/null +++ b/delivery_dhl_parcel/README.rst @@ -0,0 +1,206 @@ +=================== +Delivery DHL Parcel +=================== + +.. + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! This file is generated by oca-gen-addon-readme !! + !! changes will be overwritten. !! + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + !! source digest: sha256:deb7163f8fec3652201c513624bce833464d1d2b727b06d60eae2b832ea90b36 + !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! + +.. |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%2Fl10n--spain-lightgray.png?logo=github + :target: https://github.com/OCA/l10n-spain/tree/17.0/delivery_dhl_parcel + :alt: OCA/l10n-spain +.. |badge4| image:: https://img.shields.io/badge/weblate-Translate%20me-F47D42.png + :target: https://translation.odoo-community.org/projects/l10n-spain-17-0/l10n-spain-17-0-delivery_dhl_parcel + :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/l10n-spain&target_branch=17.0 + :alt: Try me on Runboat + +|badge1| |badge2| |badge3| |badge4| |badge5| + +Este módulo integra la API de DHL Parcel con Odoo. No es válido para +otras integraciones de DHL como el Express, que usan otras APIs. + +IMPORTANTE: Hay varias APIs distintas para DHL España y DHL Parcel, esta +API solo funciona para una de ellas. Para notificar y pedir las +credenciales correctas a tu comercial de integración, se puede pasar el +documento que hay en /doc para indicar exactamente cuál es la API que se +quiere usar. El formato de las credenciales que se usan en esta es el +siguiente: + +Usuario: 11000000EMPRESA Pass: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +Puede que acepte algún otro, pero no puedo verificarlo. + +**Table of contents** + +.. contents:: + :local: + +Installation +============ + +Depende de los módulos de OCA/delivery-carrier delivery_package_number y +delivery_state. + +La API de DHL Parcel no provee métodos de cálculo de precio, de modo que +para poder calcular los costes de envío sería recomendable instalar el +módulo delivery_price_method. + +Configuration +============= + +Para configurar el transportista: + +1. Vaya a *Inventario > Configuración > Entrega > Método de envío* y + cree uno nuevo. +2. Escoja *DHL Parcel* Como proveedor. +3. Configure los datos de servicio que tiene contratados y el producto + de envío que desea utilizar. + +El código de cliente es el UID con un guión y sin letras al final i.e. +UID = 08100000AB, tu Código de cliente sería 08-100000 + +Usage +===== + +Estas son las distintas operaciones posibles con este módulo: + +Grabar servicios +---------------- + + 1. Al confirmar el albarán, el servicio se grabará en DHL Parcel. + 2. Con la respuesta, se registrará en el chatter la referencia de + envío y las etiquetas correspondientes. + 3. Para gestionar los bultos del envío, se puede utilizar el campo de + número de bultos que añade delivery_package_number (ver el README + para mayor información) o bien el flujo nativo de Odoo con + paquetes de envío. El módulo mandará a la API de DHL Parcel el + número correspondiente y podremos descargar las etiquetas en PDF + con su correspondiente numeración. + +Cancelar servicios +------------------ + + 1. Al igual que en otros métodos de envío, en los albaranes de salida + podemos cancelar un servicio determinado mediante la acción + correspondiente en la pestaña de *Información Adicional*, sección + *Información de entrega* una vez el pedido esté confirmado y la + expedición generada. + 2. Podremos generar una nueva expedición una vez cancelado si fuese + necesario. + +Obtener etiquetas +----------------- + + 1. Si por error hubiésemos eliminado el adjunto de las etiquetas que + obtuvimos en la grabación del servicio, podemos obtenerlas de + nuevo pulsando en el botón "Etiqueta DHL Parcel" que tenemos en la + parte superior de la vista formulario del albarán. + +Seguimiento de envíos +--------------------- + + 1. El módulo está integrado con delivery_state para poder recabar la + información de seguimiento de nuestros envíos directamente desde + la API de DHL Parcel. + 2. Para ello, vaya al albarán con un envío DHL Parcel ya grabado y en + la pestaña de *Información adicional* verá el botón *Actualizar + seguimiento* para pedir a la API que actualice el estado de este + envío en Odoo. + +Cierre del día +-------------- + + 1. Desde un smart button en el formulario del transportista, se puede + cerrar el día manualmente. + +Depuración de errores +--------------------- + + 1. Es importante tener en cuenta que solo funcionará con códigos + postales de España (por lo menos para el consignatario). + 2. También puede activar Odoo con --log-level=debug para registrar + las peticiones y las respuestas en el log. + +Contrareembolso +--------------- + + 1. Al crear un envío con un método que tiene marcado el check de + contrareembolso, asumirá que hay un pedido vinculado al albarán y + usará el importe total del pedido como importe del + contrareembolso. Métodos de envío con este check no se encontrarán + disponibles en la web si se está usando. + +Known issues / Roadmap +====================== + +- La API no facilita métodos para cotizar el coste real de los envíos, + por lo que siempre se cotizan a 0. Si la cotización de envíos es + necesaria, puede instalarse el módulo OCA delivery_price_method o + bien personalizar el método de cotización para este tipo de + transportista. + +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 +------- + +* Studio73 + +Contributors +------------ + +- `Studio73 `__: + + - Ethan Hildick + +- `Tecnativa `__: + + - Víctor Martínez + +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. + +.. |maintainer-hildickethan-S73| image:: https://github.com/hildickethan-S73.png?size=40px + :target: https://github.com/hildickethan-S73 + :alt: hildickethan-S73 + +Current `maintainer `__: + +|maintainer-hildickethan-S73| + +This module is part of the `OCA/l10n-spain `_ project on GitHub. + +You are welcome to contribute. To learn how please visit https://odoo-community.org/page/Contribute. diff --git a/delivery_dhl_parcel/__init__.py b/delivery_dhl_parcel/__init__.py new file mode 100644 index 00000000000..9b4296142f4 --- /dev/null +++ b/delivery_dhl_parcel/__init__.py @@ -0,0 +1,2 @@ +from . import models +from . import wizard diff --git a/delivery_dhl_parcel/__manifest__.py b/delivery_dhl_parcel/__manifest__.py new file mode 100644 index 00000000000..3603781d145 --- /dev/null +++ b/delivery_dhl_parcel/__manifest__.py @@ -0,0 +1,21 @@ +# Copyright 2021 Studio73 - Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +{ + "name": "Delivery DHL Parcel", + "summary": "Delivery Carrier implementation for DHL Parcel using their API", + "version": "17.0.2.0.0", + "category": "Stock", + "website": "https://github.com/OCA/l10n-spain", + "author": "Studio73, Odoo Community Association (OCA)", + "license": "AGPL-3", + "application": False, + "installable": True, + "depends": ["delivery_package_number", "delivery_state"], + "data": [ + "security/ir.model.access.csv", + "views/delivery_carrier_view.xml", + "views/stock_picking_views.xml", + "wizard/dhl_parcel_end_day_wizard_views.xml", + ], + "maintainers": ["hildickethan-S73"], +} diff --git a/delivery_dhl_parcel/doc/Documento_integracion_WEB_SERVICE_B2B_B2C_ver6.pdf b/delivery_dhl_parcel/doc/Documento_integracion_WEB_SERVICE_B2B_B2C_ver6.pdf new file mode 100644 index 00000000000..49cf3762758 Binary files /dev/null and b/delivery_dhl_parcel/doc/Documento_integracion_WEB_SERVICE_B2B_B2C_ver6.pdf differ diff --git a/delivery_dhl_parcel/i18n/ca.po b/delivery_dhl_parcel/i18n/ca.po new file mode 100644 index 00000000000..4eea3619665 --- /dev/null +++ b/delivery_dhl_parcel/i18n/ca.po @@ -0,0 +1,320 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * delivery_dhl_parcel +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2022-02-10 12:04+0000\n" +"Last-Translator: Dept. Técnico \n" +"Language-Team: none\n" +"Language: ca\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.3.2\n" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__all_customer_accounts +msgid "All customer accounts" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2b +msgid "B2B Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2c +msgid "B2C Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "Cancel" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "Cash on delivery" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "Customer codes to end day for" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__delivery_type__dhl_parcel +msgid "DHL Parcel" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel API doesn't provide methods to compute delivery rates, so\n" +" you should rely on another price method instead or override " +"this\n" +" one in your custom code." +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Cancellation failed with reason: %s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "DHL Parcel Configuration" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel End Day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "DHL Parcel End day" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Expedition with reference %s cancelled" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "DHL Parcel Label" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_password +msgid "DHL Parcel Password" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__carrier_id +msgid "DHL Parcel Service" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel Shipping extra info:\n" +"Origin: {}, Customer: {}, AWB: {}, LP: {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_uid +msgid "DHL Parcel UID" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_customer_code +msgid "DHL Parcel customer code" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_incoterm +msgid "DHL Parcel incoterms" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "DHL Parcel label for %s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report +msgid "DHL Parcel last manual end day report" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__dhl_parcel_shipment_held +msgid "DHL Parcel shipment on hold" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "DHL Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__epl +msgid "EPL" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "End day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report_name +msgid "Filename" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Held shipment for {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Hold DHL Parcel shipment" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__id +msgid "ID" +msgstr "ID" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "" +"If checked, it means that the carrier is paid with cash. It assumes there is " +"a sale order linked and it will use that total amount as the value to be " +"paid. It will also exclude this carrier from the e-commerce checkout." +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "" +"If doing multiple, input them separated by commas without spaces.\n" +"i.e. '001-000001,002-000002'\n" +"You can also use 'ALL' to end all of them" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "If the product is not specified, it is considered B2B" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_label_format +msgid "Label format" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_dhl_parcel_endday_wizard +msgid "Manually end the day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__exw +msgid "Owed transport" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__pdf +msgid "PDF" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__cpt +msgid "Paid transport" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__delivery_type +msgid "Provider" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Release DHL Parcel shipment" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Released shipment for {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Server not reachable, please try again later" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_delivery_carrier +msgid "Shipping Methods" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Timeout: the server did not reply within 60s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Unsupported request type, please only use 'GET' or 'POST'" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__zpl +msgid "ZPL" +msgstr "" diff --git a/delivery_dhl_parcel/i18n/delivery_dhl_parcel.pot b/delivery_dhl_parcel/i18n/delivery_dhl_parcel.pot new file mode 100644 index 00000000000..01792da62d8 --- /dev/null +++ b/delivery_dhl_parcel/i18n/delivery_dhl_parcel.pot @@ -0,0 +1,317 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * delivery_dhl_parcel +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \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_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__all_customer_accounts +msgid "All customer accounts" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2b +msgid "B2B Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2c +msgid "B2C Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "Cancel" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "Cash on delivery" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_uid +msgid "Created by" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_date +msgid "Created on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "Customer codes to end day for" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__delivery_type__dhl_parcel +msgid "DHL Parcel" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel API doesn't provide methods to compute delivery rates, so\n" +" you should rely on another price method instead or override this\n" +" one in your custom code." +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Cancellation failed with reason: %s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "DHL Parcel Configuration" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel End Day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "DHL Parcel End day" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Expedition with reference %s cancelled" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "DHL Parcel Label" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_password +msgid "DHL Parcel Password" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__carrier_id +msgid "DHL Parcel Service" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel Shipping extra info:\n" +"Origin: {}, Customer: {}, AWB: {}, LP: {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_uid +msgid "DHL Parcel UID" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_customer_code +msgid "DHL Parcel customer code" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_incoterm +msgid "DHL Parcel incoterms" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "DHL Parcel label for %s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report +msgid "DHL Parcel last manual end day report" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__dhl_parcel_shipment_held +msgid "DHL Parcel shipment on hold" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "DHL Product" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__display_name +msgid "Display Name" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__epl +msgid "EPL" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "End day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report_name +msgid "Filename" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Held shipment for {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Hold DHL Parcel shipment" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__id +msgid "ID" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "" +"If checked, it means that the carrier is paid with cash. It assumes there is" +" a sale order linked and it will use that total amount as the value to be " +"paid. It will also exclude this carrier from the e-commerce checkout." +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "" +"If doing multiple, input them separated by commas without spaces.\n" +"i.e. '001-000001,002-000002'\n" +"You can also use 'ALL' to end all of them" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "If the product is not specified, it is considered B2B" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_label_format +msgid "Label format" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_date +msgid "Last Updated on" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_dhl_parcel_endday_wizard +msgid "Manually end the day" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__exw +msgid "Owed transport" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__pdf +msgid "PDF" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__cpt +msgid "Paid transport" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__delivery_type +msgid "Provider" +msgstr "" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Release DHL Parcel shipment" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Released shipment for {}" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_sale_order +msgid "Sales Order" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Server not reachable, please try again later" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_delivery_carrier +msgid "Shipping Methods" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Timeout: the server did not reply within 60s" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_stock_picking +msgid "Transfer" +msgstr "" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Unsupported request type, please only use 'GET' or 'POST'" +msgstr "" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__zpl +msgid "ZPL" +msgstr "" diff --git a/delivery_dhl_parcel/i18n/es.po b/delivery_dhl_parcel/i18n/es.po new file mode 100644 index 00000000000..8b63824eca3 --- /dev/null +++ b/delivery_dhl_parcel/i18n/es.po @@ -0,0 +1,335 @@ +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * delivery_dhl_parcel +# +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 14.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-07-15 08:58+0000\n" +"PO-Revision-Date: 2023-11-28 19:34+0000\n" +"Last-Translator: Ivorra78 \n" +"Language-Team: \n" +"Language: es\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 4.17\n" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__all_customer_accounts +msgid "All customer accounts" +msgstr "Todas las cuentas de clientes" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2b +msgid "B2B Product" +msgstr "Producto B2B" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_product__b2c +msgid "B2C Product" +msgstr "Producto B2C" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "Cancel" +msgstr "Cancelar" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "Cash on delivery" +msgstr "Contra reembolso" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_uid +msgid "Created by" +msgstr "Creado por" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__create_date +msgid "Created on" +msgstr "Creado el" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "Customer codes to end day for" +msgstr "Códigos de cliente para cerrar el día" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__delivery_type__dhl_parcel +msgid "DHL Parcel" +msgstr "Paquete DHL" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel API doesn't provide methods to compute delivery rates, so\n" +" you should rely on another price method instead or override " +"this\n" +" one in your custom code." +msgstr "" +"La API de DHL Parcel no proporciona métodos para calcular las tarifas de " +"entrega, por lo que\n" +" por lo que debe utilizar otro método de cálculo de precios " +"o\n" +" en su código personalizado." + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Cancellation failed with reason: %s" +msgstr "Cancelación DHL Parcel fallado por: %s" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "DHL Parcel Configuration" +msgstr "Configuración DHL Parcel" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel End Day" +msgstr "Cerrar el día (DHL Parcel)" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +msgid "DHL Parcel End day" +msgstr "Cerrar el día (DHL Parcel)" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "DHL Parcel Expedition with reference %s cancelled" +msgstr "Expedición DHL Parcel con referencia %s cancelado" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "DHL Parcel Label" +msgstr "Etiqueta DHL Parcel" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_password +msgid "DHL Parcel Password" +msgstr "Contraseña DHL Parcel" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__carrier_id +msgid "DHL Parcel Service" +msgstr "Servicio DHL Parcel" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/delivery_carrier.py:0 +#, python-format +msgid "" +"DHL Parcel Shipping extra info:\n" +"Origin: {}, Customer: {}, AWB: {}, LP: {}" +msgstr "" +"Información adicional del envío DHL Parcel:\n" +"Orígen: {}, Cliente: {}, AWB: {}, LP: {}" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_uid +msgid "DHL Parcel UID" +msgstr "UID DHL Parcel" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_customer_code +msgid "DHL Parcel customer code" +msgstr "Código de cliente DHL Parcel" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_incoterm +msgid "DHL Parcel incoterms" +msgstr "Servicio DHL Parcel" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "DHL Parcel label for %s" +msgstr "Etiqueta DHL Parcel para %s" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report +msgid "DHL Parcel last manual end day report" +msgstr "Último informe DHL Parcel de cierro de día" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__dhl_parcel_shipment_held +msgid "DHL Parcel shipment on hold" +msgstr "Envío DHL Parcel retenido" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "DHL Product" +msgstr "Producto DHL" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__display_name +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__display_name +msgid "Display Name" +msgstr "Nombre mostrado" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__epl +msgid "EPL" +msgstr "EPL" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.delivery_endday_wizard_form +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_delivery_carrier_form +msgid "End day" +msgstr "Cerrar el día" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_last_end_day_report_name +msgid "Filename" +msgstr "Nombre fichero" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Held shipment for {}" +msgstr "Envío {} retenido" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Hold DHL Parcel shipment" +msgstr "Retener envío DHL Parcel" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order__id +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking__id +msgid "ID" +msgstr "ID" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_cash_on_delivery +msgid "" +"If checked, it means that the carrier is paid with cash. It assumes there is " +"a sale order linked and it will use that total amount as the value to be " +"paid. It will also exclude this carrier from the e-commerce checkout." +msgstr "" +"Si está marcado, significa que el transportista se paga a la hora de " +"entregar. Asume que hay un pedido de venta relacionado del cual usará la " +"cantidad total como el valor a pagar. También excluirá este método de envío " +"de los disponibles en la web." + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__customer_accounts +msgid "" +"If doing multiple, input them separated by commas without spaces.\n" +"i.e. '001-000001,002-000002'\n" +"You can also use 'ALL' to end all of them" +msgstr "" +"Si se están cerrando vários, introducelos separados por comas sin espacios\n" +"i.e. '001-000001,002-000002'\n" +"También se puede usar 'ALL' para cerrarlos todos" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,help:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_product +msgid "If the product is not specified, it is considered B2B" +msgstr "Si no se especifica el producto, se considera producto B2B" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__dhl_parcel_label_format +msgid "Label format" +msgstr "Formato de etiqueta" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_sale_order____last_update +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_stock_picking____last_update +msgid "Last Modified on" +msgstr "Última modificación el" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_uid +msgid "Last Updated by" +msgstr "Última modificación por" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_dhl_parcel_endday_wizard__write_date +msgid "Last Updated on" +msgstr "Última modificación el" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_dhl_parcel_endday_wizard +msgid "Manually end the day" +msgstr "Cerrar el día manualmente" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__exw +msgid "Owed transport" +msgstr "Transporte adeudado" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__pdf +msgid "PDF" +msgstr "PDF" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_incoterm__cpt +msgid "Paid transport" +msgstr "Transporte pagado" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields,field_description:delivery_dhl_parcel.field_delivery_carrier__delivery_type +msgid "Provider" +msgstr "Proveedor" + +#. module: delivery_dhl_parcel +#: model_terms:ir.ui.view,arch_db:delivery_dhl_parcel.view_picking_withcarrier_out_form +msgid "Release DHL Parcel shipment" +msgstr "Liberar envío DHL Parcel" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/stock_picking.py:0 +#, python-format +msgid "Released shipment for {}" +msgstr "Envío {} liberado" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_sale_order +msgid "Sales Order" +msgstr "Orden de venta" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Server not reachable, please try again later" +msgstr "El servidor no responde, por favor vuelve a intentarlo más tarde" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_delivery_carrier +msgid "Shipping Methods" +msgstr "Métodos de envío" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Timeout: the server did not reply within 60s" +msgstr "El servidor no ha contestado en 60s" + +#. module: delivery_dhl_parcel +#: model:ir.model,name:delivery_dhl_parcel.model_stock_picking +msgid "Transfer" +msgstr "Albarán" + +#. module: delivery_dhl_parcel +#: code:addons/delivery_dhl_parcel/models/dhl_parcel_request.py:0 +#, python-format +msgid "Unsupported request type, please only use 'GET' or 'POST'" +msgstr "Tipo de respuesta no soportado, por favor solo usa 'GET' o 'POST'" + +#. module: delivery_dhl_parcel +#: model:ir.model.fields.selection,name:delivery_dhl_parcel.selection__delivery_carrier__dhl_parcel_label_format__zpl +msgid "ZPL" +msgstr "ZPL" diff --git a/delivery_dhl_parcel/models/__init__.py b/delivery_dhl_parcel/models/__init__.py new file mode 100644 index 00000000000..48773250317 --- /dev/null +++ b/delivery_dhl_parcel/models/__init__.py @@ -0,0 +1,4 @@ +from . import delivery_carrier +from . import dhl_parcel_request +from . import sale_order +from . import stock_picking diff --git a/delivery_dhl_parcel/models/delivery_carrier.py b/delivery_dhl_parcel/models/delivery_carrier.py new file mode 100644 index 00000000000..839acb75d9d --- /dev/null +++ b/delivery_dhl_parcel/models/delivery_carrier.py @@ -0,0 +1,315 @@ +# Copyright 2021 Studio73 - Ethan Hildick +# Copyright 2022 Tecnativa - Víctor Martínez +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import base64 + +from odoo import _, fields, models +from odoo.tools import float_compare + +from .dhl_parcel_request import ( + DHL_PARCEL_DELIVERY_STATES_STATIC, + DHL_PARCEL_INCOTERMS_STATIC, + DhlParcelRequest, +) + + +class DeliveryCarrier(models.Model): + _inherit = "delivery.carrier" + + def _compute_can_generate_return(self): + res = super()._compute_can_generate_return() + for carrier in self: + if carrier.delivery_type == "dhl_parcel": + carrier.can_generate_return = True + return res + + delivery_type = fields.Selection( + selection_add=[("dhl_parcel", "DHL Parcel")], + ondelete={"dhl_parcel": "set default"}, + ) + dhl_parcel_customer_code = fields.Char(string="DHL Parcel customer code") + dhl_parcel_incoterm = fields.Selection( + string="DHL Parcel incoterms", selection=DHL_PARCEL_INCOTERMS_STATIC + ) + dhl_parcel_product = fields.Selection( + string="DHL Product", + selection=[("B2B", "B2B Product"), ("B2C", "B2C Product")], + help="If the product is not specified, it is considered B2B", + ) + dhl_parcel_uid = fields.Char(string="DHL Parcel UID") + dhl_parcel_password = fields.Char(string="DHL Parcel Password") + dhl_parcel_last_end_day_report = fields.Binary( + string="DHL Parcel last manual end day report" + ) + dhl_parcel_last_end_day_report_name = fields.Char(string="Filename") + dhl_parcel_label_format = fields.Selection( + selection=[("PDF", "PDF"), ("ZPL", "ZPL"), ("EPL", "EPL")], + default="PDF", + string="Label format", + ) + dhl_parcel_cash_on_delivery = fields.Boolean( + string="Cash on delivery", + help=( + "If checked, it means that the carrier is paid with cash. It assumes " + "there is a sale order linked and it will use that " + "total amount as the value to be paid. " + "It will also exclude this carrier from the e-commerce checkout." + ), + ) + + def dhl_parcel_get_tracking_link(self, picking): + """Provide tracking link for the customer""" + tracking_url = ( + "https://clientesparcel.dhl.es/seguimientoenvios/integra/" + "SeguimientoDocumentos.aspx?codigo={}&anno={}&lang=sp" + ) + return tracking_url.format( + picking.carrier_tracking_ref, fields.Date.today().year + ) + + def _prepare_dhl_parcel_address_info(self, partner): + phone = partner.phone and partner.phone.replace(" ", "") or "" + mobile = partner.mobile and partner.mobile.replace(" ", "") or "" + address = partner.street or "" + if partner.street2: + address += " " + partner.street2 + return { + "Name": partner.name or partner.parent_id.name or "", + "Address": address[:40], + "City": partner.city or "", + "PostalCode": partner.zip or "", + "Country": partner.country_id.code or "", + "Phone": phone or mobile, + "Email": partner.email or "", + } + + def _get_dhl_parcel_receiver_info(self, picking): + return self._prepare_dhl_parcel_address_info(picking.partner_id) + + def _get_dhl_parcel_sender_info(self, picking): + """Optional, if the sender information is not + sent, they will be fetched from DHL web Service B2B. + """ + return self._prepare_dhl_parcel_address_info( + picking.picking_type_id.warehouse_id.partner_id + or picking.company_id.partner_id + ) + + def _get_dhl_parcel_product(self, picking): + product = self.dhl_parcel_product + if product == "B2C" and picking.is_return_picking: + product = "R2C" + return product or "B2B" + + def _prepare_dhl_parcel_shipping(self, picking): + """Convert picking values for dhl parcel api + :param picking record with picking to send + :returns dict values for the connector + """ + self.ensure_one() + # El peso debe tener 2 decimales para evitar errores en el cierre del día + weight = round(picking.shipping_weight, 2) + # El peso del envío tiene que ser como mínimo 1 kilo o como máximo 99999 kilos + if float_compare(weight, 1, precision_digits=2) == -1: + weight = 1 + vals = { + "Customer": self.dhl_parcel_customer_code, + "Receiver": self._get_dhl_parcel_receiver_info(picking), + "Sender": self._get_dhl_parcel_sender_info(picking), # [optional] + "Reference": picking.name, # [optional] + "Quantity": picking.number_of_packages, # 1-999 + "Weight": weight, # in kg, 1-99999 + "WeightVolume": "", # [optional] Volume, in kg + "CODAmount": "", # [optional] + "CODExpenses": "", # [optional], mandatory if CODAmount filled + "CODCurrency": "", # [optional], mandatory if CODAmount filled + "InsuranceAmount": "", # [optional] + "InsuranceExpenses": "", # [optional], mandatory if InsuranceAmount filled + "InsuranceCurrency": "", # [optional], mandatory if InsuranceAmount filled + "DeliveryNote": "", # [optional] + "ServiceType": "", # [optional] + "Remarks1": "", # [optional] + "Remarks2": "", # [optional] + "Incoterms": self.dhl_parcel_incoterm, # CPT paid, EXW owed + "Product": self._get_dhl_parcel_product(picking), + "ContactName": "", # [optional] + "GoodsDescription": "", # [optional] + "CustomsValue": "", # [optional] + "CustomsCurrency": "", # [optional] + "Format": self.dhl_parcel_label_format, # [optional] + "tracking_number": False, + "exact_price": 0, + } + if self.dhl_parcel_cash_on_delivery and picking.sale_id: + sale = picking.sale_id + mapped_expenses = {"CPT": "P", "EXW": "D"} + vals.update( + { + "CODAmount": sale.amount_total, + "CODExpenses": mapped_expenses.get(self.dhl_parcel_incoterm, "P"), + "CODCurrency": sale.currency_id.name, + } + ) + return vals + + def dhl_parcel_send_shipping(self, pickings): + """Send the package to DHL Parcel + :param pickings: A recordset of pickings + :return list: A list of dictionaries although in practice it's + called one by one and only the first item in the dict is taken. Due + to this design, we have to inject vals in the context to be able to + add them to the message. + """ + dhl_parcel_request = DhlParcelRequest(self) + result = [] + for picking in pickings: + vals = self._prepare_dhl_parcel_shipping(picking) + response = dhl_parcel_request.create_shipment(vals) + if not response: + result.append(vals) + continue + vals["tracking_number"] = response.get("Tracking", "") + vals["response"] = response + # We post an extra message in the chatter with the rest of the response + body = _( + "DHL Parcel Shipping extra info:\n" + "Origin: %(origin)s, Customer: %(customer)s, AWB: %(awb)s, LP: %(lp)s" + ) % { + "origin": response.get("Origin", "N/A"), + "customer": response.get("Customer", "N/A"), + "awb": response.get("AWB", "N/A"), + "lp": response.get("LP", "N/A"), + } + attachment = [] + if response.get("Label"): + label_format = picking.carrier_id.dhl_parcel_label_format.lower() + attachment = [ + ( + "dhl_parcel_{}.{}".format( + response.get("Tracking", ""), + "pdf" if label_format == "pdf" else "txt", + ), + base64.b64decode(response.get("Label")), + ) + ] + picking.message_post(body=body, attachments=attachment) + result.append(vals) + return result + + def dhl_parcel_tracking_state_update(self, picking): + """Tracking state update""" + self.ensure_one() + if not picking.carrier_tracking_ref: + return + dhl_parcel_request = DhlParcelRequest(self) + tracking_events = dhl_parcel_request.track_shipment( + picking.carrier_tracking_ref, "events" + ) + if not tracking_events: + return + picking.tracking_state_history = "\n".join( + [ + "{} {} - [{}] {}".format( + t.get("DateTime"), + t.get("Ubication"), + t.get("Code"), + t.get("Status"), + ) + for t in tracking_events + ] + ) + tracking = tracking_events.pop() + picking.tracking_state = "[{}] {}".format( + tracking.get("Code"), tracking.get("Status") + ) + picking.delivery_state = DHL_PARCEL_DELIVERY_STATES_STATIC.get( + tracking.get("Code"), "incidence" + ) + + def dhl_parcel_cancel_shipment(self, pickings): + """Cancel the expedition""" + dhl_parcel_request = DhlParcelRequest(self) + for picking in pickings.filtered("carrier_tracking_ref"): + response = dhl_parcel_request.cancel_shipment(picking.carrier_tracking_ref) + if not response: + msg = _( + "DHL Parcel Cancellation failed with reason: %s" + ) % response.get("Message", "Connection Error") + picking.message_post(body=msg) + continue + picking.carrier_tracking_ref = False + picking.message_post( + body=_("DHL Parcel Expedition with reference %s cancelled") + % picking.carrier_tracking_ref + ) + + # TODO: The label_format parameter is not used and can be removed. + def dhl_parcel_get_label(self, carrier_tracking_ref, label_format="pdf"): + """Generate label for picking + :param str carrier_tracking_ref - tracking reference + :returns base64 encoded label + """ + self.ensure_one() + if not carrier_tracking_ref: + return False + dhl_parcel_request = DhlParcelRequest(self) + label = dhl_parcel_request.print_shipment(carrier_tracking_ref) + return label or False + + def dhl_parcel_hold_shipment(self, carrier_tracking_ref): + """Hold shipment + :param str carrier_tracking_ref - tracking reference + :returns boolean + """ + self.ensure_one() + if not carrier_tracking_ref: + return False + dhl_parcel_request = DhlParcelRequest(self) + return dhl_parcel_request.hold_shipment(carrier_tracking_ref) + + def dhl_parcel_release_shipment(self, carrier_tracking_ref): + """Release shipment + :param str carrier_tracking_ref - tracking reference + :returns boolean + """ + self.ensure_one() + if not carrier_tracking_ref: + return False + dhl_parcel_request = DhlParcelRequest(self) + return dhl_parcel_request.release_shipment(carrier_tracking_ref) + + def dhl_parcel_rate_shipment(self, order): + """Not implemented with DHL, these values are so it works with websites""" + return { + "success": True, + "price": self.product_id.lst_price, + "error_message": _( + """DHL Parcel API doesn't provide methods to compute delivery rates, so + you should rely on another price method instead or override this + one in your custom code.""" + ), + "warning_message": _( + """DHL Parcel API doesn't provide methods to compute delivery rates, so + you should rely on another price method instead or override this + one in your custom code.""" + ), + } + + def action_open_end_day(self): + """Action to launch the end day wizard""" + self.ensure_one() + wizard = self.env["dhl.parcel.endday.wizard"].create( + {"carrier_id": self.id, "customer_accounts": self.dhl_parcel_customer_code} + ) + view_id = self.env.ref("delivery_dhl_parcel.delivery_endday_wizard_form").id + return { + "name": _("DHL Parcel End Day"), + "type": "ir.actions.act_window", + "view_mode": "form", + "res_model": "dhl.parcel.endday.wizard", + "view_id": view_id, + "views": [(view_id, "form")], + "target": "new", + "res_id": wizard.id, + "context": self.env.context, + } diff --git a/delivery_dhl_parcel/models/dhl_parcel_request.py b/delivery_dhl_parcel/models/dhl_parcel_request.py new file mode 100644 index 00000000000..da12ee2abb4 --- /dev/null +++ b/delivery_dhl_parcel/models/dhl_parcel_request.py @@ -0,0 +1,210 @@ +# Copyright 2021 Studio73 - Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl).. + +import requests + +from odoo import _, fields +from odoo.exceptions import UserError + +DHL_PARCEL_DELIVERY_STATES_STATIC = { + "A": "shipping_recorded_in_carrier", # Assigned + "T": "in_transit", # Transit + "R": "customer_delivered", # Received +} + +DHL_PARCEL_INCOTERMS_STATIC = [ + ("CPT", "Paid transport"), + ("EXW", "Owed transport"), +] + +DHL_PATH = "https://external.dhl.es/cimapi/api/v1/customer/" + + +class DhlParcelRequest: + """Interface between DHL Parcel API and Odoo recordset + Abstract DHL Parcel API Operations to connect them with Odoo + """ + + def __init__(self, carrier): + self.carrier_id = carrier + self.token = self._get_new_auth_token( + username=carrier.dhl_parcel_uid or "", + password=carrier.dhl_parcel_password or "", + ) + self.year = str(fields.Date.today().year)[-1] # last digit of the year + self.label_format = self.carrier_id.dhl_parcel_label_format + + def _send_api_request(self, request_type, url, data=None, skip_auth=False): + if data is None: + data = {} + try: + auth = {} + if not skip_auth: + auth = {"Authorization": f"Bearer {self.token}"} + if request_type == "GET": + res = requests.get(url=url, headers=auth, timeout=60) + elif request_type == "POST": + res = requests.post(url=url, json=data, headers=auth, timeout=60) + else: + raise UserError( + _("Unsupported request type, please only use 'GET' or 'POST'") + ) + res.raise_for_status() + dhl_parcel_last_request = ("Request type: {}\nURL: {}\nData: {}").format( + request_type, url, data + ) + self.carrier_id.log_xml(dhl_parcel_last_request, "dhl_parcel_last_request") + self.carrier_id.log_xml(res.text or "", "dhl_parcel_last_response") + except requests.exceptions.Timeout: + raise UserError(_("Timeout: the server did not reply within 60s")) from None + except (ValueError, requests.exceptions.ConnectionError): + raise UserError(_("Server not reachable, please try again later")) from None + except requests.exceptions.HTTPError as e: + error_message = _("%(error)s\n%(message)s") % { + "error": str(e), + "message": res.json().get("Message", "") if res.text else "", + } + raise UserError(error_message) from None + return res + + def _get_new_auth_token(self, username, password): + res = self._send_api_request( + request_type="POST", + url=DHL_PATH + "authenticate", + data={"Username": username, "Password": password}, + skip_auth=True, + ) + return res.json() + + def create_shipment(self, vals): + """ + :param dict vals -- data to use in create request + :return dict with format + { + "Origin": "08", + "Customer": "001000", + "Tracking": "0870002260", + "AWB": "", + "LP": ["JJD00006080070002260001"], + "Label": "JVBERi0xL..........” (Label data in Base64) + } + """ + res = self._send_api_request( + request_type="POST", url=DHL_PATH + "shipment", data=vals + ) + return res.json() + + def track_shipment(self, reference=False, track="status"): + """Gets tracking info for shipment + Event Code meaning: + T - Transit + A - Assigned + R - Received + :param str reference -- public shipping reference + :param str track -- + - "events" (events) + - "status" (current status with latest event) + _ "both" + :returns: dict with format + { + "Year": "0", + "From": "20", + "Tracking": "2013902080", + "Origin": "San Sebastián", + "Destination": "Vitoria", + "Product": "EUROPLUS DOM", + "Parcels": 1, + "Weight": 86, + "Ship_Reference": "PXKW00340DM99901", + "AWB": "", + "Receiver": "", + "Events": [ + { + "DateTime": "2020-10-02T10:40:49", + "Code": "A", + "Status": "Es posible que la fecha prevista de entrega" + " se posponga un día hábil", + "Ubication": "Araba/Álava" + } + ] + } + """ + res = self._send_api_request( + request_type="GET", + url=(DHL_PATH + f"track?id={reference}&idioma=es&show={track}"), + ) + return res.json() + + # TODO: The label_format parameter is not used and can be removed. + def print_shipment(self, reference=False, label_format="PDF"): + """Get shipping label for the given ref + :param str reference -- public shipping reference + :returns: base64 with pdf label or False + """ + res = self._send_api_request( + request_type="GET", + url=( + DHL_PATH + "shipment?" + f"Year={self.year}&Tracking={reference}&Action=PRINT" + f"&LabelFrom={1}&LabelTo={1}&Format={self.label_format}" + ), + ) + return res.json().get("Label", False) + + def cancel_shipment(self, reference=False): + """Delete shipment + :param str reference -- public shipping reference + :returns: str -- message text + """ + res = self._send_api_request( + request_type="GET", + url=DHL_PATH + "shipment?" + f"Year={self.year}&Tracking={reference}&Action=DELETE", + ) + return True if res.status_code == 200 else False + + def hold_shipment(self, reference=False): + """Hold shipment, shipping will not be documented until it's released + :param str reference -- public shipping reference + :returns: Boolean + """ + res = self._send_api_request( + request_type="GET", + url=DHL_PATH + "shipment?" + f"Year={self.year}&Tracking={reference}&Action=HOLD", + ) + return True if res.status_code == 200 else False + + def release_shipment(self, reference=False): + """Release shipment + :param str reference -- public shipping reference + :returns: Boolean + """ + res = self._send_api_request( + request_type="GET", + url=DHL_PATH + "shipment?" + f"Year={self.year}&Tracking={reference}&Action=RELEASE", + ) + return True if res.status_code == 200 else False + + def end_day(self, customers="", report_type=""): + """End day + :param str customers -- Customer codes seperated by ',' or "ALL" for all of them + :param str report_type -- PDF, DOC, XLS, RTF + :returns: dict in format + { + "Shipments": [{ + "Origin": "08", + "Customer": "001000", + "Year": "1", + "Tracking": "0824005834" + }], + "Report": "JVBERi..." + } + """ + res = self._send_api_request( + request_type="POST", + url=DHL_PATH + "endday", + data={"Accounts": customers, "Report": report_type}, + ) + return res.json() diff --git a/delivery_dhl_parcel/models/sale_order.py b/delivery_dhl_parcel/models/sale_order.py new file mode 100644 index 00000000000..0f8aecdb4a6 --- /dev/null +++ b/delivery_dhl_parcel/models/sale_order.py @@ -0,0 +1,17 @@ +# Copyright 2023 Studio73 - Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). + +from odoo import models + + +class SaleOrder(models.Model): + _inherit = "sale.order" + + def _get_delivery_methods(self): + # Exclude CoD delivery methods when getting methods for checkout in the + # website_sale_delivery module + return ( + super() + ._get_delivery_methods() + .filtered(lambda c: not c.dhl_parcel_cash_on_delivery) + ) diff --git a/delivery_dhl_parcel/models/stock_picking.py b/delivery_dhl_parcel/models/stock_picking.py new file mode 100644 index 00000000000..b9df3cd48ba --- /dev/null +++ b/delivery_dhl_parcel/models/stock_picking.py @@ -0,0 +1,51 @@ +# Copyright 2021 Studio73 - Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import base64 + +from odoo import _, fields, models + + +class StockPicking(models.Model): + _inherit = "stock.picking" + + dhl_parcel_shipment_held = fields.Boolean(string="DHL Parcel shipment on hold") + + # TODO: The label_format parameter is not used and can be removed. + def dhl_parcel_get_label(self, label_format="pdf"): + """Get DHL Parcel Label for this picking expedition""" + self.ensure_one() + tracking_ref = self.carrier_tracking_ref + if self.delivery_type != "dhl_parcel" or not tracking_ref: + return + label = base64.b64decode(self.carrier_id.dhl_parcel_get_label(tracking_ref)) + label_format = self.carrier_id.dhl_parcel_label_format.lower() + label_name = "dhl_parcel_{}.{}".format( + tracking_ref, + "pdf" if label_format == "pdf" else "txt", + ) + self.message_post( + body=(_("DHL Parcel label for %s") % tracking_ref), + attachments=[(label_name, label)], + ) + # We return label in case it wants to be printed in an inheritance + return label + + def dhl_parcel_toggle_hold_shipment(self): + """Toggle between holding and releasing the DHL Parcel shipment""" + self.ensure_one() + tracking_ref = self.carrier_tracking_ref + if self.delivery_type != "dhl_parcel" or not tracking_ref: + return + message = "" + carrier = self.carrier_id + if self.dhl_parcel_shipment_held: + self.dhl_parcel_shipment_held = not carrier.dhl_parcel_release_shipment( + tracking_ref + ) + message = _("Released shipment for {}").format(tracking_ref) + else: + self.dhl_parcel_shipment_held = carrier.dhl_parcel_hold_shipment( + tracking_ref + ) + message = _("Held shipment for {}").format(tracking_ref) + self.message_post(body=message) diff --git a/delivery_dhl_parcel/pyproject.toml b/delivery_dhl_parcel/pyproject.toml new file mode 100644 index 00000000000..4231d0cccb3 --- /dev/null +++ b/delivery_dhl_parcel/pyproject.toml @@ -0,0 +1,3 @@ +[build-system] +requires = ["whool"] +build-backend = "whool.buildapi" diff --git a/delivery_dhl_parcel/readme/CONFIGURE.md b/delivery_dhl_parcel/readme/CONFIGURE.md new file mode 100644 index 00000000000..c4e91955a7b --- /dev/null +++ b/delivery_dhl_parcel/readme/CONFIGURE.md @@ -0,0 +1,10 @@ +Para configurar el transportista: + +1. Vaya a *Inventario \> Configuración \> Entrega \> Método de envío* y + cree uno nuevo. +2. Escoja *DHL Parcel* Como proveedor. +3. Configure los datos de servicio que tiene contratados y el producto + de envío que desea utilizar. + +El código de cliente es el UID con un guión y sin letras al final i.e. +UID = 08100000AB, tu Código de cliente sería 08-100000 diff --git a/delivery_dhl_parcel/readme/CONTRIBUTORS.md b/delivery_dhl_parcel/readme/CONTRIBUTORS.md new file mode 100644 index 00000000000..e472808fd09 --- /dev/null +++ b/delivery_dhl_parcel/readme/CONTRIBUTORS.md @@ -0,0 +1,4 @@ +- [Studio73](https://www.studio73.es): + - Ethan Hildick +- [Tecnativa](https://www.tecnativa.com): + - Víctor Martínez diff --git a/delivery_dhl_parcel/readme/DESCRIPTION.md b/delivery_dhl_parcel/readme/DESCRIPTION.md new file mode 100644 index 00000000000..d376ddfa778 --- /dev/null +++ b/delivery_dhl_parcel/readme/DESCRIPTION.md @@ -0,0 +1,13 @@ +Este módulo integra la API de DHL Parcel con Odoo. No es válido para +otras integraciones de DHL como el Express, que usan otras APIs. + +IMPORTANTE: Hay varias APIs distintas para DHL España y DHL Parcel, esta +API solo funciona para una de ellas. Para notificar y pedir las +credenciales correctas a tu comercial de integración, se puede pasar el +documento que hay en /doc para indicar exactamente cuál es la API que se +quiere usar. El formato de las credenciales que se usan en esta es el +siguiente: + +Usuario: 11000000EMPRESA Pass: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx + +Puede que acepte algún otro, pero no puedo verificarlo. diff --git a/delivery_dhl_parcel/readme/INSTALL.md b/delivery_dhl_parcel/readme/INSTALL.md new file mode 100644 index 00000000000..bba88232abe --- /dev/null +++ b/delivery_dhl_parcel/readme/INSTALL.md @@ -0,0 +1,6 @@ +Depende de los módulos de OCA/delivery-carrier delivery_package_number y +delivery_state. + +La API de DHL Parcel no provee métodos de cálculo de precio, de modo que +para poder calcular los costes de envío sería recomendable instalar el +módulo delivery_price_method. diff --git a/delivery_dhl_parcel/readme/ROADMAP.md b/delivery_dhl_parcel/readme/ROADMAP.md new file mode 100644 index 00000000000..cc0dad04e96 --- /dev/null +++ b/delivery_dhl_parcel/readme/ROADMAP.md @@ -0,0 +1,4 @@ +- La API no facilita métodos para cotizar el coste real de los envíos, + por lo que siempre se cotizan a 0. Si la cotización de envíos es + necesaria, puede instalarse el módulo OCA delivery_price_method o bien + personalizar el método de cotización para este tipo de transportista. diff --git a/delivery_dhl_parcel/readme/USAGE.md b/delivery_dhl_parcel/readme/USAGE.md new file mode 100644 index 00000000000..b5f0dc66516 --- /dev/null +++ b/delivery_dhl_parcel/readme/USAGE.md @@ -0,0 +1,60 @@ +Estas son las distintas operaciones posibles con este módulo: + +## Grabar servicios + +> 1. Al confirmar el albarán, el servicio se grabará en DHL Parcel. +> 2. Con la respuesta, se registrará en el chatter la referencia de +> envío y las etiquetas correspondientes. +> 3. Para gestionar los bultos del envío, se puede utilizar el campo de +> número de bultos que añade delivery_package_number (ver el README +> para mayor información) o bien el flujo nativo de Odoo con +> paquetes de envío. El módulo mandará a la API de DHL Parcel el +> número correspondiente y podremos descargar las etiquetas en PDF +> con su correspondiente numeración. + +## Cancelar servicios + +> 1. Al igual que en otros métodos de envío, en los albaranes de salida +> podemos cancelar un servicio determinado mediante la acción +> correspondiente en la pestaña de *Información Adicional*, sección +> *Información de entrega* una vez el pedido esté confirmado y la +> expedición generada. +> 2. Podremos generar una nueva expedición una vez cancelado si fuese +> necesario. + +## Obtener etiquetas + +> 1. Si por error hubiésemos eliminado el adjunto de las etiquetas que +> obtuvimos en la grabación del servicio, podemos obtenerlas de +> nuevo pulsando en el botón "Etiqueta DHL Parcel" que tenemos en la +> parte superior de la vista formulario del albarán. + +## Seguimiento de envíos + +> 1. El módulo está integrado con delivery_state para poder recabar la +> información de seguimiento de nuestros envíos directamente desde +> la API de DHL Parcel. +> 2. Para ello, vaya al albarán con un envío DHL Parcel ya grabado y en +> la pestaña de *Información adicional* verá el botón *Actualizar +> seguimiento* para pedir a la API que actualice el estado de este +> envío en Odoo. + +## Cierre del día + +> 1. Desde un smart button en el formulario del transportista, se puede +> cerrar el día manualmente. + +## Depuración de errores + +> 1. Es importante tener en cuenta que solo funcionará con códigos +> postales de España (por lo menos para el consignatario). +> 2. También puede activar Odoo con --log-level=debug para registrar +> las peticiones y las respuestas en el log. + +## Contrareembolso + +> 1. Al crear un envío con un método que tiene marcado el check de +> contrareembolso, asumirá que hay un pedido vinculado al albarán y +> usará el importe total del pedido como importe del +> contrareembolso. Métodos de envío con este check no se encontrarán +> disponibles en la web si se está usando. diff --git a/delivery_dhl_parcel/security/ir.model.access.csv b/delivery_dhl_parcel/security/ir.model.access.csv new file mode 100644 index 00000000000..4efcde8e45d --- /dev/null +++ b/delivery_dhl_parcel/security/ir.model.access.csv @@ -0,0 +1,2 @@ +id,name,model_id:id,group_id:id,perm_read,perm_write,perm_create,perm_unlink +access_dhl_parcel_endday_wizard_user,dhl.parcel.endday.wizard.user,model_dhl_parcel_endday_wizard,base.group_user,1,1,1,1 diff --git a/delivery_dhl_parcel/static/description/icon.png b/delivery_dhl_parcel/static/description/icon.png new file mode 100644 index 00000000000..c3a022453e8 Binary files /dev/null and b/delivery_dhl_parcel/static/description/icon.png differ diff --git a/delivery_dhl_parcel/static/description/index.html b/delivery_dhl_parcel/static/description/index.html new file mode 100644 index 00000000000..f741d3a1359 --- /dev/null +++ b/delivery_dhl_parcel/static/description/index.html @@ -0,0 +1,576 @@ + + + + + +Delivery DHL Parcel + + + +
+

Delivery DHL Parcel

+ + +

Beta License: AGPL-3 OCA/l10n-spain Translate me on Weblate Try me on Runboat

+

Este módulo integra la API de DHL Parcel con Odoo. No es válido para +otras integraciones de DHL como el Express, que usan otras APIs.

+

IMPORTANTE: Hay varias APIs distintas para DHL España y DHL Parcel, esta +API solo funciona para una de ellas. Para notificar y pedir las +credenciales correctas a tu comercial de integración, se puede pasar el +documento que hay en /doc para indicar exactamente cuál es la API que se +quiere usar. El formato de las credenciales que se usan en esta es el +siguiente:

+

Usuario: 11000000EMPRESA Pass: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx

+

Puede que acepte algún otro, pero no puedo verificarlo.

+

Table of contents

+ +
+

Installation

+

Depende de los módulos de OCA/delivery-carrier delivery_package_number y +delivery_state.

+

La API de DHL Parcel no provee métodos de cálculo de precio, de modo que +para poder calcular los costes de envío sería recomendable instalar el +módulo delivery_price_method.

+
+
+

Configuration

+

Para configurar el transportista:

+
    +
  1. Vaya a Inventario > Configuración > Entrega > Método de envío y +cree uno nuevo.
  2. +
  3. Escoja DHL Parcel Como proveedor.
  4. +
  5. Configure los datos de servicio que tiene contratados y el producto +de envío que desea utilizar.
  6. +
+

El código de cliente es el UID con un guión y sin letras al final i.e. +UID = 08100000AB, tu Código de cliente sería 08-100000

+
+
+

Usage

+

Estas son las distintas operaciones posibles con este módulo:

+
+

Grabar servicios

+
+
    +
  1. Al confirmar el albarán, el servicio se grabará en DHL Parcel.
  2. +
  3. Con la respuesta, se registrará en el chatter la referencia de +envío y las etiquetas correspondientes.
  4. +
  5. Para gestionar los bultos del envío, se puede utilizar el campo de +número de bultos que añade delivery_package_number (ver el README +para mayor información) o bien el flujo nativo de Odoo con +paquetes de envío. El módulo mandará a la API de DHL Parcel el +número correspondiente y podremos descargar las etiquetas en PDF +con su correspondiente numeración.
  6. +
+
+
+
+

Cancelar servicios

+
+
    +
  1. Al igual que en otros métodos de envío, en los albaranes de salida +podemos cancelar un servicio determinado mediante la acción +correspondiente en la pestaña de Información Adicional, sección +Información de entrega una vez el pedido esté confirmado y la +expedición generada.
  2. +
  3. Podremos generar una nueva expedición una vez cancelado si fuese +necesario.
  4. +
+
+
+
+

Obtener etiquetas

+
+
    +
  1. Si por error hubiésemos eliminado el adjunto de las etiquetas que +obtuvimos en la grabación del servicio, podemos obtenerlas de +nuevo pulsando en el botón “Etiqueta DHL Parcel” que tenemos en la +parte superior de la vista formulario del albarán.
  2. +
+
+
+
+

Seguimiento de envíos

+
+
    +
  1. El módulo está integrado con delivery_state para poder recabar la +información de seguimiento de nuestros envíos directamente desde +la API de DHL Parcel.
  2. +
  3. Para ello, vaya al albarán con un envío DHL Parcel ya grabado y en +la pestaña de Información adicional verá el botón Actualizar +seguimiento para pedir a la API que actualice el estado de este +envío en Odoo.
  4. +
+
+
+
+

Cierre del día

+
+
    +
  1. Desde un smart button en el formulario del transportista, se puede +cerrar el día manualmente.
  2. +
+
+
+
+

Depuración de errores

+
+
    +
  1. Es importante tener en cuenta que solo funcionará con códigos +postales de España (por lo menos para el consignatario).
  2. +
  3. También puede activar Odoo con –log-level=debug para registrar +las peticiones y las respuestas en el log.
  4. +
+
+
+
+

Contrareembolso

+
+
    +
  1. Al crear un envío con un método que tiene marcado el check de +contrareembolso, asumirá que hay un pedido vinculado al albarán y +usará el importe total del pedido como importe del +contrareembolso. Métodos de envío con este check no se encontrarán +disponibles en la web si se está usando.
  2. +
+
+
+
+
+

Known issues / Roadmap

+
    +
  • La API no facilita métodos para cotizar el coste real de los envíos, +por lo que siempre se cotizan a 0. Si la cotización de envíos es +necesaria, puede instalarse el módulo OCA delivery_price_method o +bien personalizar el método de cotización para este tipo de +transportista.
  • +
+
+
+

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

+
    +
  • Studio73
  • +
+
+
+

Contributors

+ +
+
+

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.

+

Current maintainer:

+

hildickethan-S73

+

This module is part of the OCA/l10n-spain project on GitHub.

+

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

+
+
+
+ + diff --git a/delivery_dhl_parcel/tests/__init__.py b/delivery_dhl_parcel/tests/__init__.py new file mode 100644 index 00000000000..65fe03356ca --- /dev/null +++ b/delivery_dhl_parcel/tests/__init__.py @@ -0,0 +1 @@ +from . import test_delivery_dhl_parcel diff --git a/delivery_dhl_parcel/tests/test_delivery_dhl_parcel.py b/delivery_dhl_parcel/tests/test_delivery_dhl_parcel.py new file mode 100644 index 00000000000..8069584d032 --- /dev/null +++ b/delivery_dhl_parcel/tests/test_delivery_dhl_parcel.py @@ -0,0 +1,167 @@ +# Copyright 2021 Studio73 - Ethan Hildick +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl). +import time +from unittest import mock + +from odoo.tests import Form, common + +request_model = ( + "odoo.addons.delivery_dhl_parcel.models.dhl_parcel_request.DhlParcelRequest" +) + +# There is also no public test user so we mock all API requests + + +class TestDeliveryDhlParcel(common.SingleTransactionCase): + @classmethod + def setUpClass(cls): + super().setUpClass() + cls.shipping_product = cls.env["product.product"].create( + {"type": "service", "name": "Test Shipping costs", "list_price": 10.0} + ) + cls.carrier_dhl_parcel = cls.env["delivery.carrier"].create( + { + "name": "DHL Parcel", + "delivery_type": "dhl_parcel", + "product_id": cls.shipping_product.id, + } + ) + cls.product = cls.env["product.product"].create( + {"type": "product", "name": "Test product"} + ) + cls.partner = cls.env["res.partner"].create( + { + "name": "Mr. Odoo & Co.", + "city": "Odoo Ville", + "zip": "28001", + "street": "Calle de La Rua, 3", + } + ) + order_form = Form(cls.env["sale.order"].with_context(tracking_disable=True)) + order_form.partner_id = cls.partner + with order_form.order_line.new() as line: + line.product_id = cls.product + line.product_uom_qty = 20.0 + cls.sale_order = order_form.save() + cls.sale_order.carrier_id = cls.carrier_dhl_parcel.id + cls.sale_order.action_confirm() + cls.picking = cls.sale_order.picking_ids[0] + cls.picking.move_ids.quantity = 20 + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch( + "%s.create_shipment" % request_model, + return_value={ + "Origin": "08", + "Customer": "001000", + "Tracking": "0870002260", + "AWB": "", + "LP": ["JJD00006080070002260001"], + "Label": "JVBERiasdasdsdcfnsdhfbasdf==", + }, + ) + def test_01_dhl_parcel_picking_confirm_success(self, redirect_mock, *args): + self.picking.name = f"ODOO-TEST-{time.time()}" + self.picking.button_validate() + self.assertEqual( + self.picking.carrier_tracking_ref, + "0870002260", + "Tracking doesn't match test data", + ) + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch( + "%s.track_shipment" % request_model, + return_value=[ + { + "DateTime": "2020-10-02T10:40:49", + "Code": "A", + "Status": "Es posible que la fecha prevista de entrega" + " se posponga un día hábil", + "Ubication": "Araba/Álava", + } + ], + ) + def test_02_dhl_parcel_picking_update(self, redirect_mock, *args): + self.picking.tracking_state_update() + self.assertEqual( + self.picking.tracking_state_history, + ( + "2020-10-02T10:40:49 Araba/Álava - [A] Es posible que la fecha" + " prevista de entrega se posponga un día hábil" + ), + "History doesn't match test data", + ) + self.assertEqual( + self.picking.tracking_state, + ( + "[A] Es posible que la fecha" + " prevista de entrega se posponga un día hábil" + ), + "State doesn't match test data", + ) + self.assertEqual( + self.picking.delivery_state, + "shipping_recorded_in_carrier", + "Delivery state doesn't match test data", + ) + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch("%s.hold_shipment" % request_model, return_value=True) + @mock.patch("%s.release_shipment" % request_model, return_value=True) + def test_03_dhl_parcel_picking_toggle_hold(self, redirect_mock, *args): + self.assertFalse(self.picking.dhl_parcel_shipment_held) + self.picking.dhl_parcel_toggle_hold_shipment() # hold + self.assertTrue(self.picking.dhl_parcel_shipment_held) + self.picking.dhl_parcel_toggle_hold_shipment() # release + self.assertFalse(self.picking.dhl_parcel_shipment_held) + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch( + "%s.end_day" % request_model, + return_value={ + "Shipments": [ + { + "Origin": "08", + "Customer": "001000", + "Year": "1", + "Tracking": "0824005834", + } + ], + "Report": "JVBERiasdasdsdcfnsdhfbasdf==", + }, + ) + def test_04_dhl_parcel_endday(self, redirect_mock, *args): + wizard = self.env["dhl.parcel.endday.wizard"].browse( + self.carrier_dhl_parcel.action_open_end_day().get("res_id") + ) + wizard.button_end_day() + self.assertTrue(self.carrier_dhl_parcel.dhl_parcel_last_end_day_report) + + def test_05_dhl_parcel_get_tracking_link(self): + tracking = self.carrier_dhl_parcel.get_tracking_link(self.picking) + self.assertTrue(tracking) + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch( + "%s.print_shipment" % request_model, return_value="JVBERiasdasdsdcfnsdhfbasdf==" + ) + def test_06_dhl_parcel_get_label(self, redirect_mock, *args): + label = self.picking.dhl_parcel_get_label() + self.assertTrue(label) + + def test_07_dhl_parcel_rate_shipment(self): + msg = self.carrier_dhl_parcel.dhl_parcel_rate_shipment( + order=self.env["sale.order"] + ) + self.assertIsInstance(msg, dict) + + @mock.patch("%s._get_new_auth_token" % request_model, return_value="12345") + @mock.patch("%s.cancel_shipment" % request_model, return_value=True) + def test_08_dhl_parcel_picking_cancel(self, redirect_mock, *args): + self.picking.cancel_shipment() + self.assertFalse(self.picking.carrier_tracking_ref) + self.picking.cancel_shipment() + self.picking.dhl_parcel_toggle_hold_shipment() + self.picking.dhl_parcel_get_label() + self.picking.tracking_state_update() diff --git a/delivery_dhl_parcel/views/delivery_carrier_view.xml b/delivery_dhl_parcel/views/delivery_carrier_view.xml new file mode 100644 index 00000000000..a9f5e2c4b4c --- /dev/null +++ b/delivery_dhl_parcel/views/delivery_carrier_view.xml @@ -0,0 +1,66 @@ + + + + delivery.carrier + + + + + + + + + + + + + + + + + + + + + + +