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.
186 lines
7.9 KiB
Python
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) |