# Decision Log — Service Auto Prototype **Prototype**: ROA2WEB `feat/service-auto` **Owner**: Marius (ERP patron + sole dev) **Timeline**: 24 săptămâni @ 2-4h/săpt **Last updated**: 2026-04-11 Acest fișier urmărește verdictele pe cele 6 ipoteze din `docs/service-auto/claude-main-design-20260411-rethink.md §"Things this prototype will probe"`. Fiecare verdict e actualizat pe măsură ce testele rulează. **Recomandarea finală** (merge/no-go pentru phase 2) se completează după săpt 24. --- ## Kill Criterion Status Niciun kill criterion nu a fost declanșat până la data de mai sus. Reminder: dacă orice ipoteză eșuează cu un răspuns clar → prototype încheiat cu succes (learning obținut, decision point clar, zero cod irosit). Kill ≠ eșec. --- ## Ipoteza #1 — `python-oracledb` + OUT params **Ipoteză**: `python-oracledb` apelează curat PL/SQL proc cu IN + OUT params (NUMBER + VARCHAR2). **Status**: `CONFIRMED` ✅ **Dovezi**: - Săpt 2 (`poc/async_out_param_probe.py`): `cursor.var(oracledb.NUMBER)` + `cursor.callproc` → `out_var.getvalue()` returnează `42.0` corect. Assert PASSED. Latență callproc: ~1ms după connect. Note: `.getvalue()` returnează `float`, necesită `int()` cast. - Săpt 3 (test #1 + #2 `SP_CREEAZA_COMANDA_PROTOTIP`): SP cu 4 IN + 2 OUT (NUMBER + VARCHAR2) apelat via sync-facade în `oracle_pool.get_connection('mariusm_test')`. Test #1: 5.9ms, OUT p_id_ordl=412, p_nrord='P01-2'. Post-rollback: 0 rows ✅ - Pattern implementat în `backend/modules/service_auto/services/comanda_service.py` **Decizie adoptată**: sync-facade (consistent cu `oracle_pool.py`), nu true-async (`connect_async`). Motivat în `week1-notes.md §Gate Correction 9`. **Implicație pentru template**: pattern `cursor.var(oracledb.NUMBER/STRING)` + `callproc` + `int(out.getvalue())` e reutilizabil direct pentru orice SP cu OUT params. --- ## Ipoteza #2 — `session_callback` nu leak-uiește între requests **Ipoteză**: `session_callback` pentru `CURRENT_SCHEMA` switching nu produce state leak între requests concurente pe același pool. **Status**: `CONFIRMED` ✅ **Dovezi** (2026-04-12, `test_pool_concurrency.py` — 3/3 passed): - `test_two_concurrent_connections_return_correct_results`: 2 asyncio tasks simultane pe același pool, bind values distincte (111, 222) → rezultate corecte fără cross-bleed. - `test_session_callback_runs_per_connection`: `session_callback` înregistrat pe server config → confirmat că se apelează la prima conexiune din pool; nu leaked state inter-connection. - `test_ten_concurrent_queries_no_errors`: 10 tasks concurente pe pool cu `max=5` → `POOL_GETMODE_WAIT` queue-uiește corect excesul; toate 10 completate fără erori. **Nota bene**: pool `mariusm_test` nu folosește `session_callback` (ROA_WEB apelează SP cu schemă explicită `MARIUSM_AUTO.SP_...`). Patch-ul `session_callback` în `oracle_pool.py` e disponibil pentru modulele viitoare care au nevoie de `CURRENT_SCHEMA` switching. --- ## Ipoteza #3 — GRANTS model: EXECUTE ON SP, zero DML direct **Ipoteză**: user `ROA_WEB` cu `GRANT EXECUTE ON SP` + zero `INSERT/UPDATE/DELETE` pe tabele → INSERT direct returnează `ORA-00942`, SP call returnează succes. **Status**: `CONFIRMED` ✅ **Dovezi** (2026-04-12): - `ROA_WEB` creat de DBA cu `GRANT CREATE SESSION` + `GRANT EXECUTE ON SP` + `GRANT SELECT ON AUTO_VMASINICLIENTI` + `GRANT SELECT ON DEV_TIP_DEVIZ`. - `test_grants_integration.py` — 3/3 passed (anterior 3 skipped — fișier lipsă parola): - `test_insert_direct_fails` → `ORA-00942` ✅ (INSERT blocat) - `test_select_direct_fails` → `ORA-00942` ✅ (SELECT pe tabel neautorizat blocat) - `test_exec_sp_succeeds` → SP apelat cu succes ✅ - Verificare live: `AUTO_VMASINICLIENTI` (266 rows) + `DEV_TIP_DEVIZ` (7 rows) accesibile; `NOM_LUCRARI` direct → `ORA-00942` (Oracle ascunde complet obiectul, nu returnează 0 rows). - Pool `mariusm_test` switchat la `ROA_WEB` în `.env` / `.env.prod` / `.env.test`. **Arhitectura multi-tenant documentată** (`grants-audit.md §4`): - Oracle 21c: schema-level grants (23ai) nu există. Proxy auth respins — anulează boundary SP-only. - Soluție: grants per-obiect incluse în deployment scripts. Firmă nouă = 1 script onboarding. Obiect nou în toate schemele = 1 script companion care loopează `V_NOM_FIRME`. --- ## Ipoteza #4 — Diacritice encoding end-to-end **Ipoteză**: `RAISE_APPLICATION_ERROR(-20001, 'mesaj cu ă î ș ț â')` ajunge în Vue ca eroare user-friendly, encoding corect prin tot stack-ul (Oracle → oracledb → FastAPI → Vue). **Status**: `CONFIRMED` ✅ (Oracle→oracledb→FastAPI; Vue pending manual browser test) **Dovezi** (2026-04-12, `test_diacritice_encoding.py` — 2/2 passed): - L1 — Oracle→oracledb: `RAISE_APPLICATION_ERROR(-20001, 'Client invalid: ă î ș ț â Ă Î Ș Ț Â')` trimis via bind variable → `DatabaseError.args[0].message` conține toate diacriticele intact. Set complet testat: `ă î ș ț â Ă Î Ș Ț Â`. NLS chain funcționează fără configurare explicită. - L2 — oracledb→FastAPI: `_handle_oracle_error(e)` → `HTTPException(422, detail)` conține diacriticele, prefix `ORA-20001:` stripped corect. - Vue (manual pending): necesită browser + backend pornit; Toast PrimeVue afișează `detail` din 422 response. Probabilitate eșec mică (Vue nu modifică string-uri JSON). --- ## Ipoteza #5 — DX acceptabil (save→result < 10s) **Ipoteză**: FastAPI hot-reload + Vite dev-server + SSH tunnel Oracle e un DX acceptabil pentru side-work de 2-4h/săpt. **Status**: `CONFIRMED` ✅ **Dovezi** (`week1-notes.md §Conectivitate`): | Operație | Timp | |----------|------| | Sync connect | 33ms | | Async connect | 22ms | | Query | 0.2-3.3ms | | SP callproc (săpt 3) | 5.9ms | - Server Oracle e direct (10.0.20.121:1521) — fără SSH tunnel pe acest server. - uvicorn `--reload` detectează schimbările și reîncarcă în < 1s. - Total "save fișier → văd rezultatul" ≪ 10s. Gate trecut. **Implicație**: ecosistemul nu necesită optimizare înainte de content. DX confirmat. --- ## Ipoteza #6 — Auth multi-server fără modificări shared code **Ipoteză**: flux-ul de auth existent (login → JWT cu `server_id` → `AuthenticationMiddleware`) suportă un server nou fără modificări la `shared/` code. **Status**: `CONFIRMED` ✅ **Dovezi Oracle** (`week3-auth-audit.md`, 2026-04-11): - `pack_drepturi.verificautilizator('MARIUS M', '123')` → `803` ✅ - `V_NOM_FIRME`: 3 firme cu `schema='MARIUSM_AUTO'` (id 110/167/169) ✅ **Dovezi HTTP** (2026-04-12): ``` POST /api/auth/login {"username":"VIZUALIZARE","password":"123","server_id":"mariusm_test"} → JWT companies: ["110","167","169"] ✅ → JWT server_id: "mariusm_test" ✅ → zero modificări în shared/auth/ ✅ GET /api/service-auto/ping (Bearer JWT) → {"result":1,"server":"mariusm_test","latency_ms":0.43} ✅ ``` Nota: MARIUS M are 2FA activat (email configurat) → testat cu VIZUALIZARE (no email, aceleași 3 firme MARIUSM_AUTO). Același cod auth, același JWT structure. --- ## Decizii arhitecturale documentate ### D1 — Sync-facade în loc de true-async (Săpt 1) **Decizie**: `oracle_pool.get_connection()` + `pool.acquire()` (sync) în `async def`. **Motivare**: consistent cu pattern-ul deja proof-ed în `treasury_service.py` prod. True-async (`connect_async`) nu aduce beneficii la latențele măsurate (22-33ms connect). ### D2 — SP_CREEAZA_COMANDA_PROTOTIP nou în loc de reuse pack_auto (Săpt 3) **Decizie**: Opțiunea 3 din design doc — SP minimal cu 2 INSERT-uri (nom_lucrari → dev_ordl) și `RETURNING id_lucrare`, zero dependency pe `pack_sesiune`. **Motivare**: `pack_auto.dev_adauga_lucrare` v2 are 17 params + `pack_sesiune` coupling; SP nou cu RETURNING e idiomul Oracle modern și mai ușor de testat. ### D3 — CONTAFIN_ORACLE în faza A, ROA_WEB în faza B (Săpt 3) **Decizie**: Faza A (săpt 3+) folosește CONTAFIN_ORACLE (DBA role, zero grant work). ROA_WEB creat în faza B când ipoteza #3 e pusă la test. **Motivare**: faza A validează H1+H2+H5+H6 independent de H3. ### D4 — Pool `mariusm_test` separat de `central` (Săpt 3) **Decizie**: ID distinct `mariusm_test` în ORACLE_SERVERS deși e același host/user ca `central`. **Motivare**: pool sizing independent pentru module service_auto + swap atomic la ROA_WEB în faza B (schimbă doar `.env`, zero modificări cod). --- ## Recomandare finală **Decizie: MERGE** ✅ — prototype confirmă că arhitectura e viabilă pentru phase 2. **Data**: 2026-04-12 **Toate 6 ipoteze**: CONFIRMED (H1, H2, H3, H4, H5, H6) ### Verdict pe ipoteze | # | Ipoteză | Verdict | Implicație phase 2 | |---|---|---|---| | H1 | oracledb + OUT params | ✅ CONFIRMED | Pattern `cursor.var()` reutilizabil direct | | H2 | session_callback concurență | ✅ CONFIRMED | Disponibil pentru module multi-schemă viitoare | | H3 | ROA_WEB grants SP-only | ✅ CONFIRMED | Arhitectura grants documentată + automatizată | | H4 | Diacritice encoding | ✅ CONFIRMED | NLS chain OK fără configurare explicită | | H5 | DX < 10s | ✅ CONFIRMED | Ecosistem nu necesită optimizare | | H6 | Auth multi-server zero shared code | ✅ CONFIRMED | Pattern reutilizabil pentru orice server Oracle nou | ### Ce a funcționat mai bine decât așteptat - **Viteza SP**: 5.9ms callproc (față de estimat 50ms) — server local, zero latency Oracle - **Diacritice**: zero configurare NLS necesară — oracledb + Oracle 21c funcționează out-of-box - **Auth reuse**: zero linii modificate în `shared/` — `.env` + `register_server()` suficiente - **ROA_WEB grants**: soluția cu `V_NOM_FIRME` loop e mai curată decât anticipat ### Precauții pentru phase 2 - **session_callback multi-schemă**: testul de concurență a confirmat pool-ul, dar izolarea CURRENT_SCHEMA între două scheme diferite pe același pool nu e testată — tagged connections necesare dacă apare cazul (vezi TODO-phase2.md) - **SP production-grade**: `SP_CREEAZA_COMANDA_PROTOTIP` e minimal — nu setează `id_sucursala`, `id_inspector`, `proc_tvav`, `observatii`. Phase 2 decide: extinde SP-ul sau migrează la `pack_auto.dev_adauga_lucrare` (17 params, coupling cu pack_sesiune) - **Vue manual browser test**: ComandaNoua.vue nu a fost testată în browser (H4 Vue layer) ### Deliverables prototype livrate | Fișier | Ce conține | |---|---| | `docs/service-auto/template-modul-oracle.md` | Rețetă reutilizabilă: 11 secțiuni, de la `.env` la tests | | `docs/service-auto/learnings.md` | 7 patterns consolidate din notele săptămânale | | `docs/service-auto/grants-audit.md` | Arhitectura grants multi-tenant + scripts onboarding | | `backend/modules/service_auto/` | Modul complet: router, service, schemas, tests (22/22) | | `src/modules/service-auto/views/ComandaNoua.vue` | Formular cu date reale din Oracle | | `poc/hello_oracle.py`, `poc/async_out_param_probe.py` | POC-urile de referință | --- ## 2026-04-13 — Skip 3 integration tests obsolete (post bc481da refactor) ### Context După commit `bc481da` (multi-tenant refactor) + `9cd7f35` (phase 3 PACK_AUTO) + migrația `ff_2026_04_13_01_AUTO.sql` (grants Tier 3), 3 integration tests vechi din hypothesis-probing phase 1 au rămas pe un contract depășit. ### Teste marcate cu `@pytest.mark.skip` | Test | Cauză | Acțiune viitoare | |---|---|---| | `test_grants_integration::test_select_direct_fails` | `ff_2026_04_13_01_AUTO.sql` acordă SELECT pe NOM_LUCRARI lui ROA_WEB (necesar pentru `/operatii`). Asserția ORA-00942 e inversă realității actuale. | Șterge sau rescrie cu altă tabelă la care ROA_WEB n-are acces (dacă mai există un astfel de caz). | | `test_grants_integration::test_exec_sp_succeeds` | `SP_CREEAZA_COMANDA_PROTOTIP` nu mai e folosit în producție — `comanda_service.crea_comanda` invocă `PACK_SERII_NUMERE.NUMAR_AUTO_INI` + `PACK_AUTO.DEV_ADAUGA_LUCRARE`. Apelul cu 4 IN params cauzează PLS-00306. | Rescrie ca smoke live pe `PACK_AUTO.DEV_ADAUGA_LUCRARE` (17 params) SAU șterge — acoperit de `test_router_authorization` + live smoke tests. | | `test_comanda_persist::test_comanda_persist_and_reconnect` | Același SP obsolete + durabilitate acum validată prin live smoke `POST /api/service-auto/comenzi` (vezi HANDOFF.md, 2026-04-13). | Rescrie ca e2e peste endpoint-ul HTTP dacă e nevoie, altfel șterge. | ### Impact suite | Înainte | După | |---|---| | 62 passed, 3 failed (PLS-00306 + ORA-00942 inversat) | 62 passed, 3 skipped (0 failed) | ### Ce NU s-a atins - Nu s-a modificat cod production (`services/`, `routers/`, `schemas/`) — nu există regresie reală, doar teste care testau contractul vechi. - `test_insert_direct_fails` rămâne activ și trece — ROA_WEB încă NU are INSERT pe NOM_LUCRARI, asertiunea e corectă. - `test_pool_concurrency` (3/3 passed) și `test_diacritice_encoding` (2/2 passed) au ieșit din lista de "failing" — trec pe setup-ul curent; HANDOFF.md era puțin în urmă.