# /opt/docker/dev/service_finder/backend/app/models/audit.py import enum import uuid from datetime import datetime from typing import Any, Optional from sqlalchemy import String, DateTime, JSON, ForeignKey, text, Numeric, Boolean, BigInteger, Integer from sqlalchemy.orm import Mapped, mapped_column, relationship from sqlalchemy.sql import func from sqlalchemy.dialects.postgresql import UUID as PG_UUID, ENUM as PG_ENUM from app.database import Base class SecurityAuditLog(Base): """ Kiemelt biztonsági események és a 4-szem elv naplózása. """ __tablename__ = "security_audit_logs" __table_args__ = {"schema": "audit"} id: Mapped[int] = mapped_column(Integer, primary_key=True) action: Mapped[Optional[str]] = mapped_column(String(50)) # 'ROLE_CHANGE', 'MANUAL_CREDIT_ADJUST' actor_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) target_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) confirmed_by_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id"), nullable=True) is_critical: Mapped[bool] = mapped_column(Boolean, default=False) payload_before: Mapped[Any] = mapped_column(JSON) payload_after: Mapped[Any] = mapped_column(JSON) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class OperationalLog(Base): """ Felhasználói szintű napi üzemi események (Audit Trail). """ __tablename__ = "operational_logs" __table_args__ = {"schema": "audit"} id: Mapped[int] = mapped_column(Integer, primary_key=True, index=True) user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id", ondelete="SET NULL")) action: Mapped[str] = mapped_column(String(100), nullable=False) # pl. "ADD_VEHICLE" resource_type: Mapped[Optional[str]] = mapped_column(String(50)) resource_id: Mapped[Optional[str]] = mapped_column(String(100)) details: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb")) ip_address: Mapped[Optional[str]] = mapped_column(String(45)) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class ProcessLog(Base): """ Robotok és háttérfolyamatok futási naplója (A reggeli jelentésekhez). """ __tablename__ = "process_logs" __table_args__ = {"schema": "audit"} id: Mapped[int] = mapped_column(Integer, primary_key=True) process_name: Mapped[str] = mapped_column(String(100), index=True) # 'Master-Enricher' start_time: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) end_time: Mapped[Optional[datetime]] = mapped_column(DateTime(timezone=True)) items_processed: Mapped[int] = mapped_column(Integer, default=0) items_failed: Mapped[int] = mapped_column(Integer, default=0) details: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb")) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) class LedgerEntryType(str, enum.Enum): DEBIT = "DEBIT" CREDIT = "CREDIT" class WalletType(str, enum.Enum): EARNED = "EARNED" PURCHASED = "PURCHASED" SERVICE_COINS = "SERVICE_COINS" VOUCHER = "VOUCHER" class LedgerStatus(str, enum.Enum): PENDING = "PENDING" SUCCESS = "SUCCESS" FAILED = "FAILED" REFUNDED = "REFUNDED" REFUND = "REFUND" class FinancialLedger(Base): """ Minden pénz- és kreditmozgás központi naplója. Billing Engine alapja. """ __tablename__ = "financial_ledger" __table_args__ = {"schema": "audit"} id: Mapped[int] = mapped_column(Integer, primary_key=True) user_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) person_id: Mapped[Optional[int]] = mapped_column(BigInteger, ForeignKey("identity.persons.id")) amount: Mapped[float] = mapped_column(Numeric(18, 4), nullable=False) currency: Mapped[Optional[str]] = mapped_column(String(10)) transaction_type: Mapped[Optional[str]] = mapped_column(String(50)) related_agent_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("identity.users.id")) details: Mapped[Any] = mapped_column(JSON, server_default=text("'{}'::jsonb")) created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now()) # Új mezők double‑entry és okos levonáshoz entry_type: Mapped[LedgerEntryType] = mapped_column( PG_ENUM(LedgerEntryType, name="ledger_entry_type", schema="audit"), nullable=False ) balance_after: Mapped[Optional[float]] = mapped_column(Numeric(18, 4)) wallet_type: Mapped[Optional[WalletType]] = mapped_column( PG_ENUM(WalletType, name="wallet_type", schema="audit") ) # Economy 1: számlázási mezők issuer_id: Mapped[Optional[int]] = mapped_column(Integer, ForeignKey("finance.issuers.id"), nullable=True) invoice_status: Mapped[Optional[str]] = mapped_column(String(50), default="PENDING") tax_amount: Mapped[Optional[float]] = mapped_column(Numeric(18, 4)) gross_amount: Mapped[Optional[float]] = mapped_column(Numeric(18, 4)) net_amount: Mapped[Optional[float]] = mapped_column(Numeric(18, 4)) transaction_id: Mapped[uuid.UUID] = mapped_column( PG_UUID(as_uuid=True), default=uuid.uuid4, nullable=False, index=True ) status: Mapped[LedgerStatus] = mapped_column( PG_ENUM(LedgerStatus, name="ledger_status", schema="audit"), default=LedgerStatus.SUCCESS, nullable=False )