# Contract RAR AUTOPASS — sursa de adevăr (verificat live) > **Acesta este documentul autoritativ pentru contractul API RAR AUTOPASS.** > Înlocuiește presupunerile din `docs/plans/*` acolo unde diferă. Dacă un plan > contrazice acest fișier, **acest fișier are dreptate**. > > Surse: > - `docs/api-rar-documentatie-oficiala.md` — răspuns oficial de la programatorii RAR. > - Verificare live pe endpoint-ul de **test** (`/public/login` + `getNomenclatorPrestatii`), **2026-06-15**. > - Cod VFP testat (`rar_autopass.prg`) — confirmat ca URL-uri corecte. ## Endpoint-uri | Mediu | Bază | |---|---| | TEST (integrare — doar aici se testează) | `https://apps.rarom.ro/test-rar-autopass` | | PRODUCȚIE | `https://apps.rarom.ro/rar-autopass` | | Swagger test | `https://apps.rarom.ro/test-rar-autopass/swagger-ui/index.html` | ⚠️ `…/v3/api-docs`, `…/v2/api-docs`, `…/swagger-resources` întorc **403** (nu se pot descărca programatic). Swagger-ul nu permite autentificare directă — testarea reală se face din cod / Postman cu credențialele de test. ### Rute confirmate | Operație | Metodă + cale | Stare | |---|---|---| | Login | `POST /public/login` | ✅ verificat live | | Nomenclator prestații | `GET /nomenclator/getNomenclatorPrestatii` | ✅ verificat live (200) | | Adăugare prezentare | `POST /prezentari/postPrezentare` | din VFP testat + doc oficial | > Nota: doc-ul oficial citează operationId-ul Swagger `getPrestatiiNomUsingGET`, dar > **calea reală e `/nomenclator/getNomenclatorPrestatii`** (cea din VFP). `/nomenclator/getPrestatiiNom` > întoarce 403 — nu o folosi. operationId ≠ path. ## ⚠️ Header obligatoriu pe TOATE apelurile: `User-Agent` WAF-ul RAR întoarce **`403 Forbidden` („Request forbidden by administrative rules")** la orice request **fără header `User-Agent` de browser** — inclusiv `/public/login` și chiar `swagger-ui`. curl/clienți fără UA = blocați la WAF înainte de a ajunge la aplicație. MSXML2.XMLHTTP din VFP trimite un UA implicit, de aceea VFP-ul merge. → Gateway-ul (`app/rar_client.py`) **trebuie** să seteze `User-Agent` pe fiecare apel (ex. `Mozilla/5.0`). Confirmat live 2026-06-15: fără UA → 403; cu UA + creds greșite → 401; cu UA + creds corecte → 200. ## Autentificare `POST /public/login` body `{"email": "...", "password": "..."}` → 200, JSON cu câmpuri: ``` idUser, idAgent, cui, nume, prenume, email, token, activationToken, activ, dataActivarii, dataSfarsit, blocat, expirat, tokenExists, authorities ``` - Tokenul JWT se atașează la apelurile securizate: `Authorization: Bearer {token}`. - **JWT claims** (decodate live): `{ jti: "mobileJWT", sub: , authorities: ["ADMIN"], iat, exp }`. - **⚠️ JWT TTL = 108000 secunde = 30 de ORE** (`exp - iat`). **NU e un token scurt.** Vezi „Corecții față de planuri" #1 — schimbă strategia de robustețe a worker-ului. - `idUser` vine din răspunsul de login (ex. live: `6766`); `idAgent` poate fi `null` la login, dar serverul atribuie `idAgent` pe prezentare (vezi exemplul de răspuns oficial: `idAgent: 1587`). ## postPrezentare — payload (request) Câmpurile marcate OPTIONAL pot lipsi; restul sunt obligatorii. `tipPrestatie` **NU se trimite** (îl generează serverul). ```json { "vin": "XXXXXXXXXXXXXXXXX", "nrInmatriculare": "B999GEN", "dataPrestatie": "2024-07-25", "odometruFinal": "9999999", "odometruInitial": null, "prestatii": [ { "codPrestatie": "OE-1", "idPrezentare": null }, { "codPrestatie": "OE-2", "idPrezentare": null } ], "sistemReparat": "null", "status": "FINALIZATA", "obs": "TEST", "b64Image": "UklGR...." } ``` | Câmp | Obligatoriu | Note | |---|---|---| | `vin` | DA | 17 caractere, MAJUSCULE, fără spații/caractere speciale, **fără literele O, I, Q** | | `nrInmatriculare` | DA | max 10 caractere, litere + cifre, MAJUSCULE, fără spații/caractere speciale | | `dataPrestatie` | DA | format `YYYY-MM-DD`; **nu mai devreme de 2024-12-01**, **nu mai târziu de azi** | | `odometruFinal` | DA | indicația km la final (exemplul oficial îl trimite ca string `"9999999"`) | | `odometruInitial` | condiționat | `null` normal; **OBLIGATORIU dacă `prestatii` conține `R-ODO` sau `I-ODO`** (indicația dinainte de reparație/schimbare) | | `prestatii` | DA | listă `{codPrestatie, idPrezentare:null}`; codurile din nomenclator | | `status` | DA | **întotdeauna `"FINALIZATA"`** prin API | | `sistemReparat` | DA (poate fi `"null"`) | exemplul oficial trimite string-ul `"null"`; valori reale nedocumentate (vezi Open Q) | | `obs` | NU (OPTIONAL) | text liber | | `b64Image` | NU (OPTIONAL) | poza odometrului; **nu mai e obligatorie**; dacă se atașează, trebuie format base64 valid | ### postPrezentare — răspuns (success) — VERIFICAT LIVE 2026-06-15 Răspuns real pe contul nostru de test (record creat `data.id=68514`): ```json { "statusCode": 200, "message": "Prezentare adaugata cu succes", "data": { "id": 68514, "dataPrestatie": "2026-06-15", "vin": "WVWZZZ1KZAW000123", "odometruFinal": 123456, "idAgent": 40, "tipPrestatie": "GENERIC", "odometruInitial": null, "idUser": 6766, "sistemReparat": "null", "obs": "TEST GATEWAY", "nrInmatriculare": "B999TST", "listaPrestatii": null, "status": "FINALIZATA", "prestatii": [ { "idPrezentare": 68514, "codPrestatie": "OE-1" }, { "idPrezentare": 68514, "codPrestatie": "OE-2" } ], "b64Image": null } } ``` - `data.id` = ID-ul prezentării la RAR (de reținut ca `idPrezentare` în submission). - **`prestatii[].idPrezentare == data.id`** (live: ambele 68514). Exemplul vechi sugera un număr separat mai mare (599950 vs 59950) — **fals**, e același id. - `idAgent` = **atribuit de server** (login a întors `null`, răspunsul are `40`). - `odometruFinal` trimis ca string `"123456"` → **întors ca număr** `123456` (server normalizează). - `sistemReparat:"null"` acceptat; `b64Image` omis → `null`; `odometruInitial:null` OK. - Câmp extra nedocumentat în răspuns: **`listaPrestatii: null`** (prezent lângă `prestatii`). - `tipPrestatie` = `"GENERIC"` — **generat de server**, nu input client. ### postPrezentare — răspuns (eroare validare) — VERIFICAT LIVE 2026-06-15 HTTP **400**. `data` este un **ARRAY** de `{field, message}` (NU string), un element per eroare: ```json { "statusCode": 400, "message": "Validare eșuată pentru cererea de prezentare.", "data": [ { "field": "vin", "message": "VIN trebuie să aibă exact 17 caractere, fara spatii sau caractere speciale, litere invalide : O, I, Q." }, { "field": "vin", "message": "VIN conține caractere invalide, spatii, sau O, I, Q." } ] } ``` Mesaje exacte capturate live (de mapat în UI/loguri gateway): | Constrângere încălcată | field | message (exact) | |---|---|---| | VIN cu O/I/Q | `vin` | `VIN trebuie să aibă exact 17 caractere, fara spatii sau caractere speciale, litere invalide : O, I, Q.` + `VIN conține caractere invalide, spatii, sau O, I, Q.` | | dataPrestatie < 2024-12-01 | `dataPrestatie` | `Data prestatiei nu poate fi anterioara datei de 01.12.2024.` | | dataPrestatie în viitor | `dataPrestatie` | `Data prestatiei nu poate fi în viitor.` | → Gateway: parsează `data` ca listă de erori de câmp (nu citi `data.message`). Pe success `data` e obiect; pe 400 `data` e array — discriminează după `statusCode`/HTTP code. ## Reguli de validare (server-side RAR, de aplicat și în gateway) Aplicate deja pe ambele medii (test + producție): 1. **dataPrestatie**: `>= 2024-12-01` și `<= azi`. 2. **VIN**: exact 17 caractere, majuscule, fără spații/caractere speciale, **fără O, I, Q**. 3. **nrInmatriculare**: max 10 caractere, litere + cifre, majuscule, fără spații/caractere speciale. 4. **b64Image**: dacă e prezent, trebuie base64 valid. 5. **odometruInitial**: obligatoriu dacă `prestatii` conține `R-ODO` sau `I-ODO`. → Acestea devin reguli Pydantic exacte în `app/api`. Validează la gateway înainte de enqueue (stare `needs_data`) ca să nu primești 4xx de la RAR. ## Nomenclator prestații (18 coduri, verificat live 2026-06-15) | cod | nume | |---|---| | OE-1 | REPARAȚIE | | OE-2 | INTRETINERE | | OE-3 | REVIZIE PERIODICA | | OE-4 | REGLARE FUNCTIONALA | | OE-5 | MODIFICARE CONSTRUCTIVA | | OE-6 | RECONSTRUCTIE | | OE-7 | ACTUALIZARE SOFTWARE | | OE-8 | INLOCUIRE SEZONIERA A ANVELOPELOR | | OE-D | AVARII GRAVE LA SISTEMUL DE DIRECTIE | | OE-F | AVARII GRAVE LA SISTEMUL DE FRANARE | | OE-C | AVARII GRAVE LA STRUCTURA DE REZISTENTA A CAROSERIEI | | OE-S | AVARII GRAVE LA STRUCTURA DE REZISTENTA A SASIULUI | | OE-R | AVARII GRAVE LA UN SISTEM DE RETINERE SI PROTECTIE IN CAZ DE ACCIDENT | | OE-A | AVARII GRAVE LA UN SISTEM AVANSAT DE ASISTENTA A CONDUCATORULUI AUTO (ADAS) | | OE-I | ISTORICUL INDICATIEI ODOMETRULUI (vehicule anterior inmatriculate in alte tari) | | AITLV | INREGISTRARE ATELIER INSPECTIE TAHOGRAFE / LIMITATOARE DE VITEZA | | **R-ODO** | **REPARATIE ODOMETRU** → declanșează `odometruInitial` obligatoriu | | **I-ODO** | **INLOCUIRE ODOMETRU** → declanșează `odometruInitial` obligatoriu | > `numePrestatie` redat prescurtat pentru OE-D…OE-A (în API e textul lung complet). > Nomenclatorul se ia live din API; nu hard-coda — folosește acest tabel doar ca referință. ## Ciclu de viață prezentare (la RAR) — IMPORTANT - API-ul are **un singur scop: INSERT prezentări cu status `FINALIZATA`.** Toate celelalte operațiuni CRUD se fac din interfața web. - **Prezentările `FINALIZATA` NU se pot anula prin API** (nici din web). Doar cele `SALVATA` sunt anulabile/editabile, dar API nu produce `SALVATA`. - Încercarea de anulare a unei `FINALIZATA` întoarce: `EROARE_STATUS_ANULARE("... Id not found.")`. - Corecția datelor eronate (după FINALIZATA) = solicitare la **suport.autopass@rarom.ro** (pe test nu e cazul). **Nu există flux API de corecție/anulare pentru records-urile noastre.** ## Monitorizare (citire prezentări) - Pe **mediul de test**: la interogarea listei de prezentări finalizate **NU primești și `prestatii`** în răspuns. - Pe **producție**: prestațiile sunt disponibile; lista poate fi filtrată după keyword / interval de date și exportată în Excel. - Implicație dashboard: nu te baza pe `prestatii` din listă pe test; le ai în `submissions` local. ## Corecții față de `docs/plans/*` (citește înainte de a refolosi planurile) 1. **JWT „scurt" → de fapt 30 de ORE.** Planurile (`plan-design-review` §„Gestiunea credențialelor", `plan-eng-review` §worker) presupun JWT scurt și mută durabilitatea pe re-push din ROAAUTO („coada acoperă minutele, ROAAUTO acoperă orele"). **Fals.** Cu 30h, **worker-ul singur poate relua peste orice pană RAR realistă** în fereastra tokenului. Re-push-ul din ROAAUTO devine plasă de siguranță secundară, nu mecanismul principal. Re-evaluează: poate nu mai e nevoie de re-push în treapta 1, sau credențialele se pot reține în memorie pe durata penei. 2. **b64Image (poza odometrului) NU mai e obligatorie.** Planurile o tratau ca posibil gap de conformitate / posibil obligatorie. **Rezolvat: opțională.** → Open Question „sursa pozei în ROAAUTO" **se închide** (nu mai e blocantă). Dacă e atașată, doar trebuie base64 valid. 3. **`tipPrestatie` NU e input client** — îl generează serverul (`"GENERIC"`). Open Question #2 (valori acceptate `tipPrestatie`) **se închide pentru request** — nu-l trimite. 4. **`sistemReparat`**: planul presupunea că e derivabil server-side din coduri și nu input liber. **Parțial fals** — apare în request (exemplul oficial trimite string-ul `"null"`). Rămâne open ce valori reale acceptă; sigur: poți trimite `"null"` când nu se aplică. 5. **Anulare / corecție prin API: NU există** pentru records-urile noastre (sunt `FINALIZATA`). Scoate din scope endpoint-urile gateway `PATCH /v1/prezentari/{id}/anulare` și `/corectie` (proxy peste `markPrezentareAnulataById` / `patchPrezentare`) — nu se aplică `FINALIZATA`. Maparea stărilor + „Error & rescue map" din `plan-eng-review` trebuie ajustate. 6. **Regula `needs_data` e acum deterministă:** `odometruInitial` lipsă **doar** când `prestatii` conține `R-ODO` sau `I-ODO`. (Planul vorbea generic de „repair odometru".) 7. **URL nomenclator confirmat** = `/nomenclator/getNomenclatorPrestatii` (nu varianta din operationId Swagger). Constatarea #5 din `plan-eng-review` (URL-uri din VFP, nu din spec) — confirmată. ## Open questions rămase (actualizat) 1. ~~Sursa pozei odometrului~~ — **închis** (poză opțională). 2. `sistemReparat` — ce valori reale acceptă (în afară de `"null"`). De probat la postPrezentare test. 3. Un singur user RAR per agent economic sau mai mulți (`idUser`/`idAgent` — afectează filtrarea monitorizării). 4. ~~JWT TTL~~ — **închis: 30h.** Decizie nouă: simplifică worker-ul; reconsideră necesitatea re-push ROAAUTO. 5. ~~Comportamentul răspunsului de eroare~~ — **închis** (format `data:[{field,message}]`, mesaje capturate mai sus). 6. WAF cere `User-Agent` — **închis/confirmat** (vezi secțiunea de sus). ## ~~Rămas de verificat live (postPrezentare real pe test)~~ — ✅ FĂCUT 2026-06-15 Login + nomenclator + JWT TTL + **postPrezentare** = ✅ toate verificate live. Record de test creat: `data.id = 68514` (FINALIZATA, permanent pe test). Confirmat: - mesajele de eroare exacte (VIN O/I/Q, dată prea veche, dată viitoare) — vezi tabelul de erori; - forma răspunsului success pe contul nostru + `data.id`; - `sistemReparat:"null"` acceptat, `b64Image`/`odometruInitial` omise OK; - header `User-Agent` obligatoriu (altfel 403 WAF). Rămas neprobat: ce alte valori `sistemReparat` (în afară de `"null"`) acceptă (Open Q #2).