crud.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. """
  2. app/crud.py
  3. ───────────
  4. Data-access layer: pure SQLAlchemy CRUD functions for every domain entity.
  5. All functions are intentionally thin — they do no business logic beyond
  6. the DB operation itself.
  7. """
  8. from __future__ import annotations
  9. from typing import List, Optional
  10. from sqlalchemy.orm import Session
  11. from app.models import (
  12. Client, ClientCreate, ClientUpdate,
  13. EmailTemplate, TemplateCreate, TemplateUpdate,
  14. Invoice, InvoiceCreate, InvoiceUpdate,
  15. NotificationLog,
  16. )
  17. # ═══════════════════════════════════════════════════════════════════════════════
  18. # Clients
  19. # ═══════════════════════════════════════════════════════════════════════════════
  20. def get_clients(db: Session, skip: int = 0, limit: int = 500) -> List[Client]:
  21. return db.query(Client).offset(skip).limit(limit).all()
  22. def get_client(db: Session, client_id: int) -> Optional[Client]:
  23. return db.query(Client).filter(Client.id == client_id).first()
  24. def create_client(db: Session, data: ClientCreate) -> Client:
  25. obj = Client(**data.model_dump())
  26. db.add(obj)
  27. db.commit()
  28. db.refresh(obj)
  29. return obj
  30. def update_client(db: Session, client_id: int, data: ClientUpdate) -> Optional[Client]:
  31. obj = get_client(db, client_id)
  32. if not obj:
  33. return None
  34. for field, value in data.model_dump().items():
  35. setattr(obj, field, value)
  36. db.commit()
  37. db.refresh(obj)
  38. return obj
  39. def delete_client(db: Session, client_id: int) -> bool:
  40. obj = get_client(db, client_id)
  41. if not obj:
  42. return False
  43. db.delete(obj)
  44. db.commit()
  45. return True
  46. # ═══════════════════════════════════════════════════════════════════════════════
  47. # Email Templates
  48. # ═══════════════════════════════════════════════════════════════════════════════
  49. def get_templates(db: Session, skip: int = 0, limit: int = 500) -> List[EmailTemplate]:
  50. return db.query(EmailTemplate).offset(skip).limit(limit).all()
  51. def get_template(db: Session, template_id: int) -> Optional[EmailTemplate]:
  52. return db.query(EmailTemplate).filter(EmailTemplate.id == template_id).first()
  53. def create_template(db: Session, data: TemplateCreate) -> EmailTemplate:
  54. obj = EmailTemplate(**data.model_dump())
  55. db.add(obj)
  56. db.commit()
  57. db.refresh(obj)
  58. return obj
  59. def update_template(db: Session, template_id: int, data: TemplateUpdate) -> Optional[EmailTemplate]:
  60. obj = get_template(db, template_id)
  61. if not obj:
  62. return None
  63. for field, value in data.model_dump().items():
  64. setattr(obj, field, value)
  65. db.commit()
  66. db.refresh(obj)
  67. return obj
  68. def delete_template(db: Session, template_id: int) -> bool:
  69. obj = get_template(db, template_id)
  70. if not obj:
  71. return False
  72. db.delete(obj)
  73. db.commit()
  74. return True
  75. # ═══════════════════════════════════════════════════════════════════════════════
  76. # Invoices
  77. # ═══════════════════════════════════════════════════════════════════════════════
  78. def get_invoices(db: Session, skip: int = 0, limit: int = 500) -> List[Invoice]:
  79. return db.query(Invoice).offset(skip).limit(limit).all()
  80. def get_invoice(db: Session, invoice_id: int) -> Optional[Invoice]:
  81. return db.query(Invoice).filter(Invoice.id == invoice_id).first()
  82. def create_invoice(db: Session, data: InvoiceCreate) -> Invoice:
  83. obj = Invoice(**data.model_dump())
  84. db.add(obj)
  85. db.commit()
  86. db.refresh(obj)
  87. return obj
  88. def update_invoice(db: Session, invoice_id: int, data: InvoiceUpdate) -> Optional[Invoice]:
  89. obj = get_invoice(db, invoice_id)
  90. if not obj:
  91. return None
  92. for field, value in data.model_dump().items():
  93. setattr(obj, field, value)
  94. db.commit()
  95. db.refresh(obj)
  96. return obj
  97. def delete_invoice(db: Session, invoice_id: int) -> bool:
  98. obj = get_invoice(db, invoice_id)
  99. if not obj:
  100. return False
  101. db.delete(obj)
  102. db.commit()
  103. return True
  104. # ═══════════════════════════════════════════════════════════════════════════════
  105. # Notification Logs
  106. # ═══════════════════════════════════════════════════════════════════════════════
  107. def get_notification_logs(
  108. db: Session, skip: int = 0, limit: int = 500
  109. ) -> List[NotificationLog]:
  110. return (
  111. db.query(NotificationLog)
  112. .order_by(NotificationLog.sent_at.desc())
  113. .offset(skip)
  114. .limit(limit)
  115. .all()
  116. )
  117. def create_notification_log(
  118. db: Session,
  119. invoice_id: int,
  120. status: str,
  121. context: Optional[dict] = None,
  122. error_message: Optional[str] = None,
  123. ) -> NotificationLog:
  124. obj = NotificationLog(
  125. invoice_id=invoice_id,
  126. status=status,
  127. context=context or {},
  128. error_message=error_message,
  129. )
  130. db.add(obj)
  131. db.commit()
  132. db.refresh(obj)
  133. return obj
  134. def update_notification_status(
  135. db: Session,
  136. log_id: int,
  137. status: str,
  138. error_message: Optional[str] = None,
  139. ) -> Optional[NotificationLog]:
  140. obj = db.query(NotificationLog).filter(NotificationLog.id == log_id).first()
  141. if not obj:
  142. return None
  143. obj.status = status
  144. if error_message is not None:
  145. obj.error_message = error_message
  146. db.commit()
  147. db.refresh(obj)
  148. return obj