""" ============================================================= MIRCHI TRADING API - COMPLETE DOCUMENTATION & DB INSPECTOR ============================================================= Run: python scripts/api_docs.py """ import asyncio, json, os, sys from pathlib import Path # Add backend root to path sys.path.insert(0, str(Path(__file__).parent.parent)) # ───────────────────────────────────────────────── # SECTION 1 — DATABASE SCHEMA SUMMARY (10 lines) # ───────────────────────────────────────────────── DB_SCHEMA_SUMMARY = """ ╔══════════════════ DATABASE SCHEMA SUMMARY ══════════════════════════════╗ ║ 1. parties — Party master: id, name, phone, city, party_type, ║ ║ past_due, is_active, previous_names (JSON) ║ ║ 2. mirchi_types — Mirchi varieties: id, name, is_active ║ ║ 3. transactions — Awaak/Jawaak bills: bill_number, bill_date, ║ ║ bill_type(awaak|jawaak), party_id, total_amount, ║ ║ paid_amount, balance_amount, expenses(JSON), ║ ║ lot_number, is_mixed, is_return ║ ║ 4. transaction_items — Line items: mirchi_name, poti_count, ║ ║ poti_weights, net_weight, rate_per_kg, amount ║ ║ 5. transaction_payments — Payments per bill: amount, mode, reference, ║ ║ is_reversed ║ ║ 6. patti_transactions — Patti bills (patti_awaak|patti_jawaak): ║ ║ same as transactions + gross_weight, ║ ║ verified_net_weight ║ ║ 7. patti_transaction_items — Same structure as transaction_items ║ ║ 8. party_jama_entries — Standalone payments: party_id, date, amount, ║ ║ reference, notes ║ ║ 9. stock_ledger — Stock movements: mirchi_name, movement_type ║ ║ (IN|OUT|RETURN_IN|RETURN_OUT), quantity_in/out, ║ ║ balance_after, lot_number ║ ║ 10.bill_number_sequences — Auto-numbering: bill_type, year, batch(A-Z),║ ║ current_number (resets at 500 → next batch) ║ ╚═════════════════════════════════════════════════════════════════════════╝ """ # ───────────────────────────────────────────────── # SECTION 2 — ALL API ENDPOINTS + EXAMPLE RESPONSES # ───────────────────────────────────────────────── API_DOCS = { # ── PARTIES ────────────────────────────────── "GET /api/parties": { "desc": "List all parties (with live current_balance)", "query_params": "party_type, search, skip, limit", "example_response": { "success": True, "data": [ { "id": "p-abc123", "name": "रामलाल ट्रेडर्स", "phone": "9876543210", "city": "नांदेड", "party_type": "both", "past_due": 5000.0, "current_balance": 12500.50, "is_active": True } ], "total": 1 } }, "GET /api/parties/{party_id}": { "desc": "Single party with computed current_balance", "example_response": { "success": True, "data": { "id": "p-abc123", "name": "रामलाल ट्रेडर्स", "phone": "9876543210", "city": "नांदेड", "party_type": "both", "past_due": 5000.0, "current_balance": 12500.50, "previous_names": ["रामलाल"], "notes": "", "is_active": True } } }, "GET /api/parties/{party_id}/name-history": { "desc": "Full rename history for a party", "example_response": { "party_id": "p-abc123", "current_name": "रामलाल ट्रेडर्स", "name_history": ["रामलाल", "रामलाल एंटरप्रायझेस"] } }, "POST /api/parties": { "desc": "Create a new party", "request_body": { "name": "श्याम ट्रेडर्स", "phone": "9988776655", "city": "लातूर", "party_type": "jawaak", "past_due": 0.0 }, "example_response": { "success": True, "message": "Party created successfully", "data": {"id": "p-xyz789", "name": "श्याम ट्रेडर्स"} } }, "PUT /api/parties/{party_id}": { "desc": "Update party (rename auto-saves to history)", "request_body": {"name": "श्याम एंटरप्रायझेस", "phone": "9988776655"}, "example_response": {"success": True, "message": "Party updated successfully"} }, "DELETE /api/parties/{party_id}": { "desc": "Soft-delete a party (sets is_active=False)", "example_response": {"success": True, "message": "Party deactivated successfully"} }, # ── MIRCHI TYPES ───────────────────────────── "GET /api/mirchi-types": { "desc": "List mirchi varieties", "query_params": "active_only=true, skip, limit", "example_response": { "success": True, "data": [ {"id": "m-aaa111", "name": "लाल मिरची", "is_active": True}, {"id": "m-bbb222", "name": "हिरवी मिरची", "is_active": True} ], "total": 2 } }, "GET /api/mirchi-types/{mirchi_id}": { "desc": "Single mirchi type by ID", "example_response": {"success": True, "data": {"id": "m-aaa111", "name": "लाल मिरची", "is_active": True}} }, "POST /api/mirchi-types": { "desc": "Create new mirchi type", "request_body": {"name": "काळी मिरची"}, "example_response": {"success": True, "message": "Mirchi type created successfully", "data": {"id": "m-ccc333", "name": "काळी मिरची"}} }, "PUT /api/mirchi-types/{mirchi_id}": { "desc": "Update mirchi type name or active status", "request_body": {"name": "काळी मिरची XL", "is_active": True}, "example_response": {"success": True, "message": "Mirchi type updated successfully"} }, "DELETE /api/mirchi-types/{mirchi_id}": { "desc": "Hard delete a mirchi type", "example_response": {"success": True, "message": "Mirchi type deleted successfully"} }, # ── TRANSACTIONS (Awaak / Jawaak) ──────────── "GET /api/transactions": { "desc": "List all transactions with filters", "query_params": "bill_type(awaak|jawaak), party_id, search, start_date, end_date, skip, limit", "example_response": { "success": True, "data": [ { "id": "t-111aaa", "bill_number": "A2025A001", "bill_date": "2025-03-15", "bill_type": "awaak", "party_id": "p-abc123", "party_name": "रामलाल ट्रेडर्स", "subtotal": 15000.0, "total_amount": 15200.0, "paid_amount": 10000.0, "balance_amount": 5200.0, "lot_number": "LOT001", "is_mixed": False, "is_return": False, "items": [ { "mirchi_name": "लाल मिरची", "poti_count": 10, "net_weight": 500.0, "rate_per_kg": 30.0, "amount": 15000.0 } ], "payments": [ {"amount": 10000.0, "mode": "cash", "paid_at": "2025-03-15"} ] } ], "total": 1 } }, "GET /api/transactions/next-bill-number": { "desc": "Get next auto-generated bill number", "query_params": "bill_type=awaak", "example_response": {"success": True, "bill_number": "A2025A002"} }, "GET /api/transactions/bill-number-info": { "desc": "Batch/sequence info for bill numbering", "query_params": "bill_type=awaak&year=2025", "example_response": { "success": True, "bill_type": "awaak", "year": 2025, "current_batch": "A", "current_number": 2, "remaining": 498 } }, "GET /api/transactions/{transaction_id}": { "desc": "Get single transaction by ID with items & payments", "example_response": {"success": True, "data": {"id": "t-111aaa", "bill_number": "A2025A001"}} }, "POST /api/transactions": { "desc": "Create a new Awaak or Jawaak transaction", "request_body": { "bill_type": "awaak", "bill_date": "2025-03-15", "party_id": "p-abc123", "lot_number": "LOT001", "items": [ {"mirchi_name": "लाल मिरची", "mirchi_id": "m-aaa111", "poti_count": 10, "poti_weights": "[50,50,50,50,50,50,50,50,50,50]", "net_weight": 500.0, "rate_per_kg": 30.0} ], "expenses": {"hamali": 200, "other": 0}, "payments": [{"amount": 10000.0, "mode": "cash", "paid_at": "2025-03-15"}] }, "example_response": { "success": True, "message": "Transaction created successfully", "data": {"id": "t-111aaa", "bill_number": "A2025A001", "total_amount": 15200.0} } }, "POST /api/transactions/{transaction_id}/payment": { "desc": "Add payment to a transaction", "query_params": "amount=5000", "example_response": {"success": True, "message": "Payment updated successfully"} }, "POST /api/transactions/{transaction_id}/revert": { "desc": "Revert a transaction (creates return bill, reverses stock)", "example_response": {"success": True, "message": "Transaction reverted successfully"} }, "DELETE /api/transactions/{transaction_id}": { "desc": "Delete a transaction", "example_response": {"success": True, "message": "Transaction deleted successfully"} }, # ── PATTI TRANSACTIONS ──────────────────────── "GET /api/patti-transactions": { "desc": "List all patti transactions", "query_params": "bill_type(patti_awaak|patti_jawaak), party_id, search, start_date, end_date, skip, limit", "example_response": { "success": True, "data": [ { "id": "pt-222bbb", "bill_number": "PA2025A001", "bill_type": "patti_awaak", "party_name": "गणेश पाटील", "lot_number": "PLOT001", "gross_weight": 1000.0, "verified_net_weight": 980.0, "total_amount": 29400.0, "balance_amount": 29400.0 } ], "total": 1 } }, "GET /api/patti-transactions/lots/available/{mirchi_id}": { "desc": "Get available patti lots for Jawaak billing (with weight/bag info)", "example_response": { "success": True, "data": [ { "id": "patti-lot-PLOT001", "lot_number": "PLOT001", "mirchi_name": "लाल मिरची", "gross_weight": 1000.0, "remaining_quantity": 980.0, "poti_count": 20, "display": "PLOT001 | 980kg | 20 पोती" } ] } }, "GET /api/patti-transactions/next-bill-number": { "desc": "Get next patti bill number", "query_params": "bill_type=patti_awaak", "example_response": {"success": True, "bill_number": "PA2025A001"} }, "POST /api/patti-transactions": { "desc": "Create a patti_awaak or patti_jawaak transaction", "request_body": { "bill_type": "patti_awaak", "bill_date": "2025-03-15", "party_id": "p-abc123", "lot_number": "PLOT001", "gross_weight": 1000.0, "verified_net_weight": 980.0, "items": [ {"mirchi_name": "लाल मिरची", "mirchi_id": "m-aaa111", "poti_count": 20, "net_weight": 980.0, "rate_per_kg": 30.0} ], "expenses": {"packing": 100, "commission": 200, "hamali": 150} }, "example_response": { "success": True, "message": "Patti transaction created successfully", "data": {"id": "pt-222bbb", "bill_number": "PA2025A001"} } }, "POST /api/patti-transactions/{transaction_id}/payment": { "desc": "Add payment to patti transaction", "query_params": "amount=10000", "example_response": {"success": True, "message": "Payment updated successfully"} }, "POST /api/patti-transactions/{transaction_id}/revert": { "desc": "Revert patti transaction (reverses stock from patti holdings)", "example_response": {"success": True, "message": "Patti transaction reverted successfully"} }, "DELETE /api/patti-transactions/{transaction_id}": { "desc": "Delete a patti transaction", "example_response": {"success": True, "message": "Patti transaction deleted successfully"} }, # ── STOCK LEDGER ───────────────────────────── "GET /api/stock-ledger": { "desc": "List all stock movement entries", "query_params": "mirchi_name, movement_type(IN|OUT|RETURN_IN|RETURN_OUT), lot_number, start_date, end_date, skip, limit", "example_response": { "success": True, "data": [ { "id": "sl-333ccc", "mirchi_name": "लाल मिरची", "movement_type": "IN", "quantity_in": 500.0, "quantity_out": 0.0, "balance_after": 500.0, "lot_number": None, "created_at": "2025-03-15T10:00:00" } ], "total": 1 } }, "GET /api/stock-ledger/summary": { "desc": "Summary of current stock by mirchi type", "example_response": { "success": True, "data": [ {"mirchi_name": "लाल मिरची", "balance": 500.0, "total_in": 1000.0, "total_out": 500.0} ] } }, "GET /api/stock-ledger/lots": { "desc": "Lot-wise stock summary (patti lot tracking)", "query_params": "mirchi_name (optional)", "example_response": { "success": True, "data": [ {"mirchi_name": "लाल मिरची", "lot_number": "PLOT001", "balance": 480.0, "total_in": 980.0, "total_out": 500.0} ] } }, "GET /api/stock-ledger/available/{mirchi_id}": { "desc": "Available lots for Jawaak billing (balance > 0)", "example_response": { "success": True, "data": [ {"id": "lot-PLOT001", "lot_number": "PLOT001", "remaining_quantity": 480.0, "poti_count": 9, "display": "PLOT001 | 480kg | ~9 पोती"} ] } }, "GET /api/stock-ledger/balance/{mirchi_name}": { "desc": "Get current balance for a specific mirchi (optionally by lot)", "query_params": "lot_number (optional)", "example_response": { "success": True, "data": {"mirchi_name": "लाल मिरची", "lot_number": None, "current_balance": 500.0} } }, "GET /api/stock-ledger/analysis": { "desc": "Lot-wise P&L analysis (buy qty/amt vs sell qty/amt)", "example_response": { "success": True, "data": [ { "lot_number": "LOT001", "mirchi_name": "लाल मिरची", "farmer_name": "रामलाल ट्रेडर्स", "buy_qty": 500.0, "buy_amt": 15000.0, "buy_poti": 10, "sell_qty": 300.0, "sell_amt": 10500.0, "sell_poti": 6, "commission": 0.0, "is_patti": False } ] } }, "GET /api/stock-ledger/{ledger_id}": { "desc": "Single stock ledger entry by ID", "example_response": {"success": True, "data": {"id": "sl-333ccc", "movement_type": "IN"}} }, # ── PARTY JAMA (Ledger Payments) ───────────── "GET /api/party-jama": { "desc": "List all standalone jama (payment) entries", "query_params": "party_id, start_date, end_date, skip, limit", "example_response": { "success": True, "data": [ {"id": "pj-444ddd", "party_id": "p-abc123", "date": "2025-03-14", "amount": 5000.0, "reference": "UPI-123", "notes": ""} ], "total": 1 } }, "GET /api/party-jama/party/{party_id}/balance": { "desc": "Party's computed balance (transactions + jama)", "example_response": { "success": True, "data": { "party_id": "p-abc123", "past_due": 5000.0, "regular_balance": 7500.0, "patti_balance": 0.0, "jama_total": 2000.0, "current_balance": 10500.0 } } }, "GET /api/party-jama/party/{party_id}/ledger": { "desc": "Full chronological party ledger (bills + jama entries merged)", "query_params": "start_date, end_date", "example_response": { "success": True, "data": { "party": {"id": "p-abc123", "name": "रामलाल ट्रेडर्स"}, "entries": [ {"date": "2025-03-10", "type": "transaction", "bill_number": "A2025A001", "debit": 15200.0, "credit": 0, "balance": 15200.0}, {"date": "2025-03-14", "type": "jama", "reference": "UPI-123", "debit": 0, "credit": 5000.0, "balance": 10200.0} ], "total_debit": 15200.0, "total_credit": 5000.0, "closing_balance": 10200.0 } } }, "POST /api/party-jama": { "desc": "Create a standalone jama (payment) entry", "request_body": { "party_id": "p-abc123", "date": "2025-03-15", "amount": 5000.0, "reference": "UPI-456", "notes": "आगाऊ रक्कम" }, "example_response": {"success": True, "message": "Jama entry created successfully", "data": {"id": "pj-555eee"}} }, "PUT /api/party-jama/{entry_id}": { "desc": "Update a jama entry", "request_body": {"amount": 6000.0, "notes": "सुधारित रक्कम"}, "example_response": {"success": True, "message": "Jama entry updated successfully"} }, "DELETE /api/party-jama/{entry_id}": { "desc": "Delete a jama entry", "example_response": {"success": True, "message": "Jama entry deleted successfully"} }, # ── MISC ────────────────────────────────────── "GET /health": { "desc": "App health check", "example_response": {"status": "healthy", "app": "Mirchi Trading API", "version": "1.0.0"} }, "GET /": { "desc": "Root info endpoint", "example_response": {"message": "Welcome to Mirchi Trading API", "version": "1.0.0", "docs": "/docs"} } } # ───────────────────────────────────────────────── # SECTION 3 — LIVE DB TABLE INSPECTION (PostgreSQL) # ───────────────────────────────────────────────── async def inspect_database(): """Connect to PostgreSQL and print table names + column count""" try: from sqlalchemy.ext.asyncio import create_async_engine from sqlalchemy import text from dotenv import load_dotenv env_path = Path(__file__).parent.parent / ".env" load_dotenv(env_path) db_url = os.getenv("DATABASE_URL", "") if not db_url: print("⚠️ DATABASE_URL not found in .env") return engine = create_async_engine(db_url, echo=False) async with engine.connect() as conn: # Get all tables tables_result = await conn.execute(text(""" SELECT table_name FROM information_schema.tables WHERE table_schema = 'public' ORDER BY table_name; """)) tables = [row[0] for row in tables_result] print(f"\n{'='*60}") print(f" LIVE DATABASE TABLES ({len(tables)} tables found)") print(f"{'='*60}") for table in tables: # Get columns for each table cols_result = await conn.execute(text(f""" SELECT column_name, data_type, is_nullable, column_default FROM information_schema.columns WHERE table_schema = 'public' AND table_name = '{table}' ORDER BY ordinal_position; """)) cols = cols_result.fetchall() # Get row count count_result = await conn.execute(text(f'SELECT COUNT(*) FROM "{table}"')) row_count = count_result.scalar() print(f"\n📋 {table.upper()} ({row_count} rows)") print(f" {'Column':<35} {'Type':<25} {'Nullable'}") print(f" {'-'*35} {'-'*25} {'-'*8}") for col in cols: nullable = "YES" if col[2] == "YES" else "NO " print(f" {col[0]:<35} {col[1]:<25} {nullable}") await engine.dispose() except Exception as e: print(f"\n⚠️ Could not connect to database: {e}") print(" (Showing schema from models only)\n") # ───────────────────────────────────────────────── # MAIN RUNNER # ───────────────────────────────────────────────── def print_api_docs(): print(f"\n{'='*70}") print(" MIRCHI TRADING — ALL API ENDPOINTS + EXAMPLE RESPONSES") print(f"{'='*70}") print(f" Base URL: http://localhost:8000\n") for i, (endpoint, info) in enumerate(API_DOCS.items(), 1): print(f"\n[{i:02d}] {endpoint}") print(f" ↳ {info['desc']}") if "query_params" in info: print(f" Params: {info['query_params']}") if "request_body" in info: body = json.dumps(info['request_body'], ensure_ascii=False, indent=8) print(f" Body: {body[:200]}{'...' if len(body) > 200 else ''}") resp = json.dumps(info['example_response'], ensure_ascii=False, indent=8) resp_short = resp[:300] + ("..." if len(resp) > 300 else "") print(f" Response: {resp_short}") print(f" {'─'*65}") print(f"\n{'='*70}") print(f" TOTAL ENDPOINTS: {len(API_DOCS)}") print(f"{'='*70}\n") async def main(): print(DB_SCHEMA_SUMMARY) await inspect_database() print_api_docs() print("\n✅ Documentation complete. API docs also available at: http://localhost:8000/docs") if __name__ == "__main__": asyncio.run(main())