from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy.orm import Session from typing import List from datetime import datetime from app.db.session import get_db from app.core.security import get_current_user from app.models.user import User from app.models.client import Client from app.models.invoice import Invoice from app.models.invoice_item import InvoiceItem from app.schemas.invoice import InvoiceCreate, InvoiceUpdate, InvoiceResponse router = APIRouter() def calculate_invoice_totals(items: List[InvoiceItem], tax_rate: float = 0.0): subtotal = sum(item.total_price for item in items) tax_amount = subtotal * (tax_rate / 100) total_amount = subtotal + tax_amount return subtotal, tax_amount, total_amount @router.post("/", response_model=InvoiceResponse, status_code=status.HTTP_201_CREATED) async def create_invoice( invoice: InvoiceCreate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): client = db.query(Client).filter(Client.id == invoice.client_id, Client.owner_id == current_user.id).first() if not client: raise HTTPException(status_code=404, detail="Client not found") existing_invoice = db.query(Invoice).filter(Invoice.invoice_number == invoice.invoice_number).first() if existing_invoice: raise HTTPException(status_code=400, detail="Invoice number already exists") db_invoice = Invoice( **invoice.dict(exclude={'items'}), owner_id=current_user.id ) db.add(db_invoice) db.flush() invoice_items = [] for item_data in invoice.items: total_price = item_data.quantity * item_data.unit_price db_item = InvoiceItem( **item_data.dict(), invoice_id=db_invoice.id, total_price=total_price ) db.add(db_item) invoice_items.append(db_item) db.flush() subtotal, tax_amount, total_amount = calculate_invoice_totals(invoice_items, invoice.tax_rate) db_invoice.subtotal = subtotal db_invoice.tax_amount = tax_amount db_invoice.total_amount = total_amount db.commit() db.refresh(db_invoice) return db_invoice @router.get("/", response_model=List[InvoiceResponse]) async def read_invoices( skip: int = 0, limit: int = 100, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): invoices = db.query(Invoice).filter(Invoice.owner_id == current_user.id).offset(skip).limit(limit).all() return invoices @router.get("/{invoice_id}", response_model=InvoiceResponse) async def read_invoice( invoice_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): invoice = db.query(Invoice).filter(Invoice.id == invoice_id, Invoice.owner_id == current_user.id).first() if invoice is None: raise HTTPException(status_code=404, detail="Invoice not found") return invoice @router.put("/{invoice_id}", response_model=InvoiceResponse) async def update_invoice( invoice_id: int, invoice_update: InvoiceUpdate, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): invoice = db.query(Invoice).filter(Invoice.id == invoice_id, Invoice.owner_id == current_user.id).first() if invoice is None: raise HTTPException(status_code=404, detail="Invoice not found") update_data = invoice_update.dict(exclude_unset=True, exclude={'items'}) if invoice_update.client_id: client = db.query(Client).filter(Client.id == invoice_update.client_id, Client.owner_id == current_user.id).first() if not client: raise HTTPException(status_code=404, detail="Client not found") for field, value in update_data.items(): setattr(invoice, field, value) if invoice_update.items is not None: db.query(InvoiceItem).filter(InvoiceItem.invoice_id == invoice_id).delete() invoice_items = [] for item_data in invoice_update.items: total_price = item_data.quantity * item_data.unit_price db_item = InvoiceItem( **item_data.dict(), invoice_id=invoice.id, total_price=total_price ) db.add(db_item) invoice_items.append(db_item) db.flush() subtotal, tax_amount, total_amount = calculate_invoice_totals(invoice_items, invoice.tax_rate) invoice.subtotal = subtotal invoice.tax_amount = tax_amount invoice.total_amount = total_amount invoice.updated_at = datetime.utcnow() db.commit() db.refresh(invoice) return invoice @router.delete("/{invoice_id}") async def delete_invoice( invoice_id: int, db: Session = Depends(get_db), current_user: User = Depends(get_current_user) ): invoice = db.query(Invoice).filter(Invoice.id == invoice_id, Invoice.owner_id == current_user.id).first() if invoice is None: raise HTTPException(status_code=404, detail="Invoice not found") db.delete(invoice) db.commit() return {"message": "Invoice deleted successfully"}