Files
shopship/apps/bizapps/api/views.py
T
2026-08-24 15:54:08 +03:30

131 lines
4.3 KiB
Python

from django.db import transaction
from rest_framework import generics, status
from rest_framework.exceptions import NotFound
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.views import APIView
from apps.billing.permissions import HasActiveFeature
from apps.bizapps.models import Appointment, Customer, LedgerEntry, Product, StockMovement
from .serializers import (
AdjustStockSerializer,
AppointmentSerializer,
CustomerSerializer,
LedgerEntrySerializer,
ProductSerializer,
StockMovementSerializer,
)
# همه‌ی این ریزاپ‌ها با جم فعال می‌شوند (یا دوره‌ی آزمایشی) — `feature_key`
# روی هر ویو مشخص می‌کند HasActiveFeature باید چه چیزی را چک کند.
# permission_classes اینجا Base است تا زیرکلاس‌ها فراموش نکنند.
class _UserScopedListCreateView(generics.ListCreateAPIView):
permission_classes = [IsAuthenticated, HasActiveFeature]
def get_queryset(self):
return self.model.objects.filter(user=self.request.user)
def perform_create(self, serializer):
serializer.save(user=self.request.user)
class _UserScopedDetailView(generics.RetrieveUpdateDestroyAPIView):
permission_classes = [IsAuthenticated, HasActiveFeature]
def get_queryset(self):
return self.model.objects.filter(user=self.request.user)
class CustomerListCreateView(_UserScopedListCreateView):
model = Customer
serializer_class = CustomerSerializer
feature_key = "customers"
class CustomerDetailView(_UserScopedDetailView):
model = Customer
serializer_class = CustomerSerializer
feature_key = "customers"
class LedgerEntryListCreateView(_UserScopedListCreateView):
model = LedgerEntry
serializer_class = LedgerEntrySerializer
feature_key = "ledger"
class LedgerEntryDetailView(_UserScopedDetailView):
model = LedgerEntry
serializer_class = LedgerEntrySerializer
feature_key = "ledger"
class ProductListCreateView(_UserScopedListCreateView):
model = Product
serializer_class = ProductSerializer
feature_key = "inventory"
class ProductDetailView(_UserScopedDetailView):
model = Product
serializer_class = ProductSerializer
feature_key = "inventory"
class ProductAdjustStockView(APIView):
"""تعدیل اتمیک موجودی (+/-) + یک ردیف تاریخچه — به‌جای این‌که کلاینت
مستقیم quantity جدید را PATCH کند (که هیچ تاریخچه‌ای نمی‌سازد و اگر دو
درخواست هم‌زمان بیایند می‌تواند یکی را گم کند)."""
permission_classes = [IsAuthenticated, HasActiveFeature]
feature_key = "inventory"
@transaction.atomic
def post(self, request, pk):
try:
product = Product.objects.select_for_update().get(pk=pk, user=request.user)
except Product.DoesNotExist:
raise NotFound("کالا یافت نشد.")
serializer = AdjustStockSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
delta = serializer.validated_data["delta"]
product.quantity += delta
product.save(update_fields=["quantity", "updated_at"])
StockMovement.objects.create(
product=product,
delta=delta,
quantity_after=product.quantity,
note=serializer.validated_data.get("note", ""),
)
return Response(ProductSerializer(product).data, status=status.HTTP_200_OK)
class ProductMovementsView(generics.ListAPIView):
"""تاریخچه‌ی تغییرات موجودی یک کالا — جدیدترین اول."""
serializer_class = StockMovementSerializer
permission_classes = [IsAuthenticated, HasActiveFeature]
feature_key = "inventory"
def get_queryset(self):
return StockMovement.objects.filter(
product_id=self.kwargs["pk"], product__user=self.request.user
)
class AppointmentListCreateView(_UserScopedListCreateView):
model = Appointment
serializer_class = AppointmentSerializer
feature_key = "booking"
class AppointmentDetailView(_UserScopedDetailView):
model = Appointment
serializer_class = AppointmentSerializer
feature_key = "booking"