feat: implement category management system and product-to-category mapping functionality
Some checks failed
Deploy Beta (NATIVE) / deploy (push) Failing after 22s
Some checks failed
Deploy Beta (NATIVE) / deploy (push) Failing after 22s
This commit is contained in:
parent
e82dc2f555
commit
80ea0858ee
4 changed files with 105 additions and 1 deletions
|
|
@ -79,6 +79,21 @@ class WalletTransactionSerializer(serializers.ModelSerializer):
|
|||
model = WalletTransaction
|
||||
fields = ['id', 'wallet', 'amount', 'tx_type', 'status']
|
||||
|
||||
class CategorySerializer(serializers.ModelSerializer):
|
||||
subcategories = serializers.SerializerMethodField()
|
||||
|
||||
class Meta:
|
||||
model = Category
|
||||
fields = ['id', 'name', 'slug', 'is_active', 'sort_order', 'parent', 'subcategories']
|
||||
|
||||
def get_subcategories(self, obj):
|
||||
# We perform filtering inside Python or basic query to fetch subcategories
|
||||
if obj.parent_id is None:
|
||||
# Query subcategories where parent matches obj
|
||||
subs = Category.objects.filter(parent=obj)
|
||||
return CategorySerializer(subs, many=True).data
|
||||
return []
|
||||
|
||||
class CustomerProfileSerializer(serializers.ModelSerializer):
|
||||
name = serializers.CharField(read_only=True)
|
||||
email = serializers.CharField(read_only=True)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
from django.urls import path
|
||||
from crm.views import auth_views, seller_views, customer_views, product_views, wallet_views, debug_views, dashboard_views
|
||||
from crm.views import auth_views, seller_views, customer_views, product_views, wallet_views, debug_views, dashboard_views, category_views
|
||||
|
||||
urlpatterns = [
|
||||
# Auth
|
||||
|
|
@ -36,8 +36,13 @@ urlpatterns = [
|
|||
path('products/<int:pk>/approve/', product_views.approve_product, name='approve_product'),
|
||||
path('products/<int:pk>/hold/', product_views.hold_product, name='hold_product'),
|
||||
path('products/<int:pk>/reject/', product_views.reject_product, name='reject_product'),
|
||||
path('products/<int:pk>/map-category/', category_views.map_product_category, name='map_product_category'),
|
||||
path('products/<int:pk>/', product_views.delete_product, name='delete_product'),
|
||||
|
||||
# Categories
|
||||
path('categories/', category_views.list_categories, name='list_categories'),
|
||||
path('categories/create/', category_views.create_category, name='create_category'),
|
||||
|
||||
# Wallet / Withdrawals
|
||||
path('wallets/withdrawals/', wallet_views.list_withdrawals, name='list_withdrawals'),
|
||||
path('wallets/withdrawals/<int:pk>/approve/', wallet_views.approve_withdrawal, name='approve_withdrawal'),
|
||||
|
|
|
|||
69
crm/views/category_views.py
Normal file
69
crm/views/category_views.py
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
from rest_framework import status
|
||||
from rest_framework.decorators import api_view, permission_classes
|
||||
from rest_framework.response import Response
|
||||
from django.utils.text import slugify
|
||||
from crm.permissions import IsCRMAdmin
|
||||
from customer_models.models import Category
|
||||
from seller_models.models import Product
|
||||
from crm.serializers import CategorySerializer
|
||||
from crm.views.product_views import log_product_action
|
||||
|
||||
@api_view(['GET'])
|
||||
@permission_classes([IsCRMAdmin])
|
||||
def list_categories(request):
|
||||
# Only return top-level categories (parent is null)
|
||||
categories = Category.objects.filter(parent__isnull=True)
|
||||
serializer = CategorySerializer(categories, many=True)
|
||||
return Response(serializer.data)
|
||||
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsCRMAdmin])
|
||||
def create_category(request):
|
||||
name = request.data.get('name')
|
||||
parent_id = request.data.get('parent')
|
||||
|
||||
if not name:
|
||||
return Response({"detail": "Name is required"}, status=400)
|
||||
|
||||
slug = request.data.get('slug', slugify(name))
|
||||
|
||||
# Check if category with this slug already exists
|
||||
if Category.objects.filter(slug=slug).exists():
|
||||
slug = f"{slug}-{Category.objects.filter(slug__startswith=slug).count() + 1}"
|
||||
|
||||
parent = None
|
||||
if parent_id:
|
||||
try:
|
||||
parent = Category.objects.get(pk=parent_id)
|
||||
except Category.DoesNotExist:
|
||||
return Response({"detail": "Parent category not found"}, status=404)
|
||||
|
||||
# Save category to database
|
||||
category = Category.objects.create(
|
||||
name=name,
|
||||
slug=slug,
|
||||
parent=parent,
|
||||
is_active=True
|
||||
)
|
||||
|
||||
serializer = CategorySerializer(category)
|
||||
return Response(serializer.data, status=201)
|
||||
|
||||
@api_view(['POST'])
|
||||
@permission_classes([IsCRMAdmin])
|
||||
def map_product_category(request, pk):
|
||||
try:
|
||||
product = Product.objects.get(pk=pk)
|
||||
except Product.DoesNotExist:
|
||||
return Response({"detail": "Product not found"}, status=404)
|
||||
|
||||
category_val = request.data.get('category')
|
||||
if not category_val:
|
||||
return Response({"detail": "Category value is required"}, status=400)
|
||||
|
||||
# Save category field (holds name/slug of the category or subcategory)
|
||||
product.category = category_val
|
||||
product.save()
|
||||
|
||||
log_product_action(request.user, 'map_category', product.id, {"title": product.name, "category": category_val})
|
||||
return Response({"message": f"Product mapped under category '{category_val}' successfully"})
|
||||
|
|
@ -1,5 +1,20 @@
|
|||
from django.db import models
|
||||
|
||||
class Category(models.Model):
|
||||
name = models.CharField(max_length=100)
|
||||
slug = models.SlugField(max_length=100, unique=True, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
sort_order = models.IntegerField(default=0)
|
||||
parent = models.ForeignKey('self', on_delete=models.SET_NULL, null=True, blank=True, related_name='subcategories', db_column='parent_id')
|
||||
|
||||
class Meta:
|
||||
managed = False
|
||||
db_table = 'products_category'
|
||||
ordering = ['sort_order', 'name']
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
class CustomerProfile(models.Model):
|
||||
user_id = models.IntegerField(unique=True)
|
||||
phone = models.CharField(max_length=20, blank=True, null=True)
|
||||
|
|
|
|||
Loading…
Reference in a new issue