Adds accounting-service PATCH/archive, fiscal helpers, COA templates and setup status, plus SuperApp Accounting UI (DS, scoreboard, masters, vouchers, ledger, ops modules) with session refresh and HTTPS public API URLs. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
2.9 KiB
Python
94 lines
2.9 KiB
Python
"""General Ledger & Trial Balance API — Phase 5.3."""
|
|
from __future__ import annotations
|
|
|
|
from decimal import Decimal
|
|
from uuid import UUID
|
|
|
|
from fastapi import APIRouter, Depends
|
|
from pydantic import BaseModel
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.api.deps import get_db, require_tenant
|
|
from app.core.security import get_current_user
|
|
from app.repositories.ledger import LedgerBalanceRepository
|
|
from app.services.balance_engine import BalanceEngine
|
|
from shared.security import CurrentUser
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
class BalanceRead(BaseModel):
|
|
account_id: UUID
|
|
fiscal_period_id: UUID
|
|
opening_balance: Decimal
|
|
debit_total: Decimal
|
|
credit_total: Decimal
|
|
closing_balance: Decimal
|
|
current_balance: Decimal
|
|
|
|
model_config = {"from_attributes": True}
|
|
|
|
|
|
class TrialBalanceRead(BaseModel):
|
|
fiscal_period_id: UUID
|
|
total_debit: Decimal
|
|
total_credit: Decimal
|
|
difference: Decimal
|
|
is_balanced: bool
|
|
|
|
|
|
@router.get("/balances/{account_id}", response_model=BalanceRead | None)
|
|
async def get_account_balance(
|
|
account_id: UUID,
|
|
fiscal_period_id: UUID,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(get_current_user),
|
|
):
|
|
repo = LedgerBalanceRepository(db)
|
|
balance = await repo.get_by_account_period(tenant_id, account_id, fiscal_period_id)
|
|
return balance
|
|
|
|
|
|
@router.get("/balances", response_model=list[BalanceRead])
|
|
async def list_period_balances(
|
|
fiscal_period_id: UUID,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(get_current_user),
|
|
):
|
|
repo = LedgerBalanceRepository(db)
|
|
return await repo.list_by_period(tenant_id, fiscal_period_id)
|
|
|
|
|
|
@router.post("/trial-balance", response_model=TrialBalanceRead)
|
|
async def generate_trial_balance(
|
|
fiscal_period_id: UUID,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(get_current_user),
|
|
):
|
|
engine = BalanceEngine(db)
|
|
snapshot = await engine.generate_trial_balance(tenant_id, fiscal_period_id)
|
|
await db.commit()
|
|
return TrialBalanceRead(
|
|
fiscal_period_id=snapshot.fiscal_period_id,
|
|
total_debit=snapshot.total_debit,
|
|
total_credit=snapshot.total_credit,
|
|
difference=snapshot.difference,
|
|
is_balanced=snapshot.is_balanced,
|
|
)
|
|
|
|
|
|
@router.post("/recalculate")
|
|
async def recalculate_balances(
|
|
fiscal_period_id: UUID,
|
|
tenant_id: UUID = Depends(require_tenant),
|
|
db: AsyncSession = Depends(get_db),
|
|
_user: CurrentUser = Depends(get_current_user),
|
|
):
|
|
engine = BalanceEngine(db)
|
|
count = await engine.recalculate_period(tenant_id, fiscal_period_id)
|
|
await db.commit()
|
|
return {"recalculated_accounts": count}
|