""" Lookup data for service_auto forms — tip deviz, masini, firme. All three endpoints are read-only and infrequently changing. """ import time from typing import List, Optional, Tuple import oracledb from fastapi import HTTPException from shared.database.oracle_pool import oracle_pool from ..schemas.comanda import FirmaItem, MasinaClientItem, TipDevizItem from .. import logger # In-memory TTL cache: key → (monotonic_timestamp, value) _cache: dict = {} _TTL_TIP_DEVIZ = 86400 # 24 h — tip deviz changes only via DB migration _TTL_MASINI = 300 # 5 min — vehicle inventory changes regularly def _cache_get(key: str, ttl: float): entry: Optional[Tuple] = _cache.get(key) if entry and (time.monotonic() - entry[0]) < ttl: return entry[1] return None def _cache_set(key: str, value) -> None: _cache[key] = (time.monotonic(), value) class LookupService: @staticmethod async def get_firme(company_ids: List[str]) -> List[FirmaItem]: """ Returns firma names for the company IDs in the user's JWT. Uses 'central' pool (CONTAFIN_ORACLE) to query V_NOM_FIRME. """ if not company_ids: return [] placeholders = ", ".join(f":id{i}" for i in range(len(company_ids))) query = f""" SELECT id_firma, firma, schema, id_mama FROM CONTAFIN_ORACLE.V_NOM_FIRME WHERE id_firma IN ({placeholders}) ORDER BY id_firma """ params = {f"id{i}": int(cid) for i, cid in enumerate(company_ids)} try: async with oracle_pool.get_connection("central") as conn: with conn.cursor() as cur: cur.execute(query, params) rows = cur.fetchall() except oracledb.DatabaseError: logger.error("get_firme Oracle error", exc_info=True) raise HTTPException(status_code=503, detail="Eroare la încărcarea firmelor") return [ FirmaItem(id_firma=r[0], firma=r[1], schema_name=r[2] or "", id_mama=r[3]) for r in rows ] @staticmethod async def get_tip_deviz() -> List[TipDevizItem]: """ Returns all active tip deviz from MARIUSM_AUTO.DEV_TIP_DEVIZ. Cached in-process for 24 h (changes only via DB migration). ROA_WEB has SELECT grant on this view. """ cached = _cache_get("tip_deviz", _TTL_TIP_DEVIZ) if cached is not None: return cached query = """ SELECT id_tip, denumire, inch_validare FROM MARIUSM_AUTO.DEV_TIP_DEVIZ ORDER BY id_tip """ try: async with oracle_pool.get_connection("mariusm_test") as conn: with conn.cursor() as cur: cur.execute(query) rows = cur.fetchall() except oracledb.DatabaseError: logger.error("get_tip_deviz Oracle error", exc_info=True) raise HTTPException(status_code=503, detail="Eroare la încărcarea tipurilor de deviz") result = [ TipDevizItem(id_tip=r[0], denumire=r[1], inch_validare=r[2] or 0) for r in rows ] _cache_set("tip_deviz", result) return result @staticmethod async def get_masini() -> List[MasinaClientItem]: """ Returns active masini from MARIUSM_AUTO.AUTO_VMASINICLIENTI. Cached in-process for 5 min (vehicle inventory changes regularly). ROA_WEB has SELECT grant on this view. Label format: "PARTENER — MARCA MASINA, NRINMAT (ANFABRICATIE)" """ cached = _cache_get("masini", _TTL_MASINI) if cached is not None: return cached query = """ SELECT id_masiniclient, nrinmat, marca, masina, anfabricatie, partener FROM MARIUSM_AUTO.AUTO_VMASINICLIENTI WHERE inactiv = 0 ORDER BY partener, nrinmat """ try: async with oracle_pool.get_connection("mariusm_test") as conn: with conn.cursor() as cur: cur.execute(query) rows = cur.fetchall() except oracledb.DatabaseError: logger.error("get_masini Oracle error", exc_info=True) raise HTTPException(status_code=503, detail="Eroare la încărcarea mașinilor") result = [] for r in rows: id_mc, nrinmat, marca, masina, an, partener = r parts = [] if marca: parts.append(marca) if masina: parts.append(masina) vehicul = " ".join(parts) if parts else "?" an_str = f" ({int(an)})" if an else "" label = f"{partener or '?'} — {vehicul}, {nrinmat or '?'}{an_str}" result.append(MasinaClientItem(id_masiniclient=int(id_mc), label=label)) _cache_set("masini", result) return result