Merge branch 'develop'
This commit is contained in:
@@ -1672,6 +1672,19 @@ function getStats(PDO $db): void {
|
||||
$originalExpiry = !empty($item['expiry_date']) ? strtotime($item['expiry_date']) : null;
|
||||
|
||||
if (!empty($item['opened_at'])) {
|
||||
// For conf unit: if all whole packages (no fraction), the opened_at tracks when the
|
||||
// last package was first used, but the remaining whole confs are still sealed.
|
||||
// Use the original package expiry, not the opened shelf-life.
|
||||
if ($item['unit'] === 'conf' && $originalExpiry !== null) {
|
||||
$qty = (float)$item['quantity'];
|
||||
$frac = round($qty - (float)(int)floor($qty + 0.001), 4);
|
||||
if ($frac < 0.001) {
|
||||
// All whole: treat as sealed — use original expiry
|
||||
$item['opened_expiry'] = $item['expiry_date'] ?? null;
|
||||
$item['days_to_expiry'] = (int)round(($originalExpiry - $today) / 86400);
|
||||
goto after_expiry;
|
||||
}
|
||||
}
|
||||
// Compute opened shelf-life using AI (with rule-based fallback + persistent cache).
|
||||
// The vacuum-sealed multiplier is already handled inside getOpenedShelfLifeDays.
|
||||
$openedDays = getOpenedShelfLifeDays($item['name'], $item['category'], $item['location'], (bool)$vacuum);
|
||||
@@ -1683,6 +1696,7 @@ function getStats(PDO $db): void {
|
||||
$item['opened_expiry'] = date('Y-m-d', $finalExpiry);
|
||||
$item['days_to_expiry'] = (int)round(($finalExpiry - $today) / 86400);
|
||||
} else {
|
||||
after_expiry:
|
||||
// Legacy: no opened_at, use stored expiry_date as-is
|
||||
$item['opened_expiry'] = $item['expiry_date'] ?? null;
|
||||
$item['days_to_expiry'] = $originalExpiry !== null
|
||||
|
||||
+1
-3
@@ -2293,9 +2293,7 @@ function _renderAntiWasteSection(used30, wasted30, usedP30, wastedP30, usedP60,
|
||||
const avgAnnualKg = Math.round(bm.avgKgMonth * 12);
|
||||
const annualInfo = t('antiwaste.annual_info')
|
||||
.replace('{you}', myAnnualKg)
|
||||
.replace('{avg}', avgAnnualKg)
|
||||
.replace('{min}', bm.rangeMin)
|
||||
.replace('{max}', bm.rangeMax);
|
||||
.replace('{avg}', avgAnnualKg);
|
||||
|
||||
// Build all badge objects (shown 4 at a time, rotated every 5 min)
|
||||
const diffPct = avgRate - myRate;
|
||||
|
||||
@@ -700,7 +700,7 @@
|
||||
"live_on": "Live-Daten",
|
||||
"live_off": "Offline",
|
||||
"meals": "Mahlzeiten",
|
||||
"annual_info": "📅 Du ~{you} kg/Jahr · Ø ~{avg} kg/Jahr · Spanne {min}–{max}%",
|
||||
"annual_info": "📅 Du ~{you} kg/Jahr · Ø ~{avg} kg/Jahr",
|
||||
"badge_rate": "Verlustquote",
|
||||
"badge_saved_money": "gespart vs Ø",
|
||||
"badge_wasted": "verloren",
|
||||
|
||||
@@ -699,7 +699,7 @@
|
||||
"live_on": "Live data",
|
||||
"live_off": "Offline",
|
||||
"meals": "meals",
|
||||
"annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr · range {min}–{max}%",
|
||||
"annual_info": "📅 You ~{you} kg/yr · avg ~{avg} kg/yr",
|
||||
"badge_rate": "loss rate",
|
||||
"badge_saved_money": "saved vs avg",
|
||||
"badge_wasted": "items lost",
|
||||
|
||||
@@ -699,7 +699,7 @@
|
||||
"live_on": "Dati in tempo reale",
|
||||
"live_off": "Offline",
|
||||
"meals": "pasti",
|
||||
"annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno · intervallo {min}–{max}%",
|
||||
"annual_info": "📅 Tu ~{you} kg/anno · media ~{avg} kg/anno",
|
||||
"badge_rate": "tasso perdita",
|
||||
"badge_saved_money": "risparmio vs media",
|
||||
"badge_wasted": "prod. persi",
|
||||
|
||||
Reference in New Issue
Block a user