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:
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user