1
This commit is contained in:
@@ -0,0 +1,285 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query, status
|
||||
from asyncpg import Connection
|
||||
|
||||
from app.auth import require_auth
|
||||
from app.dependencies import get_db
|
||||
from app.models.catalogo import (
|
||||
ServicioCatalogoCreate,
|
||||
ServicioCatalogoUpdate,
|
||||
ServicioCatalogoResponse,
|
||||
CategoriaResponse,
|
||||
)
|
||||
from app.models.common import ArchivedResponse, ErrorResponse, OkResponse
|
||||
|
||||
router = APIRouter(prefix="/catalogo", tags=["Catalogo"])
|
||||
|
||||
|
||||
def _row_to_response(row, categoria_row=None) -> ServicioCatalogoResponse:
|
||||
entregables = row["entregablesDefault"]
|
||||
if isinstance(entregables, str):
|
||||
entregables = json.loads(entregables)
|
||||
|
||||
categoria = None
|
||||
if categoria_row:
|
||||
categoria = CategoriaResponse(
|
||||
id=categoria_row["id"],
|
||||
nombre=categoria_row["nombre"],
|
||||
descripcion=categoria_row["descripcion"],
|
||||
color=categoria_row["color"],
|
||||
orden=categoria_row["orden"],
|
||||
activo=categoria_row["activo"],
|
||||
createdAt=categoria_row["createdAt"],
|
||||
updatedAt=categoria_row["updatedAt"],
|
||||
)
|
||||
|
||||
return ServicioCatalogoResponse(
|
||||
id=row["id"],
|
||||
nombre=row["nombre"],
|
||||
descripcion=row["descripcion"],
|
||||
fase=row["fase"],
|
||||
tipoPago=row["tipoPago"],
|
||||
precioBase=float(row["precioBase"]),
|
||||
tiempoEntrega=row["tiempoEntrega"],
|
||||
entregablesDefault=entregables,
|
||||
categoriaId=row["categoriaId"],
|
||||
variante=row["variante"],
|
||||
activo=row["activo"],
|
||||
orden=row["orden"],
|
||||
createdAt=row["createdAt"],
|
||||
updatedAt=row["updatedAt"],
|
||||
categoriaRel=categoria,
|
||||
)
|
||||
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=list[ServicioCatalogoResponse],
|
||||
summary="List active catalog services with filters",
|
||||
)
|
||||
async def list_catalogo(
|
||||
db: Connection = Depends(get_db),
|
||||
auth: dict = Depends(require_auth),
|
||||
fase: int | None = Query(None, ge=0, le=3),
|
||||
tipoPago: str | None = Query(None, pattern="^(unico|mensual)$"),
|
||||
categoriaId: str | None = Query(None),
|
||||
q: str | None = Query(None, description="Search by nombre or descripcion"),
|
||||
):
|
||||
where_clauses: list[str] = ['activo = true']
|
||||
params: list = []
|
||||
idx = 1
|
||||
|
||||
if fase is not None:
|
||||
where_clauses.append(f"fase = ${idx}")
|
||||
params.append(fase)
|
||||
idx += 1
|
||||
|
||||
if tipoPago:
|
||||
where_clauses.append(f'"tipoPago" = ${idx}')
|
||||
params.append(tipoPago)
|
||||
idx += 1
|
||||
|
||||
if categoriaId:
|
||||
where_clauses.append(f'"categoriaId" = ${idx}')
|
||||
params.append(categoriaId)
|
||||
idx += 1
|
||||
|
||||
if q:
|
||||
where_clauses.append(f"(nombre ILIKE ${idx} OR descripcion ILIKE ${idx})")
|
||||
params.append(f"%{q}%")
|
||||
idx += 1
|
||||
|
||||
where_sql = f"WHERE {' AND '.join(where_clauses)}"
|
||||
|
||||
rows = await db.fetch(
|
||||
f'SELECT * FROM "ServicioCatalogo" {where_sql} ORDER BY fase ASC, orden ASC',
|
||||
*params,
|
||||
)
|
||||
|
||||
result: list[ServicioCatalogoResponse] = []
|
||||
for r in rows:
|
||||
cat_row = None
|
||||
if r["categoriaId"]:
|
||||
cat_row = await db.fetchrow(
|
||||
'SELECT * FROM "Categoria" WHERE id = $1', r["categoriaId"]
|
||||
)
|
||||
result.append(_row_to_response(r, cat_row))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
@router.post(
|
||||
"",
|
||||
response_model=ServicioCatalogoResponse,
|
||||
status_code=status.HTTP_201_CREATED,
|
||||
summary="Create a new catalog service",
|
||||
)
|
||||
async def create_catalogo(
|
||||
body: ServicioCatalogoCreate,
|
||||
db: Connection = Depends(get_db),
|
||||
auth: dict = Depends(require_auth),
|
||||
):
|
||||
if body.categoriaId:
|
||||
cat = await db.fetchrow(
|
||||
'SELECT id FROM "Categoria" WHERE id = $1', body.categoriaId
|
||||
)
|
||||
if not cat:
|
||||
raise HTTPException(status_code=400, detail="Categoria no encontrada")
|
||||
|
||||
entregables_json = json.dumps(body.entregablesDefault)
|
||||
|
||||
row = await db.fetchrow(
|
||||
'INSERT INTO "ServicioCatalogo" '
|
||||
'(nombre, descripcion, fase, "tipoPago", "precioBase", "tiempoEntrega", '
|
||||
'"entregablesDefault", "categoriaId", variante, activo, orden) '
|
||||
"VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, true, $10) RETURNING *",
|
||||
body.nombre,
|
||||
body.descripcion,
|
||||
body.fase,
|
||||
body.tipoPago,
|
||||
body.precioBase,
|
||||
body.tiempoEntrega,
|
||||
entregables_json,
|
||||
body.categoriaId,
|
||||
body.variante,
|
||||
body.orden,
|
||||
)
|
||||
|
||||
cat_row = None
|
||||
if row["categoriaId"]:
|
||||
cat_row = await db.fetchrow(
|
||||
'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
|
||||
)
|
||||
|
||||
return _row_to_response(row, cat_row)
|
||||
|
||||
|
||||
@router.get(
|
||||
"/{servicio_id}",
|
||||
response_model=ServicioCatalogoResponse,
|
||||
responses={404: {"model": ErrorResponse}},
|
||||
summary="Get catalog service by ID with categoria join",
|
||||
)
|
||||
async def get_catalogo(
|
||||
servicio_id: str,
|
||||
db: Connection = Depends(get_db),
|
||||
auth: dict = Depends(require_auth),
|
||||
):
|
||||
row = await db.fetchrow(
|
||||
'SELECT * FROM "ServicioCatalogo" WHERE id = $1', servicio_id
|
||||
)
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="Servicio no encontrado")
|
||||
|
||||
cat_row = None
|
||||
if row["categoriaId"]:
|
||||
cat_row = await db.fetchrow(
|
||||
'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
|
||||
)
|
||||
|
||||
return _row_to_response(row, cat_row)
|
||||
|
||||
|
||||
@router.put(
|
||||
"/{servicio_id}",
|
||||
response_model=ServicioCatalogoResponse,
|
||||
responses={404: {"model": ErrorResponse}},
|
||||
summary="Update a catalog service",
|
||||
)
|
||||
async def update_catalogo(
|
||||
servicio_id: str,
|
||||
body: ServicioCatalogoUpdate,
|
||||
db: Connection = Depends(get_db),
|
||||
auth: dict = Depends(require_auth),
|
||||
):
|
||||
existing = await db.fetchrow(
|
||||
'SELECT id FROM "ServicioCatalogo" WHERE id = $1', servicio_id
|
||||
)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Servicio no encontrado")
|
||||
|
||||
if body.categoriaId:
|
||||
cat = await db.fetchrow(
|
||||
'SELECT id FROM "Categoria" WHERE id = $1', body.categoriaId
|
||||
)
|
||||
if not cat:
|
||||
raise HTTPException(status_code=400, detail="Categoria no encontrada")
|
||||
|
||||
field_map = {
|
||||
"nombre": "nombre",
|
||||
"descripcion": "descripcion",
|
||||
"fase": "fase",
|
||||
"tipoPago": '"tipoPago"',
|
||||
"precioBase": '"precioBase"',
|
||||
"tiempoEntrega": '"tiempoEntrega"',
|
||||
"entregablesDefault": '"entregablesDefault"',
|
||||
"categoriaId": '"categoriaId"',
|
||||
"variante": "variante",
|
||||
"activo": "activo",
|
||||
"orden": "orden",
|
||||
}
|
||||
|
||||
updates: list[str] = []
|
||||
params: list = []
|
||||
idx = 1
|
||||
|
||||
for field, col in field_map.items():
|
||||
value = getattr(body, field, None)
|
||||
if value is not None:
|
||||
if field == "entregablesDefault":
|
||||
value = json.dumps(value)
|
||||
updates.append(f"{col} = ${idx}")
|
||||
params.append(value)
|
||||
idx += 1
|
||||
|
||||
if not updates:
|
||||
raise HTTPException(status_code=400, detail="No fields to update")
|
||||
|
||||
params.append(servicio_id)
|
||||
row = await db.fetchrow(
|
||||
f'UPDATE "ServicioCatalogo" SET {", ".join(updates)} WHERE id = ${idx} RETURNING *',
|
||||
*params,
|
||||
)
|
||||
|
||||
cat_row = None
|
||||
if row["categoriaId"]:
|
||||
cat_row = await db.fetchrow(
|
||||
'SELECT * FROM "Categoria" WHERE id = $1', row["categoriaId"]
|
||||
)
|
||||
|
||||
return _row_to_response(row, cat_row)
|
||||
|
||||
|
||||
@router.delete(
|
||||
"/{servicio_id}",
|
||||
response_model=ArchivedResponse,
|
||||
responses={404: {"model": ErrorResponse}},
|
||||
summary="Delete or soft-delete a catalog service",
|
||||
)
|
||||
async def delete_catalogo(
|
||||
servicio_id: str,
|
||||
db: Connection = Depends(get_db),
|
||||
auth: dict = Depends(require_auth),
|
||||
):
|
||||
existing = await db.fetchrow(
|
||||
'SELECT id FROM "ServicioCatalogo" WHERE id = $1', servicio_id
|
||||
)
|
||||
if not existing:
|
||||
raise HTTPException(status_code=404, detail="Servicio no encontrado")
|
||||
|
||||
ref_count = await db.fetchval(
|
||||
'SELECT COUNT(*) FROM "ServicioCotizado" WHERE "servicioCatalogoId" = $1',
|
||||
servicio_id,
|
||||
)
|
||||
|
||||
if ref_count > 0:
|
||||
await db.execute(
|
||||
'UPDATE "ServicioCatalogo" SET activo = false WHERE id = $1', servicio_id
|
||||
)
|
||||
return ArchivedResponse(ok=True, archived=True)
|
||||
|
||||
await db.execute('DELETE FROM "ServicioCatalogo" WHERE id = $1', servicio_id)
|
||||
return ArchivedResponse(ok=True, archived=False)
|
||||
Reference in New Issue
Block a user