import uuid from datetime import date import pytest from app.tests.conftest import TENANT_A, TENANT_B, tenant_headers @pytest.mark.asyncio async def test_health(client): resp = await client.get("/health") assert resp.status_code == 200 assert resp.json()["service"] == "accounting-service" @pytest.mark.asyncio async def test_create_chart_of_accounts(client): resp = await client.post( "/api/v1/accounts/charts", json={"name": "Default COA", "code": "COA-001", "is_default": True}, headers=tenant_headers(TENANT_A), ) assert resp.status_code == 201 data = resp.json() assert data["code"] == "COA-001" assert data["tenant_id"] == str(TENANT_A) @pytest.mark.asyncio async def test_tenant_isolation_accounts(client): chart_resp = await client.post( "/api/v1/accounts/charts", json={"name": "Tenant A COA", "code": "COA-A"}, headers=tenant_headers(TENANT_A), ) assert chart_resp.status_code == 201 chart_id = chart_resp.json()["id"] acc_resp = await client.post( "/api/v1/accounts", json={ "chart_id": chart_id, "code": "1000", "name": "Test Account", "account_type": "asset", "account_category": "current_asset", }, headers=tenant_headers(TENANT_A), ) assert acc_resp.status_code == 201 account_id = acc_resp.json()["id"] resp_b_list = await client.get( "/api/v1/accounts", headers=tenant_headers(TENANT_B), ) assert resp_b_list.status_code == 200 assert len(resp_b_list.json()) == 0 resp_b_get = await client.get( f"/api/v1/accounts/{account_id}", headers=tenant_headers(TENANT_B), ) assert resp_b_get.status_code == 404 @pytest.mark.asyncio async def test_posting_engine_balanced_voucher(client): year_resp = await client.post( "/api/v1/fiscal/years", json={"name": "1403", "start_date": "2024-03-20", "end_date": "2025-03-19", "is_current": True}, headers=tenant_headers(TENANT_A), ) assert year_resp.status_code == 201 year_id = year_resp.json()["id"] period_resp = await client.post( "/api/v1/fiscal/periods", json={ "fiscal_year_id": year_id, "name": "Month 1", "period_number": 1, "start_date": "2024-03-20", "end_date": "2024-04-19", "is_current": True, }, headers=tenant_headers(TENANT_A), ) assert period_resp.status_code == 201 period_id = period_resp.json()["id"] chart_resp = await client.post( "/api/v1/accounts/charts", json={"name": "COA", "code": "COA-POST"}, headers=tenant_headers(TENANT_A), ) chart_id = chart_resp.json()["id"] acc1_resp = await client.post( "/api/v1/accounts", json={ "chart_id": chart_id, "code": "1101", "name": "Cash", "account_type": "asset", "account_category": "current_asset", }, headers=tenant_headers(TENANT_A), ) acc2_resp = await client.post( "/api/v1/accounts", json={ "chart_id": chart_id, "code": "4101", "name": "Revenue", "account_type": "revenue", "account_category": "operating_revenue", }, headers=tenant_headers(TENANT_A), ) acc1_id = acc1_resp.json()["id"] acc2_id = acc2_resp.json()["id"] voucher_resp = await client.post( "/api/v1/posting/vouchers", json={ "fiscal_period_id": period_id, "voucher_number": "V-001", "voucher_date": str(date.today()), "description": "Test voucher", "lines": [ {"account_id": acc1_id, "debit": "1000.0000", "credit": "0"}, {"account_id": acc2_id, "debit": "0", "credit": "1000.0000"}, ], }, headers=tenant_headers(TENANT_A), ) assert voucher_resp.status_code == 201 voucher_id = voucher_resp.json()["id"] post_resp = await client.post( f"/api/v1/posting/vouchers/{voucher_id}/post", json={}, headers=tenant_headers(TENANT_A), ) assert post_resp.status_code == 200 assert post_resp.json()["status"] == "posted" @pytest.mark.asyncio async def test_unbalanced_voucher_rejected(client): year_resp = await client.post( "/api/v1/fiscal/years", json={"name": "1404", "start_date": "2025-03-20", "end_date": "2026-03-19"}, headers=tenant_headers(TENANT_A), ) year_id = year_resp.json()["id"] period_resp = await client.post( "/api/v1/fiscal/periods", json={ "fiscal_year_id": year_id, "name": "P1", "period_number": 1, "start_date": "2025-03-20", "end_date": "2025-04-19", "is_current": True, }, headers=tenant_headers(TENANT_A), ) period_id = period_resp.json()["id"] chart_resp = await client.post( "/api/v1/accounts/charts", json={"name": "COA2", "code": "COA-UNBAL"}, headers=tenant_headers(TENANT_A), ) chart_id = chart_resp.json()["id"] acc_resp = await client.post( "/api/v1/accounts", json={ "chart_id": chart_id, "code": "1102", "name": "Bank", "account_type": "asset", "account_category": "current_asset", }, headers=tenant_headers(TENANT_A), ) acc_id = acc_resp.json()["id"] voucher_resp = await client.post( "/api/v1/posting/vouchers", json={ "fiscal_period_id": period_id, "voucher_number": "V-UNBAL", "voucher_date": str(date.today()), "lines": [ {"account_id": acc_id, "debit": "500", "credit": "0"}, {"account_id": acc_id, "debit": "0", "credit": "300"}, ], }, headers=tenant_headers(TENANT_A), ) voucher_id = voucher_resp.json()["id"] validate_resp = await client.post( f"/api/v1/posting/vouchers/{voucher_id}/validate", headers=tenant_headers(TENANT_A), ) assert validate_resp.status_code == 422 @pytest.mark.asyncio async def test_trial_balance(client): tb_resp = await client.post( "/api/v1/ledger/trial-balance", params={"fiscal_period_id": str(uuid.uuid4())}, headers=tenant_headers(TENANT_A), ) assert tb_resp.status_code == 200 assert "is_balanced" in tb_resp.json() @pytest.mark.asyncio async def test_subsidiary_ledger_report(client): year_resp = await client.post( "/api/v1/fiscal/years", json={ "name": "1404-sub", "start_date": "2025-03-21", "end_date": "2026-03-20", "is_current": True, }, headers=tenant_headers(TENANT_A), ) assert year_resp.status_code == 201 year_id = year_resp.json()["id"] period_resp = await client.post( "/api/v1/fiscal/periods", json={ "fiscal_year_id": year_id, "name": "Farvardin", "period_number": 1, "start_date": "2025-03-21", "end_date": "2025-04-20", "is_current": True, }, headers=tenant_headers(TENANT_A), ) assert period_resp.status_code == 201 period_id = period_resp.json()["id"] chart_resp = await client.post( "/api/v1/accounts/charts", json={"name": "COA Sub", "code": "COA-SUB"}, headers=tenant_headers(TENANT_A), ) chart_id = chart_resp.json()["id"] await client.post( "/api/v1/accounts", json={ "chart_id": chart_id, "code": "1101", "name": "صندوق", "account_type": "asset", "account_category": "current_asset", "level": 3, "is_postable": True, "is_group": False, }, headers=tenant_headers(TENANT_A), ) resp = await client.post( "/api/v1/reporting/subsidiary-ledger", params={"fiscal_period_id": period_id}, headers=tenant_headers(TENANT_A), ) assert resp.status_code == 200 body = resp.json() assert "id" in body data = body["data"] if isinstance(data, str): import json data = json.loads(data) assert data["mode"] == "summary" assert data["level_mode"] == "subsidiary" assert isinstance(data["rows"], list) assert any(r.get("account_code") == "1101" for r in data["rows"]) @pytest.mark.asyncio async def test_requires_tenant_header(client): resp = await client.get("/api/v1/accounts") assert resp.status_code == 400