"""Nomenclature API endpoints.""" from typing import Optional, List, Annotated from fastapi import APIRouter, Depends, HTTPException, Header, Request from sqlalchemy.ext.asyncio import AsyncSession from pydantic import BaseModel from backend.modules.data_entry.db.database import get_session from backend.modules.data_entry.services.sync_service import SyncService # Import auth dependencies import sys from pathlib import Path # Path setup handled by main.py - this is redundant # project_root = Path(__file__).parent.parent.parent.parent.parent # sys.path.insert(0, str(project_root / "shared")) from shared.auth.dependencies import get_current_user from shared.auth.models import CurrentUser router = APIRouter() # ============ Selected Company Dependency ============ async def get_selected_company( current_user: CurrentUser = Depends(get_current_user), x_selected_company: Annotated[Optional[str], Header()] = None ) -> int: """ Get selected company from X-Selected-Company header. Validates user access. Falls back to first company if no header. """ if x_selected_company: try: company_id = int(x_selected_company) except ValueError: raise HTTPException(400, f"Invalid company ID: {x_selected_company}") if str(company_id) in current_user.companies: return company_id raise HTTPException(403, f"Nu aveți acces la firma {company_id}") if current_user.companies: try: return int(current_user.companies[0]) except (ValueError, IndexError): pass raise HTTPException(400, "Nu aveți nicio firmă asignată") SelectedCompany = Annotated[int, Depends(get_selected_company)] # Request/Response Models class SupplierSearchResult(BaseModel): found: bool supplier: Optional[dict] = None source: str # 'synced', 'local', 'not_found' class LocalSupplierCreate(BaseModel): name: str fiscal_code: Optional[str] = None address: Optional[str] = None class LocalSupplierResponse(BaseModel): id: int name: str fiscal_code: Optional[str] address: Optional[str] is_local: bool = True class SyncResult(BaseModel): synced: int errors: int message: str class SupplierOption(BaseModel): id: int oracle_id: Optional[int] = None name: str fiscal_code: Optional[str] source: str # 'synced' or 'local' class CashRegisterOption(BaseModel): id: int oracle_id: int name: str account_code: str register_type: str # Endpoints @router.get("/suppliers/search", response_model=SupplierSearchResult) async def search_supplier( fiscal_code: Optional[str] = None, name: Optional[str] = None, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Search for supplier by fiscal code or name.""" if not fiscal_code and not name: raise HTTPException(status_code=400, detail="Provide fiscal_code or name") cid = company_id or selected_company found, supplier, source = await SyncService.search_supplier( session, cid, fiscal_code, name ) return SupplierSearchResult(found=found, supplier=supplier, source=source) @router.get("/suppliers", response_model=List[SupplierOption]) async def get_suppliers( search: Optional[str] = None, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Get all suppliers (synced + local) for dropdown/autocomplete.""" cid = company_id or selected_company suppliers = await SyncService.get_all_suppliers(session, cid, search) return [ SupplierOption( id=s["id"], oracle_id=s.get("oracle_id"), name=s["name"], fiscal_code=s.get("fiscal_code"), source=s["source"] ) for s in suppliers ] @router.post("/suppliers/local", response_model=LocalSupplierResponse) async def create_local_supplier( data: LocalSupplierCreate, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, current_user: CurrentUser = Depends(get_current_user), ): """Create a local supplier from OCR data.""" cid = company_id or selected_company supplier = await SyncService.create_local_supplier( session, cid, data.name, data.fiscal_code, data.address, current_user.username ) return LocalSupplierResponse( id=supplier.id, name=supplier.name, fiscal_code=supplier.fiscal_code, address=supplier.address, ) @router.get("/cash-registers", response_model=List[CashRegisterOption]) async def get_cash_registers( company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Get all cash registers for a company.""" cid = company_id or selected_company registers = await SyncService.get_all_cash_registers(session, cid) return [ CashRegisterOption( id=r["id"], oracle_id=r["oracle_id"], name=r["name"], account_code=r["account_code"], register_type=r["register_type"] ) for r in registers ] @router.post("/sync/suppliers", response_model=SyncResult) async def sync_suppliers( request: Request, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Manually trigger supplier sync from Oracle.""" cid = company_id or selected_company server_id = getattr(request.state, 'server_id', None) synced, errors = await SyncService.sync_suppliers(session, cid, server_id=server_id) return SyncResult( synced=synced, errors=errors, message=f"Synced {synced} suppliers with {errors} errors" ) @router.post("/sync/cash-registers", response_model=SyncResult) async def sync_cash_registers( request: Request, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Manually trigger cash register sync from Oracle.""" cid = company_id or selected_company server_id = getattr(request.state, 'server_id', None) synced, errors = await SyncService.sync_cash_registers(session, cid, server_id=server_id) return SyncResult( synced=synced, errors=errors, message=f"Synced {synced} cash registers with {errors} errors" ) @router.post("/sync/all", response_model=dict) async def sync_all_nomenclatures( request: Request, company_id: Optional[int] = None, session: AsyncSession = Depends(get_session), selected_company: SelectedCompany = None, ): """Sync all nomenclatures (suppliers + cash registers) from Oracle.""" cid = company_id or selected_company server_id = getattr(request.state, 'server_id', None) # Sync suppliers suppliers_synced, suppliers_errors = await SyncService.sync_suppliers(session, cid, server_id=server_id) # Sync cash registers registers_synced, registers_errors = await SyncService.sync_cash_registers(session, cid, server_id=server_id) return { "suppliers": { "synced": suppliers_synced, "errors": suppliers_errors }, "cash_registers": { "synced": registers_synced, "errors": registers_errors }, "total_synced": suppliers_synced + registers_synced, "total_errors": suppliers_errors + registers_errors, "message": f"Synced {suppliers_synced} suppliers and {registers_synced} cash registers" }