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:
@@ -266,8 +266,9 @@ def _build_idempotency_key(account_id: int | None, resolved: dict[str, Any], rar
|
|||||||
return build_key(account_id, canon, rar_env)
|
return build_key(account_id, canon, rar_env)
|
||||||
|
|
||||||
|
|
||||||
# Campuri de continut editabile in preview. Operatia/codul RAR NU se editeaza
|
# Campuri de continut editabile in preview. Prestatiile (chips operatie<->cod RAR)
|
||||||
# aici (raman in panoul de mapare). obs = text liber, se trateaza ca non-canonic
|
# se editeaza tot in modal, dar se persista separat (param `prestatii` din
|
||||||
|
# apply_row_override), nu prin EDIT_FIELDS. obs = text liber, se trateaza ca non-canonic
|
||||||
# (doar .strip(), fara canonicalize_row) — urmeaza ramura `else` din _merge_override.
|
# (doar .strip(), fara canonicalize_row) — urmeaza ramura `else` din _merge_override.
|
||||||
EDIT_FIELDS = ("vin", "nr_inmatriculare", "data_prestatie", "odometru_initial", "odometru_final", "obs")
|
EDIT_FIELDS = ("vin", "nr_inmatriculare", "data_prestatie", "odometru_initial", "odometru_final", "obs")
|
||||||
|
|
||||||
@@ -318,9 +319,13 @@ def apply_row_override(
|
|||||||
account_id: int | None,
|
account_id: int | None,
|
||||||
row_index: int,
|
row_index: int,
|
||||||
fields: dict[str, str | None],
|
fields: dict[str, str | None],
|
||||||
|
prestatii: list[dict[str, str]] | None = None,
|
||||||
) -> dict[str, Any]:
|
) -> dict[str, Any]:
|
||||||
"""Persista override-ul canonic pentru un rand de preview (mutatie PURA de stocare).
|
"""Persista override-ul canonic pentru un rand de preview (mutatie PURA de stocare).
|
||||||
|
|
||||||
|
`prestatii`: None = neatins; lista = inlocuieste integral prestatiile randului
|
||||||
|
(chips editate in modal, deja validate fata de nomenclator de apelant).
|
||||||
|
|
||||||
NU recalculeaza statusul si NU atinge `submissions` — preview-ul rederiva statusul
|
NU recalculeaza statusul si NU atinge `submissions` — preview-ul rederiva statusul
|
||||||
prin `_resolve_row_for_preview` (un singur clasificator, fara drift).
|
prin `_resolve_row_for_preview` (un singur clasificator, fara drift).
|
||||||
|
|
||||||
@@ -358,6 +363,8 @@ def apply_row_override(
|
|||||||
current = dec
|
current = dec
|
||||||
|
|
||||||
new_override = _merge_override(current, fields)
|
new_override = _merge_override(current, fields)
|
||||||
|
if prestatii is not None:
|
||||||
|
new_override["prestatii"] = prestatii
|
||||||
enc = encrypt_creds(new_override) if new_override else None
|
enc = encrypt_creds(new_override) if new_override else None
|
||||||
# Resetam reviewed=0 la orice schimbare de valoare — operatorul
|
# Resetam reviewed=0 la orice schimbare de valoare — operatorul
|
||||||
# trebuie sa reconfirme dupa editare. NU conditionam pe reviewed curent: orice override
|
# trebuie sa reconfirme dupa editare. NU conditionam pe reviewed curent: orice override
|
||||||
@@ -1167,12 +1174,21 @@ def commit_import(
|
|||||||
obs_denumire = str(denumire_val).strip() if denumire_val not in (None, "") else str(operatie_val)
|
obs_denumire = str(denumire_val).strip() if denumire_val not in (None, "") else str(operatie_val)
|
||||||
mapped["obs"] = obs_denumire
|
mapped["obs"] = obs_denumire
|
||||||
|
|
||||||
|
# Override editat in preview — aplicat INAINTE de resolve_prestatii,
|
||||||
|
# ca in _resolve_row_for_preview: prestatiile din override (chips
|
||||||
|
# editate in modal) trec prin ACELASI resolve, altfel raman raw si
|
||||||
|
# hash-ul/telemetria ar folosi prestatiile pre-editare.
|
||||||
|
override = ok_row.get("override") or {}
|
||||||
|
if override:
|
||||||
|
mapped.update(override)
|
||||||
|
|
||||||
# Rezolva prestatii INAINTE de canonicalizare (altfel cheia difera de cea din preview)
|
# Rezolva prestatii INAINTE de canonicalizare (altfel cheia difera de cea din preview)
|
||||||
prestatii = mapped.get("prestatii") or []
|
prestatii = mapped.get("prestatii") or []
|
||||||
resolved, _ = resolve_prestatii(prestatii, mapping, valid_codes, text_rules)
|
resolved, _ = resolve_prestatii(prestatii, mapping, valid_codes, text_rules)
|
||||||
mapped["prestatii"] = resolved
|
mapped["prestatii"] = resolved
|
||||||
|
|
||||||
# Canonicalizare (dupa rezolvare prestatii -> cod_prestatie inclus in cheie)
|
# Canonicalizare (dupa rezolvare prestatii -> cod_prestatie inclus in cheie;
|
||||||
|
# cheia IDENTICA cu preview: _build_idempotency_key = canonicalize_row + build_key).
|
||||||
canon = canonicalize_row(mapped)
|
canon = canonicalize_row(mapped)
|
||||||
mapped.update({
|
mapped.update({
|
||||||
"vin": canon["vin"],
|
"vin": canon["vin"],
|
||||||
@@ -1180,19 +1196,6 @@ def commit_import(
|
|||||||
"odometru_final": canon["odometru_final"],
|
"odometru_final": canon["odometru_final"],
|
||||||
})
|
})
|
||||||
|
|
||||||
# Override editat in preview — aplicat ULTIMUL, ca in resolver.
|
|
||||||
override = ok_row.get("override") or {}
|
|
||||||
if override:
|
|
||||||
mapped.update(override)
|
|
||||||
# Re-canonicalizeaza pentru a obtine cheia IDENTICA cu cea din preview
|
|
||||||
# (_build_idempotency_key = canonicalize_row + build_key peste mapped).
|
|
||||||
canon = canonicalize_row(mapped)
|
|
||||||
mapped.update({
|
|
||||||
"vin": canon["vin"],
|
|
||||||
"nr_inmatriculare": canon["nr_inmatriculare"],
|
|
||||||
"odometru_final": canon["odometru_final"],
|
|
||||||
})
|
|
||||||
|
|
||||||
# Cheia de idempotenta (identica cu cheia din preview — aceeasi ordine + env)
|
# Cheia de idempotenta (identica cu cheia din preview — aceeasi ordine + env)
|
||||||
key = build_key(account_id, canon, env)
|
key = build_key(account_id, canon, env)
|
||||||
|
|
||||||
|
|||||||
@@ -3806,6 +3806,51 @@ def web_rand_display(request: Request, import_id: int, row_index: int) -> HTMLRe
|
|||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
|
|
||||||
|
def _chips_state_from_form(form) -> list[dict[str, str]] | None:
|
||||||
|
"""Reconstruieste starea chips prestatii din inputurile paralele ale formularului.
|
||||||
|
|
||||||
|
Aceeasi conventie ca /form-chips (_chips_prestatii.html): 3 liste paralele
|
||||||
|
cod_prestatie / chip_op_service / chip_denumire. Codurile alese in pickere dar
|
||||||
|
ne-aprobate prin '+' (chips_add_cod_{i}, chips_add_cod_flat) se aplica implicit,
|
||||||
|
ca in post_corectie_trimitere. Dedup pe perechea (op, cod).
|
||||||
|
|
||||||
|
Intoarce None cand formularul nu contine deloc stare de chips (form fara
|
||||||
|
sectiunea de prestatii) — apelantul lasa prestatiile neatinse.
|
||||||
|
"""
|
||||||
|
flat_picker = str(form.get("chips_add_cod_flat") or "").strip().upper()
|
||||||
|
if "cod_prestatie" not in form and "chip_op_service" not in form and not flat_picker:
|
||||||
|
return None
|
||||||
|
cod_list = [c.strip().upper() if isinstance(c, str) else "" for c in form.getlist("cod_prestatie")]
|
||||||
|
op_list = [o.strip() if isinstance(o, str) else "" for o in form.getlist("chip_op_service")]
|
||||||
|
den_list = [d.strip() if isinstance(d, str) else "" for d in form.getlist("chip_denumire")]
|
||||||
|
n = max(len(cod_list), len(op_list), len(den_list))
|
||||||
|
chips: list[dict[str, str]] = []
|
||||||
|
for i in range(n):
|
||||||
|
cod = cod_list[i] if i < len(cod_list) else ""
|
||||||
|
if not cod:
|
||||||
|
# picker per-operatie cu cod ales dar ne-aprobat prin '+ Adauga'
|
||||||
|
cod = str(form.get(f"chips_add_cod_{i}") or "").strip().upper()
|
||||||
|
chips.append({
|
||||||
|
"cod_prestatie": cod,
|
||||||
|
"cod_op_service": op_list[i] if i < len(op_list) else "",
|
||||||
|
"denumire": den_list[i] if i < len(den_list) else "",
|
||||||
|
})
|
||||||
|
pairs = {(c["cod_op_service"], c["cod_prestatie"]) for c in chips}
|
||||||
|
if flat_picker and ("", flat_picker) not in pairs:
|
||||||
|
chips.append({"cod_prestatie": flat_picker, "cod_op_service": "", "denumire": ""})
|
||||||
|
seen: set = set()
|
||||||
|
out: list[dict[str, str]] = []
|
||||||
|
for c in chips:
|
||||||
|
if not (c["cod_prestatie"] or c["cod_op_service"] or c["denumire"]):
|
||||||
|
continue
|
||||||
|
pair = (c["cod_op_service"], c["cod_prestatie"])
|
||||||
|
if pair in seen:
|
||||||
|
continue
|
||||||
|
seen.add(pair)
|
||||||
|
out.append(c)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
@router.post("/_import/{import_id}/rand/{row_index}/editeaza", response_class=HTMLResponse)
|
@router.post("/_import/{import_id}/rand/{row_index}/editeaza", response_class=HTMLResponse)
|
||||||
async def web_editeaza_rand(request: Request, import_id: int, row_index: int) -> HTMLResponse:
|
async def web_editeaza_rand(request: Request, import_id: int, row_index: int) -> HTMLResponse:
|
||||||
"""Persista override (mutatie pura) + raspunde cu OOB rand+contoare sau erori in modal.
|
"""Persista override (mutatie pura) + raspunde cu OOB rand+contoare sau erori in modal.
|
||||||
@@ -3825,13 +3870,50 @@ async def web_editeaza_rand(request: Request, import_id: int, row_index: int) ->
|
|||||||
camp: (str(form.get(camp)) if form.get(camp) is not None else None)
|
camp: (str(form.get(camp)) if form.get(camp) is not None else None)
|
||||||
for camp in EDIT_FIELDS
|
for camp in EDIT_FIELDS
|
||||||
}
|
}
|
||||||
|
# Chips prestatii din modal: None = form fara sectiune de chips (neatins).
|
||||||
|
chips_state = _chips_state_from_form(form)
|
||||||
conn = get_connection()
|
conn = get_connection()
|
||||||
try:
|
try:
|
||||||
|
# Invariant ORA-12899: orice cod trimis din form se valideaza fata de
|
||||||
|
# nomenclator INAINTE de persistare (RAR accepta NUMAI coduri valide).
|
||||||
|
if chips_state is not None:
|
||||||
|
valid_codes = load_nomenclator_codes(conn) or set()
|
||||||
|
necunoscute = sorted(
|
||||||
|
{c["cod_prestatie"] for c in chips_state if c["cod_prestatie"]} - valid_codes
|
||||||
|
)
|
||||||
|
if necunoscute:
|
||||||
|
_nom = load_nomenclator(conn)
|
||||||
|
return templates.TemplateResponse("_editare_preview_modal.html", {
|
||||||
|
"request": request,
|
||||||
|
"import_id": import_id,
|
||||||
|
"row_index": row_index,
|
||||||
|
"csrf_token": get_csrf_token(request),
|
||||||
|
"vin": str(form.get("vin") or ""),
|
||||||
|
"stare_css": "",
|
||||||
|
"stare_eticheta": "",
|
||||||
|
"form_nr": str(form.get("nr_inmatriculare") or ""),
|
||||||
|
"form_vin": str(form.get("vin") or ""),
|
||||||
|
"form_data": str(form.get("data_prestatie") or ""),
|
||||||
|
"form_odo_final": str(form.get("odometru_final") or ""),
|
||||||
|
"form_odo_initial": str(form.get("odometru_initial") or ""),
|
||||||
|
"err_map": {},
|
||||||
|
"fix_map": {},
|
||||||
|
"vin_context": str(form.get("vin") or ""),
|
||||||
|
"btn_label": "Salveaza",
|
||||||
|
"message": f"Cod RAR necunoscut in nomenclator: {', '.join(necunoscute)}. "
|
||||||
|
"Alege un cod valid din lista.",
|
||||||
|
"prestatii_chips": chips_state,
|
||||||
|
"has_r_odo": _has_r_odo_chips(chips_state),
|
||||||
|
"obs_val": str(form.get("obs") or "").strip(),
|
||||||
|
"nomenclator_rar": _nom,
|
||||||
|
"form_chips_url": "/form-chips",
|
||||||
|
})
|
||||||
|
|
||||||
# Mutatie pura de stocare (404/409/422 -> propaga; htmx hx-on::response-error
|
# Mutatie pura de stocare (404/409/422 -> propaga; htmx hx-on::response-error
|
||||||
# pastreaza formularul modal cu valorile la 4xx/5xx).
|
# pastreaza formularul modal cu valorile la 4xx/5xx).
|
||||||
apply_row_override(
|
apply_row_override(
|
||||||
conn, import_id=import_id, account_id=account_id,
|
conn, import_id=import_id, account_id=account_id,
|
||||||
row_index=row_index, fields=fields,
|
row_index=row_index, fields=fields, prestatii=chips_state,
|
||||||
)
|
)
|
||||||
result, row = _preview_one_row(conn, import_id, account_id, row_index)
|
result, row = _preview_one_row(conn, import_id, account_id, row_index)
|
||||||
if row is None or isinstance(result, str):
|
if row is None or isinstance(result, str):
|
||||||
@@ -4377,12 +4459,19 @@ async def web_confirma_import(
|
|||||||
denumire = str(denumire_val).strip() if denumire_val not in (None, "") else str(operatie_val)
|
denumire = str(denumire_val).strip() if denumire_val not in (None, "") else str(operatie_val)
|
||||||
mapped["prestatii"] = [{"cod_op_service": str(operatie_val), "denumire": denumire}]
|
mapped["prestatii"] = [{"cod_op_service": str(operatie_val), "denumire": denumire}]
|
||||||
|
|
||||||
|
# Override editat in preview — aplicat INAINTE de resolve_prestatii,
|
||||||
|
# ca in _resolve_row_for_preview: prestatiile din override (chips
|
||||||
|
# editate in modal) trec prin ACELASI resolve.
|
||||||
|
override = item.get("override") or {}
|
||||||
|
if override:
|
||||||
|
mapped.update(override)
|
||||||
|
|
||||||
# Rezolva prestatii
|
# Rezolva prestatii
|
||||||
prestatii = mapped.get("prestatii") or []
|
prestatii = mapped.get("prestatii") or []
|
||||||
resolved_p, _ = resolve_prestatii(prestatii, mapping_ops, valid_codes, text_rules)
|
resolved_p, _ = resolve_prestatii(prestatii, mapping_ops, valid_codes, text_rules)
|
||||||
mapped["prestatii"] = resolved_p
|
mapped["prestatii"] = resolved_p
|
||||||
|
|
||||||
# Canonicalizare
|
# Canonicalizare (cheia identica cu preview: canonicalize_row + build_key)
|
||||||
canon = canonicalize_row(mapped)
|
canon = canonicalize_row(mapped)
|
||||||
mapped.update({
|
mapped.update({
|
||||||
"vin": canon["vin"],
|
"vin": canon["vin"],
|
||||||
@@ -4390,17 +4479,6 @@ async def web_confirma_import(
|
|||||||
"odometru_final": canon["odometru_final"],
|
"odometru_final": canon["odometru_final"],
|
||||||
})
|
})
|
||||||
|
|
||||||
# Override editat in preview — aplicat ULTIMUL, ca in resolver.
|
|
||||||
override = item.get("override") or {}
|
|
||||||
if override:
|
|
||||||
mapped.update(override)
|
|
||||||
canon = canonicalize_row(mapped)
|
|
||||||
mapped.update({
|
|
||||||
"vin": canon["vin"],
|
|
||||||
"nr_inmatriculare": canon["nr_inmatriculare"],
|
|
||||||
"odometru_final": canon["odometru_final"],
|
|
||||||
})
|
|
||||||
|
|
||||||
key = build_key(account_id, canon, env)
|
key = build_key(account_id, canon, env)
|
||||||
|
|
||||||
rows_for_hash.append(json.dumps({
|
rows_for_hash.append(json.dumps({
|
||||||
|
|||||||
@@ -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 "
|
".btn-editeaza (sau sa verifice hx-target='#detaliu-modal-body') pentru a "
|
||||||
"apela open() si instala focus-trap-ul."
|
"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