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>
19 KiB
Bericht — Arbeitsberichte für Dolibarr
Erstellt aus den Anhängen einer Rechnung (oder eines Auftrags / Angebots) einen Arbeitsbericht als PDF. Bilder und PDFs lassen sich im Browser annotieren (Pfeile, Kreise, Rechtecke, Text, Freihand) — der fertige Bericht wird unter Verknüpfte Dokumente der Rechnung abgelegt.
Funktionen
- Reiter „Bericht" auf Rechnungen, Aufträgen, Angeboten und Lieferungen (jeweils per Konstante deaktivierbar)
- 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)
- Auftragsnummer wird automatisch aus dem Extrafield
options_auftragsnummerder Rechnung gezogen - Mehrere Berichte pro Dokument möglich
- Berichte als Entwurf speichern (jederzeit wieder editierbar) oder finalisieren (PDF erzeugen)
- Lieferschein-Bestätigung mit Kunden-Unterschrift: Vollbild-Querformat-Signatur in der PWA, Unterschrift wird via ODT-Hook (Platzhalter
{signature}) ins Lieferschein-PDF gestempelt, Expedition wird automatisch validiert und geschlossen - PWA-API für Mobile-Nutzung: Aufträge, Fotos, Sprachnotizen, Materiallisten, Lieferungen, Signaturen
- PDF-Viewer in PWA mit PDF.js Canvas-Rendering (Zoom, Seitennummerierung, Download)
Voraussetzungen
- Dolibarr ≥ 19.0
- PHP ≥ 7.4
- 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:
pdfinfooderimagickfür PDF-Seitenanzahl-Erkennung
Imagick und die PHP-Extension
exifsind 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
- Modul-Verzeichnis nach
dolibarr/htdocs/custom/bericht/(oder per Symlink aus dem Module-Mount-Pfad) kopieren - In Dolibarr unter Konfiguration → Module/Anwendungen das Modul Bericht aktivieren
- Beim Aktivieren werden die SQL-Tabellen
llx_berichtundllx_bericht_pageangelegt - Vorhandene Extrafields auf
llx_facture_extrafields(auftragsnummer,angebotsnummer, …) werden erkannt und nicht überschrieben — fehlende werden angelegt - Im Admin-Bereich (
/bericht/admin/setup.php) die ODT-Templates hochladen und Standard-Template setzen
Verwendung
- Eine Rechnung öffnen (
/compta/facture/card.php?id=…) - Reiter Bericht auswählen
- + Neuer Bericht klicken — die Auftragsnummer wird automatisch übernommen
- 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.
- 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.
- 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.
- Bericht finalisieren — PDF wird erzeugt, Deckblatt aus der ODT-Vorlage gerendert und unter den verknüpften Dokumenten der Rechnung abgelegt
ODT-Template Platzhalter
| Platzhalter | Inhalt |
|---|---|
{auftragsnummer} |
Aus extrafield options_auftragsnummer der Rechnung |
{angebotsnummer} |
Aus extrafield options_angebotsnummer |
{rechnungsnummer} |
ref der Rechnung |
{kunde_name} |
Name des Kunden (Société) |
{kunde_adresse} |
Adresse des Kunden, mehrzeilig |
{datum} |
Heutiges Datum |
{beschreibung} |
extrafield options_beschreibung |
{hinweis} |
extrafield options_hinweis |
{bericht_titel} |
Titel des Berichts |
{ersteller} |
Login-Name des erstellenden Users |
{signature} |
Kunden-Unterschrift als Bild (nur Lieferschein-Workflow, ersetzt Text-Platzhalter durch eingebettetes PNG; Größe via BERICHT_SIGNATURE_IMAGE_RATIO) |
{signer_name} |
Name des unterschreibenden Kunden |
{signed_at} |
Zeitstempel der Unterschrift |
{gps} |
GPS-Koordinaten zum Zeitpunkt der Unterschrift (falls erlaubt) |
PWA-Integration (Baustelle Mobile App)
Die Baustelle-PWA (https://awl.data-it-solution.de/baustelle/) nutzt die folgenden API-Endpoints des Bericht-Moduls:
Authentifizierung
POST /custom/bericht/api/auth.php
Body: { login: string, password: string }
Response: { ok: true, user: {...} } + HttpOnly-Cookie awl_sso
Seit der SSO-Migration läuft die Anmeldung über das Modul awlauth: auth.php setzt das
HttpOnly-Cookie awl_sso (Single-Sign-on/-out über alle AWL-Apps), Folge-Requests
authentisieren sich same-origin über dieses Cookie. Kein Bearer-Token und kein ?jwt=
mehr — auch <img>- und <audio>-Tags kommen ohne Query-Parameter aus.
Endpoints mit awlauth_require erwarten zusätzlich den Header X-Requested-With: XMLHttpRequest (CSRF-Schutz); reine GET-Reads wie photo.php und pdf.php nutzen
awlauth_verify ohne diese Anforderung, damit sie auch per window.location gehen.
Order-APIs
GET /custom/bericht/api/orders.php
GET /custom/bericht/api/orders.php?id=<id>
GET /custom/bericht/api/orders.php?id=<id>&action=photos
Je Datei: filename, size, mime, date (mtime), taken_at (EXIF-Aufnahmezeit, sonst
Dateidatum), relpath. Sortiert nach taken_at, neuste zuerst.
POST /custom/bericht/api/orders.php?action=create
Body: { socid, ref_client, title?, note_private?, date?, date_livraison?, validate? }
ref_client ("Ihr Zeichen") ist PFLICHT. validate=true gibt den Auftrag direkt frei —
OHNE Position (Commande::valid() verlangt keine, verifiziert gegen Dolibarr 22.0.2).
Die Leistungen kommen später aus dem Stundenzettel.
Dateien
GET /custom/bericht/api/photo.php?relpath=<path>
Liefert Dateien aus DOL_DATA_ROOT (Whitelist: facture/, commande/, propal/, bericht/)
Auth über das awl_sso-Cookie (same-origin), auch für <img>-Tags.
?size=thumb&w=<px> Serverseitig erzeugtes Thumbnail (GD, gecacht unter
bericht/thumbs/, mit ETag aus der mtime → Folgeaufrufe 304).
w: 40–800, Default 320. Das ist der Weg für Kachel-Ansichten.
Ist die Datei kein Bild, kommt das Original.
?size=small|mini Altes Verhalten: nutzt ein von Dolibarr vorgefertigtes
thumbs/<name>_small.<ext>. Für Dateien, die über die API
hochgeladen wurden, existiert so eines NICHT (kein vignette()) —
dort kam bis 1.5.1 immer das Original zurück.
?download=1 Attachment-Header
PDF-Ansicht
GET /custom/bericht/api/pdf.php?id=<bericht_id>&jwt=<token>
Liefert finalisiertes Bericht-PDF als Blob
Seiten-Verwaltung (PWA)
DELETE /custom/bericht/api/pages.php?id=<page_id>
POST /custom/bericht/api/pages.php?id=<page_id> Body: { note, title, rotation, layout }
POST /custom/bericht/api/pages.php?action=signature&bericht_id=<id>
Body: FormData mit file=<PNG-Blob>, signer_name, gps_lat, gps_lon
reports.php?id=<id> gibt je Seite composite_path und title mit aus. Clients sollen
composite_path anzeigen, wenn es gesetzt ist — das ist das im Editor gebaute Seitenbild
mit Anmerkungen. source_path ist nur die Quelldatei: bei Raster-Layouts eines von bis zu
sechs Bildern, bei title_only leer.
Textnotizen zum Auftrag (PWA)
GET /custom/bericht/api/note.php?order_id=<id> Liste
GET /custom/bericht/api/note.php?order_id=<id>&file=<name> eine Notiz mit Text
POST /custom/bericht/api/note.php?order_id=<id> anlegen { subject, text }
POST /custom/bericht/api/note.php?order_id=<id>&file=<name> ändern { subject, text }
POST /custom/bericht/api/note.php?order_id=<id>&file=<name>&delete=1
Notizen liegen als notiz_<betreff>_<datum>.txt im Auftrags-Verzeichnis — dasselbe Muster
wie die Sprachnotiz (voice.php). Sie sind damit auch im Dolibarr-Auftrag und in der
Anhänge-Spalte des Editors zu sehen. Der Dateikopf (bis zur ersten Leerzeile) trägt
Betreff:, Erfasst: und Geändert:, darunter steht der freie Text. file wird gegen
^notiz_[A-Za-z0-9_.\-]*\.txt$ geprüft — ohne das ließe sich über ../ jede Datei unter
DOL_DATA_ROOT überschreiben.
Lieferungen + Unterschrift (PWA)
GET /custom/bericht/api/shipments.php?order_id=<id>
Liste aller Expeditionen zum Auftrag (id, ref, date_delivery, status, signed_status, has_bericht)
GET /custom/bericht/api/shipments.php?id=<id>
Detail einer Lieferung inkl. bericht_id
GET /custom/bericht/api/shipments.php?id=<id>&action=pdf[&variant=auto|signed|unsigned]
Liefert Lieferschein-PDF (Default auto: signed wenn vorhanden, sonst Original)
POST /custom/bericht/api/shipments.php?id=<id>&action=confirm
FormData mit signature_png, signer_name, gps_lat, gps_lon, signed_at
Stempelt Unterschrift via ODT-Hook ({signature}-Platzhalter), legt <ref>-signed.pdf
in documents/expedition/<ref>/, setzt signed_status=1, validiert+schließt Expedition
wenn noch Draft. Response: { ok: true, pdf_url, bericht_id }
Konfigurations-Konstanten
Per admin/setup.php oder llx_const:
| Konstante | Default | Zweck |
|---|---|---|
BERICHT_TAB_ON_INVOICE |
1 | Reiter "Bericht" auf Rechnungen anzeigen |
BERICHT_TAB_ON_ORDER |
1 | Reiter "Bericht" auf Aufträgen anzeigen |
BERICHT_TAB_ON_PROPAL |
1 | Reiter "Bericht" auf Angeboten anzeigen |
BERICHT_TAB_ON_SHIPMENT |
1 | Reiter "Bericht" auf Lieferungen anzeigen |
BERICHT_TAB_ON_THIRDPARTY |
0 | Read-only Bericht-Tab auf Kundenkarte |
BERICHT_SIGNATURE_IMAGE_RATIO |
0.35 | Größen-Faktor für {signature}-Platzhalter im ODT (höher = größer) |
BERICHT_SIGNATURE_BOX_DEFAULT |
JSON | Default-Geometrie für FPDI-Stempel-Fallback ({"page":"last","x_mm":120,"y_mm":230,"w_mm":70,"h_mm":35,"label":"Unterschrift Kunde"}) |
BERICHT_BURN_ANNOTATIONS |
0 | Annotationen ins PDF einbrennen statt als PDF-Annotation einbetten |
BERICHT_LIBREOFFICE_BIN |
soffice |
Pfad zur LibreOffice-Binary (für ODT→PDF) |
Datenbank
| Tabelle | Zweck |
|---|---|
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 |
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
bericht/
├── core/modules/modBericht.class.php Modul-Descriptor, Tabs, Extrafields-Init, Konstanten
├── class/
│ ├── 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 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
├── 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
│ ├── 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
│ ├── 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 (Auth über awl_sso-Cookie, Modul awlauth)
│ ├── _inc.php Dolibarr-Init + awlauth_require + JSON-Helper
│ ├── auth.php Login-Endpoint
│ ├── orders.php Order-Liste, Detail, Fotos, Create
│ ├── shipments.php Lieferungen-Liste, PDF-Stream, Unterschrift-Confirm
│ ├── photo.php Datei-Serving mit Whitelist + Thumbnails (size=thumb)
│ ├── pdf.php Finalized Bericht-PDF
│ ├── pages.php Seiten-Verwaltung (Note, Rotation, Signature)
│ ├── reports.php Bericht-CRUD
│ ├── templates.php ODT-Templates
│ ├── materials.php Materiallisten
│ ├── voice.php Sprachnotizen (Upload + Transkription)
│ ├── note.php Textnotizen zum Auftrag (Liste/Lesen/Anlegen/Ändern)
│ └── transcribe.php Whisper-Transkription
├── js/
│ ├── 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
Lizenz
GPL v3+