feat(import): mapare operatie->cod RAR inline in preview + camp denumire_op
Inchide deadlock-ul din canalul de import web: operatiile nemapate dintr-un
batch in staging nu aveau unde sa fie mapate din UI. Editorul "Mapari de
rezolvat" citea doar din submissions comise, iar commit-ul arunca randurile
needs_mapping -> utilizatorul ramanea blocat fara a putea trimite.
- camp canonic nou `denumire_op`: coloana descriptiva (ex. "Reparatie Motor")
alimenteaza denumirea operatiei, deci sugestia fuzzy devine utila (inainte
denumire = codul opac). Aplicat in cele 3 locuri de resolve (preview, commit
web, commit API).
- panou inline "Operatii de mapat la cod RAR" in preview: fiecare operatie
nemapata cu sugestie preselectata + dropdown + auto-send + salveaza.
- ruta POST /_import/{id}/mapare-operatie: salveaza maparea (persistenta,
operations_mapping) si re-randeaza preview-ul; randurile trec din
needs_mapping in ok fara re-upload, maparea se retine pentru fisiere viitoare.
- fix bug pre-existent de semnatura coloane: semnatura se calcula din campurile
mapate (json_mapare.keys), nu din antetul complet -> ignorarea unei coloane
schimba semnatura si maparea retinuta nu mai era gasita la preview/re-upload.
Acum mereu din antetul complet (web + API), consecvent cu preview/commit.
Teste noi: tests/test_import_mapare_operatie.py (6). Suita: 400 passed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -63,7 +63,8 @@ _CANONICAL_SYNONYMS: dict[str, list[str]] = {
|
||||
"data_prestatie": ["Data prestatie", "Data", "Date", "Data service", "Data lucrare"],
|
||||
"odometru_final": ["Odometru final", "Odometru", "KM", "Kilometri", "Km final", "Citire contor"],
|
||||
"odometru_initial": ["Odometru initial", "KM initial", "Km start"],
|
||||
"operatie": ["Operatie", "Denumire prestatie", "Prestatie", "Lucrare", "Tip lucrare", "Cod prestatie", "Cod op"],
|
||||
"operatie": ["Operatie", "Cod prestatie", "Prestatie", "Lucrare", "Tip lucrare", "Cod op"],
|
||||
"denumire_op": ["Denumire operatie", "Denumire", "Descriere", "Denumire prestatie", "Nume operatie"],
|
||||
"obs": ["Observatii", "Obs", "Mentiuni", "Note"],
|
||||
}
|
||||
|
||||
@@ -163,11 +164,15 @@ def _resolve_row_for_preview(
|
||||
if is_amb:
|
||||
is_ambiguous_date = True
|
||||
|
||||
# Operatia: daca camp canonic e "operatie", construieste prestatii
|
||||
# Operatia: daca camp canonic e "operatie", construieste prestatii.
|
||||
# denumire_op (coloana descriptiva, ex. "Reparatie Motor") alimenteaza
|
||||
# `denumire` -> sugestia fuzzy din editorul de mapari devine utila; fara ea,
|
||||
# denumire = codul opac (ex. "OP-MOTOR") si fuzzy nu are pe ce sa lucreze.
|
||||
operatie_val = mapped.pop("operatie", None)
|
||||
denumire_val = mapped.pop("denumire_op", None)
|
||||
if operatie_val and "prestatii" not in mapped:
|
||||
# Construieste un item de prestatie din operatie
|
||||
mapped["prestatii"] = [{"cod_op_service": str(operatie_val), "denumire": 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}]
|
||||
|
||||
# Canonicalizare (T9): normalizeaza VIN/nr/odometru
|
||||
canon = canonicalize_row(mapped)
|
||||
@@ -528,8 +533,22 @@ def save_column_mapping(
|
||||
if not batch:
|
||||
raise HTTPException(status_code=404, detail="batch de import inexistent")
|
||||
|
||||
# Recalculeaza semnatura din coloanele fisierului (cheile maparii)
|
||||
# Semnatura = antetul COMPLET al fisierului (toate coloanele din batch), nu
|
||||
# doar campurile mapate. Altfel, daca clientul ignora o coloana, semnatura
|
||||
# difera de cea calculata la preview (col_names = antet complet) si maparea
|
||||
# retinuta nu mai e gasita. Citim antetul din primul rand al batch-ului.
|
||||
first_row = conn.execute(
|
||||
"SELECT raw_json FROM import_rows WHERE batch_id=? ORDER BY row_index LIMIT 1",
|
||||
(import_id,),
|
||||
).fetchone()
|
||||
columns = list(req.json_mapare.keys())
|
||||
if first_row:
|
||||
try:
|
||||
rd = decrypt_creds(first_row["raw_json"]) or {}
|
||||
if rd:
|
||||
columns = list(rd.keys())
|
||||
except Exception:
|
||||
pass
|
||||
sig = _signature(columns)
|
||||
|
||||
conn.execute(
|
||||
@@ -925,10 +944,12 @@ def commit_import(
|
||||
if iso_date:
|
||||
mapped["data_prestatie"] = iso_date
|
||||
|
||||
# Operatia -> prestatii
|
||||
# Operatia -> prestatii (denumire_op alimenteaza denumirea reala)
|
||||
operatie_val = mapped.pop("operatie", None)
|
||||
denumire_val = mapped.pop("denumire_op", None)
|
||||
if operatie_val and "prestatii" not in mapped:
|
||||
mapped["prestatii"] = [{"cod_op_service": str(operatie_val), "denumire": 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}]
|
||||
|
||||
# Rezolva prestatii INAINTE de canonicalizare (altfel cheia difera de cea din preview)
|
||||
prestatii = mapped.get("prestatii") or []
|
||||
|
||||
Reference in New Issue
Block a user