1
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import io
|
||||
|
||||
from fastapi import APIRouter, Depends, File, UploadFile
|
||||
from asyncpg import Connection
|
||||
|
||||
from app.dependencies import get_db
|
||||
from app.auth import require_auth
|
||||
|
||||
router = APIRouter(prefix="/import", tags=["Import"])
|
||||
|
||||
REQUIRED_COLUMNS = {"nombre", "fase", "tipoPago", "precioBase", "categoria"}
|
||||
|
||||
|
||||
@router.post("/catalogo")
|
||||
async def import_catalogo_csv(
|
||||
file: UploadFile = File(...),
|
||||
_auth: dict = Depends(require_auth),
|
||||
db: Connection = Depends(get_db),
|
||||
):
|
||||
content = await file.read()
|
||||
text = content.decode("utf-8-sig")
|
||||
reader = csv.DictReader(io.StringIO(text))
|
||||
|
||||
if not reader.fieldnames or not REQUIRED_COLUMNS.issubset(set(reader.fieldnames)):
|
||||
missing = REQUIRED_COLUMNS - set(reader.fieldnames or [])
|
||||
return {
|
||||
"creados": 0,
|
||||
"omitidos": 0,
|
||||
"errores": [f"Missing required columns: {', '.join(missing)}"],
|
||||
}
|
||||
|
||||
categorias = await db.fetch('SELECT id, nombre FROM "Categoria" WHERE activo = true')
|
||||
cat_map = {r["nombre"]: r["id"] for r in categorias}
|
||||
|
||||
existing = await db.fetch('SELECT nombre FROM "ServicioCatalogo" WHERE activo = true')
|
||||
existing_names = {r["nombre"] for r in existing}
|
||||
|
||||
creados = 0
|
||||
omitidos = 0
|
||||
errores: list[str] = []
|
||||
|
||||
async with db.transaction():
|
||||
for i, row in enumerate(reader, start=2):
|
||||
nombre = (row.get("nombre") or "").strip()
|
||||
if not nombre:
|
||||
omitidos += 1
|
||||
continue
|
||||
|
||||
if nombre in existing_names:
|
||||
omitidos += 1
|
||||
continue
|
||||
|
||||
cat_nombre = (row.get("categoria") or "").strip()
|
||||
cat_id = cat_map.get(cat_nombre)
|
||||
if not cat_id:
|
||||
errores.append(f"Row {i}: category '{cat_nombre}' not found")
|
||||
continue
|
||||
|
||||
try:
|
||||
fase = int(row.get("fase", "0"))
|
||||
if fase not in (0, 1, 2, 3):
|
||||
raise ValueError
|
||||
except (ValueError, TypeError):
|
||||
errores.append(f"Row {i}: invalid fase '{row.get('fase')}'")
|
||||
continue
|
||||
|
||||
tipo_pago = (row.get("tipoPago") or "").strip()
|
||||
if tipo_pago not in ("unico", "mensual"):
|
||||
errores.append(f"Row {i}: invalid tipoPago '{tipo_pago}'")
|
||||
continue
|
||||
|
||||
try:
|
||||
precio = float(row.get("precioBase", "0"))
|
||||
except (ValueError, TypeError):
|
||||
errores.append(f"Row {i}: invalid precioBase '{row.get('precioBase')}'")
|
||||
continue
|
||||
|
||||
tiempo = (row.get("tiempoEntrega") or "7 - 14 dias").strip()
|
||||
variante = (row.get("variante") or "").strip() or None
|
||||
try:
|
||||
orden = int(row.get("orden", "0"))
|
||||
except (ValueError, TypeError):
|
||||
orden = 0
|
||||
|
||||
entregables_raw = (row.get("entregablesDefault") or "").strip()
|
||||
entregables = [e.strip() for e in entregables_raw.split(";") if e.strip()] if entregables_raw else []
|
||||
|
||||
await db.execute(
|
||||
"""
|
||||
INSERT INTO "ServicioCatalogo"
|
||||
(id, nombre, descripcion, fase, "tipoPago", "precioBase",
|
||||
"tiempoEntrega", "entregablesDefault", "categoriaId",
|
||||
variante, orden, activo, "createdAt", "updatedAt")
|
||||
VALUES (gen_random_uuid(), $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, true, now(), now())
|
||||
""",
|
||||
nombre,
|
||||
(row.get("descripcion") or "").strip() or None,
|
||||
fase,
|
||||
tipo_pago,
|
||||
precio,
|
||||
tiempo,
|
||||
entregables if entregables else None,
|
||||
cat_id,
|
||||
variante,
|
||||
orden,
|
||||
)
|
||||
existing_names.add(nombre)
|
||||
creados += 1
|
||||
|
||||
return {"creados": creados, "omitidos": omitidos, "errores": errores}
|
||||
Reference in New Issue
Block a user