from django.shortcuts import render from rest_framework import viewsets, mixins, status from rest_framework.decorators import action from rest_framework.response import Response from rest_framework.permissions import IsAdminUser from drf_spectacular.utils import ( extend_schema, extend_schema_view, OpenApiResponse, OpenApiParameter, OpenApiTypes ) from .models import DeutschePostOrder, DeutschePostBulkOrder from .serializers import ( DeutschePostOrderSerializer, DeutschePostBulkOrderSerializer, DeutschePostTrackingSerializer, ) @extend_schema_view( list=extend_schema( tags=["deutschepost"], summary="List Deutsche Post Orders", description="Returns a paginated list of Deutsche Post shipping orders. Orders are created internally through Carrier model. Admin only access.", responses=DeutschePostOrderSerializer(many=True), ), retrieve=extend_schema( tags=["deutschepost"], summary="Get Deutsche Post Order Detail", description="Returns detailed information for a single Deutsche Post order including tracking data. Orders are managed through Carrier model.", responses=DeutschePostOrderSerializer, ), create=extend_schema( tags=["deutschepost"], summary="Create Deutsche Post Order", description="Create a new Deutsche Post order with recipient and shipment details. Order will automatically be created remotely if validation passes.", request=DeutschePostOrderSerializer, responses={ 201: DeutschePostOrderSerializer, 400: OpenApiResponse(description="Validation error") }, ), update=extend_schema( tags=["deutschepost"], summary="Update Deutsche Post Order", description="Update Deutsche Post order details. Cannot update orders that are already created remotely.", request=DeutschePostOrderSerializer, responses={ 200: DeutschePostOrderSerializer, 400: OpenApiResponse(description="Validation error or order already remote") }, ), partial_update=extend_schema( tags=["deutschepost"], summary="Partially Update Deutsche Post Order", description="Partially update Deutsche Post order details. Cannot update orders that are already created remotely.", request=DeutschePostOrderSerializer, responses={ 200: DeutschePostOrderSerializer, 400: OpenApiResponse(description="Validation error or order already remote") }, ), destroy=extend_schema( tags=["deutschepost"], summary="Delete Deutsche Post Order", description="Delete Deutsche Post order. If order is created remotely, it will attempt to cancel it first.", responses={ 204: OpenApiResponse(description="Order deleted successfully"), 400: OpenApiResponse(description="Cannot delete order") }, ), ) class DeutschePostOrderViewSet( viewsets.ModelViewSet # Changed to ModelViewSet for full CRUD ): queryset = DeutschePostOrder.objects.all().order_by("-created_at") serializer_class = DeutschePostOrderSerializer permission_classes = [IsAdminUser] @extend_schema( tags=["deutschepost"], summary="Refresh Tracking Information", description=""" Refresh tracking information from Deutsche Post API. This will: - Fetch latest order status from Deutsche Post - Update AWB number, barcode, and tracking URL - Update order state based on shipment status - Store raw tracking data in metadata Note: Orders are created and managed through the Carrier model in commerce app. """, request=None, responses={ 200: DeutschePostTrackingSerializer, 400: OpenApiResponse(description="Order not created remotely"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def refresh_tracking(self, request, pk=None): """Refresh tracking information from Deutsche Post API.""" order = self.get_object() try: order.refresh_tracking() # Return tracking information tracking_data = { 'order_id': order.order_id, 'awb_number': order.awb_number, 'barcode': order.barcode, 'tracking_url': order.tracking_url, 'state': order.state, 'last_updated': order.created_at, # Use created_at as proxy 'tracking_events': order.metadata.get('tracking_events', []) } serializer = DeutschePostTrackingSerializer(tracking_data) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Cancel Deutsche Post Order", description=""" Cancel the order in Deutsche Post API system. This will: - Call Deutsche Post API to cancel the order - Update the order state to CANCELLED - Cannot be undone once cancelled - Only works for orders that are not yet shipped or delivered Note: Orders are created and managed through the Carrier model in commerce app. """, request=None, responses={ 200: DeutschePostOrderSerializer, 400: OpenApiResponse(description="Order cannot be cancelled or API error"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def cancel_order(self, request, pk=None): """Cancel order in Deutsche Post API.""" order = self.get_object() try: order.cancel_remote_order() serializer = self.get_serializer(order) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Finalize Deutsche Post Order", description=""" Finalize the order in Deutsche Post API system. This will: - Call Deutsche Post API to finalize the order - Update the order state to FINALIZED - Retrieve AWB number and barcode for tracking - Enable label generation - Required before the order can be shipped """, request=None, responses={ 200: DeutschePostOrderSerializer, 400: OpenApiResponse(description="Order cannot be finalized or API error"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def finalize_order(self, request, pk=None): """Finalize order in Deutsche Post API.""" order = self.get_object() try: order.finalize_remote_order() serializer = self.get_serializer(order) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Generate Shipping Label", description=""" Generate and download shipping label PDF. This will: - Attempt to download label from Deutsche Post API - If API label not available, generate simple label with order details - Save label PDF to order.label_pdf field - Include barcode and tracking information - Requires order to be created remotely first """, request=None, responses={ 200: OpenApiResponse(description="Label generated successfully", examples={ "application/json": { "message": "Label generated successfully", "label_url": "/media/deutschepost/labels/label_123.pdf" } }), 400: OpenApiResponse(description="Order not ready for label generation or API error"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def generate_label(self, request, pk=None): """Generate shipping label PDF.""" order = self.get_object() try: order.generate_shipping_label() label_url = order.label_pdf.url if order.label_pdf else None return Response({ 'message': 'Label generated successfully', 'label_url': label_url, 'order_id': order.order_id, 'awb_number': order.awb_number }) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Get Tracking URL", description=""" Get the public tracking URL for the order that customers can use to track their shipment. Returns the URL if available, or null if tracking is not yet available. """, request=None, responses={ 200: OpenApiResponse(description="Tracking URL response", examples={ "application/json": { "tracking_url": "https://www.deutschepost.de/de/s/sendungsverfolgung.html?piececode=ABC123" } }), 404: OpenApiResponse(description="Tracking URL not available") } ) @action(detail=True, methods=['get']) def get_tracking_url(self, request, pk=None): """Get public tracking URL for the order.""" order = self.get_object() tracking_url = order.get_tracking_url() if tracking_url: return Response({'tracking_url': tracking_url}) else: return Response( {'error': 'Tracking URL not available yet'}, status=status.HTTP_404_NOT_FOUND ) @extend_schema_view( list=extend_schema( tags=["deutschepost"], summary="List Deutsche Post Bulk Orders", description="Returns a paginated list of Deutsche Post bulk shipping orders. Admin only access.", responses=DeutschePostBulkOrderSerializer(many=True), ), retrieve=extend_schema( tags=["deutschepost"], summary="Get Deutsche Post Bulk Order Detail", description="Returns detailed information for a single Deutsche Post bulk order including all constituent orders.", responses=DeutschePostBulkOrderSerializer, ), create=extend_schema( tags=["deutschepost"], summary="Create Deutsche Post Bulk Order", description=""" Create a new Deutsche Post bulk order from multiple individual orders. Requirements: - All orders must be created remotely (have order_id) - All linked commerce orders must use Deutsche Post delivery - All linked commerce orders must be completed - Orders cannot already be part of another bulk order """, request=DeutschePostBulkOrderSerializer, responses={ 201: DeutschePostBulkOrderSerializer, 400: OpenApiResponse(description="Validation error - orders not eligible for bulk processing") }, ), ) class DeutschePostBulkOrderViewSet( mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.CreateModelMixin, viewsets.GenericViewSet ): queryset = DeutschePostBulkOrder.objects.all().order_by("-created_at") serializer_class = DeutschePostBulkOrderSerializer permission_classes = [IsAdminUser] @extend_schema( tags=["deutschepost"], summary="Create Remote Bulk Order", description=""" Create the bulk order in Deutsche Post API system. This will: - Validate all constituent orders are eligible - Call Deutsche Post API to create the bulk order - Store the returned bulk_order_id - Update the bulk order status to PROCESSING - Handle API errors and validation """, request=None, responses={ 200: DeutschePostBulkOrderSerializer, 400: OpenApiResponse(description="Bulk order already created or validation error"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def create_remote(self, request, pk=None): """Create bulk order in Deutsche Post API.""" bulk_order = self.get_object() try: bulk_order.create_remote_bulk_order() serializer = self.get_serializer(bulk_order) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Cancel Remote Bulk Order", description=""" Cancel the bulk order in Deutsche Post API system. This will: - Call Deutsche Post API to cancel the bulk order - Update the bulk order status to ERROR (cancelled state) - Cannot be undone once cancelled - Only works for bulk orders that are not yet completed """, request=None, responses={ 200: DeutschePostBulkOrderSerializer, 400: OpenApiResponse(description="Bulk order cannot be cancelled or validation error"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def cancel_bulk_order(self, request, pk=None): """Cancel bulk order in Deutsche Post API.""" bulk_order = self.get_object() try: bulk_order.cancel_bulk_order() serializer = self.get_serializer(bulk_order) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Get Bulk Order URLs", description=""" Get tracking and status URLs for the bulk order. Returns: - tracking_url: Public URL for tracking bulk shipment progress - status_url: URL for monitoring bulk order processing status """, request=None, responses={ 200: OpenApiResponse(description="Bulk order URLs", examples={ "application/json": { "tracking_url": "https://www.deutschepost.de/de/b/bulk-tracking.html?bulk_id=BULK123", "status_url": "https://www.deutschepost.de/de/b/bulk-status.html?bulk_id=BULK123" } }), 404: OpenApiResponse(description="Bulk order not created remotely yet") } ) @action(detail=True, methods=['get']) def get_urls(self, request, pk=None): """Get tracking and status URLs for bulk order.""" bulk_order = self.get_object() tracking_url = bulk_order.get_tracking_url() status_url = bulk_order.get_bulk_status_url() if tracking_url or status_url: return Response({ 'tracking_url': tracking_url, 'status_url': status_url, 'bulk_order_id': bulk_order.bulk_order_id }) else: return Response( {'error': 'Bulk order not created remotely yet'}, status=status.HTTP_404_NOT_FOUND ) @extend_schema( tags=["deutschepost"], summary="Refresh Bulk Order Status", description=""" Refresh bulk order status from Deutsche Post API. This will: - Fetch latest bulk order status from Deutsche Post - Update bulk order status (PROCESSING, COMPLETED, etc.) - Store raw API response data in metadata - Update individual order statuses if needed """, request=None, responses={ 200: DeutschePostBulkOrderSerializer, 400: OpenApiResponse(description="Bulk order not created remotely"), 500: OpenApiResponse(description="Deutsche Post API error") } ) @action(detail=True, methods=['post']) def refresh_status(self, request, pk=None): """Refresh bulk order status from Deutsche Post API.""" bulk_order = self.get_object() try: bulk_order.refresh_bulk_status() serializer = self.get_serializer(bulk_order) return Response(serializer.data) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST ) @extend_schema( tags=["deutschepost"], summary="Generate Bulk Labels", description=""" Generate combined PDF with all order labels for bulk shipment. This will: - Create multi-page PDF with all order details - Include recipient addresses and tracking information - Format for warehouse processing - Save PDF to bulk_order.bulk_label_pdf field """, request=None, responses={ 200: OpenApiResponse(description="Bulk labels generated successfully", examples={ "application/json": { "message": "Bulk labels generated successfully", "labels_url": "/media/deutschepost/bulk_labels/bulk_labels_123.pdf", "orders_count": 5 } }), 400: OpenApiResponse(description="No orders in bulk shipment or generation error"), } ) @action(detail=True, methods=['post']) def generate_labels(self, request, pk=None): """Generate bulk shipping labels PDF.""" bulk_order = self.get_object() try: bulk_order.generate_bulk_labels() labels_url = bulk_order.bulk_label_pdf.url if bulk_order.bulk_label_pdf else None return Response({ 'message': 'Bulk labels generated successfully', 'labels_url': labels_url, 'bulk_order_id': bulk_order.bulk_order_id, 'orders_count': bulk_order.deutschepost_orders.count() }) except Exception as e: return Response( {'error': str(e)}, status=status.HTTP_400_BAD_REQUEST )