From 32408ed3b5236db4ee8270e8059b90f56036d97d Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Wed, 24 Jun 2026 17:34:24 +0000 Subject: [PATCH] docs(exemple): payload-uri de test CSV (import) + JSON (API) cu randuri valide si erori Doua fisiere, unul per canal: prezentari_test.csv (import web) si prezentari_test.json (API). Fiecare contine randuri valide + randuri care declanseaza erorile de validare (VIN/nr/data/odometru, prestatii goale), plus README cu cele 3 niveluri de eroare (shape 422, needs_mapping, needs_data) si comenzi curl. Operatiile folosesc coduri/denumiri proprii de service, nu coduri RAR. Co-Authored-By: Claude Opus 4.8 (1M context) --- exemple/README.md | 131 +++++++++++++++++++++++++++++++++++ exemple/prezentari_test.csv | 16 +++++ exemple/prezentari_test.json | 114 ++++++++++++++++++++++++++++++ 3 files changed, 261 insertions(+) create mode 100644 exemple/README.md create mode 100644 exemple/prezentari_test.csv create mode 100644 exemple/prezentari_test.json diff --git a/exemple/README.md b/exemple/README.md new file mode 100644 index 0000000..0b58519 --- /dev/null +++ b/exemple/README.md @@ -0,0 +1,131 @@ +# 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`. diff --git a/exemple/prezentari_test.csv b/exemple/prezentari_test.csv new file mode 100644 index 0000000..3acc14f --- /dev/null +++ b/exemple/prezentari_test.csv @@ -0,0 +1,16 @@ +VIN;Numar inmatriculare;Data prestatie;Odometru initial;Odometru final;Operatie;Observatii +WVWZZZ1KZAW000123;B123ABC;2026-06-20;;145000;Reparatie motor;Rand valid +WAUZZZ8K9BA123456;CJ45XYZ;2026-06-18;;98230;Intretinere periodica;Rand valid +VF1RFB00567890123;TM10ABC;2026-06-15;;210500;Revizie periodica completa;Rand valid +ZFA31200000654321;B77TST;2026-06-10;304000;305000;Reparatie odometru;Rand valid cu odometru initial +JTDBR32E730123456;CJ08RAR;2026-06-05;;132000;Inlocuire anvelope sezoniere;Rand valid +WVWZZZ1KZAW00Q123;B22ERR;2026-06-12;;120000;Reparatie motor;EROARE VIN_FORMAT - VIN contine Q +WAUZZZ8K9BA12345;B23ERR;2026-06-12;;120000;Intretinere periodica;EROARE VIN_FORMAT - VIN are 16 caractere +1HGCM82633A123456;BUCURESTI2026;2026-06-12;;120000;Revizie periodica;EROARE NR_INMATRICULARE_FORMAT - peste 10 caractere +JH4KA8260MC123456;B24ERR;2024-06-01;;120000;Intretinere periodica;EROARE DATA_PREA_VECHE - inainte de 01.12.2024 +5YJSA1E26HF123456;B25ERR;2027-01-01;;120000;Revizie periodica;EROARE DATA_VIITOR - data in viitor +WP0AB29915S123456;B26ERR;2026-13-05;;120000;Reparatie motor;EROARE DATA_FORMAT - luna 13 inexistenta +SB1KZ3JE60E123456;B27ERR;2026-06-12;;o-suta-mii;Intretinere periodica;EROARE ODOMETRU_FINAL_FORMAT - nu e numar +TMBJF25J5C3123456;B28ERR;2026-06-12;;150000;Reparatie odometru;EROARE ODOMETRU_INITIAL_LIPSA - R-ODO fara initial +WVWZZZ1KZAW000999;B29ERR;2026-06-12;400000;300000;Reparatie odometru;EROARE ODOMETRU_INITIAL_ORDINE - initial mai mare ca final +WAUZZZ8K9BA000888;B30ERR;2026-06-12;;120000;;EROARE PRESTATII_GOALE - fara operatie diff --git a/exemple/prezentari_test.json b/exemple/prezentari_test.json new file mode 100644 index 0000000..294ef16 --- /dev/null +++ b/exemple/prezentari_test.json @@ -0,0 +1,114 @@ +{ + "_comentariu": "Valid + erori de CONTINUT (needs_data). Cazul 422 (item fara cod) NU e aici - respinge toata cererea; vezi README sect. 'Eroare de shape'. Erorile de continut apar ca needs_data DOAR dupa ce operatia e mapata; pana atunci randul e needs_mapping.", + "prezentari": [ + { + "vin": "WVWZZZ1KZAW000123", + "nr_inmatriculare": "B123ABC", + "data_prestatie": "2026-06-20", + "odometru_final": "145000", + "prestatii": [{ "cod_op_service": "REP-MOT", "denumire": "Reparatie motor" }], + "obs": "Rand valid" + }, + { + "vin": "WAUZZZ8K9BA123456", + "nr_inmatriculare": "CJ45XYZ", + "data_prestatie": "2026-06-18", + "odometru_final": "98230", + "prestatii": [{ "cod_op_service": "INTR-PER", "denumire": "Intretinere periodica" }], + "obs": "Rand valid" + }, + { + "vin": "VF1RFB00567890123", + "nr_inmatriculare": "TM10ABC", + "data_prestatie": "2026-06-15", + "odometru_final": "210500", + "prestatii": [ + { "cod_op_service": "REV-PER", "denumire": "Revizie periodica" }, + { "cod_op_service": "SCH-ULEI", "denumire": "Schimb ulei complet" } + ], + "obs": "Rand valid cu doua operatii" + }, + { + "vin": "ZFA31200000654321", + "nr_inmatriculare": "B77TST", + "data_prestatie": "2026-06-10", + "odometru_initial": "304000", + "odometru_final": "305000", + "prestatii": [{ "cod_op_service": "REP-ODO", "denumire": "Reparatie odometru" }], + "obs": "Rand valid cu odometru initial" + }, + { + "vin": "WVWZZZ1KZAW00Q123", + "nr_inmatriculare": "B22ERR", + "data_prestatie": "2026-06-12", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "REP-MOT", "denumire": "Reparatie motor" }], + "obs": "EROARE VIN_FORMAT - VIN contine Q" + }, + { + "vin": "WAUZZZ8K9BA12345", + "nr_inmatriculare": "B23ERR", + "data_prestatie": "2026-06-12", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "INTR-PER", "denumire": "Intretinere periodica" }], + "obs": "EROARE VIN_FORMAT - VIN are 16 caractere" + }, + { + "vin": "1HGCM82633A123456", + "nr_inmatriculare": "BUCURESTI2026", + "data_prestatie": "2026-06-12", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "REV-PER", "denumire": "Revizie periodica" }], + "obs": "EROARE NR_INMATRICULARE_FORMAT - peste 10 caractere" + }, + { + "vin": "JH4KA8260MC123456", + "nr_inmatriculare": "B24ERR", + "data_prestatie": "2024-06-01", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "INTR-PER", "denumire": "Intretinere periodica" }], + "obs": "EROARE DATA_PREA_VECHE - inainte de 01.12.2024" + }, + { + "vin": "5YJSA1E26HF123456", + "nr_inmatriculare": "B25ERR", + "data_prestatie": "2027-01-01", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "REV-PER", "denumire": "Revizie periodica" }], + "obs": "EROARE DATA_VIITOR - data in viitor" + }, + { + "vin": "WP0AB29915S123456", + "nr_inmatriculare": "B26ERR", + "data_prestatie": "2026-13-05", + "odometru_final": "120000", + "prestatii": [{ "cod_op_service": "REP-MOT", "denumire": "Reparatie motor" }], + "obs": "EROARE DATA_FORMAT - luna 13 inexistenta" + }, + { + "vin": "SB1KZ3JE60E123456", + "nr_inmatriculare": "B27ERR", + "data_prestatie": "2026-06-12", + "odometru_final": "o-suta-mii", + "prestatii": [{ "cod_op_service": "INTR-PER", "denumire": "Intretinere periodica" }], + "obs": "EROARE ODOMETRU_FINAL_FORMAT - nu e numar" + }, + { + "vin": "TMBJF25J5C3123456", + "nr_inmatriculare": "B28ERR", + "data_prestatie": "2026-06-12", + "odometru_final": "150000", + "prestatii": [{ "cod_op_service": "REP-ODO", "denumire": "Reparatie odometru" }], + "obs": "EROARE ODOMETRU_INITIAL_LIPSA - R-ODO fara odometru initial" + }, + { + "vin": "WVWZZZ1KZAW000999", + "nr_inmatriculare": "B29ERR", + "data_prestatie": "2026-06-12", + "odometru_initial": "400000", + "odometru_final": "300000", + "prestatii": [{ "cod_op_service": "REP-ODO", "denumire": "Reparatie odometru" }], + "obs": "EROARE ODOMETRU_INITIAL_ORDINE - initial mai mare ca final" + } + ] +}