from django.db.models import Q from drf_spectacular.utils import OpenApiParameter, extend_schema, extend_schema_view from rest_framework import mixins, viewsets from rest_framework.decorators import action from rest_framework.permissions import AllowAny, IsAuthenticated from rest_framework.response import Response from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements from apps.core.permissions import IsStoreOwner from apps.stores.models import Store from .models import Product, ProductCategory from .serializers import ( ProductCategorySerializer, ProductDetailSerializer, ProductListSerializer, SellerProductSerializer, StockAdjustSerializer, ) class ProductCategoryViewSet(mixins.ListModelMixin, viewsets.GenericViewSet): schema_tags = ['Catalog'] permission_classes = [AllowAny] serializer_class = ProductCategorySerializer queryset = ProductCategory.objects.all() @extend_schema_view( list=extend_schema( parameters=[ OpenApiParameter('store', str, description='Filter by store UUID.'), OpenApiParameter('category', str, description='Filter by product category UUID.'), OpenApiParameter('search', str, description='Search by product name/description.'), ], ), ) class ProductViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin, viewsets.GenericViewSet): """Customer-facing product browsing (store page, product detail, search).""" schema_tags = ['Catalog'] permission_classes = [AllowAny] queryset = Product.objects.filter( is_active=True, store__status=Store.Status.APPROVED, ).select_related('store', 'category') def get_serializer_class(self): if self.action == 'retrieve': return ProductDetailSerializer return ProductListSerializer def get_queryset(self): queryset = super().get_queryset() store_uuid = self.request.query_params.get('store') if store_uuid: queryset = queryset.filter(store__uuid=store_uuid) category_uuid = self.request.query_params.get('category') if category_uuid: queryset = queryset.filter(category__uuid=category_uuid) search = self.request.query_params.get('search') if search: queryset = queryset.filter(Q(name__icontains=search) | Q(description__icontains=search)) return queryset class SellerProductViewSet(viewsets.ModelViewSet): """Seller's own product management (S06 list, S07 add, S08 inventory).""" schema_tags = ['Seller ยท Products'] serializer_class = SellerProductSerializer # permission_classes = [IsAuthenticated, IsStoreOwner] # TODO: IsStoreOwner? permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements] required_alternate_scopes = { "POST": [[]], } def get_queryset(self): if getattr(self, 'swagger_fake_view', False): return Product.objects.none() return Product.objects.filter(store=self.request.user.store).select_related('category') @action(detail=True, methods=['patch'], url_path='stock') def adjust_stock(self, request, pk=None): product = self.get_object() serializer = StockAdjustSerializer(data=request.data) serializer.is_valid(raise_exception=True) product.stock_quantity = serializer.validated_data['stock_quantity'] product.save(update_fields=['stock_quantity', 'updated_at']) return Response(SellerProductSerializer(product).data)