no message

This commit is contained in:
2025-12-05 13:27:31 +01:00
parent 353b8afb3d
commit 51e038400b
2 changed files with 131 additions and 89 deletions

View File

@@ -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",

View File

@@ -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,14 +121,23 @@ 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);
}
@@ -123,20 +146,11 @@ class Abrechnung extends IPSModule
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 = "
<h1 style='text-align:center; margin-bottom:0;'>Elektro- und Nebenkostenabrechnung</h1>
<hr>
<table width='100%' style='font-size:10px; margin-top:5px;'>
<table width='100%' style='font-size:8px; margin-top:5px;'>
<tr>
<td width='60%'>
<strong>Zählpunkte:</strong><br>";
@@ -163,23 +177,24 @@ if ($logoData !== '') {
</td>
</tr>
</table>
<p style='font-size:10px; margin-top:10px;'>
<p style='font-size:8px; margin-top:10px;'>
<strong>Abrechnungszeitraum:</strong> " . date('d.m.Y', $from) . " " . date('d.m.Y', $to) . "
</p>
<br><hr>
";
// ========================= Elektrizität =========================
$html .= "<h2 style='margin-bottom:3px;'>Elektrizität</h2>";
$html .= "<h2 style='margin-bottom:3px; font-size:10px;'>Elektrizität</h2>";
$powerResult = $this->GetCalculatedPowerCosts($user['id']);
$html .= $powerResult['html'];
$totalPower = $powerResult['sum'];
// ========== Stromtarife anzeigen ==========
// Elektrizitätstarife anzeigen
$appliedTariffs = $this->CollectTariffsForUser($tariffs, ['Netztarif', 'Solartarif', 'Einspeisetarif']);
if (!empty($appliedTariffs)) {
$html .= "<p style='font-size:8px; margin-top:4px;'><strong>Angewendete Elektrizitätstarife:</strong></p><ul style='font-size:7px;'>";
$html .= "<p style='font-size:7px; margin-top:4px;'><strong>Angewendete Elektrizitätstarife:</strong></p>
<ul style='font-size:7px;'>";
foreach ($appliedTariffs as $t) {
$html .= "<li>"
. date('d.m.Y', $t['start']) . " " . date('d.m.Y', $t['end'])
@@ -190,7 +205,7 @@ if ($logoData !== '') {
}
// ========================= Nebenkosten =========================
$html .= "<h2 style='margin-bottom:3px;'>Nebenkosten</h2>";
$html .= "<h2 style='margin-bottom:3px; font-size:10px;'>Nebenkosten</h2>";
$additionalResult = $this->CalculateAdditionalCosts($water, $tariffs, $user['id'], $from, $to);
$html .= $additionalResult['html'];
@@ -199,7 +214,7 @@ if ($logoData !== '') {
// ========================= Gesamttotal =========================
$grandTotal = $totalPower + $totalAdditional;
$html .= "
<h2 style='text-align:right; margin-top:10px;'>
<h2 style='text-align:right; margin-top:10px; font-size:11px;'>
Gesamttotal: <strong>" . number_format($grandTotal, 2) . " CHF</strong>
</h2>
";
@@ -362,7 +377,6 @@ $html = "
<th>Total CHF</th>
</tr>";
if (empty($this->powerCostCache) || !isset($this->powerCostCache[$userId])) {
$html .= "<tr><td colspan='11' align='center'>Keine Stromzähler für diesen Benutzer</td></tr></table><br>";
return ['html' => $html, 'sum' => 0.0];
@@ -415,7 +429,6 @@ $html = "
<th>Kosten CHF</th>
</tr>";
$total = 0.0;
$usedTariffs = [];
@@ -433,7 +446,6 @@ $html = "
$html .= "<tr style='background-color:#f9f9f9; font-weight:bold;'>
<td colspan='8' align='right'><b>Total Nebenkosten:</b></td>
<td align='right'><b>" . number_format($total, 2) . " CHF</b></td>
</tr></table><br>";
if (!empty($usedTariffs)) {
@@ -667,6 +679,7 @@ $html = "
}
return end($cands);
}
private function CollectTariffsForUser($tariffs, $types)
{
$result = [];
@@ -674,7 +687,9 @@ $html = "
foreach ($tariffs as $t) {
$type = strtolower(trim($t['unit_type'] ?? ''));
if (!in_array($type, $wanted)) continue;
if (!in_array($type, $wanted, true)) {
continue;
}
$start = $this->toUnixTs($t['start'], false);
$end = $this->toUnixTs($t['end'], true);
@@ -689,5 +704,34 @@ $html = "
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;
}
}
?>