Files
rar-autopass/docs/prd/prd-5.7-raspuns-onest-mapare-inline.md
Claude Agent ac57b9250a feat(5.7): raspuns API onest la blocaje + mapare inline din detaliu
Raportat din client VFP: POST /v1/prezentari raspundea submission_id+status
fara motiv pe randuri blocate (erori se popula doar pe on_unmapped_error=True),
deci un needs_data/needs_mapping parea succes.

API (aditiv): SubmissionResult += nemapate + motiv. create_prezentari
populeaza erori (validare continut, 3 niveluri) / nemapate (coduri fara
mapare, COD_NEMAPAT) / motiv (rezumat uman) pe TOATE caile non-queued —
enqueue, respins (on_unmapped_error=True) si reactivare dedup peste error,
prin helperele _rezultat_enqueue / _rezultat_respins / _motiv_clasificare.
on_unmapped_error=True pastreaza erori=COD_NEMAPAT (compat clienti vechi).

Web: mapare inline in panoul de detaliu trimitere — ruta
POST /trimitere/{id}/mapeaza (reuse save_mapping + reresolve_account, scoped
sesiune + CSRF, re-rezolva pe batch_id-ul randului), helper
_nemapate_pentru_submission + context in _detaliu_ctx, sectiune in
_trimitere_detaliu.html (selector cod RAR cu sugestie fuzzy preselectata).
Apare doar pe operatii nemapate reale (nu pe auto_send=0).

/code-review high: reparat raspuns neonest la reactivare + dublu
load_nomenclator in _detaliu_ctx.

Teste: pytest -q 765 passed. Backend trimitere (worker/masina stari/
idempotenta) si schema NEATINSE. PRD: docs/prd/prd-5.7-*.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 20:51:16 +00:00

10 KiB

PRD 5.7 — Raspuns API onest la blocaje + mapare inline din detaliu

Stare: inchis

Proces complet: docs/ROADMAP.md §5. Contract RAR (sursa de adevar): docs/api-rar-contract.md. Starea trece: draft → aprobat → in-executie → verify-pass → inchis (actualizata de lead).

1. Obiectiv

Doua goluri raportate din clientul Visual FoxPro (2026-06-23):

  1. Raspunsul POST /v1/prezentari minte prin omisiune. Cand o prezentare e blocata (needs_data — date respinse de RAR; needs_mapping — cod fara mapare), API-ul intoarce {submission_id, status} fara detalii: campul erori ramane gol (se populeaza azi DOAR pe ramura on_unmapped_error=True). Un integrator care verifica "am submission_id si erori e gol → e ok" trateaza un rand blocat ca succes. Screenshot live: rand needs_data (data in viitor) pe care clientul l-a primit "fara erori". Reparam: raspunsul devine onest — pentru orice status != queued expune erori (validare continut), nemapate (coduri fara mapare) si un motiv uman pe o linie. Strict aditiv (campuri noi + populare a unui camp existent) — clientii vechi nu se rup.

  2. Maparea unui cod nemapat cere drum prin tab-ul Mapari. Din panoul de detaliu al unei trimiteri needs_mapping, userul vede "Lipseste codul prestatiei" dar trebuie sa navigheze in alt tab ca sa o mapeze. Adaugam mapare inline: chiar in panoul de detaliu, fiecare operatie nemapata primeste un selector de cod RAR (cu sugestii fuzzy preselectate) si un buton care salveaza maparea + re-rezolva submission-ul pe loc.

Etapa 5 (ergonomie & integrare). Niciun apel live la RAR.

2. Non-Goals (anti scope-creep)

  • NU schimba masina de stari, worker-ul, idempotency-ul, schema. Zero coloane noi. classify_prezentare ramane sursa unica de clasificare (invariantul dry-run din 5.2 se pastreaza).
  • NU schimba comportamentul on_unmapped_error (ramura error/respins): erori cu COD_NEMAPAT ramane exact ca azi (lock-uit de test_on_unmapped_error_respinge_fara_enqueue). Doar adaugam nemapate + motiv si pe acea ramura.
  • NU imbogateste raspunsul deduped cu erori/motiv: un dedup intoarce starea curenta a randului existent fara re-clasificare (clientul poate cere GET /v1/prezentari/{id} pentru detaliu). Pastram semantica actuala a caii de dedup.
  • NU atinge /valideaza (dry-run-ul are deja erori+nemapate; ramane referinta de forma).
  • NU schimba editorul din tab-ul Mapari — maparea inline il completeaza, nu il inlocuieste; reutilizeaza save_mapping + reresolve_account (aceeasi logica, fara ramura noua).
  • NU mapare inline pe needs_data — acolo exista deja corectia de campuri (US-010, 3.5). Inline-ul de mapare apare DOAR pe needs_mapping cu operatii nemapate reale.

3. Stories atomice

US-001: Raspuns POST /v1/prezentari onest pentru randuri blocate

Ca integrator (ROAAUTO / soft propriu / punte VFP) vreau ca raspunsul la enqueue sa-mi spuna de ce un rand e blocat (nu doar status), pentru ca sa nu tratez un needs_data/needs_mapping drept succes si sa pot reactiona programatic.

  • Depinde de: —
  • Fisiere: app/models.py (SubmissionResult += nemapate, motiv), app/api/v1/router.py (create_prezentari populeaza din cl), tests/test_api.py + tests/test_mapping.py (~4 fisiere)
  • Test intai (RED): tests/test_api.py
    • test_needs_data_intoarce_erori (data in viitor → status="needs_data", erori negol cu cod="DATA_VIITOR" pe field="data_prestatie", motiv negol)
    • test_vin_invalid_intoarce_erori_pe_camp (VIN cu O/I/Q → erori cu field="vin")
    • test_needs_mapping_intoarce_nemapate (cod necunoscut → status="needs_mapping", nemapate negol cu cod_op_service, erori=[], motiv negol)
    • test_queued_fara_erori_nemapate (valid → erori=[], nemapate=[], motiv null)
    • tests/test_mapping.py::test_on_unmapped_error_pastreaza_erori (regresie: on_unmapped_error=True inca da erori[0].cod=="COD_NEMAPAT"; + acum nemapate negol)
  • Acceptance criteria:
    • SubmissionResult are campurile noi nemapate: list[dict] = [] si motiv: str | None = None (aditiv; erori ramane).
    • Pe enqueue cu status="needs_data": erori = exact validate_prezentare (3 niveluri), nemapate=[], motiv = rezumat uman pe o linie.
    • Pe enqueue cu status="needs_mapping" din coduri nemapate: nemapate = [{cod_op_service, denumire, cod, problema, cauza, fix, message}], erori=[], motiv negol.
    • Pe enqueue cu status="needs_mapping" din auto_send=0: motiv explica "confirmare manuala" (chiar daca erori/nemapate sunt goale).
    • Pe status="queued": erori=[], nemapate=[], motiv=None.
    • Ramura on_unmapped_error=True (status="error", submission_id=None): erori neschimbat (COD_NEMAPAT), nemapate negol, motiv negol.
    • Raspunsul nu contine niciodata echo de creds (password) — validat pe corpul serializat.
  • Verificare E2E: POST /v1/prezentari din client (sau curl) cu data in viitor → JSON cu erori vizibile; cu cod intern → nemapate.

US-002: Backend mapare inline din panoul de detaliu (ruta + context)

Ca operator in dashboard vreau sa mapez codul unei operatii direct din detaliul trimiterii pentru ca sa deblochez randul fara sa schimb tab-ul.

  • Depinde de: —
  • Fisiere: app/web/routes.py (helper _nemapate_pentru_submission, context in _detaliu_ctx, ruta POST /trimitere/{id}/mapeaza), tests/test_web_mapare_inline.py (~2 fisiere)
  • Test intai (RED): tests/test_web_mapare_inline.py
    • test_detaliu_needs_mapping_arata_operatii_nemapate (GET detaliu pe needs_mapping → HTML contine selectorul de cod RAR + denumirea operatiei + cel putin o sugestie)
    • test_mapeaza_inline_deblocheaza_randul (POST /trimitere/{id}/mapeaza cu cod valid → submission trece queued, maparea apare in operations_mapping)
    • test_mapeaza_inline_cod_necunoscut_respins (cod inexistent in nomenclator → mesaj eroare, submission ramane needs_mapping, nicio mapare salvata)
    • test_mapeaza_inline_scoped_pe_sesiune (POST pe submission al altui cont → 404, fara mapare)
    • test_mapeaza_inline_csrf_obligatoriu (fara token → 403)
    • test_mapeaza_inline_respecta_batch (submission din import cu batch_id → re-rezolvarea il atinge)
  • Acceptance criteria:
    • _detaliu_ctx pe status="needs_mapping" expune nemapate_inline = [{cod_op_service, denumire, suggestions:[{cod_prestatie, nume_prestatie, score}]}] + nomenclator.
    • POST /trimitere/{id}/mapeaza (Form: cod_op_service, cod_prestatie, auto_send, csrf_token): verifica CSRF, scope pe sesiune (404 cross-account/inexistent ca restul rutelor), respinge cod absent din nomenclator (re-randeaza detaliul cu mesaj), altfel save_mapping + reresolve_account cu batch_id al randului, re-randeaza detaliul cu starea noua + mesaj.
    • Re-rezolvarea foloseste EXACT save_mapping/reresolve_account existente (fara logica noua de clasificare); randurile-frate cu aceeasi operatie din acelasi batch se deblocheaza si ele.
    • Raspunsul trimite HX-Trigger: trimiteriChanged (lista Trimiteri se reimprospateaza).
  • Verificare E2E: dashboard pe needs_mapping → alegi cod RAR inline → randul devine queued.

US-003: UI mapare inline in _trimitere_detaliu.html

Ca operator vreau un formular clar in detaliu (operatie + sugestii + selector cod + salveaza) pentru ca maparea sa fie evidenta si rapida.

  • Depinde de: US-002
  • Fisiere: app/web/templates/_trimitere_detaliu.html, (eventual reuse din _macros.html), tests/test_web_mapare_inline.py (asserts pe HTML)
  • Test intai (RED): acoperit de US-002 (test_detaliu_needs_mapping_arata_operatii_nemapate)
  • Acceptance criteria:
    • Sectiune "Mapeaza codul operatiei" vizibila DOAR cand status="needs_mapping" si exista nemapate_inline; un <form hx-post="/trimitere/{id}/mapeaza"> per operatie, hx-target="#trimitere-detaliu".
    • Selector cod RAR populat din nomenclator, cu sugestia top (score >= 60) preselectata; sugestiile afisate cu procent (ca in tab-ul Mapari).
    • Include csrf_token + comutatorul auto-send (reuse ui.autosend_toggle, default pornit).
    • Mesaj de rezultat (succes/eroare) randat la re-render, fara a pierde restul panoului.
  • Verificare E2E: idem US-002 (browser HTMX).

4. Riscuri

  • Divergenta clasificarii: maparea inline NU trebuie sa reimplementeze rezolvarea. Mitigare: reutilizeaza save_mapping + reresolve_account (singura cale, ca tab-ul Mapari) — orice corectie de comportament ramane intr-un singur loc.
  • Scope batch: reresolve_account(batch_id=None) atinge doar canalul API (batch_id IS NULL). Un submission de import (batch_id setat) nu s-ar debloca. Mitigare: ruta paseaza batch_id-ul randului (test dedicat test_mapeaza_inline_respecta_batch).
  • Compat raspuns: noile campuri trebuie sa fie pur aditive. Mitigare: defaulturi goale + test de regresie pe on_unmapped_error si pe forma deduped.

5. Intrebari deschise — REZOLVATE (poarta de aprobare, 2026-06-23)

  • Q1 [DA]: pastram AMBELE — campuri structurate (erori/nemapate) pentru masini + motiv (string uman pe o linie) pentru log/UI rapida. Implementat ca atare.
  • Q2 [DA]: maparea inline apare DOAR pe operatii nemapate reale. Pe needs_mapping din auto_send=0 (cod deja mapat, doar trimitere oprita) NU se afiseaza — _nemapate_pentru_submission intoarce lista goala in acel caz; confirmarea ramane in tab-ul Mapari.

6. Valuri de executie (graful de dependente)

Val 1: [US-001]  [US-002]     ← fisiere disjuncte (router/models vs web routes) → paralel
Val 2: [US-003]               ← deblocat de US-002 (template peste contextul nou)

Raport VERIFY

Completat de subagentul verificator (context curat) in faza VERIFY — vezi ROADMAP §5.6. PASS/FAIL per criteriu, cu dovezi (output pytest citat, E2E pe RAR test). Lipseste pana la VERIFY.