From da46fec174974b31cba6f02730e1b5400b85b5d4 Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Wed, 29 Apr 2026 15:26:59 +0000 Subject: [PATCH] Fix anomaly banner when expected_qty is negative (untracked initial stock) --- api/index.php | 4 ++++ assets/js/app.js | 7 ++++++- translations/de.json | 3 +++ translations/en.json | 2 ++ translations/it.json | 2 ++ 5 files changed, 17 insertions(+), 1 deletion(-) diff --git a/api/index.php b/api/index.php index fcea21f..cd6cef9 100644 --- a/api/index.php +++ b/api/index.php @@ -1584,6 +1584,10 @@ function getInventoryAnomalies(PDO $db): void { if (!empty($dismissed[$key])) continue; $direction = $diff > 0 ? 'phantom' : 'missing'; + // Special case: expected is negative — more consumption recorded than entries. + // The real qty vs tx comparison is meaningless; what we actually know is that + // "initial stock was never formally registered as an 'in' transaction". + if ($expected <= 0) $direction = 'untracked'; $anomalies[] = [ 'inventory_id' => (int)$r['inventory_id'], 'product_id' => (int)$r['product_id'], diff --git a/assets/js/app.js b/assets/js/app.js index 7ac5bd2..9b58d6a 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -2962,9 +2962,14 @@ function renderBannerItem() { } else if (entry.type === 'anomaly') { const an = entry.data; const isPhantom = an.direction === 'phantom'; + const isUntracked = an.direction === 'untracked'; banner.className = 'alert-banner banner-anomaly'; iconEl.textContent = '🔍'; - if (isPhantom) { + if (isUntracked) { + // More consumption recorded than entries — initial stock was never registered + titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_untracked_title')}`; + detailEl.innerHTML = t('dashboard.banner_anomaly_untracked_detail', { inv_qty: an.inv_qty, unit: an.unit }); + } else if (isPhantom) { titleEl.textContent = `${an.name} — ${t('dashboard.banner_anomaly_phantom_title')}`; detailEl.innerHTML = t('dashboard.banner_anomaly_phantom_detail', { inv_qty: an.inv_qty, unit: an.unit, expected_qty: an.expected_qty }); } else { diff --git a/translations/de.json b/translations/de.json index 00eb1a5..60c8089 100644 --- a/translations/de.json +++ b/translations/de.json @@ -128,6 +128,9 @@ "banner_finished_check": "Kannst du nachschauen?", "banner_anomaly_phantom_title": "mehr Bestand als erwartet", "banner_anomaly_phantom_detail": "Bestand zeigt {inv_qty} {unit}, aber laut Buchungen solltest du nur {expected_qty} {unit} haben. Hast du Bestand ohne Buchung hinzugefügt?", + "banner_anomaly_untracked_title": "Anfangsbestand nicht als Eingang gebucht", + "banner_anomaly_untracked_detail": "Du hast {inv_qty} {unit} im Bestand, aber die gebuchten Abgänge übersteigen die Eingänge — der Anfangsbestand wurde wahrscheinlich nie als \"Eingang\" erfasst. Bitte korrigiere die Menge oder trage die fehlenden Eingänge nach." +, "banner_anomaly_ghost_title": "weniger Bestand als erwartet", "banner_anomaly_ghost_detail": "Laut Buchungen solltest du {expected_qty} {unit} von {name} haben, aber der Bestand zeigt nur {inv_qty} {unit}. Hast du etwas ohne Buchung entnommen?", "consumed": "Verbraucht: {n} ({pct}%)", diff --git a/translations/en.json b/translations/en.json index ccfe85d..b1bb40f 100644 --- a/translations/en.json +++ b/translations/en.json @@ -128,6 +128,8 @@ "banner_finished_check": "Can you check?", "banner_anomaly_phantom_title": "you have more stock than expected", "banner_anomaly_phantom_detail": "Inventory shows {inv_qty} {unit}, but based on records you should only have {expected_qty} {unit}. Did you add stock without recording it?", + "banner_anomaly_untracked_title": "stock not recorded as an entry", + "banner_anomaly_untracked_detail": "You have {inv_qty} {unit} in inventory, but recorded outflows exceed inflows — the initial stock was likely never added as an \"in\" transaction. You can correct the quantity or log the missing entries.", "banner_anomaly_ghost_title": "you have less stock than expected", "banner_anomaly_ghost_detail": "Based on recorded operations you should have {expected_qty} {unit} of {name}, but inventory shows only {inv_qty} {unit}. Did you take stock without recording it?", "consumed": "Consumed: {n} ({pct}%)", diff --git a/translations/it.json b/translations/it.json index 2c6b36f..728cb5f 100644 --- a/translations/it.json +++ b/translations/it.json @@ -128,6 +128,8 @@ "banner_finished_check": "Puoi controllare?", "banner_anomaly_phantom_title": "hai più scorte del previsto", "banner_anomaly_phantom_detail": "L'inventario segna {inv_qty} {unit}, ma in base alle registrazioni ne dovresti avere solo {expected_qty} {unit}. Hai aggiunto scorte senza registrarle?", + "banner_anomaly_untracked_title": "scorte non registrate come entrata", + "banner_anomaly_untracked_detail": "Hai {inv_qty} {unit} in inventario, ma le uscite registrate superano le entrate — le scorte iniziali probabilmente non sono mai state aggiunte come entrata. Puoi correggere la quantità o registrare le entrate mancanti.", "banner_anomaly_ghost_title": "hai meno scorte del previsto", "banner_anomaly_ghost_detail": "In base alle operazioni registrate dovresti avere {expected_qty} {unit} di {name}, ma l'inventario mostra solo {inv_qty} {unit}. Hai prelevato senza registrarlo?", "consumed": "Consumati: {n} ({pct}%)",