docs(service-auto): design review + VFP audit (Correction 12)
plan-design-review pass: 6/10 → 9/10. Added interaction-states table (6-state PrimeVue spec matching ReceiptCreateUnifiedView.vue toast conventions), design-system inheritance clause (DESIGN.md/TOKENS/ CSS_PATTERNS), a11y one-liner, week-3 subtask for enum+labels extraction from VFP source. Cloned gitea.romfast.ro/romfast/vfp_roaauto and wrote a 300+ line ground-truth audit (docs/service-auto/tabele-service-auto.md) covering: DEV_ORDL parent/child inheritance with NOM_LUCRARI, DEV_TIP_DEVIZ enum (1-7, including user-confirmed Productie/ Constatare for 6/7), dev_adauga_lucrare v1 (pack_devize, 12+1 params, body visible in packages.sql) vs v2 (pack_auto, 17+1, body only in MARIUSM_AUTO), and the pack_sesiune.dev_idLucrare trigger/session-state pattern (ROA ERP convention from pre- RETURNING era, not a bug). Plan Correction 12 added: the prototype needs a ~40-line SP doing two INSERTs parent→child with RETURNING id_lucrare (bypassing pack_sesiune), targeting dev_ordl via nom_lucrari. Original assumption of a 4-param new SP was wrong. Week 3 subtask updated to reference the audit draft. ACT/RUL untouched at creation time — Correction 3 still valid. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -89,8 +89,9 @@ infirme:
|
|||||||
| 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. |
|
| 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. |
|
| 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. |
|
| 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-4 eșuează cu un răspuns clar, **prototype-ul s-a încheiat cu succes** —
|
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ă.
|
learning obținut, decision point clar, zero cod irosit care trebuie să supraviețuiască.
|
||||||
|
|
||||||
## Scope wall — what is explicitly NOT in this prototype
|
## Scope wall — what is explicitly NOT in this prototype
|
||||||
@@ -106,7 +107,7 @@ Toate aceste subiecte aparțin doc-ului scrapped sau sunt defer-uri la TODO-phas
|
|||||||
- ❌ `reconcile.py`, invariant checking, parallel testing cu VFP
|
- ❌ `reconcile.py`, invariant checking, parallel testing cu VFP
|
||||||
- ❌ Runbook rollback, monthly restore drill, uptime monitoring, Telegram alerts
|
- ❌ Runbook rollback, monthly restore drill, uptime monitoring, Telegram alerts
|
||||||
- ❌ 5 ecrane MVP (doar 1 — absolute wall)
|
- ❌ 5 ecrane MVP (doar 1 — absolute wall)
|
||||||
- ❌ Mobile, responsive, iPad, tablet — desktop-only, viewport-whatever-is-convenient
|
- ❌ 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
|
- ❌ Playwright e2e, smoke parity pentru SPs
|
||||||
- ❌ Endpoint `/clienti` sau `/masini` pentru populare dropdown — hardcoded în Vue
|
- ❌ Endpoint `/clienti` sau `/masini` pentru populare dropdown — hardcoded în Vue
|
||||||
- ❌ Onboarding client nou, referral pipeline, assignment cu telefoane
|
- ❌ Onboarding client nou, referral pipeline, assignment cu telefoane
|
||||||
@@ -125,12 +126,33 @@ nu în codul prototype-ului.
|
|||||||
Această secțiune documentează ce s-a corectat față de prima versiune a planului, pentru a
|
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.
|
fi transparent despre gândirea care a dus la planul final.
|
||||||
|
|
||||||
### Correction 1 — Nu se creează `MarciusLabPool` nou
|
### Correction 1 — Nu se creează `MarciusLabPool` nou (+ patch minim la `oracle_pool.py`)
|
||||||
|
|
||||||
**Prima versiune** spunea: "modifică `OracleMultiPool` sau adaugă wrapper" și "creează NEW
|
**Prima versiune** spunea: "modifică `OracleMultiPool` sau adaugă wrapper" și "creează NEW
|
||||||
minimal helper `MarciusLabPool`". **Realitate:** `OracleMultiPool` la
|
minimal helper `MarciusLabPool`". **Realitate:** `OracleMultiPool` la
|
||||||
`shared/database/oracle_pool.py` are deja suport multi-server prin `_pools: Dict[server_id, ConnectionPool]`.
|
`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 refactor.
|
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
|
### Correction 2 — Pattern sync-in-async-facade e deja proof-ed în prod
|
||||||
|
|
||||||
@@ -152,9 +174,125 @@ jurnal contabil (double-entry bookkeeping, clasa 6/7 — vezi
|
|||||||
în ACT. ACT se hit-uiește la **facturare**, care e NOT IN SCOPE. SP-ul corect face INSERT
|
în ACT. ACT se hit-uiește la **facturare**, care e NOT IN SCOPE. SP-ul corect face INSERT
|
||||||
în tabela dedicată de comenzi.
|
în tabela dedicată de comenzi.
|
||||||
|
|
||||||
**Task în săpt 3 (preparare):** înainte de a scrie SP_CREEAZA_COMANDA, deschide VFP-ul
|
**Task în săpt 3 (preparare):** înainte de a scrie SP_CREEAZA_COMANDA, clonează repo-ul
|
||||||
producție (cu grijă, doar read), caută fluxul real de "creează comandă nouă service auto",
|
gitea al sursei VFP (`gitea.romfast.ro/romfast/vfp_roaauto` — importat 2026-04-11 ca
|
||||||
identifică tabelele atinse, documentează în `docs/service-auto/tabele-service-auto.md`.
|
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 4 — `connection.commit()` e explicit în pattern
|
### Correction 4 — `connection.commit()` e explicit în pattern
|
||||||
|
|
||||||
@@ -199,15 +337,24 @@ log-urile din săpt 2, nu ai unde. Fix local pentru `service_auto`:
|
|||||||
|
|
||||||
```python
|
```python
|
||||||
# backend/modules/service_auto/__init__.py
|
# backend/modules/service_auto/__init__.py
|
||||||
import logging, os
|
import logging
|
||||||
os.makedirs('backend/logs', exist_ok=True)
|
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 = logging.getLogger('service_auto')
|
||||||
fh = logging.FileHandler('backend/logs/service_auto.log')
|
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'))
|
fh.setFormatter(logging.Formatter('%(asctime)s %(levelname)s %(name)s: %(message)s'))
|
||||||
logger.addHandler(fh)
|
logger.addHandler(fh)
|
||||||
logger.setLevel(logging.INFO)
|
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:
|
Log structurat la fiecare call:
|
||||||
```python
|
```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 START", extra={'user': user.username, 'tip': data.tip, 'client_id': data.client_id})
|
||||||
@@ -218,6 +365,51 @@ logger.error("service_auto.create_comanda INFRA_ERROR", extra={'ora_code': code}
|
|||||||
|
|
||||||
La sfârșit de fiecare săptămână, `grep` în log pentru count-uri și duration p99.
|
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
|
### 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):**
|
**Prima versiune** aloca 12 săptămâni × 2-4 h/săpt = 24-48h. **Realitate (outside voice):**
|
||||||
@@ -241,18 +433,64 @@ reutilizabil**. Noul deliverable pentru săpt 24:
|
|||||||
nu, sub ce condiții se justifică un al doilea modul, când să nu.
|
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.
|
- `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
|
## What to reuse from the scrapped doc
|
||||||
|
|
||||||
Nu tot efortul de pe doc-ul vechi e pierdut:
|
Nu tot efortul de pe doc-ul vechi e pierdut:
|
||||||
|
|
||||||
- **Manual ROAAUTO PDF** (`docs/service-auto/manual_ROAAUTO.pdf`) — ground truth pentru
|
- **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ă
|
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ă.
|
`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`** —
|
- **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.
|
starting point pentru `ComandaNoua.vue` la ~125 linii, audit de 30 min în săptămâna 1.
|
||||||
- **Convenția de `@cached` + services** din `.claude/rules/backend-patterns.md` — dar NU
|
- **Convenția de services** din `.claude/rules/backend-patterns.md` — fiecare SP call trăiește
|
||||||
aplicat la endpoint-ul de INSERT (cache-ul e pentru reads).
|
î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.
|
- **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
|
## Risks + mitigations
|
||||||
|
|
||||||
@@ -316,12 +554,12 @@ doar spațiat pe 24 săpt în loc de 12, cu checkpoints de integration intermedi
|
|||||||
|
|
||||||
| Săpt | Target | Cum știu că e gata |
|
| 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**: decide sync-facade vs true-async. DX check (save → result < 10s?). File handler logging setup. | Log arată rezultat, `docs/service-auto/week1-notes.md` scris |
|
| 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` |
|
| 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`). **Verifică tabelele** atinse de crearea comenzii în VFP, scrie `tabele-service-auto.md`. Creează user `ROA_WEB` + grants. | `week3-grants-audit.md` + `tabele-service-auto.md` scrise |
|
| 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 |
|
| 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ă | Log-uri clare, niciun cursor tag leak |
|
| 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 FastAPI minimal (fără logic, hardcoded return) + JWT reuse din middleware + curl hit cu JWT real. Doar ca să confirm că chain-ul FastAPI + auth merge. | `curl` cu JWT real returnează 200 |
|
| 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 |
|
| 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ă |
|
| 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 |
|
| 11 | Endpoint FastAPI `/api/service-auto/comenzi` care primește Pydantic, apelează service, returnează JSON | `curl` hit cu JWT real + body JSON → row inserted |
|
||||||
@@ -426,12 +664,31 @@ Nu e un ecran. Nu e un demo. E:
|
|||||||
|--------|---------|-----|------|--------|----------|
|
|--------|---------|-----|------|--------|----------|
|
||||||
| 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 |
|
| 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 | — | — |
|
| Codex Review | `/codex review` | Independent 2nd opinion | 0 | — | — |
|
||||||
| Eng Review | `/plan-eng-review` | Architecture & tests (required) | 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 | 0 | — | — |
|
| 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 | — | — |
|
| DX Review | `/plan-devex-review` | Developer experience gaps | 0 | — | — |
|
||||||
|
|
||||||
**OUTSIDE VOICE:** Claude subagent (Codex unavailable). 15 findings total. 3 load-bearing surfaced as cross-model tension (SP→ACT modeling error, budget ~2x under-scoped, demo vs template deliverable). All 3 accepted by user and integrated into plan v2.
|
**OUTSIDE VOICE (CEO round):** Claude subagent (Codex unavailable). 15 findings total. 3 load-bearing surfaced as cross-model tension (SP→ACT 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
|
**UNRESOLVED:** 0
|
||||||
|
|
||||||
**VERDICT:** CEO CLEARED — plan rewritten as v2 with 8 corrections, 0 critical gaps, 24-week timeline, template deliverable. Eng review recommended before writing any code (optional for learning prototype but helps catch architectural issues in service_auto module structure).
|
**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).
|
||||||
|
|||||||
532
docs/service-auto/tabele-service-auto.md
Normal file
532
docs/service-auto/tabele-service-auto.md
Normal file
@@ -0,0 +1,532 @@
|
|||||||
|
# Tabele + SP-uri Service Auto — VFP ground truth audit (v2)
|
||||||
|
|
||||||
|
> **Status:** v2 generat 2026-04-11 de Claude pe baza analizei `gitea.romfast.ro/romfast/vfp_roaauto`
|
||||||
|
> (clonat în `/workspace/vfp_roaauto`). **Acum se bazează și pe `Scripturi_instalare/packages.sql`,
|
||||||
|
> `initializari.sql`, `triggere.sql`, `tabele.sql`** — nu doar pe .prg files. **DE CONFIRMAT** în
|
||||||
|
> săpt 3 prin interogări reale pe schema MARIUSM_AUTO.
|
||||||
|
>
|
||||||
|
> **Scop:** înlocuiește task-ul "deschide VFP producție cu grijă" din planul v2. Sursa e
|
||||||
|
> gitea, zero risc de modificare accidentală.
|
||||||
|
>
|
||||||
|
> **Corecție v2 (2026-04-11):** v1 presupunea că tabela principală e `comenzi`. **REALITATE:**
|
||||||
|
> tabela fizică e **`DEV_ORDL`** (PK: `id_ordl`). `vcomenzi` e un view denormalizat care face
|
||||||
|
> JOIN cu `dev_tip_deviz`, `dev_masiniclienti`, `parteneri`, etc. Planul v2 referință corectă:
|
||||||
|
> DB = `dev_ordl`, UI = "comandă".
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Entități — terminologie reconciliată
|
||||||
|
|
||||||
|
**Confirmat de Marius 2026-04-11**: terminologia vine din ierarhia ROA (ERP generic) vs
|
||||||
|
ROAAUTO (extensie service auto). Pattern de **inheritance prin două tabele**:
|
||||||
|
|
||||||
|
| Nivel | Termen | Tabelă | Ce conține |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **Base** (ROA ERP) | **comandă** | `nom_lucrari` | Doar numele/nrord-ul comenzii — vizibil în ACT (registrul jurnal) și RUL (rulaje gestiune) când comanda e referențiată la facturare |
|
||||||
|
| **Extension** (ROAAUTO) | **deviz auto** | `dev_ordl` | Toate detaliile specifice auto: mașină, km, termen, asigurator, tip deviz, inspector, observații |
|
||||||
|
|
||||||
|
**"Comandă"** = generic (valabil pentru orice industrie — service, construcții, producție...).
|
||||||
|
**"Deviz"** e mai specific: e calculul costului (manoperă + materiale consumate) pentru o
|
||||||
|
comandă într-un context de industrie: `deviz auto`, `deviz construcții`, etc. În ROAAUTO,
|
||||||
|
fiecare comandă de service auto ARE un deviz auto atașat (prin `dev_ordl` + `dev_oper` +
|
||||||
|
`dev_mat`).
|
||||||
|
|
||||||
|
**Relația FK**:
|
||||||
|
```
|
||||||
|
nom_lucrari (id_lucrare PK)
|
||||||
|
↑
|
||||||
|
│ FK (id_lucrare)
|
||||||
|
│
|
||||||
|
dev_ordl (id_ordl PK, id_lucrare FK → nom_lucrari.id_lucrare)
|
||||||
|
```
|
||||||
|
|
||||||
|
Confirmat în `tabele.sql:634`:
|
||||||
|
```sql
|
||||||
|
alter table DEV_ORDL add constraint FK_...
|
||||||
|
references NOM_LUCRARI (ID_LUCRARE);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Consecință pentru prototype**: crearea unei comenzi face INSERT în **AMBELE tabele**,
|
||||||
|
în ordine:
|
||||||
|
1. `INSERT INTO nom_lucrari (nrord, id_mod) VALUES (...)` — creează header-ul generic
|
||||||
|
2. `INSERT INTO dev_ordl (id_lucrare, id_tip, id_masiniclient, kmint, ...) VALUES (...)`
|
||||||
|
— creează extensia auto, legată prin `id_lucrare`
|
||||||
|
|
||||||
|
**ACT și RUL**: `nom_lucrari.nrord` e **vizibil** în ACT (registrul jurnal) și RUL (rulaje
|
||||||
|
gestiune) când comanda e referențiată la facturare. Dar **crearea comenzii NU touch-uiește
|
||||||
|
nici ACT nici RUL** — acestea sunt hit-uite doar la facturare, care e NOT IN SCOPE pentru
|
||||||
|
prototype. Correction 3 din planul v2 rămâne validă.
|
||||||
|
|
||||||
|
**Componente backend Pydantic**: `ComandaCreate`, `ComandaCreated` (termen UI), backend
|
||||||
|
service face cele două INSERT-uri într-o tranzacție, returnează `id_ordl` și `nr_comanda`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Tabela principală: `comenzi` (view: `vcomenzi`)
|
||||||
|
|
||||||
|
Schema extrasă din `COMUN/programe/onom_comenzi.prg:72-79`. **Partea vizibilă în UI**:
|
||||||
|
|
||||||
|
| Coloană | Tip VFP | Tip Oracle probabil | Note |
|
||||||
|
|---|---|---|---|
|
||||||
|
| `id_comanda` | `N(20)` | `NUMBER(20)` | PK, alias-ul pentru `id_ordl` |
|
||||||
|
| `id_ordl` | — | `NUMBER(20)` | Numele intern în PL/SQL (OUT param din SP) |
|
||||||
|
| `comanda_externa` | `C(20)` | `VARCHAR2(20)` | Nr. comandă extern client |
|
||||||
|
| `id_codclient` | `C(20)` | `VARCHAR2(20)` | FK client (**char**, nu numeric!) |
|
||||||
|
| `id_lucrare` | `N(20)` | `NUMBER(20)` | FK la `vlucrari_detalii` |
|
||||||
|
| `id_part` | `N(20)` | `NUMBER(20)` | FK partener |
|
||||||
|
| `id_agent` | `N(20)` | `NUMBER(20)` | FK agent vânzări |
|
||||||
|
| `id_delegat` | `N(20)` | `NUMBER(20)` | FK delegat |
|
||||||
|
| `id_masina` | `N(20)` | `NUMBER(20)` | FK la `masini_client` (id_mc) |
|
||||||
|
| `nr_comanda` | `C(100)` | `VARCHAR2(100)` | Nr. comandă intern cu prefix tip (ex: `G123`, `R456`) |
|
||||||
|
| `data_comanda` | `T` | `TIMESTAMP` | Data creării |
|
||||||
|
| `nume` | `C(70)` | `VARCHAR2(70)` | Nume client (denormalizat) |
|
||||||
|
| `tip_comanda` | `C(20)` sau `C(50)` | `VARCHAR2(50)` | **Display name denormalizat** (ex: "Garanție") |
|
||||||
|
| `data_livrare` | `T` | `TIMESTAMP` | Termen execuție/livrare |
|
||||||
|
| `nume_agent` | `C(70)` | — | Denormalizat |
|
||||||
|
| `nume_delegat` | `C(70)` | — | Denormalizat |
|
||||||
|
| `nrinmat` | `C(10)` | `VARCHAR2(10)` | Nr. înmatriculare mașină |
|
||||||
|
| `nrord` | `C(30)` | `VARCHAR2(30)` | Nr. ordine (intern) |
|
||||||
|
| `facturat` | `N(1)` | `NUMBER(1)` | Flag facturat (0/1) |
|
||||||
|
| `dataora` | `T` | `TIMESTAMP` | Audit |
|
||||||
|
| `utilizator` | `C(40)` | `VARCHAR2(40)` | Username creator |
|
||||||
|
| `data_livrat` | `T` | `TIMESTAMP` | Data livrării efective |
|
||||||
|
| `interna` | `N(1)` | `NUMBER(1)` | Flag comandă internă |
|
||||||
|
| `nr_livrare` | `C(50)` | `VARCHAR2(50)` | Nr. bon livrare |
|
||||||
|
| `inchisa` | `N(1)` | `NUMBER(1)` | Flag închisă |
|
||||||
|
| `id_sectie` | `N(5)` | `NUMBER(5)` | **Secția care a creat comanda (MANDATORY)** |
|
||||||
|
| `id_sectie2` | `N(5)` | `NUMBER(5)` | Secția care execută (dacă diferă) |
|
||||||
|
| `id_facturare` | `N(5)` | `NUMBER(5)` | FK adresa facturare |
|
||||||
|
| `id_livrare` | `N(5)` | `NUMBER(5)` | FK adresa livrare |
|
||||||
|
| `id_ctr` | `N(10)` | `NUMBER(10)` | FK contract (pentru clienți B2B cu discount-uri) |
|
||||||
|
| `numar_contract` | `C(100)` | `VARCHAR2(100)` | Nr. contract denormalizat |
|
||||||
|
|
||||||
|
**Total: ~28 coloane în view-ul `vcomenzi`.** Planul prototype-ului (4 fields: tip, client,
|
||||||
|
mașină, operații) e o simplificare **agresivă** — e OK pentru learning, dar să fii conștient
|
||||||
|
că VFP-ul real captează mult mai multă informație.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Tabele conexe (referențiate la create)
|
||||||
|
|
||||||
|
| Tabelă / view | Rol | Folosită de prototype? |
|
||||||
|
|---|---|---|
|
||||||
|
| `vcomenzi_elemente` | Linii detaliu (articole, cantități) — "rețeta" comenzii | **NU** — prototype scope wall, doar header |
|
||||||
|
| `vlucrari_detalii` | Lucrări / operații finalizate | **NU** — execuție post-creare |
|
||||||
|
| `vlucrari_elemente` | Articole consumate pe o lucrare | **NU** |
|
||||||
|
| `masini_client` | Mașinile clienților (FK `id_mc`) | **NU** — dropdown hardcodat în Vue |
|
||||||
|
| `parteneri` | Clienți (FK `id_codclient`) | **NU** — dropdown hardcodat |
|
||||||
|
| `optiuni` | Configurare per-secție (Key/Value) | Doar indirect prin `pack_comenzi.extrage_optiuni` |
|
||||||
|
| `adrese_parteneri` | Adrese facturare/livrare | **NU** — valoare default din partener |
|
||||||
|
| `contracte` | Contracte cu discount-uri B2B | **NU** — scope wall |
|
||||||
|
|
||||||
|
**Pentru prototype**: insert-ul simplificat atinge **doar `comenzi`** (view `vcomenzi`),
|
||||||
|
zero JOIN-uri cross-table. Scope wall ține.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. SP-ul real de creare — DOUĂ versiuni
|
||||||
|
|
||||||
|
Surpriză importantă: **există două versiuni** ale `dev_adauga_lucrare` în sursele ROAAUTO,
|
||||||
|
în pachete diferite.
|
||||||
|
|
||||||
|
### 4.1. Versiunea INSTALL-TIME: `pack_devize.dev_adauga_lucrare` (12 IN + 1 OUT)
|
||||||
|
|
||||||
|
**Sursa:** `Scripturi_instalare/packages.sql:182-212` — body-ul complet e vizibil.
|
||||||
|
|
||||||
|
**Semnătura:**
|
||||||
|
```sql
|
||||||
|
procedure dev_adauga_lucrare(
|
||||||
|
v_gcs in varchar2, -- schema
|
||||||
|
tnan in number, -- an
|
||||||
|
tnluna in number, -- luna
|
||||||
|
tnIdUtil in number, -- id user
|
||||||
|
pcNr in varchar2, -- nr comandă cu prefix (ex: 'G12345')
|
||||||
|
pnIdInsp in number, -- id inspector
|
||||||
|
pnIdAsig in number, -- id asigurator
|
||||||
|
pnIdMC in number, -- id mașină client (dev_masiniclienti.id_masiniclient)
|
||||||
|
pnKmInt in number, -- km la bord
|
||||||
|
pdTermen in date, -- termen execuție
|
||||||
|
pnTipCom in number, -- id tip (FK dev_tip_deviz.id_tip)
|
||||||
|
pnIdOrdl out number -- OUT: id_ordl generat
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
**Body real (din `packages.sql:196-212`):**
|
||||||
|
```sql
|
||||||
|
begin
|
||||||
|
-- 1. Side effect: creează rând în NOM_LUCRARI cu id_mod=1200
|
||||||
|
execute immediate 'INSERT INTO ' || v_gcs || '.NOM_LUCRARI (NRORD, ID_MOD)
|
||||||
|
VALUES (:1, 1200)'
|
||||||
|
using pcNr;
|
||||||
|
|
||||||
|
-- 2. Insert principal în DEV_ORDL
|
||||||
|
execute immediate 'insert into ' || v_gcs || '.dev_ordl
|
||||||
|
(an, luna, id_inspector, id_lucrare, datai, id_util_ad,
|
||||||
|
id_masiniclient, id_asigurator, id_tip, kmint, termen, dataoraad)
|
||||||
|
values (:1, :2, :3, :4, :5, :6, :7, :8, :9, :10, :11, :12)
|
||||||
|
returning id_ordl into :13'
|
||||||
|
using tnAn, tnLuna, pnIdInsp,
|
||||||
|
pack_sesiune.dev_idLucrare, -- <-- session state dependency!
|
||||||
|
v_dataora, tnIdUtil, pnIdMC, pnIdAsig, pnTipCom,
|
||||||
|
pnKmInt, pdTermen, V_DATAORA
|
||||||
|
returning into pnIdOrdl;
|
||||||
|
|
||||||
|
-- 3. Side effect: update km la mașina clientului
|
||||||
|
execute immediate 'update ' || v_gcs || '.dev_masiniclienti
|
||||||
|
set kmint=:1 where id_masiniclient=:2'
|
||||||
|
using pnKmInt, pnIdMC;
|
||||||
|
end;
|
||||||
|
```
|
||||||
|
|
||||||
|
**Observații critice (post-clarificare Marius 2026-04-11):**
|
||||||
|
- **Cele două INSERT-uri sunt pattern de inheritance, nu side effects**:
|
||||||
|
- Step 1 = INSERT în `NOM_LUCRARI` (parent, base ROA ERP) — creează header-ul generic
|
||||||
|
al comenzii (numele care va fi vizibil în ACT/RUL la facturare)
|
||||||
|
- Step 2 = INSERT în `DEV_ORDL` (child, extensie ROAAUTO) — creează extensia auto
|
||||||
|
legată prin `id_lucrare`
|
||||||
|
- Ambele sunt **esențiale**. Fără step 1, `dev_ordl` e orphan FK violation.
|
||||||
|
- **Side effect real** (step 3): UPDATE pe `dev_masiniclienti.kmint` — actualizează
|
||||||
|
odometrul mașinii clientului pentru a reflecta ultima intrare în service. Nu e parte
|
||||||
|
din creare, e sincronizare colaterală. Poate fi omis în prototype.
|
||||||
|
- **Dependency pe `pack_sesiune.dev_idLucrare`** — după step 1, trigger-ul pe `nom_lucrari`
|
||||||
|
(probabil `TRG_NOM_LUCRARI_BEFOINS`) populează `pack_sesiune.dev_idLucrare := :new.id_lucrare`.
|
||||||
|
Step 2 citește acest state session pentru a completa `id_lucrare` în `dev_ordl`.
|
||||||
|
**Pattern-ul vine din ROA ERP-ul mainframe-era**, unde session state era modul standard
|
||||||
|
de comunicare între trigger și procedură. **Nu e bug, e convenție istorică.**
|
||||||
|
- **Pentru roa2web bypass e trivial**: folosim `RETURNING id_lucrare INTO v_local` după
|
||||||
|
INSERT-ul pe `nom_lucrari` — trigger-ul încă populează `pack_sesiune.dev_idLucrare`
|
||||||
|
(dacă există în MARIUSM_AUTO), dar noi îl ignorăm și citim direct prin RETURNING clause.
|
||||||
|
Standard Oracle, zero dependency. Confirmat prin pattern-ul identic la `dev_ordl`
|
||||||
|
(`TRG_DEV_ORDL_BEFOINS` la triggere.sql:1165-1172 — `SEQ_DEV_ORDL.NEXTVAL INTO :new.id_ordl` +
|
||||||
|
`pack_sesiune.dev_idOrdl := :new.id_ordl`).
|
||||||
|
- `datai` și `dataoraad` sunt ambele `sysdate` local — nu primite ca param.
|
||||||
|
|
||||||
|
### 4.2. Versiunea PRODUCȚIE: `pack_auto.dev_adauga_lucrare` (17 IN + 1 OUT)
|
||||||
|
|
||||||
|
**Sursa:** `Programe/oproceduri_devize.prg:147-148` — doar CALL-ul VFP. Body-ul **NU** e în
|
||||||
|
`Scripturi_instalare/packages.sql` — pachetul `pack_auto` a fost adăugat după install.
|
||||||
|
Body-ul real trăiește doar în DB-ul MARIUS-ului.
|
||||||
|
|
||||||
|
**Call-ul din VFP:**
|
||||||
|
```sql
|
||||||
|
begin
|
||||||
|
pack_auto.dev_adauga_lucrare(
|
||||||
|
?gcS, ?gnAn, ?gnLuna, ?gnIdUtil, ?pcNr, ?pnIdInsp,
|
||||||
|
?pnIdAsig, ?pcNrDosar, ?pnIdMC, ?pnKmInt, ?pnOreFct,
|
||||||
|
to_date(?, 'DD/MM/YYYY'),
|
||||||
|
?pntipcom, ?pcSirIDOperatiiClient, ?pcObservatii,
|
||||||
|
?pcDefectiuni, ?pnIdPartRef, ?@pnIdOrdl
|
||||||
|
);
|
||||||
|
end;
|
||||||
|
```
|
||||||
|
|
||||||
|
**5 params adăugați în v2** față de v1:
|
||||||
|
- `pcNrDosar` — nr dosar daună (pentru garanție cu asigurator)
|
||||||
|
- `pnOreFct` — estimare ore manoperă la intrare
|
||||||
|
- `pcSirIDOperatiiClient` — listă operații pre-selectate de client (CSV de IDs)
|
||||||
|
- `pcObservatii` / `pcDefectiuni` — notes structurate
|
||||||
|
- `pnIdPartRef` — partener referent
|
||||||
|
|
||||||
|
**Sursa body-ului real în MARIUSM_AUTO** — de extras în săpt 3 cu:
|
||||||
|
```sql
|
||||||
|
SELECT text FROM all_source
|
||||||
|
WHERE owner='MARIUSM_AUTO' AND name='PACK_AUTO' AND type='PACKAGE BODY'
|
||||||
|
ORDER BY line;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4.3. Implicații pentru prototype — trei opțiuni
|
||||||
|
|
||||||
|
| # | Strategie | Pro | Contra |
|
||||||
|
|---|---|---|---|
|
||||||
|
| **1** | Reuse `pack_auto.dev_adauga_lucrare` v2 direct (17 params, majoritatea defaults hardcodate în Python) | Hedge max: dovedește reuse pachet producție inclusiv side-effects. Template deliverable max value. | Depinde de `pack_sesiune.dev_idLucrare` care nu e inițializat în sesiunea roa2web. Risc că pică la primul call pentru context lipsă. |
|
||||||
|
| **2** | Reuse `pack_devize.dev_adauga_lucrare` v1 (12 params, body vizibil în sursă) | Știi exact ce face SP-ul (body în `packages.sql`). 5 params mai puțin. | Același bug `pack_sesiune`. 12 params sunt încă mult pentru 2h/săpt. |
|
||||||
|
| **3** | **SP nou minimal** `SP_CREEAZA_COMANDA_PROTOTIP` cu INSERT direct în `dev_ordl` cu câmpurile minime + `returning id_ordl` | Control total. Zero dependențe. Simplu. Testable. Hypothesis #1 probată la fel de bine. | Bypass-uie business logic. Template deliverable e "SP simplu", nu "reuse pachet legacy". Hedge slăbit puțin. |
|
||||||
|
|
||||||
|
**Recomandare actualizată (după descoperirea `pack_sesiune`):**
|
||||||
|
|
||||||
|
**Opțiunea 3** (SP nou minimal) e acum **recomandarea principală**. Motivul: `pack_sesiune`
|
||||||
|
introduce un coupling invizibil cu session state VFP care ar sabota prototype-ul la prima
|
||||||
|
tentativă — iar ipoteza #1 (Python+oracledb apelează PL/SQL cu OUT params) e probată la fel
|
||||||
|
de bine de un SP simplu ca de unul complex. Reuse-ul production-logic rămâne pentru phase 2.
|
||||||
|
|
||||||
|
**Hedge-ul rămâne valabil**: template-ul deliverable poate documenta ambele pattern-uri
|
||||||
|
("pentru SP-uri simple: pattern X; pentru SP-uri legacy cu `pack_sesiune`: investigă în
|
||||||
|
phase 2 cum să inițializezi session state în Python").
|
||||||
|
|
||||||
|
**Decision point săpt 3/4**: verifică body-ul `pack_auto.dev_adauga_lucrare` în MARIUSM_AUTO.
|
||||||
|
Dacă e refactored fără `pack_sesiune`, revin-o la opțiunea 1. Altfel, opțiunea 3.
|
||||||
|
|
||||||
|
**SP minimal propus (opțiunea 3) — corectat pentru inheritance pattern:**
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE OR REPLACE PROCEDURE MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP(
|
||||||
|
p_id_tip IN NUMBER, -- FK dev_tip_deviz.id_tip (1-7)
|
||||||
|
p_id_masiniclient IN NUMBER, -- FK dev_masiniclienti.id_masiniclient
|
||||||
|
p_kmint IN NUMBER, -- km la bord
|
||||||
|
p_termen IN DATE, -- termen execuție
|
||||||
|
p_id_util IN NUMBER, -- user creator
|
||||||
|
p_nr_comanda IN VARCHAR2, -- generat în Python cu prefix (G/R/P/PR/C)
|
||||||
|
p_id_ordl OUT NUMBER, -- id-ul din dev_ordl (pentru UI)
|
||||||
|
p_numar_out OUT VARCHAR2 -- ecou al nr_comanda (simetrie API)
|
||||||
|
) AS
|
||||||
|
v_id_lucrare NUMBER; -- local, bypass pack_sesiune.dev_idLucrare
|
||||||
|
v_dataora DATE := SYSDATE;
|
||||||
|
BEGIN
|
||||||
|
-- 1. INSERT parent: nom_lucrari (header generic vizibil în ACT/RUL la facturare)
|
||||||
|
INSERT INTO MARIUSM_AUTO.nom_lucrari (nrord, id_mod)
|
||||||
|
VALUES (p_nr_comanda, 1200)
|
||||||
|
RETURNING id_lucrare INTO v_id_lucrare;
|
||||||
|
-- ^ RETURNING bypasses dependency on pack_sesiune.dev_idLucrare
|
||||||
|
-- ^ trigger TRG_NOM_LUCRARI_BEFOINS still fires + populates session var,
|
||||||
|
-- but we ignore it and use our local v_id_lucrare instead
|
||||||
|
|
||||||
|
-- 2. INSERT child: dev_ordl (extensia auto, FK la nom_lucrari prin id_lucrare)
|
||||||
|
INSERT INTO MARIUSM_AUTO.dev_ordl
|
||||||
|
(an, luna, id_inspector, id_lucrare, datai, id_util_ad,
|
||||||
|
id_masiniclient, id_asigurator, id_tip, kmint, termen, dataoraad)
|
||||||
|
VALUES
|
||||||
|
(EXTRACT(YEAR FROM v_dataora), EXTRACT(MONTH FROM v_dataora),
|
||||||
|
0, -- id_inspector = 0 (nu folosim inspector în prototype)
|
||||||
|
v_id_lucrare, -- FK la nom_lucrari
|
||||||
|
v_dataora, -- datai
|
||||||
|
p_id_util,
|
||||||
|
p_id_masiniclient,
|
||||||
|
0, -- id_asigurator = 0 (nu folosim asigurator)
|
||||||
|
p_id_tip,
|
||||||
|
p_kmint,
|
||||||
|
p_termen,
|
||||||
|
v_dataora) -- dataoraad
|
||||||
|
RETURNING id_ordl INTO p_id_ordl;
|
||||||
|
|
||||||
|
-- Step 3 (UPDATE dev_masiniclienti.kmint) SKIP — side effect opțional
|
||||||
|
-- pentru prototype. Dacă vrem să-l includem, adăugăm aici:
|
||||||
|
-- UPDATE dev_masiniclienti SET kmint = p_kmint
|
||||||
|
-- WHERE id_masiniclient = p_id_masiniclient;
|
||||||
|
|
||||||
|
p_numar_out := p_nr_comanda;
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
```
|
||||||
|
|
||||||
|
**~40 linii**, două INSERT-uri în ordinea FK-ului parent→child, RETURNING pentru chaining,
|
||||||
|
zero dependency pe `pack_sesiune`. Testabil din SQL Developer:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
DECLARE
|
||||||
|
v_id NUMBER;
|
||||||
|
v_num VARCHAR2(100);
|
||||||
|
BEGIN
|
||||||
|
MARIUSM_AUTO.SP_CREEAZA_COMANDA_PROTOTIP(
|
||||||
|
p_id_tip => 3, -- Regie
|
||||||
|
p_id_masiniclient => 1, -- prima mașină client existentă
|
||||||
|
p_kmint => 150000,
|
||||||
|
p_termen => SYSDATE + 10,
|
||||||
|
p_id_util => 1, -- user test
|
||||||
|
p_nr_comanda => 'R' || TO_CHAR(MARIUSM_AUTO.SEQ_NRORD.NEXTVAL),
|
||||||
|
p_id_ordl => v_id,
|
||||||
|
p_numar_out => v_num
|
||||||
|
);
|
||||||
|
DBMS_OUTPUT.PUT_LINE('id_ordl: ' || v_id || ', nr: ' || v_num);
|
||||||
|
ROLLBACK; -- pentru test, nu commit
|
||||||
|
END;
|
||||||
|
/
|
||||||
|
```
|
||||||
|
|
||||||
|
**Avantaj**: exact pattern-ul pe care îl va folosi Python (`callproc` cu OUT params + manual
|
||||||
|
commit). Dovedește ipoteza #1 pe un SP realist care atinge DOUĂ tabele în ordine corectă,
|
||||||
|
nu pe un `test_out` trivial.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. `tip_comanda` — enum real extras din DDL + seed
|
||||||
|
|
||||||
|
**Sursa primară:** `Scripturi_instalare/tabele.sql:763-794` (DDL) +
|
||||||
|
`Scripturi_instalare/initializari.sql:199-208` (seed INSERT).
|
||||||
|
|
||||||
|
**Tabela reală: `DEV_TIP_DEVIZ`** (nu `dev_tip_comenzi` — user a corectat, dar ground truth
|
||||||
|
din scripturile de instalare zice `dev_tip_deviz`; **confirmă în săpt 3 care e numele în
|
||||||
|
MARIUSM_AUTO** — poate au redenumit-o).
|
||||||
|
|
||||||
|
### DDL
|
||||||
|
|
||||||
|
```sql
|
||||||
|
create table DEV_TIP_DEVIZ
|
||||||
|
(
|
||||||
|
ID_TIP NUMBER(5) not null, -- PK, auto-generat din SEQ_DEV_TIP_DEVIZ
|
||||||
|
DENUMIRE VARCHAR2(50) not null, -- Label afișat în dropdown
|
||||||
|
STERS NUMBER(1) default 0 not null -- Soft delete flag
|
||||||
|
);
|
||||||
|
alter table DEV_TIP_DEVIZ add constraint PK_TIP_DEVIZ primary key (ID_TIP);
|
||||||
|
```
|
||||||
|
|
||||||
|
**Notă:** `update_devize.prg:424` selectează `id_tip, denumire, inch_validare from dev_tip_deviz`
|
||||||
|
— coloana `inch_validare` **NU** e în DDL-ul din `tabele.sql`, deci a fost adăugată post-install
|
||||||
|
printr-o migrație. Așteaptă-te la diferențe între schema "curată" și MARIUSM_AUTO real.
|
||||||
|
|
||||||
|
### FK în `DEV_ORDL`
|
||||||
|
|
||||||
|
Linia 203 din `packages.sql` arată coloana folosită pe tabela de comandă:
|
||||||
|
```sql
|
||||||
|
insert into <schema>.dev_ordl
|
||||||
|
(an, luna, id_inspector, id_lucrare, datai, id_util_ad, id_masiniclient,
|
||||||
|
id_asigurator, id_tip, kmint, termen, dataoraad)
|
||||||
|
values (...)
|
||||||
|
returning id_ordl into pnIdOrdl
|
||||||
|
```
|
||||||
|
|
||||||
|
Coloana este **`id_tip`** (nu `tip_comanda`). Numele `tip_comanda` apare doar în view-ul
|
||||||
|
`vcomenzi` ca alias denormalizat pentru `denumire` din JOIN-ul cu `dev_tip_deviz`.
|
||||||
|
|
||||||
|
### Seed values (install-time, din `initializari.sql:199-208`)
|
||||||
|
|
||||||
|
| ID_TIP | DENUMIRE | Prefix pe `nr_comanda` (din `oproceduri_devize.prg:109-120`) |
|
||||||
|
|---|---|---|
|
||||||
|
| 1 | **POST GARANTIE** | *(niciun prefix)* |
|
||||||
|
| 2 | **GARANTIE** | **G** |
|
||||||
|
| 3 | **REGIE** | **R** |
|
||||||
|
| 4 | **PREGATIRE** | **P** |
|
||||||
|
| 5 | **REGIE 2** | *(niciun prefix — skipped în switch)* |
|
||||||
|
|
||||||
|
**IDs 6 și 7** (cu prefix `PR`, respectiv `C`) **NU sunt în seed-ul de install** — au fost
|
||||||
|
adăugate în producție prin `INSERT INTO dev_tip_deviz` (sau prin UI-ul VFP de admin).
|
||||||
|
Denumirea lor o știe Marius — de confirmat în săpt 3.
|
||||||
|
|
||||||
|
### Verificare în săpt 3
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Lista completă curentă din MARIUSM_AUTO
|
||||||
|
SELECT id_tip, denumire, sters
|
||||||
|
FROM MARIUSM_AUTO.DEV_TIP_DEVIZ
|
||||||
|
ORDER BY id_tip;
|
||||||
|
|
||||||
|
-- Verifică dacă există alias-ul 'dev_tip_comenzi' ca synonym sau view
|
||||||
|
SELECT object_name, object_type
|
||||||
|
FROM all_objects
|
||||||
|
WHERE owner='MARIUSM_AUTO'
|
||||||
|
AND UPPER(object_name) LIKE '%TIP%COM%';
|
||||||
|
```
|
||||||
|
|
||||||
|
### Pentru prototype (decision)
|
||||||
|
|
||||||
|
`ComandaNoua.vue` are nevoie de un dropdown cu 4 opțiuni. **Hardcodarea în Vue** e OK pentru
|
||||||
|
prototype (scope wall: nu există endpoint `/tipuri-comanda`). Recomandare valori:
|
||||||
|
- `{id: 1, label: 'Post Garanție'}`
|
||||||
|
- `{id: 2, label: 'Garanție'}`
|
||||||
|
- `{id: 3, label: 'Regie'}`
|
||||||
|
- `{id: 4, label: 'Pregatire'}`
|
||||||
|
- `{id: 5, label: 'Regie 2'}`
|
||||||
|
- `{id: 6, label: 'Productie'}`
|
||||||
|
- `{id: 7, label: 'Constatare'}`
|
||||||
|
|
||||||
|
Skip ID-ul 5 (REGIE 2, probabil deprecated).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Label-uri Romanian pentru formulare
|
||||||
|
|
||||||
|
**Extrase din context VFP** (nume variabile + procese). Aceste label-uri le folosești în
|
||||||
|
`ComandaNoua.vue`:
|
||||||
|
|
||||||
|
| Câmp UI | Label Romanian | Corespondent VFP |
|
||||||
|
|---|---|---|
|
||||||
|
| Tip comandă | **Tip comandă** | `tip_comanda` / combobox în `frm_gencom` |
|
||||||
|
| Client | **Client** | `nume` (denormalizat) / `id_codclient` |
|
||||||
|
| Mașină | **Mașină** (sau **Număr înmatriculare**) | `nrinmat` / `id_masina` |
|
||||||
|
| Operații solicitate | **Operații solicitate** | `pcSirIDOperatiiClient` |
|
||||||
|
| Km la intrare | **Km la bord** | `pnKmInt` (în VFP se afișează "kmint") |
|
||||||
|
| Ore manoperă estimate | **Ore manoperă** | `pnOreFct` |
|
||||||
|
| Termen execuție | **Termen execuție** (default = `today + gnTermenZileService`) | `pdTermen` |
|
||||||
|
| Observații | **Observații** | `pcObservatii` |
|
||||||
|
| Defecțiuni | **Defecțiuni** | `pcDefectiuni` |
|
||||||
|
| Nr. dosar daună | **Nr. dosar** (doar dacă tip=garanție/asigurare) | `pcNrDosar` |
|
||||||
|
|
||||||
|
**Pentru MVP-ul prototype-ului (4 câmpuri)**: păstrează doar primele 4 din tabel
|
||||||
|
(tip / client / mașină / operații). Restul trec în plan ca TODO-phase2 sau se afișează
|
||||||
|
cu valori default hardcodate.
|
||||||
|
|
||||||
|
**Toast-uri (Romanian, convenție ROA2WEB din `ReceiptCreateUnifiedView.vue`):**
|
||||||
|
- Success: `summary="Comandă creată"`, `detail="Nr ${nr_comanda}"` (ex: "Nr G12345")
|
||||||
|
- Business error: `summary="Validare"`, `detail=<mesajul din SP după strip prefix ORA-20xxx>`
|
||||||
|
- Infra error: `summary="Eroare conexiune"` (503) / `"Eroare internă"` (500)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Funcții PL/SQL utile găsite în VFP source
|
||||||
|
|
||||||
|
Din grep `pack_auto\.` și `pack_comenzi\.` pe tot repo-ul:
|
||||||
|
|
||||||
|
| Funcție/Proc | Scop | Relevantă pentru prototype? |
|
||||||
|
|---|---|---|
|
||||||
|
| `pack_auto.dev_adauga_lucrare` | Creează comandă (17 params) | **DA** — SP-ul principal |
|
||||||
|
| `pack_auto.dev_adauga_oper_fact` | Adaugă operații pe comandă pentru facturare | NU — post-facturare |
|
||||||
|
| `pack_auto.dev_distribuie_timp_n` | Distribuie timp pe norme | NU |
|
||||||
|
| `pack_auto.citeste_discount_contract` | Citește discount-uri contract | NU |
|
||||||
|
| `pack_auto.actualizeaza_deviz` | Updatează deviz | NU |
|
||||||
|
| `pack_auto.getOptiuneInchidere(n)` | Citește opțiune închidere | NU |
|
||||||
|
| `pack_auto.set_perioada_raport` | Setează perioadă raport | NU |
|
||||||
|
| `pack_comenzi.extrage_optiuni(varvalue, id_sectie)` | Citește opțiuni pe secție | **Poate** — pentru `tip_comanda` default |
|
||||||
|
| `pack_comenzi.genereaza_nr_lucrare` | Generează nr. lucrare | **Poate** — alternativ la prefix hardcodat |
|
||||||
|
| `pack_comenzi.sectii_utilizator(id_util, id_sucursala)` | Secții accesibile utilizatorului | **Poate** — pentru multi-section support phase 2 |
|
||||||
|
|
||||||
|
**Pattern observat**: `{call pack_auto.proc(?param, ?@out_param)}` pentru apeluri cu OUT.
|
||||||
|
Confirmă că oracledb async cu OUT params e un pattern deja stabilit în stack-ul VFP → Oracle.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Next steps — săpt 3 task list (actualizat)
|
||||||
|
|
||||||
|
În loc de task-ul vag "deschide VFP producție și caută fluxul de creare comandă":
|
||||||
|
|
||||||
|
- [ ] Clonează `gitea.romfast.ro/romfast/vfp_roaauto` local (dacă nu e clonat)
|
||||||
|
- [ ] Conectează-te la `10.0.20.121/ROA` ca `ROA_WEB` (sau alt user cu acces la
|
||||||
|
`all_objects` pe MARIUSM_AUTO)
|
||||||
|
- [ ] Rulează și salvează în `week3-verification.sql`:
|
||||||
|
```sql
|
||||||
|
-- Verifică existența pachetului pack_auto
|
||||||
|
SELECT object_name, object_type, status
|
||||||
|
FROM all_objects
|
||||||
|
WHERE owner='MARIUSM_AUTO'
|
||||||
|
AND object_name IN ('PACK_AUTO','PACK_COMENZI');
|
||||||
|
|
||||||
|
-- Verifică coloanele din comenzi (confirmă schema de mai sus)
|
||||||
|
SELECT column_name, data_type, data_length, nullable
|
||||||
|
FROM all_tab_columns
|
||||||
|
WHERE owner='MARIUSM_AUTO' AND table_name='COMENZI'
|
||||||
|
ORDER BY column_id;
|
||||||
|
|
||||||
|
-- Găsește tabela/nomenclatorul pentru tip_comanda
|
||||||
|
SELECT table_name
|
||||||
|
FROM all_tables
|
||||||
|
WHERE owner='MARIUSM_AUTO'
|
||||||
|
AND UPPER(table_name) LIKE '%TIP%COM%';
|
||||||
|
|
||||||
|
-- Lista procedurilor din pack_auto (pentru a decide opțiunea 1 vs 3)
|
||||||
|
SELECT procedure_name, overload
|
||||||
|
FROM all_procedures
|
||||||
|
WHERE owner='MARIUSM_AUTO' AND object_name='PACK_AUTO'
|
||||||
|
ORDER BY procedure_name;
|
||||||
|
```
|
||||||
|
- [ ] Decide: opțiunea 1 (reuse `dev_adauga_lucrare`), 2 (wrapper), sau 3 (INSERT direct)
|
||||||
|
- [ ] Confirmă denumirile pentru `tip_comanda` = 4, 6, 7
|
||||||
|
- [ ] Completează secțiunea 5 și 6 de mai sus cu valorile confirmate
|
||||||
|
- [ ] Commit pe branch `feat/service-auto` cu mesaj `docs(service-auto): confirm VFP audit against real MARIUSM_AUTO schema`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 9. Ce NU am găsit încă (gaps pentru săpt 3)
|
||||||
|
|
||||||
|
- **Forma UI reală** — `frm_gencom` e definită într-un `.vcx` (VFP binary class file),
|
||||||
|
nu grep-abil direct. Pentru layout-ul real și ordinea câmpurilor în UI-ul VFP, fie
|
||||||
|
deschid VFP-ul (nu în scope), fie Marius trimite un screenshot în săpt 3.
|
||||||
|
- **Default values** — care sunt valorile default în `frm_gencom`? `pdTermen` are
|
||||||
|
`DATE() + gnTermenZileService` (default 10 zile) — asta e relevant. Restul defaults
|
||||||
|
trebuie verificate.
|
||||||
|
- **Validările client-side** — VFP face probabil validări în `frm_gencom.valid()`.
|
||||||
|
Pentru prototype, planul deja spune "no client-side validation" — nu e gap real.
|
||||||
|
- **Permission model** — `gcAcces=[1;2;3;4;]` apare în `onom_comenzi.prg:14` — sugerează
|
||||||
|
un sistem de ACL cu 4 nivele. Irelevant pentru prototype (1 user), dar de notat pentru
|
||||||
|
TODO-phase2.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**END OF DRAFT** — să fie revăzut și completat în săpt 3, 2026-05-05 → 2026-05-09.
|
||||||
Reference in New Issue
Block a user