feat(api): raspuns minim implicit + corelare cerere-raspuns + sugestie cod in raspuns; coloana # in lista trimiteri

- POST /v1/prezentari: camp optional 'raspuns' (minim implicit / complet);
  forma minima = 6 chei fixe (index, submission_id, vehicul, status, motiv,
  id_prezentare), formatul detaliat ramane la cerere
- corelare rezultat<->cerere: index 0-based + ecou vin/nr_inmatriculare
- sugestia principala de cod RAR pe nemapate (GOLD partajat > SILVER >
  embeddings > fuzzy peste prag), in motiv si in nemapate[].sugestie
  (si pe dry-run /valideaza); suggestion-only, nu se aplica automat
- lista Trimiteri: coloana # cu id-ul trimiterii (desktop + mobil)
- README: sectiune 'Mapari si sugestii de cod RAR'; contract actualizat

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-07-09 08:32:55 +00:00
parent c2beede084
commit 3ba35abed2
16 changed files with 239 additions and 20 deletions

View File

@@ -981,6 +981,40 @@ def enrich_suggestions(
return {"sugestie_principala": sugestie_principala, "surse": surse}
# Prag fuzzy sub care nu propunem nimic (evita sugestii de zgomot in raspunsul API).
FUZZY_SUGESTIE_MIN_SCORE = 60.0
def sugestie_pentru_op(conn, denumire: str | None, nomenclator: list[dict]) -> dict | None:
"""Sugestia principala de cod RAR pentru o operatie nemapata.
Aceeasi precedenta ca editorul de mapari (enrich_suggestions: GOLD partajat >
SILVER > embeddings), cu fallback fuzzy pe nomenclator peste prag. SUGGESTION-ONLY:
nu se aplica automat, doar se afiseaza (raspuns API / editor).
Returneaza {cod_prestatie, nume_prestatie, sursa} sau None (inclusiv pe
pre-filtrul NUL: non-operatiile nu primesc sugestie de cod).
"""
if not denumire:
return None
nume = {r.get("cod_prestatie"): r.get("nume_prestatie") for r in nomenclator}
enriched = enrich_suggestions(conn, denumire)
sp = enriched.get("sugestie_principala")
if sp and sp.get("cod_prestatie"):
cod = str(sp["cod_prestatie"])
return {"cod_prestatie": cod, "nume_prestatie": nume.get(cod), "sursa": sp.get("sursa")}
if enriched.get("surse", {}).get("nul"):
return None
fuzzy = suggest_codes(denumire, nomenclator, limit=1)
if fuzzy and float(fuzzy[0].get("score") or 0) >= FUZZY_SUGESTIE_MIN_SCORE:
return {
"cod_prestatie": fuzzy[0]["cod_prestatie"],
"nume_prestatie": fuzzy[0].get("nume_prestatie"),
"sursa": "fuzzy",
}
return None
def _emite_text_rule_hits(conn, account_id: int, submission_id: int, resolved: list[dict] | None) -> None:
"""Emite `text_rule_hit` in app_events pentru fiecare item rezolvat prin regula text.