Optimize PDF export layout with compact columns and more space for partner names. Add accounting period display to invoices matching Trial Balance format. Fix date filtering to use local timezone instead of UTC. Update invoice ordering to chronological sequence (DATAACT, NRACT, NUME). **Backend changes:** - Add accounting period query from calendar table - Add currency (valuta) and cont filter support - Change invoice ordering to chronological (DATAACT ASC, NRACT ASC, NUME) - Add accounting_period field to InvoiceListResponse model **Frontend changes:** - Optimize PDF column widths (37% for partner names, compact numeric columns) - Add custom column width support in exportUtils - Fix date conversion from UTC to local timezone (prevents day shift) - Add accounting period display in PDF exports - Enhance E2E test coverage **Cleanup:** - Remove obsolete Trial Balance feature documentation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
145 lines
6.2 KiB
Python
145 lines
6.2 KiB
Python
"""
|
|
API Router pentru facturi
|
|
"""
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from typing import List, Optional
|
|
from datetime import date
|
|
import sys
|
|
import os
|
|
sys.path.append(os.path.join(os.path.dirname(__file__), '../../../../shared'))
|
|
|
|
from auth.dependencies import get_current_user, require_company_access
|
|
from auth.models import CurrentUser
|
|
from ..models.invoice import InvoiceFilter, InvoiceListResponse, InvoiceSummary
|
|
from ..services.invoice_service import InvoiceService
|
|
|
|
router = APIRouter()
|
|
|
|
@router.get("/", response_model=InvoiceListResponse)
|
|
async def get_invoices(
|
|
company: str = Query(description="Codul firmei"),
|
|
partner_type: str = Query("CLIENTI", description="CLIENTI sau FURNIZORI"),
|
|
date_from: Optional[str] = Query(None, description="Data început (YYYY-MM-DD)"),
|
|
date_to: Optional[str] = Query(None, description="Data sfârșit (YYYY-MM-DD)"),
|
|
partner_name: Optional[str] = Query(None, description="Filtru nume partener"),
|
|
cont: Optional[str] = Query(None, description="Filtru după cont contabil"),
|
|
only_unpaid: bool = Query(True, description="Doar facturile neachitate"),
|
|
min_amount: Optional[float] = Query(None, description="Suma minimă"),
|
|
max_amount: Optional[float] = Query(None, description="Suma maximă"),
|
|
page: int = Query(1, ge=1, description="Pagina"),
|
|
page_size: int = Query(50, ge=1, le=10000000, description="Mărimea paginii"),
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""
|
|
Obține lista de facturi pentru o firmă
|
|
|
|
- Necesită autentificare JWT
|
|
- Utilizatorul trebuie să aibă acces la firma specificată
|
|
- Suportă filtrare și paginare
|
|
"""
|
|
try:
|
|
# Verifică dacă utilizatorul are acces la firma specificată
|
|
if company not in current_user.companies:
|
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
|
|
|
# Convertește string-urile de date în obiecte date
|
|
date_from_obj = None
|
|
date_to_obj = None
|
|
|
|
if date_from:
|
|
try:
|
|
date_from_obj = date.fromisoformat(date_from)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="Formatul datei de început este invalid. Folosiți YYYY-MM-DD")
|
|
|
|
if date_to:
|
|
try:
|
|
date_to_obj = date.fromisoformat(date_to)
|
|
except ValueError:
|
|
raise HTTPException(status_code=400, detail="Formatul datei de sfârșit este invalid. Folosiți YYYY-MM-DD")
|
|
|
|
filter_params = InvoiceFilter(
|
|
company=company,
|
|
partner_type=partner_type,
|
|
date_from=date_from_obj,
|
|
date_to=date_to_obj,
|
|
partner_name=partner_name,
|
|
cont=cont,
|
|
only_unpaid=only_unpaid,
|
|
min_amount=min_amount,
|
|
max_amount=max_amount,
|
|
page=page,
|
|
page_size=page_size
|
|
)
|
|
|
|
result = await InvoiceService.get_invoices(filter_params, current_user.username)
|
|
return result
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Eroare la obținerea facturilor: {str(e)}")
|
|
|
|
@router.get("/summary", response_model=InvoiceSummary)
|
|
async def get_invoices_summary(
|
|
company: str = Query(description="Codul firmei"),
|
|
partner_type: str = Query("CLIENTI", description="CLIENTI sau FURNIZORI"),
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""Obține rezumatul facturilor pentru dashboard"""
|
|
try:
|
|
# Verifică dacă utilizatorul are acces la firma specificată
|
|
if company not in current_user.companies:
|
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
|
|
|
result = await InvoiceService.get_invoice_summary(company, partner_type, current_user.username)
|
|
return result
|
|
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Eroare la obținerea rezumatului facturilor: {str(e)}")
|
|
|
|
@router.get("/{invoice_number}")
|
|
async def get_invoice_details(
|
|
invoice_number: str,
|
|
company: str = Query(description="Codul firmei"),
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""Obține detaliile unei facturi specifice"""
|
|
try:
|
|
# Verifică dacă utilizatorul are acces la firma specificată
|
|
if company not in current_user.companies:
|
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
|
|
|
result = await InvoiceService.get_invoice_details(company, invoice_number, current_user.username)
|
|
return result
|
|
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=404, detail=str(e))
|
|
except Exception as e:
|
|
raise HTTPException(status_code=500, detail=f"Eroare la obținerea detaliilor facturii: {str(e)}")
|
|
|
|
@router.get("/export/{format}")
|
|
async def export_invoices(
|
|
format: str,
|
|
company: str = Query(description="Codul firmei"),
|
|
partner_type: str = Query("CLIENTI", description="CLIENTI sau FURNIZORI"),
|
|
date_from: Optional[str] = Query(None, description="Data început (YYYY-MM-DD)"),
|
|
date_to: Optional[str] = Query(None, description="Data sfârșit (YYYY-MM-DD)"),
|
|
partner_name: Optional[str] = Query(None, description="Filtru nume partener"),
|
|
only_unpaid: bool = Query(True, description="Doar facturile neachitate"),
|
|
current_user: CurrentUser = Depends(get_current_user)
|
|
):
|
|
"""
|
|
Export facturi în format specificat (excel, pdf, csv)
|
|
Această funcție va fi implementată în viitor
|
|
"""
|
|
# Verifică dacă utilizatorul are acces la firma specificată
|
|
if company not in current_user.companies:
|
|
raise HTTPException(status_code=403, detail=f"Nu aveți acces la firma {company}")
|
|
|
|
# Verifică formatul
|
|
if format not in ["excel", "pdf", "csv"]:
|
|
raise HTTPException(status_code=400, detail="Format invalid. Formatele suportate sunt: excel, pdf, csv")
|
|
|
|
# Pentru moment, returnează o eroare că funcția nu este implementată
|
|
raise HTTPException(status_code=501, detail=f"Export în format {format} nu este încă implementat") |