Implementeaza PRD 5.6 complet (14 stories, TDD). Doua axe:
Lifecycle trimiteri blocate (Val A):
- submissions_admin.py: sterge/repune scoped (404 cross-account inaintea lui 409 stare)
- reactivare dedup peste `error` cu CAS (WHERE id=? AND status='error'), creds noi in
submissions + accounts.rar_creds_enc; worker invalideaza sesiunea RAR la creds proaspete
(JWT 30h vechi nu mai trimite cu parola gresita); camp aditiv `reactivated:true`
- retentie randuri blocate 30z; purge_expired exclude queued/sending; purge_after curatat
la reactivare/requeue
- API DELETE /v1/prezentari/{id} + /repune (200+JSON); UI butoane + bulk + banner actionabil
Observabilitate:
- app/observ.py log_event: dublu canal app_events (DB) + RotatingFileHandler per-proces,
redactare creds/PII la scriere (redact_pii/vin_partial)
- request_id middleware + X-Request-ID pe toate raspunsurile
- handler global excepții -> 500 envelope 6-chei + request_id (traceback doar in jurnal)
- audit cerere API (api_prezentari/api_auth_esuat) + audit worker (rar_login/tranzitii)
- tab "Jurnal" filtrabil scoped (non-admin doar contul sau); retentie jurnal 90z
- rar_error expus in GET /v1/prezentari/{id} (recovery observabil)
pytest -q: 741 passed, 0 failed. Docs: PRD raport VERIFY, contract endpointuri noi, ROADMAP.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
26 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.
Envelope de eroare imbogatit (PRD 5.4)
Forma unui obiect de eroare
Incepand cu PRD 5.4, fiecare obiect de eroare returnat de gateway contine 6 chei:
| Cheie | Tip | Rol | Back-compat |
|---|---|---|---|
field |
string | null | Campul care a generat eroarea (null daca eroarea e globala) | DA — existent anterior |
message |
string | Mesajul scurt (identic cu cauza cand e disponibila, altfel problema) |
DA — existent anterior |
cod |
string | Identificator stabil de tip eroare (ex. VIN_FORMAT). Camp nou. |
NU — adaugat 5.4 |
problema |
string | Ce s-a intamplat — descriere scurta, inteligibila pentru utilizator | NU — adaugat 5.4 |
cauza |
string | De ce a aparut eroarea — concret; pentru erorile RAR 400, mesajul exact de la RAR (passthrough) | NU — adaugat 5.4 |
fix |
string | Ce trebuie facut pentru remediere | NU — adaugat 5.4 |
Exemplu JSON concret (eroare VIN invalid, returnat de POST /v1/prezentari/valideaza):
{
"field": "vin",
"message": "VIN trebuie sa aiba exact 17 caractere majuscule, fara spatii/caractere speciale si fara O, I, Q.",
"cod": "VIN_FORMAT",
"problema": "VIN invalid",
"cauza": "VIN trebuie sa aiba exact 17 caractere majuscule, fara spatii/caractere speciale si fara O, I, Q.",
"fix": "Verifica VIN-ul pe talon (pozitia E) sau pe caroserie: exact 17 caractere majuscule, fara spatii si fara literele O, I, Q."
}
Nota de back-compat
Cheile field si message sunt pastrate neschimbate pe toate raspunsurile. Cheile cod, problema, cauza, fix sunt aditive (camp nou in plus). Clientii care citesc doar field/message (sau error/message la import) continua sa functioneze fara modificare.
Unde apare envelope-ul imbogatit
1. POST /v1/prezentari/valideaza (dry-run)
Campul erori (array) si campul nemapate (array) din raspuns contin obiecte cu toate cele 6 chei.
2. submissions.rar_error (stocat in DB, vizibil prin GET /v1/prezentari/{id} si in dashboard)
Campul rar_error e superset al formei de mai sus si variaza cu starea submission-ului:
needs_data— array de obiecte{field, message, cod, problema, cauza, fix}:[ { "field": "dataPrestatie", "message": "Data prestatiei nu poate fi anterioara datei de 01.12.2024.", "cod": "RAR_VALIDARE", "problema": "RAR a respins prezentarea", "cauza": "Data prestatiei nu poate fi anterioara datei de 01.12.2024.", "fix": "Corecteaza campul semnalat de RAR (vezi cauza) si reincearca; detaliile exacte sunt in mesajul tehnic RAR." } ]needs_mapping(cod nemapat): obiect cu cheileunmapped(array),cod,problema,cauza,fix:{ "unmapped": ["SCHIMB_ULEI_COMPLET"], "cod": "COD_NEMAPAT", "problema": "Lipseste codul RAR al operatiei", "cauza": "Operatia SCHIMB_ULEI_COMPLET nu are un cod RAR mapat.", "fix": "Alege codul RAR pentru aceasta operatie in tab-ul Mapari (ai sugestii automate)." }needs_mappingcuauto_sendoprit: obiect cuauto_send,cod: "AUTO_SEND_OPRIT",problema,cauza,fix.- Eroare RAR 400: array imbogatit cu
cod: "RAR_VALIDARE"pe fiecare element. - Eroare RAR 401 (creds invalide): obiect cu
cod: "RAR_CREDS_INVALIDE",problema,cauza,fix.
3. Erori de import (POST /v1/import, preview, commit)
Campul detail din raspunsurile de eroare este superset: contine cheile vechi error/message plus cod, problema, cauza, fix.
Exceptii din scope 5.4: erorile de login/signup si CSRF raman mesaje plate (fara envelope imbogatit).
Tabel cod → problema / fix (toate codurile din app/errors.CATALOG)
Validare date prestatie
| Cod | Problema | Fix |
|---|---|---|
VIN_FORMAT |
VIN invalid | Verifica VIN-ul pe talon (pozitia E) sau pe caroserie: exact 17 caractere majuscule, fara spatii si fara literele O, I, Q. |
NR_INMATRICULARE_FORMAT |
Numar de inmatriculare invalid | Foloseste doar litere si cifre majuscule, maxim 10 caractere, fara spatii sau cratima (ex. B123ABC). |
DATA_FORMAT |
Data prestatiei in format gresit | Scrie data ca AAAA-LL-ZZ (ex. 2026-06-22). |
DATA_PREA_VECHE |
Data prestatiei prea veche | RAR accepta prestatii doar incepand cu 01.12.2024; verifica data prestatiei. |
DATA_VIITOR |
Data prestatiei in viitor | Data prestatiei nu poate fi dupa ziua de azi; corecteaza data. |
ODOMETRU_FINAL_FORMAT |
Odometru final invalid | Scrie kilometrajul final ca numar intreg, fara zecimale sau text (ex. 145000). |
ODOMETRU_INITIAL_LIPSA |
Lipseste odometrul initial | Prestatiile R-ODO / I-ODO cer kilometrajul initial; completeaza-l. |
ODOMETRU_INITIAL_FORMAT |
Odometru initial invalid | Scrie kilometrajul initial ca numar intreg, fara zecimale sau text. |
ODOMETRU_INITIAL_ORDINE |
Odometru initial mai mare decat finalul | Kilometrajul initial trebuie sa fie mai mic sau egal cu cel final; verifica cele doua valori. |
PRESTATII_GOALE |
Nicio prestatie | Adauga cel putin o prestatie cu cod RAR valid. |
B64_INVALID |
Imaginea nu este base64 valid | Trimite imaginea codata base64 corect, sau omite campul daca nu ai imagine. |
Mapare operatie
| Cod | Problema | Fix |
|---|---|---|
COD_NEMAPAT |
Lipseste codul RAR al operatiei | Alege codul RAR pentru aceasta operatie in tab-ul Mapari (ai sugestii automate). |
AUTO_SEND_OPRIT |
Necesita confirmare manuala | Codul e mapat cu trimitere automata oprita; verifica randul si pune-l manual in coada. |
Erori RAR (raspuns live de la RAR)
| Cod | Problema | Fix |
|---|---|---|
RAR_VALIDARE |
RAR a respins prezentarea | Corecteaza campul semnalat de RAR (vezi cauza) si reincearca; detaliile exacte sunt in mesajul tehnic RAR. |
RAR_CREDS_INVALIDE |
Credentiale RAR invalide | Verifica email-ul si parola contului RAR in tab-ul Cont; trimiterea nu se reincearca automat la credentiale gresite. |
Import fisier
| Cod | Problema | Fix |
|---|---|---|
IMPORT_FISIER_PREA_MARE |
Fisier prea mare | Imparte fisierul in bucati de maxim 5000 de randuri si incarca-le pe rand. |
IMPORT_ANTET_NECLAR |
Antet de coloane neclar | Asigura-te ca primul rand contine numele coloanelor (ex. VIN, Numar, Data). |
IMPORT_ENCODING |
Codare de caractere nesuportata | Salveaza fisierul ca CSV UTF-8 (sau xlsx) si reincarca. |
IMPORT_FISIER_NERECUNOSCUT |
Fisier nerecunoscut | Incarca un fisier .xlsx sau .csv valid. |
IMPORT_MULTIPLE_SHEETS |
Mai multe foi in fisier | Pastreaza datele intr-o singura foaie sau alege foaia de import. |
IMPORT_FARA_MAPARE_COLOANE |
Coloanele nu sunt mapate | Mapeaza intai coloanele fisierului la campurile cerute, apoi continua. |
IMPORT_CONFIRMARE_GRESITA |
Numar confirmat gresit | Numarul confirmat difera de randurile gata de trimis; verifica preview-ul si reconfirma. |
IMPORT_OVERRIDE_ILIZIBIL |
Editarea anterioara nu se poate citi | Editarea salvata este ilizibila (probabil cheia s-a schimbat); reediteaza randul. |
COLOANE_FORMAT_JSON |
Format de coloane (JSON) invalid | Verifica sintaxa JSON a maparii de coloane (ghilimele duble, acolade inchise corect). |
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).
Lifecycle trimiteri blocate (PRD 5.6)
POST /v1/prezentari — camp aditiv in fiecare SubmissionResult: reactivated: bool.
La resubmit cu aceeasi cheie de continut peste un rand error (ex. parola RAR corectata),
randul se RE-ACTIVEAZA (re-clasificat + creds actualizate) si raspunsul poarta
reactivated: true + starea noua. deduped pastreaza semantica actuala (clientii vechi
care testeaza deduped nu se sparg). Pentru sent/queued/sending/needs_* ->
deduped: true (neschimbat).
DELETE /v1/prezentari/{id}— sterge o trimitere blocata a contului cheii API. 200 + body JSON{ok, submission_id, status_anterior}(NU 204 — clienti VFP string-parse). Scope evaluat INAINTEA starii: cross-account / inexistent -> 404 (acelasi mesaj, B3); own-accountsent/sending-> 409 (conflict de stare).POST /v1/prezentari/{id}/repune— re-pune in coada (error -> queued, re-ruleaza classify). 200 + body JSON{ok, submission_id, status_anterior, status_nou}. Acelasi oracol scope/stare.GET /v1/prezentari/{id}expune ACUM sirar_error(T9) — recovery observabil prin API (de ce a esuat); contine doar coduri/mesaje de validare RAR, niciodata creds.
Web (dashboard, scoped pe sesiune + CSRF): POST /trimitere/{id}/sterge,
POST /trimitere/{id}/repune, POST /trimiteri/sterge-bulk (selectie multipla, doar blocate).
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.
Regula de scope pe cont (B8, PRD 3.2)
Orice GET nou pe /v1/* care atinge submissions sau operations_mapping PORNESTE
cu account_id: int = Depends(resolve_account_id) si clauza de scope pe cont in SQL.
Varianta globala (fara scope) e exceptie justificata explicit — singurul exemplu actual
este GET /v1/nomenclator (cache de referinta RAR fara PII, partajat intre conturi).
Pentru submissions (account_id nullable): foloseste account_scope_clause(account_id)
din app/mapping.py care produce (account_id = ? OR (account_id IS NULL AND ? = 1)).
Randurile legacy cu account_id IS NULL apartin contului 1 (OV-2, back-compat).
Pentru operations_mapping (account_id NOT NULL): WHERE account_id = ? simplu.
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).