Skip to content
4 changes: 3 additions & 1 deletion delivery_carrier_label_paazl/README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Carrier labels for paazl
:target: https://runbot.odoo-community.org/runbot/99/12.0
:alt: Try me on Runbot

|badge1| |badge2| |badge3| |badge4| |badge5|
|badge1| |badge2| |badge3| |badge4| |badge5|

This module adds `paazl <https://paazl.com>`_ to the available carriers.

Expand All @@ -38,6 +38,7 @@ Configuration
To configure this module, you need to:

#. Add options to your paazl carrier
#. Select wanted paazl label format (PDF or ZPL) on delivery carrier options, if nothing is set default label format will be PDF.
#. Add a carrier account with delivery type ``paazl``, fill in your webshop id as account number, and the integration password as password. You find those values in Integrations/SOAP API on paazl's website
#. If you want to receive status updates (including the carrier's tracking url), you need to enable the push API in your paazl account, and fill in ``https://$yourdomain/_paazl/push_api/v1`` as notification URL. You should enable client authentication and fill in the token generated there in the `Bearer token` field of the carrier account in Odoo

Expand All @@ -50,6 +51,7 @@ To use this module, you need to:
#. Set exactly one delivery option
#. As a label needs to be generated for the tracking code, there's no need to manually generate labels via the button
#. Note that the addon passes the package's dimensions if a packaging is set on the package, and on the product's dimensions otherwise
#. Depending on the label format settings attached to the carrier option, get .zpl or .pdf format labels.

Bug Tracker
===========
Expand Down
1 change: 1 addition & 0 deletions delivery_carrier_label_paazl/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"data/delivery_carrier.xml",
"data/delivery_carrier_template_option.xml",
"views/carrier_account.xml",
"views/delivery_carrier_option.xml",
],
"demo": [],
"external_dependencies": {"python": ["zeep"]},
Expand Down
1 change: 1 addition & 0 deletions delivery_carrier_label_paazl/models/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@
from . import carrier_account
from . import delivery_carrier
from . import stock_picking
from . import delivery_carrier_option
12 changes: 12 additions & 0 deletions delivery_carrier_label_paazl/models/delivery_carrier_option.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# License AGPL-3.0 or later (https://www.gnu.org/licenses/agpl).

from odoo import fields, models


class DeliveryCarrierOption(models.Model):
_inherit = 'delivery.carrier.option'

file_format = fields.Selection([("PDF", "PDF"), ("ZPL", "ZPL")])
delivery_type = fields.Selection(related="carrier_id.delivery_type")
shipping_method_type = fields.Selection([('delivery', 'Delivery'), ('servicepoint', 'Service point')],
default="delivery")
40 changes: 37 additions & 3 deletions delivery_carrier_label_paazl/models/stock_picking.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,11 @@ def _paazl_generate_labels(self):
# as we have the labels anyways, attach them
account = self._get_carrier_account()
file_type = (account.file_format or "").lower() or "pdf"
options_paazl_label_format = self.option_ids.filtered(
lambda x: x.file_format
)
if options_paazl_label_format:
file_type = options_paazl_label_format[0].file_format.lower()
label = {
"name": self._paazl_reference(),
"file": base64.b64encode(status.labels),
Expand Down Expand Up @@ -201,15 +206,22 @@ def _paazl_commit_order_data(self):
shipping_address["customerName"] = shipping_address.pop("addresseeLine")
if self.partner_id.parent_id:
shipping_address["companyName"] = self.partner_id.parent_id.name
if self.option_ids[:1].shipping_method_type == "servicepoint":
identifier = self._paazl_get_servicepoints().shippingOption[0]["servicePoint"][0]["code"]
servicepointNotificationEmail = self.partner_id.email
else:
identifier = 0
servicepointNotificationEmail = False
return dict(
**self._paazl_auth_order_id(),
pendingOrderReference=self._paazl_reference(),
customerEmail=self.partner_id.email,
customerPhoneNumber=self.partner_id.phone,
shippingMethod=dict(
type="delivery",
identifier=0,
type=self.option_ids[:1].shipping_method_type,
identifier=identifier,
option=self.option_ids[:1].code,
servicepointNotificationEmail=servicepointNotificationEmail,
orderWeight=sum(
product["quantity"] * product["weight"]
for product in self._paazl_order_data()["products"]["product"]
Expand Down Expand Up @@ -261,9 +273,15 @@ def _paazl_generate_labels_data(self):
self.ensure_one()
auth_order_id = self._paazl_auth_order_id()
account = self._get_carrier_account()
file_format = account.file_format or "PDF"
options_paazl_label_format = self.option_ids.filtered(
lambda x: x.file_format
)
if options_paazl_label_format:
file_format = options_paazl_label_format[0].file_format
return {
"webshop": auth_order_id.pop("webshop"),
"labelFormat": account.file_format or "PDF",
"labelFormat": file_format,
"order": auth_order_id,
"includeMetaData": True,
}
Expand All @@ -287,3 +305,19 @@ def _paazl_reference(self):
"""Return a reference for a picking to be used for paazl"""
self.ensure_one()
return self.name

def _paazl_get_servicepoints(self):
"""Return servicepoints response for the order's partner hardcoded to only return the nearest DHL servicepoint
"""
api = self._paazl_api()
account = self._get_carrier_account()
country = self.partner_id.country_id.code
postcode = self.partner_id.zip
hash_data = hashlib.sha1((account.account + account.password + country + postcode).encode()).hexdigest()
webshop = account.account
response = api.service.servicePoints(
hash=hash_data, webshop=webshop, country=country, postcode=postcode,
shippingOption="DHL_PARCEL_CONNECT_SHOP",
limit=1,
)
return response
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ def _setup_mock_client(self):
mock_client.service.generateLabels.return_value = Mock(
error=False, labels=b"hello world", metaData=[{"trackingNumber": "number"}],
)
mock_client.service.servicePoints.return_value = Mock(
error=False, shippingOption=[{"servicePoint": [{"code": "1234-NL-123456"}]}],
)
with patch.object(zeep, "Client") as mock_client_class:
mock_client_class.return_value = mock_client
yield mock_client
Expand Down Expand Up @@ -114,6 +117,7 @@ def _request(self, path, method="POST", data=None, headers=None):
class TestDeliveryCarrierLabelPaazl(
DeliveryCarrierLabelPaazlCase,
carrier_label_case.TestCarrierLabel):

def test_cancel_shipment(self):
self.assertTrue(self.picking.carrier_tracking_ref)
with self._setup_mock_client() as mock_client:
Expand Down Expand Up @@ -176,6 +180,23 @@ def test_push_api(self):
)
self.assertTrue(self.picking.message_ids - messages_before)

def test_label_type_format_from_paazl(self):
"""
Test if getting .zpl format label after using delivery option with zpl format
"""
picking = self.picking.copy()
picking.option_ids.file_format = "ZPL"
self.assertEqual(picking.option_ids.file_format, "ZPL")
picking.move_lines.quantity_done = 1
with self._setup_mock_client():
payload = picking._paazl_generate_labels()

labels = payload["labels"][0]
self.assertTrue(labels)
self.assertTrue(labels["file"])
self.assertEqual(labels["file_type"], "zpl")
self.assertEqual(labels["filename"].split('.')[1], "zpl")

def test_change_order(self):
"""Test change order in paazl"""
self.assertTrue(self.picking.carrier_tracking_ref)
Expand All @@ -186,6 +207,18 @@ def test_change_order(self):
self.picking._paazl_send_update_order(change_order=True)
self.assertTrue(self.picking.carrier_tracking_ref)

def test_servicepoint_order(self):
"""Test committing an order with type servicepoint"""
picking = self.picking.copy()
picking.option_ids.shipping_method_type = "servicepoint"
picking.partner_id.country_id = self.env.ref("base.nl")
picking.partner_id.zip = "1092AD"
with self._setup_mock_client() as mock_client:
mock_client.service.servicePoints.return_value = Mock(
error=False, shippingOption=[{"servicePoint": [{"code": "1234-NL-123456"}]}],
)
picking._paazl_send_update_order()


class TestDeliveryCarrierLabelPaazlPackaging(TestDeliveryCarrierLabelPaazl):
def _picking_data(self):
Expand Down
15 changes: 15 additions & 0 deletions delivery_carrier_label_paazl/views/delivery_carrier_option.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<odoo>
<record id="delivery_carrier_option_view_form" model="ir.ui.view">
<field name="name">delivery_base.delivery_carrier_option.view_form</field>
<field name="model">delivery.carrier.option</field>
<field name="inherit_id" ref="base_delivery_carrier_label.delivery_carrier_option_view_form"/>
<field name="arch" type="xml">
<field name="tmpl_option_id" position="after">
<field name="delivery_type" invisible="1"/>
<field name="file_format" attrs="{'invisible': [('delivery_type', '!=', 'paazl')]}"/>
<field name="shipping_method_type" attrs="{'invisible': [('delivery_type', '!=', 'paazl')]}"/>
</field>
</field>
</record>
</odoo>