fix: dispensa.db auto-delete, zerowaste save, vacuum expiry, DB retention
- 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
This commit is contained in:
@@ -79,6 +79,19 @@ try {
|
||||
echo '[' . date('Y-m-d H:i:s') . '] Shelf life pre-warm warning: ' . $pe->getMessage() . "\n";
|
||||
}
|
||||
|
||||
// ── DB cleanup (retention policy) ────────────────────────────────────
|
||||
// Delete old recipes and transactions based on .env retention settings.
|
||||
try {
|
||||
ob_start();
|
||||
dbCleanup($db);
|
||||
ob_end_clean();
|
||||
echo '[' . date('Y-m-d H:i:s') . '] DB cleanup done'
|
||||
. ' (recipes >' . env('RECIPE_RETENTION_DAYS','7') . 'd'
|
||||
. ', tx >' . env('TRANSACTION_RETENTION_DAYS','7') . 'd' . ")\n";
|
||||
} catch (Throwable $ce) {
|
||||
echo '[' . date('Y-m-d H:i:s') . '] DB cleanup warning: ' . $ce->getMessage() . "\n";
|
||||
}
|
||||
|
||||
} catch (Throwable $e) {
|
||||
$msg = $e->getMessage();
|
||||
echo '[' . date('Y-m-d H:i:s') . '] ERROR: ' . $msg . "\n";
|
||||
|
||||
+45
-5
@@ -409,7 +409,13 @@ if (($_GET['action'] ?? '') === 'health_check') {
|
||||
}
|
||||
}
|
||||
|
||||
// Legacy DB still present alongside evershelf.db → warn
|
||||
// Auto-delete legacy dispensa.db if evershelf.db already exists (it's just an empty leftover)
|
||||
if ($hasLegacy && file_exists($dbPath) && filesize($legacyDb) < 1024) {
|
||||
@unlink($legacyDb);
|
||||
$hasLegacy = false;
|
||||
}
|
||||
|
||||
// Legacy DB still present alongside evershelf.db → warn (should be rare now)
|
||||
$checks['db_legacy'] = [
|
||||
'ok' => !$hasLegacy,
|
||||
'optional' => true,
|
||||
@@ -898,6 +904,10 @@ try {
|
||||
checkUpdate();
|
||||
break;
|
||||
|
||||
case 'db_cleanup':
|
||||
dbCleanup(getDB());
|
||||
break;
|
||||
|
||||
case 'gemini_product_hint':
|
||||
geminiProductHint();
|
||||
break;
|
||||
@@ -2492,14 +2502,19 @@ function getStats(PDO $db): void {
|
||||
LIMIT 4
|
||||
")->fetchAll();
|
||||
|
||||
// Expired
|
||||
$expired = $db->query("
|
||||
// Expired — vacuum-sealed items get extra days beyond printed expiry before being flagged
|
||||
$vacExtDays = (int)env('VACUUM_EXPIRY_EXTENSION_DAYS', '30');
|
||||
$expiredStmt = $db->prepare("
|
||||
SELECT i.*, p.name, p.brand, p.category, p.unit, p.default_quantity, p.package_unit,
|
||||
COALESCE(i.vacuum_sealed, 0) as vacuum_sealed
|
||||
FROM inventory i JOIN products p ON i.product_id = p.id
|
||||
WHERE i.expiry_date IS NOT NULL AND i.expiry_date < date('now') AND i.quantity > 0
|
||||
WHERE i.expiry_date IS NOT NULL
|
||||
AND julianday('now') - julianday(i.expiry_date) > CASE WHEN COALESCE(i.vacuum_sealed,0)=1 THEN ? ELSE 0 END
|
||||
AND i.quantity > 0
|
||||
ORDER BY i.expiry_date ASC
|
||||
")->fetchAll();
|
||||
");
|
||||
$expiredStmt->execute([$vacExtDays]);
|
||||
$expired = $expiredStmt->fetchAll();
|
||||
|
||||
// Opened (items with opened_at set by the app, OR fractional-qty items as legacy fallback)
|
||||
// opened_at IS NOT NULL → already has recalculated expiry_date stored when first opened
|
||||
@@ -2899,9 +2914,31 @@ function getServerSettings(): void {
|
||||
'price_country' => env('PRICE_COUNTRY', 'Italia'),
|
||||
'price_currency' => env('PRICE_CURRENCY', 'EUR'),
|
||||
'price_update_months' => (int)env('PRICE_UPDATE_MONTHS', '3'),
|
||||
'recipe_retention_days' => (int)env('RECIPE_RETENTION_DAYS', '7'),
|
||||
'transaction_retention_days' => (int)env('TRANSACTION_RETENTION_DAYS', '7'),
|
||||
'vacuum_expiry_extension_days' => (int)env('VACUUM_EXPIRY_EXTENSION_DAYS', '30'),
|
||||
]);
|
||||
}
|
||||
|
||||
function dbCleanup(?PDO $db = null): void {
|
||||
$recipeDays = max(1, (int)env('RECIPE_RETENTION_DAYS', '7'));
|
||||
$txDays = max(1, (int)env('TRANSACTION_RETENTION_DAYS', '7'));
|
||||
$pdo = $db ?? getDB();
|
||||
try {
|
||||
// Delete old recipes (generated recipe plans)
|
||||
$pdo->prepare("DELETE FROM recipes WHERE date < date('now', ? || ' days')")
|
||||
->execute(["-$recipeDays"]);
|
||||
// Delete old transactions (keep at least the last $txDays of history)
|
||||
$pdo->prepare("DELETE FROM transactions WHERE created_at < datetime('now', ? || ' days') AND undone = 0")
|
||||
->execute(["-$txDays"]);
|
||||
// Compact the database
|
||||
$pdo->exec('VACUUM');
|
||||
echo json_encode(['success' => true, 'recipe_retention_days' => $recipeDays, 'transaction_retention_days' => $txDays]);
|
||||
} catch (Throwable $e) {
|
||||
echo json_encode(['success' => false, 'error' => $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
function saveSettings(): void {
|
||||
// Require SETTINGS_TOKEN if configured
|
||||
$requiredToken = env('SETTINGS_TOKEN');
|
||||
@@ -2960,6 +2997,9 @@ function saveSettings(): void {
|
||||
'default_persons' => 'DEFAULT_PERSONS',
|
||||
'screensaver_timeout' => 'SCREENSAVER_TIMEOUT',
|
||||
'price_update_months' => 'PRICE_UPDATE_MONTHS',
|
||||
'recipe_retention_days' => 'RECIPE_RETENTION_DAYS',
|
||||
'transaction_retention_days' => 'TRANSACTION_RETENTION_DAYS',
|
||||
'vacuum_expiry_extension_days'=> 'VACUUM_EXPIRY_EXTENSION_DAYS',
|
||||
];
|
||||
// Float keys
|
||||
$floatMap = [
|
||||
|
||||
@@ -3116,6 +3116,8 @@ async function saveSettings() {
|
||||
scale_gateway_url: s.scale_gateway_url,
|
||||
meal_plan_enabled: s.meal_plan_enabled,
|
||||
screensaver_enabled: s.screensaver_enabled,
|
||||
screensaver_timeout: s.screensaver_timeout || 5,
|
||||
zerowaste_tips_enabled: s.zerowaste_tips_enabled,
|
||||
tts_enabled: s.tts_enabled,
|
||||
tts_url: s.tts_url,
|
||||
tts_token: s.tts_token,
|
||||
@@ -3133,6 +3135,9 @@ async function saveSettings() {
|
||||
price_country: s.price_country,
|
||||
price_currency: s.price_currency,
|
||||
price_update_months: s.price_update_months,
|
||||
recipe_retention_days: s.recipe_retention_days || 7,
|
||||
transaction_retention_days: s.transaction_retention_days || 7,
|
||||
vacuum_expiry_extension_days: s.vacuum_expiry_extension_days || 30,
|
||||
}, tokenHeader);
|
||||
const statusEl = document.getElementById('settings-status');
|
||||
if (result.success) {
|
||||
|
||||
+1
-1
@@ -1652,6 +1652,6 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="assets/js/app.js?v=20260518a"></script>
|
||||
<script src="assets/js/app.js?v=20260518b"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user