"""Services financiers: registre comptable, soldes et réserves.""" from sqlalchemy import desc, func, select from sqlalchemy.ext.asyncio import AsyncSession from app.core.financial_position import PositionTresorerie from app.models.depense import Depense from app.models.envoi_contribution import EnvoiContribution from app.models.mouvement_reserve import MouvementReserve from app.models.reserve import Reserve from app.models.transaction_comptable import TransactionComptable async def solde_compte(groupe_id, compte: str, db: AsyncSession) -> float: debits = float((await db.execute( select(func.coalesce(func.sum(TransactionComptable.montant), 0)).where( TransactionComptable.groupe_id == groupe_id, TransactionComptable.compte == compte, TransactionComptable.sens == "debit", ) )).scalar()) credits = float((await db.execute( select(func.coalesce(func.sum(TransactionComptable.montant), 0)).where( TransactionComptable.groupe_id == groupe_id, TransactionComptable.compte == compte, TransactionComptable.sens == "credit", ) )).scalar()) return debits - credits async def encaisse(groupe_id, db: AsyncSession) -> float: return await solde_compte(groupe_id, "encaisse", db) async def solde_banque(groupe_id, db: AsyncSession) -> float: return await solde_compte(groupe_id, "banque", db) async def engagements_non_debites(groupe_id, db: AsyncSession) -> float: depenses = float((await db.execute( select(func.coalesce(func.sum(Depense.montant), 0)).where( Depense.groupe_id == groupe_id, Depense.paye == False, Depense.methode.in_(["VIREMENT", "CHEQUE"]), ) )).scalar()) contributions = float((await db.execute( select(func.coalesce(func.sum(EnvoiContribution.montant), 0)).where( EnvoiContribution.groupe_id == groupe_id, EnvoiContribution.engagee == True, EnvoiContribution.debitee == False, ) )).scalar()) return depenses + contributions async def solde_reserve(reserve_id, db: AsyncSession) -> float: entrees = float((await db.execute( select(func.coalesce(func.sum(MouvementReserve.montant), 0)).where( MouvementReserve.destination_id == reserve_id, ) )).scalar()) sorties = float((await db.execute( select(func.coalesce(func.sum(MouvementReserve.montant), 0)).where( MouvementReserve.source_id == reserve_id, ) )).scalar()) return entrees - sorties async def total_reserves(groupe_id, db: AsyncSession, exclure_id: str | None = None) -> float: result = await db.execute(select(Reserve).where(Reserve.groupe_id == groupe_id, Reserve.active == True)) total = 0.0 for reserve in result.scalars().all(): if exclure_id and str(reserve.id) == str(exclure_id): continue total += await solde_reserve(reserve.id, db) return total async def reserve_disponible(groupe_id, db: AsyncSession) -> float: return (await position_tresorerie(groupe_id, db)).disponible_bancaire async def position_tresorerie(groupe_id, db: AsyncSession) -> PositionTresorerie: return PositionTresorerie( solde_banque=await solde_banque(groupe_id, db), encaisse=await encaisse(groupe_id, db), total_reserves=await total_reserves(groupe_id, db), engagements_non_debites=await engagements_non_debites(groupe_id, db), ) async def dernier_releve(groupe_id, db: AsyncSession): from app.models.operation_bancaire import OperationBancaire result = await db.execute( select(OperationBancaire).where( OperationBancaire.groupe_id == groupe_id, OperationBancaire.type == "releve", ).order_by(desc(OperationBancaire.date_operation)).limit(1) ) return result.scalar_one_or_none() def transaction(groupe_id, membre_id, date_transaction, type_, compte, montant, sens, source_table=None, source_id=None, note=None): return TransactionComptable( groupe_id=groupe_id, date_transaction=date_transaction, type=type_, compte=compte, montant=montant, sens=sens, source_table=source_table, source_id=source_id, note=note, cree_par=membre_id, )