import os, uuid, shutil from fastapi import APIRouter, HTTPException, UploadFile, File, Form from pydantic import BaseModel from typing import Optional from ..database import get_db router = APIRouter(prefix="/api/clients", tags=["clients"]) UPLOAD_DIR = os.environ.get("UPLOAD_DIR", "/uploads") class ClientUpdate(BaseModel): name: Optional[str] = None phone: Optional[str] = None email: Optional[str] = None address: Optional[str] = None notes: Optional[str] = None def client_full(conn, row): c = dict(row) docs = conn.execute("SELECT * FROM client_documents WHERE client_id = ? ORDER BY id", (c["id"],)).fetchall() c["documents"] = [dict(d) for d in docs] rentals = conn.execute(""" SELECT r.*, t.name as tool_name FROM rentals r JOIN tools t ON t.id = r.tool_id WHERE r.client_id = ? ORDER BY r.start_date DESC """, (c["id"],)).fetchall() c["rentals"] = [dict(r) for r in rentals] return c @router.get("") def list_clients(): with get_db() as conn: rows = conn.execute("SELECT * FROM clients ORDER BY name").fetchall() result = [] for r in rows: c = dict(r) c["rental_count"] = conn.execute("SELECT COUNT(*) FROM rentals WHERE client_id = ?", (r["id"],)).fetchone()[0] result.append(c) return result @router.get("/{client_id}") def get_client(client_id: int): with get_db() as conn: row = conn.execute("SELECT * FROM clients WHERE id = ?", (client_id,)).fetchone() if not row: raise HTTPException(404, "Client introuvable") return client_full(conn, row) @router.post("", status_code=201) def create_client( name: str = Form(...), phone: str = Form(""), email: str = Form(""), address: str = Form(""), notes: str = Form("") ): with get_db() as conn: cur = conn.execute( "INSERT INTO clients (name, phone, email, address, notes) VALUES (?,?,?,?,?)", (name.strip(), phone.strip(), email.strip(), address.strip(), notes.strip()) ) row = conn.execute("SELECT * FROM clients WHERE id = ?", (cur.lastrowid,)).fetchone() return client_full(conn, row) @router.put("/{client_id}") def update_client(client_id: int, data: ClientUpdate): with get_db() as conn: if not conn.execute("SELECT id FROM clients WHERE id = ?", (client_id,)).fetchone(): raise HTTPException(404, "Client introuvable") fields, values = [], [] for field, val in data.model_dump(exclude_none=True).items(): fields.append(f"{field} = ?") values.append(val.strip() if isinstance(val, str) else val) if fields: values.append(client_id) conn.execute(f"UPDATE clients SET {', '.join(fields)} WHERE id = ?", values) return client_full(conn, conn.execute("SELECT * FROM clients WHERE id = ?", (client_id,)).fetchone()) @router.delete("/{client_id}", status_code=204) def delete_client(client_id: int): with get_db() as conn: docs = conn.execute("SELECT filename FROM client_documents WHERE client_id = ?", (client_id,)).fetchall() for doc in docs: path = os.path.join(UPLOAD_DIR, "clients", doc["filename"]) if os.path.exists(path): os.remove(path) conn.execute("DELETE FROM clients WHERE id = ?", (client_id,)) @router.post("/{client_id}/documents", status_code=201) async def add_document(client_id: int, file: UploadFile = File(...), label: str = Form("Document")): with get_db() as conn: if not conn.execute("SELECT id FROM clients WHERE id = ?", (client_id,)).fetchone(): raise HTTPException(404, "Client introuvable") ext = os.path.splitext(file.filename)[1].lower() fname = f"{uuid.uuid4().hex}{ext}" path = os.path.join(UPLOAD_DIR, "clients", fname) with open(path, "wb") as f: shutil.copyfileobj(file.file, f) cur = conn.execute("INSERT INTO client_documents (client_id, filename, label) VALUES (?,?,?)", (client_id, fname, label.strip())) return dict(conn.execute("SELECT * FROM client_documents WHERE id = ?", (cur.lastrowid,)).fetchone()) @router.delete("/{client_id}/documents/{doc_id}", status_code=204) def delete_document(client_id: int, doc_id: int): with get_db() as conn: doc = conn.execute("SELECT * FROM client_documents WHERE id = ? AND client_id = ?", (doc_id, client_id)).fetchone() if not doc: raise HTTPException(404, "Document introuvable") path = os.path.join(UPLOAD_DIR, "clients", doc["filename"]) if os.path.exists(path): os.remove(path) conn.execute("DELETE FROM client_documents WHERE id = ?", (doc_id,))