feat(mapari): regula 'exclude de la declarare' per operatie + stare preview Nedeclarat

Operatiile care nu se declara la RAR (ex. ITP facturat in service) primesc o
regula exclus=1 in operations_mapping, setabila din panoul de mapare al
preview-ului de import si din tab-ul Mapari (optiunea 'Nu se declara la RAR').

- resolve_prestatii(excluded_ops): item nemapat cu op exclusa -> adnotat exclus,
  nu mai e needs_mapping; precedenta: cod explicit > exclus > mapare > reguli text
- split_prestatii_excluse: itemii exclusi nu intra niciodata in payload/cheie
- preview import: rand cu toate operatiile excluse -> stare 'excluded'
  (eticheta Nedeclarat), necomis; operatia dispare din panoul de mapat
- reresolve/corectie/API: submission cu toate operatiile excluse -> needs_data
  cu motiv explicit; ingestia API trateaza excluderea la clasificare
- migrare: coloana operations_mapping.exclus + rebuild import_rows pentru
  CHECK-ul resolved_status cu 'excluded' (o singura data, gardat pe sqlite_master)
- fix flake: clamp similaritate embeddings la [-1,1] (float32 dadea 1.0000001)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-07-07 17:49:47 +00:00
parent c6e29aa65d
commit 0bc618763e
14 changed files with 616 additions and 37 deletions

View File

@@ -31,6 +31,7 @@ from ...mapping import (
account_or_default,
account_scope_clause,
classify_prezentare,
load_excluded_ops,
load_mapping_meta,
load_nomenclator_codes,
load_text_rules,
@@ -69,13 +70,13 @@ def _effective_on_unmapped_error(conn, acct: int, req_value: bool | None) -> boo
return bool(row["on_unmapped_error_default"]) if row else False
def _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules=None) -> dict:
def _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules=None, excluded_ops=None) -> dict:
"""classify_prezentare + aplicarea modului on_unmapped_error.
Cand exista coduri nemapate si error_mode=True, marcheaza outcome-ul ca respingere
(blocked_error=True): rutele NU mai fac enqueue, ci intorc o eroare per-element.
"""
cl = classify_prezentare(content, mapping, mapping_meta, valid_codes, text_rules)
cl = classify_prezentare(content, mapping, mapping_meta, valid_codes, text_rules, excluded_ops)
cl["blocked_error"] = bool(cl["unmapped"]) and error_mode
return cl
@@ -186,6 +187,7 @@ def create_prezentari(
valid_codes = load_nomenclator_codes(conn) or None
# Reguli text incarcate o data per cerere (seam partajat cu dry-run).
text_rules = load_text_rules(conn, acct)
excluded_ops = load_excluded_ops(conn, acct)
error_mode = _effective_on_unmapped_error(conn, acct, req.on_unmapped_error)
# Rezolva mediul RAR tinta (cerut > default cont > ancora globala).
@@ -285,7 +287,7 @@ def create_prezentari(
# retrimiterea aceluiasi continut. Il RE-ACTIVAM (re-clasificam + actualizam
# creds + reset), printr-un UPDATE compare-and-swap pe status='error'.
if existing["status"] == "error":
cl = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules)
cl = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules, excluded_ops)
if cl["blocked_error"]:
# on_unmapped_error=True: nu reactivam; randul ramane 'error'.
results.append(_rezultat_respins(existing["id"], cl, rar_env=env))
@@ -337,7 +339,7 @@ def create_prezentari(
# Helper pur partajat cu dry-run: reproduce EXACT clasificarea
# (canonicalize + mapare op->cod + validare; fara gate auto_send).
cl = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules)
cl = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules, excluded_ops)
if cl["blocked_error"]:
# on_unmapped_error=True: respinge fara enqueue (cod necunoscut/nemapat).
results.append(_rezultat_respins(None, cl, rar_env=env))
@@ -397,6 +399,7 @@ def valideaza_prezentari(
valid_codes = load_nomenclator_codes(conn) or None
# Acelasi seam ca trimiterea reala: dry-run trebuie sa vada aceleasi reguli text.
text_rules = load_text_rules(conn, acct)
excluded_ops = load_excluded_ops(conn, acct)
error_mode = _effective_on_unmapped_error(conn, acct, req.on_unmapped_error)
# Rezolva env identic ca trimiterea reala si ecou-ieste in raspuns.
@@ -429,7 +432,7 @@ def valideaza_prezentari(
for i, prez in enumerate(req.prezentari):
content = prez.model_dump()
res = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules)
res = _classify_modal(content, mapping, mapping_meta, valid_codes, error_mode, text_rules, excluded_ops)
if res["blocked_error"]:
res = {**res, "status": "error"}
# Imbogatim fiecare element nemapat cu 3 niveluri COD_NEMAPAT