from datetime import datetime from sqlalchemy import ( Column, Integer, String, Boolean, Text, DateTime, BigInteger, SmallInteger, JSON, ForeignKey, CheckConstraint, text as sa_text ) from sqlalchemy.orm import relationship, declarative_base Base = declarative_base() class BotUser(Base): __tablename__ = "bot_users" id = Column(Integer, primary_key=True, autoincrement=True) max_user_id = Column(BigInteger, unique=True, nullable=False) first_name = Column(String(255), default="") last_name = Column(String(255), default="") username = Column(String(255), default="") phone = Column(String(50), default="") email = Column(String(255), default="") consent_given = Column(Boolean, nullable=False, default=False) consent_timestamp = Column(DateTime, nullable=True) consent_method = Column(String(50), default="") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) last_active_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) conversations = relationship("BotConversation", back_populates="user") consent_logs = relationship("BotConsentLog", back_populates="user") notification_subscriptions = relationship("BotNotificationSubscription", back_populates="user", uselist=False) class BotConversation(Base): __tablename__ = "bot_conversations" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("bot_users.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), nullable=False, default="open") inquiry_type = Column(Integer, ForeignKey("bot_categories.id", ondelete="SET NULL"), nullable=True) inquiry_text = Column(Text, default="") priority = Column(String(20), default="normal") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) closed_at = Column(DateTime, nullable=True) assigned_to = Column(String(255), default="") has_attachment = Column(Boolean, default=False) attachment_type = Column(String(50), default="") attachment_path = Column(String(500), default="") __table_args__ = ( CheckConstraint("status IN ('open','closed','handoff','1c_sent','awaiting_rating')", name="ck_bot_conv_status"), CheckConstraint("priority IN ('low','normal','high','urgent')", name="ck_bot_conv_priority"), ) user = relationship("BotUser", back_populates="conversations") category = relationship("BotCategory", back_populates="conversations") messages = relationship("BotMessage", back_populates="conversation", order_by="BotMessage.timestamp") sync_log = relationship("Bot1CSyncLog", back_populates="conversation") class BotMessage(Base): __tablename__ = "bot_messages" id = Column(Integer, primary_key=True, autoincrement=True) conversation_id = Column(Integer, ForeignKey("bot_conversations.id", ondelete="CASCADE"), nullable=False) direction = Column(String(3), nullable=False) text = Column(Text, default="") has_attachment = Column(Boolean, default=False) attachment_type = Column(String(50), default="") attachment_path = Column(String(500), default="") max_message_id = Column(BigInteger, nullable=True) timestamp = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) __table_args__ = ( CheckConstraint("direction IN ('in','out')", name="ck_bot_msg_direction"), ) conversation = relationship("BotConversation", back_populates="messages") class BotConsentLog(Base): __tablename__ = "bot_consent_logs" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("bot_users.id", ondelete="CASCADE"), nullable=False) action = Column(String(20), nullable=False) timestamp = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) method = Column(String(50), default="") ip_address = Column(String(45), default="") __table_args__ = ( CheckConstraint("action IN ('given','revoked')", name="ck_bot_consent_action"), ) user = relationship("BotUser", back_populates="consent_logs") class Bot1CSyncLog(Base): __tablename__ = "bot_1c_sync_log" id = Column(Integer, primary_key=True, autoincrement=True) conversation_id = Column(Integer, ForeignKey("bot_conversations.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), nullable=False, default="pending") request_payload = Column(Text, default="") response_payload = Column(Text, default="") sent_at = Column(DateTime, nullable=True) confirmed_at = Column(DateTime, nullable=True) __table_args__ = ( CheckConstraint("status IN ('pending','sent','confirmed','error')", name="ck_bot_1c_status"), ) conversation = relationship("BotConversation", back_populates="sync_log") class BotKnowledgeBase(Base): __tablename__ = "bot_knowledge_base" id = Column(Integer, primary_key=True, autoincrement=True) question = Column(String(500), nullable=False) answer = Column(Text, nullable=False) category = Column(String(100), default="") keywords = Column(JSON, default=list) active = Column(Boolean, nullable=False, default=True) sort_order = Column(Integer, default=0) class BotCategory(Base): __tablename__ = "bot_categories" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(100), nullable=False) description = Column(String(500), default="") active = Column(Boolean, nullable=False, default=True) sort_order = Column(Integer, default=0) icon_emoji = Column(String(10), default="📋") keywords = Column(JSON, default=list) conversations = relationship("BotConversation", back_populates="category") class BotSetting(Base): __tablename__ = "bot_settings" id = Column(Integer, primary_key=True, autoincrement=True) key = Column(String(100), unique=True, nullable=False) value = Column(Text, default="") description = Column(String(500), default="") class BotFeature(Base): __tablename__ = "bot_features" id = Column(Integer, primary_key=True, autoincrement=True) feature_key = Column(String(100), unique=True, nullable=False) name = Column(String(100), nullable=False) description = Column(String(500), default="") enabled = Column(Boolean, nullable=False, default=False) sort_order = Column(Integer, default=0) class BotResponseTemplate(Base): __tablename__ = "bot_response_templates" id = Column(Integer, primary_key=True, autoincrement=True) template_key = Column(String(100), unique=True, nullable=False) template_text = Column(Text, nullable=False) description = Column(String(500), default="") variables = Column(JSON, default=list) class FileSyncQueue(Base): __tablename__ = "file_sync_queue" id = Column(Integer, primary_key=True, autoincrement=True) local_path = Column(String(500), nullable=False) remote_path = Column(String(500), default="") entity_type = Column(String(50), nullable=False) entity_id = Column(Integer, nullable=False) status = Column(String(20), nullable=False, default="pending") error = Column(Text, default="") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) __table_args__ = ( CheckConstraint("status IN ('pending','uploading','synced','error')", name="ck_file_sync_status"), ) class BotBroadcast(Base): __tablename__ = "bot_broadcasts" id = Column(Integer, primary_key=True, autoincrement=True) text = Column(Text, nullable=False) sent_at = Column(DateTime, nullable=True) sent_by = Column(Integer, nullable=True) recipient_count = Column(Integer, default=0) recipient_ids = Column(JSON, default=list) status = Column(String(20), default="draft") __table_args__ = ( CheckConstraint("status IN ('draft','sent','failed')", name="ck_bot_broadcast_status"), ) class BotNotification(Base): __tablename__ = "bot_notifications" id = Column(Integer, primary_key=True, autoincrement=True) type = Column(String(50), nullable=False) recipient_type = Column(String(20), nullable=False) recipient_max_user_id = Column(BigInteger, nullable=True) title = Column(String(255), default="") body = Column(Text, default="") related_type = Column(String(50), default="") related_id = Column(Integer, nullable=True) is_read = Column(Boolean, nullable=False, default=False) created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) __table_args__ = ( CheckConstraint("recipient_type IN ('operator','level2','client')", name="ck_bot_notif_recipient"), ) class BotNotificationSubscription(Base): __tablename__ = "bot_notification_subscriptions" id = Column(Integer, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("bot_users.id", ondelete="CASCADE"), nullable=False) notify_on_status_change = Column(Boolean, nullable=False, default=False) notify_on_reply = Column(Boolean, nullable=False, default=False) notify_on_broadcast = Column(Boolean, nullable=False, default=False) user = relationship("BotUser", back_populates="notification_subscriptions") class Customer(Base): __tablename__ = "customers" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(255), nullable=False) contact_phone = Column(String(50), default="") class Object(Base): __tablename__ = "objects" id = Column(Integer, primary_key=True, autoincrement=True) customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL"), nullable=True) name = Column(String(255), nullable=False) status = Column(String(20), nullable=False, default="active") risk_score = Column(Integer, default=0) class SLAContract(Base): __tablename__ = "sla_contracts" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), nullable=False, default="negotiation") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) service_level = Column(String(20), default="start") sla_price_monthly = Column(Integer, default=0) class Task(Base): __tablename__ = "tasks" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), nullable=False, default="open") class Incident(Base): __tablename__ = "incidents" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) status = Column(String(20), nullable=False, default="open") created_at = Column(DateTime, nullable=False, server_default="NOW()") title = Column(String(500), default="") description = Column(Text, default="") severity = Column(String(4), nullable=False, default="P3") reported_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"), nullable=True) class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) login = Column(String(50), default="") full_name = Column(String(255), default="") role = Column(String(20), nullable=False, default="technician") is_active = Column(Boolean, nullable=False, default=True) max_user_id = Column(BigInteger, nullable=True) class BotRiskQuestion(Base): __tablename__ = "bot_risk_questions" id = Column(Integer, primary_key=True, autoincrement=True) question_key = Column(String(100), unique=True, nullable=False) question = Column(String(500), nullable=False) weight = Column(Integer, nullable=False, default=10) sort_order = Column(Integer, default=0) is_active = Column(Boolean, nullable=False, default=True) class BotTicket(Base): __tablename__ = "bot_tickets" id = Column(Integer, primary_key=True, autoincrement=True) ticket_number = Column(String(20), unique=True, nullable=False) user_id = Column(Integer, ForeignKey("bot_users.id", ondelete="SET NULL"), nullable=True) object_id = Column(Integer, nullable=True) customer_id = Column(Integer, nullable=True) title = Column(String(255), nullable=False) description = Column(Text, default="") priority = Column(String(20), default="normal") status = Column(String(20), nullable=False, default="open") created_by = Column(String(20), default="bot") sla_contract_id = Column(Integer, nullable=True) has_attachment = Column(Boolean, default=False) attachment_type = Column(String(50), default="") attachment_path = Column(String(500), default="") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) updated_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) __table_args__ = ( CheckConstraint("priority IN ('low','normal','high','urgent')", name="ck_bot_ticket_priority"), CheckConstraint("status IN ('open','in_progress','resolved','closed')", name="ck_bot_ticket_status"), ) class BotTicketMessage(Base): __tablename__ = "bot_ticket_messages" id = Column(Integer, primary_key=True, autoincrement=True) ticket_id = Column(Integer, ForeignKey("bot_tickets.id", ondelete="CASCADE"), nullable=False) direction = Column(String(10), nullable=False) text = Column(Text, default="") sender = Column(String(50), default="") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP")) class BotTicketStatus(Base): __tablename__ = "bot_ticket_statuses" id = Column(Integer, primary_key=True, autoincrement=True) ticket_id = Column(Integer, ForeignKey("bot_tickets.id", ondelete="CASCADE"), nullable=False) old_status = Column(String(20), default="") new_status = Column(String(20), nullable=False) changed_by = Column(String(50), default="") created_at = Column(DateTime, nullable=False, server_default=sa_text("CURRENT_TIMESTAMP"))