import uuid from datetime import date from typing import Sequence from sqlalchemy import and_, func, select from sqlalchemy.ext.asyncio import AsyncSession from app.models.attachment import Attachment from app.models.special_expense import SpecialExpense from app.models.site import Site from app.schemas.special_expense import SpecialExpenseCreate, SpecialExpenseUpdate async def create_special_expense( db: AsyncSession, expense_in: SpecialExpenseCreate, created_by: uuid.UUID | None, ) -> SpecialExpense: expense = SpecialExpense( project_id=expense_in.project_id, center_id=expense_in.center_id, category=expense_in.category, amount=expense_in.amount, happen_date=expense_in.happen_date, description=expense_in.description, is_paid=expense_in.is_paid, paid_date=expense_in.paid_date, is_verified=expense_in.is_verified, verified_date=expense_in.verified_date, created_by=created_by, ) db.add(expense) await db.commit() await db.refresh(expense) return expense async def get_special_expense(db: AsyncSession, expense_id: uuid.UUID) -> SpecialExpense | None: result = await db.execute(select(SpecialExpense).where(SpecialExpense.id == expense_id)) return result.scalar_one_or_none() async def list_special_expenses( db: AsyncSession, project_id: uuid.UUID, center_id: uuid.UUID | None = None, category: str | None = None, date_from: date | None = None, date_to: date | None = None, ) -> Sequence[tuple[SpecialExpense, str | None, int]]: attachment_count = func.count(Attachment.id).label("attachments_count") attachment_types = ["special_expense_voucher", "special_expense_invoice", "special_expense_other"] stmt = ( select( SpecialExpense, Site.name.label("center_name"), attachment_count, ) .outerjoin(Site, Site.id == SpecialExpense.center_id) .outerjoin( Attachment, and_( Attachment.entity_type.in_(attachment_types), Attachment.entity_id == SpecialExpense.id, Attachment.is_deleted.is_(False), ), ) .where(SpecialExpense.project_id == project_id) .group_by(SpecialExpense.id, Site.name) .order_by(SpecialExpense.happen_date.desc().nullslast(), SpecialExpense.created_at.desc()) ) if center_id: stmt = stmt.where(SpecialExpense.center_id == center_id) if category: stmt = stmt.where(SpecialExpense.category == category) if date_from: stmt = stmt.where(SpecialExpense.happen_date >= date_from) if date_to: stmt = stmt.where(SpecialExpense.happen_date <= date_to) result = await db.execute(stmt) return result.all() async def update_special_expense( db: AsyncSession, expense: SpecialExpense, expense_in: SpecialExpenseUpdate ) -> SpecialExpense: update_data = expense_in.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(expense, key, value) await db.commit() await db.refresh(expense) return expense async def delete_special_expense(db: AsyncSession, expense: SpecialExpense) -> None: await db.delete(expense) await db.commit()