from __future__ import annotations from datetime import datetime from sqlalchemy import JSON, BigInteger, Boolean, ForeignKey, String, Text, TIMESTAMP, UniqueConstraint, text from sqlalchemy.orm import Mapped, mapped_column, relationship from app.db.base import Base class SupportUser(Base): """Local support identity keyed by the BARON-SSO subject and tenant.""" __tablename__ = "support_users" __table_args__ = ( UniqueConstraint("sso_subject", "tenant_id", name="uq_support_users_subject_tenant"), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) sso_subject: Mapped[str] = mapped_column(String(100)) tenant_id: Mapped[str] = mapped_column(String(100)) email: Mapped[str | None] = mapped_column(String(320), nullable=True) name: Mapped[str | None] = mapped_column(String(100), nullable=True) department: Mapped[str | None] = mapped_column(String(100), nullable=True) phone_number: Mapped[str | None] = mapped_column(String(50), nullable=True) tenant_ids: Mapped[list[str] | None] = mapped_column(JSON, nullable=True) status: Mapped[str] = mapped_column(String(20), server_default=text("'ACTIVE'")) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), server_onupdate=text("CURRENT_TIMESTAMP"), ) role_assignments: Mapped[list[SupportRoleAssignment]] = relationship(back_populates="user") class SupportRole(Base): __tablename__ = "support_roles" code: Mapped[str] = mapped_column(String(40), primary_key=True) name: Mapped[str] = mapped_column(String(100)) description: Mapped[str | None] = mapped_column(Text, nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) assignments: Mapped[list[SupportRoleAssignment]] = relationship(back_populates="role") class SupportRoleAssignment(Base): __tablename__ = "support_role_assignments" __table_args__ = ( UniqueConstraint( "support_user_id", "role_code", "workspace_id", name="uq_support_role_assignment_user_role_workspace", ), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) support_user_id: Mapped[int] = mapped_column(ForeignKey("support_users.id")) role_code: Mapped[str] = mapped_column(ForeignKey("support_roles.code")) workspace_id: Mapped[int | None] = mapped_column(ForeignKey("workspaces.id"), nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) user: Mapped[SupportUser] = relationship(back_populates="role_assignments") role: Mapped[SupportRole] = relationship(back_populates="assignments") workspace: Mapped[Workspace | None] = relationship() class Workspace(Base): __tablename__ = "workspaces" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) workspace_type: Mapped[str] = mapped_column(String(20)) software_app_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) service_type_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) workspace_code: Mapped[str] = mapped_column(String(50), unique=True) workspace_name: Mapped[str] = mapped_column(String(100)) is_active: Mapped[bool] = mapped_column(Boolean, server_default=text("1")) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) tickets: Mapped[list[SupportTicket]] = relationship(back_populates="workspace") approval_policies: Mapped[list[WorkspaceApprovalPolicy]] = relationship(back_populates="workspace") attachments: Mapped[list[Attachment]] = relationship(back_populates="workspace") access_entries: Mapped[list[UserWorkspaceAccess]] = relationship(back_populates="workspace") role_assignments: Mapped[list[SupportRoleAssignment]] = relationship(back_populates="workspace") channel_mappings: Mapped[list[WorkspaceChannelMapping]] = relationship( "WorkspaceChannelMapping", back_populates="workspace", ) class WorkspaceChannelMapping(Base): __tablename__ = "workspace_channel_mappings" __table_args__ = ( UniqueConstraint( "workspace_id", "abc_channel_id", name="uq_workspace_channel_mappings_workspace_channel", ), ) id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) abc_project_id: Mapped[int | None] = mapped_column(BigInteger, nullable=True) abc_channel_id: Mapped[str] = mapped_column(String(100)) abc_channel_key: Mapped[str | None] = mapped_column(String(100), nullable=True) form_template_version: Mapped[str | None] = mapped_column(String(30), nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, server_default=text("1")) created_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), ) workspace: Mapped[Workspace] = relationship( "Workspace", back_populates="channel_mappings", ) class UserWorkspaceAccess(Base): __tablename__ = "user_workspace_access" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) user_id: Mapped[str] = mapped_column(String(100)) tenant_id: Mapped[str] = mapped_column(String(100)) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) workspace_role: Mapped[str] = mapped_column(String(30), server_default=text("'END_USER'")) can_read: Mapped[bool] = mapped_column(Boolean, server_default=text("1")) can_write: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) can_manage: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) can_approve: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) page_scope: Mapped[str | None] = mapped_column(String(50), nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) workspace: Mapped[Workspace] = relationship(back_populates="access_entries") class SupportTicket(Base): __tablename__ = "support_tickets" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) requester_id: Mapped[str] = mapped_column(String(100)) requester_tenant_id: Mapped[str] = mapped_column(String(100)) requester_contact: Mapped[str | None] = mapped_column(String(100), nullable=True) requester_email: Mapped[str | None] = mapped_column(String(320), nullable=True) requester_name: Mapped[str | None] = mapped_column(String(100), nullable=True) requester_department: Mapped[str | None] = mapped_column(String(100), nullable=True) requester_phone_number: Mapped[str | None] = mapped_column(String(50), nullable=True) ticket_type: Mapped[str] = mapped_column(String(30)) source_system: Mapped[str] = mapped_column(String(30), server_default=text("'ABC'")) title: Mapped[str] = mapped_column(String(255)) description: Mapped[str] = mapped_column(Text) category_code: Mapped[str | None] = mapped_column(String(20), nullable=True) status_code: Mapped[str] = mapped_column(String(20)) feedback_status: Mapped[str] = mapped_column( String(20), nullable=False, server_default=text("'NEW'"), ) approval_status: Mapped[str] = mapped_column(String(20), server_default=text("'NOT_REQUIRED'")) sync_status: Mapped[str] = mapped_column(String(20), server_default=text("'PENDING'")) issue_link_status: Mapped[str] = mapped_column(String(20), server_default=text("'NOT_REQUIRED'")) external_issue_status: Mapped[str | None] = mapped_column(String(30), nullable=True) is_secret: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) requires_approval: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) current_assignee_id: Mapped[str | None] = mapped_column(String(100), nullable=True) current_assignee_tenant_id: Mapped[str | None] = mapped_column(String(100), nullable=True) requested_start_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) requested_end_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) priority: Mapped[str] = mapped_column(String(20), server_default=text("'NORMAL'")) extra_fields: Mapped[dict[str, str] | None] = mapped_column(JSON, nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), server_onupdate=text("CURRENT_TIMESTAMP"), ) workspace: Mapped[Workspace] = relationship(back_populates="tickets") feedback_mapping: Mapped[ABCFeedbackMapping | None] = relationship(back_populates="ticket", uselist=False) approvals: Mapped[list[RequestApproval]] = relationship(back_populates="ticket") comments: Mapped[list[TicketComment]] = relationship(back_populates="ticket") attachments: Mapped[list[Attachment]] = relationship(back_populates="ticket") class ABCFeedbackMapping(Base): __tablename__ = "abc_feedback_mappings" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) ticket_id: Mapped[int] = mapped_column(ForeignKey("support_tickets.id"), unique=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) abc_channel_id: Mapped[str] = mapped_column(String(100)) abc_feedback_id: Mapped[str] = mapped_column(String(100), unique=True) abc_feedback_url: Mapped[str | None] = mapped_column(Text, nullable=True) sync_status: Mapped[str] = mapped_column(String(20), server_default=text("'SYNCED'")) last_synced_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) ticket: Mapped[SupportTicket] = relationship(back_populates="feedback_mapping") class RequestApproval(Base): __tablename__ = "request_approvals" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) ticket_id: Mapped[int] = mapped_column(ForeignKey("support_tickets.id")) workflow_policy_id: Mapped[int | None] = mapped_column(ForeignKey("workspace_approval_policies.id"), nullable=True) workflow_step_id: Mapped[int | None] = mapped_column(ForeignKey("workspace_approval_steps.id"), nullable=True) step_order: Mapped[int | None] = mapped_column(nullable=True) approver_id: Mapped[str] = mapped_column(String(100)) approver_tenant_id: Mapped[str] = mapped_column(String(100)) approval_status: Mapped[str] = mapped_column(String(20)) comment: Mapped[str | None] = mapped_column(Text, nullable=True) approved_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) ticket: Mapped[SupportTicket] = relationship(back_populates="approvals") workflow_policy: Mapped[WorkspaceApprovalPolicy | None] = relationship(back_populates="approval_history") workflow_step: Mapped[WorkspaceApprovalStep | None] = relationship(back_populates="approval_history") class WorkspaceApprovalPolicy(Base): __tablename__ = "workspace_approval_policies" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) request_category_code: Mapped[str | None] = mapped_column(String(50), nullable=True) ticket_type: Mapped[str | None] = mapped_column(String(30), nullable=True) rule_name: Mapped[str] = mapped_column(String(100)) description: Mapped[str | None] = mapped_column(Text, nullable=True) conditions: Mapped[dict[str, str] | None] = mapped_column(JSON, nullable=True) is_active: Mapped[bool] = mapped_column(Boolean, server_default=text("1")) version: Mapped[int] = mapped_column(server_default=text("1")) created_by: Mapped[str | None] = mapped_column(String(100), nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), server_onupdate=text("CURRENT_TIMESTAMP"), ) workspace: Mapped[Workspace] = relationship(back_populates="approval_policies") steps: Mapped[list[WorkspaceApprovalStep]] = relationship(back_populates="policy") approval_history: Mapped[list[RequestApproval]] = relationship(back_populates="workflow_policy") class WorkspaceApprovalStep(Base): __tablename__ = "workspace_approval_steps" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) policy_id: Mapped[int] = mapped_column(ForeignKey("workspace_approval_policies.id")) step_order: Mapped[int] = mapped_column() approver_type: Mapped[str] = mapped_column(String(30)) approver_key: Mapped[str] = mapped_column(String(100)) approver_name: Mapped[str | None] = mapped_column(String(100), nullable=True) approval_mode: Mapped[str] = mapped_column(String(20), server_default=text("'ALL'")) is_required: Mapped[bool] = mapped_column(Boolean, server_default=text("1")) sla_hours: Mapped[int | None] = mapped_column(nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) policy: Mapped[WorkspaceApprovalPolicy] = relationship(back_populates="steps") approval_history: Mapped[list[RequestApproval]] = relationship(back_populates="workflow_step") class TicketComment(Base): __tablename__ = "ticket_comments" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) ticket_id: Mapped[int] = mapped_column(ForeignKey("support_tickets.id")) parent_comment_id: Mapped[int | None] = mapped_column(ForeignKey("ticket_comments.id"), nullable=True) author_id: Mapped[str] = mapped_column(String(100)) author_tenant_id: Mapped[str] = mapped_column(String(100)) author_name: Mapped[str | None] = mapped_column(String(100), nullable=True) comment_type: Mapped[str] = mapped_column(String(20), server_default=text("'COMMENT'")) content: Mapped[str] = mapped_column(Text) is_internal: Mapped[bool] = mapped_column(Boolean, server_default=text("0")) abc_comment_id: Mapped[str | None] = mapped_column(String(100), nullable=True) sync_status: Mapped[str] = mapped_column(String(20), server_default=text("'PENDING'")) edited_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), server_onupdate=text("CURRENT_TIMESTAMP"), ) deleted_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) ticket: Mapped[SupportTicket] = relationship(back_populates="comments") parent_comment: Mapped[TicketComment | None] = relationship(remote_side=[id]) attachments: Mapped[list[Attachment]] = relationship(back_populates="comment") class Attachment(Base): __tablename__ = "attachments" id: Mapped[int] = mapped_column(BigInteger, primary_key=True, autoincrement=True) ticket_id: Mapped[int] = mapped_column(ForeignKey("support_tickets.id")) comment_id: Mapped[int | None] = mapped_column(ForeignKey("ticket_comments.id"), nullable=True) workspace_id: Mapped[int] = mapped_column(ForeignKey("workspaces.id")) uploader_id: Mapped[str] = mapped_column(String(100)) uploader_tenant_id: Mapped[str] = mapped_column(String(100)) original_file_name: Mapped[str] = mapped_column(String(255)) stored_file_name: Mapped[str | None] = mapped_column(String(255), nullable=True) storage_provider: Mapped[str] = mapped_column(String(30), server_default=text("'LOCAL'")) storage_bucket: Mapped[str | None] = mapped_column(String(255), nullable=True) storage_key: Mapped[str] = mapped_column(String(500)) mime_type: Mapped[str | None] = mapped_column(String(100), nullable=True) file_extension: Mapped[str | None] = mapped_column(String(20), nullable=True) file_size: Mapped[int | None] = mapped_column(BigInteger, nullable=True) checksum_sha256: Mapped[str | None] = mapped_column(String(64), nullable=True) abc_attachment_id: Mapped[str | None] = mapped_column(String(100), nullable=True) attachment_status: Mapped[str] = mapped_column(String(20), server_default=text("'ACTIVE'")) created_at: Mapped[datetime] = mapped_column(TIMESTAMP, server_default=text("CURRENT_TIMESTAMP")) updated_at: Mapped[datetime] = mapped_column( TIMESTAMP, server_default=text("CURRENT_TIMESTAMP"), server_onupdate=text("CURRENT_TIMESTAMP"), ) deleted_at: Mapped[datetime | None] = mapped_column(TIMESTAMP, nullable=True) ticket: Mapped[SupportTicket] = relationship(back_populates="attachments") comment: Mapped[TicketComment | None] = relationship(back_populates="attachments") workspace: Mapped[Workspace] = relationship(back_populates="attachments")