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
All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 25s
This commit is contained in:
parent
1bc9e0af96
commit
5cbc5d9da9
5 changed files with 81 additions and 12 deletions
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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):
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in a new issue