Spaces:
Runtime error
Runtime error
| """ | |
| Comprehensive Integration Test Suite for Mirchi Trading Backend | |
| Connected to PostgreSQL Database | |
| Test Scenarios: | |
| 1. 5 Parties: Awaak-only, Jawaak-only, Mixed, Patti Awaak, Patti Jawaak | |
| 2. 5 Mirchi Types: Byadgi, Teja, Kashmiri, C5, Dabbi | |
| 3. Regular Awaak -> Jawaak flow | |
| 4. Patti Awaak -> Jawaak flow | |
| 5. Mixed Jawaak scenarios | |
| 6. Payment scenarios: Full, Half Due, Cash, Online, Partial, Full Due | |
| 7. Due scenarios in Awaak, Jawaak, Mixed | |
| 8. Revert scenarios for all bill types | |
| """ | |
| import pytest | |
| import asyncio | |
| from httpx import AsyncClient, ASGITransport | |
| from sqlalchemy.ext.asyncio import AsyncSession, create_async_engine, async_sessionmaker | |
| from sqlalchemy import select | |
| from datetime import datetime | |
| import sys | |
| import os | |
| # Add backend to path | |
| sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) | |
| from app.main import app | |
| from app.core.database import Base, get_db | |
| from app.core.config import settings | |
| from app.models import Party, MirchiType, Transaction, PattiTransaction, BillNumberSequence | |
| from app.models.transaction import BillType | |
| from app.models.patti_transaction import PattiBillType | |
| from app.models.party import PartyType | |
| # Use PostgreSQL database for integration tests | |
| TEST_DATABASE_URL = settings.DATABASE_URL | |
| def event_loop(): | |
| """Create event loop for async tests""" | |
| loop = asyncio.get_event_loop_policy().new_event_loop() | |
| yield loop | |
| loop.close() | |
| async def db_session(): | |
| """Create database session for tests - uses PostgreSQL""" | |
| engine = create_async_engine(TEST_DATABASE_URL, echo=False) | |
| async_session = async_sessionmaker(engine, expire_on_commit=False) | |
| async with async_session() as session: | |
| yield session | |
| await engine.dispose() | |
| async def client(db_session): | |
| """Create test client""" | |
| async def override_get_db(): | |
| yield db_session | |
| app.dependency_overrides[get_db] = override_get_db | |
| async with AsyncClient(transport=ASGITransport(app=app), base_url="http://test") as ac: | |
| yield ac | |
| app.dependency_overrides.clear() | |
| # ============================================ | |
| # TEST DATA SETUP | |
| # ============================================ | |
| class TestDataSetup: | |
| """Setup test data - Parties and Mirchi Types""" | |
| async def test_01_create_mirchi_types(self, client: AsyncClient, db_session: AsyncSession): | |
| """Create 5 Mirchi Types: Byadgi, Teja, Kashmiri, C5, Dabbi""" | |
| mirchi_types = ["Byadgi", "Teja", "Kashmiri", "C5", "Dabbi"] | |
| created_types = [] | |
| for name in mirchi_types: | |
| response = await client.post("/api/mirchi-types", json={"name": name}) | |
| if response.status_code == 201: | |
| created_types.append(response.json()["data"]) | |
| else: | |
| # Already exists, fetch it | |
| get_resp = await client.get(f"/api/mirchi-types?search={name}") | |
| if get_resp.json()["total"] > 0: | |
| created_types.append(get_resp.json()["data"][0]) | |
| assert len(created_types) >= 5, "All 5 mirchi types should exist" | |
| print(f"\n✅ Verified {len(created_types)} Mirchi Types") | |
| async def test_02_create_parties(self, client: AsyncClient, db_session: AsyncSession): | |
| """Create 5 Parties with different types""" | |
| parties = [ | |
| {"name": "Ramesh Awaak Party", "phone": "9876543201", "city": "Sangli", "party_type": "awaak"}, | |
| {"name": "Suresh Jawaak Party", "phone": "9876543202", "city": "Kolhapur", "party_type": "jawaak"}, | |
| {"name": "Mixed Trading Co", "phone": "9876543203", "city": "Pune", "party_type": "both"}, | |
| {"name": "Patti Awaak Holder", "phone": "9876543204", "city": "Solapur", "party_type": "awaak"}, | |
| {"name": "Patti Jawaak Buyer", "phone": "9876543205", "city": "Satara", "party_type": "jawaak"}, | |
| ] | |
| created_parties = [] | |
| for party in parties: | |
| response = await client.post("/api/parties", json=party) | |
| if response.status_code == 201: | |
| created_parties.append(response.json()["data"]) | |
| assert len(created_parties) >= 5, "All 5 parties should be created" | |
| print(f"\n✅ Created {len(created_parties)} Parties") | |
| # ============================================ | |
| # REGULAR AWAAK -> JAWAAK FLOW | |
| # ============================================ | |
| class TestRegularAwaakJawaakFlow: | |
| """Regular trading flow tests""" | |
| async def test_awaak_full_payment(self, client: AsyncClient, db_session: AsyncSession): | |
| """Awaak transaction with FULL payment (cash)""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| assert parties.json()["total"] > 0, "Party should exist" | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 50000, | |
| "total_amount": 50000, | |
| "paid_amount": 50000, | |
| "balance_amount": 0, | |
| "lot_number": "LOT-AWAAK-FULL-001", | |
| "items": [{"mirchi_name": "Byadgi", "poti_count": 50, "net_weight": 2500, "rate_per_kg": 20, "amount": 50000}], | |
| "payments": [{"amount": 50000, "mode": "cash"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["balance_amount"] == 0 | |
| print(f"\n✅ Awaak Full Payment: Bill {data['data']['bill_number']}") | |
| async def test_awaak_half_due(self, client: AsyncClient, db_session: AsyncSession): | |
| """Awaak transaction with HALF payment, HALF due""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 40000, | |
| "total_amount": 40000, | |
| "paid_amount": 20000, | |
| "balance_amount": 20000, | |
| "lot_number": "LOT-AWAAK-HALF-001", | |
| "items": [{"mirchi_name": "Teja", "poti_count": 40, "net_weight": 2000, "rate_per_kg": 20, "amount": 40000}], | |
| "payments": [{"amount": 20000, "mode": "cash"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["balance_amount"] == 20000 | |
| print(f"\n✅ Awaak Half Due: Bill {data['data']['bill_number']}, Due=₹{data['data']['balance_amount']}") | |
| async def test_awaak_full_due(self, client: AsyncClient, db_session: AsyncSession): | |
| """Awaak transaction with FULL DUE (no payment)""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 30000, | |
| "total_amount": 30000, | |
| "paid_amount": 0, | |
| "balance_amount": 30000, | |
| "lot_number": "LOT-AWAAK-DUE-001", | |
| "items": [{"mirchi_name": "Kashmiri", "poti_count": 30, "net_weight": 1500, "rate_per_kg": 20, "amount": 30000}], | |
| "payments": [] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["balance_amount"] == 30000 | |
| print(f"\n✅ Awaak Full Due: Bill {data['data']['bill_number']}, Due=₹{data['data']['balance_amount']}") | |
| async def test_jawaak_full_payment_online(self, client: AsyncClient, db_session: AsyncSession): | |
| """Jawaak transaction with FULL payment (UPI/Online)""" | |
| parties = await client.get("/api/parties?search=Suresh Jawaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 60000, | |
| "total_amount": 60000, | |
| "paid_amount": 60000, | |
| "balance_amount": 0, | |
| "items": [{"mirchi_name": "Byadgi", "poti_count": 30, "net_weight": 3000, "rate_per_kg": 20, "amount": 60000}], | |
| "payments": [{"amount": 60000, "mode": "upi", "reference": "UPI-123456"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["bill_number"].startswith("J") | |
| print(f"\n✅ Jawaak Full Online Payment: Bill {data['data']['bill_number']}") | |
| async def test_jawaak_partial_payment_cash_online(self, client: AsyncClient, db_session: AsyncSession): | |
| """Jawaak with PARTIAL payment - Half Cash, Half Online""" | |
| parties = await client.get("/api/parties?search=Suresh Jawaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 50000, | |
| "total_amount": 50000, | |
| "paid_amount": 25000, | |
| "balance_amount": 25000, | |
| "items": [{"mirchi_name": "Teja", "poti_count": 25, "net_weight": 2500, "rate_per_kg": 20, "amount": 50000}], | |
| "payments": [{"amount": 15000, "mode": "cash"}, {"amount": 10000, "mode": "upi"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["paid_amount"] == 25000 | |
| assert len(data["data"]["payments"]) == 2 | |
| print(f"\n✅ Jawaak Partial (Cash+UPI): Bill {data['data']['bill_number']}") | |
| # ============================================ | |
| # PATTI AWAAK -> JAWAAK FLOW | |
| # ============================================ | |
| class TestPattiAwaakJawaakFlow: | |
| """Patti trading flow tests""" | |
| async def test_patti_awaak_no_lot(self, client: AsyncClient, db_session: AsyncSession): | |
| """Patti Awaak - No lot required""" | |
| parties = await client.get("/api/parties?search=Patti Awaak Holder") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_awaak", | |
| "party_id": party_id, | |
| "subtotal": 35000, | |
| "total_amount": 35000, | |
| "paid_amount": 0, | |
| "balance_amount": 35000, | |
| "expenses": {"packing": 200, "godown": 300, "commission": 500}, | |
| "items": [{"mirchi_name": "C5", "poti_count": 35, "net_weight": 1750, "rate_per_kg": 20, "amount": 35000}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["bill_number"].startswith("PA") | |
| print(f"\n✅ Patti Awaak (no lot): Bill {data['data']['bill_number']}") | |
| async def test_patti_awaak_with_lot(self, client: AsyncClient, db_session: AsyncSession): | |
| """Patti Awaak - With lot (optional)""" | |
| parties = await client.get("/api/parties?search=Patti Awaak Holder") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_awaak", | |
| "party_id": party_id, | |
| "subtotal": 25000, | |
| "total_amount": 25000, | |
| "paid_amount": 10000, | |
| "balance_amount": 15000, | |
| "lot_number": "LOT-PA-001", | |
| "expenses": {"packing": 150, "godown": 200}, | |
| "items": [{"mirchi_name": "Dabbi", "poti_count": 25, "net_weight": 1250, "rate_per_kg": 20, "amount": 25000}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["lot_number"] == "LOT-PA-001" | |
| print(f"\n✅ Patti Awaak (with lot): Bill {data['data']['bill_number']}") | |
| async def test_patti_jawaak_with_lot_weightage(self, client: AsyncClient, db_session: AsyncSession): | |
| """Patti Jawaak - Lot MANDATORY, weightage required""" | |
| parties = await client.get("/api/parties?search=Patti Jawaak Buyer") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_jawaak", | |
| "party_id": party_id, | |
| "subtotal": 40000, | |
| "total_amount": 40000, | |
| "paid_amount": 40000, | |
| "balance_amount": 0, | |
| "lot_number": "LOT-PJ-001", | |
| "gross_weight": 1850, | |
| "verified_net_weight": 1800, | |
| "expenses": {"packing": 200, "godown": 300}, | |
| "items": [{"mirchi_name": "C5", "poti_count": 36, "net_weight": 1800, "rate_per_kg": 22.22, "amount": 40000}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["bill_number"].startswith("PJ") | |
| assert data["data"]["lot_number"] == "LOT-PJ-001" | |
| assert data["data"]["gross_weight"] == 1850 | |
| print(f"\n✅ Patti Jawaak (lot+weightage): Bill {data['data']['bill_number']}") | |
| async def test_patti_jawaak_without_lot_fails(self, client: AsyncClient, db_session: AsyncSession): | |
| """Patti Jawaak without lot should FAIL""" | |
| parties = await client.get("/api/parties?search=Patti Jawaak Buyer") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_jawaak", | |
| "party_id": party_id, | |
| "subtotal": 20000, | |
| "total_amount": 20000, | |
| "paid_amount": 20000, | |
| "balance_amount": 0, | |
| "items": [{"mirchi_name": "Dabbi", "poti_count": 20, "net_weight": 1000, "rate_per_kg": 20, "amount": 20000}] | |
| }) | |
| assert response.status_code == 400 | |
| print(f"\n✅ Patti Jawaak without lot correctly FAILS") | |
| # ============================================ | |
| # MIXED JAWAAK SCENARIOS | |
| # ============================================ | |
| class TestMixedJawaakScenarios: | |
| """Mixed Jawaak tests""" | |
| async def test_mixed_jawaak_full_payment(self, client: AsyncClient, db_session: AsyncSession): | |
| """Mixed Jawaak with full payment""" | |
| parties = await client.get("/api/parties?search=Mixed Trading") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 70000, | |
| "total_amount": 70000, | |
| "paid_amount": 70000, | |
| "balance_amount": 0, | |
| "items": [ | |
| {"mirchi_name": "Byadgi", "poti_count": 20, "net_weight": 1000, "rate_per_kg": 30, "amount": 30000, "source": "normal"}, | |
| {"mirchi_name": "Teja", "poti_count": 20, "net_weight": 1000, "rate_per_kg": 40, "amount": 40000, "source": "patti"} | |
| ], | |
| "payments": [{"amount": 40000, "mode": "bank"}, {"amount": 30000, "mode": "cash"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["is_mixed"] is True | |
| print(f"\n✅ Mixed Jawaak: Bill {data['data']['bill_number']}, is_mixed=True") | |
| async def test_mixed_jawaak_half_due(self, client: AsyncClient, db_session: AsyncSession): | |
| """Mixed Jawaak with half due""" | |
| parties = await client.get("/api/parties?search=Mixed Trading") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 50000, | |
| "total_amount": 50000, | |
| "paid_amount": 25000, | |
| "balance_amount": 25000, | |
| "items": [ | |
| {"mirchi_name": "Kashmiri", "poti_count": 15, "net_weight": 750, "rate_per_kg": 30, "amount": 22500, "source": "normal"}, | |
| {"mirchi_name": "C5", "poti_count": 14, "net_weight": 700, "rate_per_kg": 39.29, "amount": 27500, "source": "patti"} | |
| ], | |
| "payments": [{"amount": 15000, "mode": "upi"}, {"amount": 10000, "mode": "cash"}] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["is_mixed"] is True | |
| assert data["data"]["balance_amount"] == 25000 | |
| print(f"\n✅ Mixed Jawaak Half Due: Due=₹{data['data']['balance_amount']}") | |
| async def test_mixed_jawaak_full_due(self, client: AsyncClient, db_session: AsyncSession): | |
| """Mixed Jawaak with FULL DUE""" | |
| parties = await client.get("/api/parties?search=Mixed Trading") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 30000, | |
| "total_amount": 30000, | |
| "paid_amount": 0, | |
| "balance_amount": 30000, | |
| "items": [ | |
| {"mirchi_name": "Dabbi", "poti_count": 10, "net_weight": 500, "rate_per_kg": 30, "amount": 15000, "source": "normal"}, | |
| {"mirchi_name": "Dabbi", "poti_count": 10, "net_weight": 500, "rate_per_kg": 30, "amount": 15000, "source": "patti"} | |
| ], | |
| "payments": [] | |
| }) | |
| assert response.status_code == 201 | |
| data = response.json() | |
| assert data["data"]["balance_amount"] == 30000 | |
| print(f"\n✅ Mixed Jawaak Full Due: Due=₹{data['data']['balance_amount']}") | |
| # ============================================ | |
| # REVERT SCENARIOS | |
| # ============================================ | |
| class TestRevertScenarios: | |
| """Revert tests for all bill types""" | |
| async def test_revert_awaak(self, client: AsyncClient, db_session: AsyncSession): | |
| """Revert awaak transaction""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 10000, | |
| "total_amount": 10000, | |
| "paid_amount": 0, | |
| "balance_amount": 10000, | |
| "items": [{"mirchi_name": "C5", "poti_count": 10, "net_weight": 500, "rate_per_kg": 20, "amount": 10000}], | |
| "payments": [] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| response = await client.post(f"/api/transactions/{txn_id}/revert") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Revert Awaak: Success") | |
| async def test_revert_jawaak(self, client: AsyncClient, db_session: AsyncSession): | |
| """Revert jawaak transaction - should fail with payments""" | |
| parties = await client.get("/api/parties?search=Suresh Jawaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 8000, | |
| "total_amount": 8000, | |
| "paid_amount": 8000, | |
| "balance_amount": 0, | |
| "items": [{"mirchi_name": "Dabbi", "poti_count": 8, "net_weight": 400, "rate_per_kg": 20, "amount": 8000}], | |
| "payments": [{"amount": 8000, "mode": "cash"}] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| response = await client.post(f"/api/transactions/{txn_id}/revert") | |
| # Should fail because there are unreversed payments | |
| assert response.status_code == 400 | |
| print(f"\n✅ Revert Jawaak with payments: Correctly FAILS (payments must be reversed first)") | |
| async def test_revert_patti_awaak(self, client: AsyncClient, db_session: AsyncSession): | |
| """Revert patti awaak transaction""" | |
| parties = await client.get("/api/parties?search=Patti Awaak Holder") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_awaak", | |
| "party_id": party_id, | |
| "subtotal": 6000, | |
| "total_amount": 6000, | |
| "paid_amount": 0, | |
| "balance_amount": 6000, | |
| "items": [{"mirchi_name": "Byadgi", "poti_count": 6, "net_weight": 300, "rate_per_kg": 20, "amount": 6000}] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| response = await client.post(f"/api/patti-transactions/{txn_id}/revert") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Revert Patti Awaak: Success") | |
| async def test_revert_patti_jawaak(self, client: AsyncClient, db_session: AsyncSession): | |
| """Revert patti jawaak transaction""" | |
| parties = await client.get("/api/parties?search=Patti Jawaak Buyer") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/patti-transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "patti_jawaak", | |
| "party_id": party_id, | |
| "subtotal": 5000, | |
| "total_amount": 5000, | |
| "paid_amount": 5000, | |
| "balance_amount": 0, | |
| "lot_number": "LOT-REVERT-001", | |
| "gross_weight": 550, | |
| "verified_net_weight": 500, | |
| "items": [{"mirchi_name": "Teja", "poti_count": 5, "net_weight": 250, "rate_per_kg": 20, "amount": 5000}] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| response = await client.post(f"/api/patti-transactions/{txn_id}/revert") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Revert Patti Jawaak: Success") | |
| async def test_revert_mixed_jawaak(self, client: AsyncClient, db_session: AsyncSession): | |
| """Revert mixed jawaak transaction - should fail with payments""" | |
| parties = await client.get("/api/parties?search=Mixed Trading") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "jawaak", | |
| "party_id": party_id, | |
| "subtotal": 12000, | |
| "total_amount": 12000, | |
| "paid_amount": 6000, | |
| "balance_amount": 6000, | |
| "items": [ | |
| {"mirchi_name": "Byadgi", "poti_count": 5, "net_weight": 250, "rate_per_kg": 24, "amount": 6000, "source": "normal"}, | |
| {"mirchi_name": "Teja", "poti_count": 5, "net_weight": 250, "rate_per_kg": 24, "amount": 6000, "source": "patti"} | |
| ], | |
| "payments": [{"amount": 6000, "mode": "cash"}] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| assert create_response.json()["data"]["is_mixed"] is True | |
| response = await client.post(f"/api/transactions/{txn_id}/revert") | |
| # Should fail because there are unreversed payments | |
| assert response.status_code == 400 | |
| print(f"\n✅ Revert Mixed Jawaak with payments: Correctly FAILS (payments must be reversed first)") | |
| async def test_revert_already_reverted_fails(self, client: AsyncClient, db_session: AsyncSession): | |
| """Reverting already reverted transaction should fail""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| create_response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 5000, | |
| "total_amount": 5000, | |
| "paid_amount": 0, | |
| "balance_amount": 5000, | |
| "items": [{"mirchi_name": "Kashmiri", "poti_count": 5, "net_weight": 250, "rate_per_kg": 20, "amount": 5000}], | |
| "payments": [] | |
| }) | |
| txn_id = create_response.json()["data"]["id"] | |
| # First revert | |
| await client.post(f"/api/transactions/{txn_id}/revert") | |
| # Second revert should fail | |
| response = await client.post(f"/api/transactions/{txn_id}/revert") | |
| assert response.status_code == 400 | |
| print(f"\n✅ Revert already reverted: Correctly FAILS") | |
| # ============================================ | |
| # BILL NUMBER SEQUENCE TESTS | |
| # ============================================ | |
| class TestBillNumberSequences: | |
| """Bill numbering tests""" | |
| async def test_bill_number_sequence(self, client: AsyncClient, db_session: AsyncSession): | |
| """Verify bill numbers increment correctly""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| bill_numbers = [] | |
| for i in range(3): | |
| response = await client.post("/api/transactions", json={ | |
| "bill_date": datetime.now().strftime("%Y-%m-%d"), | |
| "bill_type": "awaak", | |
| "party_id": party_id, | |
| "subtotal": 1000, | |
| "total_amount": 1000, | |
| "paid_amount": 1000, | |
| "balance_amount": 0, | |
| "items": [{"mirchi_name": "C5", "poti_count": 1, "net_weight": 50, "rate_per_kg": 20, "amount": 1000}], | |
| "payments": [{"amount": 1000, "mode": "cash"}] | |
| }) | |
| if response.status_code == 201: | |
| bill_numbers.append(response.json()["data"]["bill_number"]) | |
| year = datetime.now().year | |
| for bn in bill_numbers: | |
| assert bn.startswith(f"A{year}") | |
| print(f"\n✅ Bill Number Sequence: {bill_numbers}") | |
| async def test_bill_number_info(self, client: AsyncClient, db_session: AsyncSession): | |
| """Get bill number sequence info""" | |
| response = await client.get("/api/transactions/bill-number-info?bill_type=awaak") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Bill Number Info retrieved") | |
| # ============================================ | |
| # SEARCH AND FILTER TESTS | |
| # ============================================ | |
| class TestSearchAndFilter: | |
| """Search and filtering tests""" | |
| async def test_search_by_bill_number(self, client: AsyncClient, db_session: AsyncSession): | |
| """Search by bill number""" | |
| response = await client.get("/api/transactions?search=A202") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Search by bill number: Found {response.json()['total']} transactions") | |
| async def test_filter_by_bill_type(self, client: AsyncClient, db_session: AsyncSession): | |
| """Filter by bill type""" | |
| response = await client.get("/api/transactions?bill_type=awaak") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Filter by awaak: Found {response.json()['total']} transactions") | |
| async def test_filter_by_party(self, client: AsyncClient, db_session: AsyncSession): | |
| """Filter by party ID""" | |
| parties = await client.get("/api/parties?search=Ramesh Awaak") | |
| party_id = parties.json()["data"][0]["id"] | |
| response = await client.get(f"/api/transactions?party_id={party_id}") | |
| assert response.status_code == 200 | |
| print(f"\n✅ Filter by party: Found {response.json()['total']} transactions") | |
| if __name__ == "__main__": | |
| pytest.main([__file__, "-v", "--tb=short", "-s"]) | |