131 lines
4.3 KiB
Python
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"
|