# Changelog ## 1.5.0 — 2026-08-21 ### Anhänge-Spalte: Miniaturen statt Dateinamen - Die linke Spalte im Editor ist jetzt ein **Kachelraster mit Vorschaubildern**. Bei Fotos mit Namen wie `IMG_20260812_141233_HDR.jpg` war in der 260 px schmalen Spalte vorher nicht zu erkennen, welches Bild gemeint ist. - Neuer Endpoint **`ajax/attachment_thumb.php`**: erzeugt Miniaturen serverseitig mit GD, legt sie unter `bericht/thumbs/` ab und liefert sie mit ETag aus. Die Originale (Handyfotos mit mehreren MB) werden für die Spalte nicht mehr geladen. EXIF-Ausrichtung wird berücksichtigt — mit eigenem Mini-Parser, weil das `exif`-Modul im Prod-Container nicht geladen ist. - **PDF-Anhänge** bekommen Seite 1 als Miniatur (PDF.js, erst beim Scrollen gerendert). - **Klick auf eine Kachel öffnet die Großansicht** (`✕`/Esc schließt): Bilder mit Zoom, Pan und Wischen, PDFs im PDF-Viewer des Browsers. - Auswahl per Kachel-Checkbox, die **Reihenfolge ist sichtbar (① ② ③)** und bestimmt jetzt auch die Platzverteilung im Raster-Layout (vorher die Reihenfolge im Dokument). - Bereits verwendete Anhänge sind mit ✓ markiert, „alle"-Umschalter je Herkunft, Auswahlzähler, Umschalter zwischen Kachel- und Listenansicht (wird gemerkt). ### Arbeitstisch zeigt die echte Seite - Der Editor stellt jetzt **Blatt mit Kopfbereich, Seitenrändern und Fußbereich** dar; die Arbeitsfläche ist exakt der Bereich, der gedruckt wird. - Neue gemeinsame Geometrie-Quelle `bericht_page_geometry()` — Editor und PDF-Erzeugung rechnen mit denselben Werten (Rand 10 mm, Kopf 30 mm, Fuß 16 mm, Notizzone 25 %). - **Das PDF setzt die Arbeitsfläche 1:1 ein.** Vorher wurde das komplette Blatt noch einmal in den Satzspiegel eingepasst, der Inhalt also ein zweites Mal verkleinert — daher der breite Rand und der Unterschied zur Vorschau. - Alle Layout-Zweige (Einzelbild, Raster, Vorher/Nachher, Notiz) nutzen dieselbe Geometrie. Seiten ohne gespeichertes Composite liefen vorher über den Kopfbereich mit Logo und Titel. - Umschalter „📐 Rand" blendet die Zonen aus. - Ältere Composites (Blatt-Proportion) werden erkannt und proportional eingepasst statt verzerrt. ### Bild einer Seite austauschen - Neu: **`ajax/replace_page_source.php`**. Über „🔄" auf der Seitenminiatur lässt sich das Bild einer bestehenden Seite ersetzen — Reihenfolge, Notiz und Anmerkungen bleiben erhalten. Bisher musste man die Seite löschen und neu anhängen, wodurch sie ans Ende rutschte. - Bei Raster-Seiten ist der einzelne Platz austauschbar. Das dafür nötige `set_slot_image.php` existierte bereits, hatte aber keine Bedienung. - Finalisierte Berichte werden serverseitig abgelehnt. ### Arbeiten ohne ständiges Neuladen - Hinzufügen, Löschen, Hochladen, Bildtausch und der QR-Upload luden bisher die **ganze Seite neu**. Der Editor sprang dabei auf Seite 1 zurück und der Zoom war weg — bei 20 Fotos hintereinander entsprechend zäh. Jetzt werden nur noch die betroffenen Listen aktualisiert. - Neuer Endpoint **`ajax/fragments.php`** liefert Seiten- und Anhängeliste als HTML. Gerendert wird über `bericht_print_page_list()` / `bericht_print_attachment_list()` — dieselben Funktionen, die auch die Karte aufbauen, damit beide Darstellungen nicht auseinanderlaufen. - Nach dem Finalisieren wird weiterhin neu geladen: der Bericht wechselt den Status. ### Automatisches Speichern - Änderungen werden **4 Sekunden nach der letzten Aktion automatisch gespeichert**, außerdem beim Seitenwechsel und beim Wegschalten des Browser-Tabs. Bisher gab es nur „💾 Entwurf speichern". - In der Fußleiste steht der Stand („● nicht gespeichert" / „✓ gespeichert"), beim Verlassen der Seite mit offenen Änderungen warnt der Browser. ### Sortieren und Filtern der Anhänge - Sortierung nach **Aufnahmezeit** (auf/ab), Name oder Größe — bei Baustellenfotos ist die zeitliche Reihenfolge meist genau die richtige. Filter: alle · nur Bilder · nur PDFs · noch nicht verwendet. - Die Aufnahmezeit steht unter der Kachel. Ausgelesen wird sie mit einem **eigenen EXIF-Parser**, weil das `exif`-Modul im Prod-Container nicht geladen ist; gegen das PHP-Modul geprüft, identische Werte. Ohne EXIF gilt das Dateidatum. ### Bedienung: Tastenkürzel, Spaltenbreite, Ziehen und Ablegen - **Tastenkürzel**: `V` Auswahl, `P` Zeichnen, `R` Rechteck, `K` Kreis, `A` Pfeil, `T` Text, `Entf` löscht die Auswahl, `Strg+Z`/`Strg+Y` Rückgängig/Wiederholen, `Strg+S` speichern, `↑`/`↓` blättert durch die Seiten. In Eingabefeldern und bei offenem Dialog greifen sie nicht; das Hintergrundbild lässt sich mit `Entf` nicht versehentlich entfernen. - **Anhänge-Spalte in der Breite ziehbar** (150–620 px, Doppelklick auf die Trennlinie setzt zurück) und über „«" einklappbar — die Arbeitsfläche wird dabei neu gezeichnet. - **Kachelgröße** in drei Stufen über „⤢". - **Ziehen und Ablegen**: Kachel auf die Seitenliste legt neue Seiten an, Kachel auf die Arbeitsfläche ersetzt das Bild der offenen Seite, Kachel auf einen Raster-Platz setzt genau diesen. Dateien vom Rechner auf die Anhänge-Spalte werden hochgeladen. ### Seiten: mehrere auf einmal, verdoppeln - Klick auf die **Seitennummer** markiert eine Seite, Umschalt erweitert bis dorthin. Über die Sammelleiste lassen sich markierte Seiten **verdoppeln** oder **löschen**. - Neuer Endpoint **`ajax/page_bulk.php`**. Kopien werden direkt hinter ihrem Original einsortiert, nach dem Löschen wird die Reihenfolge lückenlos neu nummeriert. Anmerkungen, Notiz und — bei Rastern — die Bilder in den Plätzen werden mitkopiert; das gespeicherte Seitenbild bewusst nicht, sonst zeigten Original und Kopie auf dieselbe Datei. ### Raster-Plätze direkt auf der Seite - Bei Raster-Layouts liegt über der Arbeitsfläche ein **Gitter mit einer Schaltfläche je Platz** (bei Vorher/Nachher entsprechend beschriftet). Ein Klick setzt das links ausgewählte Bild, ein abgelegtes Bild setzt genau diesen Platz. Bisher ließen sich Raster nur beim Anlegen befüllen — der Endpoint dafür existierte, hatte aber keine Bedienung. - Die Plätze reagieren nur im Auswahlmodus; sobald ein Zeichenwerkzeug aktiv ist, haben die Werkzeuge Vorrang. ### Behobene Fehler - `dol_dir_list()` wurde ohne `$mode = 1` aufgerufen — `size` und `date` blieben leer, in der Anhängeliste stand „ Bytes" ohne Zahl. - Der PDF.js-Worker-Pfad war ohne `/custom`-Präfix hartkodiert. PDF.js fiel auf den „fake worker" zurück und rendert seitdem wieder im Worker statt auf dem Hauptthread. - PDF-Anhänge wurden über `document.php` **ohne** `attachment=0` verlinkt und dadurch heruntergeladen statt angezeigt. - Die Größen-Auswahl einer Seite blieb leer: der DB-Wert `1` traf die Option `1.0` nicht. - Der Datei-Upload verarbeitete nur die erste Datei einer Mehrfachauswahl. - Eingabefelder im Editor nutzen jetzt `--inputbackgroundcolor`/`--inputbordercolor`. Die Modul-Doku hielt die Variablen für im AwlDark nicht vorhanden — sie sind gesetzt (KB #512). - **Schneller Seitenwechsel konnte Inhalte vertauschen**: zwei Durchläufe von `loadPage()` liefen ineinander — der zweite speicherte die noch nicht umgeschaltete Seite ein weiteres Mal, und beide zeichneten in denselben Canvas. Dabei konnte die Notiz der einen Seite bei der anderen landen. Seitenwechsel laufen jetzt nacheinander; ein überholter Durchlauf bricht ab. - Der Dialog beim Löschen eines Anhangs sagt jetzt, dass die Datei **endgültig aus den Dokumenten des Belegs** verschwindet, und warnt, wenn sie im Bericht in Gebrauch ist. ## 1.4.0 — 2026-08-15 ### Kein Stunden-Dummy mehr beim Direkt-Freigeben - **`api/orders.php?action=create`**: Mit `validate=true` wird der Auftrag jetzt **ohne jede Position** freigegeben. Bisher hängte das Backend vorher eine Std-Lohn-Zeile mit Menge 1 an (Kundenkarten-Default `options_stundenzettel_default_service`, sonst Fallback-Produkt). - **Warum:** Die Annahme, `Commande::valid()` scheitere an einem leeren Auftrag, ist falsch — weder `valid()` noch der Handler `confirm_validate` prüfen auf Zeilen, gesperrt ist nur die Button-Darstellung in `commande/card.php` (verifiziert gegen Dolibarr 22.0.2, KB #355/#989). Die Alibi-Zeile verfälschte `total_ht`, tauchte als offene Restmenge in der Lieferauflistung auf, stand im Kunden-PDF und ließ sich nach der Freigabe nicht mehr per `deleteLine()` entfernen. Die Leistungen kommen aus dem Stundenzettel. - Das Antwortfeld `added_line` entfällt (wurde von der Baustelle-PWA nie ausgewertet). - Die Konstante `BERICHT_ORDER_DEFAULT_SERVICE_REF` wird nicht mehr verwendet. - Gleiche Umstellung in der Stunden-App: `stundenzettel/ajax/pwa_api.php` (Aktion `validate_order`) fügt ebenfalls keine Fallback-Position mehr ein, und legt mit der neuen Aktion `create_order` selbst Aufträge an (Stundenzettel 2.17.0). ## 1.3.1 — 2026-07-18 ### Fixes rund um die PWA-Auftragsanlage - **`api/customers.php`**: Zahlart und Zahlungsbedingung im Kunden-Detail werden jetzt **übersetzt** — über die Dolibarr-Sprachkeys `PaymentTypeShort` / `PaymentConditionShort` (Sprachdatei `bills`) statt der rohen englischen `libelle`-Werte aus `c_paiement`/`c_payment_term`. Behebt die Anzeige „💳 Payment Condition · 🏦 Credit transfer" in der „Übernommen"-Zeile. Fallback auf `libelle`, wenn kein Sprachkey existiert (z. B. Custom-Einträge ohne `code`). - **Baustelle-PWA** (Repo `baustelle-pwa`): „Neuer Auftrag"-Formular saß vertikal zu weit unten (der gemeinsame `.fs-body` erbte `justify-content:center`, was bei `flex-direction:column` vertikal zentriert) und wurde von der Bildschirmtastatur verdeckt → `justify-content:flex-start` im Modal + `scrollIntoView({block:'center'})` beim Feld-Fokus. ## 1.3.0 — 2026-07-18 ### PWA Auftrag-Anlegen überarbeitet (Formular + Freigabe) **Formular (Baustelle-PWA, `openNewOrderModal`)** - Feld-Reihenfolge/Beschriftung angepasst: **„Ihr Zeichen"** (= `ref_client`) steht jetzt an **erster** Stelle und ist **Pflicht**; **„Auftragsbeschreibung"** (= Extrafield `options_auftragsbeschreibung`) an zweiter Stelle und optional. (Vorher genau umgekehrt: Titel Pflicht, Kunden-Referenz optional.) - Neues Feld **„Geplanter Liefertermin"** (`datetime-local`, Datum + Uhrzeit in einem Feld) → Auftrag-Spalte `date_livraison`. - Neue Checkbox **„Auftrag direkt freigeben"**. **Backend (`api/orders.php?action=create`)** - `ref_client` ist jetzt serverseitig **Pflicht** (Geschäftsregel — Dolibarr-Core verlangt nur den Kunden). - Neuer Parameter `date_livraison` (Unix-TS) → `Commande->delivery_date`. - Neuer Parameter `validate`: legt vor der Freigabe automatisch eine **Std-Lohn-Zeile** an und ruft dann `Commande::valid()`. - Produkt: Kundenkarten-Default `options_stundenzettel_default_service`, sonst Fallback-Produkt über Ref `BERICHT_ORDER_DEFAULT_SERVICE_REF` (Default `ELEK-STD-M`, „Stundenlohn Meister" 45 €). - Preis: kundenspezifischer Preis (`PRODUIT_CUSTOMER_PRICES`) hat Vorrang, sonst Produkt-Standardpreis. Menge 1. - Response erweitert um `ref_client`, `date_livraison`, `validated`, `added_line`, `validate_warning`. ## 1.2.0 — 2026-05-27 ### Phase 1.8 Lieferschein-Bestätigung in der PWA **Neuer element_type='shipment'** - Bericht-Modul unterstützt jetzt `element_type='shipment'` mit `fk_element = llx_expedition.rowid` - Tab "Bericht" erscheint auf der Lieferung-Card (Konstante `BERICHT_TAB_ON_SHIPMENT`) - `bericht_fetch_parent()` und `bericht_fetch_shipment_with_order()` laden Expedition + verknüpften Auftrag **Signatur auf Lieferschein-PDF stempeln** - Neue Tabelle `llx_bericht_signature_box`: Pro PDF-Template (merou/rouget/espadon/...) konfigurierbare Box-Geometrie (x/y/w/h in mm, Seite first/last, Label) - Konstante `BERICHT_SIGNATURE_BOX_DEFAULT` als JSON-Default (`{"page":"last","x_mm":120,"y_mm":230,"w_mm":70,"h_mm":35,"label":"Unterschrift Kunde"}`) - `bericht_stamp_signature_on_pdf()`: FPDI importiert Original-Lieferschein, TCPDF stempelt PNG + Name + Datum + GPS in die konfigurierte Box **Visueller Admin-Editor (`admin/signature_box_editor.php`)** - PDF.js rendert ein Beispiel-Lieferschein-PDF - Fabric.js-Rechteck zum Drag&Resize der Signatur-Box auf dem Canvas - Eingabefelder für X/Y/W/H/Label/Seite werden live synchronisiert - "Speichern" persistiert via AJAX (`ajax/save_signature_box.php`) in `llx_bericht_signature_box` (UPSERT auf `template_name`) - Reset-Button setzt zurück auf Default-Konstante **Neue PWA-Route (Baustelle-App)** - Order-Detail bekommt Button "🚚 Lieferungen" - `#/orders/:id/shipments` zeigt Liste aller verknüpften Expeditionen mit Status- und Signed-Badge - `#/shipments/:id` zeigt Lieferschein-PDF inline (PDF.js Canvas) + großen Button "Lieferung unterschreiben lassen" **Vollbild-Querformat-Signatur (`openShipmentSignatureModal`)** - `requestFullscreen()` + `screen.orientation.lock('landscape')` (Best-Effort) - 2-Spalten-Layout: links Lieferschein-Info + Namens-Input + GPS-Toggle, rechts Vollbild-Canvas - HiDPI-aware Canvas (`devicePixelRatio`), Pointer/Touch-Events mit quadratischer Glättung - Beim Bestätigen: PNG → `POST /api/shipments.php?action=confirm` → Backend stempelt in PDF und legt es in `documents/expedition//-signed.pdf` **Auto-Workflow auf der Expedition** - `signed_status=1` (via `CommonSignedObject::setSignedStatus`) - Wenn Expedition noch Draft (Status 0): erst `valid()`, dann `setClosed()` - Bericht-Record (`element_type='shipment'`) wird angelegt/aktualisiert mit `final_pdf_path` zum signierten PDF - `.meta.json` neben der PNG mit voller Audit-Spur (signer, user, GPS, IP, Box, Template, signed_at) **Neue API-Endpoints** - `GET /api/shipments.php?order_id=` — Liste der Lieferungen zu einem Auftrag - `GET /api/shipments.php?id=` — Detail einer Lieferung + Bericht-ID falls vorhanden - `GET /api/shipments.php?id=&action=pdf` — PDF-Stream (Original oder gestempelt) - `POST /api/shipments.php?id=&action=confirm` — Unterschrift einstempeln + Status-Workflow ## 1.1.0 — 2026-04-08 ### Phase 1 Bericht-Modul Erweiterungen **1.6 Verknüpfte Sicht Auftrag↔Rechnung** - Bericht-Übersicht zeigt drei Sektionen: direkt zugeordnet, zusätzlich verknüpft, aus verknüpften Aufträgen - "→ Übernehmen"-Button erstellt llx_element_element-Eintrag (n:m-Verknüpfung) - "Lösen"-Button entfernt Verknüpfung - Beim Finalisieren landet das PDF auch unter den verknüpften Elementen im ECM **1.1 Live-PDF-Vorschau** - 👁️ Vorschau-Button im Editor → Modal mit eingebettetem PDF - Neuer Endpoint `ajax/preview_pdf.php` (kein ECM-Insert, kein Status-Wechsel) - ESC oder Klick auf Backdrop schließt das Modal **1.2 Anhänge löschen** - 🗑️ Icon neben jedem Anhang in der linken Spalte - Confirm-Dialog mit Auftrags-/Rechnungs-Referenz - Path-Whitelist (nur facture/, commande/, propal/), Thumbs + ECM-Eintrag werden mitgelöscht **1.3 Seitengröße A4/A3/A5/Letter + Hoch/Quer** - Format und Orientation in der Bericht-Meta wählbar - Auto-Save bei Änderung - Bilder werden dynamisch auf die Seitengröße skaliert **1.4 + 1.5 Mehrere Bilder pro Seite (DB-Schema)** - Neue Tabelle `llx_bericht_page_image` für Multi-Image-Seiten - Spalten `layout`, `image_scale`, `image_align` in `llx_bericht_page` - Grid-Rendering im Editor + PDF folgt im nächsten Commit **1.7 Tab „Berichte" auf Kundenkarte** - Read-only Übersicht aller Berichte des Kunden - Flache Tabelle sortiert nach Datum - Springt zum Bericht oder zur Quelle (Auftrag/Rechnung/Angebot) - Konstante `BERICHT_TAB_ON_THIRDPARTY` zum Aktivieren ### Sonstiges - DB-Migrationen im `init()` für bestehende Installationen (ALTER TABLE mit Error-Suppress) - `bericht_burn_annotations` und `bericht_render_cover_internal` in `lib/bericht.lib.php` zentralisiert (gemeinsam von generate_pdf + preview_pdf genutzt) - Modal-CSS für Vorschau im Dolibarr Dark-Theme ## 1.0.0 — 2026-04-08 Initiales Release. - Modul-Scaffold mit Reiter „Bericht" auf Rechnungen, Aufträgen und Angeboten - CRUD für `Bericht` und `BerichtPage` (Tabellen `llx_bericht`, `llx_bericht_page`) - Browser-Editor mit PDF.js + Fabric.js: Pfeile, Kreise, Rechtecke, Freihand, Text, Undo/Redo - Anhänge-Browser zeigt eigene Anhänge + Anhänge verknüpfter Objekte - Datei-Upload direkt in den Bericht - Seiten-Verwaltung mit Drag&Drop (SortableJS), Löschen, Drehen - Notizen pro Seite (werden im PDF gedruckt) - Admin-Setup mit ODT-Template-Verwaltung (Upload, Löschen, Standard wählen) - Platzhalter-System für ODT-Templates (`{auftragsnummer}`, `{kunde_name}`, …) - PDF-Finalisierung mit TCPDF + FPDI, ODT→PDF Konvertierung des Deckblatts via LibreOffice - Annotationen werden beim Export ins PDF eingebrannt - Auftragsnummer wird automatisch aus dem vorhandenen Extrafield `options_auftragsnummer` geholt - Beim Aktivieren werden fehlende Extrafields auf `llx_facture_extrafields` angelegt, vorhandene NICHT überschrieben - Mehrere Berichte pro Dokument möglich - Forgejo-Workflow für Deploy nach Dolibarr (Tag `[deploy]`)