Zum Inhalt springen

PDF Export von Abwesenheiten für OrangeHRM Starter

    PDF Export von Abwesenheiten für OrangeHRM Starter

    Ein PDF Export von Abwesenheiten ist eine wichtige Funktion für Unternehmen, deren Angestellten und HR Manager. Sie ermöglicht eine übersichtliche Verwaltung der Urlaubszeiten von Mitarbeitern. Mit OrangeHRM, einer Open-Source-HR-Software, können Abwesenheitsanträge eingebracht, vom Vorgesetzten genehmigt und die Daten direkt aus der Datenbank abgerufen werden. Eine externe Webseite hilft dabei, diese Informationen für jeden Mitarbeiter in ein PDF zu exportieren. Der hier gezeigte Ansatz basiert auf PHP und der FPDF-Bibliothek. Er zeigt, wie Daten angezeigt und in ein PDF-Dokument exportiert werden.

    Überblick zum Code: index.php

    Das Skript index.php erstellt eine Benutzeroberfläche. Diese ermöglicht es, einen Mitarbeiter auszuwählen und den Zeitraum einzugeben. Das Formular ruft die Mitarbeiterdaten aus der Tabelle hs_hr_employee ab und zeigt sie in einer select-Box an.

    Wichtige Merkmale:

    • PDO-Datenbankverbindung: Sorgt für eine sichere Verbindung zur Datenbank. Zudem schützt sie vor SQL-Injection.
    • Dynamische Mitarbeiterliste: Mitarbeiterdaten werden nach Nachname und Vorname sortiert. Die Anzeige erfolgt in der select-Box.
    <!DOCTYPE html>
    <html lang="de">
    <head>
        <meta charset="UTF-8">
        <title>Urlaubs Abfrage und PDF-Export</title>
    </head>
    <body>
    <a href="https://domain.tld">Zurück zu domain.tld</a><p>
        <form method="post" action="generate_pdf.php">
            <label for="employee_id">Mitarbeiter auswählen:</label>
            <select id="employee_id" name="employee_id" required>
                <?php
                include('db.php');
                try {
                    $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
                    $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    
                    // Mitarbeiterdaten abrufen
                    $stmt = $pdo->query("SELECT employee_id, emp_firstname, emp_lastname FROM hs_hr_employee ORDER BY emp_lastname, emp_firstname");
                    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
                    if ($rows) {
                        foreach ($rows as $row) {
                            echo '<option value="' . htmlspecialchars($row['employee_id']) . '">' .
                                 htmlspecialchars($row['emp_lastname']) . ', ' .
                                 htmlspecialchars($row['emp_firstname']) . ' (' .
                                 htmlspecialchars($row['employee_id']) . ')</option>';
                        }
                    } else {
                        echo '<option value="">Keine Mitarbeiter gefunden</option>';
                    }
                } catch (PDOException $e) {
                    echo '<option value="">Fehler bei der Datenbankverbindung: ' . htmlspecialchars($e->getMessage()) . '</option>';
                }
                ?>
            </select>
            <br><br>
            <label for="start_date">Startdatum:</label>
            <input type="date" id="start_date" name="start_date" value="<?php echo date('Y') . '-01-01'; ?>" required>
            <br><br>
            <label for="end_date">Enddatum:</label>
            <input type="date" id="end_date" name="end_date" value="<?php echo (date('Y') + 1) . '-02-01'; ?>" required>
            <br><br>
            <button type="submit">PDF erstellen</button>
        </form>
    </body>
    </html>
    

    Erklärung des PDF-Skripts: generate_pdf.php

    Das Skript generate_pdf.php verarbeitet die Formulardaten. Eine SQL-Abfrage ermittelt die Abwesenheiten des ausgewählten Mitarbeiters. Der Zeitraum basiert auf den Formulardaten. Mithilfe von CTEs werden zusammenhängende Urlaubszeiträume analysiert. Das Ergebnis ist eine klare Darstellung der Daten im PDF.

    Hauptpunkte des Codes:

    • Datenverarbeitung: CTEs gruppieren und analysieren Daten. Das sorgt für eine klare Struktur.
    • PDF-Erstellung: Die FPDF-Bibliothek erstellt eine PDF-Datei. Sie enthält eine Tabelle mit Namen, Urlaubszeiten, Urlaubsanspruch und der verbleibende Anzahl der Tage.
    • Logo-Integration: Ein Firmenlogo wird optional eingefügt. Dadurch wirkt das PDF professioneller. Der Dateiname muss logo.png lauten.
    <?php
    require('fpdf.php');
    include('db.php');
    
    try {
        $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password);
        $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
    } catch (PDOException $e) {
        die("Datenbankverbindung fehlgeschlagen: " . $e->getMessage());
    }
    
    // Prüfen, ob die employee_id und die Datumsangaben übergeben wurden
    if ($_SERVER['REQUEST_METHOD'] == 'POST' && !empty($_POST['employee_id']) && !empty($_POST['start_date']) && !empty($_POST['end_date'])) {
        $employee_id = $_POST['employee_id'];
        $start_date = $_POST['start_date'];
        $end_date = $_POST['end_date'];
    
        // SQL-Abfrage ausführen
        $sql = "
            WITH ordered_dates AS (
                SELECT
                    ohrm_leave.emp_number,
                    hs_hr_employee.emp_number AS emp_number_employee,
                    hs_hr_employee.employee_id,
                    ohrm_leave.date AS event_date,
                    hs_hr_employee.emp_firstname AS firstname,
                    hs_hr_employee.emp_lastname AS lastname,
                    LAG(ohrm_leave.date) OVER (PARTITION BY ohrm_leave.emp_number ORDER BY ohrm_leave.date) AS prev_date,
                    ohrm_leave.status
                FROM
                    ohrm_leave
                JOIN
                    hs_hr_employee ON ohrm_leave.emp_number = hs_hr_employee.emp_number
                WHERE
                    hs_hr_employee.employee_id = :employee_id
                    AND ohrm_leave.status NOT IN ('-1', '0', '4', '5')
                    AND ohrm_leave.date BETWEEN :start_date AND :end_date
            ),
            grouped_dates AS (
                SELECT
                    emp_number,
                    emp_number_employee,
                    employee_id,
                    event_date,
                    firstname,
                    lastname,
                    CASE
                        WHEN prev_date IS NULL OR DATEDIFF(event_date, prev_date) > 1 THEN event_date
                        ELSE NULL
                    END AS group_start
                FROM
                    ordered_dates
            ),
            filled_groups AS (
                SELECT
                    emp_number,
                    emp_number_employee,
                    employee_id,
                    event_date,
                    firstname,
                    lastname,
                    MAX(group_start) OVER (PARTITION BY emp_number ORDER BY event_date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS group_identifier
                FROM
                    grouped_dates
            ),
            working_days AS (
                SELECT
                    fg.emp_number,
                    fg.emp_number_employee,
                    fg.employee_id,
                    fg.group_identifier,
                    fg.firstname,
                    fg.lastname,
                    fg.event_date,
                    COUNT(CASE
                        WHEN WEEKDAY(fg.event_date) NOT IN (5, 6)
                        AND fg.event_date NOT IN (SELECT date FROM ohrm_holiday)
                        THEN 1
                    END) OVER (PARTITION BY fg.group_identifier)
                    + SUM(CASE
                        WHEN DATE_FORMAT(fg.event_date, '%m-%d') IN ('12-24', '12-31') THEN 0.5
                        ELSE 0
                    END) OVER (PARTITION BY fg.group_identifier) AS consumed_days
                FROM
                    filled_groups fg
            ),
            leave_difference AS (
                SELECT
                    emp_number,
                    ABS(
                        (SELECT COALESCE(SUM(length_days), 0)
                         FROM ohrm_leave
                         WHERE emp_number=fg.emp_number
                         AND YEAR(date) = YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))
                         AND status NOT IN ('-1', '0', '4', '5')
                        ) -
                        (SELECT COALESCE(SUM(no_of_days), 0)
                         FROM ohrm_leave_entitlement
                         WHERE emp_number=fg.emp_number
                         AND YEAR(from_date) = YEAR(DATE_SUB(CURDATE(), INTERVAL 1 YEAR))
                        )
                    ) AS difference
                FROM
                    filled_groups fg
                GROUP BY
                    emp_number
            ),
            current_year_days AS (
                SELECT
                    fg.emp_number,
                    SUM(CASE
                        WHEN WEEKDAY(fg.event_date) NOT IN (5, 6)
                        AND fg.event_date NOT IN (SELECT date FROM ohrm_holiday)
                        AND YEAR(fg.event_date) = YEAR(CURDATE())
                        THEN 1
                        ELSE 0
                    END) AS current_year_consumed_days
                FROM
                    working_days fg
                GROUP BY
                    fg.emp_number
            )
            SELECT
                fg.emp_number,
                fg.emp_number_employee,
                fg.employee_id,
                DATE_FORMAT(MIN(fg.event_date), '%d.%m.%Y') AS start_date,
                DATE_FORMAT(MAX(fg.event_date), '%d.%m.%Y') AS end_date,
                fg.firstname,
                fg.lastname,
                MAX(fg.consumed_days) AS total_days,
                FLOOR(
                    (SELECT COALESCE(SUM(no_of_days), 0)
                     FROM ohrm_leave_entitlement
                     WHERE emp_number = fg.emp_number
                     AND YEAR(from_date) = YEAR(CURDATE())
                     AND YEAR(to_date) = YEAR(CURDATE())
                    ) + ld.difference
                ) AS total_entitlement,
                cyd.current_year_consumed_days
            FROM
                working_days fg
            CROSS JOIN
                leave_difference ld
            LEFT JOIN
                current_year_days cyd ON fg.emp_number = cyd.emp_number
            GROUP BY
                fg.emp_number, fg.emp_number_employee, fg.employee_id, fg.group_identifier, fg.firstname, fg.lastname
            ORDER BY
                MIN(fg.event_date);
        ";
    
        $stmt = $pdo->prepare($sql);
        $stmt->bindParam(':employee_id', $employee_id, PDO::PARAM_STR);
        $stmt->bindParam(':start_date', $start_date, PDO::PARAM_STR);
        $stmt->bindParam(':end_date', $end_date, PDO::PARAM_STR);
        $stmt->execute();
    
        $results = $stmt->fetchAll(PDO::FETCH_ASSOC);
    
        if ($results) {
            // PDF-Erstellung
            $pdf = new FPDF();
            $pdf->AddPage();
    
            // Logo im Kopf hinzufügen
            $pdf->Image('logo.png', 10, 6, 30); // Position (x=10, y=6) und Breite (30)
            $pdf->SetFont('Arial', 'B', 14);
            $pdf->Ln(10); // Abstand nach dem Logo
            $pdf->Cell(0, 10, utf8_decode('Urlaube für Mitarbeiter: ' . $employee_id), 0, 1, 'C');
            $pdf->Ln(5);
    
            // Tabellenkopf
            $pdf->SetFont('Arial', 'B', 12);
            $pdf->Cell(30, 10, utf8_decode('Nachname'), 1);
            $pdf->Cell(30, 10, utf8_decode('Vorname'), 1);
            $pdf->Cell(40, 10, utf8_decode('Beginn'), 1);
            $pdf->Cell(40, 10, utf8_decode('Ende'), 1);
            $pdf->Cell(30, 10, utf8_decode('Tage'), 1);
            $pdf->Ln();
    
            // Tabelleninhalt
            $pdf->SetFont('Arial', '', 12);
            $total_sum = 0;
            $total_entitlement = 0;
            foreach ($results as $row) {
                $formatted_days = $row['total_days'] == floor($row['total_days']) ? (int)$row['total_days'] : $row['total_days'];
                $pdf->Cell(30, 10, utf8_decode($row['lastname']), 1);
                $pdf->Cell(30, 10, utf8_decode($row['firstname']), 1);
                $pdf->Cell(40, 10, utf8_decode($row['start_date']), 1);
                $pdf->Cell(40, 10, utf8_decode($row['end_date']), 1);
                $pdf->Cell(30, 10, utf8_decode($formatted_days), 1);
                $pdf->Ln();
                $total_sum += $row['total_days'];
                $total_entitlement = $row['total_entitlement'];
                $current_year_consumed_days = $row['current_year_consumed_days'];
            }
    
            // Calculate available days using only current year consumed days
            $available_days = $total_entitlement - $current_year_consumed_days;
            $formatted_available_days = $available_days == floor($available_days) ? (int)$available_days : $available_days;
    
            // Gesamturlaub und verfügbare Tage berechnen und anzeigen
            $pdf->SetFont('Arial', 'B', 12);
            $pdf->Cell(140, 10, 'Summe der gebuchten Tage', 1);
            $pdf->Cell(30, 10, utf8_decode($total_sum), 1);
            $pdf->Ln();
            $pdf->Cell(140, 10, 'Urlaubsanspruch ' . date('Y'), 1);
            $pdf->Cell(30, 10, utf8_decode((int)$total_entitlement), 1);
            $pdf->Ln();
            $pdf->Cell(140, 10, utf8_decode('Verfügbare Tage ') . date('Y'), 1);
            $pdf->Cell(30, 10, utf8_decode($formatted_available_days), 1);
            $pdf->Ln();
    
            // PDF speichern
            $outputFileName = "result_" . $employee_id . ".pdf";
            $pdf->Output($outputFileName, 'F');
    
            echo "PDF wurde erstellt: <a href='$outputFileName'>Hier herunterladen</a>";
            echo "<p>Sie werden in 5 Sekunden weitergeleitet...</p>";
            echo "<script>
                setTimeout(function() {
                    window.location.href = '$outputFileName';
                }, 5000);
            </script>";
        } else {
            echo "Keine Ergebnisse für Employee ID: " . htmlspecialchars($employee_id);
        }
    } else {
        echo "Bitte geben Sie eine Employee ID und einen Zeitraum ein.";
    }
    ?>

    Skript db.php

    Im db.php werden die Verbindungsdaten zur Datenbank hinterlegt.

    <?php
                // Verbindung zur Datenbank herstellen
                $host = 'localhost';
                $dbname = 'orangehrm';
                $username = 'orangehrm';
                $password = '';
    ?>

    Funktionen des PDF-Exports

    Das Skript zeigt die Mitarbeiterdaten tabellarisch. Es enthält Namen, Start- und Enddatum der Abwesenheit sowie die Urlaubstage. Eine Zusammenfassung berechnet die genutzten und verbleibenden Tage. Diese PDF-Datei ist für HR-Abteilungen und Mitarbeiter hilfreich. Sie bietet eine einfache Übersicht und kann intern genutzt werden. So kann man aus OrangeHRM Starter für kleine Unternehmen mit überschaubarer Anzahl an Mitarbeitern noch etwas mehr herausholen ohne auf die Advanced Version upgraden zu müssen.

    Beispiel eines PDF (anonymisiert)

    Beispiel eines PDF Exports von Urlauben eine MA

    Update

    10.01.2025 – SQL Syntax upgedatet. Urlaubsanspruch wird jetzt nur vom aktuellen Jahr + Resttage vom vergangenen ausgewertet.
    13.01.2025 – SQL nochmal überarbeitet. Auswahl älter als aktuelles Jahr damit möglich. Urlaubsanspruch und Verfügbare Tage werden aber nur für aktuelles Jahr ausgewertet. Und Datenbankparameter in db.php verlegt.

    Schreibe einen Kommentar

    Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert