feat(psa): add PsaPostLog model and migration

Audit trail for notes posted to PSA systems. Tracks session ID,
ticket ID, note type, content, status (success/failed), and any
status changes made alongside the note post.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Michael Chihlas
2026-03-14 23:28:21 -04:00
parent dcf8bce2bf
commit 7059969d05
4 changed files with 118 additions and 0 deletions

View File

@@ -37,6 +37,7 @@ from .survey_invite import SurveyInvite
from .kb_import import KBImport, KBImportNode
from .script_template import ScriptCategory, ScriptTemplate, ScriptGeneration
from .psa_connection import PsaConnection
from .psa_post_log import PsaPostLog
__all__ = [
"User",
@@ -88,4 +89,5 @@ __all__ = [
"ScriptTemplate",
"ScriptGeneration",
"PsaConnection",
"PsaPostLog",
]

View File

@@ -0,0 +1,58 @@
"""Audit trail for notes posted to PSA systems."""
import uuid
from datetime import datetime, timezone
from typing import Optional
from sqlalchemy import String, DateTime, Text, ForeignKey
from sqlalchemy.orm import Mapped, mapped_column, relationship
from sqlalchemy.dialects.postgresql import UUID
from app.core.database import Base
class PsaPostLog(Base):
__tablename__ = "psa_post_log"
id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), primary_key=True, default=uuid.uuid4
)
session_id: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True),
ForeignKey("sessions.id", ondelete="CASCADE"),
nullable=False,
index=True,
)
psa_connection_id: Mapped[Optional[uuid.UUID]] = mapped_column(
UUID(as_uuid=True),
ForeignKey("psa_connections.id", ondelete="SET NULL"),
nullable=True,
)
ticket_id: Mapped[str] = mapped_column(String(100), nullable=False)
note_type: Mapped[str] = mapped_column(String(50), nullable=False)
content_posted: Mapped[str] = mapped_column(Text, nullable=False)
external_note_id: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True
)
status: Mapped[str] = mapped_column(
String(20), nullable=False
) # 'success' or 'failed'
error_message: Mapped[Optional[str]] = mapped_column(Text, nullable=True)
status_changed_from: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True
)
status_changed_to: Mapped[Optional[str]] = mapped_column(
String(100), nullable=True
)
posted_by: Mapped[uuid.UUID] = mapped_column(
UUID(as_uuid=True), ForeignKey("users.id"), nullable=False
)
posted_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True),
nullable=False,
default=lambda: datetime.now(timezone.utc),
)
# Relationships
session = relationship("Session", foreign_keys=[session_id])
psa_connection = relationship("PsaConnection", foreign_keys=[psa_connection_id])
user = relationship("User", foreign_keys=[posted_by])