Files
moneyfy/backend/app/models/notification.py
T
moneyfyandClaude Opus 5 0b06775be3 feat: Projektfundament mit Datenmodell, Migrationen und Seed
- Repo-Struktur für Backend, Frontend, Dokumentation und Gitea-Workflows
- FastAPI-Skeleton mit pydantic-settings und RFC-7807-artigem Fehlerformat
- Vollständiges Datenmodell (15 Tabellen) als SQLAlchemy-2.0-Modelle
- Alembic gegen die Async-Engine inklusive Erstmigration
- Idempotenter Seed für den deutschen Kategoriebaum und Standardregeln
- Endpunkte /api/health und /api/version
- pytest-Infrastruktur mit transaktionsisolierten Fixtures

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014e7t8UpmoVNMtWivY5LiSH
2026-09-09 13:12:08 +02:00

74 lines
2.4 KiB
Python

"""Benachrichtigungsregeln und Versandprotokoll."""
from datetime import date, datetime
from sqlalchemy import (
Boolean,
Date,
DateTime,
ForeignKey,
Index,
Integer,
String,
Text,
UniqueConstraint,
)
from sqlalchemy.orm import Mapped, mapped_column, relationship
from app.db.base import Base, CreatedAtMixin
from app.models.enums import (
NotificationChannel,
NotificationStatus,
NotificationType,
pg_enum,
)
class NotificationRule(Base, CreatedAtMixin):
"""Eine Regel pro Ereignistyp und Kanal."""
__tablename__ = "notification_rule"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
type: Mapped[NotificationType] = mapped_column(
pg_enum(NotificationType, "notification_type"), nullable=False
)
lead_days: Mapped[int] = mapped_column(Integer, nullable=False, default=7)
channel: Mapped[NotificationChannel] = mapped_column(
pg_enum(NotificationChannel, "notification_channel"), nullable=False
)
target: Mapped[str | None] = mapped_column(Text, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, nullable=False, default=True)
logs: Mapped[list["NotificationLog"]] = relationship(
back_populates="rule", cascade="all, delete-orphan"
)
class NotificationLog(Base):
"""Versandprotokoll. `dedupe_day` verhindert Doppelversand am selben Tag."""
__tablename__ = "notification_log"
id: Mapped[int] = mapped_column(Integer, primary_key=True)
rule_id: Mapped[int] = mapped_column(
ForeignKey("notification_rule.id", ondelete="CASCADE"), nullable=False
)
ref_type: Mapped[str] = mapped_column(String(40), nullable=False)
ref_id: Mapped[str] = mapped_column(String(80), nullable=False)
dedupe_day: Mapped[date] = mapped_column(Date, nullable=False)
sent_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), nullable=False)
status: Mapped[NotificationStatus] = mapped_column(
pg_enum(NotificationStatus, "notification_status"), nullable=False
)
error: Mapped[str | None] = mapped_column(Text, nullable=True)
rule: Mapped[NotificationRule] = relationship(back_populates="logs")
__table_args__ = (
UniqueConstraint(
"rule_id", "ref_type", "ref_id", "dedupe_day", name="uq_notification_log_rule_id"
),
Index("ix_notification_log_sent_at", "sent_at"),
)