# app/modules/messaging/models/store_email_template.py """ Store email template override model. Allows stores to customize platform email templates with their own content. Platform-only templates cannot be overridden (e.g., billing, subscription emails). """ from datetime import datetime from sqlalchemy import ( Boolean, Column, ForeignKey, Integer, String, Text, UniqueConstraint, ) from sqlalchemy.orm import Session, relationship from app.core.database import Base from models.database.base import TimestampMixin class StoreEmailTemplate(Base, TimestampMixin): """ Store-specific email template override. Each store can customize email templates for their shop. Overrides are per-template-code and per-language. When sending emails: 1. Check if store has an override for the template+language 2. If yes, use store's version 3. If no, fall back to platform template Platform-only templates (is_platform_only=True on EmailTemplate) cannot be overridden. """ __tablename__ = "store_email_templates" id = Column(Integer, primary_key=True, index=True, autoincrement=True) # Store relationship store_id = Column( Integer, ForeignKey("stores.id", ondelete="CASCADE"), nullable=False, index=True, ) # Template identification (references EmailTemplate.code, not FK) template_code = Column(String(100), nullable=False, index=True) language = Column(String(5), nullable=False, default="en") # Optional custom name (if None, uses platform template name) name = Column(String(255), nullable=True) # Email content subject = Column(String(500), nullable=False) body_html = Column(Text, nullable=False) body_text = Column(Text, nullable=True) # Status is_active = Column(Boolean, default=True, nullable=False) # Relationships store = relationship("Store", back_populates="email_templates") # Unique constraint: one override per store+template+language __table_args__ = ( UniqueConstraint( "store_id", "template_code", "language", name="uq_store_email_template_code_language", ), {"sqlite_autoincrement": True}, ) def __repr__(self): return ( f"" ) @classmethod def get_override( cls, db: Session, store_id: int, template_code: str, language: str, ) -> "StoreEmailTemplate | None": """ Get store's template override if it exists. Args: db: Database session store_id: Store ID template_code: Template code to look up language: Language code (en, fr, de, lb) Returns: StoreEmailTemplate if override exists, None otherwise """ return ( db.query(cls) .filter( cls.store_id == store_id, cls.template_code == template_code, cls.language == language, cls.is_active == True, # noqa: E712 ) .first() ) @classmethod def get_all_overrides_for_store( cls, db: Session, store_id: int, ) -> list["StoreEmailTemplate"]: """ Get all template overrides for a store. Args: db: Database session store_id: Store ID Returns: List of StoreEmailTemplate objects """ return ( db.query(cls) .filter( cls.store_id == store_id, cls.is_active == True, # noqa: E712 ) .order_by(cls.template_code, cls.language) .all() ) @classmethod def create_or_update( cls, db: Session, store_id: int, template_code: str, language: str, subject: str, body_html: str, body_text: str | None = None, name: str | None = None, ) -> "StoreEmailTemplate": """ Create or update a store email template override. Args: db: Database session store_id: Store ID template_code: Template code language: Language code subject: Email subject body_html: HTML body content body_text: Optional plain text body name: Optional custom name Returns: Created or updated StoreEmailTemplate """ existing = cls.get_override(db, store_id, template_code, language) if existing: existing.subject = subject existing.body_html = body_html existing.body_text = body_text existing.name = name existing.updated_at = datetime.utcnow() return existing new_template = cls( store_id=store_id, template_code=template_code, language=language, subject=subject, body_html=body_html, body_text=body_text, name=name, ) db.add(new_template) return new_template @classmethod def delete_override( cls, db: Session, store_id: int, template_code: str, language: str, ) -> bool: """ Delete a store's template override (revert to platform default). Args: db: Database session store_id: Store ID template_code: Template code language: Language code Returns: True if deleted, False if not found """ deleted = ( db.query(cls) .filter( cls.store_id == store_id, cls.template_code == template_code, cls.language == language, ) .delete() ) return deleted > 0 __all__ = ["StoreEmailTemplate"]