"""Cheie de idempotenta = hash de continut canonic. RAR nu are camp nr. comanda si accepta duplicate -> dedup-ul e in sarcina noastra (plan.md sect. 14). Hash stabil peste o reprezentare canonica a prezentarii. """ from __future__ import annotations import hashlib import json from typing import Any def _op_identity(p: Any) -> str: """Cod RAR (normalizat) daca exista, altfel codul intern ROAAUTO.""" get = p.get if isinstance(p, dict) else (lambda k, d=None: getattr(p, k, d)) cod = (get("cod_prestatie", "") or "").strip().upper() if cod: return cod return (get("cod_op_service", "") or "").strip() def idempotency_key(account_id: int | None, prezentare: dict[str, Any]) -> str: """SHA-256 peste (account_id + campurile semnificative ale prezentarii). Exclude obs si b64Image (cosmetice, nu definesc unicitatea declaratiei). """ canonic = { "account_id": account_id, "vin": (prezentare.get("vin") or "").strip().upper(), "nr_inmatriculare": (prezentare.get("nr_inmatriculare") or "").strip().upper(), "data_prestatie": prezentare.get("data_prestatie"), "odometru_final": str(prezentare.get("odometru_final") or "").strip(), # Identitatea operatiei = codul RAR daca exista, altfel codul intern ROAAUTO # (hibrid): doua trimiteri ale aceleiasi comenzi dedup corect indiferent de # forma in care vin codurile. "prestatii": sorted(_op_identity(p) for p in (prezentare.get("prestatii") or [])), } blob = json.dumps(canonic, sort_keys=True, ensure_ascii=False, separators=(",", ":")) return hashlib.sha256(blob.encode("utf-8")).hexdigest()