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.
This commit is contained in:
2026-01-23 00:47:19 +01:00
parent 963ba6b824
commit 3a7044d551
5 changed files with 1492 additions and 121 deletions

View File

@@ -31,11 +31,48 @@ from .serializers import (
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(
mixins.ListModelMixin,
mixins.RetrieveModelMixin,
viewsets.GenericViewSet
viewsets.ModelViewSet # Changed to ModelViewSet for full CRUD
):
queryset = DeutschePostOrder.objects.all().order_by("-created_at")
serializer_class = DeutschePostOrderSerializer
@@ -112,7 +149,7 @@ class DeutschePostOrderViewSet(
order = self.get_object()
try:
order.cancel_order()
order.cancel_remote_order()
serializer = self.get_serializer(order)
return Response(serializer.data)
except Exception as e:
@@ -121,6 +158,83 @@ class DeutschePostOrderViewSet(
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",
@@ -298,3 +412,78 @@ class DeutschePostBulkOrderViewSet(
{'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
)