diff --git a/api/app/routers/mappings.py b/api/app/routers/mappings.py index 4d1ea23..81214b0 100644 --- a/api/app/routers/mappings.py +++ b/api/app/routers/mappings.py @@ -2,7 +2,7 @@ from fastapi import APIRouter, Query, Request, UploadFile, File from fastapi.responses import StreamingResponse, HTMLResponse, JSONResponse from fastapi.templating import Jinja2Templates from fastapi import HTTPException -from pydantic import BaseModel +from pydantic import BaseModel, validator from pathlib import Path from typing import Optional import io @@ -21,6 +21,12 @@ class MappingCreate(BaseModel): cantitate_roa: float = 1 procent_pret: float = 100 + @validator('sku', 'codmat') + def not_empty(cls, v): + if not v or not v.strip(): + raise ValueError('nu poate fi gol') + return v.strip() + class MappingUpdate(BaseModel): cantitate_roa: Optional[float] = None procent_pret: Optional[float] = None @@ -32,6 +38,12 @@ class MappingEdit(BaseModel): cantitate_roa: float = 1 procent_pret: float = 100 + @validator('new_sku', 'new_codmat') + def not_empty(cls, v): + if not v or not v.strip(): + raise ValueError('nu poate fi gol') + return v.strip() + class MappingLine(BaseModel): codmat: str cantitate_roa: float = 1 diff --git a/api/app/services/mapping_service.py b/api/app/services/mapping_service.py index 12290cc..9121762 100644 --- a/api/app/services/mapping_service.py +++ b/api/app/services/mapping_service.py @@ -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.""" diff --git a/api/app/static/js/mappings.js b/api/app/static/js/mappings.js index 5ce168b..94ab0df 100644 --- a/api/app/static/js/mappings.js +++ b/api/app/static/js/mappings.js @@ -669,9 +669,13 @@ async function importCsv() { try { const res = await fetch('/api/mappings/import-csv', { method: 'POST', body: formData }); const data = await res.json(); - let html = `