diff --git a/api/app/routers/mappings.py b/api/app/routers/mappings.py
index 81214b0..1ba68c9 100644
--- a/api/app/routers/mappings.py
+++ b/api/app/routers/mappings.py
@@ -52,6 +52,7 @@ class MappingLine(BaseModel):
class MappingBatchCreate(BaseModel):
sku: str
mappings: list[MappingLine]
+ auto_restore: bool = False
# HTML page
@router.get("/mappings", response_class=HTMLResponse)
@@ -141,7 +142,7 @@ async def create_batch_mapping(data: MappingBatchCreate):
try:
results = []
for m in data.mappings:
- r = mapping_service.create_mapping(data.sku, m.codmat, m.cantitate_roa, m.procent_pret)
+ r = mapping_service.create_mapping(data.sku, m.codmat, m.cantitate_roa, m.procent_pret, auto_restore=data.auto_restore)
results.append(r)
# Mark SKU as resolved in missing_skus tracking
await sqlite_service.resolve_missing_sku(data.sku)
diff --git a/api/app/services/mapping_service.py b/api/app/services/mapping_service.py
index 9121762..0f9b153 100644
--- a/api/app/services/mapping_service.py
+++ b/api/app/services/mapping_service.py
@@ -145,8 +145,11 @@ def get_mappings(search: str = "", page: int = 1, per_page: int = 50,
"counts": counts,
}
-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."""
+def create_mapping(sku: str, codmat: str, cantitate_roa: float = 1, procent_pret: float = 100, auto_restore: bool = False):
+ """Create a new mapping. Returns dict or raises HTTPException on duplicate.
+
+ When auto_restore=True, soft-deleted records are restored+updated instead of raising 409.
+ """
if not sku or not sku.strip():
raise HTTPException(status_code=400, detail="SKU este obligatoriu")
if not codmat or not codmat.strip():
@@ -170,11 +173,22 @@ def create_mapping(sku: str, codmat: str, cantitate_roa: float = 1, procent_pret
WHERE sku = :sku AND codmat = :codmat AND sters = 1
""", {"sku": sku, "codmat": codmat})
if cur.fetchone()[0] > 0:
- raise HTTPException(
- status_code=409,
- detail="Maparea a fost ștearsă anterior",
- headers={"X-Can-Restore": "true"}
- )
+ if auto_restore:
+ cur.execute("""
+ UPDATE ARTICOLE_TERTI SET sters = 0, activ = 1,
+ cantitate_roa = :cantitate_roa, procent_pret = :procent_pret,
+ data_modif = SYSDATE
+ WHERE sku = :sku AND codmat = :codmat AND sters = 1
+ """, {"sku": sku, "codmat": codmat,
+ "cantitate_roa": cantitate_roa, "procent_pret": procent_pret})
+ conn.commit()
+ return {"sku": sku, "codmat": codmat}
+ else:
+ raise HTTPException(
+ status_code=409,
+ detail="Maparea a fost ștearsă anterior",
+ headers={"X-Can-Restore": "true"}
+ )
cur.execute("""
INSERT INTO ARTICOLE_TERTI (sku, codmat, cantitate_roa, procent_pret, activ, sters, data_creare, id_util_creare)
diff --git a/api/app/static/js/mappings.js b/api/app/static/js/mappings.js
index 54b2052..e7896f4 100644
--- a/api/app/static/js/mappings.js
+++ b/api/app/static/js/mappings.js
@@ -280,7 +280,7 @@ async function openEditModal(sku, codmat, cantitate, procent) {
editingMapping = { sku, codmat };
document.getElementById('addModalTitle').textContent = 'Editare Mapare';
document.getElementById('inputSku').value = sku;
- document.getElementById('inputSku').readOnly = true;
+ document.getElementById('inputSku').readOnly = false;
document.getElementById('pctWarning').style.display = 'none';
const container = document.getElementById('codmatLines');
@@ -292,6 +292,15 @@ async function openEditModal(sku, codmat, cantitate, procent) {
const data = await res.json();
const allMappings = (data.mappings || []).filter(m => m.sku === sku && !m.sters);
+ // Show product name if available
+ const productName = allMappings[0]?.product_name || '';
+ const productNameEl = document.getElementById('addModalProductName');
+ const productNameText = document.getElementById('inputProductName');
+ if (productName && productNameEl && productNameText) {
+ productNameText.textContent = productName;
+ productNameEl.style.display = '';
+ }
+
if (allMappings.length === 0) {
// Fallback to single line with passed values
addCodmatLine();
@@ -307,6 +316,9 @@ async function openEditModal(sku, codmat, cantitate, procent) {
const lines = container.querySelectorAll('.codmat-line');
const line = lines[lines.length - 1];
line.querySelector('.cl-codmat').value = m.codmat;
+ if (m.denumire) {
+ line.querySelector('.cl-selected').textContent = m.denumire;
+ }
line.querySelector('.cl-cantitate').value = m.cantitate_roa;
line.querySelector('.cl-procent').value = m.procent_pret;
}
@@ -436,22 +448,23 @@ async function saveMapping() {
});
} else {
// Multi-CODMAT set: delete all existing then create new batch
- const existRes = await fetch(`/api/mappings?search=${encodeURIComponent(editingMapping.sku)}&per_page=100`);
+ const oldSku = editingMapping.sku;
+ const existRes = await fetch(`/api/mappings?search=${encodeURIComponent(oldSku)}&per_page=100`);
const existData = await existRes.json();
- const existing = (existData.mappings || []).filter(m => m.sku === editingMapping.sku && !m.sters);
+ const existing = (existData.mappings || []).filter(m => m.sku === oldSku && !m.sters);
- // Delete each existing CODMAT
+ // Delete each existing CODMAT for old SKU
for (const m of existing) {
await fetch(`/api/mappings/${encodeURIComponent(m.sku)}/${encodeURIComponent(m.codmat)}`, {
method: 'DELETE'
});
}
- // Create new batch
+ // Create new batch with auto_restore (handles just-soft-deleted records)
res = await fetch('/api/mappings/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
- body: JSON.stringify({ sku, mappings })
+ body: JSON.stringify({ sku, mappings, auto_restore: true })
});
}
} else if (mappings.length === 1) {
diff --git a/api/app/templates/mappings.html b/api/app/templates/mappings.html
index 32dd257..16bb21f 100644
--- a/api/app/templates/mappings.html
+++ b/api/app/templates/mappings.html
@@ -154,5 +154,5 @@
{% endblock %}
{% block scripts %}
-
+
{% endblock %}