diff --git a/api/index.php b/api/index.php index 24721c1..c2e6222 100644 --- a/api/index.php +++ b/api/index.php @@ -3349,11 +3349,31 @@ function smartShopping(PDO $db): void { $score += 40; } - // Frequently used but stock getting low (predictive) — stricter thresholds + // Frequently used but stock getting low (predictive) — scale urgency by imminence if ($urgency === 'none' && $dailyRate > 0 && $daysLeft <= 14 && $isFrequent && $isRecent) { - $urgency = 'low'; - $reasons[] = 'Previsto esaurimento tra ~' . round($daysLeft) . 'gg'; - $score += 25; + $daysLeftDisplay = (int)round($daysLeft); + $reasons[] = 'Finisce tra ~' . $daysLeftDisplay . 'gg'; + if ($daysLeftDisplay <= 3) { + // Running out within 3 days for a frequent product → high urgency + $urgency = 'high'; + $score += 70; + } elseif ($daysLeftDisplay <= 7) { + // Running out within a week → medium + $urgency = 'medium'; + $score += 45; + } else { + $urgency = 'low'; + $score += 25; + } + } + // Also upgrade existing low urgency when imminent depletion is detected + if ($urgency === 'low' && $dailyRate > 0 && (int)round($daysLeft) <= 3 && $isFrequent) { + $urgency = 'high'; + $daysLeftLbl = 'Finisce tra ~' . (int)round($daysLeft) . 'gg'; + if (!in_array($daysLeftLbl, $reasons)) { + $reasons[] = $daysLeftLbl; + } + $score += 45; } // Opened item with fast consumption — only if actually used regularly diff --git a/assets/js/app.js b/assets/js/app.js index e1d8b2e..35ae9da 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -6725,11 +6725,17 @@ async function autoAddCriticalItems() { const lastRun = parseInt(localStorage.getItem('_autoAddedCriticalTs') || '0'); if (Date.now() - lastRun < 10 * 60 * 1000) return; localStorage.setItem('_autoAddedCriticalTs', String(Date.now())); - // Auto-add: critical urgency (always) + high urgency that are completely out of stock (qty=0) - const toAdd = smartShoppingItems.filter(i => - !i.on_bring && !_isBringPurchased(i.name, i.urgency) && - (i.urgency === 'critical' || (i.urgency === 'high' && i.current_qty === 0)) - ); + // Auto-add rules: + // - critical: always + // - high: when qty=0 OR pct_left<20 (almost gone) OR days_left<=3 (imminent) + // - any urgency with days_left<=2 and uses_per_month>=5 (running out tomorrow for heavy user) + const toAdd = smartShoppingItems.filter(i => { + if (i.on_bring || _isBringPurchased(i.name, i.urgency)) return false; + if (i.urgency === 'critical') return true; + if (i.urgency === 'high' && (i.current_qty === 0 || i.pct_left < 20 || i.days_left <= 3)) return true; + if (i.days_left <= 2 && (i.uses_per_month || 0) >= 5) return true; + return false; + }); if (toAdd.length === 0) return; const itemsToAdd = toAdd.map(i => ({ name: i.name, specification: _urgencyToSpec(i.urgency, i.brand) })); try {