forked from erp-dev/erp
feat: salesitem can change/soft delete
This commit is contained in:
@@ -15,6 +15,8 @@ from shipment.models import Shipment, ShipmentDelivery
|
||||
from .serializers import (
|
||||
SalesItemDetailSerializer,
|
||||
SalesItemSerializer,
|
||||
SalesItemUpdateSerializer,
|
||||
ShipmentStatusUpdateSerializer,
|
||||
ShipmentDeliveryBindShipmentsSerializer,
|
||||
ShipmentDeliveryCreateSerializer,
|
||||
ShipmentDeliverySerializer,
|
||||
@@ -274,6 +276,69 @@ class ShipmentDetailView(RetrieveModelMixin, GenericAPIView):
|
||||
return self.patch(request, pk=pk)
|
||||
|
||||
|
||||
class ShipmentStatusUpdateView(APIView):
|
||||
"""
|
||||
更新出货单状态
|
||||
|
||||
PATCH /api/v1/shipment/shipments/<id>/status/
|
||||
PUT /api/v1/shipment/shipments/<id>/status/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get_queryset(self):
|
||||
qs = Shipment.objects.all().select_related(
|
||||
"merchant",
|
||||
"customer",
|
||||
"created_by",
|
||||
"cancelled_by",
|
||||
"approved_by",
|
||||
).prefetch_related(
|
||||
"items",
|
||||
"external_finished_products",
|
||||
)
|
||||
|
||||
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 Shipment.objects.none()
|
||||
return qs.filter(merchant=merchant)
|
||||
|
||||
def patch(self, request, pk: int):
|
||||
shipment = self.get_queryset().filter(id=pk).first()
|
||||
if shipment is None:
|
||||
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
serializer = ShipmentStatusUpdateSerializer(data=request.data)
|
||||
if not serializer.is_valid():
|
||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
from shipment.models import ShipmentStatus
|
||||
from shipment.services import modify_status
|
||||
|
||||
target_status = serializer.validated_data["status"]
|
||||
approved_by = request.user if target_status == ShipmentStatus.APPROVED else None
|
||||
|
||||
try:
|
||||
shipment = modify_status(
|
||||
shipment,
|
||||
target_status=target_status,
|
||||
operator=request.user,
|
||||
approved_by=approved_by,
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
return Response(ShipmentSerializer(shipment).data, status=status.HTTP_200_OK)
|
||||
|
||||
def put(self, request, pk: int):
|
||||
return self.patch(request, pk=pk)
|
||||
|
||||
|
||||
class ShipmentDeliveryListCreateView(ListModelMixin, GenericAPIView):
|
||||
"""
|
||||
送货单:查询列表 / 创建
|
||||
@@ -912,6 +977,7 @@ class SalesItemDetailView(GenericAPIView):
|
||||
销售品详情。
|
||||
|
||||
GET /api/v1/shipment/sales-items/<id>/
|
||||
PATCH /api/v1/shipment/sales-items/<id>/
|
||||
"""
|
||||
|
||||
permission_classes = [IsAuthenticated]
|
||||
@@ -920,7 +986,11 @@ class SalesItemDetailView(GenericAPIView):
|
||||
def get_queryset(self):
|
||||
from shipment.models import SalesItem
|
||||
|
||||
qs = SalesItem.objects.select_related("shipment", "created_by").order_by("id")
|
||||
qs = (
|
||||
SalesItem.objects.select_related("shipment", "created_by", "delete_by")
|
||||
.filter(delete_at__isnull=True)
|
||||
.order_by("id")
|
||||
)
|
||||
|
||||
user = self.request.user
|
||||
if getattr(user, "is_superuser", False):
|
||||
@@ -945,3 +1015,64 @@ class SalesItemDetailView(GenericAPIView):
|
||||
},
|
||||
)
|
||||
return Response(serializer.data)
|
||||
|
||||
def patch(self, request, pk: int):
|
||||
sales_item = self.get_queryset().filter(id=pk).first()
|
||||
if sales_item is None:
|
||||
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
allowed_fields = {"quantity", "remark", "position"}
|
||||
unexpected_fields = sorted(set(request.data.keys()) - allowed_fields)
|
||||
if unexpected_fields:
|
||||
return Response(
|
||||
{
|
||||
"detail": (
|
||||
"销售品仅允许修改以下字段: "
|
||||
f"{', '.join(sorted(allowed_fields))};"
|
||||
f"不支持字段: {', '.join(unexpected_fields)}"
|
||||
)
|
||||
},
|
||||
status=status.HTTP_400_BAD_REQUEST,
|
||||
)
|
||||
|
||||
serializer = SalesItemUpdateSerializer(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_sales_item
|
||||
|
||||
try:
|
||||
sales_item = update_sales_item(
|
||||
sales_item,
|
||||
quantity=serializer.validated_data.get("quantity"),
|
||||
remark=serializer.validated_data.get("remark"),
|
||||
position=serializer.validated_data.get("position"),
|
||||
operator=request.user,
|
||||
)
|
||||
except ValueError as e:
|
||||
return Response({"detail": str(e)}, status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
response_serializer = self.get_serializer(
|
||||
sales_item,
|
||||
context={
|
||||
**_build_sales_item_serializer_context([sales_item]),
|
||||
"request": request,
|
||||
},
|
||||
)
|
||||
return Response(response_serializer.data, status=status.HTTP_200_OK)
|
||||
|
||||
def delete(self, request, pk: int):
|
||||
if not request.user.has_perm("shipment.soft_delete_salesitem"):
|
||||
return Response(
|
||||
{"detail": "没有权限删除销售品"},
|
||||
status=status.HTTP_403_FORBIDDEN,
|
||||
)
|
||||
|
||||
sales_item = self.get_queryset().filter(id=pk).first()
|
||||
if sales_item is None:
|
||||
return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)
|
||||
|
||||
from shipment.services import delete_sales_item
|
||||
|
||||
delete_sales_item(sales_item, deleted_by=request.user)
|
||||
return Response({"detail": "销售品已标记为删除"}, status=status.HTTP_200_OK)
|
||||
|
||||
Reference in New Issue
Block a user