From 5bcaf6a9d4cb0fdfb51a27557c77f33b77c46942 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Sat, 14 Mar 2026 22:45:04 -0400 Subject: [PATCH] feat(psa): add psa_ticket_id and psa_connection_id to sessions Add columns to link sessions to PSA tickets and connections. Includes migration 059, model relationship, and response schema fields. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../059_add_psa_ticket_link_to_sessions.py | 31 +++++++++++++++++++ backend/app/models/session.py | 9 ++++++ backend/app/schemas/session.py | 29 +++++++++++++++++ 3 files changed, 69 insertions(+) create mode 100644 backend/alembic/versions/059_add_psa_ticket_link_to_sessions.py diff --git a/backend/alembic/versions/059_add_psa_ticket_link_to_sessions.py b/backend/alembic/versions/059_add_psa_ticket_link_to_sessions.py new file mode 100644 index 00000000..cb7dc14c --- /dev/null +++ b/backend/alembic/versions/059_add_psa_ticket_link_to_sessions.py @@ -0,0 +1,31 @@ +"""Add psa_ticket_id and psa_connection_id to sessions. + +Revision ID: 059 +Revises: 058 +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import postgresql + +revision = "059" +down_revision = "058" +branch_labels = None +depends_on = None + + +def upgrade() -> None: + op.add_column("sessions", sa.Column("psa_ticket_id", sa.String(100), nullable=True)) + op.add_column( + "sessions", + sa.Column( + "psa_connection_id", + postgresql.UUID(as_uuid=True), + sa.ForeignKey("psa_connections.id", ondelete="SET NULL"), + nullable=True, + ), + ) + + +def downgrade() -> None: + op.drop_column("sessions", "psa_connection_id") + op.drop_column("sessions", "psa_ticket_id") diff --git a/backend/app/models/session.py b/backend/app/models/session.py index 1e41c534..bbab74cf 100644 --- a/backend/app/models/session.py +++ b/backend/app/models/session.py @@ -83,6 +83,15 @@ class Session(Base): attachments: Mapped[list["Attachment"]] = relationship("Attachment", back_populates="session") shares: Mapped[list["SessionShare"]] = relationship("SessionShare", back_populates="session", cascade="all, delete-orphan") + # PSA ticket link + psa_ticket_id: Mapped[Optional[str]] = mapped_column(String(100), nullable=True) + psa_connection_id: Mapped[Optional[uuid.UUID]] = mapped_column( + UUID(as_uuid=True), + ForeignKey("psa_connections.id", ondelete="SET NULL"), + nullable=True, + ) + psa_connection = relationship("PsaConnection", foreign_keys=[psa_connection_id]) + # Batch tracking (maintenance flows) batch_id: Mapped[Optional[uuid.UUID]] = mapped_column( UUID(as_uuid=True), nullable=True, index=True diff --git a/backend/app/schemas/session.py b/backend/app/schemas/session.py index 61119b46..b8e10f9b 100644 --- a/backend/app/schemas/session.py +++ b/backend/app/schemas/session.py @@ -94,6 +94,10 @@ class SessionResponse(BaseModel): batch_id: Optional[UUID] = None target_label: Optional[str] = None + # PSA ticket link + psa_ticket_id: Optional[str] = None + psa_connection_id: Optional[UUID] = None + class Config: from_attributes = True @@ -140,3 +144,28 @@ class SaveAsTreeResponse(BaseModel): tree_id: UUID tree_name: str message: str + + +# ── PSA ticket link ────────────────────────────────────────────────── + + +class TicketLinkRequest(BaseModel): + """Link or unlink a PSA ticket to a session.""" + psa_ticket_id: Optional[str] = None # null to unlink + + +class PSATicketResponse(BaseModel): + """PSA ticket details returned when linking.""" + id: str + summary: str + company_name: Optional[str] = None + board_name: Optional[str] = None + status_name: Optional[str] = None + priority_name: Optional[str] = None + + +class TicketLinkResponse(BaseModel): + """Response after linking/unlinking a ticket.""" + session_id: str + psa_ticket_id: Optional[str] = None + ticket: Optional[PSATicketResponse] = None