feat(errors): erori pe 3 niveluri (problema+cauza+fix) pe API si UI (PRD 5.4)

Catalog central pur app/errors.py ca sursa unica cod->{problema,fix},
consumat de API+UI+worker. Aditiv (field/message pastrate la octet) +
rar_error stocat superset. Scope: fluxul de declarare; login/signup/CSRF
neatinse. labels.parse_erori degradeaza gratios; UI progresiv AA light+dark.
631 teste.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude Agent
2026-06-23 10:28:09 +00:00
parent b48501d8e4
commit 14e1c463f0
25 changed files with 2440 additions and 44 deletions

View File

@@ -30,6 +30,7 @@ from fastapi import APIRouter, Depends, HTTPException, UploadFile
from fastapi.responses import StreamingResponse
from pydantic import BaseModel, Field
from ... import errors
from ...auth import resolve_account_id
from ...crypto import decrypt_creds, encrypt_creds
from ...db import get_connection
@@ -326,7 +327,15 @@ def apply_row_override(
dec = decrypt_creds(row["oj"])
if dec is None:
# Decrypt fail (cheie schimbata / token corupt): no-op defensiv, NICIODATA scriere goala.
raise HTTPException(status_code=422, detail="override curent ilizibil; editare anulata")
_oi_msg = "override curent ilizibil; editare anulata"
raise HTTPException(
status_code=422,
detail={
"error": "override_ilizibil",
"message": _oi_msg,
**errors.eroare("IMPORT_OVERRIDE_ILIZIBIL", cauza=_oi_msg),
},
)
current = dec
new_override = _merge_override(current, fields)
@@ -406,6 +415,7 @@ async def upload_import(
"error": "multiple_sheets",
"message": str(ms),
"sheets": ms.sheet_names,
**errors.eroare("IMPORT_MULTIPLE_SHEETS", cauza=str(ms)),
},
)
except FileTooLarge as e:
@@ -414,6 +424,7 @@ async def upload_import(
detail={
"error": "file_too_large",
"message": str(e),
**errors.eroare("IMPORT_FISIER_PREA_MARE", cauza=str(e)),
},
)
except HeaderError as e:
@@ -423,23 +434,28 @@ async def upload_import(
"error": "header_error",
"message": str(e),
"found": e.found,
**errors.eroare("IMPORT_ANTET_NECLAR", cauza=str(e)),
},
)
except UnicodeDecodeError as e:
_enc_msg = f"Encoding nesuportat: {e.reason}"
raise HTTPException(
status_code=422,
detail={
"error": "encoding_error",
"message": f"Encoding nesuportat: {e.reason}",
"message": _enc_msg,
**errors.eroare("IMPORT_ENCODING", cauza=_enc_msg),
},
)
except Exception as e:
# Fisier corupt (BadZipFile, InvalidFileException, etc.)
_inv_msg = f"Fisier nerecunoscut (xlsx/csv): {type(e).__name__}"
raise HTTPException(
status_code=422,
detail={
"error": "invalid_file",
"message": f"Fisier nerecunoscut (xlsx/csv): {type(e).__name__}",
"message": _inv_msg,
**errors.eroare("IMPORT_FISIER_NERECUNOSCUT", cauza=_inv_msg),
},
)
@@ -713,11 +729,13 @@ def preview_import(
).fetchone()
if not mapping_row:
_ncm_msg = "Maparea coloanelor nu a fost configurata. Configureaza mai intai maparea."
raise HTTPException(
status_code=422,
detail={
"error": "no_column_mapping",
"message": "Maparea coloanelor nu a fost configurata. Configureaza mai intai maparea.",
"message": _ncm_msg,
**errors.eroare("IMPORT_FARA_MAPARE_COLOANE", cauza=_ncm_msg),
},
)
@@ -971,12 +989,14 @@ def commit_import(
# Gate HARD: n_confirmat trebuie sa fie EXACT egal cu numarul de randuri ok
n_total_ok = len(ok_rows)
if req.n_confirmat != n_total_ok:
_cg_msg = f"Ai confirmat {req.n_confirmat} dar sunt {n_total_ok} randuri gata de trimis. Verifica preview-ul."
raise HTTPException(
status_code=422,
detail={
"error": "confirmare_gresita",
"message": f"Ai confirmat {req.n_confirmat} dar sunt {n_total_ok} randuri gata de trimis. Verifica preview-ul.",
"message": _cg_msg,
"n_ok": n_total_ok,
**errors.eroare("IMPORT_CONFIRMARE_GRESITA", cauza=_cg_msg),
},
)