"""Extragere payload submission -> campuri afisabile. Helper PUR partajat intre canalul web (dashboard Trimiteri) si canalul API (`GET /v1/prezentari`), ca extragerea sa NU diverge intre cele doua (decizie eng review, DRY). Fara DB, fara request — primeste `payload_json` (text JSON plaintext, vezi `submissions.payload_json`) sau un dict deja parsat. Defensiv prin constructie: nu arunca niciodata pe payload malformat — degradeaza la em-dash. Citeste cheile tolerant (canalele API si import pot diferi usor: `nr_inmatriculare` vs `numar`/`numarInmatriculare`; coercion Excel pe odometru/VIN). """ from __future__ import annotations import json from typing import Any EMPTY = "—" def _clean_str(value: Any) -> str: """str() defensiv: None/'' -> '', altfel string strip-uit (coercion Excel safe).""" if value is None: return "" return str(value).strip() def _clean_odometru(value: Any) -> str: """Odometru afisat curat: strip '.0' din coercion Excel (123456.0 -> 123456).""" s = _clean_str(value) if "." in s: before, after = s.split(".", 1) if after == "0" and before.lstrip("-").isdigit(): return before return s def _clean_cod_rar(value: Any) -> str: """Cod RAR afisat curat: uppercase + strip '.0' defensiv (coercion Excel 'OE-2.0' -> 'OE-2'). Codurile RAR nu au zecimale, dar fii defensiv ca la odometru. """ s = _clean_str(value) if s.endswith(".0"): s = s[:-2] return s.upper() if s else "" def _vin_scurt(vin: str) -> str: """Forma trunchiata a VIN-ului pentru tabel (integral ramane in detaliu). VIN are 17 caractere; in tabel aratam ultimele 6 cu prefix elipsa ca sa incapa fara sa ascundem complet identitatea vehiculului. """ if not vin: return "" if len(vin) <= 8: return vin return "…" + vin[-6:] def _prima_prestatie(prestatii: Any) -> dict[str, Any]: """Primul item de prestatie ca dict, defensiv (lista goala/non-dict -> {}).""" if isinstance(prestatii, list): for item in prestatii: if isinstance(item, dict): return item return {} def _payload_dict(payload: str | dict | None) -> dict[str, Any]: """Normalizeaza intrarea la dict. JSON invalid / tip neasteptat -> {}.""" if payload is None: return {} if isinstance(payload, dict): return payload if isinstance(payload, str): try: data = json.loads(payload) except (ValueError, TypeError): return {} return data if isinstance(data, dict) else {} return {} def prezentare_din_payload(payload: str | dict | None) -> dict[str, str]: """Campuri afisabile dintr-un payload de submission. Intoarce un dict cu chei stabile (toate string-uri, EMPTY cand lipsesc): vehicul_nr, vin, vin_scurt, operatie, data_prestatie, odometru, cod. `operatie` = denumire prestatiei daca exista, altfel codul; `cod` = codul RAR (`cod_prestatie`) sau, in lipsa, codul intern (`cod_op_service`). """ data = _payload_dict(payload) vin = _clean_str(data.get("vin")) nr = _clean_str( data.get("nr_inmatriculare") or data.get("numar") or data.get("numarInmatriculare") ) odo = _clean_odometru( data.get("odometru_final") if data.get("odometru_final") is not None else data.get("odometru") ) data_prest = _clean_str(data.get("data_prestatie") or data.get("dataPrestatie")) item = _prima_prestatie(data.get("prestatii")) cod = _clean_str(item.get("cod_prestatie") or item.get("cod_op_service")) denumire = _clean_str(item.get("denumire")) operatie = denumire or cod # cod_rar: exclusiv din cod_prestatie (NU fallback la cod_op_service); uppercase + strip ".0" cod_rar = _clean_cod_rar(item.get("cod_prestatie")) # Operatia de service originala (codul intern + denumire venita prin API/import), # distincta de operatia RAR mapata (cod_rar). # Conventie goala: aceste campuri intorc "" (string gol) cand lipsesc — NU EMPTY="—". # Motivul: apelantul decide sa nu afiseze randul deloc (vs afisaj gol), testând `!= ""`. # Campurile vechi (vehicul_nr, vin, operatie etc.) pastreaza conventia EMPTY="—". op_service_cod = _clean_str(item.get("cod_op_service")) # op_service_denumire e relevant doar cand exista un cod de operatie de service; # altfel ar expune denumirea RAR drept op. de service, ceea ce e semantic incorect. op_service_denumire = _clean_str(item.get("denumire")) if op_service_cod else "" # obs: text liber observatii (camp RAR, optional). Conventie goala "" (nu EMPTY). # US-005 PRD 5.15: obs traieste in payload_json (nu coloana separata). obs = _clean_str(data.get("obs")) return { "vehicul_nr": nr or EMPTY, "vin": vin or EMPTY, "vin_scurt": _vin_scurt(vin) or EMPTY, "operatie": operatie or EMPTY, "data_prestatie": data_prest or EMPTY, "odometru": odo or EMPTY, "cod": cod or EMPTY, "cod_rar": cod_rar or EMPTY, # Chei cu conventie goala "" (nu EMPTY) — vezi comentariu de mai sus "op_service_cod": op_service_cod, "op_service_denumire": op_service_denumire, "obs": obs, }