first commit

This commit is contained in:
2026-04-01 13:48:40 -07:00
commit 61d386a7c7
178 changed files with 3827 additions and 0 deletions

184
app/core/affidavit.py Normal file
View File

@@ -0,0 +1,184 @@
# core/affidavit.py
from datetime import datetime
from typing import Any, Dict, Optional, List
import hashlib
import json
from sqlalchemy.orm import Session
from app.modules.affidavit_logs.models import Operation, StatusOP, AffidavitRecord
class LegalAffidavit:
"""
Certificador Legal Digital
Cada operación genera un acta notarial inmutable
"""
def __init__(self, db: Session, user, operation: str, client_id: int = None):
self.db = db
self.user = user
self.operation = operation
self.client_id = client_id
self.timestamp = datetime.utcnow()
self.entities_affected: List[Dict] = []
self.rules_validated: List[Dict] = []
self.evidence: List[Dict] = []
self._record = None
def _get_operation_enum(self) -> Operation:
"""Convierte string de operación a Enum Operation"""
operation_map = {
"create": Operation.CREATED,
"CREATE": Operation.CREATED,
"update": Operation.UPDATED,
"UPDATE": Operation.UPDATED,
"delete": Operation.DELETED,
"DELETE": Operation.DELETED,
"cancel": Operation.CANCELED,
"CANCEL": Operation.CANCELED,
"tried": Operation.TRIED,
"TRIED": Operation.TRIED,
}
return operation_map.get(self.operation.lower(), Operation.TRIED)
def _get_status_enum(self, success: bool) -> StatusOP:
"""Convierte resultado a StatusOP"""
if success:
return StatusOP.COMPLETED
return StatusOP.INVALID
def _hash_data(self, data: Any) -> str:
"""Genera hash para integridad de datos"""
data_str = json.dumps(data, sort_keys=True, default=str)
return hashlib.sha256(data_str.encode()).hexdigest()
def certify_entity(self, entity_type: str, entity_id: int, entity_data: Dict) -> 'LegalAffidavit':
"""Certifica que una entidad fue afectada"""
self.entities_affected.append({
"type": entity_type,
"id": entity_id,
"data_hash": self._hash_data(entity_data),
"timestamp": self.timestamp.isoformat()
})
return self
def validate_rule(self, rule_name: str, rule_result: bool, rule_details: str = "") -> 'LegalAffidavit':
"""Valida una regla de negocio"""
self.rules_validated.append({
"rule": rule_name,
"passed": rule_result,
"details": rule_details,
"validated_at": self.timestamp.isoformat()
})
if not rule_result:
raise AffidavitValidationError(f"Regla '{rule_name}' no cumplida: {rule_details}")
return self
def add_evidence(self, evidence_type: str, evidence_data: Any) -> 'LegalAffidavit':
"""Agrega evidencia digital"""
self.evidence.append({
"type": evidence_type,
"data": evidence_data,
"hash": self._hash_data(evidence_data),
"timestamp": self.timestamp.isoformat()
})
return self
def _generate_signature(self) -> str:
"""Genera firma digital del acta"""
data_to_sign = f"{self.user.id}{self.timestamp.isoformat()}{self.operation}{json.dumps(self.entities_affected, sort_keys=True)}"
return hashlib.sha512(data_to_sign.encode()).hexdigest()
def _build_description(self) -> str:
"""Construye la descripción del acta"""
entities = ", ".join([f"{e['type']}:{e['id']}" for e in self.entities_affected])
rules = ", ".join([r['rule'] for r in self.rules_validated if r['passed']])
desc = f"Operación {self.operation} por usuario {self.user.email}"
if entities:
desc += f" | Entidades: {entities}"
if rules:
desc += f" | Reglas: {rules}"
return desc
def sign(self, success: bool = True) -> Dict:
"""
Firma y guarda el acta notarial en la base de datos
"""
description = self._build_description()
signature = self._generate_signature()
status = self._get_status_enum(success)
operation_enum = self._get_operation_enum()
# Crear registro en BD
self._record = AffidavitRecord(
operation=operation_enum,
user_id=self.user.id,
client_id=self.client_id,
description=description,
adjustment=json.dumps(self.evidence) if self.evidence else None,
status=status,
sign=signature,
created_at=self.timestamp,
created_by=self.user.id
)
self.db.add(self._record)
self.db.commit()
self.db.refresh(self._record)
return {
"affidavit_id": self._record.id,
"operation": self.operation,
"user": {
"id": self.user.id,
"email": self.user.email,
"user_type": self.user.user_type,
"rol_operativo": self.user.rol_operativo
},
"timestamp": self.timestamp.isoformat(),
"description": description,
"status": status.value,
"signature": signature,
"entities_affected": self.entities_affected,
"rules_validated": self.rules_validated
}
def get_record_id(self) -> Optional[int]:
"""Retorna el ID del registro creado"""
return self._record.id if self._record else None
class AffidavitValidationError(Exception):
"""Error de validación notarial"""
pass
def certify_operation(operation_name: str):
"""Decorador para certificar operaciones automáticamente"""
def decorator(func):
async def wrapper(*args, **kwargs):
db = kwargs.get('db')
user = kwargs.get('current_user')
client_id = kwargs.get('client_id')
if not db and args and hasattr(args[0], 'db'):
db = args[0].db
if db and user:
affidavit = LegalAffidavit(db, user, operation_name, client_id)
kwargs['affidavit'] = affidavit
try:
result = await func(*args, **kwargs)
affidavit.sign(success=True)
return result
except Exception as e:
affidavit.sign(success=False)
raise
return await func(*args, **kwargs)
return wrapper
return decorator