first commit
This commit is contained in:
0
app/core/__init__.py
Normal file
0
app/core/__init__.py
Normal file
BIN
app/core/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/core/__pycache__/auth.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/auth.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/core/__pycache__/base.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/base.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/core/__pycache__/baseRepository.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/baseRepository.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/core/__pycache__/security.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/security.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/core/__pycache__/servo.cpython-311.pyc
Normal file
BIN
app/core/__pycache__/servo.cpython-311.pyc
Normal file
Binary file not shown.
184
app/core/affidavit.py
Normal file
184
app/core/affidavit.py
Normal 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
|
||||
160
app/core/auth.py
Normal file
160
app/core/auth.py
Normal file
@@ -0,0 +1,160 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional, List
|
||||
import os
|
||||
#========================
|
||||
import jwt
|
||||
from fastapi import Depends, HTTPException, status
|
||||
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
|
||||
|
||||
|
||||
security = HTTPBearer()
|
||||
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", "xma-secret-key-change-in-production")
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_HOURS = int(os.getenv("ACCESS_TOKEN_EXPIRE_HOURS", "1"))
|
||||
|
||||
|
||||
class UserType:
|
||||
""" User type with permisions"""
|
||||
|
||||
ROOT ="root"
|
||||
ADMIN ="admin"
|
||||
PRIVILEGED ="privileged"
|
||||
ADMIN_LICENCIAS ="admin_licencias"
|
||||
UPDATER = "updater"
|
||||
USER ="user"
|
||||
|
||||
|
||||
HIERARCHY = {
|
||||
ROOT :6,
|
||||
ADMIN :5,
|
||||
ADMIN_LICENCIAS : 4,
|
||||
PRIVILEGED : 3,
|
||||
UPDATER : 2,
|
||||
USER : 1,
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def has_permission(cls, user_type: str, required_type: str) -> bool:
|
||||
""" VERIFY if user_type has permition equal o plus required_type"""
|
||||
return cls.HIERARCHY.get(user_type, 0) >= cls.HIERARCHY.get(required_type, 0)
|
||||
|
||||
class OperationalRole:
|
||||
""" Role's"""
|
||||
COMPRAS ="compras"
|
||||
VENTAS ="ventas"
|
||||
LOGISTICA ="logistica"
|
||||
ADUANA_SOFT ="aduana_soft"
|
||||
CLIENTE ="cliente"
|
||||
OPERATIVO ="operativo"
|
||||
|
||||
class AuthService:
|
||||
""" Service auth"""
|
||||
|
||||
def __init__(self):
|
||||
self.secret_key = SECRET_KEY
|
||||
self.algorithm = ALGORITHM
|
||||
|
||||
|
||||
def create_token(self, user_id: int, email: str, user_type: str, rol_operativo: str) -> str:
|
||||
|
||||
expire = datetime.utcnow() + timedelta(hours=ACCESS_TOKEN_EXPIRE_HOURS)
|
||||
payload = {
|
||||
"sub": str(user_id),
|
||||
"email": email,
|
||||
"user_type": user_type,
|
||||
"rol_operativo": rol_operativo,
|
||||
"exp": expire,
|
||||
"iat": datetime.utcnow()
|
||||
}
|
||||
return jwt.encode(payload, self.secret_key, algorithm=self.algorithm)
|
||||
|
||||
def verify_token(self, token: str) -> dict:
|
||||
|
||||
try:
|
||||
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm])
|
||||
return payload
|
||||
except jwt.ExpiredSignatureError:
|
||||
raise HTTPException(status_code=401, detail="Token expirado")
|
||||
except jwt.InvalidTokenError:
|
||||
raise HTTPException(status_code=401, detail="Token inválido")
|
||||
|
||||
def decode_token(self, token: str) -> dict:
|
||||
|
||||
try:
|
||||
return jwt.decode(token, self.secret_key, algorithms=[self.algorithm], options={"verify_exp": False})
|
||||
except jwt.InvalidTokenError:
|
||||
raise HTTPException(status_code=401, detail="Token inválido")
|
||||
|
||||
class CurrentUser:
|
||||
"""Modelo del usuario autenticado"""
|
||||
|
||||
def __init__(self, id: int, email: str, user_type: str, rol_operativo: str):
|
||||
self.id = id
|
||||
self.email = email
|
||||
self.user_type = user_type
|
||||
self.rol_operativo = rol_operativo
|
||||
self.ip_address = None
|
||||
|
||||
def is_root(self) -> bool:
|
||||
return self.user_type == UserType.ROOT
|
||||
|
||||
def is_admin(self) -> bool:
|
||||
return self.user_type == UserType.ADMIN
|
||||
|
||||
def is_admin_licencias(self) -> bool:
|
||||
return self.user_type == UserType.ADMIN_LICENCIAS
|
||||
|
||||
def has_permission(self, required_type: str) -> bool:
|
||||
"""Verifica si el usuario tiene el tipo requerido o superior"""
|
||||
return UserType.has_permission(self.user_type, required_type)
|
||||
|
||||
def can_access_enterprise(self, enterprise_id: int, user_enterprise_id: int = None) -> bool:
|
||||
"""Verifica si puede acceder a una empresa"""
|
||||
if self.is_root():
|
||||
return True
|
||||
if user_enterprise_id and self.user_type == UserType.ADMIN:
|
||||
return enterprise_id == user_enterprise_id
|
||||
return False
|
||||
|
||||
|
||||
async def get_current_user(
|
||||
credentials: HTTPAuthorizationCredentials = Depends(security),
|
||||
auth_service: AuthService = Depends(lambda: AuthService())
|
||||
) -> CurrentUser:
|
||||
"""Dependencia para obtener el usuario actual desde el token"""
|
||||
token = credentials.credentials
|
||||
payload = auth_service.verify_token(token)
|
||||
|
||||
user = CurrentUser(
|
||||
id=int(payload.get("sub")),
|
||||
email=payload.get("email"),
|
||||
user_type=payload.get("user_type", UserType.USER),
|
||||
rol_operativo=payload.get("rol_operativo", OperationalRole.OPERATIVO)
|
||||
)
|
||||
|
||||
return user
|
||||
|
||||
|
||||
def require_user_type(required_type: str):
|
||||
"""Dependencia para requerir un tipo de usuario específico"""
|
||||
async def dependency(current_user: CurrentUser = Depends(get_current_user)):
|
||||
if not current_user.has_permission(required_type):
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"Se requiere tipo de usuario: {required_type}"
|
||||
)
|
||||
return current_user
|
||||
return dependency
|
||||
|
||||
|
||||
def require_rol(required_rol: str):
|
||||
"""Dependencia para requerir un rol operativo específico"""
|
||||
async def dependency(current_user: CurrentUser = Depends(get_current_user)):
|
||||
if current_user.rol_operativo != required_rol and not current_user.is_root():
|
||||
raise HTTPException(
|
||||
status_code=403,
|
||||
detail=f"Se requiere rol operativo: {required_rol}"
|
||||
)
|
||||
return current_user
|
||||
return dependency
|
||||
14
app/core/base.py
Normal file
14
app/core/base.py
Normal file
@@ -0,0 +1,14 @@
|
||||
"""UNIFICACION DE LOS MODELOS XMA
|
||||
Todos los modelos deben importarse para que alembic los detecte
|
||||
"""
|
||||
|
||||
from database import Base
|
||||
|
||||
# Export target_metadata for Alembic
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# Lista explicita de modelos
|
||||
__all__ = [
|
||||
"Base",
|
||||
"target_metadata",
|
||||
]
|
||||
224
app/core/baseRepository.py
Normal file
224
app/core/baseRepository.py
Normal file
@@ -0,0 +1,224 @@
|
||||
# core/base_repository.py
|
||||
from typing import Generic, TypeVar, Type, Optional, List, Dict, Any
|
||||
from datetime import datetime
|
||||
from sqlalchemy.orm import Session, Query
|
||||
from sqlalchemy import and_, desc
|
||||
|
||||
from database import Base
|
||||
|
||||
ModelType = TypeVar("ModelType", bound= Base)
|
||||
|
||||
|
||||
class BaseRepository(Generic[ModelType]):
|
||||
"""
|
||||
Repositorio base con:
|
||||
- CRUD completo
|
||||
- Soft delete integrado
|
||||
- Trazabilidad (created_by, updated_by, deleted_by)
|
||||
- Métodos para root, admin, admin_licencias
|
||||
"""
|
||||
|
||||
def __init__(self, db: Session, model: Type[ModelType]):
|
||||
self.db = db
|
||||
self.model = model
|
||||
|
||||
# ============================================
|
||||
# QUERY BASE
|
||||
# ============================================
|
||||
|
||||
def _base_query(self, include_deleted: bool = False) -> Query:
|
||||
"""
|
||||
Query base con filtro de soft delete
|
||||
include_deleted=True: incluye registros eliminados
|
||||
"""
|
||||
query = self.db.query(self.model)
|
||||
if not include_deleted:
|
||||
query = query.filter(
|
||||
self.model.deleted_at.is_(None),
|
||||
self.model.is_active == True
|
||||
)
|
||||
return query
|
||||
|
||||
# ============================================
|
||||
# CRUD BÁSICO
|
||||
# ============================================
|
||||
|
||||
def get_by_id(self, id: int, include_deleted: bool = False) -> Optional[ModelType]:
|
||||
"""Obtiene por ID"""
|
||||
return self._base_query(include_deleted).filter(self.model.id == id).first()
|
||||
|
||||
def get_all(
|
||||
self,
|
||||
skip: int = 0,
|
||||
limit: int = 100,
|
||||
filters: Dict[str, Any] = None,
|
||||
order_by: str = None,
|
||||
descending: bool = False,
|
||||
include_deleted: bool = False
|
||||
) -> List[ModelType]:
|
||||
"""Lista con paginación y filtros"""
|
||||
query = self._base_query(include_deleted)
|
||||
|
||||
# Aplicar filtros
|
||||
if filters:
|
||||
for key, value in filters.items():
|
||||
if hasattr(self.model, key) and value is not None:
|
||||
query = query.filter(getattr(self.model, key) == value)
|
||||
|
||||
# Ordenamiento
|
||||
if order_by and hasattr(self.model, order_by):
|
||||
order_col = getattr(self.model, order_by)
|
||||
query = query.order_by(desc(order_col) if descending else order_col)
|
||||
|
||||
return query.offset(skip).limit(limit).all()
|
||||
|
||||
def count(self, filters: Dict[str, Any] = None, include_deleted: bool = False) -> int:
|
||||
"""Cuenta registros"""
|
||||
query = self._base_query(include_deleted)
|
||||
if filters:
|
||||
for key, value in filters.items():
|
||||
if hasattr(self.model, key) and value is not None:
|
||||
query = query.filter(getattr(self.model, key) == value)
|
||||
return query.count()
|
||||
|
||||
def create(self, data: Dict[str, Any], created_by: int = None) -> ModelType:
|
||||
"""Crea un nuevo registro"""
|
||||
import logging
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
try:
|
||||
# Eliminar campos que deben ser automáticos
|
||||
data.pop("created_at", None)
|
||||
data.pop("updated_at", None)
|
||||
data.pop("deleted_at", None)
|
||||
data.pop("last_login", None)
|
||||
data.pop("id", None)
|
||||
|
||||
if created_by:
|
||||
data["created_by"] = created_by
|
||||
|
||||
logger.info(f"CREANDO ENTIDAD {self.model.__name__} CON DATOS: {data}")
|
||||
|
||||
entity = self.model(**data)
|
||||
self.db.add(entity)
|
||||
self.db.commit()
|
||||
self.db.refresh(entity)
|
||||
return entity
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"ERROR EN CREATE: {str(e)}")
|
||||
self.db.rollback()
|
||||
raise e
|
||||
|
||||
def update(
|
||||
self,
|
||||
id: int,
|
||||
data: Dict[str, Any],
|
||||
updated_by: int = None,
|
||||
include_deleted: bool = False
|
||||
) -> Optional[ModelType]:
|
||||
"""Actualiza un registro"""
|
||||
entity = self.get_by_id(id, include_deleted)
|
||||
if not entity:
|
||||
return None
|
||||
|
||||
# No permitir actualizar si está eliminado
|
||||
if entity.deleted_at is not None and not include_deleted:
|
||||
return None
|
||||
|
||||
# Actualizar campos
|
||||
for key, value in data.items():
|
||||
if hasattr(entity, key) and value is not None:
|
||||
setattr(entity, key, value)
|
||||
|
||||
if updated_by:
|
||||
entity.updated_by = updated_by
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(entity)
|
||||
return entity
|
||||
|
||||
def soft_delete(self, id: int, deleted_by: int = None) -> bool:
|
||||
"""Soft delete: marca is_active=False y deleted_at"""
|
||||
entity = self.get_by_id(id)
|
||||
if not entity:
|
||||
return False
|
||||
|
||||
if entity.deleted_at is not None:
|
||||
return False
|
||||
|
||||
entity.is_active = False
|
||||
entity.deleted_at = datetime.utcnow()
|
||||
if deleted_by:
|
||||
entity.deleted_by = deleted_by
|
||||
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def hard_delete(self, id: int) -> bool:
|
||||
"""Eliminación física"""
|
||||
entity = self.get_by_id(id, include_deleted=True)
|
||||
if not entity:
|
||||
return False
|
||||
|
||||
self.db.delete(entity)
|
||||
self.db.commit()
|
||||
return True
|
||||
|
||||
def restore(self, id: int, updated_by: int = None) -> Optional[ModelType]:
|
||||
"""Restaura un registro soft-deleted"""
|
||||
entity = self.get_by_id(id, include_deleted=True)
|
||||
if not entity:
|
||||
return None
|
||||
|
||||
if entity.deleted_at is None:
|
||||
return entity
|
||||
|
||||
entity.is_active = True
|
||||
entity.deleted_at = None
|
||||
if updated_by:
|
||||
entity.updated_by = updated_by
|
||||
|
||||
self.db.commit()
|
||||
self.db.refresh(entity)
|
||||
return entity
|
||||
|
||||
# ============================================
|
||||
# MÉTODOS PARA DIFERENCIACIÓN DE USUARIOS
|
||||
# ============================================
|
||||
# Estos métodos serán sobrescritos en repositorios específicos
|
||||
# si el modelo tiene los campos correspondientes
|
||||
|
||||
def get_by_user_type(self, user_type: str) -> List[ModelType]:
|
||||
"""Obtiene por tipo de usuario (root, admin, admin_licencias, user)"""
|
||||
if hasattr(self.model, "tipo_usuario"):
|
||||
return self._base_query().filter(self.model.tipo_usuario == user_type).all()
|
||||
return []
|
||||
|
||||
def get_root(self) -> List[ModelType]:
|
||||
"""Obtiene usuarios tipo root"""
|
||||
return self.get_by_user_type("root")
|
||||
|
||||
def get_admin(self) -> List[ModelType]:
|
||||
"""Obtiene usuarios tipo admin"""
|
||||
return self.get_by_user_type("admin")
|
||||
|
||||
def get_admin_licencias(self) -> List[ModelType]:
|
||||
"""Obtiene usuarios tipo admin_licencias"""
|
||||
return self.get_by_user_type("admin_licencias")
|
||||
|
||||
#===============================================
|
||||
# el tipo de usuario "PRIVILEGED" un esta por definir que clase de permisos tendras
|
||||
#=================================
|
||||
|
||||
def get_by_role(self, role: str) -> List[ModelType]:
|
||||
"""Obtiene por rol operativo"""
|
||||
if hasattr(self.model, "rol_operativo"):
|
||||
return self._base_query().filter(self.model.rol_operativo == role).all()
|
||||
return []
|
||||
|
||||
def get_by_enterprise(self, enterprise_id: int) -> List[ModelType]:
|
||||
"""Obtiene por empresa"""
|
||||
if hasattr(self.model, "enterprise_id"):
|
||||
return self._base_query().filter(self.model.enterprise_id == enterprise_id).all()
|
||||
return []
|
||||
0
app/core/dependencies.py
Normal file
0
app/core/dependencies.py
Normal file
0
app/core/intefaces.py
Normal file
0
app/core/intefaces.py
Normal file
64
app/core/security.py
Normal file
64
app/core/security.py
Normal file
@@ -0,0 +1,64 @@
|
||||
# core/security.py
|
||||
from passlib.context import CryptContext
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Optional
|
||||
#
|
||||
import os
|
||||
import jwt
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Configuración de hashing de contraseñas
|
||||
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
||||
|
||||
# Configuración de JWT
|
||||
SECRET_KEY = os.getenv("SECRET_KEY", "xma-secret-key-change-in-production")
|
||||
ALGORITHM = "HS256"
|
||||
ACCESS_TOKEN_EXPIRE_HOURS = int(os.getenv("ACCESS_TOKEN_EXPIRE_HOURS", "1"))
|
||||
|
||||
|
||||
def get_password_hash(password: str) -> str:
|
||||
"""Genera hash de contraseña con bcrypt"""
|
||||
|
||||
if password.startswith("$2b$") or password.startswith("$2a$"):
|
||||
raise ValueError("La contrasena ya esta hasheada")
|
||||
|
||||
password_bytes = password.encode('utf-8')
|
||||
if len(password_bytes) > 72:
|
||||
password = password[:72]
|
||||
|
||||
return pwd_context.hash(password)
|
||||
|
||||
|
||||
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
||||
try:
|
||||
return pwd_context.verify(plain_password, hashed_password)
|
||||
except Exception as e:
|
||||
logger.error(f"Error verifying password: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
|
||||
"""Crea token JWT"""
|
||||
to_encode = data.copy()
|
||||
if expires_delta:
|
||||
expire = datetime.utcnow() + expires_delta
|
||||
else:
|
||||
expire = datetime.utcnow() + timedelta(hours=ACCESS_TOKEN_EXPIRE_HOURS)
|
||||
|
||||
to_encode.update({"exp": expire, "iat": datetime.utcnow()})
|
||||
encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
|
||||
return encoded_jwt
|
||||
|
||||
|
||||
def decode_access_token(token: str) -> dict:
|
||||
"""Decodifica token JWT"""
|
||||
try:
|
||||
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
|
||||
return payload
|
||||
except jwt.ExpiredSignatureError:
|
||||
raise ValueError("Token expirado")
|
||||
except jwt.InvalidTokenError:
|
||||
raise ValueError("Token inválido")
|
||||
91
app/core/servo.py
Normal file
91
app/core/servo.py
Normal file
@@ -0,0 +1,91 @@
|
||||
# core/servo.py
|
||||
"""
|
||||
XMA Servomotor - Gestor simple del ciclo de vida
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
import signal
|
||||
import sys
|
||||
from enum import Enum
|
||||
from typing import Optional
|
||||
from datetime import datetime
|
||||
import logging
|
||||
|
||||
from database import test_connection, dispose_engine
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class ServoState(str, Enum):
|
||||
BOOT = "boot"
|
||||
RUN = "run"
|
||||
STOP = "stop"
|
||||
|
||||
|
||||
class XMAServo:
|
||||
"""
|
||||
Servomotor simple que:
|
||||
- Verifica BD al arrancar
|
||||
- Ejecuta tareas en segundo plano si es necesario
|
||||
- Maneja señales de apagado
|
||||
"""
|
||||
|
||||
def __init__(self, mode: str = "dev"):
|
||||
self.mode = mode
|
||||
self.state = ServoState.BOOT
|
||||
self.boot_time = datetime.utcnow()
|
||||
self._tasks = []
|
||||
self._running = False
|
||||
|
||||
async def start(self):
|
||||
"""Arranca el servomotor"""
|
||||
logger.info(f" XMA Servomotor iniciando (modo: {self.mode})")
|
||||
|
||||
# Verificar BD
|
||||
if not test_connection():
|
||||
logger.error(" Base de datos no disponible")
|
||||
sys.exit(1)
|
||||
|
||||
logger.info(" Base de datos conectada")
|
||||
|
||||
self.state = ServoState.RUN
|
||||
self._running = True
|
||||
logger.info(f" XMA Servomotor en estado RUN")
|
||||
|
||||
# Si necesitas tareas en segundo plano, las agregas aquí
|
||||
# await self._start_background_tasks()
|
||||
|
||||
async def stop(self):
|
||||
"""Detiene el servomotor"""
|
||||
logger.info(" Deteniendo servomotor...")
|
||||
self.state = ServoState.STOP
|
||||
self._running = False
|
||||
|
||||
# Cancelar tareas
|
||||
for task in self._tasks:
|
||||
task.cancel()
|
||||
|
||||
# Cerrar conexiones
|
||||
dispose_engine()
|
||||
|
||||
logger.info(" Servomotor detenido")
|
||||
|
||||
def get_status(self) -> dict:
|
||||
return {
|
||||
"state": self.state.value,
|
||||
"mode": self.mode,
|
||||
"uptime": (datetime.utcnow() - self.boot_time).total_seconds(),
|
||||
"database": "connected" if test_connection() else "disconnected"
|
||||
}
|
||||
|
||||
|
||||
# Instancia global
|
||||
_servo = None
|
||||
|
||||
def get_servo(mode: str = None) -> XMAServo:
|
||||
global _servo
|
||||
if _servo is None:
|
||||
import os
|
||||
mode = mode or os.getenv("XMA_MODE", "dev")
|
||||
_servo = XMAServo(mode=mode)
|
||||
return _servo
|
||||
52
app/modules/__init__.py
Normal file
52
app/modules/__init__.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
UNIFICACION DE LOS MODELOS XMA
|
||||
Todos los modelos deben importarse para que alembic los detecte
|
||||
"""
|
||||
|
||||
# Importar Base directamente desde database para evitar circular imports
|
||||
from database import Base
|
||||
|
||||
# Importar todos los modelos
|
||||
from app.modules.clients.models import Client
|
||||
from app.modules.users.models import Users
|
||||
from app.modules.affidavit_logs.models import AffidavitRecord
|
||||
from app.modules.branches.models import Branches
|
||||
from app.modules.coments.models import Coments
|
||||
from app.modules.configuration.models import Configuration
|
||||
from app.modules.credits.models import Credits
|
||||
from app.modules.edos.models import EDOS
|
||||
from app.modules.efos.models import EFOS
|
||||
from app.modules.feed.models import Feed
|
||||
from app.modules.files.models import Files
|
||||
from app.modules.interactions.models import Interacction
|
||||
from app.modules.invoices.models import Invoices
|
||||
from app.modules.license.models import License
|
||||
from app.modules.location.models import Locations
|
||||
from app.modules.moves.models import Moves
|
||||
from app.modules.suppliers.models import Suppliers
|
||||
|
||||
# Metadata para Alembic
|
||||
target_metadata = Base.metadata
|
||||
|
||||
# Lista explícita de modelos XMA
|
||||
__all__ = [
|
||||
"Base",
|
||||
"target_metadata",
|
||||
"Users",
|
||||
"Client",
|
||||
"Locations",
|
||||
"AffidavitRecord",
|
||||
"Branches",
|
||||
"Coments",
|
||||
"Configuration",
|
||||
"Credits",
|
||||
"EDOS",
|
||||
"EFOS",
|
||||
"Feed",
|
||||
"Interacction",
|
||||
"Files",
|
||||
"Invoices",
|
||||
"License",
|
||||
"Moves",
|
||||
"Suppliers",
|
||||
]
|
||||
BIN
app/modules/__pycache__/__init__.cpython-311.pyc
Normal file
BIN
app/modules/__pycache__/__init__.cpython-311.pyc
Normal file
Binary file not shown.
BIN
app/modules/affidavit_logs/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/affidavit_logs/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
58
app/modules/affidavit_logs/models.py
Normal file
58
app/modules/affidavit_logs/models.py
Normal file
@@ -0,0 +1,58 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum, Index
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class StatusOP(str, enum.Enum):
|
||||
COMPLETED = "completed"
|
||||
PARTIAL = "partial"
|
||||
INITIAL = "initial"
|
||||
NO_PROCECED = "no_proceced"
|
||||
INVALID = "invalid"
|
||||
|
||||
class Operation(str, enum.Enum):
|
||||
DELETED ="deleted"
|
||||
CREATED ="created"
|
||||
UPDATED ="updated"
|
||||
TRIED ="tried"
|
||||
MOST ="mosted"
|
||||
LESS ="less"
|
||||
MINUS ="minus"
|
||||
CANCELED ="canceled"
|
||||
|
||||
|
||||
class AffidavitRecord(Base):
|
||||
__tablename__ = "affidavit_logs"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
operation = Column(SQLEnum(Operation, name="operation", create_type=False), default=Operation.TRIED, nullable=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
client_id = Column(Integer, ForeignKey("clients.id"), nullable=True)
|
||||
description = Column(Text,nullable=False)
|
||||
adjustment = Column(String(100), nullable=True)
|
||||
status = Column(SQLEnum(StatusOP, name="status", create_type=False), default=StatusOP.INITIAL, nullable=True)
|
||||
sign = Column(String, nullable=False)
|
||||
|
||||
#timestamsp
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
#trace
|
||||
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
updated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
deleted_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
#relationships
|
||||
user = relationship("Users", foreign_keys=[user_id], back_populates="affidavit_logs")
|
||||
client = relationship("Client", foreign_keys=[client_id])
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_affidavit_user', 'user_id'),
|
||||
Index('idx_affidavit_client', 'client_id'),
|
||||
Index('idx_affidavit_operation', 'operation'),
|
||||
Index('idx_affidavit_status', 'status'),
|
||||
Index('idx_affidavit_created', 'created_at'),
|
||||
)
|
||||
0
app/modules/affidavit_logs/repository.py
Normal file
0
app/modules/affidavit_logs/repository.py
Normal file
0
app/modules/affidavit_logs/route.py
Normal file
0
app/modules/affidavit_logs/route.py
Normal file
0
app/modules/affidavit_logs/schema.py
Normal file
0
app/modules/affidavit_logs/schema.py
Normal file
BIN
app/modules/branches/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/branches/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
47
app/modules/branches/models.py
Normal file
47
app/modules/branches/models.py
Normal file
@@ -0,0 +1,47 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean,Index, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Branches(Base):
|
||||
__tablename__ = "branches"
|
||||
|
||||
id = Column(Integer,primary_key=True, nullable=False, autoincrement=True)
|
||||
direccion = Column(String(255), nullable=False)
|
||||
cp = Column(Integer, nullable=False)
|
||||
#physical
|
||||
is_physical = Column(Boolean, nullable=False)
|
||||
location_id = Column(Integer, ForeignKey("location.id"), nullable=False)
|
||||
#validation
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
#cliente_id
|
||||
client_id = Column(Integer, ForeignKey("clients.id"), nullable=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
#timestamsp
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
#trace
|
||||
created_by = Column(Integer, nullable=True)
|
||||
updated_by = Column(Integer, nullable=True)
|
||||
deleted_by = Column(Integer, nullable=True)
|
||||
|
||||
#indices
|
||||
__table_args__ = (
|
||||
Index('idx_branches_client', 'client_id'),
|
||||
Index('idx_branches_location', 'location_id'),
|
||||
Index('idx_branches_user', 'user_id'),
|
||||
Index('idx_branches_active', 'is_active')
|
||||
)
|
||||
|
||||
#relaciones
|
||||
|
||||
client = relationship("Client", foreign_keys=[client_id], back_populates="branches")
|
||||
location = relationship("Locations", foreign_keys=[location_id], back_populates="branches")
|
||||
user = relationship("Users", back_populates="branch")
|
||||
|
||||
0
app/modules/branches/repository.py
Normal file
0
app/modules/branches/repository.py
Normal file
0
app/modules/branches/route.py
Normal file
0
app/modules/branches/route.py
Normal file
0
app/modules/branches/schema.py
Normal file
0
app/modules/branches/schema.py
Normal file
BIN
app/modules/clients/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/clients/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
76
app/modules/clients/models.py
Normal file
76
app/modules/clients/models.py
Normal file
@@ -0,0 +1,76 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Float, Index, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class Medio(str, enum.Enum):
|
||||
MANUAL = "manual"
|
||||
DIOT = "diot"
|
||||
EXCEL = "excel"
|
||||
FACTURA = "factura"
|
||||
|
||||
class Tercero(str, enum.Enum):
|
||||
NACIONAL = "nacional"
|
||||
EXTRANJERO = "extranjero"
|
||||
GLOBAL = "global"
|
||||
|
||||
class Operacion(str, enum.Enum):
|
||||
PRESTACIONSERVICIOSPROFECIONALES = "prestacion"
|
||||
ARRENDAMIENTOSINMUENBLES = "arrendamientos"
|
||||
OTROS = "otros"
|
||||
|
||||
|
||||
|
||||
class Client(Base):
|
||||
__tablename__ = "clients"
|
||||
|
||||
#Identificacion
|
||||
id = Column(Integer, primary_key=True, index=True)
|
||||
rfc = Column(String(60), nullable=False, unique=True)
|
||||
email = Column(String(150), nullable=False, unique=True)
|
||||
|
||||
#Datos fiscales
|
||||
short_name = Column(String(255), nullable=True)
|
||||
razon_social = Column(String(255), nullable=False, index=True)
|
||||
fiscal_number = Column(String(15), nullable=False, unique=True)
|
||||
cellphone = Column(String(20), nullable=False)
|
||||
|
||||
|
||||
#clasififcacion
|
||||
medio = Column(SQLEnum(Medio, name="medio_enum", create_type=False), default=Medio.MANUAL, nullable=False)
|
||||
third_type = Column(SQLEnum(Tercero, name="tercero_enum", create_type=False), default=Tercero.GLOBAL, nullable=False)
|
||||
operation_type = Column(SQLEnum(Operacion, name="operacion_enum", create_type=False), default=Operacion.OTROS, nullable=False)
|
||||
is_foreign = Column(Boolean, nullable=True, default=False)
|
||||
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
location_id = Column(Integer, ForeignKey("location.id"), nullable=True)
|
||||
|
||||
#timestamps
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
|
||||
#trazabilidad
|
||||
created_by = Column(Integer, nullable=True)
|
||||
updated_by = Column(Integer, nullable=True)
|
||||
deleted_by = Column(Integer, nullable=True)
|
||||
|
||||
__table_args = (
|
||||
Index('idx_clients_user', 'user_id'),
|
||||
Index('idx_clients_location', 'location_id'),
|
||||
)
|
||||
|
||||
#relationships
|
||||
|
||||
user = relationship("Users", foreign_keys=[user_id], back_populates="clients")
|
||||
location = relationship("Locations", foreign_keys=[location_id])
|
||||
license = relationship("License", back_populates="client")
|
||||
branches = relationship("Branches", back_populates="client")
|
||||
|
||||
|
||||
def __repr__(self):
|
||||
return f"<Client(id={self.id}, rfc={self.rfc}, razon_social={self.razon_social})>"
|
||||
|
||||
|
||||
0
app/modules/clients/repository.py
Normal file
0
app/modules/clients/repository.py
Normal file
0
app/modules/clients/route.py
Normal file
0
app/modules/clients/route.py
Normal file
0
app/modules/clients/schema.py
Normal file
0
app/modules/clients/schema.py
Normal file
BIN
app/modules/coments/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/coments/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
37
app/modules/coments/models.py
Normal file
37
app/modules/coments/models.py
Normal file
@@ -0,0 +1,37 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Coments(Base):
|
||||
__tablename__= "coments"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
texto = Column(Text, nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
feed_id= Column( Integer, ForeignKey("feed.id"), nullable=True)
|
||||
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
created_at = Column( DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
deactivate_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
|
||||
created_by = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
deactivated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
#index
|
||||
__table_args__ = (
|
||||
Index('idx_coments_feed', 'feed_id'),
|
||||
Index('idx_coments_user', 'user_id'),
|
||||
Index('idx_coments_created', 'created_at'),
|
||||
Index('idx_coments_active', 'is_active')
|
||||
)
|
||||
|
||||
#Relaciones
|
||||
user = relationship("Users", foreign_keys=[user_id], back_populates="coments")
|
||||
feed = relationship("Feed", foreign_keys=[feed_id])
|
||||
creator = relationship("Users", foreign_keys=[created_by])
|
||||
|
||||
0
app/modules/coments/repository.py
Normal file
0
app/modules/coments/repository.py
Normal file
0
app/modules/coments/route.py
Normal file
0
app/modules/coments/route.py
Normal file
0
app/modules/coments/schema.py
Normal file
0
app/modules/coments/schema.py
Normal file
0
app/modules/coments/services.py
Normal file
0
app/modules/coments/services.py
Normal file
BIN
app/modules/configuration/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/configuration/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
34
app/modules/configuration/models.py
Normal file
34
app/modules/configuration/models.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Float, Boolean, Index, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Configuration(Base):
|
||||
__tablename__ = "configuration"
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||
|
||||
#SMTP data
|
||||
smtp_host = Column(String(100), nullable=True)
|
||||
smtp_port = Column(Integer, nullable=True)
|
||||
smtp_user = Column(String(100), nullable=True)
|
||||
smtp_password = Column(String(100), nullable=True)
|
||||
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime(timezone=True), nullable=True)
|
||||
last_send = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
|
||||
created_by = Column(Integer, nullable=False)
|
||||
updtated_by = Column(Integer, nullable=True)
|
||||
deleted_by = Column(Integer, nullable=True)
|
||||
|
||||
__table_args__ = (
|
||||
Index('idx_cofig_active', 'is_active'),
|
||||
)
|
||||
0
app/modules/configuration/repository.py
Normal file
0
app/modules/configuration/repository.py
Normal file
0
app/modules/configuration/route.py
Normal file
0
app/modules/configuration/route.py
Normal file
0
app/modules/configuration/schema.py
Normal file
0
app/modules/configuration/schema.py
Normal file
BIN
app/modules/credits/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/credits/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
50
app/modules/credits/models.py
Normal file
50
app/modules/credits/models.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
# Articulo 69 de codigo fiscal de la federacion!!!
|
||||
# 69 article of fiscal code of federation, mexican united states.
|
||||
|
||||
class Supuestos(str, enum.Enum):
|
||||
CANCELADOS ="cancelados"
|
||||
CONDONADOS ="condonados"
|
||||
FIRMES ="firmes"
|
||||
SENTENCIAS ="sentencias"
|
||||
EXIGIBLES ="exigibles"
|
||||
RETORNO_INVERSIONES ="retorno_inversiones"
|
||||
FRACCION_X ="fraccion_x"
|
||||
FRACCION_VII ="fraccion_vii"
|
||||
NO_LOCALIZADOS ="no_localizados"
|
||||
|
||||
|
||||
class Credits(Base):
|
||||
__tablename__="credits"
|
||||
|
||||
|
||||
id = Column(Integer, primary_key=True, autoincrement=True, nullable=False)
|
||||
title = Column(String(30), nullable=False)
|
||||
rfc = Column(String(25), nullable=False)
|
||||
razon_social = Column(String(100), nullable=False)
|
||||
tipo_persona = Column(String(100), nullable=False)
|
||||
supuesto = Column(SQLEnum(Supuestos, name="supuestos", create_type=False), nullable=False)
|
||||
fecha_primera_publicacion = Column(Date, nullable=False)
|
||||
fecha_publicacion_ley_transparencia = Column(Date, nullable=True)
|
||||
fecha_cancelacion = Column(Date, nullable=True)
|
||||
fecha_cancelacion_csd = Column(Date, nullable=True)
|
||||
entidad_federativa = Column(String, nullable=False)
|
||||
monto = Column(Integer, nullable=True)
|
||||
motivo = Column(Text, nullable=True)
|
||||
location_id = Column(Integer, ForeignKey("location.id"), nullable=False)
|
||||
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=True)
|
||||
|
||||
uploaded_by = Column(Integer, nullable=True)
|
||||
|
||||
# relationships
|
||||
location = relationship("Locations", back_populates="credits")
|
||||
|
||||
0
app/modules/credits/repository.py
Normal file
0
app/modules/credits/repository.py
Normal file
0
app/modules/credits/route.py
Normal file
0
app/modules/credits/route.py
Normal file
0
app/modules/credits/schema.py
Normal file
0
app/modules/credits/schema.py
Normal file
BIN
app/modules/diot/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/diot/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
0
app/modules/diot/models.py
Normal file
0
app/modules/diot/models.py
Normal file
0
app/modules/diot/repository.py
Normal file
0
app/modules/diot/repository.py
Normal file
0
app/modules/diot/route.py
Normal file
0
app/modules/diot/route.py
Normal file
0
app/modules/diot/schema.py
Normal file
0
app/modules/diot/schema.py
Normal file
BIN
app/modules/edos/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/edos/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
30
app/modules/edos/models.py
Normal file
30
app/modules/edos/models.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
#articulo 69-B-Bis del CFF
|
||||
# 69 article -B - Bis, of fiscal code of fedration of mexican united estates
|
||||
|
||||
class Situacion(str, enum.Enum):
|
||||
SENTENCIA_FAVORABLE ="sentencia_favorable"
|
||||
DEFINITIVO="definitivo"
|
||||
|
||||
class EDOS(Base):
|
||||
__tablename__="edos"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
numero = Column(Integer, nullable=False)
|
||||
razon_social = Column(String(100), nullable=False)
|
||||
situacion = Column(SQLEnum(Situacion, name="situcion", create_type=False), nullable=False)
|
||||
numero_definitivo = Column(String(60), nullable=False)
|
||||
fecha_definitivo = Column(Date, nullable=False )
|
||||
publicaccion_sat = Column(Date, nullable=True)
|
||||
numero_def_dof = Column(String(100), nullable=True)
|
||||
fecha_def_dof = Column(Date, nullable=True)
|
||||
publicacion_dof = Column(Date, nullable=True)
|
||||
numero_fav_sat = Column(String(100), nullable=True)
|
||||
|
||||
|
||||
0
app/modules/edos/repository.py
Normal file
0
app/modules/edos/repository.py
Normal file
0
app/modules/edos/route.py
Normal file
0
app/modules/edos/route.py
Normal file
0
app/modules/edos/schema.py
Normal file
0
app/modules/edos/schema.py
Normal file
BIN
app/modules/efos/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/efos/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
33
app/modules/efos/models.py
Normal file
33
app/modules/efos/models.py
Normal file
@@ -0,0 +1,33 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
#Articulo 69-B del codigo fiscal de la federacion
|
||||
#69-B article of fiscal code of federation of mexican united states
|
||||
|
||||
class Situacion(str, enum.Enum):
|
||||
DEFINITIVO ="definitivo"
|
||||
DESVIRTUADO ="desvituado"
|
||||
PRESUNTO ="presunto"
|
||||
SENTENCIA_FAVORABLE ="sentencia_favorable"
|
||||
|
||||
class EFOS(Base):
|
||||
__tablename__ = "efos"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
numero = Column(Integer, nullable=False)
|
||||
rfc = Column(String(30), nullable=False)
|
||||
nombre_contribuyente = Column(String(100), nullable=False)
|
||||
situacion = Column(String(60), SQLEnum(Situacion, name="situacion", create_type=False), nullable=False)
|
||||
publi_presuntos_sat = Column(Date, nullable=True)
|
||||
publi_desvirtuados_sat = Column(Date, nullable=True)
|
||||
publi_definitivos_sat = Column(Date, nullable=True)
|
||||
publi_favorables_sat = Column(Date, nullable=True)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=False)
|
||||
|
||||
loaded_by = Column(Integer, nullable=False)
|
||||
0
app/modules/efos/repository.py
Normal file
0
app/modules/efos/repository.py
Normal file
0
app/modules/efos/route.py
Normal file
0
app/modules/efos/route.py
Normal file
0
app/modules/efos/schema.py
Normal file
0
app/modules/efos/schema.py
Normal file
0
app/modules/entreprise/models.py
Normal file
0
app/modules/entreprise/models.py
Normal file
0
app/modules/entreprise/repository.py
Normal file
0
app/modules/entreprise/repository.py
Normal file
0
app/modules/entreprise/route.py
Normal file
0
app/modules/entreprise/route.py
Normal file
0
app/modules/entreprise/schema.py
Normal file
0
app/modules/entreprise/schema.py
Normal file
BIN
app/modules/feed/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/feed/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
26
app/modules/feed/models.py
Normal file
26
app/modules/feed/models.py
Normal file
@@ -0,0 +1,26 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class Feed(Base):
|
||||
__tablename__ = "feed"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
title = Column(String(50), nullable=False )
|
||||
body = Column(Text, nullable=True)
|
||||
document_url = Column(String, nullable=True)
|
||||
publication_date = Column(Date, nullable=True)
|
||||
is_important = Column(Boolean, default=False, nullable=False)
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
updated_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
file_id = Column(Integer, nullable=True)
|
||||
|
||||
#relationships
|
||||
user = relationship("Users", foreign_keys=[user_id], back_populates="feed")
|
||||
0
app/modules/feed/repository.py
Normal file
0
app/modules/feed/repository.py
Normal file
0
app/modules/feed/route.py
Normal file
0
app/modules/feed/route.py
Normal file
0
app/modules/feed/schema.py
Normal file
0
app/modules/feed/schema.py
Normal file
BIN
app/modules/files/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/files/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
24
app/modules/files/models.py
Normal file
24
app/modules/files/models.py
Normal file
@@ -0,0 +1,24 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class Files(Base):
|
||||
__tablename__ = "files"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
title = Column(String(50), nullable=True)
|
||||
summary = Column(Text, nullable=False)
|
||||
document_url = Column(String(100), nullable=False)
|
||||
file_type = Column(String(50), nullable=False)
|
||||
file_size = Column(Integer, nullable=False)
|
||||
|
||||
feed_id = Column(Integer, nullable=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=False )
|
||||
|
||||
uploaded_by = Column(Integer, nullable=False)
|
||||
updated_by = Column(Integer, nullable=True)
|
||||
0
app/modules/files/repository.py
Normal file
0
app/modules/files/repository.py
Normal file
0
app/modules/files/route.py
Normal file
0
app/modules/files/route.py
Normal file
0
app/modules/files/schema.py
Normal file
0
app/modules/files/schema.py
Normal file
BIN
app/modules/interactions/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/interactions/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
34
app/modules/interactions/models.py
Normal file
34
app/modules/interactions/models.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class Interaccion(str, enum.Enum):
|
||||
LIKE = "like"
|
||||
DONT_LIKE = "dont_like"
|
||||
APPROVED = "approved"
|
||||
DISAPRROVE = "disapproved"
|
||||
NONE = "none"
|
||||
|
||||
|
||||
class Interacction(Base):
|
||||
__tablename__="interacctions"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
feed_id = Column(Integer, ForeignKey("feed.id"), nullable=False)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
type_iteractions = Column(SQLEnum(Interaccion, name="type_interactios", create_type=False), default=Interaccion.NONE, nullable=False)
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
|
||||
#relationships
|
||||
user = relationship("Users", back_populates="interactions")
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
0
app/modules/interactions/repository.py
Normal file
0
app/modules/interactions/repository.py
Normal file
0
app/modules/interactions/route.py
Normal file
0
app/modules/interactions/route.py
Normal file
0
app/modules/interactions/schema.py
Normal file
0
app/modules/interactions/schema.py
Normal file
BIN
app/modules/invoices/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/invoices/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
23
app/modules/invoices/models.py
Normal file
23
app/modules/invoices/models.py
Normal file
@@ -0,0 +1,23 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
class Invoices(Base):
|
||||
__tablename__ = "invoices"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
emisor = Column(String(100), nullable=False)
|
||||
receptor = Column(String(110), nullable=False)
|
||||
uuid = Column(String(255), nullable=False)
|
||||
total = Column(Integer, nullable=False)
|
||||
tipo = Column(String(50), nullable=True)
|
||||
date = Column(DateTime, nullable=True)
|
||||
|
||||
verified_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
verified_by = Column(Integer, nullable=True)
|
||||
|
||||
|
||||
0
app/modules/invoices/repository.py
Normal file
0
app/modules/invoices/repository.py
Normal file
0
app/modules/invoices/route.py
Normal file
0
app/modules/invoices/route.py
Normal file
0
app/modules/invoices/schema.py
Normal file
0
app/modules/invoices/schema.py
Normal file
BIN
app/modules/license/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/license/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
45
app/modules/license/models.py
Normal file
45
app/modules/license/models.py
Normal file
@@ -0,0 +1,45 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
|
||||
|
||||
class License(Base):
|
||||
__tablename__ = "license"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False, autoincrement=True)
|
||||
titular = Column(Integer, ForeignKey("users.id"), nullable=False)
|
||||
|
||||
begins_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
ends_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
|
||||
token_license = Column(String(30), nullable=True)
|
||||
location_id = Column(Integer, ForeignKey("location.id"), nullable=False)
|
||||
client_id = Column(Integer, ForeignKey("clients.id"), nullable=False)
|
||||
|
||||
|
||||
|
||||
#timestamsp
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime(timezone=True), nullable=False)
|
||||
|
||||
#trace
|
||||
created_by = Column(Integer, nullable=True)
|
||||
updated_by = Column(Integer, nullable=True)
|
||||
deleted_by = Column(Integer, nullable=True)
|
||||
|
||||
__table_arg__ = (
|
||||
Index('idx_license_token', 'token_license'),
|
||||
Index('idx_license_client', 'client_id'),
|
||||
Index('idx_license_active', 'is_active'),
|
||||
Index('idx_license_ends_at', 'ends_at'),
|
||||
)
|
||||
|
||||
#Relations
|
||||
client = relationship("Client", foreign_keys=[client_id], back_populates="license")
|
||||
location = relationship("Locations", foreign_keys=[location_id], back_populates="licenses")
|
||||
user = relationship("Users", foreign_keys=[titular], back_populates="licenses")
|
||||
0
app/modules/license/repository.py
Normal file
0
app/modules/license/repository.py
Normal file
0
app/modules/license/route.py
Normal file
0
app/modules/license/route.py
Normal file
0
app/modules/license/schema.py
Normal file
0
app/modules/license/schema.py
Normal file
BIN
app/modules/location/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/location/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
50
app/modules/location/models.py
Normal file
50
app/modules/location/models.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from sqlalchemy import Column, Integer, String, DateTime, Index, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
from database import Base
|
||||
|
||||
import enum
|
||||
|
||||
class Locations(Base):
|
||||
__tablename__ = "location"
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
country = Column(String(120), nullable=False)
|
||||
country_id = Column(Integer, nullable=False)
|
||||
state = Column(String(100), nullable=False)
|
||||
state_id = Column(Integer, nullable=False)
|
||||
city = Column(String(100), nullable=False)
|
||||
city_id = Column(Integer, nullable=False)
|
||||
cp_zp = Column(Integer, nullable=True)
|
||||
street = Column(String(120), nullable=True)
|
||||
is_department = Column(Boolean, nullable=False, default=False)
|
||||
number_ext = Column(Integer, nullable=True)
|
||||
number_int = Column(Integer, nullable=True)
|
||||
|
||||
is_active = Column(Boolean, default=True, nullable=False)
|
||||
|
||||
#timestamsp
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_at = Column(DateTime, nullable=True)
|
||||
|
||||
#trace
|
||||
created_by = Column(Integer, nullable=True)
|
||||
updated_by = Column(Integer, nullable=True)
|
||||
deleted_by = Column(Integer, nullable=True)
|
||||
|
||||
#index
|
||||
__table_args__ = (
|
||||
Index('idx_location_country','country'),
|
||||
Index('idx_location_state','state'),
|
||||
Index('idx_location_city','city'),
|
||||
Index('idx_location_cp','cp_zp'),
|
||||
Index('idx_location_active','is_active'),
|
||||
)
|
||||
|
||||
#relationships
|
||||
|
||||
clients = relationship("Client",back_populates="location")
|
||||
branches = relationship("Branches",back_populates="location")
|
||||
credits = relationship("Credits",back_populates="location")
|
||||
licenses = relationship("License",back_populates="location")
|
||||
0
app/modules/location/repository.py
Normal file
0
app/modules/location/repository.py
Normal file
0
app/modules/location/route.py
Normal file
0
app/modules/location/route.py
Normal file
0
app/modules/location/schema.py
Normal file
0
app/modules/location/schema.py
Normal file
0
app/modules/location/service.py
Normal file
0
app/modules/location/service.py
Normal file
BIN
app/modules/moves/__pycache__/models.cpython-311.pyc
Normal file
BIN
app/modules/moves/__pycache__/models.cpython-311.pyc
Normal file
Binary file not shown.
71
app/modules/moves/models.py
Normal file
71
app/modules/moves/models.py
Normal file
@@ -0,0 +1,71 @@
|
||||
from sqlalchemy import Column, Integer, String, Date, Index, DateTime, Float, Boolean, Text, ForeignKey, Enum as SQLEnum
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.orm import relationship
|
||||
|
||||
from database import Base
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
|
||||
import enum
|
||||
|
||||
class Action(str, enum.Enum):
|
||||
UPLOAD = "upload"
|
||||
MATCH = "match"
|
||||
QUERY = "query"
|
||||
CONSUMPTION = "consumption"
|
||||
CREATE = "create"
|
||||
DELETE = "delete"
|
||||
SOLD = "sold"
|
||||
INTERACTION = "interaction"
|
||||
COMMENT = "comment"
|
||||
CONFIG = "config"
|
||||
EMAIL = "email"
|
||||
|
||||
class Target(str, enum.Enum):
|
||||
CLIENT = "client"
|
||||
INVOICES = "invoices"
|
||||
FEED = "feed"
|
||||
EFOS = "efos"
|
||||
CREDITS = "credits"
|
||||
USERS = "users"
|
||||
EMAIL = "email"
|
||||
|
||||
|
||||
class Moves(Base):
|
||||
__tablename__ = "moves"
|
||||
|
||||
id = Column(Integer, primary_key=True, nullable=False)
|
||||
|
||||
action_type = Column(SQLEnum(Action, name="action_type", create_type=False), nullable=False)
|
||||
target_type = Column(SQLEnum(Target, name="target_type", create_type=False), nullable=False)
|
||||
|
||||
description = Column(Text, nullable=True)
|
||||
move_metadata = Column(JSONB, nullable=True)
|
||||
|
||||
ip_address = Column(String(45), nullable=True)
|
||||
user_agent = Column(String(255), nullable=True)
|
||||
|
||||
client_id = Column(Integer, ForeignKey("clients.id"), nullable=True)
|
||||
user_id = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
is_active = Column(Boolean, nullable=False, default=True)
|
||||
|
||||
#relationships
|
||||
user = relationship("Users", foreign_keys=[user_id], back_populates="moves")
|
||||
|
||||
created_at = Column(DateTime(timezone=True), server_default=func.now(), nullable=False)
|
||||
created_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
updated_at = Column(DateTime(timezone=True), onupdate=func.now(), nullable=True)
|
||||
deleted_by = Column(Integer, ForeignKey("users.id"), nullable=True)
|
||||
|
||||
__table_args_ = (
|
||||
Index('idx_moves_client','client_id'),
|
||||
Index('idx_moves_user','user_id'),
|
||||
Index('idx_moves_action','action_type'),
|
||||
Index('idx_moves_target','target_type'),
|
||||
Index('idx_moves_created','created_at'),
|
||||
Index('idx_moves_active','is_active'),
|
||||
)
|
||||
|
||||
#Relaciones
|
||||
client = relationship("Client", foreign_keys=[client_id])
|
||||
creator = relationship("Users", foreign_keys=[created_by])
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user