From 66400988adc54c35875e0dede30bc2d6ec3bb5e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20H=C3=A4fliger?= Date: Wed, 5 Nov 2025 08:27:33 +0100 Subject: [PATCH] no message --- Abrechnung/module.php | 173 ++++++++++++++++++++++++++++-------------- 1 file changed, 116 insertions(+), 57 deletions(-) diff --git a/Abrechnung/module.php b/Abrechnung/module.php index 68d3e39..2a14cbe 100644 --- a/Abrechnung/module.php +++ b/Abrechnung/module.php @@ -225,16 +225,18 @@ private function GetValueAt(int $varId, int $timestamp, bool $nearestAfter = tru return null; } - $maxDays = 365; // 🔹 maximal 1 Jahr zurück / vor suchen - $stepDays = 7; // 🔹 Schrittweise Erweiterung (jeweils 7 Tage) + $maxDays = 365; // maximal 1 Jahr suchen + $stepDays = 30; // in 30-Tage-Blöcken durchsuchen $valueFound = null; if ($nearestAfter) { - for ($offset = 0; $offset <= $maxDays; $offset += $stepDays) { + // 🔹 Suche den ersten Wert NACH dem gewünschten Zeitpunkt + for ($offset = 0; $offset < $maxDays; $offset += $stepDays) { $from = $timestamp + $offset * 86400; $to = $from + $stepDays * 86400; - $values = @AC_GetLoggedValues($archiveID, $varId, $from, $to, 1000); + $values = @AC_GetLoggedValues($archiveID, $varId, $from, $to, 0); if (!empty($values)) { + // ersten Wert nach Timestamp nehmen foreach ($values as $v) { if ($v['TimeStamp'] >= $timestamp) { $valueFound = floatval($v['Value']); @@ -244,93 +246,150 @@ private function GetValueAt(int $varId, int $timestamp, bool $nearestAfter = tru } } } else { - for ($offset = 0; $offset <= $maxDays; $offset += $stepDays) { + // 🔹 Suche den letzten Wert VOR dem gewünschten Zeitpunkt + for ($offset = 0; $offset < $maxDays; $offset += $stepDays) { $from = $timestamp - ($offset + $stepDays) * 86400; $to = $timestamp - $offset * 86400; - $values = @AC_GetLoggedValues($archiveID, $varId, $from, $to, 1000); + $values = @AC_GetLoggedValues($archiveID, $varId, $from, $to, 0); if (!empty($values)) { - $valueFound = floatval($values[count($values) - 1]['Value']); - break; + // letzten Wert vor Timestamp nehmen + $last = null; + foreach ($values as $v) { + if ($v['TimeStamp'] <= $timestamp) { + $last = $v; + } else { + break; + } + } + if ($last !== null) { + $valueFound = floatval($last['Value']); + break; + } } } } - if ($valueFound !== null) { - return $valueFound; + // 🔹 Falls nichts im Archiv: aktuellen Variablenwert nehmen + if ($valueFound === null) { + $fallback = floatval(GetValue($varId)); + IPS_LogMessage('Abrechnung', "⚠ Kein Archivwert für $varId gefunden – nutze aktuellen Wert ($fallback)"); + return $fallback; } - // ❗ Fallback, wenn gar nichts im Archiv gefunden wird - $fallback = floatval(GetValue($varId)); - IPS_LogMessage('Abrechnung', "⚠ Kein Archivwert für $varId im Umkreis von $maxDays Tagen gefunden, nutze aktuellen Wert ($fallback)"); - return $fallback; + return $valueFound; } private function AddMeterToPDFRow($meter, $tariffs, $from, $to, $type) { - $archiveID = @IPS_GetInstanceListByModuleID('{43192F0B-135B-4CE7-A0A7-1475603F3060}')[0]; - if (!$archiveID || !IPS_VariableExists($meter['var_consumption'])) { - IPS_LogMessage('Abrechnung', "❌ AddMeterToPDFRow: Archiv oder Variable {$meter['var_consumption']} nicht gefunden"); - return ['row' => '', 'value' => 0]; - } - $rows = ''; $totalCost = 0.0; + $varId = $meter['var_consumption']; - // Tarife nach Startzeit sortieren - usort($tariffs, fn($a, $b) => strtotime($a['start']) <=> strtotime($b['start'])); - - // Filter: nur Tarife des Typs (z. B. Strombezug, Warmwasser) - $relevantTariffs = array_filter($tariffs, fn($t) => strtolower($t['unit_type']) == strtolower($type)); - if (empty($relevantTariffs)) { - IPS_LogMessage('Abrechnung', "⚠ Keine Tarife für Typ $type gefunden"); + if (!IPS_VariableExists($varId)) { + IPS_LogMessage('Abrechnung', "❌ Variable {$varId} für {$meter['name']} nicht gefunden"); return ['row' => '', 'value' => 0]; } - // Iteriere über alle Tarifabschnitte, die den Abrechnungszeitraum ganz oder teilweise überlappen - foreach ($relevantTariffs as $t) { - $tStart = strtotime($t['start']); - $tEnd = strtotime($t['end']); + // 🔹 1. Tarife nach Typ filtern + $filteredTariffs = array_filter($tariffs, function ($t) use ($type) { + $typeNorm = strtolower(trim($type)); + $tariffType = strtolower(trim($t['unit_type'] ?? '')); + $map = [ + 'strombezug' => ['strom', 'strombezug', 'stromzähler', 'stromverbrauch'], + 'warmwasser' => ['warmwasser', 'wasser', 'hww'], + 'kaltwasser' => ['kaltwasser', 'wasser', 'kww'], + 'wärme' => ['wärme', 'heizung', 'heat'] + ]; + return in_array($tariffType, $map[$typeNorm] ?? []) || $tariffType === $typeNorm; + }); - // Überlappung mit Abrechnungszeitraum prüfen - if ($tEnd < $from || $tStart > $to) continue; + if (empty($filteredTariffs)) { + IPS_LogMessage('Abrechnung', "⚠ Keine passenden Tarife für $type gefunden"); + return ['row' => '', 'value' => 0]; + } - // Effektive Tarifperiode innerhalb des Abrechnungszeitraums bestimmen - $periodStart = max($from, $tStart); - $periodEnd = min($to, $tEnd); + // 🔹 2. Zeitstempel konvertieren & sortieren + 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']); + } + unset($t); + usort($filteredTariffs, fn($a, $b) => $a['start_ts'] <=> $b['start_ts']); - // Archivwerte zu Beginn und Ende der Teilperiode holen - $valueStart = $this->GetValueAt($meter['var_consumption'], $periodStart, false); - $valueEnd = $this->GetValueAt($meter['var_consumption'], $periodEnd, true); - - if ($valueStart === null || $valueEnd === null) { - IPS_LogMessage('Abrechnung', "⚠ Keine gültigen Werte für {$meter['name']} zwischen $periodStart und $periodEnd"); - continue; + $current = $from; + while ($current < $to) { + // 🔹 Aktiven Tarif finden + $active = null; + foreach ($filteredTariffs as $t) { + if ($t['start_ts'] <= $current && $current < $t['end_ts']) { + $active = $t; + break; + } } - // Verbrauch & Kosten berechnen - $verbrauch = max(0, $valueEnd - $valueStart); - $tarifRp = floatval($t['price']); - $kostenCHF = round(($tarifRp / 100) * $verbrauch, 2); - $totalCost += $kostenCHF; + // 🔹 Zeitraumgrenzen bestimmen + if ($active) { + $segmentEnd = min($active['end_ts'], $to); + $tariffPrice = floatval($active['price']); + $tariffLabel = number_format($tariffPrice, 2); + } else { + // kein Tarif → bis zum nächsten bekannten oder Abrechnungsende + $nextStart = $to; + foreach ($filteredTariffs as $t) { + if ($t['start_ts'] > $current && $t['start_ts'] < $nextStart) { + $nextStart = $t['start_ts']; + } + } + $segmentEnd = min($nextStart, $to); + $tariffPrice = 0.0; + $tariffLabel = 'kein Tarif'; + } - // Tabellenzeile erzeugen + // Sicherheitsnetz (kein 0-Länge-Segment) + if ($segmentEnd <= $current) break; + + // 🔹 Verbrauch holen & Kosten berechnen + $startVal = $this->GetValueAt($varId, $current, false); + $endVal = $this->GetValueAt($varId, $segmentEnd, true); + $diff = max(0, $endVal - $startVal); + $costCHF = round(($tariffPrice / 100) * $diff, 2); + $totalCost += $costCHF; + + // 🔹 HTML-Zeile schreiben $rows .= " {$meter['name']} {$type} - " . date('d.m.Y H:i', $periodStart) . " - " . date('d.m.Y H:i', $periodEnd) . " - " . number_format($valueStart, 2) . " - " . number_format($valueEnd, 2) . " - " . number_format($verbrauch, 2) . " - " . number_format($tarifRp, 2) . " - " . number_format($kostenCHF, 2) . " + " . date('d.m.Y H:i', $current) . " + " . date('d.m.Y H:i', $segmentEnd) . " + " . number_format($startVal, 2) . " + " . number_format($endVal, 2) . " + " . number_format($diff, 2) . " + {$tariffLabel} + " . number_format($costCHF, 2) . " "; + + $current = $segmentEnd + 1; // nächster Abschnitt } + // 🔹 Fallback, falls keine Zeile if ($rows === '') { - $rows = "Keine Tarifabschnitte im Zeitraum gefunden"; + $startVal = $this->GetValueAt($varId, $from, false); + $endVal = $this->GetValueAt($varId, $to, true); + $diff = max(0, $endVal - $startVal); + $rows = " + + {$meter['name']} + {$type} + " . date('d.m.Y H:i', $from) . " + " . date('d.m.Y H:i', $to) . " + " . number_format($startVal, 2) . " + " . number_format($endVal, 2) . " + " . number_format($diff, 2) . " + 0 + 0.00 + "; } return ['row' => $rows, 'value' => $totalCost];