1
0
forked from erp-dev/erp

feat: salesitem can change/soft delete

This commit is contained in:
2026-04-06 14:49:02 +08:00
parent d7bc43082e
commit 9afbb73799
16 changed files with 1075 additions and 10 deletions

View File

@@ -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)