72b6879f4b
- Refactored max_bot from nested packages to flat module structure - Q2: Extended BotUser model (patronymic, email, org, address, vcf_raw, contact_hash, phone_verified, email_verified, last_interaction, total_conversations, total_tickets) - Q2: VCF parser (FN, N, TEL, EMAIL, ORG, ADR), upsert on re-contact, NLP history context (_get_user_history_context -> YandexGPT) - Q1: Broadcast preview modal with 10s confirmation timer - Q3: CSS var(--white)->var(--bg-card), var(--text)->var(--text-primary) - Q4: bot_settings showNotification(), editable max_bot_id - Q5: Webhook secret passthrough via X-Max-Bot-Api-Secret - Masking sensitive keys, dialog_cleared handler, migrate via _add_column_if_not_exists() - Rate limit (asyncio.sleep 0.5 per 10), dead code removed, conv.intent context in contact.py - Portal pages: bot_consent, bot_kb (edit), bot_settings, bot_test, bot_tickets, portal_settings - Tests: 21/21 passing, added test_yandex_gpt.py, test_email_sender.py - Deploy: deploy_full.sh, schema.sql, seed_knowledge_base.sql
515 lines
22 KiB
Python
515 lines
22 KiB
Python
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="")
|
|
max_user_id = Column(BigInteger, nullable=True)
|
|
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()")
|
|
|
|
|
|
class Idea(Base):
|
|
__tablename__ = "ideas"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
title = Column(String(500), nullable=False)
|
|
description = Column(Text, default="")
|
|
status = Column(String(20), nullable=False, default="normal")
|
|
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 ('normal','in_progress','completed')", name="ck_ideas_status"),
|
|
)
|
|
|
|
creator = relationship("User")
|
|
|
|
|
|
class QuickMenuItem(Base):
|
|
__tablename__ = "quick_menu_items"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
role = Column(String(20), nullable=False)
|
|
label = Column(String(100), nullable=False)
|
|
url = Column(String(500), nullable=False)
|
|
sort_order = Column(Integer, nullable=False, default=0)
|
|
created_by = Column(Integer, ForeignKey("users.id", ondelete="SET NULL"))
|
|
updated_at = Column(DateTime, nullable=False, server_default="NOW()", onupdate="NOW()")
|
|
|
|
creator = relationship("User")
|
|
|
|
|
|
class RoleMenuPermission(Base):
|
|
__tablename__ = "role_menu_permissions"
|
|
|
|
id = Column(Integer, primary_key=True, autoincrement=True)
|
|
role = Column(String(20), nullable=False)
|
|
menu_key = Column(String(50), nullable=False)
|
|
visible = Column(Boolean, nullable=False, default=True)
|
|
|
|
__table_args__ = (
|
|
CheckConstraint("role IN ('engineer','technician')", name="ck_role_menu_role"),
|
|
)
|
|
|
|
|
|
|