details: https://code.tryton.org/tryton/commit/57e59f5abf45
branch: default
user: Cédric Krier <[email protected]>
date: Wed Jun 24 11:01:19 2026 +0200
description:
Update DPD API to Shipment Service 4.5
diffstat:
modules/stock_package_shipping_dpd/CHANGELOG | 1 +
modules/stock_package_shipping_dpd/configuration.py | 2 +-
modules/stock_package_shipping_dpd/doc/index.rst | 2 +-
modules/stock_package_shipping_dpd/stock.py | 92 +++++++++++++++-----
4 files changed, 69 insertions(+), 28 deletions(-)
diffs (176 lines):
diff -r 936431c951bb -r 57e59f5abf45
modules/stock_package_shipping_dpd/CHANGELOG
--- a/modules/stock_package_shipping_dpd/CHANGELOG Mon Jul 06 09:07:08
2026 +0200
+++ b/modules/stock_package_shipping_dpd/CHANGELOG Wed Jun 24 11:01:19
2026 +0200
@@ -1,3 +1,4 @@
+* Update to Shipment Service 4.5
Version 8.0.0 - 2026-04-20
--------------------------
diff -r 936431c951bb -r 57e59f5abf45
modules/stock_package_shipping_dpd/configuration.py
--- a/modules/stock_package_shipping_dpd/configuration.py Mon Jul 06
09:07:08 2026 +0200
+++ b/modules/stock_package_shipping_dpd/configuration.py Wed Jun 24
11:01:19 2026 +0200
@@ -13,7 +13,7 @@
}
LOGIN_SERVICE = 'LoginService/V2_0/?wsdl'
-SHIPMENT_SERVICE = 'ShipmentService/V4_4/?wsdl'
+SHIPMENT_SERVICE = 'ShipmentService/V4_5/?wsdl'
def get_client(server, service):
diff -r 936431c951bb -r 57e59f5abf45
modules/stock_package_shipping_dpd/doc/index.rst
--- a/modules/stock_package_shipping_dpd/doc/index.rst Mon Jul 06 09:07:08
2026 +0200
+++ b/modules/stock_package_shipping_dpd/doc/index.rst Wed Jun 24 11:01:19
2026 +0200
@@ -6,7 +6,7 @@
DPD has many different web services, the module supports:
- `LoginService 2.0
<https://esolutions.dpd.com/dokumente/LoginService_V2_0.pdf>`_
-- `ShipmentService 4.4
<https://esolutions.dpd.com/dokumente/ShipmentService_V4_4.pdf>`_
+- `ShipmentService 4.5
<https://esolutions.dpd.com/dokumente/ShipmentService_V4_5.pdf>`_
Carrier Credential
******************
diff -r 936431c951bb -r 57e59f5abf45 modules/stock_package_shipping_dpd/stock.py
--- a/modules/stock_package_shipping_dpd/stock.py Mon Jul 06 09:07:08
2026 +0200
+++ b/modules/stock_package_shipping_dpd/stock.py Wed Jun 24 11:01:19
2026 +0200
@@ -234,6 +234,9 @@
mobile = address.contact_mechanism_get('mobile', usage=usage)
if mobile and len(mobile.value) <= 30:
shipping_party['mobile'] = mobile.value
+ fax = address.contact_mechanism_get('fax', usage=usage)
+ if fax and len(fax.value) <= 30:
+ shipping_party['fax'] = fax.value
email = address.contact_mechanism_get('email', usage=usage)
if email and 5 <= len(email.value) <= 100:
shipping_party['email'] = email.value
@@ -376,25 +379,30 @@
'customsAmount': int(
amount.quantize(Decimal('.01')) * Decimal(100)),
'customsCurrency': currency.code,
- 'customsTerms': self.get_customs_terms(shipment),
'customsPaper': 'A',
'customsInvoice': shipment.number[:20],
'customsInvoiceDate': invoice_date.strftime('%Y%m%d'),
+ 'reasonForExport': self.get_international_form_invoice_reason(
+ shipment),
}
if customs_agent := shipment.customs_agent:
- international.update({
- 'commercialInvoiceConsigneeVatNumber': (
- customs_agent.tax_identifier.code_compact)[:20],
- 'commercialInvoiceConsignee': self.shipping_party(
- customs_agent.party,
- customs_agent.address,
- with_contact=True),
- })
- if shipment.tax_identifier:
- international['commercialInvoiceConsignorVatNumber'] = (
- shipment.tax_identifier.code_compact[:17])
- international['commercialInvoiceConsignor'] = self.shipping_party(
- shipment.company.party, shipment.customs_from_address)
+ consignee = self.shipping_party(
+ customs_agent.party,
+ customs_agent.address,
+ with_contact=True)
+ consignee['vatNumber'] = (
+ customs_agent.tax_identifier.code_compact[:20])
+ international['commercialInvoiceConsignee'] = consignee
+ if company_party := shipment.company_party:
+ consignor = self.shipping_party(
+ company_party,
+ shipment.customs_from_address,
+ with_contact=True)
+ if (shipment.tax_identifier
+ and shipment.tax_identifier.type == 'eu_vat'):
+ consignor['eori'] = (
+ shipment.tax_identifier.code_compact[:20])
+ international['commercialInvoiceConsignor'] = consignor
international['additionalInvoiceLines'] = [
{'customsInvoicePosition': i,
**self.get_international_invoice_line(shipment, *k, **v)}
@@ -402,26 +410,47 @@
shipment.customs_products.items(), 1)]
international['numberOfArticle'] = len(
international['additionalInvoiceLines'])
+ international['clearanceCleared'] = 'F'
+ if shipment.incoterm:
+ international['incoterm'] = shipment.incoterm.code
+ if shipment.incoterm_location:
+ if shipment.incoterm_location.city:
+ international['incotermLocationTown'] = (
+ shipment.incoterm_location.city)
+ if shipment.incoterm_location.country:
+ international['incotermLocationCountryCode'] = (
+ shipment.incoterm_location.country.code)
+ international['paymentTerm'] = self.get_payment_terms(shipment)
product_and_service['international'] = international
return product_and_service
- def get_customs_terms(self, shipment):
+ def get_payment_terms(self, shipment):
if shipment and shipment.incoterm:
- if shipment.incoterm.code == 'DAP':
- return '01'
- elif shipment.incoterm.code == 'DDP':
- return '03'
- elif shipment.incoterm.code == 'EXW':
- return '05'
+ if shipment.incoterm.import_duties == 'buyer':
+ return 'A'
+ elif shipment.incoterm.import_duties == 'seller':
+ return 'B'
+
+ def get_international_form_invoice_reason(self, shipment):
+ return {
+ 'stock.shipment.out': '01',
+ 'stock.shipment.in.return': '02',
+ }.get(shipment.__class__.__name__)
def get_international_invoice_line(
self, shipment, product, price, currency, unit, quantity, weight):
- tariff_code = product.get_tariff_code({
+ recipient_tarrif_code = product.get_tariff_code({
'date': shipment.effective_date or shipment.planned_date,
'country': (
shipment.customs_to_country.id
if shipment.customs_to_country else None),
})
+ sender_tarrif_code = product.get_tariff_code({
+ 'date': shipment.effective_date or shipment.planned_date,
+ 'country': (
+ shipment.customs_from_country.id
+ if shipment.customs_from_country else None),
+ })
weight = round(weight, 2)
@@ -429,14 +458,25 @@
if not quantity.is_integer():
quantity = 1
+ quantity = int(quantity)
+ amount = int(
+ value.quantize(Decimal('.01')) * Decimal(100))
+ unit_price = amount // quantity
+
return {
- 'quantityItems': int(quantity),
+ 'quantityItems': quantity,
'customsContent': product.name[:200],
- 'customsTarif': tariff_code.code if tariff_code else None,
- 'customsAmountLine': int(
- value.quantize(Decimal('.01')) * Decimal(100)),
+ 'customsAmountLine': amount,
'customsOrigin': (
product.country_of_origin.code_numeric
if product.country_of_origin else None),
+ 'customsOriginAlphanumeric': (
+ product.country_of_origin.code
+ if product.country_of_origin else None),
'customsGrossWeight': int(weight * 100),
+ 'recipientCountryTarif': (
+ recipient_tarrif_code.code if recipient_tarrif_code else None),
+ 'senderCountryTarif': (
+ sender_tarrif_code.code if sender_tarrif_code else None),
+ 'unitPrice': unit_price,
}