# Exemple payload pentru testare Doua fisiere, cate unul pentru fiecare canal de intrare (vezi `CLAUDE.md` -> Arhitectura). Fiecare contine **randuri valide + randuri de eroare** in acelasi fisier: | Fisier | Canal | Continut | |---|---|---| | `prezentari_test.csv` | Import web (Treapta 2) | 5 randuri valide + 10 randuri de eroare | | `prezentari_test.json` | API (`POST /v1/prezentari`) | 4 prezentari valide + 9 cu erori de continut | Sunt doua fisiere si nu unul singur fiindca CSV (import) si JSON (API) sunt **canale diferite, cu formate diferite** — nu pot fi acelasi fisier. Operatiile sunt **denumiri/coduri proprii de service, NU coduri RAR** (`OE-1`, `R-ODO` etc.). Asta reflecta realitatea: utilizatorul nu cunoaste si nu trimite codurile RAR — gateway-ul le traduce prin maparile contului (`operations_mapping` / reguli text), iar ce nu se rezolva intra in editorul web (`needs_mapping`) cu sugestii fuzzy pe nomenclator. ## Maparea coloanelor la import La upload, importul sugereaza maparea coloanelor (fuzzy). Antetele din `prezentari_test.csv` se auto-sugereaza corect: | Coloana fisier | Camp canonic | |---|---| | `VIN` | `vin` | | `Numar inmatriculare` | `nr_inmatriculare` | | `Data prestatie` | `data_prestatie` | | `Odometru initial` / `Odometru final` | `odometru_initial` / `odometru_final` | | `Operatie` | `operatie` (-> `cod_op_service`) | | `Observatii` | `obs` | **Important — ce operatie se trimite:** - Operatia (ce s-a lucrat) trebuie sa fie pe coloana mapata la campul **`Operatie`**. - Campul **`Denumire operatie`** NU este o operatie de sine statatoare. Singur, nu produce nimic — doar adauga o descriere unei operatii care exista deja pe `Operatie`. - Regula simpla: **fara `Operatie` nu exista prestatie** (randul pica pe `PRESTATII_GOALE`). In `prezentari_test.csv`, coloana cu operatia se cheama `Operatie` si contine **text liber** (ex. „Reparatie motor"): `cod_op_service` devine textul integral, iar `denumire` = acelasi text, deci sugestia fuzzy din editor are pe ce lucra. Daca softul tau trimite un cod intern scurt (ex. `REP-MOT`), il pui tot pe coloana `Operatie` — se comporta identic. ## Reguli de validare (din `docs/api-rar-contract.md`) - **VIN**: exact 17 caractere, MAJUSCULE, fara spatii/caractere speciale, fara literele O, I, Q. - **nr_inmatriculare**: max 10 caractere, litere + cifre, fara spatii. - **data_prestatie**: format `AAAA-LL-ZZ`, intre `2024-12-01` si azi. - **odometru_initial**: obligatoriu doar cand operatia se mapeaza la `R-ODO` / `I-ODO`, si trebuie sa fie <= odometru_final. ## Cele trei niveluri de eroare (ordinea verificarii) 1. **Shape (422)** — JSON malformat / item de prestatie fara nici un cod. Pydantic respinge **intreaga cerere** inainte de orice procesare. Nu se aplica la CSV (la import, un rand fara operatie devine pur si simplu `PRESTATII_GOALE`, nu 422). Vezi sect. „Eroare de shape". 2. **Mapare (`needs_mapping`)** — operatia nu are cod RAR mapat. Randul NU se trimite; apare in editor. Aici ajung implicit **toate** randurile din exemple (utilizatorul nu da coduri RAR). 3. **Continut (`needs_data`)** — VIN/data/odometru invalide. Validarea de continut ruleaza **DOAR dupa ce operatia e mapata** (nivelul 2 trece). Deci randurile de eroare apar intai ca `needs_mapping`; abia dupa ce mapezi operatia (ex. „Reparatie motor" -> `OE-1`) eroarea de continut iese la suprafata ca `needs_data`. > Exceptie utila: un rand **fara nicio operatie** (ultimul rand din CSV) nu are ce mapa, deci > pica direct pe `PRESTATII_GOALE` (`needs_data`), vizibil imediat la preview. ### Erori de continut acoperite (randurile `EROARE ...`) | Cod eroare | Cum e declansat in exemple | |---|---| | `VIN_FORMAT` | VIN cu litera Q (`...00Q123`) si VIN de 16 caractere | | `NR_INMATRICULARE_FORMAT` | nr de 13 caractere (`BUCURESTI2026`) | | `DATA_FORMAT` | data `2026-13-05` (luna inexistenta) | | `DATA_PREA_VECHE` | data `2024-06-01` (inainte de 01.12.2024) | | `DATA_VIITOR` | data `2027-01-01` | | `ODOMETRU_FINAL_FORMAT` | odometru final `o-suta-mii` (nu e numar) | | `ODOMETRU_INITIAL_LIPSA` | operatie `R-ODO` fara odometru initial | | `ODOMETRU_INITIAL_ORDINE` | odometru initial `400000` > final `300000` | | `PRESTATII_GOALE` | rand fara operatie deloc (doar in CSV) | Forma erorii returnate are 6 chei (`field, message, cod, problema, cauza, fix`) — vezi `docs/api-rar-contract.md` sect. „Envelope de eroare imbogatit". ## Fluxul complet (de la upload la trimitere) 1. **La upload / POST**: niciun cod nu e mapat inca -> toate randurile devin `needs_mapping`. 2. **In editorul de mapari** (tab Mapari / `GET /v1/mapari/pending`): fiecare operatie distincta apare cu sugestii fuzzy pe nomenclator. Alegi codul RAR si salvezi. 3. **Re-rezolvare automata**: la salvarea maparii, submission-urile blocate se re-rezolva -> `queued` (sau `needs_data` daca validarea de continut mai cere ceva, ex. odometru initial la `R-ODO`). 4. **Worker**: trimite cele `queued` la RAR (doar cand send-ul e activat). ## Comenzi de test Din radacina proiectului, cu API-ul pornit (`./start.sh test both` sau `uvicorn app.main:app --reload --port 8010`): ```bash # CSV — import web (valid + erori) curl -F "file=@exemple/prezentari_test.csv" http://localhost:8010/v1/import # JSON — API (valid + erori de continut) curl -X POST http://localhost:8010/v1/prezentari \ -H "Content-Type: application/json" \ --data @exemple/prezentari_test.json # Dry-run (nu enqueue, doar verdictul): acelasi body pe /valideaza curl -X POST http://localhost:8010/v1/prezentari/valideaza \ -H "Content-Type: application/json" \ --data @exemple/prezentari_test.json ``` ### Eroare de shape (422) — exemplu inline Cazul 422 nu poate sta in `prezentari_test.json` (un singur item fara cod respinge toata cererea). Il declansezi cu un body minimal — prestatie fara `cod_prestatie` si fara `cod_op_service` (asteapta HTTP 422): ```bash curl -i -X POST http://localhost:8010/v1/prezentari \ -H "Content-Type: application/json" \ -d '{"prezentari":[{"vin":"WVWZZZ1KZAW000123","nr_inmatriculare":"B123ABC","data_prestatie":"2026-06-20","odometru_final":"145000","prestatii":[{"denumire":"fara niciun cod"}]}]}' ``` ### Optiuni pe JSON (`POST /v1/prezentari`) - `rar_credentials: {email, password}` — creds RAR efemere (top-level, nu se persista). - `on_unmapped_error: true` — respinge codurile necunoscute/nemapate fara enqueue (`submission_id=null`, `erori=[COD_NEMAPAT]`) in loc de `needs_mapping`. - `odometru_final` este **string** (per contract); `odometru_initial` doar la `R-ODO`/`I-ODO`.