Files
plantillas-proyectos/backend/api/v1/modules/a76/parts/service.py

128 lines
4.5 KiB
Python

import logging
from typing import List, Optional, Any, Dict
from fastapi import HTTPException
from sqlalchemy import and_, or_
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from .models import Part
from .dto import PartCreateDTO, PartUpdateDTO, PartSearchDTO
logger = logging.getLogger(__name__)
class PartService:
"""
Servicio de Partes compatible con TenantCRUDRoutes
"""
@staticmethod
def get_all(
db: Session,
tenant_id: int,
company_id: int,
skip: int = 0,
limit: int = 100,
filters: Optional[Dict[str, Any]] = None, # Agregamos este argumento explícito
) -> tuple[List[Part], int]:
"""Obtener todas las partes con paginación y filtros"""
try:
query = db.query(Part).filter(
Part.tenant_id == tenant_id,
Part.company_id == company_id
)
if filters:
if filters.get("part_number"):
query = query.filter(Part.part_number.ilike(f"%{filters['part_number']}%"))
if filters.get("description"):
pattern = f"%{filters['description']}%"
query = query.filter(or_(
Part.description_spanish.ilike(pattern),
Part.description_english.ilike(pattern)
))
if filters.get("client_id"):
query = query.filter(Part.client_id == filters["client_id"])
total = query.count()
items = query.offset(skip).limit(limit).all()
return items, total
except Exception as e:
logger.error(f"Error en get_all partes: {e}")
raise HTTPException(status_code=500, detail="Error al listar partes")
@staticmethod
def get_by_id(db: Session, part_id: int, tenant_id: int, company_id: int) -> Optional[Part]:
"""Obtener una parte por su ID numérico (Reemplaza a get_part)"""
return db.query(Part).filter(
Part.id == part_id,
Part.tenant_id == tenant_id,
Part.company_id == company_id
).first()
@staticmethod
def create(db: Session, part_data: PartCreateDTO, tenant_id: int, company_id: int) -> Part:
"""Crear parte (Reemplaza a create_part)"""
try:
data = part_data.model_dump()
data['company_id'] = company_id
data['tenant_id'] = tenant_id
db_part = Part(**data)
db.add(db_part)
db.commit()
db.refresh(db_part)
return db_part
except IntegrityError as e:
db.rollback()
msg = str(e.orig)
if "client_part_ukey" in msg:
raise HTTPException(status_code=400, detail="El número de parte ya existe para este cliente.")
raise HTTPException(status_code=400, detail=f"Error de integridad: {msg}")
@staticmethod
def update(
db: Session,
part_id: int,
tenant_id: int,
part_data: PartUpdateDTO,
company_id: int
) -> Optional[Part]:
"""Actualizar parte por ID (Reemplaza a update_part)"""
db_part = PartService.get_by_id(db, part_id, tenant_id, company_id)
if not db_part:
return None
update_data = part_data.model_dump(exclude_unset=True)
# Evitar que se intente actualizar el ID o las llaves de seguridad
for key in ["id", "tenant_id", "company_id"]:
update_data.pop(key, None)
for key, value in update_data.items():
setattr(db_part, key, value)
try:
db.commit()
db.refresh(db_part)
return db_part
except Exception as e:
db.rollback()
logger.error(f"Error actualizando parte {part_id}: {e}")
raise HTTPException(status_code=500, detail="Error al actualizar parte")
@staticmethod
def delete(db: Session, part_id: int, tenant_id: int, company_id: int) -> bool:
"""Eliminar parte"""
db_part = PartService.get_by_id(db, part_id, tenant_id, company_id)
if not db_part:
return False
try:
db.delete(db_part)
db.commit()
return True
except Exception as e:
db.rollback()
logger.error(f"Error eliminando parte {part_id}: {e}")
raise HTTPException(status_code=500, detail="Error al eliminar parte")