All checks were successful
Deploy Beta (NATIVE) / deploy (push) Successful in 22s
297 lines
11 KiB
Python
297 lines
11 KiB
Python
from rest_framework import status
|
|
from rest_framework.decorators import api_view, permission_classes
|
|
from rest_framework.response import Response
|
|
from crm.permissions import IsCRMAdmin
|
|
from seller_models.models import SupplierProfile, Product
|
|
from crm.models import AuditLog
|
|
from crm.serializers import SupplierProfileSerializer, ProductSerializer
|
|
from crm.services.whatsapp_service import WhatsAppService
|
|
from django.utils import timezone
|
|
|
|
whatsapp_service = WhatsAppService()
|
|
|
|
def log_action(admin, action, target_id, details):
|
|
AuditLog.objects.create(
|
|
admin=admin,
|
|
action=action,
|
|
target_type='seller',
|
|
target_id=target_id,
|
|
details=details
|
|
)
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def list_sellers(request):
|
|
sellers = SupplierProfile.objects.all()
|
|
status_filter = request.query_params.get('status')
|
|
if status_filter:
|
|
sellers = sellers.filter(status=status_filter)
|
|
|
|
serializer = SupplierProfileSerializer(sellers, many=True)
|
|
return Response(serializer.data)
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def get_seller_detail(request, pk):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
serializer = SupplierProfileSerializer(seller)
|
|
return Response(serializer.data)
|
|
|
|
APPROVED_DOCUMENTS = set()
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def get_seller_documents(request, pk):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
docs = []
|
|
aadhar_key = seller.aadhar_s3_key or seller.aadhar_file
|
|
if aadhar_key:
|
|
is_verified = f"aadhar_{pk}" in APPROVED_DOCUMENTS
|
|
preview_url = f"/sellers/documents/preview/?file={aadhar_key}"
|
|
docs.append({
|
|
"id": "aadhar",
|
|
"type": "Aadhar Card",
|
|
"filename": aadhar_key,
|
|
"url": preview_url,
|
|
"preview_url": preview_url,
|
|
"status": "Verified" if is_verified else "Pending"
|
|
})
|
|
pan_key = seller.pan_s3_key or seller.pan_file
|
|
if pan_key:
|
|
is_verified = f"pan_{pk}" in APPROVED_DOCUMENTS
|
|
preview_url = f"/sellers/documents/preview/?file={pan_key}"
|
|
docs.append({
|
|
"id": "pan",
|
|
"type": "PAN Card",
|
|
"filename": pan_key,
|
|
"url": preview_url,
|
|
"preview_url": preview_url,
|
|
"status": "Verified" if is_verified else "Pending"
|
|
})
|
|
if seller.gstin:
|
|
is_verified = f"gstin_{pk}" in APPROVED_DOCUMENTS or seller.is_gstin_verified
|
|
filename = f"gst_{seller.gstin}.pdf"
|
|
preview_url = f"/sellers/documents/preview/?file={filename}"
|
|
docs.append({
|
|
"id": "gstin",
|
|
"type": "GSTIN Certificate",
|
|
"filename": filename,
|
|
"url": preview_url,
|
|
"preview_url": preview_url,
|
|
"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):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
old_status = seller.status
|
|
seller.status = 'approved'
|
|
seller.save()
|
|
|
|
log_action(request.user, 'approve_seller', seller.id, {"old_status": old_status, "new_status": "approved"})
|
|
whatsapp_service.send_template_alert(seller.phone, "seller_approved", {"name": seller.name})
|
|
|
|
return Response({"message": f"Seller {seller.name} approved successfully"})
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def reject_seller(request, pk):
|
|
reason = request.data.get('reason', 'Documents validation failed')
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
old_status = seller.status
|
|
seller.status = 'rejected'
|
|
seller.save()
|
|
|
|
log_action(request.user, 'reject_seller', seller.id, {"old_status": old_status, "new_status": "rejected", "reason": reason})
|
|
whatsapp_service.send_template_alert(seller.phone, "seller_rejected", {"name": seller.name, "reason": reason})
|
|
|
|
return Response({"message": f"Seller {seller.name} rejected successfully"})
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def suspend_seller(request, pk):
|
|
reason = request.data.get('reason', 'Policy violation')
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
old_status = seller.status
|
|
seller.status = 'suspended'
|
|
seller.save()
|
|
|
|
log_action(request.user, 'suspend_seller', seller.id, {"old_status": old_status, "new_status": "suspended", "reason": reason})
|
|
whatsapp_service.send_template_alert(seller.phone, "seller_suspended", {"name": seller.name, "reason": reason})
|
|
|
|
return Response({"message": f"Seller {seller.name} suspended successfully"})
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def reinstate_seller(request, pk):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
old_status = seller.status
|
|
seller.status = 'approved'
|
|
seller.save()
|
|
|
|
log_action(request.user, 'reinstate_seller', seller.id, {"old_status": old_status, "new_status": "approved"})
|
|
whatsapp_service.send_template_alert(seller.phone, "seller_reinstated", {"name": seller.name})
|
|
|
|
return Response({"message": f"Seller {seller.name} reinstated successfully"})
|
|
|
|
@api_view(['POST'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def reset_seller_password(request, pk):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
# Trigger password reset flow
|
|
whatsapp_service.send_otp(seller.phone, otp_type="password_reset")
|
|
log_action(request.user, 'reset_password', seller.id, {"email": seller.email})
|
|
|
|
return Response({"message": "Password reset alert successfully dispatched"})
|
|
|
|
@api_view(['POST', 'DELETE'])
|
|
@permission_classes([IsCRMAdmin])
|
|
def delete_seller(request, pk):
|
|
try:
|
|
seller = SupplierProfile.objects.get(pk=pk)
|
|
except SupplierProfile.DoesNotExist:
|
|
return Response({"detail": "Seller not found"}, status=404)
|
|
|
|
name = seller.name
|
|
user_id = seller.user_id
|
|
|
|
# Delete supplier profile from sellerprofile db
|
|
seller.delete()
|
|
|
|
# Delete corresponding user from auth_user in sellerprofile db
|
|
from django.db import connections
|
|
try:
|
|
with connections['seller_db'].cursor() as cursor:
|
|
cursor.execute("DELETE FROM auth_user WHERE id = %s", [user_id])
|
|
except Exception as e:
|
|
print("Failed to delete user record from auth_user:", e)
|
|
|
|
log_action(request.user, 'delete_seller', pk, {"name": name})
|
|
return Response({"message": f"Seller account '{name}' and associated user permanently removed"})
|
|
|
|
import io
|
|
from django.http import FileResponse
|
|
from django.views.decorators.clickjacking import xframe_options_exempt
|
|
|
|
from rest_framework.permissions import AllowAny
|
|
|
|
@api_view(['GET'])
|
|
@permission_classes([AllowAny])
|
|
@xframe_options_exempt
|
|
def preview_document(request):
|
|
filename = request.query_params.get('file', 'document.pdf').lstrip('/')
|
|
|
|
# Dynamic S3 Presigned Redirect if AWS credentials are set
|
|
import os
|
|
aws_access = os.environ.get('AWS_ACCESS_KEY_ID') or os.environ.get('AWS_ACCESS_KEY')
|
|
aws_secret = os.environ.get('AWS_SECRET_ACCESS_KEY') or os.environ.get('AWS_SECRET_KEY')
|
|
bucket_name = os.environ.get('AWS_STORAGE_BUCKET_NAME') or os.environ.get('AWS_BUCKET_NAME') or 'betasupplierdocument-764709663363-ap-south-2-an'
|
|
|
|
if aws_access and aws_secret:
|
|
try:
|
|
import boto3
|
|
from botocore.client import Config
|
|
from django.shortcuts import redirect
|
|
s3_client = boto3.client(
|
|
's3',
|
|
aws_access_key_id=aws_access,
|
|
aws_secret_access_key=aws_secret,
|
|
region_name=os.environ.get('AWS_REGION', 'ap-south-2'),
|
|
endpoint_url=f"https://s3.{os.environ.get('AWS_REGION', 'ap-south-2')}.amazonaws.com",
|
|
config=Config(signature_version='s3v4')
|
|
)
|
|
presigned_url = s3_client.generate_presigned_url(
|
|
'get_object',
|
|
Params={'Bucket': bucket_name, 'Key': filename},
|
|
ExpiresIn=3600
|
|
)
|
|
return redirect(presigned_url)
|
|
except Exception as e:
|
|
print("S3 redirect helper failed, falling back to local files / mock:", e)
|
|
|
|
# Check if file exists locally in the mounted folder
|
|
base_dir = os.environ.get('UPLOAD_MOUNT_DIR', '/home/ubuntu/mnt/s3files/supplierdocuments')
|
|
if not os.path.exists('/home/ubuntu/mnt/s3files/supplierdocuments') and not os.environ.get('UPLOAD_MOUNT_DIR'):
|
|
from django.conf import settings
|
|
base_dir = os.path.join(settings.BASE_DIR, 'mnt/s3files/supplierdocuments')
|
|
|
|
file_path = os.path.join(base_dir, filename)
|
|
if os.path.exists(file_path):
|
|
content_type = 'application/octet-stream'
|
|
if filename.endswith('.pdf'):
|
|
content_type = 'application/pdf'
|
|
elif filename.endswith('.png'):
|
|
content_type = 'image/png'
|
|
elif filename.endswith('.jpeg') or filename.endswith('.jpg'):
|
|
content_type = 'image/jpeg'
|
|
try:
|
|
return FileResponse(open(file_path, 'rb'), content_type=content_type)
|
|
except Exception as e:
|
|
print("Failed to serve local document file:", e)
|
|
|
|
if filename.endswith('.pdf'):
|
|
pdf_data = (
|
|
b"%PDF-1.4\n"
|
|
b"1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n"
|
|
b"2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n"
|
|
b"3 0 obj\n<< /Type /Page /Parent 2 0 R /MediaBox [0 0 595 842] /Contents 4 0 R >>\nendobj\n"
|
|
b"4 0 obj\n<< /Length 73 >>\nstream\n"
|
|
b"BT\n/F1 24 Tf\n100 700 Td\n(DigiHox Verified KYC Document Preview) Tj\nET\n"
|
|
b"endstream\n"
|
|
b"endobj\n"
|
|
b"xref\n0 5\n0000000000 65535 f\n0000000009 00000 n\n0000000056 00000 n\n"
|
|
b"0000000111 00000 n\n0000000212 00000 n\n"
|
|
b"trailer\n<< /Size 5 /Root 1 0 R >>\nstartxref\n335\n%%EOF\n"
|
|
)
|
|
return FileResponse(io.BytesIO(pdf_data), content_type='application/pdf')
|
|
else:
|
|
png_data = (
|
|
b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89'
|
|
b'\x00\x00\x00\rIDATx\x9cc`\x00\x00\x00\x02\x00\x01H\xaf\xa4q\x00\x00\x00\x00IEND\xaeB`\x82'
|
|
)
|
|
return FileResponse(io.BytesIO(png_data), content_type='image/png')
|