forked from erp-dev/erp
feat: shipment_delivery
This commit is contained in:
@@ -10,11 +10,16 @@ from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
|
||||
from flower.viewsets import LimitedLimitOffsetPagination
|
||||
from shipment.models import Shipment
|
||||
from shipment.models import Shipment, ShipmentDelivery
|
||||
|
||||
from .serializers import (
|
||||
SalesItemDetailSerializer,
|
||||
SalesItemSerializer,
|
||||
ShipmentDeliveryBindShipmentsSerializer,
|
||||
ShipmentDeliveryCreateSerializer,
|
||||
ShipmentDeliverySerializer,
|
||||
ShipmentDeliveryStatusUpdateSerializer,
|
||||
ShipmentDeliveryUpdateSerializer,
|
||||
ShipmentSerializer,
|
||||
ShipmentCreateNormalSerializer,
|
||||
ShipmentCreateExternalSerializer,
|
||||
@@ -178,6 +183,9 @@ class ShipmentListCreateView(ListModelMixin, GenericAPIView):
|
||||
shipment_date=data["shipment_date"],
|
||||
sales_item_ids=data["sales_items"],
|
||||
created_by=request.user,
|
||||
address=data.get("address", ""),
|
||||
contact_name=data.get("contact_name", ""),
|
||||
contact_phone=data.get("contact_phone", ""),
|
||||
remark=data.get("remark", ""),
|
||||
area=data.get("area", ""),
|
||||
)
|
||||
@@ -246,6 +254,9 @@ class ShipmentDetailView(RetrieveModelMixin, GenericAPIView):
|
||||
shipment,
|
||||
customer_id=data.get("customer"),
|
||||
shipment_date=data.get("shipment_date"),
|
||||
address=data.get("address"),
|
||||
contact_name=data.get("contact_name"),
|
||||
contact_phone=data.get("contact_phone"),
|
||||
area=data.get("area"),
|
||||
remark=data.get("remark"),
|
||||
external_id=data.get("external_id"),
|
||||
@@ -263,6 +274,313 @@ class ShipmentDetailView(RetrieveModelMixin, GenericAPIView):
|
||||
return self.patch(request, pk=pk)
|
||||
|
||||
|
||||
class ShipmentDeliveryListCreateView(ListModelMixin, GenericAPIView):
|
||||
"""
|
||||
送货单:查询列表 / 创建
|
||||
|
||||
- GET /api/v1/shipment/deliveries/
|
||||
- POST /api/v1/shipment/deliveries/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = ShipmentDeliverySerializer
|
||||
pagination_class = LimitedLimitOffsetPagination
|
||||
|
||||
def get_queryset(self):
|
||||
qs = ShipmentDelivery.objects.all().select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
)
|
||||
|
||||
user = self.request.user
|
||||
if not getattr(user, "is_superuser", False):
|
||||
emp = getattr(user, "employee", None)
|
||||
merchant = getattr(emp, "merchant", None) if emp else None
|
||||
if not merchant:
|
||||
return ShipmentDelivery.objects.none()
|
||||
qs = qs.filter(merchant=merchant)
|
||||
|
||||
status_val = self.request.query_params.get("status")
|
||||
if status_val:
|
||||
qs = qs.filter(status=status_val)
|
||||
|
||||
driver_name = (self.request.query_params.get("driver_name") or "").strip()
|
||||
if driver_name:
|
||||
qs = qs.filter(driver_name__icontains=driver_name)
|
||||
|
||||
vehicle_trip = (self.request.query_params.get("vehicle_trip") or "").strip()
|
||||
if vehicle_trip:
|
||||
qs = qs.filter(vehicle_trip__icontains=vehicle_trip)
|
||||
|
||||
return qs.order_by("-created_at", "-id")
|
||||
|
||||
def get(self, request):
|
||||
return self.list(request)
|
||||
|
||||
def post(self, request):
|
||||
serializer = ShipmentDeliveryCreateSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
from shipment.services import create_shipment_delivery
|
||||
|
||||
try:
|
||||
delivery = create_shipment_delivery(
|
||||
driver_name=serializer.validated_data["driver_name"],
|
||||
vehicle_trip=serializer.validated_data["vehicle_trip"],
|
||||
contact_phone=serializer.validated_data.get("contact_phone", ""),
|
||||
vehicle_capacity=serializer.validated_data.get("vehicle_capacity", ""),
|
||||
shipment_ids=serializer.validated_data.get("shipments", []),
|
||||
created_by=request.user,
|
||||
remark=serializer.validated_data.get("remark", ""),
|
||||
internal_remark=serializer.validated_data.get("internal_remark", ""),
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
delivery = ShipmentDelivery.objects.select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
).get(id=delivery.id)
|
||||
|
||||
return Response(
|
||||
ShipmentDeliverySerializer(delivery).data,
|
||||
status=status.HTTP_201_CREATED,
|
||||
)
|
||||
|
||||
|
||||
class ShipmentDeliveryDetailView(RetrieveModelMixin, GenericAPIView):
|
||||
"""
|
||||
送货单详情 / 修改 / 删除
|
||||
|
||||
- GET /api/v1/shipment/deliveries/<id>/
|
||||
- PATCH /api/v1/shipment/deliveries/<id>/
|
||||
- PUT /api/v1/shipment/deliveries/<id>/
|
||||
- DELETE /api/v1/shipment/deliveries/<id>/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
serializer_class = ShipmentDeliverySerializer
|
||||
|
||||
def get_queryset(self):
|
||||
qs = ShipmentDelivery.objects.all().select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
)
|
||||
|
||||
user = self.request.user
|
||||
if getattr(user, "is_superuser", False):
|
||||
return qs
|
||||
|
||||
emp = getattr(user, "employee", None)
|
||||
merchant = getattr(emp, "merchant", None) if emp else None
|
||||
if not merchant:
|
||||
return ShipmentDelivery.objects.none()
|
||||
return qs.filter(merchant=merchant)
|
||||
|
||||
def get(self, request, pk: int):
|
||||
return self.retrieve(request, pk=pk)
|
||||
|
||||
def patch(self, request, pk: int):
|
||||
delivery = self.get_object()
|
||||
serializer = ShipmentDeliveryUpdateSerializer(data=request.data, partial=True)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
from shipment.services import update_shipment_delivery
|
||||
|
||||
try:
|
||||
delivery = update_shipment_delivery(
|
||||
delivery,
|
||||
driver_name=serializer.validated_data.get("driver_name"),
|
||||
vehicle_trip=serializer.validated_data.get("vehicle_trip"),
|
||||
contact_phone=serializer.validated_data.get("contact_phone"),
|
||||
vehicle_capacity=serializer.validated_data.get("vehicle_capacity"),
|
||||
shipment_ids=serializer.validated_data.get("shipments"),
|
||||
remark=serializer.validated_data.get("remark"),
|
||||
internal_remark=serializer.validated_data.get("internal_remark"),
|
||||
operator=request.user,
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
delivery = self.get_queryset().get(id=delivery.id)
|
||||
|
||||
return Response(
|
||||
ShipmentDeliverySerializer(delivery).data,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
def put(self, request, pk: int):
|
||||
return self.patch(request, pk=pk)
|
||||
|
||||
def delete(self, request, pk: int):
|
||||
delivery = self.get_object()
|
||||
|
||||
from shipment.services import delete_shipment_delivery
|
||||
|
||||
delete_shipment_delivery(delivery)
|
||||
return Response(status=status.HTTP_204_NO_CONTENT)
|
||||
|
||||
|
||||
class ShipmentDeliveryStatusUpdateView(APIView):
|
||||
"""
|
||||
修改送货单状态
|
||||
|
||||
POST /api/v1/shipment/deliveries/<id>/status/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk: int):
|
||||
user = request.user
|
||||
queryset = ShipmentDelivery.objects.all()
|
||||
if not getattr(user, "is_superuser", False):
|
||||
emp = getattr(user, "employee", None)
|
||||
merchant = getattr(emp, "merchant", None) if emp else None
|
||||
if not merchant:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
queryset = queryset.filter(merchant=merchant)
|
||||
|
||||
delivery = queryset.filter(id=pk).first()
|
||||
if delivery is None:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
serializer = ShipmentDeliveryStatusUpdateSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
from shipment.services import modify_shipment_delivery_status
|
||||
|
||||
try:
|
||||
delivery = modify_shipment_delivery_status(
|
||||
delivery,
|
||||
target_status=serializer.validated_data["status"],
|
||||
operator=request.user,
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
delivery = ShipmentDelivery.objects.select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
).get(id=delivery.id)
|
||||
|
||||
return Response(
|
||||
ShipmentDeliverySerializer(delivery).data,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
class ShipmentDeliveryCancelView(APIView):
|
||||
"""
|
||||
取消送货单
|
||||
|
||||
POST /api/v1/shipment/deliveries/<id>/cancel/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk: int):
|
||||
if not request.user.has_perm("shipment.cancel_shipmentdelivery"):
|
||||
return Response(
|
||||
{"detail": "没有权限取消送货单"},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
user = request.user
|
||||
queryset = ShipmentDelivery.objects.all()
|
||||
if not getattr(user, "is_superuser", False):
|
||||
emp = getattr(user, "employee", None)
|
||||
merchant = getattr(emp, "merchant", None) if emp else None
|
||||
if not merchant:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
queryset = queryset.filter(merchant=merchant)
|
||||
|
||||
delivery = queryset.filter(id=pk).first()
|
||||
if delivery is None:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
from shipment.services import cancel_shipment_delivery
|
||||
|
||||
delivery = cancel_shipment_delivery(
|
||||
delivery,
|
||||
cancelled_by=request.user,
|
||||
operator=request.user,
|
||||
)
|
||||
|
||||
delivery = ShipmentDelivery.objects.select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
).get(id=delivery.id)
|
||||
|
||||
return Response(
|
||||
ShipmentDeliverySerializer(delivery).data,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
class ShipmentDeliveryBindShipmentsView(APIView):
|
||||
"""
|
||||
追加绑定出货单到现有送货单
|
||||
|
||||
POST /api/v1/shipment/deliveries/<id>/bind-shipments/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def post(self, request, pk: int):
|
||||
user = request.user
|
||||
queryset = ShipmentDelivery.objects.all()
|
||||
if not getattr(user, "is_superuser", False):
|
||||
emp = getattr(user, "employee", None)
|
||||
merchant = getattr(emp, "merchant", None) if emp else None
|
||||
if not merchant:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
queryset = queryset.filter(merchant=merchant)
|
||||
|
||||
delivery = queryset.filter(id=pk).first()
|
||||
if delivery is None:
|
||||
return Response({"detail": "未找到送货单"}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
serializer = ShipmentDeliveryBindShipmentsSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
from shipment.services import bind_shipments_to_delivery
|
||||
|
||||
try:
|
||||
delivery = bind_shipments_to_delivery(
|
||||
delivery,
|
||||
shipment_ids=serializer.validated_data["shipments"],
|
||||
operator=request.user,
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
delivery = ShipmentDelivery.objects.select_related(
|
||||
"merchant", "created_by", "operator", "cancelled_by"
|
||||
).prefetch_related(
|
||||
"shipments",
|
||||
"shipments__customer",
|
||||
).get(id=delivery.id)
|
||||
|
||||
return Response(
|
||||
ShipmentDeliverySerializer(delivery).data,
|
||||
status=status.HTTP_200_OK,
|
||||
)
|
||||
|
||||
|
||||
class ShipmentExternalCreateView(APIView):
|
||||
"""
|
||||
创建出货单(external 版)
|
||||
@@ -293,6 +611,9 @@ class ShipmentExternalCreateView(APIView):
|
||||
external_id=data["external_id"],
|
||||
external_finished_products=data["external_finished_products"],
|
||||
created_by=request.user,
|
||||
address=data.get("address", ""),
|
||||
contact_name=data.get("contact_name", ""),
|
||||
contact_phone=data.get("contact_phone", ""),
|
||||
remark=data.get("remark", ""),
|
||||
area=data.get("area", ""),
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user