"""DATEV export service: create exports, list exports, generate CSV.""" from __future__ import annotations import os import uuid from datetime import date from decimal import Decimal from typing import Any from sqlalchemy import and_, func, select from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.orm import selectinload from app.models.datev_export import DATEVExport from app.models.sale import Sale from app.utils.datev import generate_datev_csv, validate_datev_csv async def create_export( db: AsyncSession, start_date: date, end_date: date, output_dir: str = "/tmp/datev_exports", ) -> DATEVExport: """Create a DATEV export for the given date range. Queries all completed sales within the date range, generates a CSV file, and stores the export record. Args: db: Async session. start_date: Start of the date range (inclusive). end_date: End of the date range (inclusive). output_dir: Directory to save the CSV file. Returns: DATEVExport model instance. """ # Query completed sales within date range stmt = ( select(Sale) .options( selectinload(Sale.vehicle), selectinload(Sale.buyer), selectinload(Sale.seller), ) .where( and_( Sale.sale_date >= start_date, Sale.sale_date <= end_date, Sale.status == "completed", ) ) .order_by(Sale.sale_date.asc()) ) result = await db.execute(stmt) sales = list(result.scalars().all()) # Generate CSV content csv_content = generate_datev_csv(sales) # Calculate total amount total_amount = sum( (s.sale_price or Decimal("0")) for s in sales ) # Save CSV file os.makedirs(output_dir, exist_ok=True) export_id = uuid.uuid4() file_path = os.path.join(output_dir, f"datev_export_{export_id}.csv") with open(file_path, "w", encoding="utf-8") as f: f.write(csv_content) # Create export record export = DATEVExport( id=export_id, start_date=start_date, end_date=end_date, file_path=file_path, total_amount=total_amount, ) db.add(export) await db.flush() await db.refresh(export) return export async def list_exports( db: AsyncSession, page: int = 1, page_size: int = 20, ) -> tuple[list[DATEVExport], int]: """List DATEV exports with pagination. Returns (exports, total_count). """ count_stmt = select(func.count(DATEVExport.id)) total_result = await db.execute(count_stmt) total = total_result.scalar_one() data_stmt = ( select(DATEVExport) .order_by(DATEVExport.created_at.desc()) .offset((page - 1) * page_size) .limit(page_size) ) result = await db.execute(data_stmt) exports = list(result.scalars().all()) return exports, total async def get_export_csv(db: AsyncSession, export_id: uuid.UUID) -> tuple[str, bytes] | None: """Get the CSV content for a DATEV export. Args: db: Async session. export_id: Export ID. Returns: Tuple of (filename, csv_bytes) or None if export not found. """ stmt = select(DATEVExport).where(DATEVExport.id == export_id) result = await db.execute(stmt) export = result.scalar_one_or_none() if export is None: return None if export.file_path and os.path.exists(export.file_path): with open(export.file_path, "r", encoding="utf-8") as f: csv_content = f.read() else: # Regenerate from sales if file is missing sales_stmt = ( select(Sale) .options(selectinload(Sale.vehicle)) .where( and_( Sale.sale_date >= export.start_date, Sale.sale_date <= export.end_date, Sale.status == "completed", ) ) .order_by(Sale.sale_date.asc()) ) sales_result = await db.execute(sales_stmt) sales = list(sales_result.scalars().all()) csv_content = generate_datev_csv(sales) filename = f"datev_export_{export.id}.csv" return filename, csv_content.encode("utf-8")