feature/catalogos-generales-permisos
This commit is contained in:
@@ -19,6 +19,8 @@ from core.database import get_core_db
|
||||
from core.s3_keys import company_certificate_key, company_logo_key
|
||||
from core.storage_s3 import delete_object_if_exists, get_object_bytes, put_object_bytes
|
||||
from core.security import (
|
||||
collect_company_ids_from_app_membership,
|
||||
collect_user_role_names,
|
||||
get_current_user,
|
||||
get_tenant_from_token,
|
||||
resolve_effective_tenant_id_from_user,
|
||||
@@ -37,6 +39,48 @@ MAX_FILE_SIZE = 5 * 1024 * 1024 # 5MB
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def _user_is_admin(current_user: dict) -> bool:
|
||||
return "admin" in collect_user_role_names(current_user)
|
||||
|
||||
|
||||
def _assert_permission_any_company(
|
||||
db: Session,
|
||||
current_user: dict,
|
||||
permission_code: str,
|
||||
) -> None:
|
||||
"""
|
||||
Enforces a permission when endpoint has no explicit company_id param.
|
||||
"""
|
||||
if _user_is_admin(current_user):
|
||||
return
|
||||
|
||||
user_id = current_user.get("sub") or current_user.get("id")
|
||||
if not user_id:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")
|
||||
|
||||
company_ids = collect_company_ids_from_app_membership(db, str(user_id))
|
||||
if not company_ids:
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")
|
||||
|
||||
from api.v1.modules.core.permissions.service import PermissionService
|
||||
|
||||
permission_service = PermissionService(db)
|
||||
for company_id in company_ids:
|
||||
if permission_service.has_any_permission(str(user_id), int(company_id), [permission_code]):
|
||||
return
|
||||
|
||||
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")
|
||||
|
||||
|
||||
def _assert_permission_for_company(
|
||||
db: Session,
|
||||
company_id: int,
|
||||
current_user: dict,
|
||||
permission_code: str,
|
||||
) -> int:
|
||||
return validate_access_to_resource(db, company_id, current_user, [permission_code])
|
||||
|
||||
|
||||
def _resolve_tenant_id_int(current_user: dict) -> int:
|
||||
"""Misma lógica que validate_access_to_resource: entero estable para BD y claves S3."""
|
||||
tid = get_tenant_from_token(current_user)
|
||||
@@ -87,6 +131,7 @@ async def create_company(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
_assert_permission_any_company(db, current_user, "cat_company.create")
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
@@ -114,6 +159,8 @@ async def list_companies(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get paginated list of companies for current tenant with optional filters"""
|
||||
_assert_permission_any_company(db, current_user, "cat_company.view")
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
@@ -142,7 +189,7 @@ async def list_companies(
|
||||
|
||||
return {
|
||||
"items": [
|
||||
CompanyResponseDTO.model_validate(service.flatten_company_dto(item))
|
||||
CompanyResponseDTO.model_validate(service.flatten_company_dto(item))
|
||||
for item in items
|
||||
],
|
||||
"total": total,
|
||||
@@ -167,6 +214,8 @@ async def get_my_companies(
|
||||
Un usuario solo con roles de app y sin ``tenant_id`` en /auth/me sigue pudiendo
|
||||
listar sus compañías asignadas.
|
||||
"""
|
||||
_assert_permission_any_company(db, current_user, "cat_company.view")
|
||||
|
||||
user_id = current_user.get("sub") or current_user.get("id")
|
||||
tenant_id = resolve_effective_tenant_id_from_user(current_user)
|
||||
|
||||
@@ -194,6 +243,8 @@ async def get_company(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get a specific company by ID"""
|
||||
_assert_permission_for_company(db, company_id, current_user, "cat_company.view")
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
@@ -224,6 +275,8 @@ async def update_company(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Update a company"""
|
||||
_assert_permission_for_company(db, company_id, current_user, "cat_company.edit")
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
@@ -299,6 +352,8 @@ async def delete_company(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Delete a company"""
|
||||
_assert_permission_for_company(db, company_id, current_user, "cat_company.delete")
|
||||
|
||||
tenant_id = current_user.get("tenant_id")
|
||||
if not tenant_id:
|
||||
raise HTTPException(
|
||||
@@ -328,6 +383,8 @@ async def upload_company_logo(
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Upload a logo for a company"""
|
||||
_assert_permission_for_company(db, company_id, current_user, "cat_company.edit")
|
||||
|
||||
tenant_id = _resolve_tenant_id_int(current_user)
|
||||
|
||||
# Validar que la empresa existe
|
||||
@@ -409,6 +466,8 @@ async def upload_company_certificate(
|
||||
Upload a certificate for a company
|
||||
certificate_type: fiel_cer, fiel_key, cfdi_cert_cer, cfdi_cert_key, cancel_cer, cancel_key
|
||||
"""
|
||||
_assert_permission_for_company(db, company_id, current_user, "cat_company.edit")
|
||||
|
||||
tenant_id = _resolve_tenant_id_int(current_user)
|
||||
|
||||
# Validar que la empresa existe
|
||||
|
||||
@@ -30,6 +30,10 @@ base_router = TenantCRUDRoutes(
|
||||
enable_filters=False,
|
||||
default_page_size=100,
|
||||
max_page_size=1000,
|
||||
get_permissions=["cat_depreciation_catalog.view"],
|
||||
create_permissions=["cat_depreciation_catalog.create"],
|
||||
update_permissions=["cat_depreciation_catalog.edit"],
|
||||
delete_permissions=["cat_depreciation_catalog.delete"],
|
||||
)
|
||||
|
||||
router = APIRouter(prefix="/depreciation-catalog", tags=["a76 / general catalogs / depreciation catalog"])
|
||||
@@ -49,7 +53,9 @@ async def list_depreciation_catalog(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["cat_depreciation_catalog.view"]
|
||||
)
|
||||
|
||||
items, total = DepreciationCatalogService.get_all(
|
||||
db, tenant_id, company_id, page, page_size, search
|
||||
|
||||
@@ -254,7 +254,9 @@ async def print_doda_pdf(
|
||||
Genera o reutiliza el PDF almacenado en S3 cuando el contenido no ha cambiado
|
||||
(huella SHA-256 de DODA + hijos).
|
||||
"""
|
||||
tenant_id = int(validate_access_to_resource(db, company_id, current_user))
|
||||
tenant_id = int(
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
)
|
||||
doda = DodaService.get_by_id(db, doda_id, tenant_id, company_id)
|
||||
if not doda:
|
||||
raise HTTPException(
|
||||
@@ -327,9 +329,12 @@ async def print_doda_pdf(
|
||||
)
|
||||
async def get_doda_containers(
|
||||
doda_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get all containers for a specific DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
containers = DodaService.get_containers(db, doda_id)
|
||||
return [DodaContainerResponseDTO.model_validate(c) for c in containers]
|
||||
|
||||
@@ -343,9 +348,12 @@ async def get_doda_containers(
|
||||
async def add_container(
|
||||
doda_id: int,
|
||||
container_data: DodaContainerCreateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Add a new container to a DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
container = DodaService.add_container(db, doda_id, container_data)
|
||||
if not container:
|
||||
raise HTTPException(
|
||||
@@ -364,9 +372,12 @@ async def update_container(
|
||||
doda_id: int,
|
||||
container_line: int,
|
||||
container_data: DodaContainerUpdateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Update a container"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
container = DodaService.update_container(
|
||||
db, doda_id, container_line, container_data
|
||||
)
|
||||
@@ -386,12 +397,15 @@ async def update_container(
|
||||
async def delete_container(
|
||||
doda_id: int,
|
||||
container_line: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""
|
||||
Elimina un contenedor del DODA.
|
||||
Devuelve 409 si el contenedor tiene precintos (candados) asignados.
|
||||
"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
DodaService.delete_container(db, doda_id, container_line)
|
||||
return None
|
||||
|
||||
@@ -415,8 +429,11 @@ def _seal_to_response(seal) -> DodaContainerSealResponseDTO:
|
||||
async def get_container_seals(
|
||||
doda_id: int,
|
||||
container_line: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
seals = DodaService.get_seals_for_container(db, doda_id, container_line)
|
||||
return [_seal_to_response(s) for s in seals]
|
||||
|
||||
@@ -431,8 +448,11 @@ async def add_container_seal(
|
||||
doda_id: int,
|
||||
container_line: int,
|
||||
seal_data: DodaContainerSealCreateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
seal = DodaService.add_seal(db, doda_id, container_line, seal_data)
|
||||
return _seal_to_response(seal)
|
||||
|
||||
@@ -446,8 +466,11 @@ async def delete_container_seal(
|
||||
doda_id: int,
|
||||
container_line: int,
|
||||
seal_line: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
DodaService.delete_seal(db, doda_id, container_line, seal_line)
|
||||
return None
|
||||
|
||||
@@ -460,9 +483,12 @@ async def delete_container_seal(
|
||||
)
|
||||
async def get_american_pedimentos(
|
||||
doda_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get all American pedimentos for a specific DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
pedimentos = DodaService.get_american_pedimentos(db, doda_id)
|
||||
return [DodaAmericanPedimentoResponseDTO.model_validate(p) for p in pedimentos]
|
||||
|
||||
@@ -476,9 +502,12 @@ async def get_american_pedimentos(
|
||||
async def add_american_pedimento(
|
||||
doda_id: int,
|
||||
pedimento_data: DodaAmericanPedimentoCreateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Add a new American pedimento to a DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
pedimento = DodaService.add_american_pedimento(db, doda_id, pedimento_data)
|
||||
if not pedimento:
|
||||
raise HTTPException(
|
||||
@@ -497,9 +526,12 @@ async def add_american_pedimento(
|
||||
async def delete_american_pedimento(
|
||||
doda_id: int,
|
||||
pedimento_line: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Elimina un pedimento americano del DODA."""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
DodaService.delete_american_pedimento(db, doda_id, pedimento_line)
|
||||
return None
|
||||
|
||||
@@ -512,9 +544,12 @@ async def delete_american_pedimento(
|
||||
)
|
||||
async def get_doda_pedimentos(
|
||||
doda_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get all pedimentos for a specific DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
pedimentos = DodaService.get_pedimentos(db, doda_id)
|
||||
return [DodaPedimentoResponseDTO.model_validate(p) for p in pedimentos]
|
||||
|
||||
@@ -528,9 +563,12 @@ async def get_doda_pedimentos(
|
||||
async def add_pedimento(
|
||||
doda_id: int,
|
||||
pedimento_data: DodaPedimentoCreateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Add a new pedimento to a DODA"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
pedimento = DodaService.add_pedimento(db, doda_id, pedimento_data)
|
||||
if not pedimento:
|
||||
raise HTTPException(
|
||||
@@ -548,9 +586,12 @@ async def add_pedimento(
|
||||
async def delete_pedimento(
|
||||
doda_id: int,
|
||||
pedimento_line: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Elimina un pedimento del DODA."""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
DodaService.delete_pedimento(db, doda_id, pedimento_line)
|
||||
return None
|
||||
|
||||
@@ -575,7 +616,7 @@ async def get_doda_elegibilidad(
|
||||
Porta las validaciones del sistema legacy (campos requeridos, max 4 contenedores,
|
||||
gafete si DODA, patente vs agente, certificados DODA en VU).
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
user_email = (
|
||||
current_user.get("email")
|
||||
or current_user.get("preferred_username")
|
||||
@@ -614,7 +655,7 @@ async def post_doda_alta(
|
||||
Verifica elegibilidad, construye el payload desde los datos del DODA y su VU,
|
||||
y envía el alta al servicio externo. Retorna {task_id, status, message} para polling.
|
||||
"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
user_email = (
|
||||
current_user.get("email")
|
||||
or current_user.get("preferred_username")
|
||||
@@ -694,11 +735,14 @@ async def post_doda_alta(
|
||||
)
|
||||
async def get_doda_alta_status(
|
||||
task_id: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Any:
|
||||
"""
|
||||
Proxy transparente al servicio externo para consultar el estado de una tarea de alta DODA.
|
||||
"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
try:
|
||||
ext = DodaExternalService()
|
||||
return ext.get_status(task_id)
|
||||
@@ -727,7 +771,7 @@ async def post_doda_consulta(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
user_email = (
|
||||
current_user.get("email")
|
||||
or current_user.get("preferred_username")
|
||||
@@ -788,8 +832,11 @@ async def post_doda_consulta(
|
||||
)
|
||||
async def get_doda_consulta_status(
|
||||
task_id: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Any:
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
try:
|
||||
ext = DodaExternalService()
|
||||
return ext.get_consulta_status(task_id)
|
||||
@@ -818,7 +865,9 @@ async def post_doda_consulta_apply(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = int(validate_access_to_resource(db, company_id, current_user))
|
||||
tenant_id = int(
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
)
|
||||
doda_record = DodaService.get_by_id(db, doda_id, tenant_id, company_id)
|
||||
if not doda_record:
|
||||
raise HTTPException(status_code=404, detail="DODA no encontrado.")
|
||||
@@ -880,7 +929,7 @@ async def post_doda_eliminar(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Dict[str, Any]:
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
user_email = (
|
||||
current_user.get("email")
|
||||
or current_user.get("preferred_username")
|
||||
@@ -960,8 +1009,11 @@ async def post_doda_eliminar(
|
||||
)
|
||||
async def get_doda_eliminar_status(
|
||||
task_id: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
) -> Any:
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
try:
|
||||
ext = DodaExternalService()
|
||||
return ext.get_eliminar_status(task_id)
|
||||
@@ -996,7 +1048,7 @@ async def list_doda_alta_logs(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
return DodaAltaLogService.list(
|
||||
db, company_id, int(tenant_id), page, page_size, doda_id, search
|
||||
)
|
||||
@@ -1014,7 +1066,7 @@ async def get_doda_alta_log(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.view"])
|
||||
record = DodaAltaLogService.get(db, log_id, company_id, int(tenant_id))
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Registro de alta DODA no encontrado.")
|
||||
@@ -1034,7 +1086,7 @@ async def create_doda_alta_log(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.create"])
|
||||
record = DodaAltaLogService.create(db, dto, company_id, int(tenant_id))
|
||||
return DodaAltaLogResponseDTO.model_validate(record)
|
||||
|
||||
@@ -1052,7 +1104,7 @@ async def update_doda_alta_log(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.edit"])
|
||||
record = DodaAltaLogService.get(db, log_id, company_id, int(tenant_id))
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Registro de alta DODA no encontrado.")
|
||||
@@ -1072,7 +1124,7 @@ async def delete_doda_alta_log(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_doda.delete"])
|
||||
record = DodaAltaLogService.get(db, log_id, company_id, int(tenant_id))
|
||||
if not record:
|
||||
raise HTTPException(status_code=404, detail="Registro de alta DODA no encontrado.")
|
||||
|
||||
@@ -4,12 +4,12 @@ Rutas para gestión de catálogos de errores
|
||||
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user
|
||||
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
|
||||
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes, validate_access_to_resource
|
||||
from .dto import (
|
||||
ErrorClassificationCreateDTO,
|
||||
ErrorClassificationResponseDTO,
|
||||
@@ -35,6 +35,11 @@ classification_crud = TenantCRUDRoutes(
|
||||
tags=["error-classifications"],
|
||||
resource_name="ErrorClassification",
|
||||
enable_list=True,
|
||||
list_permissions=["cat_errors.view"],
|
||||
get_permissions=["cat_errors.view"],
|
||||
create_permissions=["cat_errors.create"],
|
||||
update_permissions=["cat_errors.edit"],
|
||||
delete_permissions=["cat_errors.delete"],
|
||||
)
|
||||
|
||||
# Add custom endpoints for classifications
|
||||
@@ -47,15 +52,17 @@ classification_crud = TenantCRUDRoutes(
|
||||
)
|
||||
async def get_classification_by_code(
|
||||
code: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get an error classification by its code with all related errors"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_errors.view"])
|
||||
classification = ErrorClassificationService.get_by_code(
|
||||
db,
|
||||
code,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
if not classification:
|
||||
raise HTTPException(
|
||||
@@ -74,15 +81,17 @@ async def get_classification_by_code(
|
||||
)
|
||||
async def get_classification(
|
||||
id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get an error classification by its ID with all related errors"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_errors.view"])
|
||||
classification = ErrorClassificationService.get_by_id(
|
||||
db,
|
||||
id,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
if not classification:
|
||||
raise HTTPException(
|
||||
@@ -104,6 +113,11 @@ catalog_crud = TenantCRUDRoutes(
|
||||
tags=["error-catalogs"],
|
||||
resource_name="ErrorCatalog",
|
||||
enable_list=True,
|
||||
list_permissions=["cat_errors.view"],
|
||||
get_permissions=["cat_errors.view"],
|
||||
create_permissions=["cat_errors.create"],
|
||||
update_permissions=["cat_errors.edit"],
|
||||
delete_permissions=["cat_errors.delete"],
|
||||
)
|
||||
|
||||
# Add custom endpoints for catalogs
|
||||
@@ -116,15 +130,17 @@ catalog_crud = TenantCRUDRoutes(
|
||||
)
|
||||
async def get_error_by_code(
|
||||
code: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get an error by its code with classification details"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_errors.view"])
|
||||
error = ErrorCatalogService.get_by_code(
|
||||
db,
|
||||
code,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
if not error:
|
||||
raise HTTPException(
|
||||
@@ -141,15 +157,17 @@ async def get_error_by_code(
|
||||
)
|
||||
async def get_errors_by_classification(
|
||||
classification_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get all errors for a specific classification"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_errors.view"])
|
||||
errors = ErrorCatalogService.get_by_classification(
|
||||
db,
|
||||
classification_id,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
return [ErrorCatalogResponseDTO.model_validate(error) for error in errors]
|
||||
|
||||
@@ -163,15 +181,17 @@ async def get_errors_by_classification(
|
||||
)
|
||||
async def get_error(
|
||||
id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get an error by its ID with classification details"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user, ["cat_errors.view"])
|
||||
error = ErrorCatalogService.get_by_id(
|
||||
db,
|
||||
id,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
if not error:
|
||||
raise HTTPException(
|
||||
|
||||
@@ -25,9 +25,9 @@ def list_canadian_fractions(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_canadian.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_canadian.view"]
|
||||
)
|
||||
skip = (page - 1) * page_size
|
||||
service = CanadianTariffFractionService(db)
|
||||
items, total = service.get_multi(
|
||||
@@ -52,9 +52,9 @@ def get_canadian_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_canadian.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_canadian.view"]
|
||||
)
|
||||
service = CanadianTariffFractionService(db)
|
||||
item = service.get(id, tenant_id, company_id)
|
||||
if not item:
|
||||
@@ -68,9 +68,9 @@ def create_canadian_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_canadian.create"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_canadian.create"]
|
||||
)
|
||||
service = CanadianTariffFractionService(db)
|
||||
return service.create(item_in, tenant_id, company_id)
|
||||
|
||||
@@ -82,9 +82,9 @@ def update_canadian_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_canadian.edit"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_canadian.edit"]
|
||||
)
|
||||
service = CanadianTariffFractionService(db)
|
||||
item = service.get(id, tenant_id, company_id)
|
||||
if not item:
|
||||
@@ -98,9 +98,9 @@ def delete_canadian_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_canadian.delete"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_canadian.delete"]
|
||||
)
|
||||
service = CanadianTariffFractionService(db)
|
||||
item = service.get(id, tenant_id, company_id)
|
||||
if not item:
|
||||
|
||||
@@ -20,9 +20,9 @@ def get_historical_fractions(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.view"]
|
||||
)
|
||||
skip = (page - 1) * page_size
|
||||
service = HistoricalTariffFractionService(db)
|
||||
items, total = service.get_multi(tenant_id, company_id, skip=skip, limit=page_size, historical_fraction=historical_fraction)
|
||||
@@ -46,9 +46,9 @@ async def get_rate(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.view"]
|
||||
)
|
||||
|
||||
# Parse date
|
||||
try:
|
||||
@@ -80,9 +80,9 @@ def get_historical_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.view"]
|
||||
)
|
||||
service = HistoricalTariffFractionService(db)
|
||||
fraction = service.get(id, tenant_id, company_id)
|
||||
if not fraction:
|
||||
@@ -96,9 +96,9 @@ def create_historical_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.create"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.create"]
|
||||
)
|
||||
service = HistoricalTariffFractionService(db)
|
||||
return service.create(fraction_in, tenant_id, company_id)
|
||||
|
||||
@@ -110,9 +110,9 @@ def update_historical_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.edit"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.edit"]
|
||||
)
|
||||
service = HistoricalTariffFractionService(db)
|
||||
fraction = service.get(id, tenant_id, company_id)
|
||||
if not fraction:
|
||||
@@ -126,9 +126,9 @@ def delete_historical_fraction(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user = Depends(get_current_user)
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_historical.delete"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_historical.delete"]
|
||||
)
|
||||
service = HistoricalTariffFractionService(db)
|
||||
fraction = service.get(id, tenant_id, company_id)
|
||||
if not fraction:
|
||||
|
||||
@@ -40,8 +40,10 @@ async def list_tariff_fractions(
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
# Validar permisos según el catálogo solicitado
|
||||
if catalog in ["mex", "usa"]:
|
||||
if catalog == "mex":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_sitar.view"])
|
||||
elif catalog == "usa":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_sitar_us.view"])
|
||||
elif catalog == "american":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_american.view"])
|
||||
|
||||
@@ -83,12 +85,19 @@ async def list_tariff_fractions(
|
||||
)
|
||||
async def get_tariff_fraction(
|
||||
tariff_fraction_id: int,
|
||||
catalog: str = Query("mex", description="Catalog source: 'mex', 'usa', or 'american'"),
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
# Por defecto asumimos vista de SITAR para este endpoint de consulta por ID general
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_sitar.view"])
|
||||
if catalog == "mex":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_sitar.view"])
|
||||
elif catalog == "usa":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_sitar_us.view"])
|
||||
elif catalog == "american":
|
||||
validate_access_to_resource(db, company_id, current_user, ["frac_american.view"])
|
||||
else:
|
||||
raise HTTPException(status_code=400, detail=f"Unsupported catalog '{catalog}'")
|
||||
item = TariffFractionService.get_by_id(db, tariff_fraction_id)
|
||||
if not item:
|
||||
from fastapi import HTTPException
|
||||
|
||||
@@ -77,11 +77,9 @@ async def list_us_tariff_fractions(
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(get_current_user),
|
||||
):
|
||||
from core.security import validate_access_to_resource as validate_perm
|
||||
validate_perm(db, company_id, current_user, ["frac_american.view"])
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
|
||||
validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["frac_sitar_us.view"]
|
||||
)
|
||||
|
||||
skip = (page - 1) * page_size
|
||||
try:
|
||||
|
||||
@@ -17,4 +17,9 @@ router = TenantCRUDRoutes(
|
||||
enable_list=True,
|
||||
enable_filters=True,
|
||||
max_page_size=1000,
|
||||
list_permissions=["cat_locations.view"],
|
||||
get_permissions=["cat_locations.view"],
|
||||
create_permissions=["cat_locations.create"],
|
||||
update_permissions=["cat_locations.edit"],
|
||||
delete_permissions=["cat_locations.delete"],
|
||||
).router
|
||||
|
||||
@@ -4,12 +4,12 @@ Rutas para gestión de prevalidadores
|
||||
|
||||
from typing import List
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, status
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from sqlalchemy.orm import Session
|
||||
|
||||
from core.database import get_core_db
|
||||
from core.security import get_current_user
|
||||
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes
|
||||
from api.v1.common.tenant_crud_routes import TenantCRUDRoutes, validate_access_to_resource
|
||||
from .dto import (
|
||||
PrevalidatorCreateDTO,
|
||||
PrevalidatorResponseDTO,
|
||||
@@ -29,6 +29,11 @@ prevalidator_crud = TenantCRUDRoutes(
|
||||
tags=["Prevalidators"],
|
||||
resource_name="Prevalidator",
|
||||
enable_list=True,
|
||||
list_permissions=["cat_prevalidators.view"],
|
||||
get_permissions=["cat_prevalidators.view"],
|
||||
create_permissions=["cat_prevalidators.create"],
|
||||
update_permissions=["cat_prevalidators.edit"],
|
||||
delete_permissions=["cat_prevalidators.delete"],
|
||||
)
|
||||
|
||||
# Add custom endpoints
|
||||
@@ -41,15 +46,19 @@ prevalidator_crud = TenantCRUDRoutes(
|
||||
)
|
||||
async def get_prevalidator_by_code(
|
||||
code: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get a prevalidator by its code"""
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["cat_prevalidators.view"]
|
||||
)
|
||||
prevalidator = PrevalidatorService.get_by_code(
|
||||
db,
|
||||
code,
|
||||
tenant_id=current_user["tenant_id"],
|
||||
company_id=current_user["company_id"]
|
||||
tenant_id=tenant_id,
|
||||
company_id=company_id,
|
||||
)
|
||||
if not prevalidator:
|
||||
raise HTTPException(
|
||||
@@ -78,9 +87,12 @@ async def create_prevalidator(
|
||||
async def update_prevalidator(
|
||||
prevalidator_id: int,
|
||||
prevalidator_data: PrevalidatorUpdateDTO,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Update a prevalidator"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_prevalidators.edit"])
|
||||
prevalidator = PrevalidatorService.update(
|
||||
db, prevalidator_id, prevalidator_data)
|
||||
if not prevalidator:
|
||||
@@ -98,9 +110,12 @@ async def update_prevalidator(
|
||||
)
|
||||
async def delete_prevalidator(
|
||||
prevalidator_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Delete a prevalidator"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_prevalidators.delete"])
|
||||
success = PrevalidatorService.delete(db, prevalidator_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
@@ -117,10 +132,15 @@ async def delete_prevalidator(
|
||||
)
|
||||
async def get_prevalidators_by_customs(
|
||||
customs: str,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Get all prevalidators for a specific customs"""
|
||||
prevalidators = PrevalidatorService.get_by_customs(db, customs)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["cat_prevalidators.view"]
|
||||
)
|
||||
prevalidators = PrevalidatorService.get_by_customs(db, customs, tenant_id, company_id)
|
||||
return [
|
||||
PrevalidatorResponseDTO.model_validate(prevalidator)
|
||||
for prevalidator in prevalidators
|
||||
|
||||
@@ -27,6 +27,11 @@ signature_crud = TenantCRUDRoutes(
|
||||
enable_filters=True, # Enable filtering by code
|
||||
default_page_size=50,
|
||||
max_page_size=100,
|
||||
list_permissions=["cat_signatures.view"],
|
||||
get_permissions=["cat_signatures.view"],
|
||||
create_permissions=["cat_signatures.create"],
|
||||
update_permissions=["cat_signatures.edit"],
|
||||
delete_permissions=["cat_signatures.delete"],
|
||||
)
|
||||
|
||||
router = signature_crud.router
|
||||
@@ -44,7 +49,9 @@ async def get_signature_by_code(
|
||||
current_user: Dict[str, Any] = Depends(signature_crud.auth_dependency),
|
||||
):
|
||||
"""Get a signature by its code"""
|
||||
tenant_id = validate_access_to_resource(db, company_id, current_user)
|
||||
tenant_id = validate_access_to_resource(
|
||||
db, company_id, current_user, ["cat_signatures.view"]
|
||||
)
|
||||
signature = SignatureService.get_by_code(db, code, tenant_id, company_id)
|
||||
if not signature:
|
||||
raise HTTPException(
|
||||
@@ -61,9 +68,12 @@ async def get_signature_by_code(
|
||||
)
|
||||
async def delete_signature(
|
||||
signature_id: int,
|
||||
company_id: int = Query(..., description="Company ID"),
|
||||
db: Session = Depends(get_core_db),
|
||||
current_user: Dict[str, Any] = Depends(signature_crud.auth_dependency),
|
||||
):
|
||||
"""Delete a signature"""
|
||||
validate_access_to_resource(db, company_id, current_user, ["cat_signatures.delete"])
|
||||
success = SignatureService.delete(db, signature_id)
|
||||
if not success:
|
||||
raise HTTPException(
|
||||
|
||||
Reference in New Issue
Block a user