Files
vontor-cz/backend/thirdparty/deutschepost/serializers.py
Brunobrno 3a7044d551 Enhance Deutsche Post integration with API and label support
Expanded DeutschePostOrder and DeutschePostBulkOrder models to support full Deutsche Post API integration, including authentication, order creation, finalization, tracking, cancellation, and label/document generation. Added new fields for label PDFs and bulk paperwork, improved country mapping, and implemented comprehensive validation and utility methods. Updated serializers to expose new fields and computed properties. Added HTML templates for individual and bulk shipping labels.
2026-01-23 00:47:19 +01:00

186 lines
7.9 KiB
Python

from rest_framework import serializers
from rest_framework.exceptions import ValidationError
from .models import DeutschePostOrder, DeutschePostBulkOrder
class DeutschePostOrderSerializer(serializers.ModelSerializer):
state_display = serializers.CharField(source='get_state_display', read_only=True)
label_size_display = serializers.CharField(source='get_label_size_display', read_only=True)
tracking_url = serializers.SerializerMethodField(read_only=True)
estimated_delivery_days = serializers.SerializerMethodField(read_only=True)
shipping_cost_estimate = serializers.SerializerMethodField(read_only=True)
can_be_finalized = serializers.SerializerMethodField(read_only=True)
can_be_cancelled = serializers.SerializerMethodField(read_only=True)
is_trackable = serializers.SerializerMethodField(read_only=True)
class Meta:
model = DeutschePostOrder
fields = [
'id', 'created_at', 'state', 'state_display',
'order_id', 'customer_ekp',
'recipient_name', 'recipient_phone', 'recipient_email',
'address_line1', 'address_line2', 'address_line3',
'city', 'address_state', 'postal_code', 'destination_country',
'product_type', 'service_level', 'shipment_gross_weight',
'shipment_amount', 'shipment_currency',
'sender_tax_id', 'importer_tax_id', 'return_item_wanted',
'cust_ref', 'awb_number', 'barcode', 'tracking_url',
'label_pdf', 'label_size', 'label_size_display',
'metadata', 'last_error',
'estimated_delivery_days', 'shipping_cost_estimate',
'can_be_finalized', 'can_be_cancelled', 'is_trackable'
]
read_only_fields = [
'id', 'created_at', 'order_id', 'awb_number', 'barcode',
'tracking_url', 'metadata', 'last_error', 'label_pdf'
]
def get_tracking_url(self, obj):
return obj.get_tracking_url()
def get_estimated_delivery_days(self, obj):
return obj.get_estimated_delivery_days()
def get_shipping_cost_estimate(self, obj):
return float(obj.get_shipping_cost_estimate())
def get_can_be_finalized(self, obj):
return obj.can_be_finalized()
def get_can_be_cancelled(self, obj):
return obj.can_be_cancelled()
def get_is_trackable(self, obj):
return obj.is_trackable()
def validate_destination_country(self, value):
"""Validate country code format."""
if len(value) != 2:
raise ValidationError("Country code must be 2 characters (ISO format)")
return value.upper()
def validate_shipment_gross_weight(self, value):
"""Validate weight is reasonable."""
if value <= 0:
raise ValidationError("Shipment weight must be greater than 0")
if value > 30000: # 30kg limit for most Deutsche Post products
raise ValidationError("Shipment weight cannot exceed 30kg (30000g)")
return value
def validate(self, attrs):
"""Validate the complete order data."""
# Check required fields for shipping
required_fields = ['recipient_name', 'address_line1', 'city', 'postal_code', 'destination_country']
missing_fields = [field for field in required_fields if not attrs.get(field)]
if missing_fields:
raise ValidationError(f"Required fields missing: {', '.join(missing_fields)}")
# Check contact information
if not attrs.get('recipient_email') and not attrs.get('recipient_phone'):
raise ValidationError("Either recipient email or phone is required for delivery notifications")
return attrs
class DeutschePostBulkOrderSerializer(serializers.ModelSerializer):
deutschepost_order_ids = serializers.ListField(
child=serializers.IntegerField(),
write_only=True,
required=True,
help_text="List of DeutschePostOrder IDs to include in bulk order"
)
status_display = serializers.CharField(source='get_status_display', read_only=True)
orders_count = serializers.SerializerMethodField(read_only=True)
total_weight_kg = serializers.SerializerMethodField(read_only=True)
tracking_url = serializers.SerializerMethodField(read_only=True)
status_url = serializers.SerializerMethodField(read_only=True)
can_be_cancelled = serializers.SerializerMethodField(read_only=True)
class Meta:
model = DeutschePostBulkOrder
fields = [
'id', 'created_at', 'status', 'status_display',
'bulk_order_id', 'bulk_order_type', 'description',
'deutschepost_orders', 'deutschepost_order_ids', 'orders_count',
'total_weight_kg', 'tracking_url', 'status_url', 'can_be_cancelled',
'bulk_label_pdf', 'paperwork_pdf',
'metadata', 'last_error'
]
read_only_fields = [
'id', 'created_at', 'bulk_order_id', 'deutschepost_orders',
'bulk_label_pdf', 'paperwork_pdf', 'metadata', 'last_error'
]
def get_orders_count(self, obj):
return obj.deutschepost_orders.count()
def get_total_weight_kg(self, obj):
return obj.get_total_weight_kg()
def get_tracking_url(self, obj):
return obj.get_tracking_url()
def get_status_url(self, obj):
return obj.get_bulk_status_url()
def get_can_be_cancelled(self, obj):
return obj.can_be_cancelled()
def validate_deutschepost_order_ids(self, value):
"""Validate that all orders exist and are eligible for bulk processing."""
if not value:
raise ValidationError("At least one Deutsche Post order is required")
errors = []
orders = DeutschePostOrder.objects.filter(id__in=value)
if len(orders) != len(value):
missing_ids = set(value) - set(orders.values_list('id', flat=True))
errors.append(f"Orders not found: {missing_ids}")
for order in orders:
# Check if order is created remotely
if not order.order_id:
errors.append(f"Order {order.id} not created remotely")
# Check if commerce order uses Deutsche Post
if order.commerce_order:
if not order.commerce_order.carrier:
errors.append(f"Commerce order {order.commerce_order.id} has no carrier")
elif order.commerce_order.carrier.shipping_method != "deutschepost":
errors.append(f"Commerce order {order.commerce_order.id} doesn't use Deutsche Post")
elif order.commerce_order.status != order.commerce_order.OrderStatus.COMPLETED:
errors.append(f"Commerce order {order.commerce_order.id} is not completed")
# Check if order is already in another bulk order
if order.bulk_orders.exists():
errors.append(f"Order {order.id} already in bulk order")
if errors:
raise ValidationError(errors)
return value
def create(self, validated_data):
order_ids = validated_data.pop('deutschepost_order_ids')
bulk_order = super().create(validated_data)
# Add orders to bulk order
orders = DeutschePostOrder.objects.filter(id__in=order_ids)
bulk_order.deutschepost_orders.set(orders)
return bulk_order
class DeutschePostTrackingSerializer(serializers.Serializer):
"""Serializer for tracking information response."""
order_id = serializers.CharField(read_only=True)
awb_number = serializers.CharField(read_only=True)
barcode = serializers.CharField(read_only=True)
tracking_url = serializers.URLField(read_only=True)
state = serializers.CharField(read_only=True)
last_updated = serializers.DateTimeField(read_only=True)
tracking_events = serializers.JSONField(read_only=True)