diff --git a/CLAUDE.md b/CLAUDE.md index 93448fc..bbea19d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # Bericht-Modul — Projekt-Status & Architektur -## Stand 2026-05-27 +## Stand 2026-08-21 (Version 1.5.0) Dolibarr-Custom-Modul für Arbeitsberichte mit Browser-PDF-Editor + PWA-API-Layer + Lieferschein-Unterschrift. @@ -34,19 +34,42 @@ Berichte gehören 1:1 zu einem Parent (`element_type` + `fk_element`). Auf einer **500033** — kollidiert NICHT mit BankImport (500021). Permission-IDs sind 500033 + n. ### CSS-Variablen (Dolibarr awl-dark Theme) -Verwendete: `--colorbacktitle1`, `--colortext`, `--colorbackbody`, `--colorboxbordertitle1`, `--colortextlink`, `--colorbackhmenu1`, `--colortextbackhmenu`, `--colorbackvmenu1` +Verwendete: `--colorbacktitle1`, `--colortext`, `--colorbackbody`, `--colorboxbordertitle1`, +`--colortextlink`, `--colorbackhmenu1`, `--colortextbackhmenu`, `--colorbackvmenu1`, +`--colorbacklinepair1`, `--colorbacklinebreak`, `--colorbacklinepairchecked`, +`--colortexttitlenotab`, `--colortexttitlenotab2`, `--inputbackgroundcolor`, `--inputbordercolor`, +`--btncolorborderhover` -NICHT vorhanden im awl-dark: `--inputbackgroundcolor`, `--inputtextcolor` — stattdessen Fallbacks nutzen. +**Korrektur 2026-08-21:** Hier stand, `--inputbackgroundcolor` und `--inputtextcolor` gebe es im +awl-dark nicht. Das stimmt für `--inputbackgroundcolor` **nicht** — die Variable ist gesetzt +(`rgb(20,22,24)`), ebenso `--inputbordercolor` (`rgb(54,57,62)`). Wegen der falschen Notiz waren +die Eingabefelder im Editor mit `--colorbackbody` gestylt und sahen anders aus als Dolibarrs +eigene Felder. Vollständige, per DevTools geprüfte Liste: **KB #512**. + +`--colorbackhmenu1` (`rgb(39,44,49)`) taugt **nicht** für Aktiv-Zustände — praktisch derselbe Ton +wie der Toolbar-Hintergrund. Dafür `--colorbacklinepairchecked` (`rgb(30,87,116)`) nehmen. ### JS-Libraries (lokal in js/lib/) - pdf.min.js (PDF.js 3.11) -- pdf.worker.min.js +- pdf.worker.min.js — **Pfad NIE hartkodieren**, sonst fehlt `/custom` und PDF.js fällt auf den + „fake worker" zurück (rendert dann im Hauptthread). Kommt über + `cfg.urls.pdf_worker` aus `dol_buildpath()`. - fabric.min.js (Fabric.js 5.3) -- Sortable.min.js (SortableJS 1.15) +- Sortable.min.js (SortableJS 1.15) — mit `group: {put: false}`, sonst kollidiert das Sortieren + mit dem eigenen Ablegen von Anhang-Kacheln +- qrcode.min.js (QR-Code für den Mobile-Upload) ### LocalStorage Settings -Key: `bericht.editor.settings.v1` -Speichert: color, stroke, fontFamily, fontSize, bold, italic, zoom +| Key | Inhalt | +|---|---| +| `bericht.editor.settings.v1` | color, stroke, fontFamily, fontSize, bold, italic, zoom | +| `bericht.attachments.view.v1` | Kachel- oder Listenansicht der Anhänge | +| `bericht.attachments.sort.v1` | Sortierung (taken / taken_desc / name / size_desc) | +| `bericht.attachments.filter.v1` | Filter (all / image / pdf / unused) | +| `bericht.attachments.tilesize.v1` | Kachelgröße (s / m / l) | +| `bericht.attachments.width.v1` | Breite der Anhänge-Spalte in px | +| `bericht.attachments.collapsed.v1` | Spalte eingeklappt (1/0) | +| `bericht.editor.margins.v1` | Seitenränder im Arbeitstisch anzeigen (1/0) | ### Forgejo - Repo: `data/bericht` (NICHT `data-it/bericht` — Token hat keine org-write rechte) @@ -56,7 +79,7 @@ Speichert: color, stroke, fontFamily, fontSize, bold, italic, zoom --- -## Phase 1 — Bericht-Modul-Erweiterungen (in Arbeit) +## Phase 1 — Bericht-Modul-Erweiterungen ✅ (abgeschlossen mit 1.5.0) ### ✅ Erledigt vor Phase 1 - Modul-Scaffold (modBericht, SQL, Lang, Rechte korrekt nach Stundenzettel-Format) @@ -75,16 +98,40 @@ Speichert: color, stroke, fontFamily, fontSize, bold, italic, zoom - [x] **1.1 Live-PDF-Vorschau** ✅ - [x] **1.2 Anhänge löschen** ✅ - [x] **1.3 Seitengröße A4/A3/Letter + Hoch/Quer** ✅ — global pro Bericht (nicht pro Seite override) -- [/] **1.4 Mehrere Bilder pro Seite** — DB + Klassen-Skelett da, Grid-Rendering im PDF + Editor noch offen (siehe TODO unten) -- [/] **1.5 Bildgröße pro Seite** — image_scale/image_align Spalten da, UI noch offen -- [ ] **1.7 Kunden-Tab** — Tab "Berichte" auf thirdparty +- [x] **1.4 Mehrere Bilder pro Seite** ✅ 1.5.0 — Raster im PDF und im Editor; Plätze direkt auf + der Seite anklickbar, einzeln tauschbar +- [x] **1.5 Bildgröße pro Seite** ✅ — image_scale/image_align mit Auswahl in der Werkzeugleiste +- [x] **1.7 Kunden-Tab** ✅ — `bericht_thirdparty.php`, Konstante `BERICHT_TAB_ON_THIRDPARTY` -### TODO Phase 1.4 + 1.5 (Folge-Commit) -- BerichtPage::getImages() liest llx_bericht_page_image -- Editor: Layout-Dropdown pro Seite (1/2/4/6 Slots) -- Drag&Drop von Anhängen in Slots oder "Als Grid hinzufügen"-Button -- generate_pdf/preview_pdf: Grid-Rendering (calculate slot rects) -- Bei `single`-Layout: image_scale (1.0/0.7/0.5) + image_align (fit/center/topleft/topright) +### Seitengeometrie — der wichtigste Begriff im Modul (seit 1.5.0) + +`bericht_page_geometry($format, $orientation, $has_note)` in `lib/bericht.lib.php` ist die +**einzige** Quelle für Seitenmaße und Ränder. Editor **und** PDF-Erzeugung rechnen damit: + +- Ränder 10 mm, Kopfbereich **30 mm** (dort druckt TCPDF Logo/Titel, Trennlinie bei y=27), + Fußbereich 16 mm, Notizzone 25 % der Seitenhöhe (nur wenn die Seite eine Notiz hat) +- Der Editor-Canvas ist der **Inhaltsbereich**, nicht das Blatt. Das PDF setzt das gespeicherte + Composite-PNG 1:1 dorthin. +- **Nicht** erneut einpassen — genau das war vor 1.5.0 der Fehler (Inhalt wurde zweimal + verkleinert, breiter Rand, Vorschau stimmte nicht). +- Composites von vor 1.5.0 haben Blatt-Proportion; sie werden an der Proportion erkannt und + proportional eingepasst statt verzerrt. Diese Weiche nicht entfernen, solange Altberichte + neu erzeugt werden könnten. +- Raster-Plätze über `BerichtPage::slotRectsInBox()` (PHP) bzw. `slotRectsPercent()` (JS) — + beide teilen denselben Inhaltsbereich auf und müssen zusammenpassen. + +### Weitere Fallen (2026-08-21 verifiziert) + +- `dol_dir_list()` braucht `$mode = 1` für `size`/`date` **und** `$nohook = 1` — sonst läuft der + `getDirList`-Hook fremder Module mit, der `global $object` auswertet (im AJAX-Kontext nicht da). + Siehe KB #1060. +- `document.php` braucht `&attachment=0`, sonst wird das PDF heruntergeladen statt angezeigt. +- **Prod-Container hat kein `exif` und kein Imagick** — EXIF liest das Modul selbst + (`bericht_jpeg_taken_at()`), PDF-Vorschauen laufen über PDF.js im Browser. +- Seitenwechsel sind serialisiert (Kette + Token in `loadPage()`). Beim schnellen Durchklicken + überlappten sich sonst zwei Ladevorgänge und die Notiz landete auf der falschen Seite. +- Fabric-Ereignisse (`object:added` …) feuern auch beim **Aufbauen** einer Seite — deshalb + `buildingPage`-Flag, sonst meldet das Autosave sofort „nicht gespeichert". --- diff --git a/README.md b/README.md index e32c4eb..dc937e2 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,16 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te ## Funktionen - **Reiter „Bericht"** auf Rechnungen, Aufträgen, Angeboten und Lieferungen (jeweils per Konstante deaktivierbar) -- **Anhänge-Browser** zeigt alle Dateien des aktuellen Dokuments **und** der direkt verknüpften Objekte (z. B. der Auftrag zur Rechnung) -- **Auswahl per Checkbox** — markierte Dateien werden als Seiten in den Bericht übernommen -- **Browser-Editor** mit PDF.js + Fabric.js: Pfeile, Kreise, Rechtecke, Freihand, Text, Farbe, Strichstärke, Undo/Redo -- **Seiten-Verwaltung** per Drag&Drop (SortableJS): umordnen, löschen, drehen, neue Seiten hochladen +- **Anhänge-Spalte als Kachelraster** mit Vorschaubildern — zeigt alle Dateien des aktuellen Dokuments **und** der direkt verknüpften Objekte (z. B. der Auftrag zur Rechnung). Bild-Miniaturen entstehen serverseitig (GD, gecacht), PDF-Anhänge zeigen Seite 1 (PDF.js). Klick öffnet die Großansicht — Bilder mit Zoom und Wischen, PDFs im PDF-Viewer des Browsers. +- **Sortieren und Filtern** der Anhänge nach Aufnahmezeit (EXIF), Name oder Größe · nur Bilder / nur PDFs / noch nicht verwendete. Spaltenbreite ziehbar, Spalte einklappbar, Kachelgröße in drei Stufen. +- **Auswahl per Kachel** — die Reihenfolge der Auswahl (① ② ③) bestimmt auch die Verteilung auf die Plätze im Raster-Layout. Bereits verwendete Dateien sind markiert. +- **Browser-Editor** mit PDF.js + Fabric.js: Pfeile, Kreise, Rechtecke, Freihand, Text, Farbe, Strichstärke, Undo/Redo — mit Tastenkürzeln (V/P/R/K/A/T, Entf, Strg+Z/Y/S, ↑/↓) +- **Arbeitstisch zeigt die echte Seite**: Kopfbereich (Logo/Titel), Seitenränder und Fußbereich sind sichtbar; die Arbeitsfläche entspricht genau dem Bereich, der gedruckt wird — das PDF übernimmt sie 1:1 +- **Automatisches Speichern** kurz nach der letzten Änderung, beim Seitenwechsel und beim Wegschalten des Tabs; beim Verlassen mit offenen Änderungen warnt der Browser +- **Seiten-Verwaltung** per Drag&Drop (SortableJS): umordnen, löschen, drehen, neue Seiten hochladen. Mehrere Seiten markieren (Klick auf die Seitennummer, Umschalt für einen Bereich) und gesammelt löschen oder verdoppeln. +- **Bild einer Seite austauschen** ohne sie zu löschen — Reihenfolge, Notiz und Anmerkungen bleiben erhalten. Bei Raster-Layouts lässt sich jeder Platz einzeln tauschen, direkt auf der Seite anklickbar. +- **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. +- **Mobiler Upload per QR-Code** — Handy scannt, lädt Fotos direkt in den offenen Bericht (zeitlich begrenzte Token) - **Notizen pro Seite** — werden im finalen PDF unten auf der Seite gedruckt - **Deckblatt aus ODT-Vorlage** mit Platzhaltern (`{auftragsnummer}`, `{kunde_name}`, `{datum}`, …) - **ODT-Templates** im Admin-Bereich verwaltbar (mehrere Vorlagen, Standard wählbar) @@ -27,8 +33,13 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te - TCPDF (in Dolibarr enthalten) - **FPDI** (für PDF-Anhänge in den Bericht zu mergen) — empfohlen, optional - **LibreOffice headless** (für ODT→PDF Konvertierung der Deckblätter) +- **GD** (PHP-Extension) für die Vorschaubilder der Anhänge und die Raster-Vorschau - Optional: `pdfinfo` oder `imagick` für PDF-Seitenanzahl-Erkennung +> Imagick und die PHP-Extension `exif` sind im Produktions-Container **nicht** installiert. +> PDF-Vorschauen laufen deshalb über PDF.js im Browser, EXIF-Daten liest das Modul mit einem +> eigenen Parser (`bericht_jpeg_taken_at()` / `bericht_jpeg_orientation()`). + ## Installation 1. Modul-Verzeichnis nach `dolibarr/htdocs/custom/bericht/` (oder per Symlink aus dem Module-Mount-Pfad) kopieren @@ -42,9 +53,9 @@ Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Te 1. Eine Rechnung öffnen (`/compta/facture/card.php?id=…`) 2. Reiter **Bericht** auswählen 3. **+ Neuer Bericht** klicken — die Auftragsnummer wird automatisch übernommen -4. Im Editor links die gewünschten Anhänge ankreuzen → **Auswahl in Bericht übernehmen** -5. Im mittleren Editor mit den Werkzeugen Pfeile, Texte etc. zeichnen -6. Seiten rechts per Drag&Drop sortieren, einzelne Seiten löschen, neue Dateien hochladen +4. Im Editor links die gewünschten Anhänge auswählen (Kachel-Checkbox; die Reihenfolge der Auswahl zählt) → **Auswahl in Bericht übernehmen**. Alternativ eine Kachel direkt in die Seitenliste ziehen. +5. Im mittleren Editor mit den Werkzeugen Pfeile, Texte etc. zeichnen — der graue Bereich oben und unten zeigt, wo Logo, Titel und Seitenzahl gedruckt werden. Änderungen werden automatisch gespeichert. +6. Seiten rechts per Drag&Drop sortieren, einzelne Seiten löschen oder über die Seitennummer mehrere markieren und gesammelt löschen/verdoppeln. Ein falsches Bild lässt sich über **🔄** auf der Seitenminiatur austauschen. 7. **Bericht finalisieren** — PDF wird erzeugt, Deckblatt aus der ODT-Vorlage gerendert und unter den verknüpften Dokumenten der Rechnung abgelegt ## ODT-Template Platzhalter @@ -152,8 +163,8 @@ Per `admin/setup.php` oder `llx_const`: |---|---| | `llx_bericht` | Bericht-Header (element_type ∈ {invoice, order, propal, shipment}, fk_element, status, …) | | `llx_bericht_page` | Einzelne Seiten mit Fabric-JSON-Annotationen, Layout, Notiz | -| `llx_bericht_page_image` | Multi-Image-Layout pro Seite (Phase 1.4) | -| `llx_bericht_upload_token` | Mobile-Upload-Tokens (geplant) | +| `llx_bericht_page_image` | Bilder der einzelnen Plätze bei Raster-Layouts (grid_2 … grid_6, before_after) | +| `llx_bericht_upload_token` | Zeitlich begrenzte Tokens für den QR-Mobile-Upload | | `llx_bericht_signature_box` | Pro Lieferschein-Template gespeicherte Signatur-Box-Geometrie (mm) | ## Architektur @@ -162,26 +173,48 @@ Per `admin/setup.php` oder `llx_const`: bericht/ ├── core/modules/modBericht.class.php Modul-Descriptor, Tabs, Extrafields-Init, Konstanten ├── class/ -│ ├── bericht.class.php Bericht + BerichtPage CRUD +│ ├── bericht.class.php Bericht + BerichtPage CRUD, Slot-Geometrie +│ ├── upload_token.class.php Tokens für den QR-Mobile-Upload │ └── actions_bericht.class.php Hook: beforeODTSave setzt {signature} + Meta-Variablen -├── lib/bericht.lib.php Helper (Anhänge, Auftragsnr., Templates, Shipment-PDF, -│ Signature-Box, FPDI-Stempel-Fallback) +├── lib/bericht.lib.php Helper: Anhänge sammeln, Vorschaubilder, EXIF-Parser, +│ Seitengeometrie (bericht_page_geometry), PDF-Rendering, +│ Render-Funktionen für Seiten-/Anhängeliste, +│ Signature-Box, FPDI-Stempel-Fallback ├── bericht_card.php Editor-Seite (Tab-Inhalt) ├── admin/ │ ├── setup.php Admin: ODT-Templates, Konstanten, Signatur-Größe │ ├── signature_box_editor.php Visueller PDF-Editor für Signatur-Box-Position │ └── signature_box_preview.php Beispiel-PDF-Renderer für den Editor -├── ajax/ Legacy Endpoints (Token-geschützt) -│ ├── _inc.php Gemeinsamer Header +├── bericht_batch.php Sammel-Erstellung von Berichten +├── bericht_thirdparty.php Read-only Übersicht auf der Kundenkarte +├── mobile_upload.php Upload-Seite für den QR-Code (Handy) +├── ajax/ Endpoints für den Editor (Token-geschützt) +│ ├── _inc.php Gemeinsamer Header (Rechte, JSON, Fatal-Handler) │ ├── add_attachment.php Anhang als Seite hinzufügen -│ ├── upload_extra.php Direkter Upload -│ ├── save_annotations.php Fabric-JSON speichern -│ ├── save_signature_box.php UPSERT der Signatur-Box pro Template +│ ├── attachment_thumb.php Vorschaubild eines Anhangs (GD, gecacht, ETag) +│ ├── create_grid_page.php Raster-Seite aus mehreren Bildern anlegen +│ ├── create_upload_token.php Token für den QR-Mobile-Upload +│ ├── delete_attachment.php Datei aus den Dokumenten des Belegs löschen +│ ├── delete_page.php Einzelne Seite löschen +│ ├── fragments.php Seiten-/Anhängeliste als HTML (Arbeiten ohne Reload) +│ ├── generate_pdf.php Finalisierung: TCPDF + FPDI + ODT-Deckblatt +│ ├── get_photo.php / list_photos.php Fotos für die PWA +│ ├── list_pages.php Seitenliste als JSON (Polling) +│ ├── page_bulk.php Mehrere Seiten löschen oder verdoppeln +│ ├── page_image.php Seitenbild/PDF ausliefern (Raster: Composite via GD) │ ├── page_meta.php Annotationen + Notiz laden -│ ├── page_image.php Seitenbild/PDF ausliefern -│ ├── delete_page.php -│ ├── reorder_pages.php -│ └── generate_pdf.php Finalisierung: TCPDF + FPDI + ODT-Deckblatt +│ ├── preview_pdf.php PDF-Vorschau ohne zu finalisieren +│ ├── process_document.php Import hochgeladener Dokumente +│ ├── reorder_pages.php Reihenfolge speichern +│ ├── replace_page_source.php Bild einer Seite / eines Rasterplatzes austauschen +│ ├── save_annotations.php Fabric-JSON + Composite-PNG speichern +│ ├── save_as_template.php Bericht als Vorlage sichern +│ ├── save_meta.php Titel, Format, Ausrichtung +│ ├── save_page_options.php Layout, Bildgröße, Position je Seite +│ ├── save_signature_box.php UPSERT der Signatur-Box pro Template +│ ├── set_slot_image.php Bild eines Rasterplatzes setzen +│ ├── upload_extra.php Direkter Upload +│ └── verify_signature.php Unterschrift prüfen ├── api/ REST-API (JWT-Auth) │ ├── _inc.php JWT-Authentifizierung + Dolibarr-Init │ ├── _jwt.php JWT encoding/decoding (mit ?jwt= Query-Fallback) @@ -197,12 +230,18 @@ bericht/ │ ├── voice.php Sprachnotizen (Upload + Transkription) │ └── transcribe.php Whisper-Transkription ├── js/ -│ ├── editor.js PDF.js + Fabric.js Integration -│ └── lib/ PDF.js, Fabric.js, SortableJS (lokal) -├── css/bericht.css +│ ├── editor.js PDF.js + Fabric.js Integration, Anhänge-Spalte, +│ │ Seitengeometrie, Drag&Drop, Autosave, Tastenkürzel +│ ├── imageviewer.js Großansicht (Zoom, Wischen, PDF-Modus) +│ └── lib/ PDF.js, Fabric.js, SortableJS, QRCode (lokal) +├── css/ +│ ├── bericht.css Editor-Layout, Kacheln, Blatt-Darstellung +│ └── imageviewer.css Großansicht ├── sql/ │ ├── llx_bericht.sql / .key.sql │ ├── llx_bericht_page.sql / .key.sql +│ ├── llx_bericht_page_image.sql / .key.sql +│ ├── llx_bericht_upload_token.sql / .key.sql │ └── llx_bericht_signature_box.sql / .key.sql └── langs/{de_DE,en_US}/bericht.lang ``` diff --git a/ROADMAP.md b/ROADMAP.md deleted file mode 100644 index d8c98b3..0000000 --- a/ROADMAP.md +++ /dev/null @@ -1,151 +0,0 @@ -# ROADMAP — Modul Bericht - -Stand: 2026-08-21 · Version im Repo: 1.5.0 - ---- - -## 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. - ---- - -## Erledigt - -### 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 diff --git a/lib/bericht.lib.php b/lib/bericht.lib.php index ae6c48e..8c057b1 100644 --- a/lib/bericht.lib.php +++ b/lib/bericht.lib.php @@ -482,7 +482,7 @@ function bericht_collect_attachments(DoliDB $db, CommonObject $parent, $element_ if ($dir_key && !empty($conf->{$dir_key}->multidir_output[$parent->entity])) { $upload_dir = $conf->{$dir_key}->multidir_output[$parent->entity].'/'.dol_sanitizeFileName($parent->ref); if (is_dir($upload_dir)) { - $files = dol_dir_list($upload_dir, 'files', 1, '', '(\.meta|_preview.*\.png|thumbs)$', 'name', SORT_ASC, 1); + $files = dol_dir_list($upload_dir, 'files', 1, '', '(\.meta|_preview.*\.png|thumbs)$', 'name', SORT_ASC, 1, 1); foreach ($files as $f) { $result[] = array( 'source' => $element_type, @@ -509,7 +509,7 @@ function bericht_collect_attachments(DoliDB $db, CommonObject $parent, $element_ if (empty($conf->{$linked_dolibarr_type}->multidir_output[$lobj->entity])) continue; $linked_dir = $conf->{$linked_dolibarr_type}->multidir_output[$lobj->entity].'/'.dol_sanitizeFileName($lobj->ref); if (!is_dir($linked_dir)) continue; - $files = dol_dir_list($linked_dir, 'files', 1, '', '(\.meta|_preview.*\.png|thumbs)$', 'name', SORT_ASC, 1); + $files = dol_dir_list($linked_dir, 'files', 1, '', '(\.meta|_preview.*\.png|thumbs)$', 'name', SORT_ASC, 1, 1); foreach ($files as $f) { $result[] = array( 'source' => $linked_module_type,