Files
roa2web-service-auto/docs/service-auto/claude-main-design-20260411-rethink.md
Claude Agent 43484db45e docs(service-auto): ground truth audit v3 from MARIUSM_AUTO production
Add real production sources as authoritative reference (supersedes
vfp_roaauto/Scripturi_instalare/packages.sql which is for a different
product — devize producție, not service auto):

- mariusm_ddl_export.sql: 5127 lines DDL from DBMS_METADATA (tables,
  views, triggers) of MARIUSM_AUTO schema
- pack_auto.pck: main business package (17 procedures)
- PACK_FACTURARE.pck, PACK_SESIUNE.pck, PACK_CONTAFIN.pck,
  PACK_COMENZI.pck: dependency packages
- export_ddl.sql: SQL export helper using DBMS_METADATA + DBMS_OUTPUT
  with discovery via ALL_OBJECTS LIKE patterns

Rewrite tabele-service-auto.md v3 (~600 lines) fully grounded in
production sources. Map all flows end-to-end:

- Create (pack_auto.dev_adauga_lucrare) → NOM_LUCRARI + DEV_ORDL
- Normare (dev_adauga_operatie) → DEV_OPER + DEV_OPER_MECANICI
- Validate ops (dev_valideaza_operatii) → DEV_OPER.VALIDAT
- Validate order (dev_valideaza_comanda) → DEV_ORDL.VALIDAT + CALENDAR
- Archive (dev_arhiveaza_comanda) → DEV_ORDL.INCHIS_FORTAT
- Bonuri consum: generic ROA (ointroduceri.prg tip=3) → RUL.id_lucrare
- Facturare: pack_facturare.* + pack_auto.actualizeaza_deviz

Key business semantics confirmed by Marius 2026-04-11:

- DEV_TIP_DEVIZ.inch_validare = 1 means validation alone closes the
  order (no closing note). inch_validare = 0 means additional closing
  required (via invoice for billable types, or 711=332 journal entry
  for internal types). View AUTO_LISTARE_MAN_TOT_COM has the exact
  "closed" condition as (validat=1 AND inch_validare=1) OR
  (facturat=1 AND inch_validare=0).
- Live DEV_TIP_DEVIZ values: 1=POST GARANTIE, 2=GARANTIE, 3=REGIE,
  4=PREGATIRE, 5=REGIE 2, 6=PRODUCTIE, 7=CONSTATARE. REGIE/PRODUCTIE/
  CONSTATARE have inch_validare=1 (internal, closed at validation).
- DEV_OPER for service auto contains only manopera (id_norme). The
  id_articol/id_rul_aux columns exist in DDL for another product that
  shares the table but are not populated by pack_auto.
- Real materials consumed on an order live in RUL tagged by id_lucrare,
  not in DEV_OPER. DEV_ESTIMARI_REP is a separate pre-sale estimate
  (both manopera and materiale lines) given to the client, independent
  of the real manopera (DEV_OPER) and real materials (RUL).

Plan Correction 13 (claude-main-design-20260411-rethink.md):

- Invalidate Scripturi_instalare references
- Confirm NOM_LUCRARI ← DEV_ORDL inheritance pattern
- Confirm pack_sesiune.dev_idLucrare/dev_idOrdl populated by triggers
- Refine prototype SP (Option 3) template based on real schema
- Timeline unchanged, scope wall reconfirmed

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-06-05 09:37:09 +00:00

780 lines
57 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Design: Service Auto Web — Learning + Talent-Risk Hedge (rethink v2)
Generated by /plan-ceo-review on 2026-04-11 (pivot from the scrapped 2026-04-10 doc)
Revised by /plan-ceo-review on 2026-04-11 (v2 after HOLD SCOPE review + outside voice)
Branch: feat/service-auto
Repo: romfast/roa2web
Status: ACTIVE
Owner: Marius (ERP patron + sole dev)
Supersedes: `claude-main-design-20260410-205024.md` — premise invalidated (old doc was a product delivery plan; real intent is learning + capability hedge)
## What this is (and what it is NOT)
**This is:** a side-exploration prototype. Part learning project (Vue 3 + FastAPI async +
oracledb + PrimeVue), part strategic hedge against the "VFP is dead and I can't find
programmers" problem. Target = 1 end-to-end screen with 1 SP write, on the existing test
schema MARIUSM_AUTO at the office server `10.0.20.121/ROA`. Budget ≈ 2-4 hrs/week, ≈48-96
hours total over 24 weeks (doubled from the original 12-week plan after sincere budget
recalibration — see Review Corrections).
**This is NOT:** a product, an MVP, a migration plan, or a commitment to ship anything to
any client. The 4 existing clients stay on VFP + Oracle XE local. Zero migration. Zero
customer-facing delivery. Zero timeline pressure.
**The deliverable is NOT a demo.** The deliverable is **a reusable template for future
Oracle-backed modules + a decision log**. See "Final deliverable" below.
## Strategic driver (the one that matters)
Visual FoxPro is a dead language. Marius cannot hire programmers willing to learn it.
Every year on VFP is another year of compounding talent risk on an ERP that powers 4
long-term clients and generates stable revenue. The only realistic path to "someone else
can maintain this eventually" is moving incrementally to web stacks (Python/Vue) where the
labor pool exists.
**Constraint that makes this hard:** Oracle coupling. The ERP has 25 years of PL/SQL,
cross-schema synonyms, tenant-per-schema architecture. Postgres migration is not 1:1 —
the PL/SQL and schema mechanics don't translate cleanly. Therefore the migration path is
**"stay on Oracle, switch UI + business logic layer to Python/Vue, keep PL/SQL as the
durable layer."** Service auto is the lowest-risk place to probe whether this path works.
**Honest caveat on the hedge thesis:** PL/SQL + Oracle domain knowledge is itself a
scarce skill on the Romanian market. This prototype does NOT solve the hiring pipeline
problem for PL/SQL maintainers. What it solves is the Python+Vue+integration layer — the
UI/API half of the stack — where the labor pool is genuinely larger. The hedge is
"replace the half of the stack where hiring is easier"; it is NOT "fix the hiring problem
in one shot." This distinction matters for phase 2 scoping.
**Success definition for this prototype:** after 24 weeks (or sooner), Marius has
direct evidence (working code, not hand-wavy) that the roa2web monolith can drive Oracle
business logic end-to-end via PL/SQL stored procedures, AND a reusable template
documenting exactly how. If that evidence is positive, the same pattern applies to the
next module. If it's negative, Marius knows this before sinking years into it.
## The prototype — minimum viable experiment
One screen, one SP, one test schema, one developer. Absolute scope wall.
### Target feature
**Screen:** `ComandaNoua.vue` — formular creare comandă nouă service auto. Minimal
fields: tip comandă (select 4 opțiuni), client (dropdown **hardcoded în Vue** — 2-3
clienți test cu 1-2 mașini fiecare), operații solicitate (textarea). Submit → backend →
PL/SQL → insert row → return ID → display "Comanda X creată" via **PrimeVue Toast**.
**SP:** `SP_CREEAZA_COMANDA(p_tip IN, p_client_id IN, p_masina_id IN, p_operatii IN,
p_comanda_id OUT, p_numar OUT)`. **Insert într-un tabel DEDICAT de comenzi** (nume real
în MARIUSM_AUTO — de verificat în VFP înainte de a scrie SP-ul, vezi pregătire săpt 3).
**NU inserează în ACT** (registrul jurnal) — ACT se hit-uiește doar la facturare, care e
NOT IN SCOPE pentru prototype. Validare minimă, return ID + număr.
**Schema:** **MARIUSM_AUTO** — schemă dedicată de test, deja existentă pe serverul de la
biroul Marius (`10.0.20.121/ROA`), care nu e folosită de altcineva. Zero conexiune la
schemele de producție ale celor 4 clienți. Zero copy / impdp necesar — schema există deja.
**User Oracle tehnic:** **`ROA_WEB`** — user nou dedicat app-ului FastAPI, cu grants
explicite per SP (`GRANT EXECUTE ON MARIUSM_AUTO.SP_CREEAZA_COMANDA TO ROA_WEB`), fără
acces direct la tabele. Pattern multi-tenant compatibil — același user va avea grants pe
alte scheme în phase 2 dacă probează.
### Things this prototype will probe (the real learning)
Fiecare din acestea e o ipoteză pe care prototype-ul trebuie să o confirme sau să o
infirme:
| # | Ipoteză | Cum o probez |
|---|--------|-------------|
| 1 | `python-oracledb` async cleanly calls PL/SQL proc cu IN+OUT params | Săpt 1 POC: `poc/hello_async_out.py` cu `oracledb.connect_async()` + `cursor.var(oracledb.NUMBER)` + `await cursor.callproc('test_out', [out])`. **GATE**: dacă merge curat, prototype-ul continuă pe true-async. Dacă NU merge, fallback la pattern-ul sync-facade din `treasury_service.py` (deja proof-ed în prod). Fallback e pre-aprobat, NU blochează săpt 2+. |
| 2 | `session_callback` pentru CURRENT_SCHEMA switching nu leak-uiește între requests concurente | Săpt 4: 2 conexiuni paralele pe **aceeași schemă MARIUSM_AUTO**, ambele setează CURRENT_SCHEMA, rulează CALL-uri distincte, verific cu log-uri că nu se încurcă cursor tag reuse. **Limitare acceptată conștient:** testul pe 1 schemă nu dovedește 100% izolarea la multi-schemă — pentru asta ar trebui o a doua schemă test (ex: copie CLEVER), care e în afara budget-ului. Probă de primitive, nu de multi-schemă. |
| 3 | GRANTS de tip "EXECUTE ON SP, zero INSERT/UPDATE/DELETE pe tabele" țin în practică | Săpt 3: user `ROA_WEB` încearcă `INSERT INTO MARIUSM_AUTO.ACT``ORA-00942` sau `ORA-01031`; `SELECT * FROM MARIUSM_AUTO.ACT``ORA-00942`; `EXEC MARIUSM_AUTO.SP_CREEAZA_COMANDA(...)` → success. Un test automat pytest-marcat `integration` care rulează local. |
| 4 | `RAISE_APPLICATION_ERROR(-20001, 'mesaj cu diacritice ă î ș ț â')` ajunge în Vue ca eroare user-friendly, encoding corect | Săpt 11-12: SP aruncă eroare (client inexistent, tip invalid, operatii gol), serviciul traduce `oracledb.DatabaseError.code` la HTTP status (20001-20999 → 422 business, 12541/12170 → 503 infra, rest → 500), Vue afișează PrimeVue Toast cu mesajul. Obligatoriu test pe un mesaj cu diacritice pentru a proba `NLS_LANG` / encoding chain end-to-end. |
| 5 | FastAPI hot-reload + Vite dev-server + SSH tunnel Oracle e un DX acceptabil pentru side-work de 2-4h/săpt | Săpt 1: măsor timpul de la "save file" la "văd rezultatul" — dacă e > 10s, fix ecosistem înainte de content. **Documentat** în `docs/service-auto/week1-notes.md` cu numere concrete. |
| 6 | **Flux-ul de auth multi-server existent** (login → `email_server_cache``CONTAFIN_ORACLE.UTILIZATORI` → JWT cu `server_id``AuthenticationMiddleware``request.state.server_id`) suportă un server nou **fără modificări la shared code** | Săpt 3: (a) adaugă `MARIUSM_AUTO` la `ORACLE_SERVERS` în `backend/.env`; (b) `INSERT INTO CONTAFIN_ORACLE.UTILIZATORI (username, server_id, ...)` pentru Marius; (c) log in prin UI, verifică că server selector arată `mariusm_test`; (d) după login, inspectează JWT payload în browser devtools — confirmă `server_id='mariusm_test'`; (e) verifică ce returnează `auth_service.get_user_companies('MARIUS M', 'mariusm_test')` — dacă MARIUSM_AUTO nu are `V_NOM_FIRME`, vezi dacă login-ul tolerează `companies=[]` sau dacă trebuie un fake. Documentat în `week3-auth-audit.md`. **Gate**: dacă auth-ul cere modificări la shared code pentru a accepta un server non-CONTAFIN, scope creep → replanning sau kill criterion. |
Dacă oricare din 1-6 eșuează cu un răspuns clar, **prototype-ul s-a încheiat cu succes**
learning obținut, decision point clar, zero cod irosit care trebuie să supraviețuiască.
## Scope wall — what is explicitly NOT in this prototype
Toate aceste subiecte aparțin doc-ului scrapped sau sunt defer-uri la TODO-phase2. Niciunul nu intră aici:
- ❌ Cei 4 clienți existenți, vreodată. Ei rămân pe VFP + XE local. Zero atingere.
- ❌ Migrare scheme, SSH tunnel VFP→Oracle central, decomisionare XE local
- ❌ eFactura / ANAF / SPV upload — irelevant fără producție
- ❌ Feature flags per-tenant, `FEATURE_FLAGS` table
-`SP_BON_CONSUM` (cross-schema synonyms), `SP_VALIDEAZA_COMANDA`, `SP_EMITE_FACTURA` — toate deferred
-**Insert în ACT la creare comandă** — ACT e registrul jurnal, se hit-uiește la facturare, care e NOT IN SCOPE
-`reconcile.py`, invariant checking, parallel testing cu VFP
- ❌ Runbook rollback, monthly restore drill, uptime monitoring, Telegram alerts
- ❌ 5 ecrane MVP (doar 1 — absolute wall)
- ❌ Mobile, responsive, iPad, tablet — desktop-only, viewport-whatever-is-convenient (accesibilitate: PrimeVue defaults gestionează keyboard tab order, ARIA labels, focus ring — **zero work custom**; nu e "ignorat", e "inherited free")
- ❌ Playwright e2e, smoke parity pentru SPs
- ❌ Endpoint `/clienti` sau `/masini` pentru populare dropdown — hardcoded în Vue
- ❌ Onboarding client nou, referral pipeline, assignment cu telefoane
- ❌ Phase 2, Phase 3, Phase 4 — menționate doar în decision-log.md la finalul prototype-ului
- ❌ SLA, RPO, RTO, warm standby, backup discipline
-`X-Idempotency-Key`, double-submit protection, retry policy
- ❌ A doua schemă de test (ex: copie CLEVER) pentru probă multi-schemă — costă prea mult budget
Dacă în timpul construcției una dintre aceste tentații începe să pară "doar 30 min în
plus", răspunsul e **scriu o notă, nu scriu cod**. Scope wall ține. Note de tipul
"probabil o să am nevoie de X pentru phase 2" merg în `docs/service-auto/TODO-phase2.md`,
nu în codul prototype-ului.
## Review corrections (from /plan-ceo-review session, 2026-04-11)
Această secțiune documentează ce s-a corectat față de prima versiune a planului, pentru a
fi transparent despre gândirea care a dus la planul final.
### Correction 1 — Nu se creează `MarciusLabPool` nou (+ patch minim la `oracle_pool.py`)
**Prima versiune** spunea: "modifică `OracleMultiPool` sau adaugă wrapper" și "creează NEW
minimal helper `MarciusLabPool`". **Realitate:** `OracleMultiPool` la
`shared/database/oracle_pool.py` are deja suport multi-server prin `_pools: Dict[server_id, ConnectionPool]`.
MARIUSM_AUTO se adaugă ca **server nou** prin `register_server('mariusm_test', host='10.0.20.121', port=1521, user='ROA_WEB', password='...', service_name='ROA')` — zero pool nou, zero clasă nouă.
**Însă NU e chiar "zero refactor".** `register_server` (linii 55-83) acceptă `**kwargs` dar le
ignoră — nu le stochează în `_pool_configs`. `_get_or_create_pool` (linii 108-117)
construiește `pool_params` dintr-un set fix de chei. **Pentru a activa `session_callback`
(ipoteza #2), trebuie un patch backward-compatible de ~8 linii la `shared/database/oracle_pool.py`:**
```python
def register_server(self, ..., session_callback=None, **kwargs):
self._pool_configs[server_id] = {
...
'session_callback': session_callback,
}
# în _get_or_create_pool, după pool_params:
if config.get('session_callback'):
pool_params['session_callback'] = config['session_callback']
```
Zero caller-i existenți folosesc `session_callback`, deci patch-ul e backward-compatible.
**Planificat explicit în săpt 5** (vezi timeline) — nu e "scope creep", e un task minim
necesar și conștient de scope wall-ul shared code.
### Correction 2 — Pattern sync-in-async-facade e deja proof-ed în prod
**Prima versiune** trata ipoteza #1 ("oracledb async + OUT params") ca unknown. **Realitate:**
`backend/modules/reports/services/treasury_service.py` deja apelează SPs cu OUT params de
tip `oracledb.CURSOR` și `oracledb.NUMBER` în producție, folosind pattern-ul
sync-in-async-facade (`async with oracle_pool.get_connection(...)`, cursor sync, fără
`await` pe execute/callproc). **Consecință:** ipoteza #1 e reîncadrată ca probă a
pattern-ului `oracledb.connect_async()` **true-async** — nou, neprobeat în codebase.
Săpt 1 e **gate** (vezi mai sus): dacă true-async merge → adoptă; dacă nu → fallback la
sync-facade care e deja dovedit.
### Correction 3 — SP nu inserează în ACT
**Prima versiune** spunea "Insert în ACT, validare minimă". **Realitate:** ACT e registrul
jurnal contabil (double-entry bookkeeping, clasa 6/7 — vezi
`.claude/rules/financial-indicators.md`). O comandă de service auto la CREARE aterizează
într-un **tabel dedicat** (ex: `COMENZI`, `COMD_SA`, nume real de verificat în VFP), NU
în ACT. ACT se hit-uiește la **facturare**, care e NOT IN SCOPE. SP-ul corect face INSERT
în tabela dedicată de comenzi.
**Task în săpt 3 (preparare):** înainte de a scrie SP_CREEAZA_COMANDA, clonează repo-ul
gitea al sursei VFP (`gitea.romfast.ro/romfast/vfp_roaauto` — importat 2026-04-11 ca
ground truth pentru acest prototype), `grep` după fluxul "creează comandă nouă service
auto" în sursele .PRG / .SCX, identifică tabelele atinse și SP-urile / UPDATE-urile
existente, documentează în `docs/service-auto/tabele-service-auto.md`. **Nu mai e nevoie
să deschizi VFP-ul producție** — gitea e strict mai sigur (zero risk de modificare
accidentală) și mai rapid (text search vs IDE navigation).
**Audit preliminar deja scris 2026-04-11**: `docs/service-auto/tabele-service-auto.md` e
un **DRAFT** generat de Claude pe baza clonei gitea. Conține (a) schema reală `comenzi` /
`vcomenzi` cu ~28 coloane, (b) SP-ul real de creare **`pack_auto.dev_adauga_lucrare`**
(17 params IN, 1 OUT — **substanțial diferit** de ipoteza inițială a planului), (c) enum
real `tip_comanda` (ID-uri 1-7 cu prefixe G/R/P/PR/C), (d) label-urile Romanian pentru
câmpuri, (e) lista funcțiilor `pack_auto.*` și `pack_comenzi.*` relevante, (f) query-uri
SQL de verificat în săpt 3 împotriva schemei reale. Task-ul săpt 3 e **să confirme** draft-ul
împotriva bazei reale, nu să-l scrie de la zero. Vezi **Correction 12** pentru implicațiile
strategice ale descoperirii SP-ului existent.
### Correction 12 — Tabela reală e `DEV_ORDL`, SP-ul are două versiuni, și există un `pack_sesiune` coupling
**Prima versiune a planului** presupunea că:
- Tabela e `comenzi` (fizică)
- SP-ul `SP_CREEAZA_COMANDA` va fi nou, simplu, cu ~4 IN + 2 OUT
- Nu există dependențe ascunse
**Realitate (2026-04-11 post-audit pe gitea VFP + scripturi instalare):**
**(a) Inheritance pattern: comandă în `nom_lucrari` + extensie auto în `dev_ordl`**
(confirmat de Marius 2026-04-11). `nom_lucrari` e tabela parent din ROA ERP generic —
conține header-ul "comenzii" (nrord), vizibil în ACT (registrul jurnal) și RUL (rulaje
gestiune) **doar când comanda e referențiată la facturare**. `dev_ordl` e extensia
ROAAUTO (id_lucrare FK → nom_lucrari.id_lucrare) care conține toate detaliile service auto
(mașină, km, termen, tip deviz, etc.). Crearea unei comenzi face INSERT în **AMBELE tabele**,
în ordine FK parent → child. **Crearea nu touch-uiește ACT/RUL** — Correction 3 rămâne validă.
Ce vedem ca `comenzi` / `vcomenzi` e un VIEW denormalizat care JOIN-uiește cele două tabele
+ lookup-uri (`dev_tip_deviz`, `dev_masiniclienti`, `parteneri`).
**(b) Tabela enum pentru `tip_comanda` este `DEV_TIP_DEVIZ`** — cu schema:
```sql
DEV_TIP_DEVIZ (ID_TIP NUMBER(5) PK, DENUMIRE VARCHAR2(50), STERS NUMBER(1))
```
Seed install (`initializari.sql`): `{1:'POST GARANTIE', 2:'GARANTIE', 3:'REGIE', 4:'PREGATIRE', 5:'REGIE 2'}`.
IDs 6 și 7 adăugate în producție: `{6:'Productie' (prefix PR), 7:'Constatare' (prefix C)}`
(confirmat de Marius 2026-04-11).
**(c) Există DOUĂ versiuni ale `dev_adauga_lucrare`**:
- **v1 INSTALL** — `pack_devize.dev_adauga_lucrare(12 IN + 1 OUT)` — body vizibil în
`Scripturi_instalare/packages.sql:182-212`
- **v2 PRODUCȚIE** — `pack_auto.dev_adauga_lucrare(17 IN + 1 OUT)` — apelat din VFP
(`Programe/oproceduri_devize.prg:147-148`), **body NU e în sursă** (pachet adăugat
post-install, trăiește doar în DB-ul lui Marius). 5 params noi: `nr_dosar`, `ore_fct`,
`operatii_csv`, `observatii`, `defectiuni`, `id_part_ref`.
**(d) `pack_sesiune.dev_idLucrare` NU e un bug, e convenție ROA ERP** — pattern-ul e:
trigger `TRG_NOM_LUCRARI_BEFOINS` populează `pack_sesiune.dev_idLucrare := :new.id_lucrare`
după INSERT-ul pe `nom_lucrari`; apoi INSERT-ul pe `dev_ordl` citește acel session state.
Confirmat prin pattern-ul identic la `dev_ordl` (`triggere.sql:1165-1172`:
`SEQ_DEV_ORDL.NEXTVAL INTO :new.id_ordl` + `pack_sesiune.dev_idOrdl := :new.id_ordl`) și
la `dev_oper` (`triggere.sql:928`).
**Pentru roa2web: bypass trivial** — folosim `RETURNING id_lucrare INTO v_local` după
INSERT-ul pe `nom_lucrari`. Trigger-ul încă rulează și populează session state (irelevant
pentru noi), iar noi citim `id_lucrare` local prin RETURNING. Standard Oracle. Zero dependency.
**(e) Flux-ul real** — v1 body face 3 operații:
1. `INSERT INTO NOM_LUCRARI (NRORD, ID_MOD) VALUES (nr_com, 1200)`**parent** (header
generic) — ESENȚIAL
2. `INSERT INTO DEV_ORDL (..., id_lucrare, ...) RETURNING id_ordl`**child** (extensia
auto, FK la parent) — ESENȚIAL
3. `UPDATE DEV_MASINICLIENTI SET kmint=... WHERE id_masiniclient=...` — sync odometru —
OPȚIONAL (skip pentru prototype)
Deci nu e "flux cu side effects" — e un pattern de inheritance normal (parent + child),
plus o sync colaterală opțională.
### Trei opțiuni strategice (decise în săpt 3/4 după extragerea body-ului real)
| # | Strategie | Pro | Contra |
|---|---|---|---|
| **1** | Reuse `pack_auto.dev_adauga_lucrare` v2 direct | Hedge max — dovedește reuse pachet producție cu side-effects | Depinde de `pack_sesiune`; risc sabotaj la primul call |
| **2** | Reuse `pack_devize.dev_adauga_lucrare` v1 | Body vizibil în sursă, 5 params mai puțin | Același `pack_sesiune` bug, tot 12 params |
| **3** | **SP nou minimal** `SP_CREEAZA_COMANDA_PROTOTIP` cu INSERT direct în `dev_ordl` + `id_lucrare=0` + skip side-effects | Control total, zero dependențe, ~20 linii PL/SQL, testabil clean, probează ipoteza #1 perfect | Bypass-uie business logic (dar asta oricum e phase 2) |
**Recomandare actualizată: OPȚIUNEA 3** (SP nou minimal cu două INSERT-uri parent+child).
Motivul: deși `pack_sesiune` s-a dovedit a nu fi bug ci convenție, reuse-ul direct al
v1/v2 aduce alte complicații (17 params pentru v2, business logic care ar trebui
reimplementată sau bypass-ată, etc.). **Opțiunea 3 cu cele două INSERT-uri în ordine**
(nom_lucrari → dev_ordl, cu `RETURNING id_lucrare`) e **exact pattern-ul corect pentru
inheritance**, nu un bypass urât. E idiomul Oracle modern (post-9i când RETURNING a devenit
standard), iar pack-urile legacy foloseau `pack_sesiune` doar pentru că n-aveau RETURNING
când au fost scrise inițial.
**Rezultatul**: prototype-ul dovedește ipoteza #1 pe un SP realist care atinge **două**
tabele în ordine FK corectă (nu unul banal `test_out`), iar template deliverable-ul din
săpt 21-22 documentează exact pattern-ul de inheritance parent+child cu RETURNING —
**direct reutilizabil** pentru orice alt modul ROAAUTO sau ROA.
**SP minimal propus (Opțiunea 3):** vezi §4.3 din `docs/service-auto/tabele-service-auto.md`
pentru corpul complet (~40 linii PL/SQL): două INSERT-uri parent (nom_lucrari) + child
(dev_ordl) cu `RETURNING id_lucrare` între ele, zero dependency pe `pack_sesiune`.
### Impact asupra timeline
- **Săpt 3** — task-ul își schimbă scopul: confirmă existența `pack_devize`, `pack_auto`,
`pack_sesiune` în MARIUSM_AUTO + citește body-ul real al `pack_auto.dev_adauga_lucrare`
pentru decizie finală (opțiunea 1 vs 3). Query din §4.2 tabele-service-auto.md.
- **Săpt 7-8** — "scrie SP nou" devine "scrie SP_CREEAZA_COMANDA_PROTOTIP" (opțiunea 3).
Simplu: INSERT direct în `dev_ordl` + `RETURNING id_ordl`.
- **Ipoteza #1** rămâne la fel de puternică — testezi că Python+oracledb cu OUT params
apelează un SP real care face INSERT + RETURNING, nu un `test_out(p OUT NUMBER)` trivial.
- **Ipoteza #2** (`session_callback`) devine **mai relevantă ca niciodată**`pack_sesiune`
e exact genul de state care ar putea fi inițializat prin session_callback pattern. Săpt 5
task-ul `session_callback` devine și o investigație preliminară pentru phase 2.
**Scope wall ține** — tot 1 ecran, tot 1 SP, tot 1 schema test, tot write-only, deliverable =
template + decision-log. Ce se schimbă: **țintim o tabelă reală (`dev_ordl`), folosim un enum
real (`dev_tip_deviz`), și documentăm un path clar spre reuse-ul pachet-urilor legacy pentru
phase 2**. Hedge-ul devine mai credibil, nu mai slab.
### Correction 13 — Ground truth complet din MARIUSM_AUTO (2026-04-11 later)
**Ce s-a schimbat:** Marius a furnizat versiunile reale din producție:
- `docs/service-auto/mariusm_ddl_export.sql` (5127 linii DDL: tabele + views + triggere)
- `docs/service-auto/pack_auto.pck`, `PACK_FACTURARE.pck`, `PACK_SESIUNE.pck`,
`PACK_CONTAFIN.pck`, `PACK_COMENZI.pck` — body-urile reale
**Ce s-a invalidat:**
- **`vfp_roaauto/Scripturi_instalare/packages.sql` = alt produs** ("devize producție"), NU
service auto. Toate referințele v2 la `pack_devize.dev_adauga_lucrare` erau la un pachet
care nu există în MARIUSM_AUTO.
- **`VCOMENZI` din MARIUSM_AUTO nu e pentru service auto** — e pentru ROA ERP base
(vânzări/contracte generice), cu coloane `id_codclient`/`interna`/`id_masina` care
nu aparțin flux-ului auto.
- **`dev_distribuie_timp_n`** are semnătură nouă: `(v_luna, v_an)` nu `(v_gcs, v_filtru)`.
**Ce s-a reconfirmat ca fiind corect:**
- Pattern-ul de inheritance `NOM_LUCRARI` (parent) + `DEV_ORDL` (child, FK `id_lucrare`)
- `TRG_NOM_LUCRARI_BEFOINS` populează `pack_sesiune.dev_idLucrare` din `SEQ_NOM_LUCRARI.NEXTVAL`
- `TRG_DEV_ORDL_BEFOINS` populează `pack_sesiune.dev_idOrdl` din `SEQ_DEV_ORDL.NEXTVAL`
- Bypass `pack_sesiune` prin `RETURNING id_lucrare INTO v_local` e **trivial și safe**
(trigger-ul rulează oricum, noi îl ignorăm)
**Descoperiri NOI critice:**
1. **`DEV_ORDL` are 37 coloane în producție** (nu 28-30 cum estima v2), cu
`PROC_TVAV`, `SOLICITARI_CLIENT CLOB`, `OBSERVATII`, `DEFECTIUNI`, `NR_DOSAR`,
`ID_PART` (FK la NOM_PARTENERI — client direct, nu doar prin mașină), `ID_AGENT`,
`ID_PART_REF`, `ORE_FUNCTIONARE`, `IN_LUCRU`, `COADA_DEVIZ`, `ID_UTIL_INCHIS`,
`DATAORAINCHIS`, `DATA_IN_LUCRU`, `DATAORAINLUCRU`, `FACTUREZMIX`, `DATA_CURS`,
`ID_VALUTA_DEVIZ`, `INCHIS_FORTAT`. FK constraints ENABLE:
`FK_DEV_ORDL_001..006` pe `DEV_NOM_INSPECTORI`, `NOM_LUCRARI`, `NOM_PARTENERI` ×3,
`NOM_VALUTE`.
2. **`DEV_OPER` e polimorfic**: poate fi **manoperă** (`id_norme` non-NULL) SAU **linie
material** (`id_articol` non-NULL + `id_rul_aux` FK la mișcarea de stoc). Materialele
pe comandă nu au tabela `DEV_MAT` separată — sunt linii în `DEV_OPER` cu tip diferit.
3. **`DEV_ESTIMARI_REP`** e tabelă nouă (pre-sale estimate) — linii cu `id_lucrare` FK,
fiecare linie e fie manoperă fie material. `pack_auto.adauga_manopera_de` /
`adauga_material_de` scriu aici. View `AUTO_VESTIMARI_REP` cu `pack_sesiune.calculeaza_*`
pentru prețuri. **Out of scope** pentru prototype.
4. **`DEV_TIP_DEVIZ.INCH_VALIDARE`** (NUMBER(1) default 1) e flag-ul care decide între
**închidere prin validare** (`dev_valideaza_comanda`) sau **închidere prin arhivare**
(`dev_arhiveaza_comanda`). Citit prin `pack_auto.getOptiuneInchidere(id_tip)`.
5. **`actualizeaza_deviz` (pack_auto)** e procedura care replace-uiește vechiul
`dev_completeaza_rul`. Face 3 UPDATE-uri într-o tranzacție: `DEV_ORDL.PROC_TVAV`,
`RUL.ID_FACT` (pentru toate materialele consumate), și — condiționat de `id_set` în
(31003-31011) — `NOM_LUCRARI.ID_FACT`.
6. **Bonuri consum = flux generic ROA, nu `pack_auto`**`ointroduceri.prg tip=3` scrie
direct în `RUL` + `RUL_AUXILIAR` cu `ID_LUCRARE` tag. `DEV_OPER` capturează liniile
prin `id_rul_aux` FK. **Out of scope.**
7. **View-urile UI**: `AUTO_NORMARE_COMENZI`, `AUTO_VALIDARE_COMENZI`, `AUTO_ORDL_FACTURARE`,
`AUTO_COMENZI_VALIDATE` (cu time-aware validat flag prin `pack_sesiune.getluna()`),
`AUTO_VORDL_MAN`, `AUTO_VORDL_MAT` (citește din `MV_ORDL_MAT` materialized view),
`AUTO_VESTIMARI_REP`.
8. **`pack_audit.verifica_val`** — trigger-ele `_BEFOUPD` pe `NOM_LUCRARI`, `DEV_ORDL`,
`DEV_OPER`, `DEV_OPER_MECANICI` apelează această procedură pentru fiecare câmp modificat
→ audit trail automat pentru TOATE UPDATE-urile. Triggerele `_BEFOINS` **nu** apelează
pack_audit — deci prototype-ul care doar inserează **nu atinge pack_audit**.
**Audit complet:** `docs/service-auto/tabele-service-auto.md` a fost rescris complet
(v3, ~500 linii) cu referințe directe la DDL + pack_auto + pack_facturare real. Conține:
- Diagrama ierarhiei `NOM_LUCRARI → DEV_ORDL → DEV_OPER → DEV_OPER_MECANICI`
- Schema reală `DEV_ORDL` (37 coloane) + `DEV_OPER` (polimorfic) + `DEV_ESTIMARI_REP`
- Map procedure → tabele pentru întreg `pack_auto` (17 proceduri/funcții)
- Flux VFP real pentru create, normare, validare operații, validare comandă, arhivare,
facturare (cu referințe la `pack_facturare.initializeaza_date_factura`,
`adauga_articol_factura_deviz`, `scrie_in_vanzari`)
- List complete de view-uri UI cu rolul fiecăruia
- SP minimal propus pentru prototype (Opțiunea 3, rafinată) — 30 linii PL/SQL, zero
dependențe pe `pack_contafin`/`STRINGAGG`/`pack_sesiune`, doar două INSERT-uri cu
RETURNING și un duplicate-check
**Scope wall reconfirmat**: prototype-ul rămâne la **creare comandă simplă**. Normare,
validare operații, validare comandă, arhivare, facturare, bonuri consum, estimare =
**phase 2+**. Fluxul complet e documentat ca referință viitoare, nu ca commitment.
**Impact asupra timeline:** **zero** — task-urile săpt 1-24 rămân identice, doar că săpt 7-8
("scrie SP_CREEAZA_COMANDA") are acum templatul PL/SQL exact definitivat în tabele-service-auto.md
§12.2, gata de copy-paste + commit.
**Impact asupra ipotezelor:** niciuna invalidată. Ipoteza #1 rămâne centrală — probă că
Python+oracledb apelează PL/SQL cu OUT params pe un INSERT dual cu RETURNING. Ipoteza #2
(`session_callback`) rămâne independentă. Ipoteza #3 (grants) este și mai **ușor de probat**
acum că știm exact cele două tabele atinse (`NOM_LUCRARI`, `DEV_ORDL`) și putem scrie
testul negativ cu nume fixe.
### Correction 4 — `connection.commit()` e explicit în pattern
**Prima versiune** nu menționa commit. **Realitate:** `oracledb` driver are autocommit OFF
default. Fără `connection.commit()` după `cursor.callproc`, SP-ul rulează dar datele NU se
salvează — **silent failure clasică**. Pattern-ul corect e:
```python
async with oracle_pool.get_connection('mariusm_test') as conn:
with conn.cursor() as cursor:
out_id = cursor.var(oracledb.NUMBER)
out_numar = cursor.var(oracledb.STRING, size=50)
cursor.callproc('SP_CREEAZA_COMANDA', [..., out_id, out_numar])
conn.commit() # <-- CRITICAL
```
**Test obligatoriu** în săpt 5-6: după `callproc + commit`, închide conexiunea, deschide
alta, `SELECT ... WHERE id = :id` — confirmă că row-ul persistă. Dacă nu, commit-ul e
problemă.
### Correction 5 — Error code classification e explicită în plan
**Prima versiune** zicea doar "ORA-20001 → toast". **Realitate:** există multe categorii
de erori Oracle, iar trducerea la HTTP trebuie să fie range-based:
| Cod Oracle | HTTP | Ce vede user-ul |
|---|---|---|
| 20001 — 20999 (`RAISE_APPLICATION_ERROR`) | 422 | Mesajul din SP, curat de prefix |
| 12541, 12170, 12154 (connection / listener errors) | 503 | "Serviciul bazei de date e temporar indisponibil" |
| 01017 (invalid credentials) | 500 + critical log | "Eroare de configurare. Contactați administratorul." |
| 00942 (table or view not exist — grants leak) | 500 + critical log | "Eroare internă. Contactați administratorul." |
| Orice alt cod | 500 + full log | "Eroare internă neașteptată" |
Săpt 11-12: scrie service-layer-ul cu `try/except oracledb.DatabaseError` + clasificare
pe range. Test cu diacritice în mesaj.
### Correction 6 — Logging persistent per-modul
**Prima versiune** nu menționa logging. **Realitate:** `backend/main.py:57` folosește
`logging.basicConfig` cu output doar în stdout. Dacă în săpt 5 vrei să recitești
log-urile din săpt 2, nu ai unde. Fix local pentru `service_auto`:
```python
# backend/modules/service_auto/__init__.py
import logging
from pathlib import Path
# Absolute path rezolvat relativ la acest file — zero dependență de CWD
_LOG_DIR = Path(__file__).resolve().parents[2] / 'logs'
_LOG_DIR.mkdir(parents=True, exist_ok=True)
logger = logging.getLogger('service_auto')
logger.propagate = False # fără dublă scriere în stdout via root logger
fh = logging.FileHandler(_LOG_DIR / 'service_auto.log')
fh.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s'))
logger.addHandler(fh)
logger.setLevel(logging.INFO)
```
Notă: `propagate = False` evită ca mesajele să apară și în stdout prin root logger-ul
din `main.py`. Dacă vrei **ambele** (file + stdout), șterge rândul cu `propagate`.
Log structurat la fiecare call:
```python
logger.info("service_auto.create_comanda START", extra={'user': user.username, 'tip': data.tip, 'client_id': data.client_id})
logger.info("service_auto.create_comanda OK", extra={'comanda_id': comanda_id, 'duration_ms': elapsed})
logger.warning("service_auto.create_comanda BUSINESS_ERROR", extra={'ora_code': code})
logger.error("service_auto.create_comanda INFRA_ERROR", extra={'ora_code': code}, exc_info=True)
```
La sfârșit de fiecare săptămână, `grep` în log pentru count-uri și duration p99.
### Correction 9 — Week 1 gate reframing pentru ipoteza #1 (true-async)
**Prima versiune a planului** trata Week 1 gate ca un "flag flip": "dacă true-async merge
→ adoptă; dacă nu → sync-facade". **Realitate:** `shared/database/oracle_pool.py` folosește
`oracledb.create_pool()` — API sync. Pentru adevărat-async există `create_pool_async()` care
e o **clasă diferită**, cu acquire/close semantics distincte. **Integrarea în pool-ul shared
e un refactor non-trivial, NU un flag.**
**Gate reframe:**
> - Dacă `connect_async()` + OUT params merge în POC → **decizia e**: folosește `connect_async`
> ad-hoc în `service_auto` (zero pooling, acceptă trade-off-ul "1 user, 1 connection per
> request"), log-ează ca **ipoteză phase-2** că integrarea `AsyncConnectionPool` în
> `OracleMultiPool` ar fi worthwhile. Prototype-ul NU atinge shared-code async pool.
> - Dacă POC-ul eșuează → sync-facade prin pool-ul existent (treasury_service pattern).
> Pre-approved, zero timp pierdut.
Intenția originală se păstrează — învățare reală despre true-async — fără să ascundă în
Week 1 gate o săptămână de refactor la shared code.
### Correction 10 — Module layout pre-declarat în săpt 1
**Prima versiune** creează `backend/modules/service_auto/__init__.py` doar pentru logger,
restul structurii apare ad-hoc. **Realitate:** convenția repo-ului
(`backend/modules/data_entry/`) e `routers/ services/ schemas/ models/ db/`. Template
deliverable-ul săpt 21 e mai puternic dacă *documentează* o structură deja curată decât
dacă *retrofitează* una.
**Action în săpt 1 alongside logger:** `mkdir -p backend/modules/service_auto/{routers,services,schemas,models}`
+ `touch` câte un `__init__.py` gol în fiecare. Zero cod, doar scaffold.
### Correction 11 — `TODO-phase2.md` creat în săpt 1
**Prima versiune** referenția `docs/service-auto/TODO-phase2.md` ca destinație pentru scope-creep
thoughts, dar fișierul nu există. Creează-l în săpt 1 cu un header gol:
```markdown
# TODO — Phase 2 ideas for service_auto
Temptations captured during the 24-week prototype. NOT commitments. NOT scoped.
This file is a graveyard for "ooh, we could also..." thoughts.
```
Angajamentul scope-wall devine astfel durabil și citibil.
### Correction 7 — Timeline dublat: 12 → 24 săptămâni
**Prima versiune** aloca 12 săptămâni × 2-4 h/săpt = 24-48h. **Realitate (outside voice):**
10 tehnologii noi (Vue 3, Pinia, PrimeVue, Axios, FastAPI async, oracledb true-async,
`session_callback`, PL/SQL OUT params, JWT reuse cu auth store, error mapping) ÷ 24h =
~2.4h per tehnologie, **zero slack**. Plus 2-3 săpt pierdute realist pe VFP urgențe din
12. **Buget onest: 24 săptămâni × 2-4 h/săpt = 48-96h total.** Scope-ul rămâne același,
tempo-ul e mai onest.
### Correction 8 — Week-12-style deliverable schimbă: template reutilizabil, nu demo
**Prima versiune** zicea săpt 12 = "cleanup + phase 2 notes". **Realitate:** un demo pe un
branch throwaway nu e un hedge — e o demonstrație. Hedge-ul real e un **template
reutilizabil**. Noul deliverable pentru săpt 24:
- `docs/service-auto/template-modul-oracle.md` — rețetă de 1 pagină pentru "cum adaugi un
modul nou peste Oracle în roa2web": register_server pattern, SP signature convention,
grants model, error map range, `session_callback` snippet, JWT reuse via
AuthenticationMiddleware, logging setup, file layout.
- `docs/service-auto/decision-log.md` — go / no-go pentru phase 2: ce a funcționat, ce
nu, sub ce condiții se justifică un al doilea modul, când să nu.
- `docs/service-auto/learnings.md` — notele săptămânale consolidate în lesson patterns.
## UX spec — interaction states for `ComandaNoua.vue` (from /plan-design-review 2026-04-11)
Hypothesis #4 validates error *codes end-to-end*. Această secțiune fixează
comportamentul *formularului în fiecare stare* — ca să nu se improvizeze
în săptămâna 14 la 2h/săpt, în timp ce Marius debug-uiește async Oracle.
**Convenția Toast** e aliniată cu `src/modules/data-entry/views/receipts/ReceiptCreateUnifiedView.vue:666-787`
(existing `toast.add({severity, summary, detail, life})`), zero componente noi.
| State | Trigger | UX behavior | PrimeVue primitives |
|---|---|---|---|
| **Idle / first load** | Mount `ComandaNoua.vue` | Focus auto pe `Select "tip comandă"`. Placeholder-uri scurte în română ("Alege tip", "Alege client", "Alege mașina", "Descrie operațiile"). Submit button disabled until toate 4 câmpurile sunt non-empty. | `Select`, `Textarea`, `Button disabled` |
| **Loading / in-flight** | User clicks Submit | (a) Submit button → `:loading="true"` (PrimeVue spinner inline). (b) Toate câmpurile `:disabled="true"` ca să nu editeze în timpul SP call. (c) Fără overlay / modal. (d) Dacă SP depășește 10s, nimic special (rely on FastAPI default timeout) — tracking ca TODO-phase2 dacă devine problemă. | `Button :loading`, `:disabled` pe inputs |
| **Success** | HTTP 200 + `{comanda_id, numar}` | (1) Toast `severity: 'success'`, summary `'Comandă creată'`, detail `'Nr ${numar}'`, `life: 3000`. (2) Reset: `client_id=null, masina_id=null, operatii=''`. **PRESERVE `tip_comandă`** (user creează multiple same-type în succesiune). (3) Re-enable form. (4) Focus revine pe `client_id` dropdown. | `toast.add`, form ref reset parțial |
| **Business error (20001-20999)** | HTTP 422 cu detail din SP | (1) Toast `severity: 'error'`, summary `'Validare'`, detail = mesajul din SP (diacritice!), `life: 5000`. (2) Form **PRESERVĂ input-ul** (user vrea să corecteze, nu să retype). (3) Re-enable form. (4) Fără field-level highlight pentru prototype — scope wall, SP-ul nu returnează field name. | `toast.add severity error` |
| **Infra error (12xxx, 01017, 00942)** | HTTP 503 sau 500 | (1) Toast `severity: 'error'`, summary `'Eroare conexiune'` (503) sau `'Eroare internă'` (500), detail = mesaj generic din tabelul Correction 5, `life: 5000`. (2) Form preservă input. (3) Re-enable Submit — user poate retry. (4) Fără retry-uri automate (keep it simple). | `toast.add severity error` |
| **Client-side validation** | — | **NONE pentru prototype.** SP-ul e sursa de adevăr pentru validare. Rationale: o singură sursă = mai puține layer-e de debug în săpt 13-16. Fewer moving parts = more learning. Un singur control: Submit disabled dacă vreun câmp e empty (trivial, nu contează ca "validare"). | — |
**Dark mode requirement** (din `.claude/rules/css-design-system.md`): testare OBLIGATORIE
în ambele teme (auto / light / dark) via toggle din `AppHeader.vue`. Fiindcă
`ComandaNoua.vue` folosește PrimeVue + semantic tokens (`--surface-card`,
`--text-color`, etc.) importate din roa2web, **zero CSS custom** în prototype →
dark mode "free" fără efort. Dacă apare vreo regresie de culoare, e bug în
component-ul PrimeVue, nu în ComandaNoua — NU fix în prototype, log în `TODO-phase2.md`.
**Test pass în săpt 13-16**: pentru fiecare rând din tabel, un click manual
verified, documentat în `docs/service-auto/week13-ux-states.md` (1 paragraf, 6 rânduri).
Zero e2e automation — scope wall.
## What to reuse from the scrapped doc
Nu tot efortul de pe doc-ul vechi e pierdut:
- **Manual ROAAUTO PDF** (`docs/service-auto/manual_ROAAUTO.pdf`) — ground truth user-level pentru
fluxul real de service auto. Folosit ca input pentru ce câmpuri trebuie să aibă
`ComandaNoua.vue` și ca referință pentru verificarea tabelelor atinse la creare comandă.
- **Sursa VFP roaauto în gitea** (`gitea.romfast.ro/romfast/vfp_roaauto`, importată 2026-04-11)
— ground truth **code-level** pentru cum se citesc / scriu datele în schemă. Folosită
în săpt 3 pentru extragerea: (a) tabelelor atinse la creare comandă, (b) celor 4 valori
enum `tip comandă`, (c) label-urilor Romanian pentru câmpuri, (d) pattern-ului SP-urilor
existente (dacă există) ca referință pentru `SP_CREEAZA_COMANDA`. Înlocuiește nevoia de a
deschide VFP-ul producție — zero risk, text searchable, diff-abil.
- **Legacy `/workspace/roaauto/frontend/src/views/orders/OrderCreateView.vue`** —
starting point pentru `ComandaNoua.vue` la ~125 linii, audit de 30 min în săptămâna 1.
- **Convenția de services** din `.claude/rules/backend-patterns.md` — fiecare SP call trăiește
într-un service, nu în router. Decoratorul `@cached` există LOCAL la `backend/modules/reports/cache/decorators.py`,
nu în `shared/` — deci NU se importă în `service_auto`. Oricum e pentru reads, prototype-ul e
write-only. Dacă phase 2 aduce reads, decide atunci dacă e worth promovat la `shared/`.
- **Pattern-ul `treasury_service.py`** ca **golden example** de apel PL/SQL cu OUT params.
- **Design system roa2web** — `docs/DESIGN.md`, `docs/DESIGN_TOKENS.md`,
`docs/CSS_PATTERNS.md`, `.claude/rules/css-design-system.md`. `ComandaNoua.vue`
**MUST** folosi semantic tokens (`--surface-card`, `--text-color`, `--color-primary`,
spacing scale `--space-*`), zero valori hardcodate, zero CSS scoped custom (doar
layout minimal via `.form-stack { display: grid; gap: var(--space-md); }`).
Toast-uri prin `useToast()` + convenția `{severity, summary, detail, life}` cu
life 3000ms success / 5000ms error. Dark mode testare obligatorie (`[data-theme]`
toggle din `AppHeader.vue`). Zero componente noi — reuse PrimeVue `Select`,
`Textarea`, `Button`, `Toast`.
## Risks + mitigations
### Risk 1: 2-4 hrs/week + learning curve = prototype drifts into 12+ months
Corecția de buget (24 săpt) ajută. **Mitigare principală:** stop-point discipline
(milestone self-contained per săptămână) + **kill criterion explicit** (vezi mai jos).
Săpt 1 goal e **Hello World Oracle connection** — nu SP, nu schema, nu UI.
### Risk 2: oracledb async + PL/SQL OUT params sunt poorly documented
Săpt 1 POC e exact asta, pe o procedură trivială. **Fallback pre-aprobat:** dacă
true-async nu merge, prototype-ul continuă cu sync-facade (pattern deja în prod). Zero
timp pierdut.
### Risk 3: ~~CURRENT_SCHEMA switching conflictează cu OracleMultiPool~~
**Corectat:** nu conflictează. `OracleMultiPool` izolează per `server_id`.
`session_callback` se aplică la `create_pool` call, nu la nivel de instance. Poți adăuga
un argument `session_callback=` la pool creation pentru `mariusm_test` fără să atingi
pool-urile altor servere.
### Risk 4: Marius abandonează side project-ul după săpt 3 pentru că VFP-ul real cere atenție
Realitate acceptată (outside voice): 2-3 săpt pierdute în 12 e probabil. **Mitigare:**
timeline dublat la 24, fiecare săptămână e stoppable milestone, plus **kill criterion**.
### Risk 5 (NEW): Integration issues concentrate la final
Outside voice observă: fiecare layer e mock-uit până la săpt 11, apoi totul se integrează.
Dacă Vue↔FastAPI↔oracledb are un CORS/proxy/JWT header issue, apare fără buffer.
**Mitigare:** două **integration checkpoints** intermediare:
- Săpt 6 checkpoint: un `curl` hit pe endpoint-ul fără UI, cu JWT real, care cheamă SP-ul și returnează JSON. Confirmă FastAPI↔oracledb chain.
- Săpt 9 checkpoint: `ComandaNoua.vue` hit endpoint-ul cu JWT real, confirmă doar un drum happy path fără error handling. Confirmă Vue↔FastAPI chain.
### Risk 6 (NEW): Grants inventory pe MARIUSM_AUTO nedocumentat
Outside voice observă: testul negativ săpt 3 presupune clean slate. **Mitigare:** săpt 3,
înainte de testul negativ, rulează un `SELECT * FROM DBA_TAB_PRIVS WHERE grantee = 'ROA_WEB'`
+ `SELECT * FROM DBA_SYS_PRIVS WHERE grantee = 'ROA_WEB'` și documentează starting state
în `docs/service-auto/week3-grants-audit.md`.
## Kill criterion (NEW — from outside voice)
**Prototype-ul se oprește definitiv** (nu "pauză de o săptămână", ci "end of project,
scrie decision-log și gata") dacă ORICARE din:
1. **Săpt 4 checkpoint:** `session_callback` nu funcționează după 2 săpt de încercări
(adică nu ai ajuns la o conexiune care schimbă CURRENT_SCHEMA curat, fără race). Decizia:
scrie `decision-log.md` cu "session_callback nefezabil în acest oracledb/Python combo,
multi-tenant necesită alt approach", apoi stop.
2. **Săpt 8 checkpoint:** nu ai un SP `SP_CREEAZA_COMANDA` funcțional testat din SQL
Developer + Python + grants OK. Decizia: `decision-log.md` cu "pattern SP-first prea
greu pentru side-project budget", apoi stop.
3. **Săpt 14 checkpoint:** nu ai un curl care apelează endpoint-ul + JWT real + SP + returnează JSON.
4. **Oricând:** 3 săpt consecutive în care nu ai atins nici măcar 1 oră de work pe
prototype. Side project-ul e mort, acceptă realitatea, `decision-log.md` cu
"abandoned — VFP demands exceeded budget".
Stop NU înseamnă eșec. Stop înseamnă "am învățat ce trebuia, continuarea nu mai produce
valoare marginală". Fiecare stop produce un `decision-log.md` citibil în 6 luni.
## Timeline (ajustat pentru 2-4 hrs/week × 24 săptămâni)
**Fiecare săptămână e self-contained + oprire curată.** Ordering-ul original e păstrat,
doar spațiat pe 24 săpt în loc de 12, cu checkpoints de integration intermediare.
| Săpt | Target | Cum știu că e gata |
|---|---|---|
| 1 | Oracle central rulează + `poc/hello_oracle.py` (sync) + `poc/hello_oracle_async.py` (`connect_async`) conectează cu `SELECT 1 FROM DUAL`. **Gate (reframed — vezi Correction 9)**: dacă `connect_async` merge → folosește ad-hoc în service_auto, refactorul de pool rămâne phase-2; dacă nu → sync-facade. DX check (save → result < 10s?). File handler logging setup cu absolute path + `propagate=False`. **Module scaffold**: `mkdir backend/modules/service_auto/{routers,services,schemas,models}`. Creează `docs/service-auto/TODO-phase2.md` cu header gol. | Log arată rezultat, `docs/service-auto/week1-notes.md` scris, scaffold există, `TODO-phase2.md` există |
| 2 | `poc/async_out_param_probe.py` test SP trivial (`CREATE PROCEDURE test_out(p OUT NUMBER) AS BEGIN p := 42; END;`) + Python apel cu `cursor.var(int)` | Script scris, primește `42` |
| 3 | **Preparare:** audit grants curenți pe MARIUSM_AUTO (`DBA_TAB_PRIVS`, `DBA_SYS_PRIVS`). **Clonează `gitea.romfast.ro/romfast/vfp_roaauto`** și grep prin .PRG / .SCX pentru fluxul "creează comandă nouă service auto" scrie `tabele-service-auto.md` cu: (a) tabele atinse, (b) cele 4 valori enum `tip comandă`, (c) label-uri Romanian pentru câmpuri, (d) pattern SP-uri existente (dacă există). Creează user `ROA_WEB` + grants. **Auth path audit (ipoteza #6)**: adaugă `MARIUSM_AUTO` la `ORACLE_SERVERS` în `backend/.env`, inserează row în `CONTAFIN_ORACLE.UTILIZATORI` pentru Marius, log in prin UI, confirmă server selector + JWT payload, verifică `get_user_companies('MARIUS M', 'mariusm_test')` scrie `week3-auth-audit.md`. | `week3-grants-audit.md` + `tabele-service-auto.md` (cu enum + labels + SP-uri din gitea) + `week3-auth-audit.md` scrise, login funcțional pentru server nou |
| 4 | Test negativ ipoteza #3 (INSERT direct ORA-00942; SELECT direct ORA-00942; CALL SP success). Pytest integration marker. | Test automat rulează local, toate cele 3 asserts trec |
| 5 | `session_callback` pentru CURRENT_SCHEMA switching, test concurent pe 2 conexiuni paralele pe aceeași schemă. **Task explicit**: patch ~8 linii la `shared/database/oracle_pool.py` (`register_server` acceptă `session_callback`, `_get_or_create_pool` îl propagă la `create_pool`). Backward compatible, zero callers existenți afectați. | Log-uri clare, niciun cursor tag leak, patch la shared code review-ed și commit-ed |
| 6 | **Checkpoint integration 1**: endpoint `GET /api/service-auto/ping` care rulează `SELECT 1 FROM DUAL` pe `mariusm_test` prin `oracle_pool.get_connection('mariusm_test')`. Curl cu JWT real (după auth-ul probeat în săpt 3). Probează: auth middleware pool connection query round-trip pentru serverul nou întregul chain, fără SP. | `curl -H "Authorization: Bearer ..." /api/service-auto/ping` returnează `{"result": 1}` |
| 7-8 | **Verify tabele** în VFP (din săpt 3), scrie `SP_CREEAZA_COMANDA` în SQL Developer, test manual cu `EXEC` | Row apare în tabela dedicată cu ID returnat prin OUT |
| 9-10 | Service-layer Python (`comenzi_service.create_comanda`) care apelează SP-ul prin pattern-ul ales în săpt 1 + `connection.commit()` + test persist după reconnect | Test automat: callproc + commit + new connection + SELECT row există |
| 11 | Endpoint FastAPI `/api/service-auto/comenzi` care primește Pydantic, apelează service, returnează JSON | `curl` hit cu JWT real + body JSON row inserted |
| 12 | **Checkpoint integration 2**: `ComandaNoua.vue` minimal (hardcoded dropdowns) + Pinia store + axios call endpoint. Doar happy path, zero error handling. | Submit în browser row inserted console log "OK" |
| 13-16 | Error handling end-to-end: ORA-20001 (diacritice!) HTTP 422 PrimeVue Toast în română. ORA-12541 HTTP 503 Toast generic. Spinner pe buton cât SP rulează. Reset form la success. | Aruncă intenționat erori în SP, verifică fiecare drum |
| 17-20 | Buffer pentru VFP urgențe + lessons refactor (dacă săpt 1-16 au produs cod care trebuie curățat). Dacă nu e nevoie de buffer, rula ipoteza #2 mai adânc (2 conexiuni, concurență mai agresivă). | N/A buffer |
| 21-22 | Scrie `template-modul-oracle.md` reteta reutilizabilă | Doc scris, citibil de către un alt dev |
| 23 | Scrie `decision-log.md` go/no-go phase 2, learnings consolidate | Doc scris, decizie clară |
| 24 | Cleanup: ștergerea `poc/` dacă e cazul, final commit pe branch, `learnings.md` consolidat | Branch clean, commit final |
**Timeline-ul e intenționat conservativ.** La 2-4 hrs/week, 24 săpt = 48-96 ore totale.
Dacă merge mai repede, perfect atingi mai devreme la sfârșit. Dacă merge mai încet,
fiecare săptămână e încă un milestone self-contained.
## What would make this prototype fail (and it would still be useful)
- Ipoteza #1 eșuează pentru **true-async**: fallback la sync-facade, prototype continue.
Zero învățare pierdută sync-facade e deja în producție, știi merge.
- Ipoteza #1 eșuează pentru **ambele** (sync + true-async): dacă nici sync-facade-ul din
`treasury_service.py` nu merge la MARIUSM_AUTO, e ceva radical prost cu oracledb
instalat la tine. **Stop și investighează**.
- Ipoteza #2 eșuează: `session_callback` fragil în practică Marius știe pool-per-tenant
e necesar, nu pool-single-with-tag. Design major changed, decision-log documentează.
- Ipoteza #3 eșuează: GRANTS model nu ține (SP cheamă alte SPs cu privilegii neașteptate)
Marius știe "Regula de aur via GRANTS" e naivă și trebuie altceva (VPD?
row-level security?). Design major changed.
- Ipoteza #4 eșuează: error propagation urâtă (encoding, truncation) Marius știe
trebuie protocol de eroare dedicat, nu `RAISE_APPLICATION_ERROR`.
- Ipoteza #5 eșuează: DX pentru 2-4 hrs/week e unusable Marius știe side project pe
acest stack nu e viabil și trebuie alt approach.
Orice din aceste failures e **valoare mai mare** decât "prototype care merge happy path
fără fi întâmpinat nicio problemă reală". Succesul real = învățare, nu cod.
## Next actions (săpt 1, 2026-04-14 → 2026-04-18)
Cel mai concret lucru, o singură zi, 2-4 ore:
1. **Verifică SSH tunnel setup** de la containerul de dev (sau workstation-ul de acasă)
la `10.0.20.121/ROA`. Există `ssh-tunnel-manager.py` în `backend/shared/` vezi dacă
se refolosește.
2. **`poc/hello_oracle.py`** (sync, la `MARIUSM_AUTO` user sau alt user Oracle existent)
`oracledb.connect(...)` + `SELECT 1 FROM DUAL`.
3. **`poc/hello_oracle_async.py`** (true-async) `oracledb.connect_async(...)` + același
`SELECT 1 FROM DUAL` cu `await cursor.execute(...)`.
4. **Setup logging file handler** pentru modulul `service_auto` (nu exista încă, dar
directory-ul `backend/modules/service_auto/` se poate crea în săpt 1 just to house
the logging setup).
5. **Raport săpt 1** `docs/service-auto/week1-notes.md`, max 15 linii: ce a mers, ce a
fost greu, DX latency măsurat (save result în secunde), gate decision (sync vs
true-async), buget rămas din săpt 1.
Zero SP-uri, zero Vue, zero auth, zero scheme noi. Doar "Python vorbește cu Oracle local"
în ambele pattern-uri.
## Non-negotiables
1. **Cei 4 clienți nu sunt atinși niciodată în acest prototype.** MARIUSM_AUTO e
dedicat, zero copy, zero conexiune la producție.
2. **Scope wall la 1 ecran + 1 SP.** Tentațiile de scope creep merg la
`docs/service-auto/TODO-phase2.md`, nu în cod.
3. **Stop points weekly.** Fiecare săptămână e oprire curată. Nu se acceptă "oh, nu pot
opresc aici pentru e jumătate făcut".
4. **Succesul se măsoară în învățare + template reutilizabil**, nu în ecrane livrate. Un
prototype care descoperă o problemă e mai valoros decât unul care evită toate
problemele.
5. **Kill criterion trigger-ed → scrie decision-log → stop.** Nu "mai dau 2 săpt văd".
## Final deliverable (săpt 24)
Nu e un ecran. Nu e un demo. E:
1. `docs/service-auto/template-modul-oracle.md` rețetă de 1 pagină citibilă de către un alt dev în 10 minute:
- Cum înregistrezi un server nou în `OracleMultiPool`
- Signature convention pentru SPs cu OUT params
- Grants model (user tehnic + `EXECUTE ON SP` + `ALTER SESSION SET CURRENT_SCHEMA`)
- Error code range classification (20xxx 422, 12xxx 503, else 500)
- `session_callback` snippet pentru CURRENT_SCHEMA switching
- JWT reuse prin `AuthenticationMiddleware`
- Logging file handler per-modul
- File layout (`routers/`, `services/`, `models/`)
2. `docs/service-auto/decision-log.md` go / no-go pentru phase 2:
- Ce ipoteze s-au confirmat, care nu
- Evidența concretă (log-uri, teste, numere DX)
- Condițiile sub care un al doilea modul e justificat
- Condițiile sub care NU e justificat (kill criterion retroactiv)
3. `docs/service-auto/learnings.md` notele săptămânale consolidate, pattern-uri noi,
surprize, workarounds descoperite, Oracle edge cases.
4. Cod: tot sub branch `feat/service-auto`, NU merged în main. Branch = arhivă + referință.
**Acest deliverable e valoros chiar dacă prototype-ul n-a ajuns la 100% din ipoteze.**
---
**END OF RETHINK DESIGN v2. ~9 pages. Budget 2-4 hrs/week × 24 weeks = 48-96 hrs total.**
---
## GSTACK REVIEW REPORT
| Review | Trigger | Why | Runs | Status | Findings |
|--------|---------|-----|------|--------|----------|
| CEO Review | `/plan-ceo-review` | Scope & strategy | 1 | CLEAR (PLAN) | HOLD SCOPE mode, 0 critical gaps post-fix, 10 items deferred to TODO-phase2, plan file rewritten v2 with 8 review corrections |
| Codex Review | `/codex review` | Independent 2nd opinion | 0 | | |
| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 1 | CLEAR (PLAN) | 8 issues (3 P1/P2 load-bearing + 5 minor committed), 1 critical gap (JWT auth flow) mitigated via new hypothesis #6 + week-3 auth audit, Corrections 9/10/11 added, timeline table updated for weeks 1/3/5/6 |
| Design Review | `/plan-design-review` | UI/UX gaps | 1 | CLEAR (PLAN) | score 6/10 9/10, 4 decisions added (interaction-states table, design-system inheritance clause, a11y one-liner, week-3 enum+labels capture), 0 unresolved, mockups skip-by-design (learning prototype on existing roa2web theme) |
| DX Review | `/plan-devex-review` | Developer experience gaps | 0 | | |
**OUTSIDE VOICE (CEO round):** Claude subagent (Codex unavailable). 15 findings total. 3 load-bearing surfaced as cross-model tension (SPACT modeling error, budget ~2x under-scoped, demo vs template deliverable). All 3 accepted by user and integrated into plan v2.
**OUTSIDE VOICE (ENG round):** Skipped. Rationale: design already absorbed one full outside-voice round in CEO review; advisor consulted during orientation as independent second perspective; prototype scope has diminishing returns on third adversarial pass; Codex CLI unavailable in this environment.
**OUTSIDE VOICE (DESIGN round):** Skipped by design. Advisor consulted once during orientation to sanity-check the "skip mockups" judgment (confirmed: generating fictional mockups for a learning prototype that must inherit existing roa2web PrimeVue theme would be actively misleading). No second adversarial pass design surface is ~15 lines of plan text, not a visual system.
**DESIGN REVIEW SUMMARY:**
- **Passes 1, 3** (Info Arch, User Journey): no findings single form, 1-user prototype.
- **Pass 4** (AI Slop): N/A no custom design surface, every pixel inherited from existing roa2web theme.
- **Pass 2** (Interaction States) 3/10 9/10: 6-state table added (idle/loading/success/business-error/infra-error/client-validation) with concrete PrimeVue behavior, toast lifecycle aligned to `ReceiptCreateUnifiedView.vue` convention (life 3000/5000ms), `tip comandă` preserved on success reset, dark-mode inheritance documented, client-side validation intentionally omitted for prototype (fewer layers to debug).
- **Pass 5** (Design System) 2/10 9/10: inheritance clause added referencing `DESIGN.md`, `DESIGN_TOKENS.md`, `CSS_PATTERNS.md`, `.claude/rules/css-design-system.md`. Semantic tokens mandatory, zero hardcoded values, zero custom components.
- **Pass 6** (A11y) 5/10 9/10: one-liner clarifying PrimeVue defaults handle keyboard/ARIA/focus so it isn't mistaken for scope creep.
- **Pass 7** (Unresolved Decisions): 2 items (4 `tip comandă` enum values + Romanian field labels) folded into week-3 VFP audit subtask sourced from ground truth, not invented.
**ENG REVIEW SUMMARY:**
- **Architecture (3 issues):** (#1, P1) JWT auth flow for MARIUSM_AUTO undesigned added hypothesis #6 + week-3 auth audit subtask. (#2, P2) `oracle_pool.py` needs ~8-line backward-compatible patch to thread `session_callback`; "zero refactor" claim corrected in Correction 1 and scheduled explicitly in week 5. (#3, P2) Week 1 gate reworded: true-async success means ad-hoc `connect_async` inside service_auto, NOT pool refactor AsyncConnectionPool integration becomes phase-2 hypothesis (Correction 9).
- **Code Quality (3 committed fixes):** Correction 6 logging snippet fixed (absolute path + `propagate=False`), `@cached` wording clarified (reports-local, not shared), Correction 10 added (module scaffold in week 1), Correction 11 added (`TODO-phase2.md` stub in week 1).
- **Test plan:** 7/12 hypothesis-branches planned, JWT flow test added (manual smoke in week 3), diacritics byte-level assertion flagged as week-17-20 buffer refinement. Test plan artifact written to `~/.gstack/projects/romfast-roa2web/claude-feat-service-auto-eng-review-test-plan-20260411-140656.md`.
- **Performance:** N/A for 1-user prototype. Optional: register `mariusm_test` with `min=1, max=2`.
- **Parallelization:** N/A single dev, inherently sequential.
**UNRESOLVED:** 0
**VERDICT:** CEO + ENG + DESIGN CLEARED plan v2 + eng-review corrections + design-review UX spec (1 interaction-states table, 1 design-system inheritance clause, 1 a11y note, 1 week-3 enum/labels audit task). 24-week learning prototype is ready to execute. Next action: week 1 per timeline (hello_oracle POC + DX check + module scaffold + `TODO-phase2.md` stub + logging setup).