Inlocuieste cele 3 controale de asociere cod RAR cu un singur rand de chips + un singur select care adauga instant la change. Stare = un hidden chips_state JSON versionat; post_form_chips redus la 2 actiuni (add/remove). Optgroup Sugestii (fuzzy/k-NN) + optiune "Nu se declara la RAR" in select, cu tinta implicita evidentiata si placeholder care o numeste. exclus persistat prin payload_json (treapta noua de precedenta in resolve_prestatii, round-trip complet). Siguranta: itemii exclusi sunt scosi din payload la momentul trimiterii (worker split_prestatii_excluse inainte de build_rar_payload + filtru defensiv), ca sa nu ajunga NICIODATA la RAR ca codPrestatie:null. Suita: 1680 passed, 1 skipped. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
70 lines
2.6 KiB
Python
70 lines
2.6 KiB
Python
"""Constructor payload postPrezentare.
|
|
|
|
Reguli din contract (docs/api-rar-contract.md), confirmate live:
|
|
- status mereu "FINALIZATA".
|
|
- tipPrestatie NU se trimite (server-generated GENERIC).
|
|
- odometruFinal ca string; odometruInitial ca string cand e prezent, altfel null.
|
|
- sistemReparat trimis mereu (default "null").
|
|
- prestatii: [{codPrestatie, idPrezentare: null}].
|
|
- obs / b64Image optionale — se OMIT din payload daca lipsesc.
|
|
|
|
Snapshot test fata de exemplul oficial: tests/test_payload.py.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Any
|
|
|
|
|
|
def _upper(value: object) -> str:
|
|
return str(value or "").strip().upper()
|
|
|
|
|
|
def _str_or_none(value: object) -> str | None:
|
|
"""str.strip() pentru valori non-goale; None pentru None / string gol."""
|
|
if value is None:
|
|
return None
|
|
s = str(value).strip()
|
|
return s or None
|
|
|
|
|
|
def _cod(p: object) -> str | None:
|
|
cod = p.get("cod_prestatie") if isinstance(p, dict) else getattr(p, "cod_prestatie", None)
|
|
return str(cod).strip().upper() if cod else None
|
|
|
|
|
|
def _declarabil(p: object) -> bool:
|
|
"""Un item pleaca la RAR doar daca are cod si nu e adnotat exclus.
|
|
|
|
Filtru no-op pentru payload deja ingustat la declarabile; sarit ca ultima
|
|
plasa de siguranta — un item fara cod nu produce niciodata {codPrestatie: null}.
|
|
"""
|
|
exclus = p.get("exclus") if isinstance(p, dict) else getattr(p, "exclus", None)
|
|
return not exclus and _cod(p) is not None
|
|
|
|
|
|
def build_rar_payload(prezentare: dict[str, Any]) -> dict[str, Any]:
|
|
"""Mapeaza o prezentare interna (snake_case) -> payload exact pentru RAR postPrezentare."""
|
|
payload: dict[str, Any] = {
|
|
"vin": _upper(prezentare.get("vin")),
|
|
"nrInmatriculare": _upper(prezentare.get("nr_inmatriculare")),
|
|
"dataPrestatie": str(prezentare.get("data_prestatie") or "").strip(),
|
|
# odometruFinal ramane string (nu folosim `or` ca sa nu pierdem "0").
|
|
"odometruFinal": str(prezentare.get("odometru_final")).strip()
|
|
if prezentare.get("odometru_final") is not None else "",
|
|
"odometruInitial": _str_or_none(prezentare.get("odometru_initial")),
|
|
"prestatii": [
|
|
{"codPrestatie": _cod(p), "idPrezentare": None}
|
|
for p in (prezentare.get("prestatii") or [])
|
|
if _declarabil(p)
|
|
],
|
|
"sistemReparat": prezentare.get("sistem_reparat") or "null",
|
|
"status": "FINALIZATA",
|
|
}
|
|
# tipPrestatie: NICIODATA in payload (server-generated GENERIC).
|
|
if prezentare.get("obs"):
|
|
payload["obs"] = prezentare["obs"]
|
|
if prezentare.get("b64_image"):
|
|
payload["b64Image"] = prezentare["b64_image"]
|
|
return payload
|