# ROADMAP — Modul Bericht Stand: 2026-08-22 · Version im Repo: 1.5.1 > Diese Datei war mit `eb37a4b` gelöscht worden, weil alle Punkte abgehakt waren. Sie bleibt > ab jetzt liegen — erledigte Punkte werden abgehakt, nicht entfernt. --- ## 0. API-Seite für die Baustelle-PWA ✅ 1.5.1 Die PWA (`data/baustelle-pwa`) spricht ausschließlich `api/`. Mit 1.5.0 war Infrastruktur entstanden, die dort nicht ankam — mit 1.5.1 nachgezogen. Gegenstück: `ROADMAP.md` im PWA-Repo, Abschnitt 1. - [x] **`api/photo.php`: `size=thumb&w=`** ✅ 1.5.1 — bis dahin lieferte der Endpoint nur ein Thumbnail, wenn Dolibarr schon eines unter `thumbs/_small.` erzeugt hat. Fotos aus der PWA kommen über `orders.php?action=upload_photo` herein, und dort läuft **kein `vignette()`** — es gibt also nie eins, und die PWA lädt für jede Kachel das volle Foto. `bericht_attachment_thumb()` (1.5.0, GD + Cache + EXIF-Rotation) macht genau das Richtige und ist über die bereits eingebundene `lib/bericht.lib.php` erreichbar. Dazu ETag aus der mtime, damit wiederholte Aufrufe mit 304 enden. - [x] **`api/orders.php?action=photos`: `taken_at`** ✅ 1.5.1 — sortiert wurde nach `filemtime`. Der Editor nutzt seit 1.5.0 die EXIF-Aufnahmezeit (`bericht_file_taken_at()`, eigener Parser weil im Prod-Container kein `exif`-Modul). Bei Uploads aus der Galerie ist die Upload-Zeit die falsche Reihenfolge. - [x] **`api/reports.php`: `title` im `pages`-Array** ✅ 1.5.1 — `api/pages.php` konnte den Seitentitel setzen und die PWA rendert ihn, aber das Detail gab ihn nicht aus. Der Titel wirkte in der PWA deshalb, als würde er nicht gespeichert. - [x] **`api/reports.php`: `composite_path` im `pages`-Array** ✅ 1.5.1 — die PWA zeigte `source_path`, also das Rohbild ohne Anmerkungen. Bei Raster-Layouts (`grid_2` … `grid_6`, `before_after`) ist das nur eines von bis zu sechs Bildern, bei `title_only` gar keins (die PWA zeigte dann eine ❌-Kachel). - [x] **Neu: `api/note.php`** ✅ 1.5.1 — Textnotizen zum Auftrag (Liste, Lesen, Anlegen, Ändern, Löschen). Ablage wie bei der Sprachnotiz: `notiz__.txt` im Auftrags-Verzeichnis, damit die Notiz auch im Dolibarr-Auftrag und in der Anhänge-Spalte des Editors auftaucht. Der `file`-Parameter ist gegen ein Muster geprüft (Pfad-Ausbruch). Gegenstück in der PWA: siehe `ROADMAP.md` im Repo `baustelle-pwa`, Abschnitte 1 und 3 — dort ebenfalls erledigt. --- ## 1. Anhänge-Spalte (links im Editor) — Hauptbaustelle Ausgangslage (Eddy, 21.08.2026): *„die Dateinamen können in dem schmalen Fenster nicht ganz angezeigt werden, ich habe Schwierigkeiten die richtigen Bilder auszusuchen."* Die Spalte ist 260 px breit und zeigt pro Datei nur eine Zeile `🖼 langer_dateiname… 2,3 MB 🗑️`. Bei 20–40 Baustellenfotos mit Namen wie `IMG_20260812_141233_HDR.jpg` ist das unbrauchbar. ### 1.1 Miniatur-Vorschau statt Dateiname ✅ 1.5.0 - [x] Kachel-Raster (2 Spalten) statt Liste - [x] Server-Thumbnails für Bilder (GD, gecacht) — Original-Handyfotos werden **nicht** mehr voll geladen - [x] PDF-Anhänge: Seite 1 als Miniatur (PDF.js, clientseitig, lazy) - [x] Klick auf Miniatur → Lightbox (Zoom/Pan/Swipe), `✕` bzw. Esc schließt — PDFs im Vollbild-Viewer - [x] Dateiname zweizeilig unter der Kachel + voller Name als Tooltip ### 1.2 Auswahl bedienbar machen ✅ 1.5.0 - [x] Checkbox als Overlay auf der Kachel (großes Klickziel), Klick auf die Namenszeile wählt ebenfalls aus - [x] Auswahl-Reihenfolge sichtbar (① ② ③) — bestimmt die Slot-Reihenfolge im Grid-Layout (vorher: DOM-Reihenfolge, also nicht steuerbar) - [x] „Alle / Keine" pro Herkunfts-Gruppe + Zähler „3 ausgewählt" - [x] Bereits im Bericht verwendete Anhänge werden markiert (✓-Badge, ausgegraut) ### 1.3 Sortierung und Filter ✅ 1.5.0 - [x] Sortierung nach **Aufnahmezeit** (auf- und absteigend), Name, Größe — jeweils innerhalb der Herkunfts-Gruppe, damit die Zuordnung zum Beleg erhalten bleibt - [x] Filter: alle · nur Bilder · nur PDFs · noch nicht verwendet - [x] Aufnahmezeit steht unter der Kachel (`12.08. 11:48 · 56 Kb`) - [x] EXIF-Parser in PHP (`bericht_jpeg_taken_at`), weil das `exif`-Modul im Container `90-Dolibarr-Prod-Custom` **nicht geladen** ist. Gegen das PHP-Modul geprüft: identische Werte. Fallback auf das Dateidatum, wenn kein EXIF vorhanden ist. - [x] Auswahl von Sortierung/Filter/Ansicht wird gemerkt (localStorage) ### 1.4 Spaltenbreite, Kachelgröße, Ziehen und Ablegen ✅ 1.5.0 - [x] **Spaltenbreite ziehbar** — Trennlinie zwischen Anhängen und Arbeitsfläche (150–620 px), Doppelklick setzt auf 260 px zurück; Breite wird gemerkt - [x] **Ein-/ausklappbar** über „«" in der Kopfzeile — eingeklappt bleiben 26 px stehen - [x] **Kachelgröße** klein / mittel / groß über „⤢" (in der kleinsten Stufe entfällt die Größenangabe) - [x] **Kachel auf die Seitenliste ziehen** → neue Seite(n). Sind mehrere Kacheln angekreuzt und man zieht eine davon, kommen alle mit. - [x] **Kachel auf die Arbeitsfläche ziehen** → ersetzt das Bild der offenen Seite - [x] **Kachel auf einen Raster-Platz ziehen** → setzt genau diesen Platz - [x] **Dateien vom Rechner auf die Spalte ziehen** → Upload (nicht unterstützte Typen werden benannt und übersprungen) - [x] SortableJS bekam `group: {put: false}` — sonst streiten sich Sortierung und Ablegen um die gleichen Ereignisse --- ## 2. Seitenansicht ("Arbeitstisch") ✅ 1.5.0 Vorher zeigte der Editor ein A4-Blatt, auf dem das Foto formatfüllend lag. Beim PDF wurde dieses ganze Blatt **nochmal** in den Satzspiegel eingepasst — der Inhalt schrumpfte also ein zweites Mal, und die Vorschau entsprach nicht dem Ergebnis. Seiten, die nie im Editor gespeichert wurden, liefen zudem über den Kopfbereich (Logo/Titel) hinweg. - [x] Eine gemeinsame Geometrie-Quelle: `bericht_page_geometry($format, $orientation, $has_note)` in `lib/bericht.lib.php` — liefert Seitenmaß, Ränder (10 mm), Kopfbereich (30 mm), Fußbereich (16 mm), Notizzone und den Inhaltsbereich. Editor **und** PDF rechnen damit. - [x] Arbeitsfläche = Inhaltsbereich. Drumherum das Blatt mit schraffierter Kopf-/Fußzone (das, was TCPDF selbst druckt) und einer Notizzone, sobald die Seite eine Notiz hat. - [x] PDF setzt das Composite 1:1 in den Inhaltsbereich statt es erneut einzupassen - [x] Alle Layout-Zweige der PDF-Erzeugung (Einzelbild, Raster, Vorher/Nachher, Notiz) auf dieselbe Geometrie umgestellt — vorher rechneten sie mit `margin = 10` ab Blattkante und schoben Bilder unter den Kopfbereich - [x] Raster-Vorschau (`ajax/page_image.php`) rendert im Format des Inhaltsbereichs - [x] Umschalter „📐 Rand" in der Werkzeugleiste (Zustand wird gemerkt) - [x] Rückwärtskompatibel: Composites aus der Zeit davor haben Blatt-Proportion und werden an der Proportion erkannt und proportional eingepasst statt verzerrt --- ## 3. Bild einer Seite austauschen ✅ 1.5.0 - [x] `ajax/replace_page_source.php` — tauscht die Quelle einer Seite; Reihenfolge, Notiz und Anmerkungen bleiben erhalten (vorher: Seite löschen und hinten neu anhängen) - [x] Raster-Seiten: einzelner Platz austauschbar (nutzt das vorhandene `set_slot_image.php`, für das es bis dahin keine Bedienung gab) - [x] Bedienung: „🔄" auf der Seitenminiatur → Statusleiste am unteren Rand → links auf das Ersatzbild klicken. Esc bricht ab. Bei Rastern vorher den Platz (1…n) wählen. - [x] Finalisierte Berichte werden serverseitig abgewiesen --- ## 4. Editor / Arbeitsablauf - [x] **Kein `location.reload()` mehr** ✅ 1.5.0 — Hinzufügen, Löschen, Hochladen, Bildtausch und der QR-Upload aktualisieren nur noch die betroffenen Listen. 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 wie beim Seitenaufbau. Der Editor bleibt auf der bearbeiteten Seite, Zoom und Ansicht bleiben erhalten. Einzige Ausnahme: nach dem Finalisieren wird bewusst neu geladen, weil der Bericht den Status wechselt und die ganze Karte anders aussieht. - [x] **Autosave** ✅ 1.5.0 — 4 Sekunden nach der letzten Änderung wird gespeichert, ebenso beim Seitenwechsel und beim Wegschalten des Tabs. Zustand steht in der Fußleiste („● nicht gespeichert" / „✓ gespeichert"), beim Verlassen der Seite warnt der Browser. Wichtig: das Aufbauen einer Seite (und Zoomen) löst Fabric-Events aus, zählt aber nicht als Änderung — sonst stünde direkt nach dem Öffnen „nicht gespeichert" da. - [x] **Tastenkürzel** ✅ 1.5.0 — V/P/R/K/A/T für die Werkzeuge, Entf löscht die Auswahl (das Hintergrundbild ist ausgenommen), Strg+Z/Y, Strg+S, ↑/↓ blättert durch die Seiten. In Eingabefeldern und bei offenem Dialog oder offener Großansicht greifen sie nicht. Die Kürzel stehen in den Tooltips der Schaltflächen. - [x] **Seitenliste: Mehrfachauswahl** ✅ 1.5.0 — Klick auf die Seitennummer markiert, Umschalt erweitert bis dorthin. Sammelleiste mit Verdoppeln (⧉), Löschen (🗑️) und Aufheben (Esc). Neuer Endpoint `ajax/page_bulk.php`; Kopien landen direkt hinter ihrem Original (`bericht_reorder_with_copies()`), nach dem Löschen wird lückenlos neu nummeriert. `composite_path` wird bewusst **nicht** mitkopiert — sonst zeigten beide Seiten auf dieselbe Datei und das Bearbeiten der einen überschriebe die andere. - [x] **Raster-Plätze direkt anklicken** ✅ 1.5.0 — über der Arbeitsfläche liegt ein Gitter mit einer Schaltfläche je Platz (bei Vorher/Nachher entsprechend beschriftet). Klick setzt das links ausgewählte Bild, Ablegen per Drag setzt das gezogene. Die Aufteilung entspricht `BerichtPage::slotRectsInBox()`. Die Plätze reagieren nur im Auswahlmodus — beim Zeichnen haben die Werkzeuge Vorrang, sonst käme man auf Raster-Seiten nicht mehr zum Zeichnen. - [x] **Seitenwechsel serialisiert** ✅ 1.5.0 — beim schnellen Durchklicken überlappten sich zwei Durchläufe von `loadPage()`: der zweite speicherte die noch nicht umgeschaltete Seite ein zweites Mal, und beide rendern in denselben Canvas — so konnte die Notiz der einen Seite bei der anderen landen. Jeder Aufruf bekommt jetzt eine Nummer und bricht ab, sobald ein neuerer gestartet ist. ## 5. Kleinkram / Fixes - [x] **Mehrfach-Upload**: `bindExtraUpload` verarbeitete nur `inp.files[0]` — Mehrfachauswahl im Dateidialog war möglich, es kam aber nur eine Datei an ✅ 1.5.0 - [x] Upload-Fortschritt ✅ 1.5.0 — der Knopf zeigt beim Mehrfach-Upload „⏳ 3 / 8" - [x] `att-delete` löscht die Datei wirklich aus den Dokumenten des Belegs ✅ 1.5.0 — der Dialog sagt das jetzt deutlich und warnt zusätzlich, wenn die Datei im Bericht in Gebrauch ist. - [ ] **`BERICHT_TAB_ON_THIRDPARTY` ist seit 1.7.1 tot** (gefunden 2026-09-18 beim Doku-Abgleich): Der Descriptor legt die Konstante weiter an (Default `1`), aber seit der Reiter an der Kundenkarte über den Hook `completeTabsHead` kommt, fragt sie **niemand** mehr ab (`grep -rn TAB_ON_THIRDPARTY` findet nur Descriptor, README, CLAUDE.md, ChangeLog). Der Reiter lässt sich also nicht mehr abschalten. Entweder im Hook auswerten (`getDolGlobalInt('BERICHT_TAB_ON_THIRDPARTY', 1)`) oder Konstante samt Admin-Schalter entfernen. - [ ] **Doku-Drift im Modul** (gefunden 2026-09-18, zentrale Spec `dolibarr-docs/module/bericht.md` ist bereits auf 1.7.1 nachgezogen): `CLAUDE.md` trägt noch „Stand 2026-08-22 (Version 1.5.1)" und nennt als PWA-Hosting `awl.data-it-solution.de/baustelle/` — tatsächlich `/custom/baustelle/` (Manifest `start_url`, Prod-Zugriffslog). `README.md` nennt für `BERICHT_TAB_ON_THIRDPARTY` den Default `0`, der Descriptor setzt `1`. 1.6.0 (md5-Abgleich), 1.7.0 (Fotoqualität, `api/config.php`) und 1.7.1 (Hook-Reiter) fehlen in `CLAUDE.md` ganz. → eigener `/docs`-Lauf im Bericht-Repo. --- ## Erledigt ### 1.5.1 — 2026-08-22 API-Seite zur PWA nachgezogen (Abschnitt 0): Thumbnail-Endpoint `size=thumb` statt Ausliefern der Originaldateien (9,8 KB statt 235 KB je Kachel, mit ETag/304), EXIF-Aufnahmezeit in der Anhängeliste, `title` und `composite_path` in der Bericht-Detailantwort, neuer Endpoint `api/note.php` für Textnotizen. ### 1.5.0 — 2026-08-21 Vollständig abgearbeitet. Anhänge-Kachelraster mit Miniaturen (1.1–1.4), Server-Thumbnail-Endpoint, PDF-Lightbox, Arbeitstisch mit echter Seitengeometrie (2), Bildaustausch auf bestehenden Seiten (3), Arbeiten ohne Neuladen, Autosave, Tastenkürzel, Sammelaktionen auf Seiten und Raster-Plätze (4). Nebenbei gefundene und behobene Fehler: - `dol_dir_list()` wurde ohne `$mode = 1` aufgerufen — dadurch waren `size` und `date` leer und in der Anhängeliste stand „ Bytes" ohne Zahl - PDF.js-Worker-Pfad war hartkodiert ohne `/custom`-Präfix → PDF.js lief im „fake worker"-Modus auf dem Hauptthread (spürbar langsamer beim Rendern) - `document.php` wurde ohne `attachment=0` verlinkt → PDFs wurden heruntergeladen statt angezeigt - Größen-Auswahl der Seite blieb leer, weil der DB-Wert `1` die Option `1.0` nicht traf - Mehrfach-Upload nahm nur die erste Datei - `--inputbackgroundcolor` wurde im Modul als „im AwlDark nicht vorhanden" behandelt; die Variable existiert (KB #512, per DevTools bestätigt) — Editor-Eingabefelder nutzen jetzt dieselben Farben wie Dolibarr selbst