<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Ronny Schrader</title>
    <description>The latest articles on DEV Community by Ronny Schrader (@filemakerexperts).</description>
    <link>https://dev.to/filemakerexperts</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3147668%2Fc624d2ec-2117-4581-b833-79a2adb7dc86.jpg</url>
      <title>DEV Community: Ronny Schrader</title>
      <link>https://dev.to/filemakerexperts</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/filemakerexperts"/>
    <language>en</language>
    <item>
      <title>automatisierter UBL-XML-Generator in PHP in Kombination mit FileMaker</title>
      <dc:creator>Ronny Schrader</dc:creator>
      <pubDate>Sun, 11 May 2025 12:01:34 +0000</pubDate>
      <link>https://dev.to/filemakerexperts/automatisierter-ubl-xml-generator-in-php-in-kombination-mit-filemaker-5e1</link>
      <guid>https://dev.to/filemakerexperts/automatisierter-ubl-xml-generator-in-php-in-kombination-mit-filemaker-5e1</guid>
      <description>&lt;p&gt;Während meiner Arbeit an der serverseitigen PDF-Generierung mit ZUGFeRD wurde mir schnell klar, dass viele Kunden zunehmend auf den UBL-Standard setzen – gerade im internationalen Kontext oder in Verbindung mit elektronischen Rechnungsplattformen. Also habe ich kurzerhand ein eigenes PHP-Skript geschrieben, das auf POST-Daten aus FileMaker oder anderen Quellen reagiert und daraus eine gültige UBL-Rechnung im XML-Format erstellt.&lt;/p&gt;

&lt;p&gt;Wie so oft war der Aufbau des XML-Dokuments der anspruchsvollste Teil. Viele Details wie Namespaces, Pflichtfelder und ISO-konforme Datums- und Betragsformate mussten exakt stimmen. Außerdem wollte ich vermeiden, dass mein System bei fehlenden Daten abstürzt – darum habe ich Fallbacks eingebaut und ein eigenes Logging-System integriert.&lt;/p&gt;

&lt;p&gt;Das Skript liest die Rechnungsdaten, Kunden- und Lieferantendaten sowie die Rechnungspositionen ein, berechnet die Summen und schreibt daraus ein vollständiges XML-Dokument nach dem UBL 2.1-Standard, das sich z. B. auch für die XRechnung weiterverwenden lässt. Die resultierende Datei ist kompatibel mit Plattformen wie PEPPOL, eRechnung.gv.at oder Zentralplattformen der öffentlichen Hand.&lt;/p&gt;

&lt;p&gt;Die Daten werden in FileMaker gesammelt, das ganze klassisch über schleifen. Die Daten werden über ein einfaches application/x-www-form-urlencoded-POST-Request übergeben. Alle Felder werden als Key-Value-Paare übermittelt. Die Rechnungspositionen (line items) sind dabei als kompaktes Raw-String-Feld lineItemsRaw codiert, das einzelne Positionen mit | trennt und innerhalb der Position durch ; strukturiert ist.&lt;/p&gt;

&lt;p&gt;FileMaker bietet zwar mittlerweile solide Funktionen für JSON-Manipulation – aber bei 25+ Feldern und einer schlichten Punkt-zu-Punkt-Kommunikation mit meinem PHP-Skript war mir das einfach zu umständlich. Ich wollte keine JSON-Parser-Bastelei, sondern einfach Daten senden. Daher nutze ich application/x-www-form-urlencoded, was mit curl ohnehin besser lesbar ist und mir in PHP direkt über $_POST zur Verfügung steht.&lt;/p&gt;

&lt;p&gt;“-X POST ” &amp;amp; “–header "Content-Type: application/x-www-form-urlencoded" ” &amp;amp; “–data ” &amp;amp; Zitat ( “invoiceNumber=” &amp;amp; $invoiceNumber &amp;amp; “&amp;amp;invoiceDate=” &amp;amp; $invoiceDate &amp;amp; “&amp;amp;invoiceCurrencyCode=” &amp;amp; $invoiceCurrencyCode &amp;amp; “&amp;amp;invoiceTypeCode=” &amp;amp; $invoiceTypeCode &amp;amp; “&amp;amp;dueDate=” &amp;amp; $dueDate &amp;amp; “&amp;amp;paymentTerms=” &amp;amp; $paymentTerms &amp;amp; “&amp;amp;deliveryTerms=” &amp;amp; $deliveryTerms &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;sellerName=” &amp;amp; $sellerName &amp;amp; “&amp;amp;sellerStreet=” &amp;amp; $sellerStreet &amp;amp; “&amp;amp;sellerPostalCode=” &amp;amp; $sellerPostalCode &amp;amp; “&amp;amp;sellerCity=” &amp;amp; $sellerCity &amp;amp; “&amp;amp;sellerCountryCode=” &amp;amp; $sellerCountryCode &amp;amp; “&amp;amp;sellerTaxID=” &amp;amp; $sellerTaxID &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;lieferschein_nr=” &amp;amp; $lieferschein_nr &amp;amp; “&amp;amp;kunden_nr=” &amp;amp; $kunden_nr &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;buyerName=” &amp;amp; $buyerName &amp;amp; “&amp;amp;buyerStreet=” &amp;amp; $buyerStreet &amp;amp; “&amp;amp;buyerPostalCode=” &amp;amp; $buyerPostalCode &amp;amp; “&amp;amp;buyerCity=” &amp;amp; $buyerCity &amp;amp; “&amp;amp;buyerCountryCode=” &amp;amp; $buyerCountryCode &amp;amp; “&amp;amp;buyerTaxID=” &amp;amp; $buyerTaxID &amp;amp;&lt;/p&gt;

&lt;p&gt;Der Aufbau geht dann weiter über Verkäufer- und Käuferdaten, Zahlungsinformationen, steuerliche Angaben und natürlich die Rechnungspositionen, die als cac:InvoiceLine-Blöcke angelegt werden.&lt;/p&gt;

&lt;p&gt;Die Daten werden direkt auf dem Server verarbeitet, im Anschluss kann ich die XML-Datei wieder in ein FileMaker-Feld laden (Aus URL einfügen).&lt;/p&gt;

&lt;p&gt;Da ich mit horstoeko/zugferd arbeite, wird in der Folgeversion noch die Validierung erfolgen. Die derzeit händische, zeigt alle Werte, keine Fehler, keine Warnungen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwgofwp69oiq4eev1o8p.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fvwgofwp69oiq4eev1o8p.png" alt="Image description" width="800" height="327"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Sequentielle Suche mit FileMaker und PHP</title>
      <dc:creator>Ronny Schrader</dc:creator>
      <pubDate>Sun, 11 May 2025 11:49:26 +0000</pubDate>
      <link>https://dev.to/filemakerexperts/sequentielle-suche-mit-filemaker-und-php-49oj</link>
      <guid>https://dev.to/filemakerexperts/sequentielle-suche-mit-filemaker-und-php-49oj</guid>
      <description>&lt;p&gt;Manchmal sind es nicht die großen Frameworks oder hochkomplexen API-Integrationen, die den Unterschied machen, sondern ein bewusst einfacher, robuster Ansatz, der sich nahtlos in vorhandene Workflows integriert. Genau das war der Ausgangspunkt für eine kleine aber wirkungsvolle Lösung, die FileMaker mit einem PHP-basierten Webinterface verbindet – mit dem Ziel, eine sequentielle Suche über eine größere Datenmenge performant und flexibel umzusetzen.&lt;/p&gt;

&lt;p&gt;Das derzeitige Problem beim Kunden, vor Jahren habe ich eine Sequentielle Suche nativ in FileMaker umgesetzt. Die übliche Vorgehensweise, ein Script sucht bei jedem Tastenanschlag, genutzt werden alle Felder die in der Schnellsuche eingeschlossen sind. Über die Jahre wuchs der Datenbestand, es wurden Felder innerhalb von Ausschnitten erfasst. Es musste so kommen, der Kunde konnte die Suche kaum mehr nutzen, tippen, warten, tippen. Sehr unschön und langsam.&lt;/p&gt;

&lt;p&gt;Dann kam mir im Zuge einer Listenansicht, in die ich eine Suche integriert habe, die Idee. FileMaker überträgt per -Aus URL einfügen- Tabellendaten an ein PHP-Script. Im WebViewer wird dann die Tabelle angezeigt, nebst einem Suchfeld. Also frisch ans Werk gemacht und schnell festgestellt, die Felder in Variablen zu verpacken, macht in der Menge keinen Spaß. Also das ganze per Schleife, Feldnamen holen, die Werte dazu sammeln. In Schleife, ist das gut machbar, aber trotzdem ein nicht unerheblicher Zeitaufwand. Dann eine Eingebung, exportiere die Tabelle und verarbeite die Daten direkt auf dem Server per PHP.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd43yv66ut6l76tpnsvpu.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fd43yv66ut6l76tpnsvpu.png" alt="Image description" width="800" height="556"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vorgehen: FileMaker exportiert eine strukturierte Datenmenge als CSV – diese Datei wird im Hintergrund automatisch an ein kleines PHP-Script übermittelt. Dort wird sie interpretiert, analysiert und in einer visuellen Oberfläche dargestellt, über die eine freie Volltextsuche möglich ist. Der Clou: Mit jedem Tastendruck wird die Ergebnisliste dynamisch reduziert – und bei Bedarf lassen sich über die Enter-Taste direkt Projektnummern an ein FileMaker-Script zurückgeben, das dann wiederum die interne Detailansicht aktualisiert. Ganz ohne Datenbankabfragen im Webserver, ganz ohne MySQL, Redis oder externe Services.&lt;/p&gt;

&lt;p&gt;Die PHP-Logik bleibt dabei angenehm überschaubar. Ein Beispiel für das Parsen und Darstellen der Daten sieht so aus:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
$csvData = [];
$data = file_get_contents("php://input");

if ($data &amp;amp;&amp;amp; strlen($data) &amp;gt; 0) {
    $lines = preg_split('/\r\n|\r|\n/', $data);
    foreach ($lines as $line) {
        if (trim($line) !== "") {
            $csvData[] = str_getcsv($line, "\t"); // Tab-getrennt
        }
    }
    if (!empty($csvData) &amp;amp;&amp;amp; empty(array_filter(end($csvData)))) {
        array_pop($csvData);
    }
}
$spaltenIndizes = range(0, count($csvData[0] ?? []) - 1);
?&amp;gt;
In der Darstellung im WebViewer werden alle Datensätze tabellarisch angezeigt. Der Clou kommt mit JavaScript: Dort wird bei jeder Eingabe automatisch geprüft, welche Zeilen noch zum aktuellen Suchbegriff passen. Zusätzlich hebt ein kleiner Style-Block die passenden Zellen farblich hervor, um die Treffer visuell zu unterstützen. Und weil alles clientseitig passiert, bleibt es schnell – auch bei mehreren tausend Einträgen.

Besonders elegant wirkt die Integration in FileMaker: Die Projektnummern der sichtbaren Zeilen werden bei einem Enter-Klick gesammelt und per fmp://-URL an ein FileMaker-Script übergeben. Diese Direktverbindung ermöglicht, das Webinterface wie eine native Erweiterung der Datenbank zu nutzen – ohne Performanceverlust, ohne Redundanz, ohne Hürden.

document.getElementById("searchInput").addEventListener("keypress", function(event) {
    if (event.key === "Enter") {
        event.preventDefault();
        const rows = document.querySelectorAll("#csvTable tbody tr:not(.hide)");
        const ids = [];

        rows.forEach(row =&amp;gt; {
            const id = row.querySelectorAll("td")[0]?.textContent.trim(); // Erste Spalte = ID
            if (id) ids.push(id);
        });

        if (ids.length &amp;gt; 0) {
            const param = encodeURIComponent(ids.join("|"));
            const url = `fmp://$/AVAGmbH?script=Projekt_LIST_Suche_PHP¶m=${param}`;
            window.location.href = url;
        }
    }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nach dem Klick, startet das FM-Script. Wir holen uns die ID,s nach üblicher Vorgangsweise und suchen in Schleife alle ID,s zusammen. In dem Zug, wird natürlich auch das Suchfenster in FileMaker geschlossen.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdabkrmu46cmcjmexsnwc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fdabkrmu46cmcjmexsnwc.png" alt="Image description" width="715" height="433"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Diese Form der sequentiellen Suche hat sich im Test als stabil und pflegeleicht erwiesen – gerade in Szenarien, in denen FileMaker allein bei umfangreichen Datensätzen an die Grenzen kommt, etwa bei mehrdimensionalen Suchen über unstrukturierte Felder oder bei extern generierten Listen.&lt;/p&gt;

&lt;p&gt;Und auch wenn es kein High-End-AI-Suchcluster ist: Die Lösung hat Charme. Weil sie genau das tut, was sie soll. Weil sie den Workflow nicht verbiegt, sondern erweitert. Und weil sie etwas bietet, das man oft zu selten hat: unmittelbare Rückmeldung und Kontrolle über den gesamten Prozess.&lt;/p&gt;

&lt;p&gt;Jetzt wird nur noch ein wenig mit CSS das ganze verschönt, dann kann der Kunde damit arbeiten.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Kanban mit FileMaker und PHP</title>
      <dc:creator>Ronny Schrader</dc:creator>
      <pubDate>Sun, 11 May 2025 11:15:27 +0000</pubDate>
      <link>https://dev.to/filemakerexperts/kanban-mit-filemaker-und-php-34m9</link>
      <guid>https://dev.to/filemakerexperts/kanban-mit-filemaker-und-php-34m9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dg8xebd0mwg7p8syfxf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8dg8xebd0mwg7p8syfxf.png" alt="Image description" width="800" height="386"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Die Verwaltung von Aufgaben kann schnell unübersichtlich werden, wenn viele Prozesse gleichzeitig laufen und verschiedene Mitarbeiter involviert sind. Eine klassische Methode zur Visualisierung solcher Abläufe ist die Kanban-Tafel, die durch ihre intuitive Struktur einen schnellen Überblick über den Status der einzelnen Aufgaben bietet. Doch während viele Tools auf dem Markt existieren, soll in diesem Artikel eine maßgeschneiderte Lösung direkt in FileMaker realisiert werden – mit Hilfe von PHP und JavaScript in einem Webviewer. Es gibt natürlich eine FileMaker-Eigene Implementierung über die Add-ons. Mir persönlich ist diese Möglichkeit aber zu eingeschränkt. So sind die Tafeln nicht Dynamisch, ich kann z.B. keine Symbole oder Bilder mit integrieren und die Steuerung der Abläufe ist nicht richtig konsistent.&lt;/p&gt;

&lt;p&gt;Deshalb die Idee, ich setze das einfach mit PHP, Java-Script über den WebViewer von FileMaker um. Das Ziel ist es, eine interaktive Kanban-Tafel direkt in FileMaker zu integrieren, die alle relevanten Aufgaben übersichtlich darstellt und es ermöglicht, diese per Drag &amp;amp; Drop zwischen den verschiedenen Status-Spalten zu verschieben. Diese Statusänderungen sollen dann automatisch an FileMaker zurückgemeldet werden, sodass keine doppelte Pflege von Daten notwendig ist. Die Techniken dahinter: -PHP als Schnittstelle zur Verarbeitung und Bereitstellung der Daten. -JavaScript mit jQuery UI für die interaktive Drag &amp;amp; Drop-Funktionalität. -FileMaker-Skripte, um die Änderungen zurück in die FileMaker-Datenbank zu schreiben.&lt;/p&gt;

&lt;p&gt;Als erstes müssen wir uns klar werden wie die Daten an das PHP-Script übergeben werden. Ich persönlich nutze gern die Möglichkeit Einträge mit einem Pipe getrennt zu übertragen. Das lässt sich in FileMaker wunderbar über eine Schleife realisieren. In meinem Beispiel werden verschiedene ID,s übertragen.&lt;/p&gt;

&lt;p&gt;Damit die Kanban-Ansicht ihre Inhalte aus FileMaker beziehen kann, müssen die relevanten Daten über eine URL in PHP übergeben werden. Dazu werden die folgenden Parameter als GET-Request an eine PHP-Datei gesendet:&lt;/p&gt;

&lt;p&gt;projects: Enthält die Auftragsnummern. staff: Enthält die zugeordneten Mitarbeiter. anlage: Enthält die Referenz zur Anlage. status: Gibt den aktuellen Status der Aufgabe an (z. B. “In Planung” oder “Erledigt”). task: Enthält die eindeutige Task-ID (Aufgaben ID) zur Identifikation. image: Gibt den Namen des Symbols an, das in der Kanban-Karte angezeigt werden soll.&lt;/p&gt;

&lt;p&gt;Meine fertige URL schaut ungefähr so aus: &lt;a href="https://maeine_url_.de/cap/kanban.php?projects=24944%7C24945%7C24946&amp;amp;staff=STA020%7CSTA023%7CSTA025&amp;amp;anlage=B14C380C%E2%80%934329-E54B-ACA1-B2054C5D4B3A%7CB14C380C%E2%80%934329-E54B-ACA1-B2054C5D4B3A&amp;amp;status=Erledigt%7CIn" rel="noopener noreferrer"&gt;https://maeine_url_.de/cap/kanban.php?projects=24944|24945|24946&amp;amp;staff=STA020|STA023|STA025&amp;amp;anlage=B14C380C–4329-E54B-ACA1-B2054C5D4B3A|B14C380C–4329-E54B-ACA1-B2054C5D4B3A&amp;amp;status=Erledigt|In&lt;/a&gt; Planung|Material bestellt&amp;amp;task=TSK004729|TSK004730|TSK004731&amp;amp;image=Blau|Gelb|Rot&lt;/p&gt;

&lt;p&gt;Der Weg diese zu generieren sollte klar sein. Per Schleife durch die Aufgaben, entweder gefiltert nach Datum oder Zeitraum etc. Nun wird dieser Wert in eine Globale Variable geschrieben z.B. $$URL. Dann der FileMaker Befehl, aus URL einfügen. Dabei generieren wir eine Variable z.B. $$URL_WEBVIEWER. Diese kommt dann als Inhalt in unseren Webviewer den wir uns innerhalb eines FileMaker-Layouts platzieren.&lt;/p&gt;

&lt;p&gt;Für die Darstellung ein kleines PHP-Script:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;?php
// Header für UTF-8 Encoding
header("Content-Type: text/html; charset=UTF-8");

// Daten aus der URL holen
$projects = isset($_GET['projects']) ? explode('|', $_GET['projects']) : [];
$staff = isset($_GET['staff']) ? explode('|', $_GET['staff']) : [];
$anlage = isset($_GET['anlage']) ? explode('|', $_GET['anlage']) : [];
$status = isset($_GET['status']) ? explode('|', $_GET['status']) : [];
$task = isset($_GET['task']) ? explode('|', $_GET['task']) : [];
$images = isset($_GET['image']) ? explode('|', $_GET['image']) : [];

// Basis-URL für die Icons
$imageBaseUrl = "http://Deine_URL.de/GoogleMarkers/";

// Status und Farben definieren
$status_colors = [
    "Material bestellt" =&amp;gt; "#FFD700", // Gelb
    "In Planung" =&amp;gt; "#FFA500", // Orange
    "Verplant" =&amp;gt; "#FF4500", // Dunkelorange
    "Ausgeführt" =&amp;gt; "#008000", // Grün
    "Angebot folgt" =&amp;gt; "#FF0000", // Rot
    "Erledigt" =&amp;gt; "#808080" // Grau
];

// Spalten für das Kanban-Board
$columns = ["Material bestellt", "In Planung", "Verplant", "Ausgeführt", "Angebot folgt", "Erledigt"];

// Aufgaben nach Status sortieren
$tasks_by_status = [];
foreach ($status as $index =&amp;gt; $task_status) {
    $tasks_by_status[$task_status][] = [
        "id" =&amp;gt; $task[$index] ?? "TSK-Unbekannt",
        "project" =&amp;gt; $projects[$index] ?? "Unbekannt",
        "staff" =&amp;gt; $staff[$index] ?? "Kein Mitarbeiter",
        "anlage" =&amp;gt; $anlage[$index] ?? "Keine Anlage",
        "image" =&amp;gt; isset($images[$index]) &amp;amp;&amp;amp; !empty($images[$index]) ? $imageBaseUrl . $images[$index] . ".png" : "",
        "status" =&amp;gt; $task_status
    ];
}
?&amp;gt;
&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html lang="de"&amp;gt;
&amp;lt;head&amp;gt;
    &amp;lt;meta charset="UTF-8"&amp;gt;
    &amp;lt;title&amp;gt;Aufgaben Board&amp;lt;/title&amp;gt;
    &amp;lt;script src="https://code.jquery.com/jquery-3.6.0.min.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;script src="https://cdnjs.cloudflare.com/ajax/libs/jqueryui/1.12.1/jquery-ui.min.js"&amp;gt;&amp;lt;/script&amp;gt;
    &amp;lt;style&amp;gt;
        body {
            font-family: Arial, sans-serif;
            background-color: #f4f4f4;
        }
        .kanban-board {
            display: flex;
            justify-content: space-between;
            padding: 20px;
            gap: 10px;
        }
        .kanban-column {
            width: 16%;
            min-height: 400px;
            background: #ddd;
            padding: 10px;
            border-radius: 5px;
            display: flex;
            flex-direction: column;
            align-items: center;
        }
        .kanban-card {
            background: white;
            padding: 10px;
            margin-bottom: 10px;
            border-radius: 5px;
            box-shadow: 2px 2px 5px rgba(0, 0, 0, 0.1);
            cursor: grab;
            width: 90%;
            text-align: center;
            transition: background-color 0.3s;
            position: relative;
        }
        .kanban-card img {
            position: absolute;
            top: 5px;
            left: 5px;
            width: 20px;
            height: 20px;
        }
        .sortable {
            width: 100%;
            min-height: 50px;
        }
        .sortable-placeholder {
            background: #ccc;
            height: 50px;
            border-radius: 5px;
            margin-bottom: 10px;
            width: 90%;
        }
    &amp;lt;/style&amp;gt;
&amp;lt;/head&amp;gt;
&amp;lt;body&amp;gt;

&amp;lt;h2 style="text-align: center;"&amp;gt;Aufgaben Board&amp;lt;/h2&amp;gt;

&amp;lt;div class="kanban-board"&amp;gt;
    &amp;lt;?php foreach ($columns as $column): ?&amp;gt;
        &amp;lt;div class="kanban-column" id="&amp;lt;?= htmlspecialchars($column) ?&amp;gt;"&amp;gt;
            &amp;lt;h3&amp;gt;&amp;lt;?= htmlspecialchars($column) ?&amp;gt;&amp;lt;/h3&amp;gt;
            &amp;lt;div class="sortable" id="sortable-&amp;lt;?= htmlspecialchars($column) ?&amp;gt;"&amp;gt;
                &amp;lt;?php if (!empty($tasks_by_status[$column])): ?&amp;gt;
                    &amp;lt;?php foreach ($tasks_by_status[$column] as $task): ?&amp;gt;
                        &amp;lt;div class="kanban-card"
                            id="&amp;lt;?= htmlspecialchars($task['id']) ?&amp;gt;"
                            data-task-id="&amp;lt;?= htmlspecialchars($task['id']) ?&amp;gt;"
                            data-status="&amp;lt;?= htmlspecialchars($task['status']) ?&amp;gt;"
                            style="background-color: &amp;lt;?= $status_colors[$column] ?? '#ffffff' ?&amp;gt;;"&amp;gt;

                            &amp;lt;?php if (!empty($task['image'])): ?&amp;gt;
                                &amp;lt;img src="&amp;lt;?= $task['image'] ?&amp;gt;" alt="Symbol"&amp;gt;
                            &amp;lt;?php endif; ?&amp;gt;

                            &amp;lt;strong&amp;gt;Auftrag:&amp;lt;/strong&amp;gt; &amp;lt;?= htmlspecialchars($task["project"]) ?&amp;gt;&amp;lt;br&amp;gt;
                            &amp;lt;strong&amp;gt;Anlage:&amp;lt;/strong&amp;gt; &amp;lt;?= htmlspecialchars($task["anlage"]) ?&amp;gt;&amp;lt;br&amp;gt;
                            &amp;lt;strong&amp;gt;Mitarbeiter:&amp;lt;/strong&amp;gt; &amp;lt;?= htmlspecialchars($task["staff"]) ?&amp;gt;
                        &amp;lt;/div&amp;gt;
                    &amp;lt;?php endforeach; ?&amp;gt;
                &amp;lt;?php endif; ?&amp;gt;
            &amp;lt;/div&amp;gt;
        &amp;lt;/div&amp;gt;
    &amp;lt;?php endforeach; ?&amp;gt;
&amp;lt;/div&amp;gt;

&amp;lt;script&amp;gt;
    // Status und zugehörige Farben
    var statusColors = {
        "Material bestellt": "#FFD700", // Gelb
        "In Planung": "#FFA500", // Orange
        "Verplant": "#FF4500", // Dunkelorange
        "Ausgeführt": "#008000", // Grün
        "Angebot folgt": "#FF0000", // Rot
        "Erledigt": "#808080" // Grau
    };

    $(document).ready(function() {
        console.log(" jQuery und jQuery-UI wurden geladen.");

        $(".sortable").sortable({
            connectWith: ".sortable",
            placeholder: "sortable-placeholder",
            revert: true,
            start: function(event, ui) {
                console.log("Drag gestartet für:", ui.item.attr("id"));
            },
            stop: function(event, ui) {
                var newStatus = ui.item.closest(".kanban-column").attr("id");
                var taskId = ui.item.attr("id");

                console.log("Aufgabe verschoben:", taskId, "Neuer Status:", newStatus);

                // Neue Farbe setzen
                if (statusColors[newStatus]) {
                    ui.item.css("background-color", statusColors[newStatus]);
                }

                // FileMaker-Skript über fmp:// URL aufrufen
                var fileMakerScriptURL = "fmp://$/Deine_Datei?script=UpdateTaskStatus&amp;amp;param=" + encodeURIComponent(taskId + "|" + newStatus);
                console.log(" FileMaker-Skript aufrufen:", fileMakerScriptURL);

                // WebViewer öffnet FileMaker-Skript
                window.location = fileMakerScriptURL;
            }
        }).disableSelection();
    });
&amp;lt;/script&amp;gt;

&amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
Die Updates der Änderungen werden über ein kleines weiteres PHP Script abgefangen.

&amp;lt;?php
// POST-Daten empfangen
$taskId = $_POST['id'] ?? null;
$newStatus = $_POST['status'] ?? null;

if ($taskId &amp;amp;&amp;amp; $newStatus) {
    // Hier würdest du die Daten in FileMaker oder einer Datenbank speichern
    file_put_contents("kanban_log.txt", "ID: $taskId -&amp;gt; $newStatus\n", FILE_APPEND);
    echo "OK";
} else {
    echo "Fehler: Ungültige Daten";
}
?&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Führe ich nun eine Änderung durch, wird das FileMaker-Script -UpdateTaskStatus- aufgerufen, es übergibt als Parameter die Task-ID und den neuen Status. Damit habe ich natürlich die Möglichkeit im Hintergrund sofort die Änderungen zu verarbeiten. Also, neues Fenster, 1x1 px, bei –30000px. Dort suche ich die Task-ID und ändere den Status. Fenster schließen und der Vorgang ist abgeschlossen. Das war es. Keine komplizierte Hinterlegung aller Felder, versteckt im FileMaker-Layout. Sonst unumgänglich wenn die FM-Impementierte Kanban-Ansicht verwendet wird.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>ZUGFeRD mit horstoeko/zugferd und FileMaker</title>
      <dc:creator>Ronny Schrader</dc:creator>
      <pubDate>Sat, 10 May 2025 17:23:54 +0000</pubDate>
      <link>https://dev.to/filemakerexperts/zugferd-mit-horstoekozugferd-und-filemaker-c91</link>
      <guid>https://dev.to/filemakerexperts/zugferd-mit-horstoekozugferd-und-filemaker-c91</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1q8pbjhiaaq1c7nzjhm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fz1q8pbjhiaaq1c7nzjhm.png" alt="Image description" width="800" height="704"&gt;&lt;/a&gt;Nach unzähligen Versuchen mit TCPDF, FPDI und verschiedensten Merge-Strategien, habe ich mich letztlich für einen pragmatischeren Weg entschieden: Ich lasse sowohl das PDF als auch das XML direkt auf dem Server erzeugen – ohne nachträglichen Merge. Die Lösung basiert auf dem PHP-Paket horstoeko/zugferd, welches sich nach einigen Stolpersteinen als zuverlässig herausgestellt hat – sobald man seine Eigenheiten akzeptiert.&lt;/p&gt;

&lt;p&gt;Im ersten Szenario habe ich die fertige PDF aus FileMaker auf den Server, dort dann die Daten als POST-Parameter empfangen. Dann versucht die beiden Dateien, XML und PDF zu verschmelzen. Keine Change, bin fast verzweifelt, kenne die Doku zu Horstoeko/Zugferd aus dem FF. Aber es hat nicht geklappt. Also jetzt der pragmatische Ansatz. Die Daten werden übertragen und dann wird auf dem Server alles erzeugt.&lt;/p&gt;

&lt;p&gt;Der FileMaker-Teil übergibt die notwendigen Daten per POST an ein PHP-Skript auf dem Server. Dieses Skript generiert daraus die ZUGFeRD-konforme XML, erzeugt gleichzeitig ein einfaches PDF mit den wichtigsten Rechnungsdaten (z. B. Rechnungstitel, Nummer etc.) – und bindet die XML direkt beim Erzeugen ein. Kein nachträgliches Anhängen mehr nötig. Kein Merge-Objekt, kein Zwischenschritt. Die erzeugte Datei ist PDF/A–3B und enthält die eingebettete Rechnung als XML. Dabei wird die PDF gleich im PHP etwas angepasst. Vermutlich wird das ganze noch etwas schicker mit css Implementierung, aber für den Anfang reicht es so.&lt;/p&gt;

&lt;p&gt;Hier ist der komplette PHP-Code, den ich aktuell produktiv im Testsystem einsetze:&lt;/p&gt;

&lt;p&gt;$_POST['sellerName'] ?? '',&lt;br&gt;
        'street' =&amp;gt; $_POST['sellerStreet'] ?? '',&lt;br&gt;
        'zip' =&amp;gt; $_POST['sellerPostalCode'] ?? '',&lt;br&gt;
        'city' =&amp;gt; $_POST['sellerCity'] ?? '',&lt;br&gt;
        'country' =&amp;gt; $_POST['sellerCountryCode'] ?? 'DE',&lt;br&gt;
        'tax_id' =&amp;gt; $_POST['sellerTaxID'] ?? ''&lt;br&gt;
    ];&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Rechnungsempfänger-Daten
$buyer = [
    'name' =&amp;gt; $_POST['buyerName'] ?? '',
    'street' =&amp;gt; $_POST['buyerStreet'] ?? '',
    'zip' =&amp;gt; $_POST['buyerPostalCode'] ?? '',
    'city' =&amp;gt; $_POST['buyerCity'] ?? '',
    'country' =&amp;gt; $_POST['buyerCountryCode'] ?? 'DE',
    'tax_id' =&amp;gt; $_POST['buyerTaxID'] ?? ''
];

// Zahlungsinformationen
$payment = [
    'means_code' =&amp;gt; $_POST['paymentMeansCode'] ?? '',
    'financial_institution' =&amp;gt; $_POST['payeeFinancialInstitution'] ?? '',
    'iban' =&amp;gt; $_POST['payeeIBAN'] ?? '',
    'bic' =&amp;gt; $_POST['payeeBIC'] ?? '',
    'reference' =&amp;gt; $_POST['paymentReference'] ?? ''
];

// Steuerinformationen
$tax = [
    'rate' =&amp;gt; floatval(str_replace(',', '.', $_POST['taxRate'] ?? '19')),
    'amount' =&amp;gt; floatval(str_replace(',', '.', $_POST['taxAmount'] ?? '0')),
    'taxable_amount' =&amp;gt; floatval(str_replace(',', '.', $_POST['taxableAmount'] ?? '0')),
    'category_code' =&amp;gt; $_POST['taxCategoryCode'] ?? 'S'
];

// Positionen aus lineItemsRaw extrahieren
$positions = [];
$lineItems = explode('|', $_POST['lineItemsRaw'] ?? '');

foreach ($lineItems as $lineItem) {
    if (empty($lineItem)) continue;

    $parts = explode(';', $lineItem);
    if (count($parts) &amp;gt;= 7) {
        $quantity = floatval($parts[3]);
        $netPrice = floatval(str_replace(',', '.', $parts[5]));
        $total = $quantity * $netPrice;
        $orderDate = isset($parts[8]) ? date('d.m.Y', strtotime($parts[8])) : '';

        $positions[] = [
            'position' =&amp;gt; $parts[0],
            'description' =&amp;gt; $parts[1],
            'article_number' =&amp;gt; $parts[2],
            'quantity' =&amp;gt; $quantity,
            'unit' =&amp;gt; $parts[4],
            'net_price' =&amp;gt; $netPrice,
            'tax_rate' =&amp;gt; floatval($parts[6]),
            'total' =&amp;gt; $total,
            'order_date' =&amp;gt; $orderDate
        ];
    }
}

// Summen neu berechnen
$totalNet = 0;
$totalTax = 0;

foreach ($positions as $position) {
    $totalNet += $position['total'];
    $totalTax += $position['total'] * ($position['tax_rate'] / 100);
}

$totals = [
    'net' =&amp;gt; $totalNet,
    'tax' =&amp;gt; $totalTax,
    'gross' =&amp;gt; $totalNet + $totalTax
];

// Pfade definieren
$pdfPath = $uploadDir . $invoiceNumber . '.pdf';
$xmlPath = $uploadDir . 'inv_' . $invoiceNumber . '.xml';
$outputPath = $uploadDir . $invoiceNumber . '_ZUGFeRD.pdf';

// PDF-Datei überprüfen
if (!checkFile($pdfPath, "PDF-Datei")) {
    throw new Exception("PDF-Datei nicht verfügbar");
}

// ZUGFeRD-Dokument erstellen
logMessage("Erstelle ZUGFeRD-Dokument...");
$document = ZugferdDocumentBuilder::createNew(ZugferdProfiles::PROFILE_BASIC);
logMessage("DocumentBuilder initialisiert");

// Basisinformationen setzen
logMessage("Setze Basisinformationen...");
$document-&amp;gt;setDocumentInformation(
    $invoiceTypeCode,                    // Dokumenttyp (380 = Rechnung)
    $invoiceNumber,                    // Rechnungsnummer
    new DateTime($invoiceDate),        // Rechnungsdatum
    $currency                          // Währung
);
logMessage("Basisinformationen gesetzt");

// Rechnungssteller setzen
logMessage("Setze Rechnungssteller...");
$document-&amp;gt;setDocumentSeller(
    $seller['name'],                   // Name
    $seller['zip'],                    // PLZ
    $seller['city'],                   // Stadt
    $seller['street'],                 // Straße
    $seller['country']                 // Land
);
logMessage("Rechnungssteller gesetzt");

// Rechnungsempfänger setzen
logMessage("Setze Rechnungsempfänger...");
$document-&amp;gt;setDocumentBuyer(
    $buyer['name'],                    // Name
    $buyer['zip'],                     // PLZ
    $buyer['city'],                    // Stadt
    $buyer['street'],                  // Straße
    $buyer['country']                  // Land
);
logMessage("Rechnungsempfänger gesetzt");

// Positionen hinzufügen
logMessage("Füge Positionen hinzu...");
foreach ($positions as $index =&amp;gt; $position) {
    $document-&amp;gt;addNewPosition((string)($index + 1));
    $document-&amp;gt;setDocumentPositionProductDetails($position['description']);
    $document-&amp;gt;setDocumentPositionNetPrice($position['net_price']);
    $document-&amp;gt;setDocumentPositionQuantity($position['quantity'], $position['unit']);
    $document-&amp;gt;addDocumentPositionTax("S", "VAT", $position['tax_rate']);
    $document-&amp;gt;setDocumentPositionLineSummation($position['total']);
    logMessage("Position {$position['description']} hinzugefügt");
}

// PDF mit Rechnungsdaten erstellen
logMessage("Erstelle PDF mit Rechnungsdaten...");
try {
    $pdf = new FPDF();
    $pdf-&amp;gt;AddPage();
    $pdf-&amp;gt;SetAutoPageBreak(true, 20);

    // Schriftarten definieren
    $pdf-&amp;gt;SetFont('Arial', '', 10);

    // Absenderadresse
    $pdf-&amp;gt;SetXY(20, 20);
    $pdf-&amp;gt;Cell(0, 5, $seller['name'], 0, 1);
    $pdf-&amp;gt;Cell(0, 5, 'GmbH &amp;amp; Co. KG', 0, 1);
    $pdf-&amp;gt;Cell(0, 5, $seller['street'], 0, 1);
    $pdf-&amp;gt;Cell(0, 5, 'D-' . $seller['zip'] . ' ' . $seller['city'], 0, 1);

    // Rechnungsinformationen rechts oben
    $pdf-&amp;gt;SetXY(120, 20);
    $pdf-&amp;gt;SetFont('Arial', '', 9);

    // Rechte Spalte mit Informationen
    $rightColumnData = [
        ['Nummer', $invoiceNumber],
        ['Datum', date('d.m.Y', strtotime($invoiceDate))],
        ['Kunden Nr.', $_POST['customerNumber'] ?? '16105'],
        ['Lieferschein', $_POST['deliveryNote'] ?? ''],
        ['Lief. Datum', date('d.m.Y', strtotime($_POST['deliveryDate'] ?? $invoiceDate))]
    ];

    foreach ($rightColumnData as $row) {
        $pdf-&amp;gt;SetX(120);
        $pdf-&amp;gt;Cell(30, 5, $row[0], 0, 0);
        $pdf-&amp;gt;Cell(40, 5, $row[1], 0, 1);
    }

    // Überschrift "Rechnung"
    $pdf-&amp;gt;SetFont('Arial', '', 14);
    $pdf-&amp;gt;SetXY(20, 70);
    $pdf-&amp;gt;Cell(0, 10, 'Rechnung', 0, 1);

    // Positionen Header
    $pdf-&amp;gt;SetFont('Arial', '', 8);
    $pdf-&amp;gt;SetFillColor(240, 240, 240);
    $pdf-&amp;gt;SetY($pdf-&amp;gt;GetY() + 5);

    // Spaltenbreiten
    $colWidths = [
        'auftrag' =&amp;gt; 25,
        'bestellung' =&amp;gt; 35,
        'kommission' =&amp;gt; 35,
        'artikel' =&amp;gt; 25,
        'bezeichnung' =&amp;gt; 60,
        'menge' =&amp;gt; 20,
        'preis' =&amp;gt; 25,
        'gesamt' =&amp;gt; 25
    ];

    // Positionen ausgeben
    foreach ($positions as $index =&amp;gt; $position) {
        // Grauer Balken für Auftragskopf
        $pdf-&amp;gt;SetFillColor(240, 240, 240);
        $y = $pdf-&amp;gt;GetY();

        // Auftragskopf mit Datum
        $pdf-&amp;gt;Cell($colWidths['auftrag'], 5, 'Auftrag: ' . $position['article_number'] . ' / ' . $position['order_date'], 0, 0, 'L', true);
        $pdf-&amp;gt;Cell($colWidths['bestellung'], 5, 'Ihre Bestellung: ' . $_POST['orderNumber'], 0, 0, 'L', true);
        $pdf-&amp;gt;Cell($colWidths['kommission'], 5, 'Ihre Kommission:', 0, 1, 'L', true);

        // Positionsdetails
        $pdf-&amp;gt;SetFont('Arial', '', 8);
        $pdf-&amp;gt;Cell($colWidths['artikel'], 5, $position['article_number'], 0, 0);
        $pdf-&amp;gt;Cell($colWidths['bezeichnung'], 5, $position['description'], 0, 0);
        $pdf-&amp;gt;Cell($colWidths['menge'], 5, $position['quantity'] . ' ' . $position['unit'], 0, 0, 'R');
        $pdf-&amp;gt;Cell($colWidths['preis'], 5, number_format($position['net_price'], 2, ',', '.') . ' €', 0, 0, 'R');
        $pdf-&amp;gt;Cell($colWidths['gesamt'], 5, number_format($position['total'], 2, ',', '.') . ' €', 0, 1, 'R');

        $pdf-&amp;gt;Ln(2);
    }

    // Summen am Ende
    $pdf-&amp;gt;SetY(-60);
    $pdf-&amp;gt;SetFont('Arial', '', 9);

    // Zahlungsbedingungen
    $pdf-&amp;gt;Cell(0, 5, 'Rechnungsbetrag zahlbar bis ' . date('d.m.Y', strtotime($dueDate)), 0, 1);
    $pdf-&amp;gt;Cell(0, 5, 'Bei Zahlung bis ' . date('d.m.Y', strtotime($dueDate)) . ' Skonto 3 %', 0, 1);

    // Summen rechtsbündig
    $pdf-&amp;gt;SetX(-80);
    $pdf-&amp;gt;Cell(30, 5, 'Summe netto', 0, 0, 'R');
    $pdf-&amp;gt;Cell(50, 5, number_format($totals['net'], 2, ',', '.') . ' €', 0, 1, 'R');

    $pdf-&amp;gt;SetX(-80);
    $pdf-&amp;gt;Cell(30, 5, 'USt. ' . number_format($tax['rate'], 0) . ' %', 0, 0, 'R');
    $pdf-&amp;gt;Cell(50, 5, number_format($totals['tax'], 2, ',', '.') . ' €', 0, 1, 'R');

    $pdf-&amp;gt;SetX(-80);
    $pdf-&amp;gt;SetFont('Arial', 'B', 9);
    $pdf-&amp;gt;Cell(30, 5, 'Rechnungsbetrag', 0, 0, 'R');
    $pdf-&amp;gt;Cell(50, 5, number_format($totals['gross'], 2, ',', '.') . ' €', 0, 1, 'R');

    // Bestellnummer
    $pdf-&amp;gt;SetFont('Arial', '', 9);
    $pdf-&amp;gt;SetY(-30);
    $pdf-&amp;gt;Cell(0, 5, 'Zu Kommission:        Bestell-Nr.: ' . ($_POST['orderReference'] ?? ''), 0, 1);

    // Horizontale Linie am Ende
    $pdf-&amp;gt;SetY(-20);
    $pdf-&amp;gt;Line(20, $pdf-&amp;gt;GetY(), 190, $pdf-&amp;gt;GetY());

    $pdf-&amp;gt;Output('F', $outputPath);
    logMessage("PDF erfolgreich generiert: $outputPath");

    if (file_exists($outputPath)) {
        logMessage("ZUGFeRD-PDF erfolgreich erstellt: " . basename($outputPath));
        logMessage("Dateigröße: " . filesize($outputPath) . " Bytes");
    } else {
        throw new Exception("ZUGFeRD-PDF wurde nicht erstellt!");
    }
} catch (Exception $e) {
    logMessage(" Fehler beim PDF-Generieren: " . $e-&amp;gt;getMessage());
    logMessage(" Stack Trace:\n" . $e-&amp;gt;getTraceAsString());
    throw $e;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;} catch (Exception $e) {&lt;br&gt;
    logMessage(" Fehler beim Erstellen der ZUGFeRD-PDF: " . $e-&amp;gt;getMessage());&lt;br&gt;
    logMessage(" Stack Trace:\n" . $e-&amp;gt;getTraceAsString());&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Debug-Informationen
logMessage("\nDebug-Informationen:");
logMessage("PHP Version: " . PHP_VERSION);
logMessage("Memory Limit: " . ini_get('memory_limit'));
logMessage("Max Execution Time: " . ini_get('max_execution_time'));
logMessage("Upload Directory Permissions: " . substr(sprintf('%o', fileperms($uploadDir)), -4));

// XML-Datei prüfen
if (file_exists($xmlPath)) {
    logMessage("XML-Datei existiert: " . filesize($xmlPath) . " Bytes");
    logMessage("XML-Inhalt (erste 200 Zeichen):\n" . substr(file_get_contents($xmlPath), 0, 200));
} else {
    logMessage("XML-Datei existiert nicht");
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;}&lt;br&gt;
Natürlich ist das so erst der Anfang, so fehlt die Überprüfung des fertigen PDF-A, aber bei händischen Tests, wurde alles akzeptiert.&lt;/p&gt;

&lt;p&gt;Die Vorgehensweise aus FileMaker heraus ist klar. Daten sammeln und als POST übertragen. Der POST schaut in etwa so aus. Aus URL einfügen:&lt;/p&gt;

&lt;p&gt;“-X POST ” &amp;amp; “–header "Content-Type: application/x-www-form-urlencoded" ” &amp;amp; “–data ” &amp;amp; Zitat ( “invoiceNumber=” &amp;amp; $invoiceNumber &amp;amp; “&amp;amp;invoiceDate=” &amp;amp; $invoiceDate &amp;amp; “&amp;amp;invoiceCurrencyCode=” &amp;amp; $invoiceCurrencyCode &amp;amp; “&amp;amp;invoiceTypeCode=” &amp;amp; $invoiceTypeCode &amp;amp; “&amp;amp;dueDate=” &amp;amp; $dueDate &amp;amp; “&amp;amp;paymentTerms=” &amp;amp; $paymentTerms &amp;amp; “&amp;amp;deliveryTerms=” &amp;amp; $deliveryTerms &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;sellerName=” &amp;amp; $sellerName &amp;amp; “&amp;amp;sellerStreet=” &amp;amp; $sellerStreet &amp;amp; “&amp;amp;sellerPostalCode=” &amp;amp; $sellerPostalCode &amp;amp; “&amp;amp;sellerCity=” &amp;amp; $sellerCity &amp;amp; “&amp;amp;sellerCountryCode=” &amp;amp; $sellerCountryCode &amp;amp; “&amp;amp;sellerTaxID=” &amp;amp; $sellerTaxID &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;buyerName=” &amp;amp; $buyerName &amp;amp; “&amp;amp;buyerStreet=” &amp;amp; $buyerStreet &amp;amp; “&amp;amp;buyerPostalCode=” &amp;amp; $buyerPostalCode &amp;amp; “&amp;amp;buyerCity=” &amp;amp; $buyerCity &amp;amp; “&amp;amp;buyerCountryCode=” &amp;amp; $buyerCountryCode &amp;amp; “&amp;amp;buyerTaxID=” &amp;amp; $buyerTaxID &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;paymentMeansCode=” &amp;amp; $paymentMeansCode &amp;amp; “&amp;amp;payeeFinancialInstitution=” &amp;amp; $payeeFinancialInstitution &amp;amp; “&amp;amp;payeeIBAN=” &amp;amp; $payeeIBAN &amp;amp; “&amp;amp;payeeBIC=” &amp;amp; $payeeBIC &amp;amp; “&amp;amp;paymentReference=” &amp;amp; $paymentReference &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;taxRate=” &amp;amp; $taxRate &amp;amp; “&amp;amp;taxAmount=” &amp;amp; $taxAmount &amp;amp; “&amp;amp;taxableAmount=” &amp;amp; $taxableAmount &amp;amp; “&amp;amp;taxCategoryCode=” &amp;amp; $taxCategoryCode &amp;amp;&lt;/p&gt;

&lt;p&gt;“&amp;amp;totalNetAmount=” &amp;amp; $totalNetAmount &amp;amp; “&amp;amp;totalTaxAmount=” &amp;amp; $totalTaxAmount &amp;amp; “&amp;amp;totalGrossAmount=” &amp;amp; $totalGrossAmount &amp;amp; “&amp;amp;lineItemsRaw=” &amp;amp; $lineItemsRaw&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjssb0qzorp3q4hjivhkk.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjssb0qzorp3q4hjivhkk.png" alt="Image description" width="507" height="574"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>filemaker</category>
      <category>zugferd</category>
    </item>
  </channel>
</rss>
