from datetime import datetime from sqlalchemy import ( Column, Integer, String, Boolean, Text, DateTime, Numeric, Date, BigInteger, SmallInteger, JSON, ForeignKey, CheckConstraint ) from sqlalchemy.orm import relationship, declarative_base Base = declarative_base() class User(Base): __tablename__ = "users" id = Column(Integer, primary_key=True, autoincrement=True) login = Column(String(50), unique=True, nullable=False) password_hash = Column(String(255), nullable=False) role = Column(String(20), nullable=False, default="technician") full_name = Column(String(255), nullable=False) phone = Column(String(50), default="") email = Column(String(255), default="") is_active = Column(Boolean, nullable=False, default=True) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("role IN ('owner','engineer','technician')", name="ck_users_role"), ) objects_created = relationship("Object", foreign_keys="Object.created_by", back_populates="creator") tasks_created = relationship("Task", foreign_keys="Task.created_by", back_populates="creator") tasks_assigned = relationship("Task", foreign_keys="Task.assigned_to", back_populates="assignee") class LoginAttempt(Base): __tablename__ = "login_attempts" id = Column(Integer, primary_key=True, autoincrement=True) ip_address = Column(String(45), nullable=False) login = Column(String(50), nullable=False, default="") attempt_time = Column(DateTime, nullable=False, server_default="NOW()") class AuditLog(Base): __tablename__ = "audit_log" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) action = Column(String(255), nullable=False) details = Column(Text) ip_address = Column(String(45), default="") created_at = Column(DateTime, nullable=False, server_default="NOW()") class Customer(Base): __tablename__ = "customers" id = Column(Integer, primary_key=True, autoincrement=True) name = Column(String(255), nullable=False) inn = Column(String(12), default="") kpp = Column(String(9), default="") legal_address = Column(String(500), default="") contact_person = Column(String(255), default="") contact_phone = Column(String(50), default="") contact_email = Column(String(255), default="") status = Column(String(20), nullable=False, default="active") notes = Column(Text) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("status IN ('active','inactive','prospect')", name="ck_customers_status"), ) objects = relationship("Object", back_populates="customer") sla_contracts = relationship("SLAContract", back_populates="customer") class Object(Base): __tablename__ = "objects" id = Column(Integer, primary_key=True, autoincrement=True) customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL")) name = Column(String(255), nullable=False) address = Column(String(500), default="") object_type = Column(String(100), default="") area_sqm = Column(Numeric(10, 2), default=0) employees_count = Column(Integer, default=0) contact_person = Column(String(255), default="") contact_phone = Column(String(50), default="") status = Column(String(20), nullable=False, default="active") notes = Column(Text) risk_score = Column(Numeric(5, 2), default=0) complexity_index = Column(Numeric(5, 2), default=0) infrastructure_load = Column(Numeric(5, 2), default=0) service_history = Column(Numeric(5, 2), default=0) object_index = Column(Numeric(5, 2), default=0) sla_price_monthly = Column(Numeric(12, 2), default=0) region_factor = Column(Numeric(3, 2), default=1.00) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("status IN ('active','inactive','audit','prospective')", name="ck_objects_status"), ) customer = relationship("Customer", back_populates="objects") creator = relationship("User", foreign_keys=[created_by], back_populates="objects_created") assignments = relationship("ObjectAssignment", back_populates="object") tasks = relationship("Task", back_populates="object") reports = relationship("Report", back_populates="object") incidents = relationship("Incident", back_populates="object") sla_contracts = relationship("SLAContract", back_populates="object") passports = relationship("ObjectPassport", back_populates="object") class ObjectAssignment(Base): __tablename__ = "object_assignments" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) role = Column(String(20), nullable=False) assigned_at = Column(DateTime, nullable=False, server_default="NOW()") unassigned_at = Column(DateTime) __table_args__ = ( CheckConstraint("role IN ('engineer','technician')", name="ck_assignments_role"), ) object = relationship("Object", back_populates="assignments") user = relationship("User") class Task(Base): __tablename__ = "tasks" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) assigned_to = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) title = Column(String(500), nullable=False) description = Column(Text) priority = Column(String(4), nullable=False, default="P3") status = Column(String(20), nullable=False, default="open") deadline = Column(DateTime) sla_remaining_hours = Column(Numeric(6, 1), default=0) response_time_minutes = Column(Integer, default=0) resolution_time_minutes = Column(Integer, default=0) sla_compliant = Column(Boolean) checklist_data = Column(JSON) result_notes = Column(Text) closed_at = Column(DateTime) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("priority IN ('P1','P2','P3','P4')", name="ck_tasks_priority"), CheckConstraint("status IN ('open','in_progress','completed','cancelled')", name="ck_tasks_status"), ) object = relationship("Object", back_populates="tasks") creator = relationship("User", foreign_keys=[created_by], back_populates="tasks_created") assignee = relationship("User", foreign_keys=[assigned_to], back_populates="tasks_assigned") comments = relationship("TaskComment", back_populates="task") photos = relationship("TaskPhoto", back_populates="task") class TaskComment(Base): __tablename__ = "task_comments" id = Column(Integer, primary_key=True, autoincrement=True) task_id = Column(Integer, ForeignKey("tasks.id", ondelete="CASCADE"), nullable=False) user_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) comment = Column(Text, nullable=False) created_at = Column(DateTime, nullable=False, server_default="NOW()") task = relationship("Task", back_populates="comments") user = relationship("User") class TaskPhoto(Base): __tablename__ = "task_photos" id = Column(Integer, primary_key=True, autoincrement=True) task_id = Column(Integer, ForeignKey("tasks.id", ondelete="SET NULL")) report_id = Column(Integer, ForeignKey("reports.id", ondelete="SET NULL")) file_path = Column(String(500), nullable=False) description = Column(String(500), default="") type = Column(String(20), default="other") uploaded_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_at = Column(DateTime, nullable=False, server_default="NOW()") __table_args__ = ( CheckConstraint("type IN ('before','after','evidence','other')", name="ck_photos_type"), ) task = relationship("Task", back_populates="photos") uploader = relationship("User") class Report(Base): __tablename__ = "reports" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) report_type = Column(String(20), nullable=False, default="service") title = Column(String(500), default="") description = Column(Text) findings = Column(Text) recommendations = Column(Text) work_done = Column(Text) cause_classification = Column(String(100)) result_status = Column(String(20), default="ok") status = Column(String(20), nullable=False, default="draft") client_confirmed = Column(Boolean, default=False) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("report_type IN ('inspection','service','emergency','audit','monthly')", name="ck_reports_type"), CheckConstraint("result_status IN ('fixed','partial','revisit','ok')", name="ck_reports_result"), CheckConstraint("status IN ('draft','final','cancelled')", name="ck_reports_status"), ) object = relationship("Object", back_populates="reports") creator = relationship("User") class QuestionnaireSession(Base): __tablename__ = "questionnaire_sessions" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="SET NULL")) client_name = Column(String(255), default="") created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) status = Column(String(20), nullable=False, default="draft") current_step = Column(SmallInteger, nullable=False, default=1) risk_score = Column(Numeric(5, 2), default=0) complexity_index = Column(Numeric(5, 2), default=0) infrastructure_load = Column(Numeric(5, 2), default=0) service_history = Column(Numeric(5, 2), default=0) object_index = Column(Numeric(5, 2), default=0) sla_price_monthly = Column(Numeric(12, 2), default=0) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("status IN ('draft','completed','cancelled')", name="ck_qsessions_status"), ) answers = relationship("QuestionnaireAnswer", back_populates="session") passport = relationship("ObjectPassport", back_populates="session") class QuestionnaireAnswer(Base): __tablename__ = "questionnaire_answers" id = Column(Integer, primary_key=True, autoincrement=True) session_id = Column(Integer, ForeignKey("questionnaire_sessions.id", ondelete="CASCADE"), nullable=False) step = Column(SmallInteger, nullable=False) question_key = Column(String(100), nullable=False) answer_value = Column(Text) created_at = Column(DateTime, nullable=False, server_default="NOW()") session = relationship("QuestionnaireSession", back_populates="answers") class QuestionnaireItem(Base): __tablename__ = "questionnaire_items" id = Column(Integer, primary_key=True, autoincrement=True) step = Column(Integer, nullable=False, default=1) section = Column(String(100), nullable=False, default="") question_key = Column(String(100), unique=True, nullable=False) label = Column(String(255), nullable=False) type = Column(String(20), nullable=False, default="text") options = Column(JSON) required = Column(Boolean, nullable=False, default=False) sort_order = Column(Integer, nullable=False, default=0) is_active = Column(Boolean, nullable=False, default=True) help_text = Column(String(500), default="") created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("type IN ('text','number','select','radio','checkbox','textarea')", name="ck_qitems_type"), ) class ObjectPassport(Base): __tablename__ = "object_passports" id = Column(Integer, primary_key=True, autoincrement=True) session_id = Column(Integer, ForeignKey("questionnaire_sessions.id", ondelete="SET NULL")) object_id = Column(Integer, ForeignKey("objects.id", ondelete="SET NULL")) object_index = Column(Numeric(5, 2), default=0) risk_score = Column(Numeric(5, 2), default=0) complexity_index = Column(Numeric(5, 2), default=0) sla_price_monthly = Column(Numeric(12, 2), default=0) passport_data = Column(JSON) created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_at = Column(DateTime, nullable=False, server_default="NOW()") session = relationship("QuestionnaireSession", back_populates="passport") object = relationship("Object", back_populates="passports") creator = relationship("User") class SLAContract(Base): __tablename__ = "sla_contracts" id = Column(Integer, primary_key=True, autoincrement=True) customer_id = Column(Integer, ForeignKey("customers.id", ondelete="SET NULL")) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) contract_number = Column(String(50), default="") client_name = Column(String(255), default="") description = Column(Text) start_date = Column(Date, nullable=False) end_date = Column(Date) sla_price_monthly = Column(Numeric(12, 2), nullable=False, default=0) penalty_rate = Column(Numeric(5, 2), default=0) response_time_p1 = Column(Integer, default=4) response_time_p2 = Column(Integer, default=8) response_time_p3 = Column(Integer, default=24) response_time_hours = Column(Numeric(5, 1)) service_level = Column(String(20), nullable=False, default="business") status = Column(String(20), nullable=False, default="negotiation") created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("service_level IN ('start','business','enterprise')", name="ck_sla_level"), CheckConstraint("status IN ('active','expired','cancelled','negotiation')", name="ck_sla_status"), ) customer = relationship("Customer", back_populates="sla_contracts") object = relationship("Object", back_populates="sla_contracts") creator = relationship("User") class Incident(Base): __tablename__ = "incidents" id = Column(Integer, primary_key=True, autoincrement=True) object_id = Column(Integer, ForeignKey("objects.id", ondelete="CASCADE"), nullable=False) reported_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) assigned_to = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) title = Column(String(500), nullable=False) description = Column(Text) severity = Column(String(4), nullable=False, default="P3") status = Column(String(20), nullable=False, default="open") resolution = Column(Text) cause = Column(String(500), default="") resolution_time_minutes = Column(Integer, default=0) sla_breached = Column(Boolean, default=False) resolved_at = Column(DateTime) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("severity IN ('P1','P2','P3')", name="ck_incidents_severity"), CheckConstraint("status IN ('open','in_progress','resolved','closed')", name="ck_incidents_status"), ) object = relationship("Object", back_populates="incidents") reporter = relationship("User", foreign_keys=[reported_by]) assignee = relationship("User", foreign_keys=[assigned_to]) class EngineerKPI(Base): __tablename__ = "engineer_kpi" id = Column(Integer, primary_key=True, autoincrement=True) engineer_id = Column(Integer, ForeignKey("users.id", ondelete="CASCADE"), nullable=False) period_start = Column(Date, nullable=False) period_end = Column(Date, nullable=False) sla_compliance = Column(Numeric(5, 2), default=0) response_time_score = Column(Numeric(5, 2), default=0) resolution_time_score = Column(Numeric(5, 2), default=0) diagnosis_accuracy = Column(Numeric(5, 2), default=0) reopen_rate = Column(Numeric(5, 2), default=0) risk_coverage_score = Column(Numeric(5, 2), default=0) documentation_quality = Column(Numeric(5, 2), default=0) engineer_score = Column(Numeric(5, 2), default=0) grade = Column(String(20), default="") components_json = Column(JSON) created_at = Column(DateTime, nullable=False, server_default="NOW()") engineer = relationship("User") class SHSRecord(Base): __tablename__ = "shs_records" id = Column(Integer, primary_key=True, autoincrement=True) recorded_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) period_start = Column(Date, nullable=False) period_end = Column(Date, nullable=False) sla_stability = Column(Numeric(5, 2), default=0) revenue_stability = Column(Numeric(5, 2), default=0) retention = Column(Numeric(5, 2), default=0) engineer_performance = Column(Numeric(5, 2), default=0) incident_stability = Column(Numeric(5, 2), default=0) sales_flow = Column(Numeric(5, 2), default=0) operational_efficiency = Column(Numeric(5, 2), default=0) shs_score = Column(Numeric(5, 2), default=0) shs_status = Column(String(20), default="") components_json = Column(JSON) created_at = Column(DateTime, nullable=False, server_default="NOW()") recorder = relationship("User") class BlogPost(Base): __tablename__ = "blog_posts" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(500), nullable=False) slug = Column(String(500), unique=True, nullable=False) content = Column(Text, nullable=False) excerpt = Column(String(1000), default="") category = Column(String(50), nullable=False, default="audit") author_id = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) status = Column(String(20), nullable=False, default="draft") published_at = Column(DateTime) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") __table_args__ = ( CheckConstraint("category IN ('audit','sla','incident','supervision','documentation','risk','cases')", name="ck_blog_category"), CheckConstraint("status IN ('draft','published','archived')", name="ck_blog_status"), ) author = relationship("User") class FormulaCoefficient(Base): __tablename__ = "formula_coefficients" id = Column(Integer, primary_key=True, autoincrement=True) key = Column(String(100), unique=True, nullable=False) value = Column(Numeric(10, 4), nullable=False, default=0) description = Column(String(500), default="") formula_ref = Column(String(255), default="") is_active = Column(Boolean, nullable=False, default=True) updated_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL")) updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()") updater = relationship("User") class Case(Base): __tablename__ = "cases" id = Column(Integer, primary_key=True, autoincrement=True) title = Column(String(500), nullable=False) text = Column(Text, nullable=False) effect = Column(Text, nullable=False) sort_order = Column(Integer, nullable=False, default=0) is_active = Column(Boolean, nullable=False, default=True) created_at = Column(DateTime, nullable=False, server_default="NOW()") updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()")