""" SQLAlchemy models for Compliance Template Rules — profilbasierte Empfehlungs-Regeln mit Versionierung, Approval-Workflow und Tenant-Overrides. Tables: - compliance_template_rules: Regel-Hülle - compliance_template_rule_versions: Versionen mit Lifecycle - compliance_template_rule_approvals: Audit-Trail - compliance_tenant_rule_overrides: Pro-Tenant-Overrides globaler Regeln """ import uuid from datetime import datetime from sqlalchemy import ( Column, String, Text, SmallInteger, Integer, DateTime, Index, ForeignKey, ) from sqlalchemy.dialects.postgresql import UUID, JSONB from classroom_engine.database import Base class TemplateRuleDB(Base): """Regel-Hülle: 1 Regel = 1 Empfehlung für ein document_type.""" __tablename__ = 'compliance_template_rules' id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) rule_key = Column(String(150), nullable=False, unique=True) document_type = Column(String(100), nullable=False) title = Column(String(300), nullable=False) current_version_id = Column(UUID(as_uuid=True)) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) __table_args__ = ( Index('idx_template_rules_type', 'document_type'), ) def __repr__(self) -> str: return f"" class TemplateRuleVersionDB(Base): """Eine Version einer Regel mit Lifecycle (draft → review → approved → published).""" __tablename__ = 'compliance_template_rule_versions' id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) rule_id = Column( UUID(as_uuid=True), ForeignKey('compliance_template_rules.id', ondelete='CASCADE'), nullable=False, ) version_number = Column(Integer, nullable=False) status = Column(String(20), default='draft', nullable=False) is_live = Column(SmallInteger, default=0, nullable=False) classification = Column(String(20), nullable=False) conditions = Column(JSONB, nullable=False, default=dict) source_citation = Column(Text, nullable=False, default='') rationale = Column(Text) change_summary = Column(Text) created_by = Column(String(200)) submitted_by = Column(String(200)) submitted_at = Column(DateTime) approved_by = Column(String(200)) approved_at = Column(DateTime) published_by = Column(String(200)) published_at = Column(DateTime) rejected_by = Column(String(200)) rejected_at = Column(DateTime) rejection_reason = Column(Text) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) __table_args__ = ( Index('idx_rule_versions_rule', 'rule_id'), Index('idx_rule_versions_status', 'status'), ) def __repr__(self) -> str: return f"" class TemplateRuleApprovalDB(Base): """Audit-Trail aller Lifecycle-Aktionen auf einer Regel-Version.""" __tablename__ = 'compliance_template_rule_approvals' id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) version_id = Column( UUID(as_uuid=True), ForeignKey('compliance_template_rule_versions.id', ondelete='CASCADE'), nullable=False, ) action = Column(String(50), nullable=False) approver = Column(String(200)) comment = Column(Text) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) __table_args__ = ( Index('idx_rule_approvals_version', 'version_id'), ) def __repr__(self) -> str: return f"" class TenantRuleOverrideDB(Base): """Override einer globalen Regel pro Tenant/Kanzlei.""" __tablename__ = 'compliance_tenant_rule_overrides' id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4) tenant_id = Column(String(100), nullable=False) rule_id = Column( UUID(as_uuid=True), ForeignKey('compliance_template_rules.id', ondelete='CASCADE'), nullable=False, ) override_classification = Column(String(20)) # null = deaktiviert reason = Column(Text, nullable=False) created_by = Column(String(200)) created_at = Column(DateTime, default=datetime.utcnow, nullable=False) updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow) __table_args__ = ( Index('idx_tenant_overrides_tenant', 'tenant_id'), ) def __repr__(self) -> str: return f""