bericht/ChangeLog.md
Eduard Wisch a8d8291c3a API 1.5.1: Thumbnails, Aufnahmezeit, Seitenfelder, Textnotizen
Die API-Seite zur Baustelle-PWA holt nach, was der Editor mit 1.5.0 bekommen hat.

photo.php kann jetzt size=thumb&w=<px> und rendert ueber bericht_attachment_thumb()
(GD, Cache, EXIF-Rotation) statt die Originaldatei auszuliefern. size=small konnte nur
ein von Dolibarr vorgefertigtes thumbs/<name>_small.<ext> liefern - fuer Uploads ueber
orders.php?action=upload_photo gibt es das nicht, dort laeuft kein vignette(). Die PWA
lud dadurch fuer jede Kachel das komplette Foto: gemessen 9,8 KB statt 235 KB je Kachel.
Antwort mit ETag aus der mtime, Folgeaufrufe enden mit 304.

orders.php?action=photos liefert taken_at (EXIF-Aufnahmezeit ueber bericht_file_taken_at,
sonst Dateidatum) und sortiert danach statt nach filemtime.

reports.php gibt je Seite title und composite_path aus. title konnte ueber pages.php
gesetzt werden, kam aber nie zurueck. composite_path ist das im Editor gebaute
Seitenbild - ohne das zeigen Clients nur das Rohbild ohne Anmerkungen, bei
Raster-Layouts eines von bis zu sechs Bildern und bei title_only gar keins.

Neu: api/note.php - Textnotizen zum Auftrag, aufgebaut wie die Sprachnotiz. Die Notiz
liegt als notiz_<betreff>_<datum>.txt im Auftragsverzeichnis und ist damit auch im
Dolibarr-Auftrag und in der Anhaenge-Spalte des Editors sichtbar. Liste, Lesen, Anlegen,
Aendern, Loeschen. Der file-Parameter wird gegen ^notiz_[A-Za-z0-9_.-]*\.txt$ geprueft;
gegen Pfad-Ausbrueche getestet.

ROADMAP.md wiederhergestellt - sie war mit eb37a4b geloescht worden, weil alle Punkte
abgehakt waren. Sie bleibt ab jetzt liegen.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-22 18:11:42 +02:00

305 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Changelog
## 1.5.1 — 2026-08-22
Die API-Seite zur Baustelle-PWA holt nach, was der Editor mit 1.5.0 bekommen hat.
### Thumbnails für die PWA
- **`api/photo.php` kann jetzt `size=thumb&w=<px>`** und rendert damit über
`bericht_attachment_thumb()` — dieselbe Funktion, die auch die Anhänge-Kacheln im Editor
füllt (GD, Cache unter `bericht/thumbs/`, EXIF-Ausrichtung).
- Bisher kannte der Endpoint nur `size=small|mini`, und das lieferte ausschließlich ein
Thumbnail, das **Dolibarr selbst** unter `thumbs/<name>_small.<ext>` abgelegt hatte. Fotos aus
der PWA kommen über `orders.php?action=upload_photo` herein, und dort läuft **kein
`vignette()`** — es gab also nie eines, und die Fotoliste lud für jede Kachel die
Originaldatei. Gemessen an einer Testdatei: **9,8 KB statt 235 KB je Kachel**.
- Antwort mit `ETag` aus der mtime und `Cache-Control: max-age=30 Tage`; ein wiederholter
Aufruf endet mit `304`. Ein ersetztes Original bekommt automatisch einen neuen ETag.
- Nicht-Bilder (PDF, Video) fallen unverändert auf das Original zurück.
### Aufnahmezeit in der Anhängeliste
- **`api/orders.php?action=photos` liefert `taken_at`** — die EXIF-Aufnahmezeit über
`bericht_file_taken_at()`, sonst das Dateidatum. Sortiert wird danach statt nach `filemtime`.
Bei Uploads aus der Galerie war die Upload-Reihenfolge bisher die falsche Reihenfolge.
### Bericht-Seiten: Titel und fertiges Seitenbild
- **`api/reports.php` gibt je Seite `title` und `composite_path` mit aus.**
- `title` konnte über `api/pages.php` gesetzt werden und wurde in der PWA auch gerendert, kam
aber nie zurück — der Titel wirkte, als würde er nicht gespeichert.
- `composite_path` ist das im Editor gebaute Seitenbild. Ohne dieses Feld zeigt ein Client nur
`source_path`, also das Rohbild ohne Anmerkungen — und bei einem Raster-Layout eines von bis
zu sechs Bildern. Bei `title_only` ist `source_path` leer, die PWA zeigte dort eine
Fehler-Kachel.
### Neu: Textnotizen zum Auftrag (`api/note.php`)
- Getippte Notiz zum Auftrag, aufgebaut wie die Sprachnotiz: die Notiz liegt als
`notiz_<betreff>_<datum>.txt` im Auftrags-Verzeichnis und ist damit im Dolibarr-Auftrag und
in der Anhänge-Spalte des Editors sichtbar.
- `GET` (Liste mit Betreff, Vorschau und Zeitstempel), `GET &file=` (eine Notiz),
`POST` (anlegen), `POST &file=` (ändern), `POST &file=&delete=1` (löschen).
- Dateikopf bis zur ersten Leerzeile trägt `Betreff:`, `Erfasst:` und `Geändert:` (jeweils mit
Login), darunter der freie Text. Bewusst als Klartext lesbar, auch wenn jemand die `.txt`
direkt im ECM öffnet.
- Der `file`-Parameter wird gegen `^notiz_[A-Za-z0-9_.\-]*\.txt$` geprüft — ohne das ließe sich
über `../` jede Datei unter `DOL_DATA_ROOT` überschreiben. Gegen Pfad-Ausbrüche getestet.
## 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** (150620 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<code>` / `PaymentConditionShort<code>` (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/<ref>/<ref>-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
- `<sig>.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=<id>` — Liste der Lieferungen zu einem Auftrag
- `GET /api/shipments.php?id=<id>` — Detail einer Lieferung + Bericht-ID falls vorhanden
- `GET /api/shipments.php?id=<id>&action=pdf` — PDF-Stream (Original oder gestempelt)
- `POST /api/shipments.php?id=<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]`)