fix(import): persista chips prestatii la salvarea din modalul de editare rand

Editorul de rand din preview afisa chips-urile operatie<->cod RAR dar salvarea
extragea doar EDIT_FIELDS — codurile alese se pierdeau si randul ramanea
needs_mapping. Starea chips se reconstruieste din form (conventia /form-chips),
se valideaza fata de nomenclator si se persista in override_json.

Caile de commit (API + web) aplica acum override-ul INAINTE de resolve_prestatii,
ca in preview — altfel prestatiile editate ajungeau nerezolvate in payload.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-07-07 17:20:01 +00:00
parent 5a2c976d46
commit c6e29aa65d
3 changed files with 262 additions and 29 deletions

View File

@@ -538,3 +538,155 @@ def test_base_html_deschide_modal_pentru_btn_editeaza():
".btn-editeaza (sau sa verifice hx-target='#detaliu-modal-body') pentru a "
"apela open() si instala focus-trap-ul."
)
# --------------------------------------------------------------------------- #
# Persistarea chips-urilor prestatii la salvarea din modal #
# --------------------------------------------------------------------------- #
_ROWS_ITP = [
{
"VIN": "WVWZZZ3CZ9E123456",
"Nr": "TM789BC",
"Data": "2026-05-15",
"KM": "82500",
"Operatie": "OP-ITP",
},
]
def _seed_nomenclator_extra() -> None:
"""Nomenclator cu mai multe coduri pentru testele de chips (fara mapare OP-ITP)."""
from app.db import get_connection
conn = get_connection()
try:
for cod, nume in (("R-FRANE", "Reparatie frane"), ("OE-2", "Verificare"), ("OE-3", "Reglaj")):
conn.execute(
"INSERT OR REPLACE INTO nomenclator_rar (cod_prestatie, nume_prestatie) VALUES (?, ?)",
(cod, nume),
)
conn.commit()
finally:
conn.close()
def _preview_row0(client: TestClient, iid: int) -> dict:
"""Recalculeaza preview-ul si intoarce randul 0 rezolvat (direct pe DB)."""
from app.db import get_connection
from app.web.routes import _preview_one_row
conn = get_connection()
try:
result, row = _preview_one_row(conn, iid, 1, 0)
assert row is not None and not isinstance(result, str)
return row
finally:
conn.close()
def test_salvare_chips_persista_si_deblocheaza_randul(client):
"""Bug reprodus: cod ales in editor pentru operatie nemapata + coduri extra,
Salveaza -> randul ramanea needs_mapping si chips-urile se pierdeau la redeschidere.
Dupa fix: chips-urile din form se persista in override -> randul devine ok,
iar redeschiderea editorului arata codurile alese.
"""
_seed_nomenclator_extra()
iid = _upload_and_preview(client, rows=_ROWS_ITP)
row = _preview_row0(client, iid)
assert row["resolved_status"] == "needs_mapping"
# Salvare cu chips: OP-ITP -> OE-2 + cod extra OE-3 (fara operatie).
r = client.post(f"/_import/{iid}/rand/0/editeaza", data={
"cod_prestatie": ["OE-2", "OE-3"],
"chip_op_service": ["OP-ITP", ""],
"chip_denumire": ["Inspectie Tehnica Periodica", ""],
})
assert r.status_code == 200, r.text
assert "inchideModal" in r.headers.get("HX-Trigger-After-Settle", ""), \
"Salvarea cu chips valide trebuie sa inchida modalul (succes)"
row = _preview_row0(client, iid)
assert row["resolved_status"] == "ok", f"Randul trebuie deblocat, e {row['resolved_status']}"
coduri = [p.get("cod_prestatie") for p in row["resolved"]["prestatii"]]
assert coduri == ["OE-2", "OE-3"], coduri
# Redeschiderea editorului arata chips-urile salvate.
r2 = client.get(f"/_import/{iid}/rand/0/editare-modal")
assert r2.status_code == 200
assert "OE-2" in r2.text and "OE-3" in r2.text
def test_salvare_chips_cod_din_picker_neaprobat_se_aplica(client):
"""Cod ales in pickerul per-operatie dar ne-aprobat prin '+ Adauga' se aplica la salvare."""
_seed_nomenclator_extra()
iid = _upload_and_preview(client, rows=_ROWS_ITP)
r = client.post(f"/_import/{iid}/rand/0/editeaza", data={
"cod_prestatie": "",
"chip_op_service": "OP-ITP",
"chip_denumire": "Inspectie Tehnica Periodica",
"chips_add_cod_0": "OE-2",
})
assert r.status_code == 200, r.text
row = _preview_row0(client, iid)
assert row["resolved_status"] == "ok"
assert row["resolved"]["prestatii"][0]["cod_prestatie"] == "OE-2"
def test_salvare_chips_cod_necunoscut_respins(client):
"""Cod inexistent in nomenclator -> modal re-randat cu eroare, override neatins."""
_seed_nomenclator_extra()
iid = _upload_and_preview(client, rows=_ROWS_ITP)
r = client.post(f"/_import/{iid}/rand/0/editeaza", data={
"cod_prestatie": "XX-INEXISTENT",
"chip_op_service": "OP-ITP",
"chip_denumire": "Inspectie",
})
assert r.status_code == 200
assert "inchideModal" not in r.headers.get("HX-Trigger-After-Settle", "")
assert "necunoscut" in r.text
row = _preview_row0(client, iid)
assert row["resolved_status"] == "needs_mapping", "Codul invalid nu trebuie persistat"
def test_salvare_fara_sectiune_chips_lasa_prestatiile_neatinse(client):
"""Form fara inputuri de chips (doar campuri de continut) nu atinge prestatiile."""
_seed_op1()
iid = _upload_and_preview(client)
r = client.post(f"/_import/{iid}/rand/0/editeaza", data={"data_prestatie": "2026-06-15"})
assert r.status_code == 200
row = _preview_row0(client, iid)
assert row["resolved"]["prestatii"][0]["cod_prestatie"] == "R-FRANE"
def test_commit_foloseste_prestatiile_editate_in_modal(client):
"""Confirmarea batch-ului trimite prestatiile din override (chips editate), re-rezolvate."""
import json as _json
_seed_nomenclator_extra()
iid = _upload_and_preview(client, rows=_ROWS_ITP)
r = client.post(f"/_import/{iid}/rand/0/editeaza", data={
"cod_prestatie": "OE-2",
"chip_op_service": "OP-ITP",
"chip_denumire": "Inspectie Tehnica Periodica",
})
assert r.status_code == 200, r.text
csrf = _get_csrf(client)
rc = client.post(f"/_import/{iid}/confirma", data={"n_confirmat": "1", "csrf_token": csrf})
assert rc.status_code == 200, rc.text
from app.db import get_connection
conn = get_connection()
try:
sub = conn.execute(
"SELECT payload_json FROM submissions WHERE batch_id=?", (iid,)
).fetchone()
finally:
conn.close()
assert sub is not None, "Randul editat trebuie enqueued la confirmare"
payload = _json.loads(sub["payload_json"])
assert payload["prestatii"][0]["cod_prestatie"] == "OE-2"