# Learnings — Service Auto Prototype Consolidare din notele săptămânale. Fiecare pattern e aplicabil la orice modul Oracle nou. --- ## L1 — Sync-facade e suficient (nu trebuie true-async Oracle) `oracledb.create_pool()` (sync) + `pool.acquire()` (sync) în `async def` FastAPI funcționează perfect. True-async (`connect_async`) există și merge (22ms vs 33ms) dar nu aduce beneficii măsurabile la latențele unui server local. Consistența cu `oracle_pool.py` existent > purism async. **Pattern adoptat** (`shared/database/oracle_pool.py`): ```python async with oracle_pool.get_connection('server_id') as conn: with conn.cursor() as cur: cur.execute(query, params) ``` --- ## L2 — `cursor.var()` pentru OUT params, întotdeauna `int()` pe NUMBER `cursor.var(oracledb.NUMBER).getvalue()` returnează `float`, nu `int`. `cursor.var(oracledb.STRING).getvalue()` returnează `str | None`. ```python out_id = cursor.var(oracledb.NUMBER) out_nrord = cursor.var(oracledb.STRING) cursor.callproc("SCHEMA.SP_NUMESC", [..., out_id, out_nrord]) id_result = int(out_id.getvalue()) # float → int obligatoriu nr_result = out_nrord.getvalue() or "" ``` --- ## L3 — RETURNING INTO evită coupling cu pack_sesiune TRG_NOM_LUCRARI_BEFOINS și TRG_DEV_ORDL_BEFOINS populează `pack_sesiune` state (global per-sesiune). Pentru un SP nou fără dependency pe `pack_sesiune`, folosește `RETURNING INTO` local — trigger-ul rulează, dar citești ID-ul tău, nu din state-ul pachetului: ```sql INSERT INTO NOM_LUCRARI (...) VALUES (...) RETURNING id_lucrare INTO v_id_lucrare; INSERT INTO DEV_ORDL (..., id_lucrare) VALUES (..., v_id_lucrare) RETURNING id_ordl INTO p_id_ordl; ``` Zero dependențe, testabil cu ROLLBACK simplu. --- ## L4 — ORA-20xxx range pentru business errors, prefix stripped în handler SP-urile aruncă business errors cu `-20001` … `-20999`. Python handler strippuiește prefix-ul `ORA-2xxxx:` și returnează HTTP 422 cu mesajul curat: ```python if 20001 <= code <= 20999: clean = re.sub(r"^ORA-\d+:\s*", "", raw_message).strip() raise HTTPException(status_code=422, detail=clean) ``` Mesajele cu diacritice (`ă î ș ț â`) trec corect prin NLS chain fără configurare explicită. --- ## L5 — ROA_WEB grants nu scalează per-obiect, dar se automatizează prin V_NOM_FIRME Schema-level grants (Oracle 23ai+) nu există pe 21c. Proxy auth nu e opțiune (anulează boundary SP-only). Soluția: grants per-obiect incluse **în deployment scripts existente**, nu administrate separat. **Firmă nouă** (după `impdp`): 1 script onboarding cu grant-urile curente pentru schema nouă. **Obiect nou în toate schemele**: script companion la migrare care loopează `V_NOM_FIRME`: ```sql BEGIN FOR firm IN (SELECT DISTINCT schema FROM contafin_oracle.v_nom_firme WHERE schema IS NOT NULL) LOOP BEGIN EXECUTE IMMEDIATE 'GRANT EXECUTE ON ' || firm.schema || '.SP_NOUA TO ROA_WEB'; EXCEPTION WHEN OTHERS THEN NULL; END; END LOOP; END; ``` --- ## L6 — Auth reuse zero shared code changes Adăugarea unui server nou în `ORACLE_SERVERS` (`.env`) + `register_server()` în `main.py` e suficientă. JWT conține automat `companies[]` din `V_NOM_FIRME` și `server_id`. `AuthenticationMiddleware` injectează `request.state.user` fără modificări. Testat: `server_id="mariusm_test"` → JWT cu `companies=["110","167","169"]` + `/ping` → `{server: "mariusm_test"}`. --- ## L7 — `Promise.allSettled` pentru lookup-uri paralele în Vue Lookup-urile independente (firme, tip-deviz, masini) se încarcă în paralel. `allSettled` (nu `all`) permite degradare gracefulă: dacă un endpoint pică, celelalte se afișează totuși, iar utilizatorul vede un toast de avertizare specific. ```javascript const [firmeRes, tipuriRes, masiniRes] = await Promise.allSettled([ api.getFirme(), api.getTipDeviz(), api.getMasini() ]) // Fiecare are .status === 'fulfilled' | 'rejected' ```