diff --git a/context.txt b/context.txt index 5720328..bcaa962 100644 --- a/context.txt +++ b/context.txt @@ -19,7 +19,7 @@ The system is divided into five distinct PostgreSQL databases on the server: * `sellerprofile`: Contains seller profiles (`api_supplierprofile`) and credentials (`auth_user`). * `productprofile`: Contains approved product listings (`api_product`) and submissions (`product_submissions`). * `customerprofile`: Contains user wallets (`api_wallet` & `api_wallettransaction`). -* `customerwebsite`: Contains customer registrations (`orders_customerprofile`), orders (`orders_order`), and credentials (`auth_user`). +* `customerwebsite`: Contains customer registrations (`orders_customerprofile`), orders (`orders_order`), and credentials (`auth_user`), plus product categories (`products_category`). 3. CRM DATABASE ROUTING (`db_router.py`) ---------------------------------------- @@ -29,20 +29,44 @@ In `crmbackend/digihoxbackend/db_router.py`, models are mapped as follows: * `seller_models.Product` -> `product_db` (`productprofile` database) * `seller_models.Wallet`/`WalletTransaction` -> `customer_db` (`customerwebsite` database) * `customer_models.CustomerProfile`/`Order` -> `customer_db` (`customerwebsite` database) +* `customer_models.Category` -> `customer_db` (`customerwebsite` database) 4. IMPORTANT MODEL MAPPINGS (Unmanaged Proxy Models) ------------------------------------------------------ To align with the production databases, CRM models are mapped to the actual tables: * `SupplierProfile` (unmanaged) -> `api_supplierprofile` - Name and Email are mapped to properties fetching dynamically from `auth_user` table in `seller_db`. + - Mapped document keys: `aadhar_file`, `pan_file`, `aadhar_s3_key`, `pan_s3_key`. * `Product` (unmanaged) -> `api_product` (with column `title` mapped to property `name`). - - Status/is_active write operations are bypassed during saves since table lacks status columns. + - Contains status and visibility fields mapping to `status` and `is_active`. * `CustomerProfile` (unmanaged) -> `orders_customerprofile` - Name and Email fetched dynamically from `auth_user` in `customer_db`. - Customer status property is mapped to the `is_active` flag in customer website database credentials. +* `Category` (unmanaged) -> `products_category` (with self-referencing `parent` ForeignKey mapping to `parent_id` for hierarchical subcategories). * `Order` (unmanaged) -> `orders_order`. 5. CUSTOMER ACTION ACTIONS -------------------------- * Banning/suspending customers executes `UPDATE auth_user SET is_active = False` in the `customer_db` database to disable customer site logins. Reinstating sets `is_active = True`. * Removing a seller deletes the profile from `api_supplierprofile` and deletes the login user record from `auth_user` inside `seller_db`. + +6. DYNAMIC CATEGORIES MANAGEMENT +--------------------------------- +* Categories can be created as parent categories or mapped as nested subcategories under parent nodes dynamically. +* Products can be mapped under categories/subcategories using a selector dropdown in the details overlay. +* Mapped categorizations are stored in the product's `category` column (string-based matching for compatibility). + +7. LIST PAGINATION & PAGINATION CONTROLS +----------------------------------------- +* Integrated list pagination controls on Products, Sellers, Customers, and System Audit Logs pages. +* Supports dynamic page size selectors (5, 10, 20, 50, 100) and resets the current page automatically when filters or search queries change. + +8. KYC DOCUMENT PREVIEW & DOWNLOAD +----------------------------------- +* Direct S3 download links are retrieved. +* Dynamic document previews are rendered using the Google Docs PDF Viewer. +* Preview requests are routed through a secure, public backend endpoint: + `/api/crm/sellers/documents/preview/?file=` + - Bypass authorization checking (`AllowAny`) to let Google Docs engines fetch the document stream. + - Generates secure S3 presigned URLs dynamically using `boto3` under bucket `supplier-docs` and region `eu-central-1` (Frankfurt) and redirects there to display the live file. + - Automatically falls back to serving a mock locally generated PDF/PNG document if AWS is not configured. diff --git a/crm/views/seller_views.py b/crm/views/seller_views.py index 6ca0701..5122422 100644 --- a/crm/views/seller_views.py +++ b/crm/views/seller_views.py @@ -55,39 +55,36 @@ def get_seller_documents(request, pk): aadhar_key = seller.aadhar_s3_key or seller.aadhar_file if aadhar_key: is_verified = f"aadhar_{pk}" in APPROVED_DOCUMENTS - s3_url = f"https://supplier-docs.s3.ap-south-2.amazonaws.com/{aadhar_key}?token=presigned123" preview_url = f"/sellers/documents/preview/?file={aadhar_key}" docs.append({ "id": "aadhar", "type": "Aadhar Card", "filename": aadhar_key, - "url": s3_url, + "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 - s3_url = f"https://supplier-docs.s3.ap-south-2.amazonaws.com/{pan_key}?token=presigned123" preview_url = f"/sellers/documents/preview/?file={pan_key}" docs.append({ "id": "pan", "type": "PAN Card", "filename": pan_key, - "url": s3_url, + "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" - s3_url = f"https://supplier-docs.s3.ap-south-2.amazonaws.com/{filename}?token=presigned123" preview_url = f"/sellers/documents/preview/?file={filename}" docs.append({ "id": "gstin", "type": "GSTIN Certificate", "filename": filename, - "url": s3_url, + "url": preview_url, "preview_url": preview_url, "status": "Verified" if is_verified else "Pending" }) @@ -233,7 +230,7 @@ def preview_document(request): 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 'supplier-docs' + 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: