fix: smarter proactive shopping list urgency
- PHP: predictive urgency block now scales by imminence: round(days_left) <= 3 → high, <= 7 → medium, <= 14 → low (was flat 'low' for any days_left <= 14) - PHP: also upgrades existing 'low' urgency to 'high' when imminent depletion detected (round(days_left) <= 3, isFrequent) - JS: autoAddCriticalItems now also adds: - high urgency items with pct_left < 20% (nearly empty) - high urgency items with days_left <= 3 (imminent) - any item with days_left <= 2 and uses_per_month >= 5 Result: Latte di Montagna (27.8x/mo, 3 days left) now appears on shopping list before running out, as do Lenticchie/Riso Basmati at 1% stock and Sandwich at 1 day left.
This commit is contained in:
+24
-4
@@ -3349,11 +3349,31 @@ function smartShopping(PDO $db): void {
|
|||||||
$score += 40;
|
$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) {
|
if ($urgency === 'none' && $dailyRate > 0 && $daysLeft <= 14 && $isFrequent && $isRecent) {
|
||||||
$urgency = 'low';
|
$daysLeftDisplay = (int)round($daysLeft);
|
||||||
$reasons[] = 'Previsto esaurimento tra ~' . round($daysLeft) . 'gg';
|
$reasons[] = 'Finisce tra ~' . $daysLeftDisplay . 'gg';
|
||||||
$score += 25;
|
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
|
// Opened item with fast consumption — only if actually used regularly
|
||||||
|
|||||||
+11
-5
@@ -6725,11 +6725,17 @@ async function autoAddCriticalItems() {
|
|||||||
const lastRun = parseInt(localStorage.getItem('_autoAddedCriticalTs') || '0');
|
const lastRun = parseInt(localStorage.getItem('_autoAddedCriticalTs') || '0');
|
||||||
if (Date.now() - lastRun < 10 * 60 * 1000) return;
|
if (Date.now() - lastRun < 10 * 60 * 1000) return;
|
||||||
localStorage.setItem('_autoAddedCriticalTs', String(Date.now()));
|
localStorage.setItem('_autoAddedCriticalTs', String(Date.now()));
|
||||||
// Auto-add: critical urgency (always) + high urgency that are completely out of stock (qty=0)
|
// Auto-add rules:
|
||||||
const toAdd = smartShoppingItems.filter(i =>
|
// - critical: always
|
||||||
!i.on_bring && !_isBringPurchased(i.name, i.urgency) &&
|
// - high: when qty=0 OR pct_left<20 (almost gone) OR days_left<=3 (imminent)
|
||||||
(i.urgency === 'critical' || (i.urgency === 'high' && i.current_qty === 0))
|
// - 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;
|
if (toAdd.length === 0) return;
|
||||||
const itemsToAdd = toAdd.map(i => ({ name: i.name, specification: _urgencyToSpec(i.urgency, i.brand) }));
|
const itemsToAdd = toAdd.map(i => ({ name: i.name, specification: _urgencyToSpec(i.urgency, i.brand) }));
|
||||||
try {
|
try {
|
||||||
|
|||||||
Reference in New Issue
Block a user