import asyncio import logging from fastapi import APIRouter, Depends, Query, BackgroundTasks, Body from typing import Optional from auth.models import TokenPayload from auth.dependencies import require_permission from crm.models import CustomerCreate, CustomerUpdate, CustomerInDB, CustomerListResponse from crm import service, nextcloud from config import settings router = APIRouter(prefix="/api/crm/customers", tags=["crm-customers"]) logger = logging.getLogger(__name__) @router.get("", response_model=CustomerListResponse) async def list_customers( search: Optional[str] = Query(None), tag: Optional[str] = Query(None), sort: Optional[str] = Query(None), _user: TokenPayload = Depends(require_permission("crm", "view")), ): customers = service.list_customers(search=search, tag=tag, sort=sort) if sort == "latest_comm": customers = await service.list_customers_sorted_by_latest_comm(customers) return CustomerListResponse(customers=customers, total=len(customers)) @router.get("/tags", response_model=list[str]) def list_tags( _user: TokenPayload = Depends(require_permission("crm", "view")), ): return service.list_all_tags() @router.get("/{customer_id}", response_model=CustomerInDB) def get_customer( customer_id: str, _user: TokenPayload = Depends(require_permission("crm", "view")), ): return service.get_customer(customer_id) @router.post("", response_model=CustomerInDB, status_code=201) async def create_customer( body: CustomerCreate, background_tasks: BackgroundTasks, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): customer = service.create_customer(body) if settings.nextcloud_url: background_tasks.add_task(_init_nextcloud_folder, customer) return customer async def _init_nextcloud_folder(customer) -> None: try: nc_path = service.get_customer_nc_path(customer) base = f"customers/{nc_path}" for sub in ("media", "documents", "sent", "received"): await nextcloud.ensure_folder(f"{base}/{sub}") await nextcloud.write_info_file(base, customer.name, customer.id) except Exception as e: logger.warning("Nextcloud folder init failed for customer %s: %s", customer.id, e) @router.put("/{customer_id}", response_model=CustomerInDB) def update_customer( customer_id: str, body: CustomerUpdate, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): return service.update_customer(customer_id, body) @router.delete("/{customer_id}", status_code=204) async def delete_customer( customer_id: str, wipe_comms: bool = Query(False), wipe_files: bool = Query(False), wipe_nextcloud: bool = Query(False), _user: TokenPayload = Depends(require_permission("crm", "edit")), ): customer = service.delete_customer(customer_id) nc_path = service.get_customer_nc_path(customer) if wipe_comms or wipe_nextcloud: await service.delete_customer_comms(customer_id) if wipe_files or wipe_nextcloud: await service.delete_customer_media_entries(customer_id) if settings.nextcloud_url: folder = f"customers/{nc_path}" if wipe_nextcloud: try: await nextcloud.delete_file(folder) except Exception as e: logger.warning("Could not delete NC folder for customer %s: %s", customer_id, e) elif wipe_files: stale_folder = f"customers/STALE_{nc_path}" try: await nextcloud.rename_folder(folder, stale_folder) except Exception as e: logger.warning("Could not rename NC folder for customer %s: %s", customer_id, e) @router.post("/{customer_id}/toggle-negotiating", response_model=CustomerInDB) async def toggle_negotiating( customer_id: str, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): return await service.toggle_negotiating(customer_id) @router.post("/{customer_id}/toggle-problem", response_model=CustomerInDB) async def toggle_problem( customer_id: str, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): return await service.toggle_problem(customer_id) @router.get("/{customer_id}/last-comm-direction") async def get_last_comm_direction( customer_id: str, _user: TokenPayload = Depends(require_permission("crm", "view")), ): direction = await service.get_last_comm_direction(customer_id) return {"direction": direction}