Inlocuieste planurile vechi (consolidate/realizate) cu un singur docs/ROADMAP.md: dashboard de progres (Treapta 1+2 DONE LIVE, Etapa 3 TODO) + proces de dezvoltare embedded (PLAN separat de EXECUTE/VERIFY pe sesiuni, PRD per livrabila cu stories atomice, agent team, bootstrap reluabil din starea PRD). - adauga docs/prd/TEMPLATE-prd.md (schelet PRD) - sterge docs/plans/plan.md (Treapta 1 realizat), plan-treapta2.md (Treapta 2 realizat), docs/CONTEXT.md (snapshot neactual) - actualizeaza referintele in README.md si api-rar-contract.md Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
17 KiB
Contract RAR AUTOPASS — sursa de adevăr (verificat live)
Acesta este documentul autoritativ pentru contractul API RAR AUTOPASS. Înlocuiește presupunerile din planurile vechi 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: <email>, 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. idUservine din răspunsul de login (ex. live:6766);idAgentpoate finullla login, dar serverul atribuieidAgentpe 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).
{
"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):
{
"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 caidPrezentareî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 întorsnull, răspunsul are40).odometruFinaltrimis ca string"123456"→ întors ca număr123456(server normalizează).sistemReparat:"null"acceptat;b64Imageomis →null;odometruInitial:nullOK.- 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:
{
"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):
- dataPrestatie:
>= 2024-12-01și<= azi. - VIN: exact 17 caractere, majuscule, fără spații/caractere speciale, fără O, I, Q.
- nrInmatriculare: max 10 caractere, litere + cifre, majuscule, fără spații/caractere speciale.
- b64Image: dacă e prezent, trebuie base64 valid.
- odometruInitial: obligatoriu dacă
prestatiiconțineR-ODOsauI-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 |
numePrestatieredat 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
FINALIZATANU se pot anula prin API (nici din web). Doar celeSALVATAsunt anulabile/editabile, dar API nu produceSALVATA.- Încercarea de anulare a unei
FINALIZATAîntoarce:EROARE_STATUS_ANULARE("... Id not found.").
- Încercarea de anulare a unei
- 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) — VERIFICAT LIVE 2026-06-15
Rută: GET /prezentari/getAllPrezentariFinalizate (Bearer). Confirmat live.
Răspuns: {statusCode, message, data: {totalCount, content: [...]}} — listă în data.content.
Fiecare item din content (live):
{
"id": 68514, "dataPrestatie": "2026-06-15", "vin": "WVWZZZ1KZAW000123",
"odometruFinal": 123456, "idAgent": 40, "tipPrestatie": null,
"odometruInitial": null, "idUser": 6766, "sistemReparat": null, "obs": "...",
"nrInmatriculare": "B999TST", "listaPrestatii": null, "status": "FINALIZATA",
"prestatii": null, "b64Image": null
}
odometruFinale NUMĂR (int) în listare (deși lapostPrezentarese trimite string). Reconcilierea compară ca int.- Pe test:
prestatiivinenull(confirmă: nu te baza peprestatiidin listă — le ai local însubmissions). - Filtrele NU funcționează pe test:
?vin=,?search=,?keyword=,?dataPrestatie=sunt IGNORATE (întorc tot setul).?page=&size=rup răspunsul (non-JSON). → fetch tot setul, filtrează client-side. Pe prod doc-ul promite filtrare keyword/dată + export Excel (de re-verificat pe prod). - RAR acceptă DUPLICATE: live există 2 perechi de records identice pe
vin+dataPrestatie+odometruFinal(id 63622≡63625, 63623≡63626). De aceea reconcilierea pe răspuns pierdut e necesară, iar matcher-ul alege id-ul maxim când există mai multe potriviri.
Reconciliere (T2): înainte de re-send pe un rând
sending, GET finalizate, match pevin + dataPrestatie + odometruFinal(int); dacă există → marcheazăsentcu id-ul găsit, NU re-trimite.
Corecții față de planurile inițiale (context istoric)
- 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. - 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.
tipPrestatieNU e input client — îl generează serverul ("GENERIC"). Open Question #2 (valori acceptatetipPrestatie) se închide pentru request — nu-l trimite.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ă.- Anulare / corecție prin API: NU există pentru records-urile noastre (sunt
FINALIZATA). Scoate din scope endpoint-urile gatewayPATCH /v1/prezentari/{id}/anulareși/corectie(proxy pestemarkPrezentareAnulataById/patchPrezentare) — nu se aplicăFINALIZATA. Maparea stărilor + „Error & rescue map" dinplan-eng-reviewtrebuie ajustate. - Regula
needs_datae acum deterministă:odometruInitiallipsă doar cândprestatiiconțineR-ODOsauI-ODO. (Planul vorbea generic de „repair odometru".) - URL nomenclator confirmat =
/nomenclator/getNomenclatorPrestatii(nu varianta din operationId Swagger). Constatarea #5 dinplan-eng-review(URL-uri din VFP, nu din spec) — confirmată.
API gateway (ROAAUTO -> gateway): mapare operatii (hibrid, 2026-06-15)
Aceasta e suprafata gateway-ului, nu RAR. Un item din prestatii la
POST /v1/prezentari poate veni in doua forme (cel putin una obligatorie):
| Camp item | Note |
|---|---|
cod_prestatie |
cod RAR direct (ex. OE-1). Trece neatins -> validare T3 -> coada. |
cod_op_service |
cod intern ROAAUTO. Gateway-ul il traduce in cod RAR prin operations_mapping. |
denumire |
denumirea operatiei ROAAUTO; folosita pentru fuzzy lookup in editor. |
Daca lipsesc ambele coduri -> 422 (shape). Op cu cod_op_service necunoscut
(fara mapare) -> submission needs_mapping (NU se trimite la RAR), apare in editorul
web. La salvarea maparii, submission-urile blocate pe acel cod se re-rezolva automat
(-> queued, sau needs_data daca regula odometru/continut cere asta). Codul RAR
rezolvat se scrie inapoi in payload_json, deci payload builder + worker raman
code-driven.
Endpointuri noi:
GET /v1/mapari/pending— operatii nemapate distincte + sugestii fuzzy ({cod_prestatie, nume_prestatie, score}).POST /v1/mapari{account_id?, cod_op_service, cod_prestatie, auto_send}— upsert mapare + re-rezolvare. Respingecod_prestatieinexistent in nomenclator (422).- Web:
GET /_fragments/mapari(editor HTMX),POST /mapari(form, salveaza + re-randeaza).
Fuzzy: rapidfuzz.token_sort_ratio pe denumire normalizata (fara diacritice, upper).
Nomenclatorul se ia live din RAR (worker upsert la fiecare login); seed fallback
de 18 coduri la boot (app/nomenclator_seed.py) ca editorul sa mearga offline.
Auth API-key (CORE) inca neimplementat -> account_id curge ca NULL si e atribuit
contului default id=1 (seed in schema); cand auth livreaza, account_id real curge natural.
Open questions rămase (actualizat)
Sursa pozei odometrului— închis (poză opțională).sistemReparat— ce valori reale acceptă (în afară de"null"). De probat la postPrezentare test.- Un singur user RAR per agent economic sau mai mulți (
idUser/idAgent— afectează filtrarea monitorizării). JWT TTL— închis: 30h. Decizie nouă: simplifică worker-ul; reconsideră necesitatea re-push ROAAUTO.Comportamentul răspunsului de eroare— închis (formatdata:[{field,message}], mesaje capturate mai sus).- 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/odometruInitialomise OK;- header
User-Agentobligatoriu (altfel 403 WAF).
Rămas neprobat: ce alte valori sistemReparat (în afară de "null") acceptă (Open Q #2).