feat(editor): editor prestatii unificat in modal - un select, un rand de chips (PRD 5.23)
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>
This commit is contained in:
@@ -212,7 +212,7 @@ def test_camp_observatii_prezent(client):
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_chips_multi_select_prestatii(client):
|
||||
"""Submission cu cod_prestatie setat afiseaza chip cu hidden input."""
|
||||
"""Submission cu cod_prestatie setat afiseaza chip cu OE-1 in starea hidden chips_state."""
|
||||
acct = _create_account_user("chips.test@test.com")
|
||||
_login(client, "chips.test@test.com")
|
||||
_seed_cod("OE-1", "Schimb ulei motor")
|
||||
@@ -222,14 +222,13 @@ def test_chips_multi_select_prestatii(client):
|
||||
assert resp.status_code == 200
|
||||
html = resp.text
|
||||
|
||||
# Trebuie sa existe un input (de obicei hidden) cu name="cod_prestatie" si valoarea "OE-1"
|
||||
has_cod_prestatie_chip = bool(
|
||||
re.search(r'<input[^>]+name="cod_prestatie"[^>]+value="OE-1"', html) or
|
||||
re.search(r'<input[^>]+value="OE-1"[^>]+name="cod_prestatie"', html)
|
||||
)
|
||||
assert has_cod_prestatie_chip, (
|
||||
"Formularul trebuie sa contina un hidden input cu name='cod_prestatie' value='OE-1' "
|
||||
"reprezentand chip-ul de prestatie."
|
||||
# Starea chips traieste intr-UN SINGUR hidden JSON versionat (D4, PRD 5.23).
|
||||
m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', html)
|
||||
assert m, "Formularul trebuie sa contina hidden input name='chips_state'"
|
||||
payload = json.loads(m.group(1).replace(""", '"'))
|
||||
coduri = [it["cod"] for it in payload["items"]]
|
||||
assert "OE-1" in coduri, (
|
||||
f"chips_state trebuie sa contina codul OE-1 al prestatiei. items={payload['items']}"
|
||||
)
|
||||
|
||||
|
||||
@@ -244,19 +243,19 @@ def test_adauga_sterge_chip(client):
|
||||
_seed_cod("OE-1", "Schimb ulei motor")
|
||||
csrf = _csrf(client)
|
||||
|
||||
# POST /form-chips: adauga OE-1 la prima operatie (index 0)
|
||||
# POST /form-chips: adauga OE-1 la prima operatie (index 0), stare curenta = un
|
||||
# singur hidden chips_state (JSON versionat, D4).
|
||||
state = json.dumps({"v": 1, "items": [
|
||||
{"cod": "", "op": "Op-A", "den": "Schimb ulei motor", "exclus": 0},
|
||||
]})
|
||||
resp = client.post(
|
||||
"/form-chips",
|
||||
data={
|
||||
"csrf_token": csrf,
|
||||
# Starea curenta: o operatie fara cod
|
||||
"chip_op_service": ["Op-A"],
|
||||
"chip_denumire": ["Schimb ulei motor"],
|
||||
"cod_prestatie": [""], # nemaapat initial
|
||||
# Actiunea
|
||||
"chips_state": state,
|
||||
"chips_action": "add",
|
||||
"chips_add_op_index": "0",
|
||||
"chips_add_cod_0": "OE-1",
|
||||
"chips_target_index": "0",
|
||||
"chips_pick": "OE-1",
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 200, f"/form-chips a returnat {resp.status_code}: {resp.text[:400]}"
|
||||
@@ -266,34 +265,31 @@ def test_adauga_sterge_chip(client):
|
||||
assert "OE-1" in html, (
|
||||
f"Dupa add, OE-1 trebuie sa apara in raspunsul /form-chips. html[:500]={html[:500]}"
|
||||
)
|
||||
# Si hidden input cu valoarea OE-1
|
||||
has_hidden = bool(
|
||||
re.search(r'<input[^>]+name="cod_prestatie"[^>]+value="OE-1"', html) or
|
||||
re.search(r'<input[^>]+value="OE-1"[^>]+name="cod_prestatie"', html)
|
||||
)
|
||||
assert has_hidden, (
|
||||
"Dupa add, trebuie sa existe un input cu name='cod_prestatie' value='OE-1' "
|
||||
f"in raspunsul /form-chips. html[:600]={html[:600]}"
|
||||
# Si in starea hidden chips_state
|
||||
m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', html)
|
||||
assert m, f"hidden chips_state lipsa. html[:600]={html[:600]}"
|
||||
payload = json.loads(m.group(1).replace(""", '"'))
|
||||
assert payload["items"][0]["cod"] == "OE-1", (
|
||||
f"Dupa add, chips_state trebuie sa contina OE-1. items={payload['items']}"
|
||||
)
|
||||
|
||||
|
||||
def test_sterge_chip(client):
|
||||
"""POST /form-chips cu action=remove sterge chip-ul la indexul dat."""
|
||||
"""POST /form-chips cu action=remove sterge codul chip-ului la indexul dat."""
|
||||
acct = _create_account_user("form.chips.del@test.com")
|
||||
_login(client, "form.chips.del@test.com")
|
||||
_seed_cod("OE-1", "Schimb ulei motor")
|
||||
csrf = _csrf(client)
|
||||
|
||||
# POST /form-chips: sterge chip-ul de la index 0 (OE-1 existent)
|
||||
# POST /form-chips: sterge codul chip-ului de la index 0 (OE-1 existent, mapat pe Op-A)
|
||||
state = json.dumps({"v": 1, "items": [
|
||||
{"cod": "OE-1", "op": "Op-A", "den": "Schimb ulei motor", "exclus": 0},
|
||||
]})
|
||||
resp = client.post(
|
||||
"/form-chips",
|
||||
data={
|
||||
"csrf_token": csrf,
|
||||
# Starea curenta: OE-1 mapat pe Op-A
|
||||
"chip_op_service": ["Op-A"],
|
||||
"chip_denumire": ["Schimb ulei motor"],
|
||||
"cod_prestatie": ["OE-1"],
|
||||
# Actiunea: sterge indexul 0
|
||||
"chips_state": state,
|
||||
"chips_action": "remove",
|
||||
"chips_remove_index": "0",
|
||||
},
|
||||
@@ -301,15 +297,15 @@ def test_sterge_chip(client):
|
||||
assert resp.status_code == 200, f"/form-chips remove a returnat {resp.status_code}: {resp.text[:400]}"
|
||||
|
||||
html = resp.text
|
||||
# Dupa remove, OE-1 nu mai apare ca chip (input hidden cu acea valoare)
|
||||
has_oe1_chip = bool(
|
||||
re.search(r'<input[^>]+name="cod_prestatie"[^>]+value="OE-1"', html) or
|
||||
re.search(r'<input[^>]+value="OE-1"[^>]+name="cod_prestatie"', html)
|
||||
)
|
||||
assert not has_oe1_chip, (
|
||||
"Dupa remove, OE-1 NU mai trebuie sa apara ca chip "
|
||||
f"(hidden input cu cod_prestatie=OE-1). html[:500]={html[:500]}"
|
||||
# Dupa remove, OE-1 nu mai apare ca cod in starea chips (operatia RAMANE, nemapata)
|
||||
m = re.search(r'name="chips_state"\s+value=\'([^\']*)\'', html)
|
||||
assert m
|
||||
payload = json.loads(m.group(1).replace(""", '"'))
|
||||
coduri = [it["cod"] for it in payload["items"]]
|
||||
assert "OE-1" not in coduri, (
|
||||
f"Dupa remove, OE-1 NU mai trebuie sa apara ca cod in chips_state. items={payload['items']}"
|
||||
)
|
||||
assert payload["items"][0]["op"] == "Op-A", "Operatia trebuie sa ramana (doar codul dispare)"
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
@@ -437,11 +433,16 @@ def test_form_chips_reveal_r_odo(client):
|
||||
|
||||
|
||||
# --------------------------------------------------------------------------- #
|
||||
# Test 8: Picker per operatie (E4 binding) -- format op-row #
|
||||
# Test 8: Chip warning pentru operatie nemapata (control unificat, PRD 5.23) #
|
||||
# --------------------------------------------------------------------------- #
|
||||
|
||||
def test_picker_per_operatie_in_form(client):
|
||||
"""Operatie nemapata (needs_mapping) -> formularul afiseaza picker pe operatie."""
|
||||
"""Operatie nemapata (needs_mapping) -> formularul afiseaza chip warning + selectul unic.
|
||||
|
||||
PRD 5.23 US-002: controlul unificat elimina `.op-row` (rand per operatie cu
|
||||
picker propriu); operatia nemapata devine un chip warning in containerul
|
||||
unic `.chips`, iar selectul e unul singur (`chips_pick`, id `chips-picker`).
|
||||
"""
|
||||
acct = _create_account_user("picker.op@test.com")
|
||||
_login(client, "picker.op@test.com")
|
||||
# NU seed-uim nicio mapare -> operatia ramane nemapata
|
||||
@@ -455,17 +456,15 @@ def test_picker_per_operatie_in_form(client):
|
||||
assert resp.status_code == 200
|
||||
html = resp.text
|
||||
|
||||
# Operatia REVIZIE PERIODICA trebuie sa apara in form (op-row cu clasa specifica)
|
||||
# clasa "op-row" din CSS base.html e adaugata NUMAI de chips_prestatii.html
|
||||
has_op_row = "op-row" in html
|
||||
assert has_op_row, (
|
||||
"Formularul trebuie sa contina clasa 'op-row' "
|
||||
"pentru picker-ul per-operatie. "
|
||||
"Aceasta clasa e adaugata de _chips_prestatii.html. "
|
||||
# Controlul unificat (PRD 5.23): fara .op-row, UN singur container .chips.
|
||||
assert "op-row" not in html, (
|
||||
"op-row a fost eliminat de controlul unificat (PRD 5.23 US-002). "
|
||||
f"html[:600]={html[:600]}"
|
||||
)
|
||||
# Operatia REVIZIE PERIODICA trebuie sa apara in context op-row
|
||||
assert 'class="chips"' in html
|
||||
assert 'id="chips-picker"' in html
|
||||
# Operatia REVIZIE PERIODICA trebuie sa apara ca chip warning
|
||||
assert "REVIZIE PERIODICA" in html, (
|
||||
"Operatia 'REVIZIE PERIODICA' trebuie sa apara in formularul de editare (op-row). "
|
||||
f"html[:500]={html[:500]}"
|
||||
"Operatia 'REVIZIE PERIODICA' trebuie sa apara in formularul de editare "
|
||||
f"(chip warning). html[:500]={html[:500]}"
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user