from datetime import UTC, datetime from fastapi import APIRouter, Depends, HTTPException from fastapi.responses import Response from sqlalchemy import select, text from sqlalchemy.ext.asyncio import AsyncSession from app.db.models.invoice import Invoice from app.db.models.order import Order from app.db.models.order_line import OrderLine from app.db.models.tenant import Tenant from app.db.models.vehicle import Vehicle from app.db.session import get_db from app.deps import get_tenant_id from app.orders import schemas, service from app.pdf.service import generate_deviz router = APIRouter() @router.get("") async def list_orders( tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): return await service.list_orders(db, tenant_id) @router.post("") async def create_order( data: schemas.CreateOrderRequest, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): order = await service.create_order( db, tenant_id, data.vehicle_id, data.tip_deviz_id, data.km_intrare, data.observatii, ) return {"id": order.id} @router.get("/{order_id}") async def get_order( order_id: str, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): result = await service.get_order(db, tenant_id, order_id) if not result: raise HTTPException(status_code=404, detail="Order not found") return result @router.post("/{order_id}/lines") async def add_line( order_id: str, data: schemas.AddLineRequest, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): try: line = await service.add_line( db, tenant_id, order_id, data.tip, data.descriere, data.ore, data.pret_ora, data.cantitate, data.pret_unitar, data.um, ) return {"id": line.id} except ValueError as e: raise HTTPException(status_code=422, detail=str(e)) @router.post("/{order_id}/validate") async def validate_order( order_id: str, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): try: order = await service.validate_order(db, tenant_id, order_id) return {"status": order.status} except ValueError as e: raise HTTPException(status_code=422, detail=str(e)) @router.get("/{order_id}/pdf/deviz") async def get_deviz_pdf( order_id: str, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): r = await db.execute( select(Order).where(Order.id == order_id, Order.tenant_id == tenant_id) ) order = r.scalar_one_or_none() if not order: raise HTTPException(status_code=404, detail="Order not found") r = await db.execute(select(Vehicle).where(Vehicle.id == order.vehicle_id)) vehicle = r.scalar_one_or_none() r = await db.execute(select(Tenant).where(Tenant.id == tenant_id)) tenant = r.scalar_one() r = await db.execute( select(OrderLine).where(OrderLine.order_id == order.id) ) lines = r.scalars().all() order_data = { "id": order.id, "data_comanda": order.data_comanda, "nr_auto": vehicle.nr_inmatriculare if vehicle else "", "client_nume": vehicle.client_nume if vehicle else "", "marca_denumire": "", "model_denumire": "", "total_manopera": order.total_manopera, "total_materiale": order.total_materiale, "total_general": order.total_general, } tenant_data = { "nume": tenant.nume, "cui": tenant.cui, "adresa": tenant.adresa, "telefon": tenant.telefon, } lines_data = [ { "tip": l.tip, "descriere": l.descriere, "ore": l.ore, "pret_ora": l.pret_ora, "cantitate": l.cantitate, "pret_unitar": l.pret_unitar, "um": l.um, "total": l.total, } for l in lines ] pdf_bytes = generate_deviz(order_data, lines_data, tenant_data) return Response( content=pdf_bytes, media_type="application/pdf", headers={ "Content-Disposition": f'inline; filename="deviz-{order.id[:8]}.pdf"' }, ) @router.put("/{order_id}") async def update_order( order_id: str, data: schemas.UpdateOrderRequest, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): r = await db.execute( select(Order).where(Order.id == order_id, Order.tenant_id == tenant_id) ) order = r.scalar_one_or_none() if not order: raise HTTPException(status_code=404, detail="Order not found") if order.status != "DRAFT": raise HTTPException(status_code=422, detail="Can only update DRAFT orders") update_data = data.model_dump(exclude_unset=True) for key, value in update_data.items(): setattr(order, key, value) order.updated_at = datetime.now(UTC).isoformat() await db.commit() await db.refresh(order) return { "id": order.id, "vehicle_id": order.vehicle_id, "client_id": order.client_id, "tip_deviz_id": order.tip_deviz_id, "status": order.status, "km_intrare": order.km_intrare, "observatii": order.observatii, "client_nume": order.client_nume, "client_telefon": order.client_telefon, "nr_auto": order.nr_auto, "marca_denumire": order.marca_denumire, "model_denumire": order.model_denumire, } @router.post("/{order_id}/devalidate") async def devalidate_order( order_id: str, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): r = await db.execute( select(Order).where(Order.id == order_id, Order.tenant_id == tenant_id) ) order = r.scalar_one_or_none() if not order: raise HTTPException(status_code=404, detail="Order not found") if order.status != "VALIDAT": raise HTTPException(status_code=422, detail="Can only devalidate VALIDAT orders") # Check no invoice exists for this order r = await db.execute( select(Invoice).where( Invoice.order_id == order_id, Invoice.tenant_id == tenant_id ) ) invoice = r.scalar_one_or_none() if invoice: raise HTTPException( status_code=422, detail="Cannot devalidate order with existing invoice" ) order.status = "DRAFT" order.updated_at = datetime.now(UTC).isoformat() await db.commit() return {"ok": True, "status": "DRAFT"} @router.delete("/{order_id}") async def delete_order( order_id: str, tenant_id: str = Depends(get_tenant_id), db: AsyncSession = Depends(get_db), ): r = await db.execute( select(Order).where(Order.id == order_id, Order.tenant_id == tenant_id) ) order = r.scalar_one_or_none() if not order: raise HTTPException(status_code=404, detail="Order not found") if order.status == "FACTURAT": # Check if invoice exists r = await db.execute( select(Invoice).where( Invoice.order_id == order_id, Invoice.tenant_id == tenant_id ) ) invoice = r.scalar_one_or_none() if invoice: raise HTTPException( status_code=422, detail="Cannot delete order with existing invoice" ) # Delete order lines first await db.execute( text("DELETE FROM order_lines WHERE order_id = :oid AND tenant_id = :tid"), {"oid": order_id, "tid": tenant_id}, ) # Delete the order await db.execute( text("DELETE FROM orders WHERE id = :oid AND tenant_id = :tid"), {"oid": order_id, "tid": tenant_id}, ) await db.commit() return {"ok": True}