implement seller document verification workflow, add product rejection status, and update product and seller models.
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 25s

This commit is contained in:
vickytechkey 2026-08-16 15:09:35 +05:30
parent 1bc9e0af96
commit 5cbc5d9da9
5 changed files with 81 additions and 12 deletions

View file

@ -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)

View file

@ -14,6 +14,7 @@ urlpatterns = [
path('sellers/', seller_views.list_sellers, name='list_sellers'),
path('sellers/<int:pk>/', seller_views.get_seller_detail, name='get_seller_detail'),
path('sellers/<int:pk>/documents/', seller_views.get_seller_documents, name='get_seller_documents'),
path('sellers/<int:pk>/documents/<str:doc_id>/approve/', seller_views.approve_seller_document, name='approve_seller_document'),
path('sellers/<int:pk>/approve/', seller_views.approve_seller, name='approve_seller'),
path('sellers/<int:pk>/reject/', seller_views.reject_seller, name='reject_seller'),
path('sellers/<int:pk>/suspend/', seller_views.suspend_seller, name='suspend_seller'),
@ -34,6 +35,7 @@ urlpatterns = [
path('products/', product_views.list_products, name='list_products'),
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>/', product_views.delete_product, name='delete_product'),
# Wallet / Withdrawals

View file

@ -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):

View file

@ -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):

View file

@ -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)