first commit
This commit is contained in:
4
.env
Normal file
4
.env
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# .env
|
||||||
|
|
||||||
|
|
||||||
|
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/postgres
|
||||||
0
.envexample
Normal file
0
.envexample
Normal file
0
.gitingnore
Normal file
0
.gitingnore
Normal file
0
Dockerfile
Normal file
0
Dockerfile
Normal file
16
Dockerfile.dev
Normal file
16
Dockerfile.dev
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
# Dockerfile.dev
|
||||||
|
FROM python:3.11-alpine
|
||||||
|
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
RUN apk add --no-cache \
|
||||||
|
gcc \
|
||||||
|
musl-dev \
|
||||||
|
postgresql-dev
|
||||||
|
|
||||||
|
COPY requirements.txt .
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
EXPOSE 8000
|
||||||
116
alembic.ini
Normal file
116
alembic.ini
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# A generic, single database configuration.
|
||||||
|
|
||||||
|
[alembic]
|
||||||
|
# path to migration scripts
|
||||||
|
script_location = migrations
|
||||||
|
|
||||||
|
# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s
|
||||||
|
# Uncomment the line below if you want the files to be prepended with date and time
|
||||||
|
# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file
|
||||||
|
# for all available tokens
|
||||||
|
# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s
|
||||||
|
|
||||||
|
# sys.path path, will be prepended to sys.path if present.
|
||||||
|
# defaults to the current working directory.
|
||||||
|
prepend_sys_path = .
|
||||||
|
|
||||||
|
# timezone to use when rendering the date within the migration file
|
||||||
|
# as well as the filename.
|
||||||
|
# If specified, requires the python-dateutil library that can be
|
||||||
|
# installed by adding `alembic[tz]` to the pip requirements
|
||||||
|
# string value is passed to dateutil.tz.gettz()
|
||||||
|
# leave blank for localtime
|
||||||
|
# timezone =
|
||||||
|
|
||||||
|
# max length of characters to apply to the
|
||||||
|
# "slug" field
|
||||||
|
# truncate_slug_length = 40
|
||||||
|
|
||||||
|
# set to 'true' to run the environment during
|
||||||
|
# the 'revision' command, regardless of autogenerate
|
||||||
|
# revision_environment = false
|
||||||
|
|
||||||
|
# set to 'true' to allow .pyc and .pyo files without
|
||||||
|
# a source .py file to be detected as revisions in the
|
||||||
|
# versions/ directory
|
||||||
|
# sourceless = false
|
||||||
|
|
||||||
|
# version location specification; This defaults
|
||||||
|
# to migrations/versions. When using multiple version
|
||||||
|
# directories, initial revisions must be specified with --version-path.
|
||||||
|
# The path separator used here should be the separator specified by "version_path_separator" below.
|
||||||
|
# version_locations = %(here)s/bar:%(here)s/bat:migrations/versions
|
||||||
|
|
||||||
|
# version path separator; As mentioned above, this is the character used to split
|
||||||
|
# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep.
|
||||||
|
# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas.
|
||||||
|
# Valid values for version_path_separator are:
|
||||||
|
#
|
||||||
|
# version_path_separator = :
|
||||||
|
# version_path_separator = ;
|
||||||
|
# version_path_separator = space
|
||||||
|
version_path_separator = os # Use os.pathsep. Default configuration used for new projects.
|
||||||
|
|
||||||
|
# set to 'true' to search source files recursively
|
||||||
|
# in each "version_locations" directory
|
||||||
|
# new in Alembic version 1.10
|
||||||
|
# recursive_version_locations = false
|
||||||
|
|
||||||
|
# the output encoding used when revision files
|
||||||
|
# are written from script.py.mako
|
||||||
|
# output_encoding = utf-8
|
||||||
|
|
||||||
|
sqlalchemy.url = postgresql+psycopg2://postgres:postgres@postgres:5432/postgres
|
||||||
|
|
||||||
|
|
||||||
|
[post_write_hooks]
|
||||||
|
# post_write_hooks defines scripts or Python functions that are run
|
||||||
|
# on newly generated revision scripts. See the documentation for further
|
||||||
|
# detail and examples
|
||||||
|
|
||||||
|
# format using "black" - use the console_scripts runner, against the "black" entrypoint
|
||||||
|
# hooks = black
|
||||||
|
# black.type = console_scripts
|
||||||
|
# black.entrypoint = black
|
||||||
|
# black.options = -l 79 REVISION_SCRIPT_FILENAME
|
||||||
|
|
||||||
|
# lint with attempts to fix using "ruff" - use the exec runner, execute a binary
|
||||||
|
# hooks = ruff
|
||||||
|
# ruff.type = exec
|
||||||
|
# ruff.executable = %(here)s/.venv/bin/ruff
|
||||||
|
# ruff.options = --fix REVISION_SCRIPT_FILENAME
|
||||||
|
|
||||||
|
# Logging configuration
|
||||||
|
[loggers]
|
||||||
|
keys = root,sqlalchemy,alembic
|
||||||
|
|
||||||
|
[handlers]
|
||||||
|
keys = console
|
||||||
|
|
||||||
|
[formatters]
|
||||||
|
keys = generic
|
||||||
|
|
||||||
|
[logger_root]
|
||||||
|
level = WARN
|
||||||
|
handlers = console
|
||||||
|
qualname =
|
||||||
|
|
||||||
|
[logger_sqlalchemy]
|
||||||
|
level = WARN
|
||||||
|
handlers =
|
||||||
|
qualname = sqlalchemy.engine
|
||||||
|
|
||||||
|
[logger_alembic]
|
||||||
|
level = INFO
|
||||||
|
handlers =
|
||||||
|
qualname = alembic
|
||||||
|
|
||||||
|
[handler_console]
|
||||||
|
class = StreamHandler
|
||||||
|
args = (sys.stderr,)
|
||||||
|
level = NOTSET
|
||||||
|
formatter = generic
|
||||||
|
|
||||||
|
[formatter_generic]
|
||||||
|
format = %(levelname)-5.5s [%(name)s] %(message)s
|
||||||
|
datefmt = %H:%M:%S
|
||||||
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")
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user