from fastapi import APIRouter, Depends, Query from fastapi.responses import StreamingResponse from typing import Optional import io from auth.dependencies import require_permission from auth.models import TokenPayload from crm.quotation_models import ( NextNumberResponse, QuotationCreate, QuotationInDB, QuotationListResponse, QuotationUpdate, ) from crm import quotations_service as svc router = APIRouter(prefix="/api/crm/quotations", tags=["crm-quotations"]) # IMPORTANT: Static paths must come BEFORE /{id} to avoid route collision in FastAPI @router.get("/next-number", response_model=NextNumberResponse) async def get_next_number( _user: TokenPayload = Depends(require_permission("crm", "view")), ): """Returns the next available quotation number (preview only — does not commit).""" next_num = await svc.get_next_number() return NextNumberResponse(next_number=next_num) @router.get("/customer/{customer_id}", response_model=QuotationListResponse) async def list_quotations_for_customer( customer_id: str, _user: TokenPayload = Depends(require_permission("crm", "view")), ): quotations = await svc.list_quotations(customer_id) return QuotationListResponse(quotations=quotations, total=len(quotations)) @router.get("/{quotation_id}/pdf") async def proxy_quotation_pdf( quotation_id: str, _user: TokenPayload = Depends(require_permission("crm", "view")), ): """Proxy the quotation PDF from Nextcloud to bypass browser cookie restrictions.""" pdf_bytes = await svc.get_quotation_pdf_bytes(quotation_id) return StreamingResponse( io.BytesIO(pdf_bytes), media_type="application/pdf", headers={"Content-Disposition": "inline"}, ) @router.get("/{quotation_id}", response_model=QuotationInDB) async def get_quotation( quotation_id: str, _user: TokenPayload = Depends(require_permission("crm", "view")), ): return await svc.get_quotation(quotation_id) @router.post("", response_model=QuotationInDB, status_code=201) async def create_quotation( body: QuotationCreate, generate_pdf: bool = Query(False), _user: TokenPayload = Depends(require_permission("crm", "edit")), ): """ Create a quotation. Pass ?generate_pdf=true to immediately generate and upload the PDF. """ return await svc.create_quotation(body, generate_pdf=generate_pdf) @router.put("/{quotation_id}", response_model=QuotationInDB) async def update_quotation( quotation_id: str, body: QuotationUpdate, generate_pdf: bool = Query(False), _user: TokenPayload = Depends(require_permission("crm", "edit")), ): """ Update a quotation. Pass ?generate_pdf=true to regenerate the PDF. """ return await svc.update_quotation(quotation_id, body, generate_pdf=generate_pdf) @router.delete("/{quotation_id}", status_code=204) async def delete_quotation( quotation_id: str, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): await svc.delete_quotation(quotation_id) @router.post("/{quotation_id}/regenerate-pdf", response_model=QuotationInDB) async def regenerate_pdf( quotation_id: str, _user: TokenPayload = Depends(require_permission("crm", "edit")), ): """Force PDF regeneration and re-upload to Nextcloud.""" return await svc.regenerate_pdf(quotation_id)