diff --git a/Abrechnung/form.json b/Abrechnung/form.json index 04d47c5..68f389e 100644 --- a/Abrechnung/form.json +++ b/Abrechnung/form.json @@ -5,17 +5,15 @@ "caption": "🧾 Abrechnungseinstellungen" }, { - "type": "Image", - "name": "Logo", - "caption": "Firmenlogo" + "type": "SelectMedia", + "name": "LogoMediaID", + "caption": "Logo auswählen" }, { "type": "ValidationTextBox", "name": "FooterText", "caption": "Fusszeile" }, - - { "type": "List", diff --git a/Abrechnung/module.php b/Abrechnung/module.php index 340239c..423a4bd 100644 --- a/Abrechnung/module.php +++ b/Abrechnung/module.php @@ -3,19 +3,33 @@ declare(strict_types=1); include_once __DIR__ . '/libs/vendor/autoload.php'; // TCPDF via Composer - -class PDFWithFooter extends TCPDF { +/** + * Eigene TCPDF-Klasse mit Logo im Header und Text in der Fusszeile + */ +class InvoicePDF extends TCPDF +{ + public $logoFile = null; public $footerText = ''; - public function Footer() { + public function Header() + { + if ($this->logoFile !== null && file_exists($this->logoFile)) { + // x, y, width in mm + $this->Image($this->logoFile, 15, 10, 40); + $this->SetY(28); + } else { + $this->SetY(15); + } + } + + public function Footer() + { $this->SetY(-15); $this->SetFont('dejavusans', '', 8); $this->Cell(0, 10, $this->footerText, 0, 0, 'C'); } } - - class Abrechnung extends IPSModule { public function Create() @@ -26,7 +40,7 @@ class Abrechnung extends IPSModule $this->RegisterPropertyString('PowerMeters', '[]'); $this->RegisterPropertyString('WaterMeters', '[]'); $this->RegisterPropertyString('Tariffs', '[]'); - $this->RegisterPropertyString('Logo', ''); + $this->RegisterPropertyInteger('LogoMediaID', 0); $this->RegisterPropertyString('FooterText', 'Belevo AG • 6122 Menznau • www.belevo.ch'); $this->RegisterVariableInteger('FromDate', 'Startdatum', '~UnixTimestamp', 1); @@ -107,53 +121,53 @@ class Abrechnung extends IPSModule // Stromkosten einmal für alle User berechnen (15-Minuten-Logik) $this->CalculateAllPowerCosts($power, $tariffs, $from, $to); - $pdf = new PDFWithFooter('P', 'mm', 'A4', true, 'UTF-8', false); - $pdf->footerText = $this->ReadPropertyString('FooterText'); + // Logo & Fusszeile vorbereiten + $logoFile = $this->GetLogoFile(); + $footerText = $this->ReadPropertyString('FooterText'); + + // PDF-Objekt + $pdf = new InvoicePDF('P', 'mm', 'A4', true, 'UTF-8', false); + $pdf->logoFile = $logoFile; + $pdf->footerText = $footerText; + $pdf->SetCreator('IPSymcon Abrechnung'); - $pdf->SetMargins(15, 15, 15); + // Oben mehr Platz für Logo lassen + $pdf->SetMargins(15, 35, 15); $pdf->SetAutoPageBreak(true, 20); - $pdf->SetFont('dejavusans', '', 9); + $pdf->SetFont('dejavusans', '', 8); foreach ($users as $user) { + $pdf->AddPage(); $this->BuildUserInvoice($pdf, $user, $power, $water, $tariffs, $from, $to); } return $pdf->Output('Abrechnung.pdf', 'S'); } -private function BuildUserInvoice($pdf, $user, $power, $water, $tariffs, $from, $to) -{ - $logoData = $this->ReadPropertyString('Logo'); -if ($logoData !== '') { - $img = base64_decode($logoData); - $pdf->Image('@' . $img, 15, 10, 40); - $pdf->Ln(25); -} - - $pdf->AddPage(); - - // Kopfbereich - $html = " + private function BuildUserInvoice($pdf, $user, $power, $water, $tariffs, $from, $to) + { + // Kopfbereich + $html = "

Elektro- und Nebenkostenabrechnung


- +
Zählpunkte:
"; - // Alle Zählerpunkte des Users auflisten - foreach ($power as $m) { - if ($m['user_id'] == $user['id']) { - $html .= htmlspecialchars($m['name']) . "
"; + // Alle Zählerpunkte des Users auflisten + foreach ($power as $m) { + if ($m['user_id'] == $user['id']) { + $html .= htmlspecialchars($m['name']) . "
"; + } } - } - foreach ($water as $m) { - if ($m['user_id'] == $user['id']) { - $html .= htmlspecialchars($m['name']) . "
"; + foreach ($water as $m) { + if ($m['user_id'] == $user['id']) { + $html .= htmlspecialchars($m['name']) . "
"; + } } - } - $html .= " + $html .= "
Rechnungsadresse:
@@ -163,49 +177,50 @@ if ($logoData !== '') {
-

+

Abrechnungszeitraum: " . date('d.m.Y', $from) . " – " . date('d.m.Y', $to) . "



- "; + "; - // ========================= Elektrizität ========================= - $html .= "

Elektrizität

"; + // ========================= Elektrizität ========================= + $html .= "

Elektrizität

"; - $powerResult = $this->GetCalculatedPowerCosts($user['id']); - $html .= $powerResult['html']; - $totalPower = $powerResult['sum']; + $powerResult = $this->GetCalculatedPowerCosts($user['id']); + $html .= $powerResult['html']; + $totalPower = $powerResult['sum']; - // ========== Stromtarife anzeigen ========== - $appliedTariffs = $this->CollectTariffsForUser($tariffs, ['Netztarif','Solartarif','Einspeisetarif']); - if (!empty($appliedTariffs)) { - $html .= "

Angewendete Elektrizitätstarife:



"; - } - // ========================= Nebenkosten ========================= - $html .= "

Nebenkosten

"; + // ========================= Nebenkosten ========================= + $html .= "

Nebenkosten

"; - $additionalResult = $this->CalculateAdditionalCosts($water, $tariffs, $user['id'], $from, $to); - $html .= $additionalResult['html']; - $totalAdditional = $additionalResult['sum']; + $additionalResult = $this->CalculateAdditionalCosts($water, $tariffs, $user['id'], $from, $to); + $html .= $additionalResult['html']; + $totalAdditional = $additionalResult['sum']; - // ========================= Gesamttotal ========================= - $grandTotal = $totalPower + $totalAdditional; - $html .= " -

+ // ========================= Gesamttotal ========================= + $grandTotal = $totalPower + $totalAdditional; + $html .= " +

Gesamttotal: " . number_format($grandTotal, 2) . " CHF

- "; + "; - $pdf->writeHTML($html, true, false, true, false, ''); -} + $pdf->writeHTML($html, true, false, true, false, ''); + } // ====================== Stromkosten (15-Minuten, alle User) ====================== @@ -346,7 +361,7 @@ if ($logoData !== '') { private function GetCalculatedPowerCosts($userId) { -$html = " + $html = " @@ -362,7 +377,6 @@ $html = " "; - if (empty($this->powerCostCache) || !isset($this->powerCostCache[$userId])) { $html .= "
ZählerTotal CHF
Keine Stromzähler für diesen Benutzer

"; return ['html' => $html, 'sum' => 0.0]; @@ -401,7 +415,7 @@ $html = " private function CalculateAdditionalCosts($waterMeters, $tariffs, $userId, $from, $to) { -$html = " + $html = " @@ -415,7 +429,6 @@ $html = " "; - $total = 0.0; $usedTariffs = []; @@ -433,7 +446,6 @@ $html = " $html .= " -
ZählerKosten CHF
Total Nebenkosten: " . number_format($total, 2) . " CHF

"; if (!empty($usedTariffs)) { @@ -667,27 +679,59 @@ $html = " } return end($cands); } + private function CollectTariffsForUser($tariffs, $types) -{ - $result = []; - $wanted = array_map('strtolower', $types); + { + $result = []; + $wanted = array_map('strtolower', $types); - foreach ($tariffs as $t) { - $type = strtolower(trim($t['unit_type'] ?? '')); - if (!in_array($type, $wanted)) continue; + foreach ($tariffs as $t) { + $type = strtolower(trim($t['unit_type'] ?? '')); + if (!in_array($type, $wanted, true)) { + continue; + } - $start = $this->toUnixTs($t['start'], false); - $end = $this->toUnixTs($t['end'], true); + $start = $this->toUnixTs($t['start'], false); + $end = $this->toUnixTs($t['end'], true); - $result[] = [ - 'start' => $start, - 'end' => $end, - 'price' => (float)$t['price'] - ]; + $result[] = [ + 'start' => $start, + 'end' => $end, + 'price' => (float)$t['price'] + ]; + } + + return $result; } - return $result; -} + private function GetLogoFile() + { + $mediaID = (int)$this->ReadPropertyInteger('LogoMediaID'); + if ($mediaID <= 0 || !IPS_MediaExists($mediaID)) { + return null; + } + $media = IPS_GetMedia($mediaID); + $ext = 'png'; + if (!empty($media['MediaFile'])) { + $extFromFile = pathinfo($media['MediaFile'], PATHINFO_EXTENSION); + if ($extFromFile !== '') { + $ext = $extFromFile; + } + } + + $path = IPS_GetKernelDir() . 'media/logo_' . $mediaID . '.' . $ext; + + // Bild aus der Symcon-Media-Datenbank extrahieren + $raw = IPS_GetMediaContent($mediaID); // base64 + $bin = base64_decode($raw); + if ($bin === false) { + return null; + } + + file_put_contents($path, $bin); + + return $path; + } } ?>