__tablename__ = "campaign_recipients" id = Column(Integer, primary_key=True) campaign_id = Column(Integer, ForeignKey("campaigns.id", ondelete="CASCADE"), nullable=False, index=True) recipient_id = Column(Integer, ForeignKey("recipients.id", ondelete="CASCADE"), nullable=False) email = Column(String(320), nullable=False, index=True) status = Column(String(30), default="pending") # pending|queued|processing|sent|temp_failed|perm_failed|suppressed|skipped generated_invoice_number = Column(String(100), default="") generated_amount = Column(Float) generated_shipping = Column(Float) generated_subject_id = Column(Integer, ForeignKey("subject_lines.id", ondelete="SET NULL")) generated_template_id = Column(Integer, ForeignKey("html_templates.id", ondelete="SET NULL")) generated_phone = Column(String(60), default="") assigned_account_id = Column(Integer, ForeignKey("gmail_accounts.id", ondelete="SET NULL")) attempt_count = Column(Integer, default=0) last_error = Column(Text, default="") msg_id = Column(String(200), default="") # Gmail message id thread_id = Column(String(200), default="") # Gmail thread id sent_at = Column(DateTime(timezone=True)) seq = Column(Integer, default=0) # ordering within campaign campaign = relationship("Campaign") recipient = relationship("Recipient") subject = relationship("SubjectLine") template = relationship("EmailTemplate") account = relationship("GmailAccount") __table_args__ = (UniqueConstraint("campaign_id", "recipient_id", name="uq_campaign_recipient"), Index("ix_campaign_recipient_status", "campaign_id", "status"),) class Attachment(Base, TimestampMixin): __tablename__ = "attachments" id = Column(Integer, primary_key=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False, index=True) name = Column(String(300), nullable=False) filename = Column(String(300), nullable=False) stored_path = Column(String(500), nullable=False) # relative path under uploads/ mime_type = Column(String(120), default="application/octet-stream") size_bytes = Column(Integer, default=0) kind = Column(String(30), default="file") # text|html|pdf|image|file description = Column(Text, default="")