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:
@@ -49,11 +49,13 @@ from ...mapping import (
|
||||
_emite_text_rule_hits,
|
||||
account_or_default,
|
||||
has_no_auto_send,
|
||||
load_excluded_ops,
|
||||
load_mapping_meta,
|
||||
load_nomenclator_codes,
|
||||
load_text_rules,
|
||||
normalize_for_match,
|
||||
resolve_prestatii,
|
||||
split_prestatii_excluse,
|
||||
)
|
||||
from ...validation import validate_prezentare
|
||||
from ...rar_env import MediuIndisponibil, rar_env_efectiv_cont, rezolva_rar_env
|
||||
@@ -135,11 +137,12 @@ def _resolve_row_for_preview(
|
||||
valid_codes: set[str] | None = None,
|
||||
text_rules: list[dict] | None = None,
|
||||
reviewed: bool = False,
|
||||
excluded_ops: set[str] | None = None,
|
||||
) -> dict[str, Any]:
|
||||
"""Rezolva un rand din import pentru preview: aplica mapare coloane + validare.
|
||||
|
||||
Intoarce un dict cu:
|
||||
resolved_status: ok/needs_mapping/needs_data/needs_review
|
||||
resolved_status: ok/needs_mapping/needs_data/needs_review/excluded
|
||||
resolved: valorile finale rezolvate (VIN, data, km, prestatii)
|
||||
errors: lista erori validare
|
||||
flags: motive needs_review
|
||||
@@ -218,7 +221,7 @@ def _resolve_row_for_preview(
|
||||
|
||||
# Rezolvare prestatii
|
||||
prestatii = mapped.get("prestatii") or []
|
||||
resolved, unmapped = resolve_prestatii(prestatii, mapping, valid_codes, text_rules)
|
||||
resolved, unmapped = resolve_prestatii(prestatii, mapping, valid_codes, text_rules, excluded_ops)
|
||||
mapped["prestatii"] = resolved
|
||||
|
||||
# Determinare stare
|
||||
@@ -230,6 +233,19 @@ def _resolve_row_for_preview(
|
||||
"flags": all_flags,
|
||||
}
|
||||
|
||||
# Operatii excluse de la declarare: ies din prestatiile trimise (si din cheia
|
||||
# de idempotenta). Toate excluse -> rand 'excluded' (nu se trimite, nu blocheaza).
|
||||
declarabile, excluse = split_prestatii_excluse(resolved)
|
||||
if excluse and not declarabile:
|
||||
return {
|
||||
"resolved_status": "excluded",
|
||||
"resolved": mapped,
|
||||
"errors": [],
|
||||
"flags": all_flags,
|
||||
}
|
||||
if excluse:
|
||||
mapped["prestatii"] = declarabile
|
||||
|
||||
# Validare continut
|
||||
errors = validate_prezentare(mapped)
|
||||
|
||||
@@ -770,9 +786,10 @@ def preview_import(
|
||||
# Incarca maparea de operatii o singura data
|
||||
mapping_meta = load_mapping_meta(conn, acct)
|
||||
mapping = {op: meta["cod_prestatie"] for op, meta in mapping_meta.items()}
|
||||
# Validare nomenclator + reguli text incarcate O DATA, inainte de bucla pe randuri.
|
||||
# Validare nomenclator + reguli text + excluderi incarcate O DATA, inainte de bucla.
|
||||
valid_codes = load_nomenclator_codes(conn) or None
|
||||
text_rules = load_text_rules(conn, acct)
|
||||
excluded_ops = load_excluded_ops(conn, acct)
|
||||
|
||||
# Mediul RAR efectiv al contului — folosit la calculul cheii de idempotenta
|
||||
# la preview (trebuie sa coincida cu ce va folosi commit-ul fara rar_env explicit).
|
||||
@@ -828,6 +845,7 @@ def preview_import(
|
||||
override=overrides[i] or None,
|
||||
valid_codes=valid_codes,
|
||||
text_rules=text_rules,
|
||||
excluded_ops=excluded_ops,
|
||||
)
|
||||
|
||||
# Calculeaza cheia de idempotenta pentru randurile ok/needs_review
|
||||
@@ -1118,9 +1136,10 @@ def commit_import(
|
||||
# Incarca maparea de operatii
|
||||
mapping_meta = load_mapping_meta(conn, acct)
|
||||
mapping = {op: meta["cod_prestatie"] for op, meta in mapping_meta.items()}
|
||||
# Validare nomenclator + reguli text incarcate O DATA, inainte de bucla pe randuri.
|
||||
# Validare nomenclator + reguli text + excluderi incarcate O DATA, inainte de bucla.
|
||||
valid_codes = load_nomenclator_codes(conn) or None
|
||||
text_rules = load_text_rules(conn, acct)
|
||||
excluded_ops = load_excluded_ops(conn, acct)
|
||||
|
||||
# Construieste payload-urile submissions
|
||||
enqueued: list[dict] = []
|
||||
@@ -1184,7 +1203,12 @@ def commit_import(
|
||||
|
||||
# Rezolva prestatii INAINTE de canonicalizare (altfel cheia difera de cea din preview)
|
||||
prestatii = mapped.get("prestatii") or []
|
||||
resolved, _ = resolve_prestatii(prestatii, mapping, valid_codes, text_rules)
|
||||
resolved, _ = resolve_prestatii(prestatii, mapping, valid_codes, text_rules, excluded_ops)
|
||||
# Prestatiile excluse de la declarare NU pleaca la RAR (ca in preview);
|
||||
# rand fara nicio prestatie declarabila = 'excluded' in preview -> defensiv skip.
|
||||
resolved, _excluse = split_prestatii_excluse(resolved)
|
||||
if not resolved:
|
||||
continue
|
||||
mapped["prestatii"] = resolved
|
||||
|
||||
# Canonicalizare (dupa rezolvare prestatii -> cod_prestatie inclus in cheie;
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user