From 5cbc5d9da966ba3f1ead098ffce229aef76c6881 Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Sun, 16 Aug 2026 15:09:35 +0530 Subject: [PATCH] implement seller document verification workflow, add product rejection status, and update product and seller models. --- crm/serializers.py | 11 ++++++-- crm/urls.py | 2 ++ crm/views/product_views.py | 13 +++++++++ crm/views/seller_views.py | 56 ++++++++++++++++++++++++++++++++++---- seller_models/models.py | 11 ++++++-- 5 files changed, 81 insertions(+), 12 deletions(-) diff --git a/crm/serializers.py b/crm/serializers.py index 776796b..5748e93 100644 --- a/crm/serializers.py +++ b/crm/serializers.py @@ -52,13 +52,18 @@ class SupplierProfileSerializer(serializers.ModelSerializer): class ProductSerializer(serializers.ModelSerializer): name = serializers.CharField(read_only=True) - status = serializers.CharField(read_only=True) - is_active = serializers.BooleanField(read_only=True) supplier_name = serializers.CharField(source='supplier.name', read_only=True) + supplier_status = serializers.CharField(source='supplier.status', read_only=True) + supplier_phone = serializers.CharField(source='supplier.phone', read_only=True) + supplier_gstin = serializers.CharField(source='supplier.gstin', read_only=True) + supplier_email = serializers.CharField(source='supplier.email', read_only=True) class Meta: model = Product - fields = ['id', 'name', 'price', 'stock', 'status', 'is_active', 'supplier_name'] + fields = [ + 'id', 'name', 'price', 'stock', 'status', 'is_active', 'category', 'sku', 'image', + 'supplier_name', 'supplier_status', 'supplier_phone', 'supplier_gstin', 'supplier_email' + ] class WalletSerializer(serializers.ModelSerializer): balance = serializers.DecimalField(max_digits=12, decimal_places=2, read_only=True) diff --git a/crm/urls.py b/crm/urls.py index b7b8a3d..aeed4a5 100644 --- a/crm/urls.py +++ b/crm/urls.py @@ -14,6 +14,7 @@ urlpatterns = [ path('sellers/', seller_views.list_sellers, name='list_sellers'), path('sellers//', seller_views.get_seller_detail, name='get_seller_detail'), path('sellers//documents/', seller_views.get_seller_documents, name='get_seller_documents'), + path('sellers//documents//approve/', seller_views.approve_seller_document, name='approve_seller_document'), path('sellers//approve/', seller_views.approve_seller, name='approve_seller'), path('sellers//reject/', seller_views.reject_seller, name='reject_seller'), path('sellers//suspend/', seller_views.suspend_seller, name='suspend_seller'), @@ -34,6 +35,7 @@ urlpatterns = [ path('products/', product_views.list_products, name='list_products'), path('products//approve/', product_views.approve_product, name='approve_product'), path('products//hold/', product_views.hold_product, name='hold_product'), + path('products//reject/', product_views.reject_product, name='reject_product'), path('products//', product_views.delete_product, name='delete_product'), # Wallet / Withdrawals diff --git a/crm/views/product_views.py b/crm/views/product_views.py index f6079d8..9d69b22 100644 --- a/crm/views/product_views.py +++ b/crm/views/product_views.py @@ -48,6 +48,19 @@ def hold_product(request, pk): log_product_action(request.user, 'hold_product', product.id, {"title": product.name}) return Response({"message": f"Product '{product.name}' placed on hold"}) +@api_view(['POST']) +@permission_classes([IsCRMAdmin]) +def reject_product(request, pk): + try: + product = Product.objects.get(pk=pk) + except Product.DoesNotExist: + return Response({"detail": "Product not found"}, status=404) + + product.status = 'rejected' + product.save() + log_product_action(request.user, 'reject_product', product.id, {"title": product.name}) + return Response({"message": f"Product '{product.name}' rejected"}) + @api_view(['DELETE']) @permission_classes([IsCRMAdmin]) def delete_product(request, pk): diff --git a/crm/views/seller_views.py b/crm/views/seller_views.py index bee5991..4b0537b 100644 --- a/crm/views/seller_views.py +++ b/crm/views/seller_views.py @@ -41,17 +41,61 @@ def get_seller_detail(request, pk): serializer = SupplierProfileSerializer(seller) return Response(serializer.data) +APPROVED_DOCUMENTS = set() + @api_view(['GET']) @permission_classes([IsCRMAdmin]) def get_seller_documents(request, pk): - # Mocking returning presigned document URLs - docs = [ - {"type": "Aadhar Card", "filename": "aadhar_back_front.pdf", "url": "https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/aadhar.pdf?token=presigned123"}, - {"type": "PAN Card", "filename": "pan_card_copy.jpg", "url": "https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/pan.jpg?token=presigned123"}, - {"type": "GSTIN Certificate", "filename": "gst_cert_2026.pdf", "url": "https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/gst.pdf?token=presigned123"} - ] + try: + seller = SupplierProfile.objects.get(pk=pk) + except SupplierProfile.DoesNotExist: + return Response({"detail": "Seller not found"}, status=404) + + docs = [] + if seller.aadhar_file: + is_verified = f"aadhar_{pk}" in APPROVED_DOCUMENTS + docs.append({ + "id": "aadhar", + "type": "Aadhar Card", + "filename": seller.aadhar_file, + "url": f"https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/{seller.aadhar_file}?token=presigned123", + "status": "Verified" if is_verified else "Pending" + }) + if seller.pan_file: + is_verified = f"pan_{pk}" in APPROVED_DOCUMENTS + docs.append({ + "id": "pan", + "type": "PAN Card", + "filename": seller.pan_file, + "url": f"https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/{seller.pan_file}?token=presigned123", + "status": "Verified" if is_verified else "Pending" + }) + if seller.gstin: + is_verified = f"gstin_{pk}" in APPROVED_DOCUMENTS or seller.is_gstin_verified + docs.append({ + "id": "gstin", + "type": "GSTIN Certificate", + "filename": f"gst_{seller.gstin}.pdf", + "url": f"https://digihox-kyc-bucket.s3.ap-south-2.amazonaws.com/gst_{seller.gstin}.pdf?token=presigned123", + "status": "Verified" if is_verified else "Pending" + }) + return Response(docs) +@api_view(['POST']) +@permission_classes([IsCRMAdmin]) +def approve_seller_document(request, pk, doc_id): + try: + seller = SupplierProfile.objects.get(pk=pk) + except SupplierProfile.DoesNotExist: + return Response({"detail": "Seller not found"}, status=404) + + APPROVED_DOCUMENTS.add(f"{doc_id}_{pk}") + if doc_id == "gstin": + seller.is_gstin_verified = True + seller.save() + return Response({"message": f"Document '{doc_id}' successfully approved"}) + @api_view(['POST']) @permission_classes([IsCRMAdmin]) def approve_seller(request, pk): diff --git a/seller_models/models.py b/seller_models/models.py index 582a76f..aa25a45 100644 --- a/seller_models/models.py +++ b/seller_models/models.py @@ -4,9 +4,12 @@ class SupplierProfile(models.Model): user_id = models.IntegerField(unique=True) phone = models.CharField(max_length=20, blank=True, null=True) gstin = models.CharField(max_length=15, blank=True, null=True) + is_gstin_verified = models.BooleanField(default=False) status = models.CharField(max_length=50, default='pending_approval') submitted_at = models.DateTimeField(blank=True, null=True) store_name = models.CharField(max_length=255, blank=True, null=True) + aadhar_file = models.CharField(max_length=255, blank=True, null=True) + pan_file = models.CharField(max_length=255, blank=True, null=True) class Meta: managed = False @@ -30,8 +33,13 @@ class SupplierProfile(models.Model): class Product(models.Model): supplier = models.ForeignKey(SupplierProfile, on_delete=models.DO_NOTHING, db_column='supplier_id') title = models.CharField(max_length=255) + category = models.CharField(max_length=100, blank=True, null=True) price = models.DecimalField(max_digits=10, decimal_places=2) stock = models.IntegerField(default=0) + sku = models.CharField(max_length=100, blank=True, null=True) + image = models.TextField(blank=True, null=True) + status = models.CharField(max_length=50, default='pending') + is_active = models.BooleanField(default=True) class Meta: managed = False @@ -41,9 +49,6 @@ class Product(models.Model): def name(self): return self.title - status = models.CharField(max_length=50, default='pending') - is_active = models.BooleanField(default=True) - class Wallet(models.Model): supplier_id = models.IntegerField(unique=True) outstanding = models.DecimalField(max_digits=12, decimal_places=2, default=0.00)