- New shopping_list SQLite table (migration in migrateDB)
- shoppingGetList/Add/Remove — delegates to Bring! or internal DB
based on SHOPPING_MODE env var (default: internal)
- isShoppingBringMode() guard: requires mode=bring + BRING credentials
- bringQuickSyncProduct updated to support both modes
- All bring_* JS calls replaced with shopping_* (bring_migrate_names kept)
- New settings tab 'Lista spesa' (tab-bring) with:
- Enable/disable shopping list toggle
- Provider radio: internal vs Bring!
- Bring! sub-section (shown only when mode=bring)
- AI smart suggestions toggle
- Forecast toggle
- Auto-add threshold (qty slider)
- Price estimation section
- _applyShoppingSettingsUI, onShoppingEnabledChange, onShoppingModeChange
- SHOPPING_* env vars documented in .env.example
- cron_smart_shopping respects SHOPPING_MODE and SHOPPING_SMART_SUGGESTIONS
- Translations: 12 new keys in all 5 languages (it/en/de/fr/es)
- DB busy_timeout=5000ms + WAL pragma in getDB() (fixes#95)
- api/index.php: auto-delete legacy dispensa.db when evershelf.db exists
and dispensa.db is empty (<1KB); vacuum-sealed items only show as
expired after VACUUM_EXPIRY_EXTENSION_DAYS (default 30) past printed
date; add dbCleanup() function; add recipe/tx/vacuum params to
getServerSettings + saveSettings intMap; add 'db_cleanup' action
- api/cron_smart_shopping.php: run dbCleanup() each cron cycle
- app.js: add zerowaste_tips_enabled + screensaver_timeout + retention
days to saveSettings POST payload (were missing, causing reset on sync);
asset version bumped to v=20260518b
- .env: added ZEROWASTE_TIPS_ENABLED, RECIPE_RETENTION_DAYS=7,
TRANSACTION_RETENTION_DAYS=7, VACUUM_EXPIRY_EXTENSION_DAYS=30
- Remove recentlyExhausted bypass from shopping_name family suppression:
products recently exhausted (<14d) were incorrectly flagged as critical
even when the same family had ample stock (Yogurt 2002g, Affettato 1022g,
Pane 400g). recentlyExhausted now only bypasses loose token-based coverage.
- Add prewarmShelfLifeCache() in cron: pre-warms opened shelf life via
Gemini AI (max 5 items/cycle) so the UI never blocks on first load.
PHP api/index.php:
- DB connection failure (500) now calls _phpErrorReport()
- Main router catch-all (500) now calls _phpErrorReport()
- undoTransaction DB error (500) now calls _phpErrorReport()
PHP api/cron_smart_shopping.php:
- cron Throwable catch now calls _phpErrorReport() before exit(1)
(fires even in CRON_MODE since _phpErrorReport() has its own guard)
Scale Gateway GatewayWebSocketServer.kt:
- onError() now calls ErrorReporter.report(ex, ...) in addition to Log.e
Combined with previous kiosk commit, every error path in the entire
EverShelf stack now sends an automatic GitHub Issue.