from rest_framework import serializers from rest_framework.exceptions import ValidationError from .models import DeutschePostOrder, DeutschePostBulkOrder class DeutschePostOrderSerializer(serializers.ModelSerializer): commerce_order_id = serializers.IntegerField(write_only=True, required=False) state_display = serializers.CharField(source='get_state_display', read_only=True) class Meta: model = DeutschePostOrder fields = [ 'id', 'created_at', 'state', 'state_display', 'commerce_order', 'commerce_order_id', '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', 'metadata', 'last_error' ] read_only_fields = [ 'id', 'created_at', 'order_id', 'awb_number', 'barcode', 'tracking_url', 'metadata', 'last_error' ] def validate_commerce_order_id(self, value): """Validate that commerce order exists and uses Deutsche Post delivery.""" try: from commerce.models import Order order = Order.objects.get(id=value) if not order.carrier: raise ValidationError("Commerce order must have a carrier assigned") if order.carrier.shipping_method != "deutschepost": raise ValidationError("Commerce order must use Deutsche Post delivery method") if order.status != Order.OrderStatus.COMPLETED: raise ValidationError("Commerce order must be completed before creating Deutsche Post order") return value except Order.DoesNotExist: raise ValidationError("Commerce order does not exist") 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 create(self, validated_data): commerce_order_id = validated_data.pop('commerce_order_id', None) if commerce_order_id: from commerce.models import Order validated_data['commerce_order'] = Order.objects.get(id=commerce_order_id) return super().create(validated_data) 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) tracking_url = serializers.SerializerMethodField(read_only=True) status_url = 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', 'tracking_url', 'status_url', 'metadata', 'last_error' ] read_only_fields = [ 'id', 'created_at', 'bulk_order_id', 'deutschepost_orders', 'metadata', 'last_error' ] def get_orders_count(self, obj): return obj.deutschepost_orders.count() def get_tracking_url(self, obj): return obj.get_tracking_url() def get_status_url(self, obj): return obj.get_bulk_status_url() 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)