diff --git a/Abrechnung/module.php b/Abrechnung/module.php index d7a7419..6f8849a 100644 --- a/Abrechnung/module.php +++ b/Abrechnung/module.php @@ -284,19 +284,20 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) { $rows = ''; $totalCost = 0.0; - $varId = $meter['var_consumption']; - IPS_LogMessage('Abrechnung', "🔧 [AddMeterToPDFRow] Starte Berechnung für {$meter['name']} ($type) von " . date('d.m.Y H:i', $from) . " bis " . date('d.m.Y H:i', $to)); + $varId = $meter['var_consumption']; + IPS_LogMessage('Abrechnung', "🔧 [AddMeterToPDFRow] Start für Zähler '{$meter['name']}' (Typ: {$type}), Zeitraum: " . date('d.m.Y H:i', $from) . " - " . date('d.m.Y H:i', $to) . ", VarID={$varId}"); if (!IPS_VariableExists($varId)) { - IPS_LogMessage('Abrechnung', "❌ Variable {$varId} für {$meter['name']} nicht gefunden"); + IPS_LogMessage('Abrechnung', "❌ [AddMeterToPDFRow] Variable {$varId} für {$meter['name']} nicht gefunden"); return ['row' => '', 'value' => 0]; } - // --- 1. Relevante Tarife filtern --- + // --- Relevante Tarife nach Typ filtern (LOGGING ONLY) --- $filteredTariffs = array_filter($tariffs, function ($t) use ($type) { - $typeNorm = strtolower(trim($type)); $tariffType = strtolower(trim($t['unit_type'] ?? '')); + $typeNorm = strtolower(trim($type)); + $map = [ 'strombezug' => ['strom', 'strombezug', 'stromzähler', 'stromverbrauch'], 'warmwasser' => ['warmwasser', 'wasser', 'hww'], @@ -306,50 +307,47 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) return in_array($tariffType, $map[$typeNorm] ?? []) || $tariffType === $typeNorm; }); - IPS_LogMessage('Abrechnung', "🔹 Gefundene Tarife für Typ '$type': " . count($filteredTariffs)); + IPS_LogMessage('Abrechnung', "🔹 [AddMeterToPDFRow] passende Tarife für '{$type}': " . count($filteredTariffs)); if (empty($filteredTariffs)) { - IPS_LogMessage('Abrechnung', "⚠ Keine passenden Tarife gefunden → alles 0 Rp"); + IPS_LogMessage('Abrechnung', "⚠ [AddMeterToPDFRow] Keine passenden Tarife für Typ '{$type}' gefunden → 0 Rp"); return ['row' => '', 'value' => 0]; } - // --- 2. Zeitstempel normalisieren & sortieren --- + // --- Tarife sicher in Zeiträume umwandeln (LOGGING ONLY) --- foreach ($filteredTariffs as &$t) { $t['start_ts'] = is_numeric($t['start']) ? intval($t['start']) : strtotime($t['start']); $t['end_ts'] = is_numeric($t['end']) ? intval($t['end']) : strtotime($t['end']); IPS_LogMessage('Abrechnung', sprintf( - " 🕓 Tarif: %s - %s @ %.3f Rp", - date('d.m.Y H:i', $t['start_ts']), - date('d.m.Y H:i', $t['end_ts']), - floatval($t['price']) + " 🕓 Tariffenster: %s → %s | Preis: %s Rp | unit_type='%s'", + $t['start_ts'] ? date('d.m.Y H:i', $t['start_ts']) : '❌', + $t['end_ts'] ? date('d.m.Y H:i', $t['end_ts']) : '❌', + isset($t['price']) ? str_replace('.', ',', (string)$t['price']) : '❌', + $t['unit_type'] ?? '—' )); } unset($t); + + // Sortiere Tarife nach Startzeit (unverändert) usort($filteredTariffs, fn($a, $b) => $a['start_ts'] <=> $b['start_ts']); - // --- 3. Iteration über Tarifabschnitte --- $current = $from; $segIndex = 1; while ($current < $to) { - IPS_LogMessage('Abrechnung', "➡️ [Segment $segIndex] Start: " . date('d.m.Y H:i', $current)); + IPS_LogMessage('Abrechnung', "➡️ [AddMeterToPDFRow] Segment {$segIndex} START: " . date('d.m.Y H:i', $current)); - // Aktiven Tarif finden - $active = null; + // Finde aktiven Tarif für diesen Zeitpunkt (unverändert) + $activeTariff = null; foreach ($filteredTariffs as $t) { - if ($t['start_ts'] <= $current && $current < $t['end_ts']) { - $active = $t; + if ($t['start_ts'] <= $current && $current <= $t['end_ts']) { + $activeTariff = $t; break; } } - if ($active) { - $segmentEnd = min($active['end_ts'], $to); - $tariffPrice = floatval($active['price']); - $tariffLabel = number_format($tariffPrice, 2); - IPS_LogMessage('Abrechnung', sprintf(" ✅ Aktiver Tarif gefunden: %.3f Rp (gültig bis %s)", $tariffPrice, date('d.m.Y H:i', $active['end_ts']))); - } else { - // Kein aktiver Tarif gefunden → Nulltarif + // Wenn kein aktiver Tarif, nimm nächsten oder rechne 0 Rp (unverändert) + if (!$activeTariff) { $nextStart = $to; foreach ($filteredTariffs as $t) { if ($t['start_ts'] > $current && $t['start_ts'] < $nextStart) { @@ -359,33 +357,49 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) $segmentEnd = min($nextStart, $to); $tariffPrice = 0.0; $tariffLabel = 'kein Tarif'; - IPS_LogMessage('Abrechnung', sprintf(" ⚠ Kein Tarif aktiv – verwende 0 Rp bis %s", date('d.m.Y H:i', $segmentEnd))); + IPS_LogMessage('Abrechnung', " ⚠ Kein aktiver Tarif → 0 Rp bis " . date('d.m.Y H:i', $segmentEnd)); + } else { + $segmentEnd = min($activeTariff['end_ts'], $to); + $tariffPrice = floatval($activeTariff['price']); + $tariffLabel = number_format($tariffPrice, 2, ',', ''); + IPS_LogMessage('Abrechnung', sprintf( + " ✅ Aktiver Tarif: %.3f Rp | gültig bis %s | SegmentEnd=%s", + $tariffPrice, + date('d.m.Y H:i', $activeTariff['end_ts']), + date('d.m.Y H:i', $segmentEnd) + )); } + // Sicherheitsnetz (unverändert) if ($segmentEnd <= $current) { - IPS_LogMessage('Abrechnung', " ⚠ SegmentEnd <= current – Abbruch (Ende erreicht oder Tariflücke fehlerhaft)"); + IPS_LogMessage('Abrechnung', " ⚠ segmentEnd <= current (" . date('d.m.Y H:i', $segmentEnd) . " <= " . date('d.m.Y H:i', $current) . "), Abbruch der Schleife."); break; } - // --- 4. Werte holen --- - $startVal = $this->GetValueAt($varId, $current, false); + // --- Verbrauch für diesen Zeitraum (unverändert) --- + IPS_LogMessage('Abrechnung', " 🔎 Hole Startwert @ " . date('d.m.Y H:i', $current) . " (nearestAfter=true)"); + $startVal = $this->GetValueAt($varId, $current, true); + + IPS_LogMessage('Abrechnung', " 🔎 Hole Endwert @ " . date('d.m.Y H:i', $segmentEnd) . " (nearestAfter=true)"); $endVal = $this->GetValueAt($varId, $segmentEnd, true); + + if ($startVal === null || $endVal === null) { + IPS_LogMessage('Abrechnung', " ⚠ Keine Werte gefunden (startVal=" . var_export($startVal, true) . ", endVal=" . var_export($endVal, true) . ") → Segment übersprungen"); + $current = $segmentEnd + 1; + $segIndex++; + continue; + } + $diff = max(0, $endVal - $startVal); $costCHF = round(($tariffPrice / 100) * $diff, 2); $totalCost += $costCHF; IPS_LogMessage('Abrechnung', sprintf( - " 📊 Werte: Start=%.3f (%.s), Ende=%.3f (%.s), Verbrauch=%.3f, Tarif=%.3f Rp, Kosten=%.3f CHF", - $startVal, - date('d.m.Y H:i', $current), - $endVal, - date('d.m.Y H:i', $segmentEnd), - $diff, - $tariffPrice, - $costCHF + " 📊 Segment %d: Start=%.3f, Ende=%.3f, Verbrauch=%.3f, Tarif=%s, Kosten=%.2f CHF", + $segIndex, $startVal, $endVal, $diff, $tariffLabel, $costCHF )); - // --- 5. HTML-Zeile erstellen --- + // --- Ausgabezeile (unverändert) --- $rows .= " {$meter['name']} @@ -395,20 +409,26 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) " . number_format($startVal, 2) . " " . number_format($endVal, 2) . " " . number_format($diff, 2) . " - {$tariffLabel} + " . $tariffLabel . " " . number_format($costCHF, 2) . " "; + $current = $segmentEnd + 1; // unverändert $segIndex++; - $current = $segmentEnd + 1; } - // --- 6. Falls keine Zeilen --- - if ($rows === '') { - IPS_LogMessage('Abrechnung', "⚠ Keine Tarifabschnitte oder Werte berechnet – Erstelle Nullzeile"); + // --- Falls nichts berechnet wurde (unverändert) --- + if ($rows == '') { + IPS_LogMessage('Abrechnung', "⚠ [AddMeterToPDFRow] Keine Segmente berechnet – Fallback-Zeile mit 0 Rp"); $startVal = $this->GetValueAt($varId, $from, false); $endVal = $this->GetValueAt($varId, $to, true); $diff = max(0, $endVal - $startVal); + IPS_LogMessage('Abrechnung', sprintf( + " 🔁 Fallback: Start=%.3f (@%s), Ende=%.3f (@%s), Verbrauch=%.3f", + $startVal, date('d.m.Y H:i', $from), + $endVal, date('d.m.Y H:i', $to), + $diff + )); $rows = " {$meter['name']} @@ -423,7 +443,9 @@ private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) "; } - IPS_LogMessage('Abrechnung', sprintf("✅ [AddMeterToPDFRow] Fertig: %.3f CHF total (%d Segmente)", $totalCost, $segIndex - 1)); + IPS_LogMessage('Abrechnung', sprintf("✅ [AddMeterToPDFRow] Fertig für '%s' (%s): Total=%.2f CHF", + $meter['name'], $type, $totalCost + )); return ['row' => $rows, 'value' => $totalCost]; }