feat(mappings): strict validation + silent CSV skip for missing CODMAT
Add Pydantic validators and service-level checks that reject empty SKU/CODMAT on create/edit (400). CSV import now silently skips rows without CODMAT and counts them in skipped_no_codmat instead of treating them as errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -147,6 +147,10 @@ def get_mappings(search: str = "", page: int = 1, per_page: int = 50,
|
||||
|
||||
def create_mapping(sku: str, codmat: str, cantitate_roa: float = 1, procent_pret: float = 100):
|
||||
"""Create a new mapping. Returns dict or raises HTTPException on duplicate."""
|
||||
if not sku or not sku.strip():
|
||||
raise HTTPException(status_code=400, detail="SKU este obligatoriu")
|
||||
if not codmat or not codmat.strip():
|
||||
raise HTTPException(status_code=400, detail="CODMAT este obligatoriu")
|
||||
if database.pool is None:
|
||||
raise HTTPException(status_code=503, detail="Oracle unavailable")
|
||||
|
||||
@@ -229,6 +233,10 @@ def delete_mapping(sku: str, codmat: str):
|
||||
def edit_mapping(old_sku: str, old_codmat: str, new_sku: str, new_codmat: str,
|
||||
cantitate_roa: float = 1, procent_pret: float = 100):
|
||||
"""Edit a mapping. If PK changed, soft-delete old and insert new."""
|
||||
if not new_sku or not new_sku.strip():
|
||||
raise HTTPException(status_code=400, detail="SKU este obligatoriu")
|
||||
if not new_codmat or not new_codmat.strip():
|
||||
raise HTTPException(status_code=400, detail="CODMAT este obligatoriu")
|
||||
if database.pool is None:
|
||||
raise HTTPException(status_code=503, detail="Oracle unavailable")
|
||||
|
||||
@@ -283,23 +291,27 @@ def import_csv(file_content: str):
|
||||
|
||||
reader = csv.DictReader(io.StringIO(file_content))
|
||||
created = 0
|
||||
updated = 0
|
||||
skipped_no_codmat = 0
|
||||
errors = []
|
||||
|
||||
with database.pool.acquire() as conn:
|
||||
with conn.cursor() as cur:
|
||||
for i, row in enumerate(reader, 1):
|
||||
sku = row.get("sku", "").strip()
|
||||
codmat = row.get("codmat", "").strip()
|
||||
|
||||
if not sku:
|
||||
errors.append(f"Rând {i}: SKU lipsă")
|
||||
continue
|
||||
|
||||
if not codmat:
|
||||
skipped_no_codmat += 1
|
||||
continue
|
||||
|
||||
try:
|
||||
sku = row.get("sku", "").strip()
|
||||
codmat = row.get("codmat", "").strip()
|
||||
cantitate = float(row.get("cantitate_roa", "1") or "1")
|
||||
procent = float(row.get("procent_pret", "100") or "100")
|
||||
|
||||
if not sku or not codmat:
|
||||
errors.append(f"Row {i}: missing sku or codmat")
|
||||
continue
|
||||
|
||||
# Try update first, insert if not exists (MERGE)
|
||||
cur.execute("""
|
||||
MERGE INTO ARTICOLE_TERTI t
|
||||
USING (SELECT :sku AS sku, :codmat AS codmat FROM DUAL) s
|
||||
@@ -314,16 +326,14 @@ def import_csv(file_content: str):
|
||||
(sku, codmat, cantitate_roa, procent_pret, activ, sters, data_creare, id_util_creare)
|
||||
VALUES (:sku, :codmat, :cantitate_roa, :procent_pret, 1, 0, SYSDATE, -3)
|
||||
""", {"sku": sku, "codmat": codmat, "cantitate_roa": cantitate, "procent_pret": procent})
|
||||
|
||||
# Check if it was insert or update by rowcount
|
||||
created += 1 # We count total processed
|
||||
created += 1
|
||||
|
||||
except Exception as e:
|
||||
errors.append(f"Row {i}: {str(e)}")
|
||||
errors.append(f"Rând {i}: {str(e)}")
|
||||
|
||||
conn.commit()
|
||||
|
||||
return {"processed": created, "errors": errors}
|
||||
return {"processed": created, "skipped_no_codmat": skipped_no_codmat, "errors": errors}
|
||||
|
||||
def export_csv():
|
||||
"""Export all mappings as CSV string."""
|
||||
|
||||
Reference in New Issue
Block a user