|
@@ -694,3 +694,221 @@ class ProductAttributesUploadView(APIView):
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
|
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+from rest_framework.views import APIView
|
|
|
|
|
+from rest_framework.response import Response
|
|
|
|
|
+from rest_framework import status
|
|
|
|
|
+from .models import ProductType, ProductAttribute, AttributePossibleValue
|
|
|
|
|
+from .serializers import ProductTypeSerializer, ProductAttributeSerializer, AttributePossibleValueSerializer
|
|
|
|
|
+from django.db import transaction
|
|
|
|
|
+
|
|
|
|
|
+class ProductTypeAttributesView(APIView):
|
|
|
|
|
+ """
|
|
|
|
|
+ API to view, create, update, and delete product type attributes and their possible values.
|
|
|
|
|
+ Also supports dynamic product type creation.
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ def get(self, request):
|
|
|
|
|
+ """
|
|
|
|
|
+ Retrieve all product types with their attributes and possible values.
|
|
|
|
|
+ """
|
|
|
|
|
+ product_types = ProductType.objects.all()
|
|
|
|
|
+ serializer = ProductTypeSerializer(product_types, many=True)
|
|
|
|
|
+
|
|
|
|
|
+ # Transform the serialized data into the requested format
|
|
|
|
|
+ result = []
|
|
|
|
|
+ for pt in serializer.data:
|
|
|
|
|
+ for attr in pt['attributes']:
|
|
|
|
|
+ result.append({
|
|
|
|
|
+ 'product_type': pt['name'],
|
|
|
|
|
+ 'attribute_name': attr['name'],
|
|
|
|
|
+ 'is_mandatory': 'Yes' if attr['is_mandatory'] else 'No',
|
|
|
|
|
+ 'possible_values': ', '.join([pv['value'] for pv in attr['possible_values']])
|
|
|
|
|
+ })
|
|
|
|
|
+
|
|
|
|
|
+ return Response(result, status=status.HTTP_200_OK)
|
|
|
|
|
+
|
|
|
|
|
+ def post(self, request):
|
|
|
|
|
+ """
|
|
|
|
|
+ Create a new product type or attribute with possible values.
|
|
|
|
|
+ Expected payload example:
|
|
|
|
|
+ {
|
|
|
|
|
+ "product_type": "Hardware Screws",
|
|
|
|
|
+ "attribute_name": "Material", // Optional if only creating product type
|
|
|
|
|
+ "is_mandatory": "Yes", // Optional if only creating product type
|
|
|
|
|
+ "possible_values": "Steel, Zinc Plated, Stainless Steel" // Optional
|
|
|
|
|
+ }
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ product_type_name = request.data.get('product_type')
|
|
|
|
|
+ attribute_name = request.data.get('attribute_name', '')
|
|
|
|
|
+ is_mandatory = request.data.get('is_mandatory', '').lower() in ['yes', 'true', '1']
|
|
|
|
|
+ possible_values = request.data.get('possible_values', '')
|
|
|
|
|
+
|
|
|
|
|
+ if not product_type_name:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": "product_type is required"
|
|
|
|
|
+ }, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
+
|
|
|
|
|
+ with transaction.atomic():
|
|
|
|
|
+ # Get or create product type
|
|
|
|
|
+ product_type, created = ProductType.objects.get_or_create(name=product_type_name)
|
|
|
|
|
+
|
|
|
|
|
+ if created and not attribute_name:
|
|
|
|
|
+ # Only product type was created
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": f"Product type '{product_type_name}' created successfully",
|
|
|
|
|
+ "data": {"product_type": product_type_name}
|
|
|
|
|
+ }, status=status.HTTP_201_CREATED)
|
|
|
|
|
+
|
|
|
|
|
+ if attribute_name:
|
|
|
|
|
+ # Create attribute
|
|
|
|
|
+ attribute, attr_created = ProductAttribute.objects.get_or_create(
|
|
|
|
|
+ product_type=product_type,
|
|
|
|
|
+ name=attribute_name,
|
|
|
|
|
+ defaults={'is_mandatory': is_mandatory}
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if not attr_created:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": f"Attribute '{attribute_name}' already exists for product type '{product_type_name}'"
|
|
|
|
|
+ }, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
+
|
|
|
|
|
+ # Handle possible values
|
|
|
|
|
+ if possible_values:
|
|
|
|
|
+ for val in [v.strip() for v in possible_values.split(',') if v.strip()]:
|
|
|
|
|
+ AttributePossibleValue.objects.create(attribute=attribute, value=val)
|
|
|
|
|
+
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": "Attribute created successfully",
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "product_type": product_type_name,
|
|
|
|
|
+ "attribute_name": attribute_name,
|
|
|
|
|
+ "is_mandatory": "Yes" if is_mandatory else "No",
|
|
|
|
|
+ "possible_values": possible_values
|
|
|
|
|
+ }
|
|
|
|
|
+ }, status=status.HTTP_201_CREATED)
|
|
|
|
|
+
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": f"Product type '{product_type_name}' already exists",
|
|
|
|
|
+ "data": {"product_type": product_type_name}
|
|
|
|
|
+ }, status=status.HTTP_200_OK)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
+
|
|
|
|
|
+ def put(self, request):
|
|
|
|
|
+ """
|
|
|
|
|
+ Update an existing product type attribute and its possible values.
|
|
|
|
|
+ Expected payload example:
|
|
|
|
|
+ {
|
|
|
|
|
+ "product_type": "Hardware Screws",
|
|
|
|
|
+ "attribute_name": "Material",
|
|
|
|
|
+ "is_mandatory": "Yes",
|
|
|
|
|
+ "possible_values": "Steel, Zinc Plated, Stainless Steel, Brass"
|
|
|
|
|
+ }
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ product_type_name = request.data.get('product_type')
|
|
|
|
|
+ attribute_name = request.data.get('attribute_name')
|
|
|
|
|
+ is_mandatory = request.data.get('is_mandatory', '').lower() in ['yes', 'true', '1']
|
|
|
|
|
+ possible_values = request.data.get('possible_values', '')
|
|
|
|
|
+
|
|
|
|
|
+ if not all([product_type_name, attribute_name]):
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": "product_type and attribute_name are required"
|
|
|
|
|
+ }, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
+
|
|
|
|
|
+ with transaction.atomic():
|
|
|
|
|
+ try:
|
|
|
|
|
+ product_type = ProductType.objects.get(name=product_type_name)
|
|
|
|
|
+ attribute = ProductAttribute.objects.get(
|
|
|
|
|
+ product_type=product_type,
|
|
|
|
|
+ name=attribute_name
|
|
|
|
|
+ )
|
|
|
|
|
+ except ProductType.DoesNotExist:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": f"Product type '{product_type_name}' not found"
|
|
|
|
|
+ }, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
+ except ProductAttribute.DoesNotExist:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": f"Attribute '{attribute_name}' not found for product type '{product_type_name}'"
|
|
|
|
|
+ }, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
+
|
|
|
|
|
+ # Update attribute
|
|
|
|
|
+ attribute.is_mandatory = is_mandatory
|
|
|
|
|
+ attribute.save()
|
|
|
|
|
+
|
|
|
|
|
+ # Update possible values
|
|
|
|
|
+ AttributePossibleValue.objects.filter(attribute=attribute).delete()
|
|
|
|
|
+ if possible_values:
|
|
|
|
|
+ for val in [v.strip() for v in possible_values.split(',') if v.strip()]:
|
|
|
|
|
+ AttributePossibleValue.objects.create(attribute=attribute, value=val)
|
|
|
|
|
+
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": "Attribute updated successfully",
|
|
|
|
|
+ "data": {
|
|
|
|
|
+ "product_type": product_type_name,
|
|
|
|
|
+ "attribute_name": attribute_name,
|
|
|
|
|
+ "is_mandatory": "Yes" if is_mandatory else "No",
|
|
|
|
|
+ "possible_values": possible_values
|
|
|
|
|
+ }
|
|
|
|
|
+ }, status=status.HTTP_200_OK)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
|
|
|
+
|
|
|
|
|
+ def delete(self, request):
|
|
|
|
|
+ """
|
|
|
|
|
+ Delete a product type or a specific attribute.
|
|
|
|
|
+ Expected payload example:
|
|
|
|
|
+ {
|
|
|
|
|
+ "product_type": "Hardware Screws",
|
|
|
|
|
+ "attribute_name": "Material" // Optional: if omitted, deletes entire product type
|
|
|
|
|
+ }
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ product_type_name = request.data.get('product_type')
|
|
|
|
|
+ attribute_name = request.data.get('attribute_name', '')
|
|
|
|
|
+
|
|
|
|
|
+ if not product_type_name:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": "product_type is required"
|
|
|
|
|
+ }, status=status.HTTP_400_BAD_REQUEST)
|
|
|
|
|
+
|
|
|
|
|
+ with transaction.atomic():
|
|
|
|
|
+ try:
|
|
|
|
|
+ product_type = ProductType.objects.get(name=product_type_name)
|
|
|
|
|
+ except ProductType.DoesNotExist:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": f"Product type '{product_type_name}' not found"
|
|
|
|
|
+ }, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
+
|
|
|
|
|
+ if attribute_name:
|
|
|
|
|
+ # Delete specific attribute
|
|
|
|
|
+ try:
|
|
|
|
|
+ attribute = ProductAttribute.objects.get(
|
|
|
|
|
+ product_type=product_type,
|
|
|
|
|
+ name=attribute_name
|
|
|
|
|
+ )
|
|
|
|
|
+ attribute.delete()
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": f"Attribute '{attribute_name}' deleted successfully from product type '{product_type_name}'"
|
|
|
|
|
+ }, status=status.HTTP_200_OK)
|
|
|
|
|
+ except ProductAttribute.DoesNotExist:
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "error": f"Attribute '{attribute_name}' not found for product type '{product_type_name}'"
|
|
|
|
|
+ }, status=status.HTTP_404_NOT_FOUND)
|
|
|
|
|
+ else:
|
|
|
|
|
+ # Delete entire product type (and its attributes and possible values)
|
|
|
|
|
+ product_type.delete()
|
|
|
|
|
+ return Response({
|
|
|
|
|
+ "message": f"Product type '{product_type_name}' and all its attributes deleted successfully"
|
|
|
|
|
+ }, status=status.HTTP_200_OK)
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ return Response({"error": str(e)}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|