Fix: any-token product family grouping + auto timer reset on cache change

Root cause: autoAddCriticalItems used stale in-memory cache (old critical items)
and re-added items to Bring right after manual removal, because on_bring was
now false but urgency was still 'critical' in the old cache.

PHP smartShopping():
- Rename stockByFirstToken → stockByAnyToken (indexes ALL significant tokens)
- 'Passata di pomodoro' depleted + 'Polpa di pomodoro' in stock → share token
  'pomodoro' → passata no longer flagged as critical (COVERS: passata/polpa/pelato
  and any future tomato product variant)
- Same logic: 'aglio'/'aglio rosso', 'latte'/'latte di montagna', etc.

JS loadSmartShopping():
- When critical item set changes (items added OR removed), immediately reset
  _autoAddedCriticalTs and _bringCleanupTs so next shopping load uses fresh data
  instead of debounced old data

JS cleanupObsoleteBringItems():
- Use any-token matching (like PHP) for both stockByAnyToken and urgentSmartByToken
  → 'Passata di pomodoro' in Bring, 'polpa' in stock → share 'pomodoro' → removed
This commit is contained in:
dadaloop82
2026-04-07 15:26:35 +00:00
parent dcc7e9de42
commit dccda8ebc9
2 changed files with 51 additions and 32 deletions
+18 -12
View File
@@ -2631,17 +2631,18 @@ function smartShopping(PDO $db): void {
}
} catch (Exception $e) { /* ignore */ }
// 4b. Build stockByFirstToken: first-significant-token → total in-stock qty.
// Used to skip depleted products that have an equivalent product in stock
// (e.g. "Aglio rosso" depleted but "Aglio" has 3 pz → don't re-add Aglio rosso to list)
$stockByFirstToken = [];
// 4b. Build stockByAnyToken: every significant token of in-stock products → total qty.
// Used to skip depleted products covered by any equivalent in-stock product.
// Any-token (not just first) groups product families:
// 'Passata di pomodoro' + 'Polpa di pomodoro' + 'Pelato Cirio' all share 'pomodoro'
// 'Aglio rosso' + 'Aglio' share 'aglio'
// 'Latte di Montagna' + 'Latte Parzialmente Scremato' share 'latte'
$stockByAnyToken = [];
foreach ($products as $pStock) {
$qty = isset($inventory[$pStock['id']]) ? (float)$inventory[$pStock['id']]['total_qty'] : 0;
if ($qty <= 0) continue;
$toks = $nameTokens($pStock['name']);
if (!empty($toks)) {
$tok = $toks[0];
$stockByFirstToken[$tok] = ($stockByFirstToken[$tok] ?? 0) + $qty;
foreach ($nameTokens($pStock['name']) as $tok) {
$stockByAnyToken[$tok] = ($stockByAnyToken[$tok] ?? 0) + $qty;
}
}
@@ -2721,11 +2722,16 @@ function smartShopping(PDO $db): void {
// Out of stock
if ($qty <= 0) {
// If another product with the same first-token has stock, this depletion is covered
// (e.g. "Aglio rosso" depleted but "Aglio" has 3 pz → skip "Aglio rosso")
// If ANY significant token of this depleted product also appears in an in-stock product,
// the user's need is already covered — skip flagging it.
// Examples: 'Passata di pomodoro' depleted, 'Polpa di pomodoro' in stock → share 'pomodoro' → skip
// 'Aglio rosso' depleted, 'Aglio' in stock → share 'aglio' → skip
$pToks = $nameTokens($p['name']);
$pFirst = $pToks[0] ?? null;
if ($pFirst && ($stockByFirstToken[$pFirst] ?? 0) > 0) continue;
$coveredByEquivalent = false;
foreach ($pToks as $tok) {
if (($stockByAnyToken[$tok] ?? 0) > 0) { $coveredByEquivalent = true; break; }
}
if ($coveredByEquivalent) continue;
if ($isFrequent && $isRecent && $buyCount >= 2) {
// Frequently used, recently active, AND bought multiple times → critical