""" app/crud.py ─────────── Data-access layer: pure SQLAlchemy CRUD functions for every domain entity. All functions are intentionally thin — they do no business logic beyond the DB operation itself. """ from __future__ import annotations from typing import List, Optional from sqlalchemy.orm import Session from app.models import ( Client, ClientCreate, ClientUpdate, EmailTemplate, TemplateCreate, TemplateUpdate, Invoice, InvoiceCreate, InvoiceUpdate, NotificationLog, ) # ═══════════════════════════════════════════════════════════════════════════════ # Clients # ═══════════════════════════════════════════════════════════════════════════════ def get_clients(db: Session, skip: int = 0, limit: int = 500) -> List[Client]: return db.query(Client).offset(skip).limit(limit).all() def get_client(db: Session, client_id: int) -> Optional[Client]: return db.query(Client).filter(Client.id == client_id).first() def create_client(db: Session, data: ClientCreate) -> Client: obj = Client(**data.model_dump()) db.add(obj) db.commit() db.refresh(obj) return obj def update_client(db: Session, client_id: int, data: ClientUpdate) -> Optional[Client]: obj = get_client(db, client_id) if not obj: return None for field, value in data.model_dump().items(): setattr(obj, field, value) db.commit() db.refresh(obj) return obj def delete_client(db: Session, client_id: int) -> bool: obj = get_client(db, client_id) if not obj: return False db.delete(obj) db.commit() return True # ═══════════════════════════════════════════════════════════════════════════════ # Email Templates # ═══════════════════════════════════════════════════════════════════════════════ def get_templates(db: Session, skip: int = 0, limit: int = 500) -> List[EmailTemplate]: return db.query(EmailTemplate).offset(skip).limit(limit).all() def get_template(db: Session, template_id: int) -> Optional[EmailTemplate]: return db.query(EmailTemplate).filter(EmailTemplate.id == template_id).first() def create_template(db: Session, data: TemplateCreate) -> EmailTemplate: obj = EmailTemplate(**data.model_dump()) db.add(obj) db.commit() db.refresh(obj) return obj def update_template(db: Session, template_id: int, data: TemplateUpdate) -> Optional[EmailTemplate]: obj = get_template(db, template_id) if not obj: return None for field, value in data.model_dump().items(): setattr(obj, field, value) db.commit() db.refresh(obj) return obj def delete_template(db: Session, template_id: int) -> bool: obj = get_template(db, template_id) if not obj: return False db.delete(obj) db.commit() return True # ═══════════════════════════════════════════════════════════════════════════════ # Invoices # ═══════════════════════════════════════════════════════════════════════════════ def get_invoices(db: Session, skip: int = 0, limit: int = 500) -> List[Invoice]: return db.query(Invoice).offset(skip).limit(limit).all() def get_invoice(db: Session, invoice_id: int) -> Optional[Invoice]: return db.query(Invoice).filter(Invoice.id == invoice_id).first() def create_invoice(db: Session, data: InvoiceCreate) -> Invoice: obj = Invoice(**data.model_dump()) db.add(obj) db.commit() db.refresh(obj) return obj def update_invoice(db: Session, invoice_id: int, data: InvoiceUpdate) -> Optional[Invoice]: obj = get_invoice(db, invoice_id) if not obj: return None for field, value in data.model_dump().items(): setattr(obj, field, value) db.commit() db.refresh(obj) return obj def delete_invoice(db: Session, invoice_id: int) -> bool: obj = get_invoice(db, invoice_id) if not obj: return False db.delete(obj) db.commit() return True # ═══════════════════════════════════════════════════════════════════════════════ # Notification Logs # ═══════════════════════════════════════════════════════════════════════════════ def get_notification_logs( db: Session, skip: int = 0, limit: int = 500 ) -> List[NotificationLog]: return ( db.query(NotificationLog) .order_by(NotificationLog.sent_at.desc()) .offset(skip) .limit(limit) .all() ) def create_notification_log( db: Session, invoice_id: int, status: str, context: Optional[dict] = None, error_message: Optional[str] = None, ) -> NotificationLog: obj = NotificationLog( invoice_id=invoice_id, status=status, context=context or {}, error_message=error_message, ) db.add(obj) db.commit() db.refresh(obj) return obj def update_notification_status( db: Session, log_id: int, status: str, error_message: Optional[str] = None, ) -> Optional[NotificationLog]: obj = db.query(NotificationLog).filter(NotificationLog.id == log_id).first() if not obj: return None obj.status = status if error_message is not None: obj.error_message = error_message db.commit() db.refresh(obj) return obj