T5 reinterpretat: nu import DBF, ci editor web al maparii operatie ROAAUTO -> cod RAR, cu fuzzy lookup si validare de catre utilizator. - Contract hibrid: item prestatie accepta cod_prestatie (RAR direct, back-compat) SAU cod_op_service+denumire (mapat de gateway prin operations_mapping). - Ingestie: op intern necunoscut -> submission needs_mapping (nu pleaca la RAR); codul rezolvat se scrie inapoi in payload_json -> payload builder + worker neatinse. - Editor HTMX (_mapari.html + GET /_fragments/mapari, POST /mapari): listeaza op-urile nemapate, fuzzy preselecteaza codul RAR, save -> re-rezolvare automata (queued / needs_data). - Fuzzy: rapidfuzz.token_sort_ratio pe denumire normalizata (fara diacritice). - Nomenclator: seed fallback 18 coduri la boot (offline) + refresh live din worker. - Cont default id=1 cat timp auth API-key (CORE) nu exista (account_id NULL). - Endpointuri API: GET /v1/mapari/pending, POST /v1/mapari (respinge cod inexistent). - 15 teste noi (tests/test_mapping.py); 69 pass total. - Contract actualizat (docs/api-rar-contract.md), rapidfuzz==3.14.5 in requirements. 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
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: <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 docs/plans/* (citește înainte de a refolosi planurile)
- 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).